Drop make-promises-safe and better-sqlite3

Unhandled rejections crash Node by default since v15, and node:sqlite
covers the Store: readBigInts replaces defaultSafeIntegers, a manual
BEGIN/COMMIT helper replaces transaction(), busy detection keys on
errcode, and lookupRule normalizes the null-prototype rows. No native
build step remains (allowBuilds gone from the workspace).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Christopher Monsanto
2026-08-17 17:36:24 -04:00
parent c1dfaaec8c
commit 0711002c37
7 changed files with 70 additions and 319 deletions

269
pnpm-lock.yaml generated
View File

@@ -39,18 +39,12 @@ importers:
tools/build:
dependencies:
'@types/better-sqlite3':
specifier: ^7.6.13
version: 7.6.13
'@types/debug':
specifier: ^4.1.5
version: 4.1.13
'@types/node':
specifier: ^24.10.7
version: 24.13.3
better-sqlite3:
specifier: ^12.4.1
version: 12.11.1
debug:
specifier: ^4.1.1
version: 4.4.3
@@ -84,9 +78,6 @@ importers:
'@types/node':
specifier: ^24.10.7
version: 24.13.3
make-promises-safe:
specifier: ^5.1.0
version: 5.1.0
tools/smogdexspritesheet:
dependencies:
@@ -111,9 +102,6 @@ importers:
packages:
'@types/better-sqlite3@7.6.13':
resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
'@types/debug@4.1.13':
resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
@@ -326,34 +314,15 @@ packages:
resolution: {integrity: sha512-mlmkfc2WqdDtMl/id4qm3A7RjW6jxcbAoMjdRmsPiwQP0ufD4oXItYMnPgVHe80lnAIy+1xwzhHE1s4FoIceSw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
better-sqlite3@12.11.1:
resolution: {integrity: sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==}
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x}
bin-pack@1.0.2:
resolution: {integrity: sha512-aOk0SxEon5LF9cMxQFViSKb4qccG6rs7XKyMXIb1J8f8LA2acTIWnHdT0IOTe4gYBbqgjdbuTZ5f+UP+vlh4Mw==}
bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
clone-stats@0.0.1:
resolution: {integrity: sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==}
@@ -398,35 +367,16 @@ packages:
supports-color:
optional: true
decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
ecc-jsbn@0.1.2:
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
end-of-stream@1.4.5:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
events-universal@1.0.1:
resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==}
expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
@@ -443,9 +393,6 @@ packages:
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
first-chunk-stream@1.0.0:
resolution: {integrity: sha512-ArRi5axuv66gEsyl3UuK80CzW7t56hem73YGNYxNWTGNKFJUadSb9Gu9SHijYEUi8ulQMf1bJomYNwSCPHhtTQ==}
engines: {node: '>=0.10.0'}
@@ -457,9 +404,6 @@ packages:
resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
engines: {node: '>= 0.12'}
fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
get-pixels@3.3.3:
resolution: {integrity: sha512-5kyGBn90i9tSMUVHTqkgCHsoWoR+/lGbl4yC83Gefyr0HLIhgSWEx/2F/3YgsZ7UpYNuM6pDhDK7zebrUJ5nXg==}
@@ -470,9 +414,6 @@ packages:
resolution: {integrity: sha512-HMfSa+EIng62NbDhM63QGYoc49/m8DcZ9hhBtw+CXX9mKboSpeFVxjZ2WEWaMFZ14MUjfACK7jsrxrJffIVrCg==}
engines: {node: '>= 0.8.0'}
github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -489,15 +430,9 @@ packages:
resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
engines: {node: '>=0.8', npm: '>=1.3.7'}
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
iota-array@1.0.0:
resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==}
@@ -547,9 +482,6 @@ packages:
resolution: {integrity: sha512-+kdgg25XW11BA4cl9vF+SH01HaBipld2Nf/PlU2kSYncAbdUbDoahzrlh6yhR93N/wR2TGgcFoxebzR1LKmZUg==}
engines: {node: '>= 0.8.0'}
make-promises-safe@5.1.0:
resolution: {integrity: sha512-AfdZ49rtyhQR/6cqVKGoH7y4ql7XkS5HJI1lZm0/5N6CQosy1eYbBJ/qbhkKHzo17UH7M918Bysf6XB9f3kS1g==}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -558,22 +490,9 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
napi-build-utils@2.0.0:
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
ndarray-ops@1.2.2:
resolution: {integrity: sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==}
@@ -583,10 +502,6 @@ packages:
ndarray@1.0.19:
resolution: {integrity: sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==}
node-abi@3.94.0:
resolution: {integrity: sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==}
engines: {node: '>=10'}
node-bitmap@0.0.1:
resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==}
engines: {node: '>=v0.6.5'}
@@ -600,9 +515,6 @@ packages:
omggif@1.0.10:
resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
parse-data-uri@0.2.0:
resolution: {integrity: sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==}
@@ -621,12 +533,6 @@ packages:
resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
engines: {node: '>=4.0.0'}
prebuild-install@7.1.3:
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
engines: {node: '>=10'}
deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
hasBin: true
process-nextick-args@1.0.7:
resolution: {integrity: sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==}
@@ -636,9 +542,6 @@ packages:
psl@1.15.0:
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
pump@3.0.4:
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -647,10 +550,6 @@ packages:
resolution: {integrity: sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==}
engines: {node: '>=0.6'}
rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
readable-stream@1.0.34:
resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==}
@@ -663,10 +562,6 @@ packages:
readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
replace-ext@0.0.1:
resolution: {integrity: sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==}
engines: {node: '>= 0.4'}
@@ -692,17 +587,6 @@ packages:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
semver@7.8.5:
resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
engines: {node: '>=10'}
hasBin: true
simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
spritesmith@3.5.1:
resolution: {integrity: sha512-FrzGx9cD5SkH3Kxuk24vJJLBYHLyDskUJ2t6DSWfWkCd8+yFQAfBGVE7F0IhOpKu+aq1CZ9rgXnpaa4luIQgGg==}
engines: {node: '>= 4.0.0'}
@@ -721,9 +605,6 @@ packages:
string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
strip-bom-stream@1.0.0:
resolution: {integrity: sha512-7jfJB9YpI2Z0aH3wu10ZqitvYJaE0s5IzFuWE+0pbb4Q/armTloEUShymkDO47YSLnjAW52mlXT//hs9wXNNJQ==}
engines: {node: '>=0.10.0'}
@@ -732,17 +613,6 @@ packages:
resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==}
engines: {node: '>=0.10.0'}
strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
tar-fs@2.1.5:
resolution: {integrity: sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==}
tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
tar-stream@3.2.0:
resolution: {integrity: sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==}
@@ -809,19 +679,12 @@ packages:
resolution: {integrity: sha512-Ci3wnR2uuSAWFMSglZuB8Z2apBdtOyz8CV7dC6/U1XbltXBC+IuutUkXQISz01P+US2ouBuesSbV6zILZ6BuzQ==}
engines: {node: '>= 0.9'}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
snapshots:
'@types/better-sqlite3@7.6.13':
dependencies:
'@types/node': 24.13.3
'@types/debug@4.1.13':
dependencies:
'@types/ms': 2.1.0
@@ -963,38 +826,14 @@ snapshots:
dependencies:
to-data-view: 2.0.0
base64-js@1.5.1: {}
bcrypt-pbkdf@1.0.2:
dependencies:
tweetnacl: 0.14.5
better-sqlite3@12.11.1:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.3
bin-pack@1.0.2: {}
bindings@1.5.0:
dependencies:
file-uri-to-path: 1.0.0
bl@4.1.0:
dependencies:
buffer: 5.7.1
inherits: 2.0.4
readable-stream: 3.6.2
buffer@5.7.1:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
caseless@0.12.0: {}
chownr@1.1.4: {}
clone-stats@0.0.1: {}
clone@1.0.4: {}
@@ -1031,33 +870,19 @@ snapshots:
dependencies:
ms: 2.1.3
decompress-response@6.0.0:
dependencies:
mimic-response: 3.1.0
deep-extend@0.6.0: {}
delayed-stream@1.0.0: {}
detect-libc@2.1.2: {}
ecc-jsbn@0.1.2:
dependencies:
jsbn: 0.1.1
safer-buffer: 2.1.2
end-of-stream@1.4.5:
dependencies:
once: 1.4.0
events-universal@1.0.1:
dependencies:
bare-events: 2.9.1
transitivePeerDependencies:
- bare-abort-controller
expand-template@2.0.3: {}
extend@3.0.2: {}
extsprintf@1.3.0: {}
@@ -1068,8 +893,6 @@ snapshots:
fast-json-stable-stringify@2.1.0: {}
file-uri-to-path@1.0.0: {}
first-chunk-stream@1.0.0: {}
forever-agent@0.6.1: {}
@@ -1080,8 +903,6 @@ snapshots:
combined-stream: 1.0.8
mime-types: 2.1.35
fs-constants@1.0.0: {}
get-pixels@3.3.3:
dependencies:
data-uri-to-buffer: 0.0.3
@@ -1104,8 +925,6 @@ snapshots:
dependencies:
readable-stream: 1.1.14
github-from-package@0.0.0: {}
graceful-fs@4.2.11: {}
har-schema@2.0.0: {}
@@ -1121,12 +940,8 @@ snapshots:
jsprim: 1.4.2
sshpk: 1.18.0
ieee754@1.2.1: {}
inherits@2.0.4: {}
ini@1.3.8: {}
iota-array@1.0.0: {}
is-buffer@1.1.6: {}
@@ -1164,24 +979,14 @@ snapshots:
dependencies:
bin-pack: 1.0.2
make-promises-safe@5.1.0: {}
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
mimic-response@3.1.0: {}
minimist@1.2.8: {}
mkdirp-classic@0.5.3: {}
ms@2.1.3: {}
napi-build-utils@2.0.0: {}
ndarray-ops@1.2.2:
dependencies:
cwise-compiler: 1.1.3
@@ -1196,10 +1001,6 @@ snapshots:
iota-array: 1.0.0
is-buffer: 1.1.6
node-abi@3.94.0:
dependencies:
semver: 7.8.5
node-bitmap@0.0.1: {}
oauth-sign@0.9.0: {}
@@ -1208,10 +1009,6 @@ snapshots:
omggif@1.0.10: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
parse-data-uri@0.2.0:
dependencies:
data-uri-to-buffer: 0.0.3
@@ -1233,21 +1030,6 @@ snapshots:
pngjs@3.4.0: {}
prebuild-install@7.1.3:
dependencies:
detect-libc: 2.1.2
expand-template: 2.0.3
github-from-package: 0.0.0
minimist: 1.2.8
mkdirp-classic: 0.5.3
napi-build-utils: 2.0.0
node-abi: 3.94.0
pump: 3.0.4
rc: 1.2.8
simple-get: 4.0.1
tar-fs: 2.1.5
tunnel-agent: 0.6.0
process-nextick-args@1.0.7: {}
process-nextick-args@2.0.1: {}
@@ -1256,22 +1038,10 @@ snapshots:
dependencies:
punycode: 2.3.1
pump@3.0.4:
dependencies:
end-of-stream: 1.4.5
once: 1.4.0
punycode@2.3.1: {}
qs@6.5.5: {}
rc@1.2.8:
dependencies:
deep-extend: 0.6.0
ini: 1.3.8
minimist: 1.2.8
strip-json-comments: 2.0.1
readable-stream@1.0.34:
dependencies:
core-util-is: 1.0.3
@@ -1305,12 +1075,6 @@ snapshots:
string_decoder: 1.1.1
util-deprecate: 1.0.2
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
replace-ext@0.0.1: {}
request@2.88.2:
@@ -1354,16 +1118,6 @@ snapshots:
semver@5.7.2: {}
semver@7.8.5: {}
simple-concat@1.0.1: {}
simple-get@4.0.1:
dependencies:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
spritesmith@3.5.1:
dependencies:
concat-stream: 1.5.2
@@ -1399,10 +1153,6 @@ snapshots:
dependencies:
safe-buffer: 5.1.2
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
strip-bom-stream@1.0.0:
dependencies:
first-chunk-stream: 1.0.0
@@ -1412,23 +1162,6 @@ snapshots:
dependencies:
is-utf8: 0.2.1
strip-json-comments@2.0.1: {}
tar-fs@2.1.5:
dependencies:
chownr: 1.1.4
mkdirp-classic: 0.5.3
pump: 3.0.4
tar-stream: 2.2.0
tar-stream@2.2.0:
dependencies:
bl: 4.1.0
end-of-stream: 1.4.5
fs-constants: 1.0.0
inherits: 2.0.4
readable-stream: 3.6.2
tar-stream@3.2.0:
dependencies:
b4a: 1.8.1
@@ -1529,6 +1262,4 @@ snapshots:
clone-stats: 0.0.1
replace-ext: 0.0.1
wrappy@1.0.2: {}
xtend@4.0.2: {}

