{"_id":"thetowersdk","_rev":"42-d6781e5c2f6b02b0513edb72dc1048df","name":"thetowersdk","dist-tags":{"latest":"0.11.0"},"versions":{"0.1.0":{"name":"thetowersdk","version":"0.1.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.1.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"5e2822ceb1cf86ee4177c64bcee299d5dfc14efb","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.1.0.tgz","fileCount":1123,"integrity":"sha512-VNIim560JhGyQX19++dmFVp5rZuUiLU73au3bD9j33qV+eD8VveTZoiltcZDN/ZW5yycMdl2dmRcZ3vrmxf/xA==","signatures":[{"sig":"MEUCIQD3o4uv92lEIYhYo3hHMSl9BXyjag6nsWyoFWqPfjuFKQIgJvGykCamFyj8HwPvEdS7224YBqQBb9a3Qp5XL3kvzvw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11240463},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"93bb8310afa53a5eb8c712c038582503f6c8b607","scripts":{"lint":"eslint .","test":"vitest run","build":"pnpm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"pnpm lint && pnpm lint:conventions && pnpm type-check && pnpm test && pnpm build","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"pnpm run clean && pnpm run build","lint:conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.1.0_1786376362816_0.9789499530212364","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"thetowersdk","version":"0.1.1","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.1.1","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"d57d79967b6a56a1411c059609fea6b598e94deb","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.1.1.tgz","fileCount":1141,"integrity":"sha512-SnmPPuda95n1N0fUCTnzogQUF6ehL/79DFO6z/k2KdSsJ35B+/yTpi3+Q/9CGmP+1C8kOK+sWYHMrvUS/L+kdw==","signatures":[{"sig":"MEQCIGIbq7oa4sbi7+fM3qnFUYO2aIW+NnxNLxrd1KgPwYwYAiBga/GeH///XpSk/er38p2CCLZgn8L7ne9osd+4RdYEqA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11332615},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"ab134c3222fd3de4575ae1df6917becb1ca341ef","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.1.1_1786382786492_0.5207280054184471","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"thetowersdk","version":"0.2.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.2.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"a0f7cecc67c37570a3afa74497fc01f812ed7f3a","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.2.0.tgz","fileCount":1145,"integrity":"sha512-nXG2war+mkfydPQUr/8y+DMHsm0HpqYdH5Co8VHuab1z9B3gO+OmdqSXJeoPVy93uZ6Kw8v2RFU33jAkQQwSSA==","signatures":[{"sig":"MEYCIQCwbK3jtX3p8jHXGZWDANjLgf3ymRkdcUIWeW0K3mDp6AIhAMp5lnb7GS7HpG6kH5/SXFm1uustAz2fNBvAyviPs4w6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11302875},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"80c0ead008d9a33e7e4a1c6c1e375fcc07e4e219","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.2.0_1786421613246_0.017668714516558826","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"thetowersdk","version":"0.3.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.3.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"ba5ae15bb35c00ded7589d9759f1080cd9ba926d","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.3.0.tgz","fileCount":1177,"integrity":"sha512-KIOhZeUk0Ngz/e/ymOk/yhjsMZenesmjJQwraaA2zT/OFJkw/oANcjLi24fx3E1QYuWOQ387KNhd/n7k2j/kgw==","signatures":[{"sig":"MEYCIQCXRWVedvNTNeElH8kSqYGFpSrICT+reqAC/zLkZA2lGAIhALCfm9UJqb2tR0TsHJpjiQHy/62Puq0WEjs67QhOV6bL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11589552},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"27fc510f14228715a1d515ffe136dded1aedcb7f","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.3.0_1786466216419_0.1659295028639094","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"thetowersdk","version":"0.3.1","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.3.1","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"ee3d0e0a4b0f24c350fc8e58cace0cb1405ed360","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.3.1.tgz","fileCount":1177,"integrity":"sha512-t9n4kbqj8E1TDggwj6t4D/Y6AWT6DZvAHcx61sAFHJhBDYAhAjlj9mYi+7jiQ0OBZG/2YYPfvy3lN9ZMklbk7A==","signatures":[{"sig":"MEUCIAKHt5xsGEgA1JC18XL9GbugyNNYl8iN6xOvmVbxSV+lAiEA/N8s465sT7KpHHOS1jkKTZ5553MukEQcOllzGqc0Cxk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11589994},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"a02b5d6901576d6382885125d19264156d02081b","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.3.1_1786466745182_0.6132443830924614","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"thetowersdk","version":"0.4.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.4.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"dad084e3b9a4d45d595bffd2fe416e369e850cf9","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.4.0.tgz","fileCount":1335,"integrity":"sha512-PjUpAfY3rtDj7EgXceIiFbzfZnn8vBxZO+hfEHiUUUUKYZcovAuzlzbbNs6zLSO5i3sjTs0+PmvqkQ4M6tFQqA==","signatures":[{"sig":"MEQCIHucjWGGWZHZmCkr7AehAX4Y45kfgLgJ29sRQgwcTlj5AiAVoh5t8ALUqvvdUbYY0zcNuGvA+dNlzBYUrVxEzT85TA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":12812608},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"b993b6ee202154a8291750e2e925e0e7da3977ec","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.4.0_1786683447017_0.08730048403814217","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"thetowersdk","version":"0.4.1","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.4.1","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"1947d1419fcbe6ea976b02d6770c9b684eeafa48","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.4.1.tgz","fileCount":1335,"integrity":"sha512-dp7DWZ6Jw+tPt9Dsusn2DnSOpojdinUBkc2XLLHR+IeQgUa5HxnUz2L/1EKeC9/pIHHcR1mgZsW+F4Cqdg0WMw==","signatures":[{"sig":"MEUCIQC7hfI9RJS736NGcgCtxLnxxh+P95LVvDGsasCq4sJsXwIgDQLzH/r9tCe2wGwuVgy/6w+tIhf56ybDa+326TG95LA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":12816743},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"638a582a1ab7157218d9189077ff437a2c51b919","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.4.1_1786708194862_0.6388095499241944","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"thetowersdk","version":"0.5.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.5.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"812c21e90d50f2791a8feeb567f9d00365509398","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.5.0.tgz","fileCount":1558,"integrity":"sha512-nZIocV/eb6yk/jhr5xWkqHAPO/AudyiKnGjHyGt43iHI8aiGgfE0MMRg0uszUzCf8QNrN5JUwZLrGYno0ML7kw==","signatures":[{"sig":"MEQCIBQSFw3M3C3VFokXa+MVwS9f/mVMr3I9iY6dTi9VQMA3AiAZM3VBqvyU5WuatCa2os6Lo7S3x2dQYeWXYxjRtQcNpg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14282233},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"4a057708c3c7bdd5f763a5b45787ab10b01c40f5","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run type-check:examples && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"peerDependencies":{"@tmrxjd/governance-engine":">=0.1.0"},"peerDependenciesMeta":{"@tmrxjd/governance-engine":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.5.0_1786839231091_0.7413539407014362","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"thetowersdk","version":"0.5.1","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.5.1","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"f94ff9273dd8e633d0359679b77d8512b00f9843","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.5.1.tgz","fileCount":1514,"integrity":"sha512-knfnDTv/SjCYyi/Gr8KiPcTHzlptlI+dZlNAgA+GRyNe1Vc13wFTIE+SYTTy3e2VqsCIidfbN9Bx342DvbhYPg==","signatures":[{"sig":"MEUCIAnS198wY3R5LLW3Y6iuME3HXw02tePrT98pab3OuSwxAiEAuYEHGR7byIu+92js8//tRQ0ejKoSQXKlozTv+G2pQ2c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14152977},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"4a057708c3c7bdd5f763a5b45787ab10b01c40f5","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run type-check:examples && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"peerDependencies":{"@tmrxjd/governance-engine":">=0.1.0"},"peerDependenciesMeta":{"@tmrxjd/governance-engine":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.5.1_1786839462657_0.45740275548859266","host":"s3://npm-registry-packages-npm-production"}},"0.5.2":{"name":"thetowersdk","version":"0.5.2","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.5.2","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://github.com/TmRxJD/TheTowerSDK#readme","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"e0c3b7b063e9782968aec2756e521c0f3bb4694d","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.5.2.tgz","fileCount":1514,"integrity":"sha512-xR1uV9WzpfB6Ga5DNDbwhl4f+CTR0M6dyHZvOZSmEbj4TbYb6jO0O9S3/iD3IF4OvLMgOq3CgXmIIPfuXfJFfQ==","signatures":[{"sig":"MEUCIQCo9yaLCMtzVbhfrIJYa1EL/XNt2UguaHd3g2ESVHRZcQIgKCn04lRvrc9xLDTWF2UYFps8AA7OVaiJi1j2pa4lmdg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14152196},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json"},"gitHead":"4a057708c3c7bdd5f763a5b45787ab10b01c40f5","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run type-check && npm run type-check:examples && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"peerDependencies":{"@tmrxjd/governance-engine":">=0.1.0"},"peerDependenciesMeta":{"@tmrxjd/governance-engine":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.5.2_1786839988689_0.049126077435495796","host":"s3://npm-registry-packages-npm-production"}},"0.5.3":{"name":"thetowersdk","version":"0.5.3","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.5.3","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK-site/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"b12c9fae1aa0ff8e642823fde3e71a7b439039fe","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.5.3.tgz","fileCount":1693,"integrity":"sha512-Hc/jjpq2InP83dBKrLf/vBAcGjCOTixFWgysq6SWMrExPVSB06w8vtn9Af4GJbildPA/hRO5ssX4fd2vDNEOSg==","signatures":[{"sig":"MEQCIErWfAgW84WpMJbgrB/JSZOcvE3/jxgxat7OI22pK+DKAiBu/WSVoqISl1Fj3av+qIbIJDSUlrslhJwQnG97T0LJWA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":16339322},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"9af4c3c5416e42fb64eb2874f6b45fa8d8f73d19","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run lint:docs && npm run type-check && npm run type-check:examples && npm run build && npm test","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","lint:docs":"node scripts/check-readme-links.mjs","type-check":"tsc -p tsconfig.build.json --noEmit","test:schema":"vitest run src/data/schemas.test.ts","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","prepublishOnly":"npm run clean && npm run build","lint:conventions":"node scripts/check-conventions.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"charts":["dist/charts/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"peerDependencies":{"@tmrxjd/governance-engine":">=0.1.0"},"peerDependenciesMeta":{"@tmrxjd/governance-engine":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.5.3_1787005756281_0.12276027509697762","host":"s3://npm-registry-packages-npm-production"}},"0.5.4":{"name":"thetowersdk","version":"0.5.4","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.5.4","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"4e13277af5f05183e16a01a8a8500202368882f9","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.5.4.tgz","fileCount":2002,"integrity":"sha512-844o8D3OeheFD+qdTLkkmeX6BZge3ous+LjMxkPKtt16nxmkHghjjLgdI/bWkZXfdptH/QyKihoh0Gfl6oBaCg==","signatures":[{"sig":"MEUCIHQr0aOTWBRkX+X+G6uObwvM4kgqJlQR1p9Hctse1DDXAiEArdH36Sj4T5iXqObWSAcaxGPriawf/l18/M5r2Ydgf24=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20246912},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","import":"./dist/bot/index.js","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","import":"./dist/assets/index.js","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./inputs":{"types":"./dist/inputs/index.d.ts","default":"./dist/inputs/index.js","require":"./dist/inputs/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","import":"./dist/sheets/index.js","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","import":"./dist/builders/index.js","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"9b7bf4ae86c08c2d501a6a244063c6cd2fd2b801","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run lint:acronyms && npm run lint:docs && npm run wiki:check && npm run lint:published && npm run type-check && npm run type-check:examples && npm run build && npm test && npm run docs:check","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-asset-manifest.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"inputs":["dist/inputs/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.5.4_1787689971041_0.4000625258738617","host":"s3://npm-registry-packages-npm-production"}},"0.5.5":{"name":"thetowersdk","version":"0.5.5","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.5.5","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"3a6676e00cb1a754f01e245abdebf70bc4551157","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.5.5.tgz","fileCount":2010,"integrity":"sha512-QFMeGIhxP+2NB/pdfTrVIT3mYNJ5IHxEZJ2UGiAhnJnA1KG4jd8vNnmO2vHrFj7l417T6m5+dpB68Eaprbl50w==","signatures":[{"sig":"MEUCIEQjgS+10tcFG8NKiy+fclzyAv4YZkK0f2WgL9YBh6qWAiEA02eNKAtA5ILP9v45yaROX6ZHl43FdEvn9Qt0/zwXVfc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20599741},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","import":"./dist/bot/index.js","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","import":"./dist/assets/index.js","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./inputs":{"types":"./dist/inputs/index.d.ts","default":"./dist/inputs/index.js","require":"./dist/inputs/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","import":"./dist/sheets/index.js","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","import":"./dist/builders/index.js","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"9ec43414c64ac066205ff907cdc1139cdbeb6440","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run lint:acronyms && npm run lint:docs && npm run wiki:check && npm run lint:published && npm run type-check && npm run type-check:examples && npm run build && npm test && npm run docs:check","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.5.6 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"inputs":["dist/inputs/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.5.5_1787707881167_0.9263528876276461","host":"s3://npm-registry-packages-npm-production"}},"0.5.6":{"name":"thetowersdk","version":"0.5.6","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.5.6","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"4d2fd5f561daaaa5b15d9655ec27d32094e51dd3","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.5.6.tgz","fileCount":2010,"integrity":"sha512-uUjWeFnY6ORQGdqO+METH2VfbSUx44YqObdDoMGsK9ELhfodlY4TdrKCcHxum8O7aEA9GtXOMFZ+Lx3ur0a4MQ==","signatures":[{"sig":"MEQCIH6Ei8B04iQYk0C6TuKq4xJP1p//qG0v7WizrQCJBNl8AiAp8LQlb6KL1G4+HGbIA26qjxEHnDMZwfFouPXchxPu2Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20578820},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","import":"./dist/bot/index.js","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","import":"./dist/assets/index.js","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./inputs":{"types":"./dist/inputs/index.d.ts","default":"./dist/inputs/index.js","require":"./dist/inputs/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","import":"./dist/sheets/index.js","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","import":"./dist/builders/index.js","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"a75b453e3ed92b62d0a4f276bdd60976d8797922","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run lint:acronyms && npm run lint:docs && npm run wiki:check && npm run lint:published && npm run type-check && npm run type-check:examples && npm run build && npm test && npm run docs:check","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.6.0 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"inputs":["dist/inputs/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.5.6_1787713173515_0.6621597218104349","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"thetowersdk","version":"0.6.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.6.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"726ca3dcc7af12ddbb991491b4a7a574f0d09bb3","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.6.0.tgz","fileCount":2006,"integrity":"sha512-gJW9K2dNYSpf3yPBP+kDU1eRAJl6NR5nuYx2T2Ura2HAy6E8MU8K2AwIK4U5Ok+l2pKGKDVMEYmij8EJfzTdGA==","signatures":[{"sig":"MEUCIDVRWNcmgvdPY7EMf82bC8nti7imI+wKgqF1Oh+/03BvAiEAuQUb7ZUZpebhsrHIruFDoHdtDduQ9nrd9FYXDY/KRpc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20503331},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","import":"./dist/bot/index.js","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","import":"./dist/assets/index.js","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","import":"./dist/sheets/index.js","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","import":"./dist/builders/index.js","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"eaff6afb7506acb82165fb663d4ca3397b4b805b","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"npm run lint && npm run lint:conventions && npm run lint:acronyms && npm run lint:docs && npm run wiki:check && npm run lint:published && npm run type-check && npm run type-check:examples && npm run build && npm test && npm run docs:check","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.6.0_1787715248667_0.3824068194150483","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"thetowersdk","version":"0.7.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.7.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"ef849f9e1e90ebf38b44770b9420d923eb55aa81","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.7.0.tgz","fileCount":2012,"integrity":"sha512-naRu8stW8qFKFiEOue+smButHMK1Uq0JpppUFN2cHq92WMefiTZCYtulT6WkJlLJLhdZj2Fh7xf6y3v+wPsuOA==","signatures":[{"sig":"MEUCIGqwOprO18HTbOysEhM7dZEQeWJ+yX21R1I5HTv0nMOkAiEA7Uni3Ku+O7WFJ+fl4EVY0/WIqdxQaOSTpKYUwaTgraU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20546967},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","import":"./dist/bot/index.js","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","import":"./dist/assets/index.js","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","import":"./dist/sheets/index.js","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","import":"./dist/builders/index.js","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"c8939abdccd76746f00458f651d3217a5088c7d2","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"node scripts/verify.mjs","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","verify:serial":"node scripts/verify.mjs --serial","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.8.0 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.7.0_1787721507940_0.16829865061586569","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"thetowersdk","version":"0.8.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.8.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"85c665a3cf24a2cf988e0925d2cc8b1158d0b26b","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.8.0.tgz","fileCount":2020,"integrity":"sha512-YdgViwF8c4IPUvsb0XlnJ8Ue0Zq9onb6uS7xbGsfSB504uUlVvF2Ee7ZdnYlPDtLzbs4zGVO89ag5p6PEMeA4Q==","signatures":[{"sig":"MEUCICn/qHZILZLxo5LE85h5r4ibO7aqbeFw2+KboZ93oSVqAiEA5NNEf+s+JQTwfqUQ5TwKNXNJftnWJMLDPR1/Z6b21R4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20567167},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","import":"./dist/bot/index.js","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","import":"./dist/assets/index.js","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","import":"./dist/sheets/index.js","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","import":"./dist/builders/index.js","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./save-decoder":{"types":"./dist/save-decoder/index.d.ts","import":"./dist/save-decoder/index.js","default":"./dist/save-decoder/index.js","require":"./dist/save-decoder/index.js"},"./contributions":{"types":"./dist/contributions/index.d.ts","import":"./dist/contributions/index.js","default":"./dist/contributions/index.js","require":"./dist/contributions/index.js"},"./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"c7b8d4bf4c38cc650bf4d5912fac75da5fdceca7","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"node scripts/verify.mjs","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","verify:serial":"node scripts/verify.mjs --serial","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.9.0 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"],"save-decoder":["dist/save-decoder/index.d.ts"],"contributions":["dist/contributions/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.8.0_1787723551026_0.834881517082467","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"thetowersdk","version":"0.9.0","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.9.0","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"d430264be941dc659eb3a8ef3aada725277e9b5a","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.9.0.tgz","fileCount":2024,"integrity":"sha512-Qqf5jofB/8wfEZDugK0SVAz2TE9aQ1Vx4XqDXvwJzolgwObENjScX/bmiUPKai1qmJuh0Nxsn1Ts772u4hkTAw==","signatures":[{"sig":"MEQCICY6zRerzEj4fzx9YAYGqsdBD+xmIceGwrysfPTmzVYjAiA4O6asO+7uuJmpc7XQC0H/EzYTGQb5w2i4NZlu+qAoqw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20587725},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./save-decoder":{"types":"./dist/save-decoder/index.d.ts","default":"./dist/save-decoder/index.js","require":"./dist/save-decoder/index.js"},"./contributions":{"types":"./dist/contributions/index.d.ts","default":"./dist/contributions/index.js","require":"./dist/contributions/index.js"},"./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"5cb37f0e09ffe47f709bebd8f75706a400aa4d22","scripts":{"mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"node scripts/verify.mjs","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","wasm:call":"node wasm/run.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wasm:build":"node wasm/build.mjs","wasm:check":"node wasm/build.mjs --check","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","verify:serial":"node scripts/verify.mjs --serial","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.9.1 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"],"save-decoder":["dist/save-decoder/index.d.ts"],"contributions":["dist/contributions/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.9.0_1787731348093_0.8579345304834411","host":"s3://npm-registry-packages-npm-production"}},"0.9.1":{"name":"thetowersdk","version":"0.9.1","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.9.1","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"e39eb977772e4c77e6b9f3adb3aada816c80607a","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.9.1.tgz","fileCount":1866,"integrity":"sha512-bHSD1qjpSCWOTafZCfFnUnv6DupHDMXae2Iuf1muLhhU4/y0FwC7pudw8R2cpSwSm9VxArnuPipJO0qd2LoEQQ==","signatures":[{"sig":"MEUCIQCihZHJ/3npypoO9fI9D0RNlC8uJ27mZERCH7kMqHVe6gIgZo6Jywzw7izxKXPMvrMb3Isen3S4D5Y+8G8caawej/k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21750557},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./save-decoder":{"types":"./dist/save-decoder/index.d.ts","default":"./dist/save-decoder/index.js","require":"./dist/save-decoder/index.js"},"./contributions":{"types":"./dist/contributions/index.d.ts","default":"./dist/contributions/index.js","require":"./dist/contributions/index.js"},"./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"d7551d100da411dcbae9eb0a07f94f1df2b3055a","scripts":{"map":"node scripts/map-the-repo.mjs","mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"node scripts/verify.mjs","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","lint:map":"node scripts/map-the-repo.mjs --check","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","wasm:call":"node wasm/run.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wasm:build":"node wasm/build.mjs","wasm:check":"node wasm/build.mjs --check","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","lint:package":"node scripts/audit-package-contents.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","verify:serial":"node scripts/verify.mjs --serial","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","lint:conventions":"node scripts/check-conventions.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json","epaths:refresh-reference":"node scripts/refresh-effective-paths-reference.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.10.1 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"],"save-decoder":["dist/save-decoder/index.d.ts"],"contributions":["dist/contributions/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.9.1_1787770048463_0.7934653277301125","host":"s3://npm-registry-packages-npm-production"}},"0.10.1":{"name":"thetowersdk","version":"0.10.1","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","_id":"thetowersdk@0.10.1","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"homepage":"https://tmrxjd.github.io/TheTowerSDK/","bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"bin":{"thetowersdk-mcp":"mcp/server.mjs"},"dist":{"shasum":"4626df6365c6ba11f28c903776dbc4227add3f41","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.10.1.tgz","fileCount":2166,"integrity":"sha512-eLpAvQFlHld+lyZKtkV54p1Musy3Uu9TpdbWWXQFExnuTTiH2YO8J2vHjxZkT7elmnflNHouz0icGqEa8cKnuQ==","signatures":[{"sig":"MEUCIGcv22gEd3OgXLNLH/X5cawj+ffxcLY60UkV98rvRm/7AiEA+hekUnssSsDSABKM64qPw9iHeThOWMwZ8gR8VkP4wXE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":22282319},"main":"./dist/index.js","type":"commonjs","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./internal/*":{"types":"./dist/*.d.ts","default":"./dist/*.js","require":"./dist/*.js"},"./package.json":"./package.json","./save-decoder":{"types":"./dist/save-decoder/index.d.ts","default":"./dist/save-decoder/index.js","require":"./dist/save-decoder/index.js"},"./contributions":{"types":"./dist/contributions/index.d.ts","default":"./dist/contributions/index.js","require":"./dist/contributions/index.js"},"./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"84120c2ad974d0e91a7f452fb2fefc2e2119b37a","scripts":{"map":"node scripts/map-the-repo.mjs","mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"node scripts/verify.mjs","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","lint:map":"node scripts/map-the-repo.mjs --check","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","vault:v29":"node scripts/build-vault-v29.mjs --write","wasm:call":"node wasm/run.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wasm:build":"node wasm/build.mjs","wasm:check":"node wasm/build.mjs --check","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run src/data/schemas.test.ts","examples:run":"node scripts/run-examples.mjs","lint:package":"node scripts/audit-package-contents.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","verify:serial":"node scripts/verify.mjs --serial","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","labs:categories":"node scripts/build-lab-categories.mjs","lint:specifiers":"node scripts/check-sdk-specifiers.mjs","lint:conventions":"node scripts/check-conventions.mjs","lint:organization":"node scripts/check-organization.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json","daily-missions:goals":"node scripts/build-daily-mission-goals.mjs","vault:v29:enemy-binary":"node scripts/build-vault-v29-enemy-binary.mjs","vault:v29:enemy-overlay":"node scripts/apply-vault-v29-enemy-overlay.mjs","epaths:refresh-reference":"node scripts/refresh-effective-paths-reference.mjs"},"_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"deprecated":"Superseded — please upgrade to thetowersdk@0.11.0 or later.","repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"internal/*":["dist/*.d.ts"],"save-decoder":["dist/save-decoder/index.d.ts"],"contributions":["dist/contributions/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","xlsx":"^0.18.5","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"tmp":"tmp/thetowersdk_0.10.1_1788542598308_0.6622900217734002","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"_id":"thetowersdk@0.11.0","bin":{"thetowersdk-mcp":"mcp/server.mjs"},"bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"dist":{"shasum":"0d3c7e5440cb66cd7b831f98b38b8a75bf34e9fc","tarball":"https://registry.npmjs.org/thetowersdk/-/thetowersdk-0.11.0.tgz","fileCount":2306,"integrity":"sha512-WT6cXWDangI0fkwdR1qL65OA6o/9vCJH0Vhgy+QlTlKl/6gNUg8aVaWZpp0SCMvj7yPknZ/zLPosMDWsQTzsKw==","signatures":[{"sig":"MEYCIQD91Ig79AKZZqQAM1hcNzvW4gWgjNqiCKMhBySfjHBSrgIhAJwLuHpouB429qX2UIApVMwaxkSSDqeQ+V8KbgcTErSK","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCFwDaH8tJz/BvSjUob9HHTeWZSIRIWmrtxd+jgb4o5HgIhAKcdux0HBCICoOyl0pXa1GPkbeBpFQRvpEUXoyWgAzrt"}],"unpackedSize":20632806},"main":"./dist/index.js","name":"thetowersdk","type":"commonjs","types":"./dist/index.d.ts","author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js","require":"./dist/index.js"},"./bot":{"types":"./dist/bot/index.d.ts","default":"./dist/bot/index.js","require":"./dist/bot/index.js"},"./data":{"types":"./dist/data/index.d.ts","default":"./dist/data/index.js","require":"./dist/data/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js","require":"./dist/node/index.js"},"./save":{"types":"./dist/save/index.d.ts","default":"./dist/save/index.js","require":"./dist/save/index.js"},"./wiki":{"types":"./dist/wiki/index.d.ts","default":"./dist/wiki/index.js","require":"./dist/wiki/index.js"},"./assets":{"types":"./dist/assets/index.d.ts","default":"./dist/assets/index.js","require":"./dist/assets/index.js"},"./charts":{"types":"./dist/charts/index.d.ts","default":"./dist/charts/index.js","require":"./dist/charts/index.js"},"./sheets":{"types":"./dist/sheets/index.d.ts","default":"./dist/sheets/index.js","require":"./dist/sheets/index.js"},"./builders":{"types":"./dist/builders/index.d.ts","default":"./dist/builders/index.js","require":"./dist/builders/index.js"},"./knowledge":{"types":"./dist/knowledge/index.d.ts","default":"./dist/knowledge/index.js","require":"./dist/knowledge/index.js"},"./mechanics":{"types":"./dist/mechanics/index.d.ts","default":"./dist/mechanics/index.js","require":"./dist/mechanics/index.js"},"./formatting":{"types":"./dist/formatting/index.d.ts","default":"./dist/formatting/index.js","require":"./dist/formatting/index.js"},"./calculators":{"types":"./dist/calculators/index.d.ts","default":"./dist/calculators/index.js","require":"./dist/calculators/index.js"},"./package.json":"./package.json","./save-decoder":{"types":"./dist/save-decoder/index.d.ts","default":"./dist/save-decoder/index.js","require":"./dist/save-decoder/index.js"},"./contributions":{"types":"./dist/contributions/index.d.ts","default":"./dist/contributions/index.js","require":"./dist/contributions/index.js"},"./knowledge-graph.json":"./dist/knowledge/knowledge-graph.v1.json","./knowledge.schema.json":"./dist/knowledge/knowledge.schema.json"},"gitHead":"d2ced4aae26e395c51b12352bb765eabd1ad52bc","license":"MIT","scripts":{"map":"node scripts/map-the-repo.mjs","mcp":"node mcp/server.mjs","lint":"eslint .","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json && node scripts/copy-assets.mjs && npm run knowledge:emit","clean":"node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"","verify":"node scripts/verify.mjs","//assets":"Local only. These read a directory of the game's artwork and emit a catalogue of its filenames — useful for working against a private copy, and NOT part of what this package ships. Their output is gitignored; see src/assets/index.ts.","bot:demo":"tsx templates/bot-starter.ts","bot:test":"vitest run src/bot","glossary":"npm run build && node scripts/generate-glossary.mjs","lint:fix":"eslint . --fix","lint:map":"node scripts/map-the-repo.mjs --check","docs:seed":"npm run docs:snippets && npm run wiki:build","lint:docs":"node scripts/check-readme-links.mjs","vault:v29":"node scripts/build-vault-v29.mjs --write","wasm:call":"node wasm/run.mjs","docs:check":"node scripts/build-doc-snippets.mjs --check && npm run wiki:check && node scripts/check-readme-links.mjs && node scripts/build-patch-notes.mjs --check","type-check":"tsc -p tsconfig.build.json --noEmit","wasm:build":"node wasm/build.mjs","wasm:check":"node wasm/build.mjs --check","wiki:build":"node scripts/build-wiki.mjs","wiki:check":"node scripts/build-wiki.mjs --check","assets:game":"node scripts/build-game-asset-manifest.mjs","sheets:test":"vitest run src/sheets","test:schema":"vitest run tests/data/schemas.test.ts tests/data/schemas-cover-what-they-claim.test.ts","examples:run":"node scripts/run-examples.mjs","lint:package":"node scripts/audit-package-contents.mjs","wiki:publish":"node scripts/publish-wiki.mjs","docs:snippets":"node scripts/build-doc-snippets.mjs","lint:acronyms":"tsx scripts/check-acronym-expansions.ts","verify:serial":"node scripts/verify.mjs --serial","knowledge:emit":"node scripts/emit-knowledge-graph.mjs","lint:published":"node scripts/audit-published-surface.mjs","prepublishOnly":"npm run clean && npm run build","assets:manifest":"node scripts/build-asset-manifest.mjs","labs:categories":"node scripts/build-lab-categories.mjs","lint:specifiers":"node scripts/check-sdk-specifiers.mjs","lint:conventions":"node scripts/check-conventions.mjs","lint:organization":"node scripts/check-organization.mjs","patch-notes:build":"node scripts/build-patch-notes.mjs","patch-notes:ingest":"node scripts/ingest-patch-notes.mjs","type-check:examples":"tsc -p tsconfig.examples.json","daily-missions:goals":"node scripts/build-daily-mission-goals.mjs","epaths:refresh-reference":"node scripts/refresh-effective-paths-reference.mjs"},"version":"0.11.0","_npmUser":{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"},"homepage":"https://tmrxjd.github.io/TheTowerSDK/","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"_npmVersion":"10.9.3","description":"Game data and save-file reading for The Tower — build your own calculators and tools.","directories":{},"maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"zod":"^4.3.6"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"typesVersions":{"*":{"*":["dist/index.d.ts"],"bot":["dist/bot/index.d.ts"],"data":["dist/data/index.d.ts"],"node":["dist/node/index.d.ts"],"save":["dist/save/index.d.ts"],"wiki":["dist/wiki/index.d.ts"],"assets":["dist/assets/index.d.ts"],"charts":["dist/charts/index.d.ts"],"sheets":["dist/sheets/index.d.ts"],"builders":["dist/builders/index.d.ts"],"knowledge":["dist/knowledge/index.d.ts"],"mechanics":["dist/mechanics/index.d.ts"],"formatting":["dist/formatting/index.d.ts"],"calculators":["dist/calculators/index.d.ts"],"save-decoder":["dist/save-decoder/index.d.ts"],"contributions":["dist/contributions/index.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","xlsx":"^0.18.5","eslint":"^9.18.0","vitest":"^3.0.0","@eslint/js":"^9.18.0","typescript":"~5.8.3","@types/node":"^22.10.0","typescript-eslint":"^8.20.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/thetowersdk_0.11.0_1789367087514_0.41049659798029814"}}},"time":{"created":"2026-08-10T15:39:22.642Z","modified":"2026-09-14T06:24:47.882Z","0.1.0":"2026-08-10T15:39:23.052Z","0.1.1":"2026-08-10T17:26:26.684Z","0.2.0":"2026-08-11T04:13:33.432Z","0.3.0":"2026-08-11T16:36:56.673Z","0.3.1":"2026-08-11T16:45:45.359Z","0.4.0":"2026-08-14T04:57:27.296Z","0.4.1":"2026-08-14T11:49:55.106Z","0.5.0":"2026-08-16T00:13:51.508Z","0.5.1":"2026-08-16T00:17:42.864Z","0.5.2":"2026-08-16T00:26:28.879Z","0.5.3":"2026-08-17T22:29:16.509Z","0.5.4":"2026-08-25T20:32:51.249Z","0.5.5":"2026-08-26T01:31:21.441Z","0.5.6":"2026-08-26T02:59:33.731Z","0.6.0":"2026-08-26T03:34:08.998Z","0.7.0":"2026-08-26T05:18:28.269Z","0.8.0":"2026-08-26T05:52:31.266Z","0.9.0":"2026-08-26T08:02:28.341Z","0.9.1":"2026-08-26T18:47:28.736Z","0.10.1":"2026-09-04T17:23:18.607Z","0.11.0":"2026-09-14T06:24:47.696Z"},"bugs":{"url":"https://github.com/TmRxJD/TheTowerSDK/issues"},"author":{"url":"https://github.com/TmRxJD","name":"TmRxJD"},"license":"MIT","homepage":"https://tmrxjd.github.io/TheTowerSDK/","keywords":["the-tower","thetower","tower-defense","game-data","save-file","calculator","sdk"],"repository":{"url":"git+https://github.com/TmRxJD/TheTowerSDK.git","type":"git"},"description":"Game data and save-file reading for The Tower — build your own calculators and tools.","maintainers":[{"name":"tmrxjd","email":"thetowerruntracker@gmail.com"}],"readme":"# TheTowerSDK\n\nGame data, save-file reading and the game's own formulas for **The Tower**, as typed TypeScript —\nso you can build calculators, planners, spreadsheets, charts and bots without transcribing a single\nnumber by hand.\n\n```bash\nnpm install thetowersdk\n```\n\n- **Game data** — labs, workshop, modules, cards, perks, relics, guardians, bots, ultimate weapons\n  and the vault, with their cost curves and effect values, as typed arrays.\n- **Save reading** — turn a player's `playerInfo.dat` into typed values: what they have researched,\n  what they own, what is equipped, their run history.\n- **Formulas** — enemy scaling, damage, ultimate weapons, drops and workshop stats, as functions you\n  call with a tier, a wave or a level.\n- **Charts** — every level progression is already a plot-ready series, so the shipped catalogs cover\n  [800 chartable series](#charts) without you authoring one.\n\nTypeScript, MIT licensed, one runtime dependency (`zod`).\n\n### 📖 [Documentation, live demos and runnable examples → thetowersdk website](https://tmrxjd.github.io/TheTowerSDK/)\n\n> **Pre-1.0.** The package is published so it can be installed and exercised, not because the API is\n> settled. Exports may be renamed or removed between releases without deprecation. Pin an exact\n> version if you depend on it.\n\n---\n\n## Quick Start\n\n### Use The Game Data\n\n```ts\nimport { LAB_CATALOG } from 'thetowersdk/data'\n\nconst costToMax = (lab) => lab.levels.reduce((sum, level) => sum + level.cost, 0)\n\nconst priciest = LAB_CATALOG\n  .map((lab) => ({ name: lab.name, total: costToMax(lab) }))\n  .sort((a, b) => b.total - a.total)[0]\n```\n\nEvery `cost` is a plain number of coins. There is no scaling factor to apply and no currency field\nto read first — a lab that costs 1.1 quadrillion is `1.1e15`, so you can add two labs together\nwithout checking where either came from.\n\n### Read a Save File\n\n```ts\nimport { readFile } from 'node:fs/promises'\nimport { decodePlayerInfoSaveBytes } from 'thetowersdk/node'\nimport { readLabsFromSaveRoot } from 'thetowersdk/save'\n\nconst { parsedRoot } = decodePlayerInfoSaveBytes(await readFile('playerInfo.dat'))\n\nconst labs = readLabsFromSaveRoot(parsedRoot)\nconsole.log(`${labs.researchedCount} researched, ${labs.maxedCount} maxed`)\n```\n\nDecode once, then extract whatever you need.\n\n---\n\n## Entry Points\n\n| Import | Contains | Browser-safe |\n|---|---|---|\n| `thetowersdk/data` | Game tables — costs, levels, effects, catalogs | Yes |\n| `thetowersdk/save` | `read*FromSaveRoot()` readers and save inspection | Yes |\n| `thetowersdk/node` | The save decoder | Node — [see below](#decoding-in-a-browser) |\n| `thetowersdk/save-decoder` | The same decoder with no Node imports, for the browser | Yes |\n| `thetowersdk/formatting` | Number and duration formatting matching the game | Yes |\n| `thetowersdk/mechanics` | Game formulas — see [below](#formulas) | Yes |\n| `thetowersdk/wiki` | Fandom wikitext → Markdown — see [below](#reading-the-community-wiki) | Yes |\n| `thetowersdk/contributions` | Whose work this package carries, as data | Yes |\n| `thetowersdk/charts` | Curated chart catalog and its data — see [below](#charts) | Yes |\n| `thetowersdk/builders` | Ready-made calculators — see [below](#builders) | Yes |\n| `thetowersdk/calculators` | Every exported formula declared by name — what it reads, what it produces, how to call it | Node |\n| `thetowersdk/bot` | Command registry, and every calculator as a command — see [below](#building-a-bot-on-this) | Yes |\n| `thetowersdk/sheets` | Google Sheets reads and writes — see [below](#google-sheets) | Yes |\n| `thetowersdk/knowledge` | The mechanics oracle, and five years of patch notes — see [below](#patch-notes) | Yes |\n| `thetowersdk/assets` | Path helpers for artwork **you supply** — see [below](#using-your-own-artwork) | Yes |\n\n`import { … } from 'thetowersdk'` re-exports `data`, `save` and `formatting` together. Prefer the\nsubpaths in real projects so your bundler can drop what you don't use.\n\n```ts\nimport { computeWaveBaseHealth, abilityDamage } from 'thetowersdk/mechanics'\nimport { formatDuration } from 'thetowersdk/formatting'\n```\n\n---\n\n## How It Fits Together\n\n```\nplayerInfo.dat ──decodePlayerInfoSaveBytes()──► save root (plain object)\n                                                     │\n                     ┌───────────────────────────────┼──────────────────────────────┐\n                     ▼                               ▼                              ▼\n           readLabsFromSaveRoot()   readModulesFromSaveRoot()     readVaultFromSaveRoot()\n                     │                               │                              │\n                     ▼                               ▼                              ▼\n               typed lab data                 typed module data             typed vault data\n```\n\nThe **save root** is a plain JavaScript object. Extractors read from it and never modify it, so you\ncan run as many as you like over the same root.\n\n---\n\n## Getting a Save File\n\n`playerInfo.dat` is The Tower's save file.\n\n- **Android** — `Android/data/com.TechTreeGames.TheTower/files/playerInfo.dat`\n- **Android emulator** (BlueStacks, LDPlayer, WSA…) — the same path inside the emulated device\n- **macOS** — the native App Store build stores it in its app container under\n  `~/Library/Containers/<bundle-id>/Data/Library/Application Support/…`, with non-sandboxed installs\n  under `~/Library/Application Support/…`\n- **iOS** — inside an encrypted device backup; not practical to read directly\n\nOn Windows the save always comes from an emulator. On a Mac the native build is right there on disk.\n\nCopying that file by hand is the tedious part of building anything save-driven, so\n[**adb-bridge**](https://github.com/TmRxJD/adb-bridge) removes the step. It talks to a connected\nphone or a running emulator over ADB, or reads the macOS container directly, and hands the bytes\nstraight to whatever needs them:\n\n```bash\nnpx adb-bridge\n```\n\n- Installs Google's official platform-tools for you if `adb` isn't already on the machine (Android\n  only — the macOS path needs no tooling).\n- Serves the save to a local page over a WebSocket bound to `127.0.0.1`, so a browser app can read a\n  real account with no upload step and no file picker.\n- Can watch the save and re-send it whenever the game writes, which keeps a view live while you\n  play instead of forcing a manual re-import.\n- Pulls only. It never modifies anything on the device, needs no root, and touches only the games you\n  enable.\n- One install covers multiple games — adding another registers it with the bridge you already have.\n\nFeed the bytes it gives you to `decodePlayerInfoSaveBytes` and everything above applies unchanged.\n\n> Save files are personal data. If your tool uploads them anywhere, tell your users plainly.\n\n### Decoding In a Browser\n\nThe decoder is in `thetowersdk/node` only because it uses `node:zlib`. In a browser, gunzip with\n`DecompressionStream` and call the NRBF reader directly — both are exported and pure:\n\n```ts\nimport { NRBFReader, nrbfToJSON } from 'thetowersdk/node'\n\nasync function decodeInBrowser (file: File): Promise<Record<string, unknown>> {\n  let bytes = new Uint8Array(await file.arrayBuffer())\n  if (bytes[0] === 0x1f && bytes[1] === 0x8b) {\n    const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream('gzip'))\n    bytes = new Uint8Array(await new Response(stream).arrayBuffer())\n  }\n  return nrbfToJSON(NRBFReader.readStream(bytes)) as Record<string, unknown>\n}\n```\n\nEverything in `thetowersdk/save` and `thetowersdk/data` then works on the result unchanged.\n\n---\n\n## Reading a Save\n\n| Function | Returns |\n|---|---|\n| `readLabsFromSaveRoot` | Research levels, what's maxed, the active queue |\n| `readWorkshopFromSaveRoot` | Upgrade levels, enhancements, saved presets |\n| `readModulesFromSaveRoot` | Owned modules, rarities, substats, equipped |\n| `readCardsFromSaveRoot` | Card levels, copies, mastery, equipped slots |\n| `readGuardiansFromSaveRoot` | Guardian levels and upgrades |\n| `readBotsFromSaveRoot` | Bot levels, Bot+ unlocks, synchronicity slots, medals spent |\n| `readUltimateWeaponsFromSaveRoot` | UW levels, unlocks, plus-levels, stones |\n| `readVaultFromSaveRoot` | Vault power tree progress |\n| `readRelicsFromSaveRoot` | Owned relics |\n| `readCollectedThemeNamesFromSaveRoot` | Unlocked themes |\n| `readLifetimeFromSaveRoot` | Lifetime totals |\n| `readDissonanceCycleWavesFromSaveRoot` | Best wave per tier this dissonance cycle |\n| `listImportableBattleRuns` | Run history |\n\nPlus perks, \"killed by\" and per-run battle report fields — see [`src/save/index.ts`](src/save/index.ts).\n\n### Check Before You Read\n\nExtractors return `null` when a save has no data for that feature, rather than throwing, so older\nsaves degrade instead of failing:\n\n```ts\nconst labs = readLabsFromSaveRoot(parsedRoot)\nif (!labs) return\n\nif (labs.warnings.length) {\n  // Values the extractor could not interpret. Usually means the save shape changed.\n  console.warn(labs.warnings)\n}\n```\n\n### Run History And Battle Reports\n\nEvery completed run the game kept is available, with all of its stored fields:\n\n```ts\nimport { listImportableBattleRuns, buildBattleReportStatFields } from 'thetowersdk/save'\n\nconst runs = listImportableBattleRuns(parsedRoot)\n\n// The raw entry — tier, wave, duration, coins, cells, damage dealt and taken,\n// per-source damage breakdowns, what killed you, and everything else the game\n// recorded for that run.\nconsole.log(Object.keys(runs[0]))\n\n// Or the same run flattened into named stat fields.\nconst stats = buildBattleReportStatFields(runs[0])\n```\n\n`listImportableBattleRuns` hands back the decoded entries themselves, not a filtered view, so you\nare not limited to the fields this SDK happens to name. There are also helpers for duration\nformatting, date parsing and deduplication — see [`src/save/index.ts`](src/save/index.ts).\n\n### Reading Any Field In a Save\n\n`parsedRoot` is the decoded file as a plain object, so every field the game stores is yours to read\ndirectly. The named extractors are a convenience layer over that same object — reach past them\nwhenever you want a field they do not name.\n\nValues are loosely typed and arrays arrive in several shapes, so use the readers rather than\nhand-parsing:\n\n```ts\nimport { coerceSaveNumber, readSaveBoolean, readSaveIntList } from 'thetowersdk/save'\n\ncoerceSaveNumber(parsedRoot.someKey) // number | null\nreadSaveBoolean(parsedRoot.someFlag) // boolean\nreadSaveIntList(parsedRoot.someList) // number[]\n```\n\nWorked out a field that isn't covered? A PR adding an extractor is very welcome.\n\n---\n\n## Reading The Community Wiki\n\nThe Tower's wiki is on Fandom, which serves **wikitext** rather than anything you can render:\ntemplates, infoboxes, `[[File:…]]` links and vertical wikitables. This converts it to Markdown.\n\n```ts\nimport { fetchFandomPageAsMarkdown } from 'thetowersdk/wiki'\n\nconst markdown = await fetchFandomPageAsMarkdown('Cards')\n```\n\nOr the halves separately, if you fetch pages your own way — from a cache, a mirror, or a build step:\n\n```ts\nimport { convertFandomWikitextToMarkdown, resolveFandomFileImages } from 'thetowersdk/wiki'\n\nconst markdown = convertFandomWikitextToMarkdown(wikitext, { pageTitle: 'Cards' })\n```\n\nA page that does not exist returns `200 OK` with a `missing` marker rather than a 404, so\n`fetchFandomWikitext` throws on it instead of returning an empty page.\n\n**The conversion ships here; the wiki's content does not.** Wiki text is CC-BY-SA and this package\nis MIT, so fetch what you need and honor the wiki's license in whatever you ship. It is a\nvolunteer-run wiki — cache what you fetch, and space out requests when pulling many pages.\n\n## Formulas\n\n`thetowersdk/mechanics` is the calculation layer:\n\n- **Enemy scaling** — wave/tier base health and damage, enemy type multipliers, level skip,\n  elite spawn chance, wave-info panel values\n- **Damage** — thorns, knockback, multishot and bounce shot, rend armor, projectile damage, crowd\n  control, damage reduction, shockwave, land mines, tower fire and range\n- **Ultimate weapons** — the shared hit and absorb pipeline, plus Chrono Field slow, Poison Swamp\n  ticks and stun, and Inner Land Mines including Charge Mines\n- **Economy** — coin and enemy drop simulation, interest, ROI scaling, workshop cost tables\n- **Workshop stats** — attack, defense and utility stat tables and their build-up\n- **Bots and guardians** — bot hit multipliers, effective range and coverage, Wildfire duration,\n  medal planning and simulation, Bot Bot overlap, cooldowns\n- **Battle conditions** — resistance levels, tournament heat, counter labs\n\n```ts\nimport { computeWaveBaseHealth, abilityDamage, goldenComboBonus } from 'thetowersdk/mechanics'\n```\n\n---\n\n## Builders\n\nReady-made calculators. Each one pairs the maths in `thetowersdk/mechanics` with the things a tool\nneeds around it and never gets for free: a complete set of defaults, a description of every input,\nnormalisation of whatever half-filled state a form is in, and a result that says what it could not\nwork out.\n\n```ts\nimport { labResearchCalculator } from 'thetowersdk/builders'\n\nconst result = labResearchCalculator.compute({ labName: 'Damage', targetLevel: 10 })\nconsole.log(result.totalCoinCost, result.totalHours)\nfor (const note of result.notes) console.warn(note)\n```\n\n`compute` takes a `Partial` on purpose — a form hands you half-filled state constantly, and a\ncalculator that throws or returns `NaN` on that pushes the problem back into the UI.\n\nRender a form without knowing which calculator it is:\n\n```ts\nfor (const field of labResearchCalculator.fields) {\n  // field.kind is 'number' | 'select' | 'boolean' | 'number-list'\n}\n```\n\n| Builder | Answers |\n|---|---|\n| `labResearchCalculator` | Coins and time to take a lab from one level to another |\n| `workshopUpgradeCalculator` | Coins to move a workshop stat between two levels |\n| `moduleCostCalculator` | Shards and coins to level a module, capped by its rarity |\n| `ultimateWeaponCalculator` | Power Stones for an ultimate weapon stat |\n| `uptimeCalculator` | What share of the time an ability is actually running |\n| `guardianCalculator` | Bits for a guardian stat |\n| `botUpgradeCalculator` | Medals for a bot stat |\n| `enemyWaveCalculator` | Health and damage per enemy kind at a tier and wave |\n| `damageReductionCalculator` | What reaches the tower after every mitigation layer |\n| `assistModuleStonesCalculator` | Stones for an assist efficiency slot |\n| `coinsPerKillCalculator` | What one enemy pays, all six bonus sources applied |\n| `thornsCalculator` | Damage returned to an enemy on contact |\n| `dissonanceCalculator` | The multiplier your tier personal bests apply to a stat |\n| `enemyDropsCalculator` | Module drop chances, reroll shards, shatter shards |\n| `innerLandMinesCalculator` | Mine damage, count, cooldown, and what charge time is worth |\n\n`CALCULATOR_BUILDERS` is all of them, and `findCalculatorBuilder(id)` looks one up — enough to\nbuild a calculator picker that needs no per-calculator code at all.\n\nThey wrap `thetowersdk/mechanics` rather than reimplementing it, so a correction to a formula\nreaches every tool without anyone re-deriving it.\n\n### Caps, clamps and level indexing\n\nEvery builder handles these the same way, so a number means the same thing whichever one produced it.\n\n| Rule | How |\n|---|---|\n| **A level is capped by its own catalog** | `clampLevel(value, cap, fallback)`, where `cap` comes from the curve that stat actually uses |\n| **An open-ended quantity is bounded so the result stays finite** | `clampMagnitude(value, fallback)`, bounded by `MAX_INPUT_MAGNITUDE` |\n| **A level buys against the previous row** | `costIndexForLevel(level)` — buying level L reads entry L−1 |\n| **Anything only a formula bounds gets a named constant** | e.g. `MAX_MODEL_WAVE`, where the wave curve stops producing a real number |\n| **Clamping is reported** | the result's `notes` say what was clamped and to what |\n\nCaps come from the data rather than a shared constant because the curves genuinely differ: workshop\nAttack Speed prices 75 levels, Enemy Level Skip 60, Damage 400. Level indexing follows tower-oracle\n`workshop.upgradeTable`: a cost row buys the **next** level, so entry L is what is charged at level L\nand buying level L costs entry L−1.\n\n### Lookups by a name you did not choose\n\nIds, slugs and level indices arrive from saves, from the community sheet, from URLs and from tool\narguments. Lookups keyed by them use own-key access (`ownLookup`) for records and a\nbounds-and-integer check (`atIndex`) for arrays, so an unknown key returns `undefined` rather than\nwhatever sits on `Object.prototype`.\n\nThe reason it is a rule rather than a caution: `record['constructor']` is the `Object` function and\n`record['toString']` is a function, and neither `??` nor `||` nor a truthiness check will reject one.\nThe fallback simply never fires, and a function travels on from a signature that promised a string.\n\nCost ladders are bounded the same way, by the table rather than by the caller: a level past the end\nof a curve is excluded rather than priced at zero.\n\n---\n\n## Charts\n\nCharts are built on [the formulas above](#formulas) and the catalogs — which is the whole reason\nthis section follows that one. You need the numbers before you can plot them.\n\nThere is no chart API here, and that is deliberate: **a level progression already is a series.** The\nlevel is your x-axis and each measured field on it is a line, so generating a chart is a `map`, not\nan integration. A helper would only wrap `Array.map` while forcing an opinion about rendering on you.\n\n### Why This Beats Keeping Images\n\nThe usual way a project ends up with charts is somebody makes a picture. Then the game rebalances,\nor a tier gets added, and now there is a PNG showing last patch's numbers with nothing anywhere to\nflag it. The image cannot tell you it is stale, so it stays wrong until a player notices.\n\nGenerating from the data inverts that. **You never make a new image — you add rows to the array and\nevery chart drawn from it regenerates.** One new lab level, one rebalanced cost, one extra ultimate\nweapon, and every view over that data is correct at once: the cost curve, the comparison chart, the\ntable, the tooltip. Charts stay consistent with each other because they are not separate artifacts\nthat happen to agree — they are the same numbers rendered more than one way.\n\n```ts\nimport { LAB_CATALOG } from 'thetowersdk/data'\n\nconst lab = LAB_CATALOG.find((l) => l.name === 'Attack Speed')\n\nconst costCurve = lab.levels.map((l) => ({ x: l.level, y: l.cost }))\nconst timeCurve = lab.levels.map((l) => ({ x: l.level, y: l.duration }))\n```\n\nHand those points to whatever already draws your charts — Chart.js, D3, Vega, a spreadsheet, an SVG\npath you build yourself. The same operation across a whole catalog gives you a comparison chart:\n\n```ts\nimport { uwStoneChartData } from 'thetowersdk/data'\n\n// One series per ultimate weapon: stone cost to reach each cooldown level.\nconst datasets = Object.values(uwStoneChartData).map((weapon) => {\n  const cooldown = weapon.stats.find((s) => s.name === 'Cooldown')\n  return {\n    label: weapon.name,\n    data: cooldown.levels\n      .filter((l) => typeof l.cost === 'number')\n      .map((l) => ({ x: l.level, y: l.cost })),\n  }\n})\n```\n\n### A Table Is a Chart Too\n\nMost of what players actually want is a table, not a curve — \"what does the next level cost, and\nwhat do I get for it\". Same data, same `map`, different rendering:\n\n```ts\nimport { uwStoneChartData } from 'thetowersdk/data'\nimport { formatNumberForDisplay } from 'thetowersdk/formatting'\n\nconst gt = Object.values(uwStoneChartData).find((w) => w.name === 'Golden Tower')\nconst multiplier = gt.stats.find((s) => s.name === 'Multiplier')\n\nlet running = 0\nfor (const level of multiplier.levels) {\n  if (typeof level.cost === 'number') running += level.cost\n  console.log(level.level, level.value, level.cost, formatNumberForDisplay(running))\n}\n```\n\n```\nGolden Tower — every stat\n\n  Stat             Levels     To max  Final\n  ---------------- ------  ---------  ------------\n  Multiplier           21     8.434K  x21.0\n  Duration             39    14.052K  53s\n  Cooldown             21       4.7K  100s\n  Golden Combo         15     20.07K  0.45%\n```\n\nThat table is emitted, not written. When a stat gains levels or a cost is rebalanced, the table and\nevery chart over the same array update together — which is the point of\n[generating rather than keeping images](#why-this-beats-keeping-images). The same loop emits\nMarkdown, so documentation tables regenerate instead of rotting.\nSee [`examples/05-generate-a-cost-table.ts`](examples/05-generate-a-cost-table.ts).\n\n**How much is chartable.** The shipped catalogs support **800 distinct series** — one per measurable\nfield that varies across a level range:\n\n| Source | Series | What varies |\n|---|---|---|\n| Labs | 450 | Coin cost and research time, per lab |\n| Workshop | 141 | Stat value, cash cost, coin cost |\n| Cards | 62 | Level values and mastery values |\n| Ultimate weapons | 72 | Stat value and stone cost, per stat |\n| Guardians | 36 | Chip stat value and cost |\n| Bots | 25 | Per-stat progressions, including plus variants |\n\nThat is a floor, not a ceiling. It counts only fields that already vary by level, and excludes\ncatalogs with nothing to plot — relics carry a single flat value, module substats are cluster\nmetadata, and vault nodes are identifiers. Anything you derive from [the formulas](#formulas) is\nadditional.\n\n### The Curated Catalog\n\n`thetowersdk/charts` adds a registry of ready-made chart definitions, for when you want\nthe same views rather than your own:\n\n```ts\nimport { SHARED_CHART_REGISTRY, findChartByPathId } from 'thetowersdk/charts'\n```\n\nIt answers *what charts exist and what is in them* — never how they look. Rendering, styling and\nlocalisation stay with your application. `CHART_MECHANIC_LINKS` maps each chart to the entities in\n`thetowersdk/knowledge` it documents, so a mechanic can find its chart and vice versa.\n\nSee [`examples/04-generate-a-chart.ts`](examples/04-generate-a-chart.ts).\n\n---\n\n## Effective Paths\n\n[Effective Paths][ep] is the community spreadsheet that works out the cheapest order to buy things\nin — which lab, workshop stat or module to put your next coins into for the most effect. Its authors\ntake the numbers from the developers, which is why the SDK already checks its own tables against it:\nsee [`fixtures/data/README.md`](fixtures/data/README.md).\n\nThe solver is ported. Four models, each with the sheet's own paths:\n\n| Model | Planner | Paths |\n|---|---|---|\n| eHP | `planEffectiveHealthPath` | `lab-time` · `lab-coins` · `stone` · `coin` |\n| eRegen | `planEffectiveRegenPath` | `lab-time` · `lab-coins` |\n| eDamage | `planEffectiveDamagePath` | `lab-time` · `lab-coins` · `stone` · `coin` · `keys` |\n| eEcon | `planEffectiveEconomyPath` | `time` · `coin` · `stone` |\n| eEcon Discount | `planEffectiveEconomyDiscountPath` | its own, ranking coins **saved** |\n\nThe lab path appears twice everywhere because the sheet prices the same candidates two ways — in\nresearch days or in coins — and which one binds depends on the player.\n\n```ts\nimport {\n  planEffectiveDamagePath,\n  ZERO_EFFECTIVE_DAMAGE_LEVELS,\n  zeroEffectiveDamageConfig,\n} from 'thetowersdk/mechanics'\n\nconst plan = planEffectiveDamagePath({\n  config: zeroEffectiveDamageConfig(),   // the account: what is unlocked, owned, equipped\n  levels: ZERO_EFFECTIVE_DAMAGE_LEVELS,  // what is bought, per candidate\n  variant: 'lab-time',\n  steps: 25,\n})\n\nplan.steps      // what to buy, in order, with cost, gain and ROI\nplan.excluded   // what it did not offer, and why\nplan.issues     // why it could not plan at all — empty on every plan that ran\n```\n\n### Planning For a Real Player\n\nA plan takes two things, and they are different:\n\n- a **config** — the account around the numbers: which weapons are unlocked, which cards are owned,\n  what the modules are, which perks are taken;\n- **levels** — how far each candidate is already bought.\n\n**The SDK does not build either from a save.** It supplies the model and the zero records; mapping a\nplayer onto them is yours to write, because where a player's data comes from — a save file, your own\ndatabase, a form — is your decision, not this package's.\n\nThe candidate lists are what make that mapping short. Each entry carries the `id` a plan reports\nback, the `band` and `key` its level lives under, and the `sheetName` the community sheet uses:\n\n```ts\nimport {\n  EFFECTIVE_ECONOMY_UPGRADES,\n  ZERO_EFFECTIVE_ECONOMY_LEVELS,\n  planEffectiveEconomyPath,\n  zeroEffectiveEconomyConfig,\n} from 'thetowersdk/mechanics'\n\n// However you got it — a save, a database, a form.\nconst myLabLevels = { 'Coins / Kill Bonus': 40, 'Golden Tower Bonus': 12 }\n\n// Start from a complete zero record and fill it in. Do not build one by hand:\n// a missing key reads as `undefined`, which becomes a NaN the planner refuses.\nconst levels = structuredClone(ZERO_EFFECTIVE_ECONOMY_LEVELS)\nfor (const candidate of EFFECTIVE_ECONOMY_UPGRADES) {\n  const known = myLabLevels[candidate.sheetName]\n  if (known !== undefined) levels[candidate.band][candidate.key] = known\n}\n\nconst plan = planEffectiveEconomyPath({\n  config: zeroEffectiveEconomyConfig(),\n  levels,\n  variant: 'time',\n  steps: 5,\n})\n\nplan.steps    // → Coins / Kill Bonus L41, L42, L43 … — it continues from 40\nplan.excluded // → Golden Tower Bonus — \"the weapon is not unlocked\"\n```\n\nThat exclusion is the config talking, not the levels: a zero config owns no weapons, so the whole\nGolden Tower band is out. Fill the config in the same way — start from `zeroEffectiveEconomyConfig()`\nand set what you know — and those candidates appear.\n\nReading a save is a separate step, and the extractors are in\n[`thetowersdk/save`](#reading-a-save): `readLabsFromSaveRoot`, `readWorkshopFromSaveRoot`,\n`readModulesFromSaveRoot` and the rest give you the numbers to map from.\n\n### An Empty Plan Always Says Why\n\nThe four families — damage, eHP, economy and regen — check their levels before planning and refuse\nrather than compute against a record they cannot use. When that happens `steps` is empty and\n`issues` names the offending key and what was wrong with it: a `NaN`, a missing entry, a level\nstored as text.\n\n```ts\nif (plan.issues.length > 0) {\n  // Not \"this player has nothing worth buying\" — \"these levels are unusable\".\n  console.error(plan.issues) // [{ path: 'time.coinsKillBonus', message: '…' }]\n}\n```\n\nA `NaN` level makes every gain `NaN`; every candidate then compares false against every other and\nthe greedy loop returns an empty path, which is indistinguishable from a fully-upgraded account.\nLevels are validated once per plan rather than inside the evaluation loop, which runs many times\nover inputs that do not change.\n\nLevels are held to **completeness and finiteness, not magnitude**. Negative and fractional levels\nare accepted, because the source model contains them.\n\n### `excluded` is half the answer\n\nEvery planner reports the candidates it passed over, each with a reason: `already at its cap of 99`,\n`the weapon is not unlocked`, `priced at 0 for level 12, which is not a cost`. **A candidate is\nplanned, or it is explained — never neither.**\n\nA path that stops after one step usually means every other candidate is at its cap. Read\n`excluded` before treating a short path as an error.\n\nPassing a variant a planner does not publish throws, naming the ones it does. Note that `lab` is a\ndamage *band* while `lab-time` is a *variant*.\n\n### Discount Is a Different Quantity\n\n`planEffectiveEconomyDiscountPath` ranks coins **saved**, not coins earned, so it is not comparable\nto the others and has its own entry point. `planEffectiveEconomyPath` refuses `discount` and says so\nrather than returning a table of zeroes.\n\n### Checking It Against The Sheet\n\nThe port cites the cell behind every rule it implements — `eEcon!E6`, `eDamage Coins!EZ2` — and those\ncitations are enumerated by a test that checks the tab exists. If you are changing a formula, read\nthe cell first — and know that the spreadsheet API will mislead you about what is in one: a spilled\nrange reads as *empty* while `COUNTA` sees a hundred rows of it.\n\nThe MCP server's `plan_effective_path` tool runs any of this without a scratch script — see\n[`mcp/README.md`](mcp/README.md).\n\n[`docs/EFFECTIVE_PATHS.md`](docs/EFFECTIVE_PATHS.md) is the longer version: how a config differs from\nlevels, why the damage and economy levels are banded, and what a step guarantees.\n\n[ep]: https://docs.google.com/spreadsheets/d/1YwZtKP6B4WYhRba5T6APJ1YxKNdfnIGQnprgnxmO7zc/edit\n\n---\n\n### Reading a Player's IDS Master\n\nMost Effective Paths users keep an **IDS Master** behind their workbook — one `_IDS` tab that is\nnothing but inputs: their labs, workshop, cards, bots, guardians, weapons, modules and vault. It is\nthe same account data a save carries, in a form a player maintains by hand.\n\nThe readers turn that block into typed rows:\n\n```ts\nimport { readIdsLabs, readIdsWorkshop, readIdsCards } from 'thetowersdk/save'\n\nconst labs = readIdsLabs(grid)          // grid: the `_IDS` range as rows of cells\nlabs.rows                               // [{ name, level, target, max, saveIndex }, …]\nlabs.unmatchedNames                     // names the catalog does not know\nlabs.warnings                           // cells that would not read as levels\n```\n\nEvery reader returns the same shape — `rows`, `unmatchedNames`, `warnings`. Blocks are located by\ntheir heading rather than by column, so a reader works across IDS versions that lay the `_IDS` tab\nout differently. A row's `saveIndex` is its position in the matching save array, so rows join to\nsave data by index rather than by name.\n\n**These give you rows, not a player.** As with the planners above, mapping them onto your own\nstores, database or forms is yours to write.\n\n**A level is a plain number or a plain digit string.** Anything else — `\"6,000\"`, `\"1e3\"`, `\"0x10\"`,\na negative, an empty cell — reads as `null`, meaning the cell says nothing about that level. Names\nare matched case- and spacing-insensitively, and a name the catalog does not know is reported in\n`unmatchedNames` rather than dropped.\n\n## Examples\n\nRunnable, in [`examples/`](examples):\n\n```bash\nnpx tsx examples/01-browse-game-data.ts                        # the tables, no save needed\nnpx tsx examples/02-read-a-save-file.ts ~/playerInfo.dat       # what one player has\nnpx tsx examples/03-plan-upgrades-from-a-save.ts ~/playerInfo.dat  # a tool: what to buy next\nnpx tsx examples/04-generate-a-chart.ts                        # catalogs -> plot-ready series\nnpx tsx examples/05-generate-a-cost-table.ts \"Golden Tower\"    # the same data as a cost table\nnpx tsx examples/06-read-the-community-wiki.ts \"Golden Tower\"  # wiki page -> Markdown\nnpx tsx examples/07-format-like-the-game.ts                    # numbers in and out, both ways\nnpx tsx examples/08-build-a-calculator.ts uw.stones            # a whole calculator, rendered generically\nnpx tsx examples/09-build-a-bot.ts                             # every calculator as a bot command\nnpx tsx examples/10-read-a-sheet.ts                            # a sheet read, and the two ways it lies\nnpx tsx examples/11-build-a-knowledge-base.ts                  # chunks derived from the catalogs\nnpx tsx examples/12-show-module-and-card-art.ts                # which image belongs to which module\n```\n\nThe third is the one to read if you are building something: it goes save → extractors → planner,\nmaps a player onto the model, and reports what it could not map rather than passing a silent zero\ninto the plan. They are type-checked against the package on every `npm run verify`.\n\n---\n\n## Templates\n\nStarting points to copy into your own project, rather than scripts to read. Each is complete,\ntype-checked against the package source, and commented where the shape of the SDK is easy to\nget wrong.\n\n| File | For | Runtime |\n|---|---|---|\n| [`save-cli.ts`](templates/save-cli.ts) | A command-line tool that reads a player's save | Node |\n| [`browser-widget.ts`](templates/browser-widget.ts) | A UI module with no Node built-ins anywhere | Browser |\n| [`calculator.ts`](templates/calculator.ts) | A calculator: typed input, pure compute, separate formatting | Either |\n| [`bot-starter.ts`](templates/bot-starter.ts) | A complete bot: every calculator, a hand-written command, a CLI | Node |\n| [`sheets-client.ts`](templates/sheets-client.ts) | The googleapis adapter for `thetowersdk/sheets` | Node |\n\n### The three things they encode\n\n**Decode once.** `decodePlayerInfoSaveBytes` is the only step that needs Node. The save root it\nreturns is a plain object that extractors read and never mutate, so run as many as you like over\none root.\n\n**Only `thetowersdk/node` needs Node.** `data`, `save`, `formatting`, `mechanics`, `wiki`,\n`charts` and `knowledge` are all browser-safe. A widget that never opens a save file\nnever imports the decoder — that is why `browser-widget.ts` exists as a separate file.\n\n**Say what you could not do.** Extractors return `null` for features a save predates and carry a\n`warnings` array; `calculator.ts` returns a `notes` array for the same reason. A planner that\nsilently returns `0` for an unknown lab is worse than one that says it does not know the lab.\n\n### Two traps these avoid\n\n- **Durations are text, and not all one shape.** Most catalog rows are `\"HH:MM:SS\"` with hours\n  running past 24 (`\"500:00:00\"`), but some are `\"0s\"`. Use `parseDurationToHours`; splitting on\n  `\":\"` yourself returns `NaN` on the `\"0s\"` rows and poisons the whole sum.\n- **Format with the game's ladder.** If your tool prints `1.4e21` where the game prints `1.4s`,\n  a player cannot check your answer against their screen — and that is the only way they can\n  trust it. `formatLargeNumber` and `parseResource` round-trip.\n\n---\n\n## Building a Bot On This\n\nA Tower bot answers the same few hundred questions from data that does not change between releases,\nso most of it is already written:\n\n```ts\nimport { calculatorCommands, createTowerBot } from 'thetowersdk/bot'\n\nconst bot = createTowerBot({ commands: calculatorCommands() })\nconst reply = await bot.run('lab-research', { args: { labName: 'Damage', targetLevel: 10 } })\n```\n\n`calculatorCommands()` turns every builder into a command — options, coercion, formatting and\n`notes` — because each builder already describes its own inputs. A builder added to the SDK later\nappears in your bot with no edit. The registry handles dispatch, argument coercion, memoising and\nerrors; `bot.run` never throws, so a gateway handler cannot leave someone watching a spinner until\nthe interaction expires.\n\nNothing in it knows what a gateway is, so the same commands run under discord.js, on a CLI, and at\nfull speed in a test.\n\n```bash\nnpm run bot:demo                                            # list the commands\nnpm run bot:demo -- lab-research labName=Damage targetLevel=10\nnpm run bot:test\n```\n\n[`templates/bot-starter.ts`](templates/bot-starter.ts) is a complete bot with a hand-written command\nand a CLI. [`docs/BUILDING_A_BOT.md`](docs/BUILDING_A_BOT.md) covers performance and the Discord\nadapter.\n\n### Rules that came out of running three of them\n\nThree Discord bots are built on this package and run in production. The\nrules below came out of getting that wrong first, and almost none of them are Discord-specific —\nthey apply to Slack, Telegram, Matrix, or anything else built around callbacks with opaque ids.\n\n**Treat the bot as a first-class client, not a bolt-on.** Domain logic — parsing, normalization,\ncost math, run shapes — belongs in a shared package that both the bot and your UI import. Only\nplatform glue (embeds, components, modals, collectors) lives in the bot repo. The failure this\nprevents is subtle and expensive: a bot that reimplements a calculation drifts from the website, and\nnow the same question has two answers depending on where a player asks it. That is the whole reason\nthis SDK is framework-agnostic.\n\n**One router.** Every interaction dispatches through a single entry point, with persistent handlers\nregistered against it. Scattered per-feature listeners are how you get two handlers responding to\none click, and the second one erroring because the first already replied.\n\n**Component ids get exactly one owner.** Build *and* parse them in one module, and let handlers\nconsume parsed values only. A custom id is a wire format — it is serialized, handed to a remote\nclient, and handed back later, possibly after your process restarted. Once a handler does\n`split(':')` inline, the format is defined in as many places as it is read, and adding a field\nbreaks callers nobody remembered. Registry lookup should use exact or longest-prefix matching for\nthe same reason, never `slice(prefix.length)` at each call site.\n\n**Interactions are owned.** Wait on a modal by filtering on the component id **and** the initiating\nuser. Without both, one user's click resolves another user's pending wait — a bug that never appears\nin single-user testing and appears constantly in a busy channel.\n\n**Guard tokens before touching session state.** Anything carrying a session token must check it is\npresent and unexpired first. Callbacks arrive late, arrive twice, and arrive after a redeploy;\ntreating an expired token as a valid one corrupts state rather than erroring.\n\n**Support every component kind from the start.** Buttons, all select-menu variants, and modals —\neven when the current feature only uses one. Retrofitting a router that assumed buttons is worse\nthan writing it general, and it is a small amount of extra work up front.\n\n**Stay quiet when the interaction is not yours.** An unregistered submission that belongs to a\ncommand-local ownership flow should return silently, not log an error or reply. Otherwise normal\noperation fills your logs with noise and hides the real failures.\n\n**Keep the probe scripts.** Diagnostic tooling — \"what does this record actually look like in the\ndatabase\" — belongs in a checked-in `scripts/` directory, not in a throwaway file. You will need it\nagain, and next time it will be during an incident.\n\n---\n\n## Google Sheets\n\nCommunity workbooks hold things no save file does. `thetowersdk/sheets` reads and writes them, and\nhandles the parts that mislead:\n\n```ts\nimport { TowerSheets } from 'thetowersdk/sheets'\n\nconst sheets = new TowerSheets({ transport, spreadsheetId, protectSpreadsheets: [COMMUNITY] })\nconst grid = await sheets.readGrid('Costs!A1:C50')     // padded to the shape you asked for\nconst formulas = await sheets.readFormulas('Derived!A1:A200')\nif (formulas.likelySpilled) { /* only the anchor carries a formula */ }\n```\n\nTwo reads look like data and are not. A **spilled** cell has no formula of its own, so a formula\nread of an `ARRAYFORMULA` range comes back empty and reads as \"plain typed values\" — the opposite of\nthe truth. And the API **truncates** trailing empties, so a blank cell and an absent column arrive as\nthe same `undefined`. Both are reported rather than smoothed over.\n\nWrites to a spreadsheet you name as protected throw rather than skipping, because a silent refusal\nhas your tool report success while the sheet never changed.\n\nThere is no `googleapis` dependency: the package describes the two calls it needs and you supply\nthem, which keeps it browser-safe and leaves your auth library your choice.\n[`templates/sheets-client.ts`](templates/sheets-client.ts) is the adapter, and\n[`docs/GOOGLE_SHEETS.md`](docs/GOOGLE_SHEETS.md) walks through registering a service account and\nsharing a sheet with it.\n\n---\n\n## Desktop and Mobile\n\nThe SDK is plain TypeScript with one runtime dependency (zod, for schema validation) and no DOM\nassumptions, so it runs unchanged\nin an Electron renderer, an Electron main process, and a Capacitor WebView. `thetowersdk/node` is the\n**only** entry point that needs Node — which is the boundary you want, since the decoder belongs on\nthe side that is allowed to read files.\n\nPin the toolchain first, with the hash corepack verifies against:\n\n```json\n{ \"packageManager\": \"pnpm@10.8.1+sha512.c50088ba…\", \"engines\": { \"node\": \">=22 <23\" } }\n```\n\n[`docs/DESKTOP_AND_MOBILE.md`](docs/DESKTOP_AND_MOBILE.md) covers the main/renderer split, the IPC\nboundary, running the bridge in-process, and what does and does not work on mobile.\n\n---\n\n## Optional Add-ons\n\nTwo packages sit alongside this one. Neither is a dependency and neither is bundled — install one\nwhen you want what it does, and nothing here changes if you never do.\n\n| Package | What it adds | Install |\n|---|---|---|\n| [`towerai`](https://www.npmjs.com/package/towerai) | A knowledge base and an answering layer over this SDK's data | `npm install towerai` |\n| [`adb-bridge`](https://www.npmjs.com/package/adb-bridge) | Pulls a save off an Android device to a local page | `npx adb-bridge` |\n\n**TowerAI** answers questions; this package supplies the numbers it answers with. Build a knowledge\nbase by deriving prose from the catalogs rather than typing it, so a game update moves the corpus\ninstead of silently invalidating it:\n\n```ts\nimport { buildTrackerAiCanonicalKbChunks, validateCanonicalKbArray } from 'towerai/kb'\nimport { LAB_CATALOG } from 'thetowersdk/data'\n\nconst mine = LAB_CATALOG.map(lab => ({\n  chunk_id: `mine_lab_${lab.name}`,\n  disambiguation: 'How many levels this lab has — not what order to research it in.',\n  content: `The ${lab.name} lab has ${lab.levels?.length ?? 0} levels.`,\n  // …\n}))\n\nvalidateCanonicalKbArray([...buildTrackerAiCanonicalKbChunks(), ...mine])\n```\n\n**adb-bridge** hands you save bytes over a WebSocket on `127.0.0.1:43781`; this package reads them.\nGate on the bridge's reported **protocol**, never its release number — the package was renamed and\nrestarted at `0.x`, so a version comparison reports a working bridge as too old.\n\n[`docs/OPTIONAL_ADD_ONS.md`](docs/OPTIONAL_ADD_ONS.md) covers both in full.\n\n---\n\n## Using It With An AI Agent\n\nThere's an MCP server in [`mcp/`](mcp/README.md). Point your agent at it and it can read the real\nAPI and the real game data instead of guessing at both.\n\n```bash\nclaude mcp add thetowersdk -- node ./node_modules/thetowersdk/mcp/server.mjs\n```\n\nAny MCP client works — it speaks stdio.\n\nOr by config file:\n\n```json\n{\n  \"mcpServers\": {\n    \"thetowersdk\": {\n      \"command\": \"node\",\n      \"args\": [\"./node_modules/thetowersdk/mcp/server.mjs\"]\n    }\n  }\n}\n```\n\n| Tool | Ask it for |\n|---|---|\n| `list_exports` · `get_export` | What exists, and one table previewed rather than dumped |\n| `describe_schema` | A table's declared shape, not one guessed from a sample row |\n| `decode_save` · `run_extractor` | What is in a `playerInfo.dat`, and one reader's output |\n| `define_term` | What an acronym means, and whether it is ambiguous |\n| `calc_list` · `calc_describe` · `calc_run` · `calc_chart` · `calc_graph` | Every calculator: find it, read its inputs, run it, chart it, see what feeds it |\n| `plan_effective_path` | An Effective Path, with the candidates it left out and why |\n| `wiki_search` · `wiki_page` | How a mechanic actually behaves, from the community wiki |\n| `sdk_sandbox_run` | A scratchpad over what the package ships — list, read an export, run a calculator, format, decode |\n| `begin_mechanic_task` · `record_mechanic_note` · `record_work_status` · `compliance_contract` | A mechanics compliance session, with notes and a ledger written to `docs/mechanics-map/` |\n\nThe graph, trust and debug tools (`sdk_graph_*`, `trust_*`, `sdk_debug_*` and a few others) run\nscripts from the repository this package is developed in. An installed server leaves them out of\n`tools/list` rather than advertising tools that cannot run.\n\n### The Tower Oracle\n\nA knowledge graph of game mechanics, shipped with the package as its own MCP module. Nodes are\nmechanics, edges are the relationships between them, and every claim carries its source and whether\nanything here has verified it. It answers how a mechanic behaves, what it interacts with, and the\nspecific ways it has been misread before.\n\n| Tool | Ask it for |\n|---|---|\n| `oracle_traps` | **Call this first.** Every known way this mechanic has been got wrong |\n| `oracle_expand` | What an acronym means, from a closed set — `GT+`, `CF`, `DW`, `BH` |\n| `oracle_brief` | A short orientation on a mechanic before you model it |\n| `oracle_get` · `oracle_search` | One node in full, or find the node by phrasing |\n| `oracle_map` | How a mechanic connects to the rest of the game |\n| `oracle_footguns` | Cross-cutting mistakes not tied to one mechanic |\n| `oracle_coverage` | How well a compartment is actually covered |\n| `oracle_contradictions` | Claims that disagree, ranked by source authority |\n\nThree properties shape how you read its answers:\n\n- **`claimType`** marks every claim `objective` or `sentiment`, keeping measured values and community\n  opinion distinguishable.\n- **`oracle_coverage`** reports how well a compartment is covered, so an empty `oracle_traps` result\n  can be read as *unexercised* or as *clean* rather than guessed at.\n- **`oracle_contradictions`** surfaces claims that disagree, ranked by source authority\n  (`game` › `save` › `code` › `user` › `sheet` › `wiki` › `external-repo`), and returns both values.\n\n`oracle_expand` resolves acronyms from a closed set, so an unknown shorthand returns as unknown.\n\n### Two tools worth knowing\n\n**`plan_effective_path`** runs any family and variant without a scratch script — `{ family:\n\"economy\", variant: \"time\", steps: 5 }`. It plans from a zero config, so read it for *structure*:\nwhich candidates a variant offers, and why the rest are excluded. Pass a wrong variant and it names\nthe ones that exist rather than returning an empty result.\n\n**`wiki_page`** is the one to reach for before describing game behavior. This package supplies\nvalues, not semantics — a table says a number changes, not what it means or what it interacts with.\n`wiki_search { query }` finds the title, `wiki_page { title, section? }` reads it. Pages are cached\nafter first read; set `TOWER_WIKI_DIR` to a directory of `slug.md` files to serve them offline, and\nevery response says whether it came from `local`, `cache` or `fandom`.\n\nAgent instructions live in [AGENTS.md](AGENTS.md), which ships with the package; `CLAUDE.md` and\n`.github/copilot-instructions.md` point at the same file.\n\n---\n\n## Using Your Own Artwork\n\nThis package ships **no artwork and no index of it**. The art is TechTree Games’, and it is not\nthis package’s to redistribute.\n\nWhat ships is the naming rule, so a tool can find a file in a directory of artwork **you supply**.\n\n```ts\nimport { gameAssetPath, gameAssetPaths, towerAssetUrl, assetSlug } from 'thetowersdk/assets'\n\ngameAssetPath('Amplifying Strike', { domain: 'modules' })\n// 'modules/amplifying-strike-md.webp'\n\ngameAssetPaths('Om Chip', { domain: 'modules' })\n// { sm: 'modules/om-chip-sm.webp', md: '…-md.webp', lg: '…-lg.webp' }\n\ntowerAssetUrl(gameAssetPath('Om Chip', { domain: 'modules' }), '/art')\n// '/art/modules/om-chip-md.webp'\n```\n\n### Where to put the files\n\n```\n<your art root>/\n  modules/amplifying-strike-sm.webp\n  modules/amplifying-strike-md.webp\n  modules/amplifying-strike-lg.webp\n  cards/…   relics/…   enemies/…   guardians/…   perks/…\n```\n\nOne directory per domain, one file per size, named `<slug>-<size>.<ext>`. The slug is the name\nlowercased with runs of non-alphanumerics collapsed to a single hyphen — `Om Chip` becomes\n`om-chip`, `Damage / Meter` becomes `damage-meter`. `assetSlug()` applies exactly that rule, so\nname your files with it rather than reimplementing it: two implementations of one convention is\nhow a name stops matching its file.\n\n`ASSET_DOMAINS` lists the directory names, `ASSET_SIZES` the sizes. Use whichever subset you have\n— nothing requires all three sizes, and `extension` overrides `webp` if your files are `png`.\n\n### It builds paths, it does not check them\n\nThese functions never touch the disk, because the disk is yours. A path pointing at a file you do\nnot have renders as a gap rather than throwing, so verify your own directory once at startup\ninstead of trusting a returned string. `null` comes back only when a name slugifies to nothing,\nwhich stops `modules/-md.webp` from ever looking like a real path.\n\n---\n\n## Names And Acronyms\n\nThe game and the community use a lot of shorthand, and plenty of it collides. `SL` is Stellar Lift\nto one player and Spotlight to another; `GC` is Galaxy Compressor or a glass-cannon run. The SDK\nships a glossary of 499 terms so you do not have to guess:\n\n```ts\nimport { lookupGlossary, expandAcronym, listAmbiguousGlossaryTerms } from 'thetowersdk/data'\n\nexpandAcronym('CF')      // 'Chrono Field'\nexpandAcronym('SL')      // null — it is ambiguous, so there is no single answer\n\nlookupGlossary('CF')[0]  // { term, kind, domain: 'ultimate-weapon', expansion, definition }\nlookupGlossary('SL')     // Stellar Lift (module) and Spotlight (ultimate weapon)\nlistAmbiguousGlossaryTerms()   // 27 terms that resolve to more than one thing\n```\n\nEach entry carries a `domain`, which is usually enough to pick the one you meant. Usually, not\nalways: `SR` returns both Shrink Ray and Solar Reflector and *both* are modules, so for the terms in\n`listAmbiguousGlossaryTerms()` you need the `expansion` rather than the domain.\n\nThe glossary covers what the game calls things, plus a short list of run concepts. Community\nshorthand outside both — critical factor for `CF` — is not in it, so `expandAcronym` gives you the\ngame's meaning and nothing else.\n\nNames in it are generated from the same catalogs the SDK ships, and every acronym is checked against\nthose names, so a term cannot appear unless it is real.\n\n## Patch Notes\n\nFive years of the developers' own announcements, queryable. The catalogs say what a number **is**\ntoday; this says **when it became that**, and what was said about it at the time.\n\n```ts\nimport {\n  whenIntroduced,\n  searchPatchNotes,\n  patchNotesForVersion,\n  patchNotesBetween,\n  recentPatchNotes,\n  PATCH_NOTES_SOURCE,\n} from 'thetowersdk/knowledge'\n\nwhenIntroduced('shockwave')\n// { postedAt: '2021-07-15…', version: '0.1.29', title: 'v0.1.29 is out for everyone now…' }\n\nsearchPatchNotes('guardian')          // every note mentioning it, newest first\npatchNotesForVersion('26.1.2')        // or 'v26.1.2' — both work\npatchNotesBetween('2024-01-01', '2024-12-31')\nrecentPatchNotes(5)                   // what changed lately\n\nPATCH_NOTES_SOURCE\n// { notes: 232, withVersion: 177, earliest: '2021-07-15…', latest: '2026-08-25…', … }\n```\n\nEvery note carries the message id it came from, so a claim is traceable to the post rather than\nto this package.\n\n### What it will not tell you\n\n**The archive begins on 2021-07-15**, the oldest post in the channel. `whenIntroduced` returning\n`null` means *not in this archive*, not *this never existed*.\n\n**A note mentioning a mechanic is not evidence the mechanic changed.** `searchPatchNotes` is a\ntext search over announcements: it finds leads, and the note still has to be read.\n\n**`version` is `null` on 55 of the 232 notes**, because those notes state no version. It is not\ninferred from the notes around it — a wrong version attached to a real change reads as fact.\n\n### Keeping it current\n\n```bash\nnpm run patch-notes:ingest   # read-only, resumable; needs a bot in the server\nnpm run patch-notes:build    # regenerate the shipped dataset\n```\n\nBoth refuse rather than write something wrong. The ingest stops if the messages come back empty —\nmost notes are *forwarded*, and a forward carries its text in `message_snapshots`, not in\n`content`. The build stops if more than half the notes land on one day, which is what dating by\nthe forward instead of the original looks like, and it stops if a version falls outside the range\nthe game has ever used.\n\n`thetowersdk/knowledge` also carries a `patch-notes` compartment describing those traps, so an\nagent asking *when did this change* meets the dating rule before it reaches a date.\n\n---\n\n## Accuracy\n\nData tables are exact values transcribed from the game. Where a table was taken from a particular\nbuild, its docblock names that version.\n\nThe formulas under `mechanics/` are not all exact. Some reproduce the game's own code; others are\nfitted to observed in-game behavior and are close but not exact, particularly at very high waves\nand tiers. Don't rely on one to match the game to the last digit unless its docblock says it does.\n\nWave scaling in particular builds on earlier community work — see [Credits](#credits).\n\n## Versioning\n\nBelow 1.0 the version is a build marker, not a compatibility promise: any release may rename or\nremove an export, and data values may change with the game. Pin an exact version.\n\nFrom 1.0:\n\n- **Patch** — fixes, new extractors, additive data.\n- **Minor** — data updated for a new game version; existing values may change.\n- **Major** — breaking API changes.\n\nThe entry points listed above are the whole public API; there is no path past them.\n\n## Where the Docs Live\n\n| Surface | What it is | Edit it? |\n|---|---|---|\n| [thetowersdk website](https://tmrxjd.github.io/TheTowerSDK/) | Browsable docs, live demos, runnable examples | Generated |\n| This repo | The source of truth: this README, [`docs/`](docs), [`examples/`](examples), [`templates/`](templates) | **Yes** |\n| [GitHub wiki](https://github.com/TmRxJD/TheTowerSDK/wiki) | The same content, page-per-section, for reading on GitHub | **No — generated** |\n\nThree surfaces, one source. Everything downstream is rendered from the files in this repository by\none command, so there is nothing to remember to update:\n\n```bash\nnpm run docs:seed    # regenerate the wiki pages and the shared code snippets\nnpm run docs:check   # fail if any of them are stale (runs in `npm run verify`)\n```\n\nThe wiki is rendered by [`scripts/build-wiki.mjs`](scripts/build-wiki.mjs). A GitHub wiki is a\nseparate repository that no pull request reviews, so hand-editing it would create a third docs\nsurface that silently drifts — every page carries a banner saying so, and an edit made there is\noverwritten on the next push.\n\nCode samples shared with the website come out of\n[`scripts/build-doc-snippets.mjs`](scripts/build-doc-snippets.mjs), which extracts marked regions\nfrom files that `npm run examples:run` **executes**. A hand-copied sample is correct on the day it\nis pasted and silently wrong afterwards; an extracted one fails in CI before it can reach a page.\n\n```ts\n// >>> snippet: bot-in-one-line\nconst bot = createTowerBot({ commands: calculatorCommands() })\n// <<< snippet\n```\n\nThe link checker is strict on purpose: a relative link that escapes the package root resolves in the\nauthoring monorepo but 404s in the published repo, and it has found live ones.\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). Especially useful: extractors for save fields not yet\ncovered, data corrections (with a save file or screenshot showing the real value), and examples of\ntools you've built.\n\n## Credits\n\n### The Effective Paths team\n\nEvery `effectivePaths*` export in this package is a **port, not original work**. The maths is the\nEffective Paths team's; this package only translates their\n[spreadsheet](https://docs.google.com/spreadsheets/d/1YwZtKP6B4WYhRba5T6APJ1YxKNdfnIGQnprgnxmO7zc)\ninto TypeScript so other tools can reuse it. A large amount of the reference data here — the cost,\nmastery and substat tables especially — was compiled with the help of that work.\n\n- **Mattew** (`matteweon` on Discord) — IDS Master; the Effective Paths spreadsheets for The Tower.\n- **QuietFanta** — eEcon Squirrel; the eHP Workshop+ sheet and the Workshop+ additions.\n- **Bisse** — maintainer; also contributed the save-format reference this package's decoder is\n  checked against.\n- **Shiriru**, **Gladiator**, **Meringue** — maintainers and helpers.\n- And everyone the sheet lists as a contributor — the full roster ships in code as\n  `EFFECTIVE_PATHS_CONTRIBUTORS`, so a tool built on this can render the real list rather than a\n  copy that drifts.\n\n> **Support the Effective Paths team.** Enter creator code **`SHEETLORD`** at checkout in the\n> [The Tower webstore](https://store.techtreegames.com/thetower/). It costs you nothing extra and\n> supports the people who actually derived these formulas. If you ship anything built on the\n> `effectivePaths*` exports, please pass this along — `EFFECTIVE_PATHS_ATTRIBUTION_WITH_SUPPORT`\n> is a ready-made line for a footer or about box.\n\n```ts\nimport {\n  EFFECTIVE_PATHS_ATTRIBUTION_WITH_SUPPORT,\n  EFFECTIVE_PATHS_AUTHORS,\n  EFFECTIVE_PATHS_CONTRIBUTORS,\n  EFFECTIVE_PATHS_MAINTAINERS,\n  EFFECTIVE_PATHS_SUPPORT,\n} from 'thetowersdk/mechanics'\n\nconsole.log(EFFECTIVE_PATHS_ATTRIBUTION_WITH_SUPPORT)\nconsole.log(EFFECTIVE_PATHS_SUPPORT.creatorCode) // 'SHEETLORD'\n```\n\n### The wiki contributors\n\nThe `thetowersdk/wiki` helpers fetch and reformat pages that other people wrote. Nothing there\nproduces knowledge of its own. Thank you to the volunteer editors of the\n[Fandom — The Tower: Idle Tower Defense Wiki](https://the-tower-idle-tower-defense.fandom.com/wiki/)\nand [Game Vault — The Tower Wiki and Guides](https://the-tower-idle-tower-defense.game-vault.net/wiki/).\n\nIndividual editors are deliberately not listed in code: wiki authorship changes continuously and is\nrecorded per page in each wiki's own history, so a snapshot would be wrong within a week and would\nsilently drop people. `fetchWikiPageFromAnySource()` returns the source `url` so you can link back to the page and\nits edit history. `WIKI_ATTRIBUTION` is a ready-made credit line.\n\n### Wave scaling\n\nThe wave scaling code builds on\n[**tower-idle-toolkit**](https://github.com/tower-idle-toolkit/tower-idle-toolkit) by **skye**,\nused under ISC. That project worked out the structure this scaler still uses, and several of its\nconstants come from there directly. It's no longer maintained, which is part of why this exists —\nbut the groundwork is theirs. See [NOTICE](NOTICE).\n\n### Author, and supporting the work\n\nBuilt and maintained by **TmRxJD**, a moderator on the Official Tower Discord, alongside\n[the-tower-run-tracker.com](https://the-tower-run-tracker.com/) — which runs on this package and is\nwhere most of it was first proven.\n\nSupporting it costs nothing: enter creator code **`JDEVO`** at checkout in\n[The Tower webstore](https://store.techtreegames.com/thetower/). One-off donations are welcome on\n[Ko-fi](https://ko-fi.com/F1F41FUGWR) or\n[PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=thetowertracker%40gmail.com).\n\nIf you only support one thing here, make it the Effective Paths team above — their maths is the\npart of this package that took the most work, and none of it is mine.\n\n## License\n\nMIT — see [LICENSE](LICENSE). Build open or closed source, commercial or not.\n\nThe one runtime dependency, [zod](https://github.com/colinhacks/zod), is MIT licensed and is\nbundled into the WebAssembly build. Its licence text and every other attribution are in\n[NOTICE](NOTICE).\n\nNot affiliated with or endorsed by Tech Tree Games. \"The Tower\" is their trademark; this is a\ncommunity project.\n","readmeFilename":"README.md"}