View File

@@ -1,6 +1,6 @@
packages:
- 'tools/*'
- 'lib/*'
- "data/lib"
- 'tools/*'
- 'lib/*'
- "data/lib"
allowBuilds:
better-sqlite3: true
better-sqlite3: set this to true or false

View File

@@ -1,9 +1,7 @@
{
"dependencies": {
"@types/better-sqlite3": "^7.6.13",
"@types/debug": "^4.1.5",
"@types/node": "^24.10.7",
"better-sqlite3": "^12.4.1",
"debug": "^4.1.1"
},
"scripts": {

View File

@@ -1,7 +1,7 @@
import fs from 'fs';
import pathlib from 'path';
import Database from 'better-sqlite3';
import {DatabaseSync} from 'node:sqlite';
import {BuildError} from './errors.ts';
import type {FileStat} from './hash.ts';
@@ -39,21 +39,37 @@ CREATE TABLE IF NOT EXISTS rule_outputs (
);
`;
function userVersion(db : DatabaseSync) : number {
const row = db.prepare('PRAGMA user_version').get() as {user_version : bigint | number};
return Number(row.user_version);
}
export class Store {
private db : Database.Database;
private db : DatabaseSync;
constructor(dbPath : string) {
fs.mkdirSync(pathlib.dirname(dbPath), {recursive: true});
this.db = new Database(dbPath);
this.db.defaultSafeIntegers(true);
this.db.pragma('journal_mode = WAL');
this.db.pragma('foreign_keys = ON');
this.db.pragma('synchronous = NORMAL');
this.db = new DatabaseSync(dbPath, {readBigInts: true});
this.db.exec('PRAGMA journal_mode = WAL');
this.db.exec('PRAGMA foreign_keys = ON');
this.db.exec('PRAGMA synchronous = NORMAL');
this.migrate();
}
private transaction<T>(fn : () => T) : T {
this.db.exec('BEGIN');
try {
const result = fn();
this.db.exec('COMMIT');
return result;
} catch (err) {
this.db.exec('ROLLBACK');
throw err;
}
}
private migrate() : void {
const version = Number(this.db.pragma('user_version', {simple: true}));
const version = userVersion(this.db);
if (version === 0) {
this.db.exec('BEGIN;' + DDL + 'PRAGMA user_version = 2; COMMIT;');
} else if (version === 1) {
@@ -74,10 +90,12 @@ export class Store {
loadFileCache() : Map<string, FileStat> {
const result = new Map<string, FileStat>();
const rows = this.db.prepare<[], {path : string, size : bigint, mtime_ns : bigint, hash : Buffer}>(
'SELECT path, size, mtime_ns, hash FROM file_cache').all();
const rows = this.db.prepare('SELECT path, size, mtime_ns, hash FROM file_cache').all() as
unknown as {path : string, size : bigint, mtime_ns : bigint, hash : Uint8Array}[];
for (const row of rows) {
result.set(row.path, {size: row.size, mtimeNs: row.mtime_ns, hash: row.hash});
const h = row.hash;
result.set(row.path, {size: row.size, mtimeNs: row.mtime_ns,
hash: Buffer.from(h.buffer, h.byteOffset, h.byteLength)});
}
return result;
}
@@ -87,79 +105,84 @@ export class Store {
INSERT INTO file_cache (path, size, mtime_ns, hash) VALUES (?, ?, ?, ?)
ON CONFLICT(path) DO UPDATE SET
size = excluded.size, mtime_ns = excluded.mtime_ns, hash = excluded.hash`);
this.db.transaction(() => {
this.transaction(() => {
for (const [path, stat] of entries) {
upsert.run(path, stat.size, stat.mtimeNs, stat.hash);
}
})();
});
}
pruneFileCache(live : Set<string>) : void {
const paths = this.db.prepare<[], {path : string}>('SELECT path FROM file_cache').all();
const paths = this.db.prepare('SELECT path FROM file_cache').all() as
unknown as {path : string}[];
const del = this.db.prepare('DELETE FROM file_cache WHERE path = ?');
this.db.transaction(() => {
this.transaction(() => {
for (const {path} of paths) {
if (!live.has(path)) {
del.run(path);
}
}
})();
});
}
lookupRule(key : string) : StoredOutput[] | null {
return this.db.transaction(() => {
const rule = this.db.prepare<[string], {id : bigint}>(
'SELECT id FROM rules WHERE key = ?').get(key);
return this.transaction(() => {
const rule = this.db.prepare('SELECT id FROM rules WHERE key = ?').get(key) as
{id : bigint} | undefined;
if (rule === undefined) {
return null;
}
return this.db.prepare<[bigint], StoredOutput>(
'SELECT digest, ext, size FROM rule_outputs WHERE rule_id = ? ORDER BY ord').all(rule.id);
})();
const rows = this.db.prepare(
'SELECT digest, ext, size FROM rule_outputs WHERE rule_id = ? ORDER BY ord')
.all(rule.id) as unknown as StoredOutput[];
// node:sqlite rows have a null prototype; return plain objects.
return rows.map(r => ({digest: r.digest, ext: r.ext, size: r.size}));
});
}
// One transaction per completed rule: an interrupted build only ever
// contains fully-recorded rules.
recordRule(key : string, cmds : string[], outputs : StoredOutput[]) : void {
const upsert = this.db.prepare<[string, string], {id : bigint}>(`
const upsert = this.db.prepare(`
INSERT INTO rules (key, cmds) VALUES (?, ?)
ON CONFLICT(key) DO UPDATE SET cmds = excluded.cmds
RETURNING id`);
const delOutputs = this.db.prepare('DELETE FROM rule_outputs WHERE rule_id = ?');
const insOutput = this.db.prepare(
'INSERT INTO rule_outputs (rule_id, ord, digest, ext, size) VALUES (?, ?, ?, ?, ?)');
this.db.transaction(() => {
const {id} = upsert.get(key, cmds.join('\n'))!;
this.transaction(() => {
const {id} = upsert.get(key, cmds.join('\n')) as {id : bigint};
delOutputs.run(id);
outputs.forEach((o, i) => insOutput.run(id, i, o.digest, o.ext, o.size));
})();
});
}
// GC: drop every rule whose key is not live. Returns the removal count.
deleteKeysNotIn(live : Set<string>) : number {
const keys = this.db.prepare<[], {id : bigint, key : string}>('SELECT id, key FROM rules').all();
const keys = this.db.prepare('SELECT id, key FROM rules').all() as
unknown as {id : bigint, key : string}[];
const del = this.db.prepare('DELETE FROM rules WHERE id = ?');
let removed = 0;
this.db.transaction(() => {
this.transaction(() => {
for (const {id, key} of keys) {
if (!live.has(key)) {
del.run(id);
removed++;
}
}
})();
});
return removed;
}
// Every CAS object referenced by some rule, as "<digest>.<ext>" basenames.
liveObjects() : Set<string> {
const rows = this.db.prepare<[], {digest : string, ext : string}>(
'SELECT digest, ext FROM rule_outputs').all();
const rows = this.db.prepare('SELECT digest, ext FROM rule_outputs').all() as
unknown as {digest : string, ext : string}[];
return new Set(rows.map(r => `${r.digest}.${r.ext}`));
}
close() : void {
this.db.pragma('wal_checkpoint(TRUNCATE)');
this.db.exec('PRAGMA wal_checkpoint(TRUNCATE)');
this.db.close();
}
}
@@ -170,9 +193,9 @@ export function dbVersion(dbPath : string) : number | null {
if (!fs.existsSync(dbPath)) {
return null;
}
const db = new Database(dbPath, {readonly: true});
const db = new DatabaseSync(dbPath, {readOnly: true});
try {
return Number(db.pragma('user_version', {simple: true}));
return userVersion(db);
} finally {
db.close();
}
@@ -182,12 +205,12 @@ export function dbVersion(dbPath : string) : number | null {
// by the OS on any crash, so there are no stale lock files.
export function acquireLock(lockPath : string) : () => void {
fs.mkdirSync(pathlib.dirname(lockPath), {recursive: true});
const lock = new Database(lockPath, {timeout: 0});
const lock = new DatabaseSync(lockPath, {timeout: 0});
try {
lock.exec('BEGIN EXCLUSIVE');
} catch (err) {
lock.close();
if ((err as {code? : string}).code === 'SQLITE_BUSY') {
if ((err as {errcode? : number}).errcode === 5) { // SQLITE_BUSY
throw new BuildError('Another build is already running.');
}
throw err;

View File

@@ -5,7 +5,7 @@ import os from 'node:os';
import pathlib from 'node:path';
import {test} from 'node:test';
import Database from 'better-sqlite3';
import {DatabaseSync} from 'node:sqlite';
import {Store} from '../store.ts';
@@ -45,7 +45,7 @@ test('file cache roundtrip and prune', () => {
test('migrates a v1 db: drops rule tables, keeps file_cache', () => {
const dbPath = makeDbPath();
const v1 = new Database(dbPath);
const v1 = new DatabaseSync(dbPath);
v1.exec(`BEGIN;
CREATE TABLE file_cache (
path TEXT PRIMARY KEY, size INTEGER NOT NULL,
@@ -81,14 +81,15 @@ test('migrates a v1 db: drops rule tables, keeps file_cache', () => {
assert.deepEqual(store.lookupRule('new'), [{digest: 'd', ext: 'png', size: 1n}]);
store.close();
const check = new Database(dbPath);
assert.equal(Number(check.pragma('user_version', {simple: true})), 2);
const check = new DatabaseSync(dbPath);
const {user_version} = check.prepare('PRAGMA user_version').get() as {user_version : number};
assert.equal(Number(user_version), 2);
check.close();
});
test('rejects unknown schema versions', () => {
const dbPath = makeDbPath();
const weird = new Database(dbPath);
const weird = new DatabaseSync(dbPath);
weird.exec('PRAGMA user_version = 7');
weird.close();
assert.throws(() => new Store(dbPath), /Unknown build db schema version 7/);

View File

@@ -1,5 +1,4 @@
import 'make-promises-safe';
import cp from 'child_process';
import path from 'path';

View File

@@ -1,7 +1,6 @@
{
"type": "module",
"dependencies": {
"@types/node": "^24.10.7",
"make-promises-safe": "^5.1.0"
"@types/node": "^24.10.7"
}
}