{"_id":"@amerilux/netsuite-repository","_rev":"11-faf872e73c42ab7eecee2ced012d7ca7","name":"@amerilux/netsuite-repository","dist-tags":{"latest":"1.4.0"},"versions":{"0.1.0":{"name":"@amerilux/netsuite-repository","version":"0.1.0","license":"MIT","_id":"@amerilux/netsuite-repository@0.1.0","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"dist":{"shasum":"dd769a87eed126e231ffbd1dacd4121a836e6bdc","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-0.1.0.tgz","fileCount":13,"integrity":"sha512-e3xlslOlieLi8EcBnHU/XHuFS47TEjQZ9ACv3OIAQj1K7oBv8yJFfR6yWxgJdkOQm0qUHwpk1YRxdFWCCTZSFQ==","signatures":[{"sig":"MEYCIQCjdvzL38GJOWmV9dvThW/a3bvOvkM6QXcOdX8NX/58zgIhAIB1WnRPWkJ6iyXXPRdotbo4jvEXmec8GiunmhzKJaFk","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":117506},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./query":{"types":"./dist/query.d.ts","default":"./dist/query.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"cf59da990b03e27feed1b78c2de25203855b6d24","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish"},"_npmUser":{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"},"_npmVersion":"10.9.7","description":"Typed query configuration and record update helpers for NetSuite SuiteScript.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"tag":"latest","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_0.1.0_1779979113714_0.8910304319577877","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@amerilux/netsuite-repository","version":"0.2.0","license":"MIT","_id":"@amerilux/netsuite-repository@0.2.0","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"1dae6b8b9f9fdbc5d9da9dc1893fa2eda1c05692","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-0.2.0.tgz","fileCount":115,"integrity":"sha512-1h2IrdbaeNhKIUSPx0k6gDsdF8mMUzyKFlXLphlCxhqeZUm6TjHc4Y7lTUG9CCeOuOMvirgqKCsLd9U4KonWsw==","signatures":[{"sig":"MEUCIQDqgZoBhEnNBIOz9aMhCpZgvK1xjbNXgkX24RFIKsAfXgIgEkI4MlAMe4ypCoXmSY8J0F2yOkrJVSfJXPS92+OiTYc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":581127},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query.d.ts","default":"./dist/query.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"7a78b2f7e98d95e96598173db9a0680f172b5c1c","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish"},"_npmUser":{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"},"_npmVersion":"10.9.7","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed SuiteQL queries, change tracking, and record updates.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"tag":"latest","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_0.2.0_1788461509766_0.015621887534769563","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@amerilux/netsuite-repository","version":"0.4.0","license":"MIT","_id":"@amerilux/netsuite-repository@0.4.0","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"d311d7b85444f53994c8467e69b2d90f0c68a0f1","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-0.4.0.tgz","fileCount":131,"integrity":"sha512-ebjN2RrF7/oEebR3YVFpqJZPXznKnxAdNXHGJ0sjo1e/aLQnlPONpH7Y7RjWQ/upAZ+WZvme3pxMYvAqzfdIng==","signatures":[{"sig":"MEUCID4TQElhv3tKl/ePv1TpyNjMpkdI/i9xOa8zRgZ20rKdAiEA+EYAuyvhNDnSLmiDPq56fEUaHBGTPO8Ae+bTB4ZmI4c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":681091},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query.d.ts","default":"./dist/query.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"da27166a7c47381d3746a261580e80014ab32c6f","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish"},"_npmUser":{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"},"_npmVersion":"10.9.7","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed SuiteQL queries, change tracking, and record updates.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"tag":"latest","access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_0.4.0_1788554038181_0.12140734556035082","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@amerilux/netsuite-repository","version":"0.5.0","license":"MIT","_id":"@amerilux/netsuite-repository@0.5.0","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"a9e64f77d851768132874b09287fc33fed2bc181","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-0.5.0.tgz","fileCount":149,"integrity":"sha512-sOFTOen11Tq7HMEMI+b2nBPVcFmcl9ibNiCVAXQaIDcSU8Z2u1qWAzqWbsWWwxIocSQxxDtRCroDr9+E5OxITw==","signatures":[{"sig":"MEUCIHfvLM36utdSCOwa7iILncsx+cSng3DS5ClNIM6Wy3AyAiEAzfDA4Dr+iGkLyGVm1tzYcUwLQl4aTQF6UXQeK/BIwH4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":756171},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"cf82a29440b731b35712d5c7a9f107c768a2657e","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish"},"_npmUser":{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"},"_npmVersion":"10.9.7","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_0.5.0_1789064947356_0.38215453518139975","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@amerilux/netsuite-repository","version":"1.0.0","license":"MIT","_id":"@amerilux/netsuite-repository@1.0.0","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"5f0efd7e73556f970cf30960ffb53e8cebf405f0","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-1.0.0.tgz","fileCount":151,"integrity":"sha512-356WtF6HjiNRlr+QoEHrMfC9P2mvNFfevpNzj5voz1mjchJmvnuIT728Jw7Qd9yGv6b+lScgAEYpkv0KpRAwCw==","signatures":[{"sig":"MEUCIQD5LkwkZEEdRh9HUYUUEo/nw6GsHfL08r/J5VMP5whBGAIgJqxMnkqfsDqu12spNUgyP/qgD/kTiHhzPg/ARIKnzPk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":766723},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"19d4860126d8a65ed640ff84c78598c5fa474b15","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish"},"_npmUser":{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"},"_npmVersion":"10.9.7","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_1.0.0_1789094902732_0.72417022437759","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@amerilux/netsuite-repository","version":"1.1.1","license":"MIT","_id":"@amerilux/netsuite-repository@1.1.1","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"homepage":"https://github.com/AmeriLux-Dev/netsuite-repository#readme","bugs":{"url":"https://github.com/AmeriLux-Dev/netsuite-repository/issues"},"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"a8762f73243a241476534592c46684e2a94e8c16","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-1.1.1.tgz","fileCount":135,"integrity":"sha512-ja9oqxYXgz9Jm2nltnL53ITtvAXS8BbDr35lYzXBwIMoCX9uaYEpUsGofBKbLDa1UCfz4vGXOz6jflRM83X3Bw==","signatures":[{"sig":"MEUCIQDnmxXdLbTDZHV39k6sx/Dl53yd7+Cywua03lLVgItaVQIgI+64hcc55sygd+2l5i6RHPYi0QrlVpYikaqZvG5+Gew=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amerilux%2fnetsuite-repository@1.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":702731},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"f9a3c2a7f5045b675ca891688c17c62eaf347f82","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:546036bb-8b83-477c-89c3-1227467746f5"}},"repository":{"url":"git+https://github.com/AmeriLux-Dev/netsuite-repository.git","type":"git"},"_npmVersion":"12.0.2","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_1.1.1_1789148661758_0.70694111483329","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@amerilux/netsuite-repository","version":"1.2.0","license":"MIT","_id":"@amerilux/netsuite-repository@1.2.0","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"homepage":"https://github.com/AmeriLux-Dev/netsuite-repository#readme","bugs":{"url":"https://github.com/AmeriLux-Dev/netsuite-repository/issues"},"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"ae34088676e43ddb2689547e148b1897e364c097","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-1.2.0.tgz","fileCount":139,"integrity":"sha512-x0X+DyA4zTUneFFf9hhXW5iJck8LzoCzHxlVWx4SgLnk5Zuo9nG/F+cfkAuFJOJR5yrLG258IZ+coHn60zgqwA==","signatures":[{"sig":"MEUCICJQbUjzpIKYCWc1WDcQlExtnA0+XKYqXHZnVVPnJLhpAiEAqctU1MjGDuckUHV58EJbCWPl4khS5ojzTmRTZTRpELI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amerilux%2fnetsuite-repository@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":764019},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"2f59394bebec57319797a6fe4e3395734163adec","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish","generate:record-types":"node scripts/generateNetsuiteRecordTypes.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:546036bb-8b83-477c-89c3-1227467746f5"}},"repository":{"url":"git+https://github.com/AmeriLux-Dev/netsuite-repository.git","type":"git"},"_npmVersion":"12.0.2","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_1.2.0_1789570173797_0.511812380772404","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@amerilux/netsuite-repository","version":"1.2.1","license":"MIT","_id":"@amerilux/netsuite-repository@1.2.1","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"homepage":"https://github.com/AmeriLux-Dev/netsuite-repository#readme","bugs":{"url":"https://github.com/AmeriLux-Dev/netsuite-repository/issues"},"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"bc7fa06ef3ef12fb51cec4393f4dddbb02f6c106","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-1.2.1.tgz","fileCount":145,"integrity":"sha512-hSknRr26hanytewD0WtyWEbMJ4xm0n4xA6Ynq9prKMcrx1ZOBpTASMOCVrdEfvzBFFNM8gh238ZV8SwVGvuqlg==","signatures":[{"sig":"MEUCIQCgPQCMuh2LFngRMhuAm7aLhgnG+kWNz/Lp9RBvjXj4BQIgNJvAbj0Tm0MNHRij+3/6J0RLLmsKFONYoB6HcE3X5Cc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIFSlE6HJKHophRqxGHTc+TrA8RoLkzj90qZjUFFfQH+VAiEApqFjyMSwgLGXTHciQTXaIRc8Ofx/Shd7bk827Y4w5AA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amerilux%2fnetsuite-repository@1.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":836402},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"e968842b97da022883c3b1545ed5a45ce3564aa2","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish","generate:record-types":"node scripts/generateNetsuiteRecordTypes.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:546036bb-8b83-477c-89c3-1227467746f5"}},"repository":{"url":"git+https://github.com/AmeriLux-Dev/netsuite-repository.git","type":"git"},"_npmVersion":"12.1.0","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_1.2.1_1790277928570_0.7593424399331554","host":"s3://npm-registry-packages-npm-production"}},"1.2.2":{"name":"@amerilux/netsuite-repository","version":"1.2.2","license":"MIT","_id":"@amerilux/netsuite-repository@1.2.2","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"homepage":"https://github.com/AmeriLux-Dev/netsuite-repository#readme","bugs":{"url":"https://github.com/AmeriLux-Dev/netsuite-repository/issues"},"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"1ee49d1911472189e55b7d19355b2db6901eb621","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-1.2.2.tgz","fileCount":145,"integrity":"sha512-+Zpxlkg5ZPeGht+FxGqtyn1mVnT/IAB/9xQLw+2YvyfFTUPgFdYlYBwHduDnQR4xkM6fSUVqhjMHFpxk77lAoQ==","signatures":[{"sig":"MEUCIB594CFdq/U6UNFwjt0e3JzHzWu7joNRk5axLdIbnValAiEA65kR2Gh9ALn7tlBD+bNumJuZOZemFd4XbCATsp+MC5s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIEM1AP/RwQwtHiZ/lkLA2TiL1N6Qy7VVdBunOLjQTyE2AiEAyG1iw+MPxmBg+9HgCluYJaR6o1oXvl+MfBhGwq95FC4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amerilux%2fnetsuite-repository@1.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":845489},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"097c446446234be65cea6cfc6a1ac39d35a17cc7","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish","generate:record-types":"node scripts/generateNetsuiteRecordTypes.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:546036bb-8b83-477c-89c3-1227467746f5"}},"repository":{"url":"git+https://github.com/AmeriLux-Dev/netsuite-repository.git","type":"git"},"_npmVersion":"12.1.0","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_1.2.2_1790365537288_0.37688820339031537","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@amerilux/netsuite-repository","version":"1.3.0","license":"MIT","_id":"@amerilux/netsuite-repository@1.3.0","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"homepage":"https://github.com/AmeriLux-Dev/netsuite-repository#readme","bugs":{"url":"https://github.com/AmeriLux-Dev/netsuite-repository/issues"},"bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"dist":{"shasum":"6e2ff9ce37ad04b99ed6d0e6809e4bb23ad082e2","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-1.3.0.tgz","fileCount":145,"integrity":"sha512-eBGWOYvCbfcyOXhZvEuZ4Gp4+FiP861PVdZecfwHxIw31T3RxjwBguuIwN/zdW3YcGaWdL2qmoW8ZnOmNLZ+Wg==","signatures":[{"sig":"MEUCIQDEvDMGa4JHjTHvzRuwpmpsptRYtqacvtJkMV5NM2L6NgIgcBKML3998jAjdVO/D1y/RDcHqfeE06CYklP0APIbbzQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCxbE95IFMHkEmCcI8qv+MzL99ZBZWUGtuZwqBlykl7nAIgJ2z3w4g3cSbAEWfXEN4o4iujqDQ0jC+wkSQE+rHIOos=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amerilux%2fnetsuite-repository@1.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":869714},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"3baf81cba9a8015fc5b1f9cc0f830e91750c51a8","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish","generate:record-types":"node scripts/generateNetsuiteRecordTypes.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:546036bb-8b83-477c-89c3-1227467746f5"}},"repository":{"url":"git+https://github.com/AmeriLux-Dev/netsuite-repository.git","type":"git"},"_npmVersion":"12.1.0","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/netsuite-repository_1.3.0_1790608605107_0.6719150649647128","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"_id":"@amerilux/netsuite-repository@1.4.0","bin":{"netsuite-repository":"dist-tooling/tooling/cli/bin.js"},"bugs":{"url":"https://github.com/AmeriLux-Dev/netsuite-repository/issues"},"dist":{"shasum":"2892401570a69c332d8eaba67c4f0344dab37fb0","tarball":"https://registry.npmjs.org/@amerilux/netsuite-repository/-/netsuite-repository-1.4.0.tgz","fileCount":149,"integrity":"sha512-wu9MAcSxzBKWt9D0ppjHJVHvvKmMfW4CMwPiKbRgIadSUyNa5QLJH4zQBmWkrlnQ41uplyETzURT+K3AIzKL0g==","signatures":[{"sig":"MEYCIQDSy5GT4tFfDsB8AjIeqBMHhE1qimwSVDyxK223KE0v9QIhAKoKTcYpukSfcQ5Ay6RvtE2esZibzi8YGkZoEo35PVmw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDRtxyLJMAU1mcN1ATySrO8AQwmbGQU1UwvKEApQrvORQIgeW03+9bMSNHeBAQnJYYtFp8AdBCBBANtLFT2XTHEoFM="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amerilux%2fnetsuite-repository@1.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":948727},"main":"dist/index.js","name":"@amerilux/netsuite-repository","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist-tooling/tooling/index.d.ts","default":"./dist-tooling/tooling/index.js"},"./model":{"types":"./dist/model/index.d.ts","default":"./dist/model/index.js"},"./query":{"types":"./dist/query/index.d.ts","default":"./dist/query/index.js"},"./plugin":{"types":"./dist-tooling/tooling/plugin.d.ts","default":"./dist-tooling/tooling/plugin.js"},"./context":{"types":"./dist/context.d.ts","default":"./dist/context.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./coercion":{"types":"./dist/coercion.d.ts","default":"./dist/coercion.js"},"./tracking":{"types":"./dist/tracking/index.d.ts","default":"./dist/tracking/index.js"},"./record-updater":{"types":"./dist/record-updater.d.ts","default":"./dist/record-updater.js"}},"gitHead":"2654ccb072fed5016e30f16acd356a0046173258","license":"MIT","private":false,"scripts":{"pack":"npm pack","test":"jest","build":"npm run build:runtime && npm run build:tooling","typecheck":"tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.tooling.json --noEmit","publish:npm":"npm publish","pack:dry-run":"npm pack --dry-run","build:runtime":"tsc -p tsconfig.build.json","build:tooling":"tsc -p tsconfig.tooling.json","test:coverage":"jest --coverage","packAndPublish":"npm run build && npm run test && npm publish","generate:record-types":"node scripts/generateNetsuiteRecordTypes.mjs"},"version":"1.4.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"546036bb-8b83-477c-89c3-1227467746f5"}},"homepage":"https://github.com/AmeriLux-Dev/netsuite-repository#readme","repository":{"url":"git+https://github.com/AmeriLux-Dev/netsuite-repository.git","type":"git"},"_npmVersion":"12.1.0","description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","directories":{},"maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"_nodeVersion":"22.23.2","publishConfig":{"tag":"latest","access":"public"},"typesVersions":{"*":{"cli":["dist-tooling/tooling/index.d.ts"],"model":["dist/model/index.d.ts"],"query":["dist/query/index.d.ts"],"plugin":["dist-tooling/tooling/plugin.d.ts"],"context":["dist/context.d.ts"],"testing":["dist/testing/index.d.ts"],"coercion":["dist/coercion.d.ts"],"tracking":["dist/tracking/index.d.ts"],"record-updater":["dist/record-updater.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","ts-jest":"^29.2.6","typescript":"5.8.3","@types/jest":"^29.5.14","@types/node":"^25.9.1","@nizos/probity":"^1.5.1","@hitc/netsuite-types":"2025.1.11"},"peerDependencies":{"typescript":">=5.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/netsuite-repository_1.4.0_1790695147325_0.5245882150142713"}}},"time":{"created":"2026-05-28T14:38:33.595Z","modified":"2026-09-29T15:19:07.861Z","0.1.0":"2026-05-28T14:38:33.872Z","0.2.0":"2026-09-03T18:51:49.930Z","0.4.0":"2026-09-04T20:33:58.339Z","0.5.0":"2026-09-10T18:29:07.525Z","1.0.0":"2026-09-11T02:48:22.966Z","1.1.1":"2026-09-11T17:44:21.966Z","1.2.0":"2026-09-16T14:49:33.942Z","1.2.1":"2026-09-24T19:25:28.712Z","1.2.2":"2026-09-25T19:45:37.396Z","1.3.0":"2026-09-28T15:16:45.216Z","1.4.0":"2026-09-29T15:19:07.459Z"},"bugs":{"url":"https://github.com/AmeriLux-Dev/netsuite-repository/issues"},"license":"MIT","homepage":"https://github.com/AmeriLux-Dev/netsuite-repository#readme","repository":{"url":"git+https://github.com/AmeriLux-Dev/netsuite-repository.git","type":"git"},"description":"Entity Framework style data access for NetSuite SuiteScript: decorated models, a build step that generates configs and types, typed queries through the N/query object model, change tracking, and record updates. This is not for production use and is here f","maintainers":[{"name":"michael-young-amerilux","email":"Michael.ThomasYoung@amerilux.com"}],"readme":"# @amerilux/netsuite-repository\n\nEntity Framework style data access for NetSuite SuiteScript, in NetSuite's own vocabulary.\n\n- **A model is a class.** `@RecordType('salesorder')` on a class makes it a record set on the context. Every declared property is a field; the member decorators name what NetSuite calls the rest: `@Subrecord` on an object-typed property, `@Sublist` on an array-typed property, `@Reference` on a property typed as another record class. Decorators only override what the model itself already says.\n- **A build step generates the config and type files** from the classes, TanStack Router style, so the SuiteScript bundle carries plain objects and no decorator machinery.\n- **The runtime queries through the N/query object model** (`query.create`, `autoJoin`, `joinTo`, `joinFrom`, columns, conditions, sorts, `runPaged`), maps rows into typed objects, and writes through `N/record` with the fast path (`submitFields`) whenever the change allows it. SuiteQL text is never assembled.\n- **The library carries no NetSuite schema.** Tables, key columns, and join predicates come from N/query at run time; the few NetSuite facts a model needs are declared in the model.\n- **Change tracking** gives you `find()`, mutate, `saveChanges()`.\n\n## Vocabulary\n\n| NetSuite term | Entity Framework equivalent | In a model |\n| --- | --- | --- |\n| Record type | Entity | a class with `@RecordType('salesorder')` |\n| Internal id | Key | the property `id` |\n| Field | Column / property | any declared property |\n| Select field | Foreign key | a `number` property holding an internal id, `customerId` |\n| Reference | Reference navigation | a property typed as another record class, `customer?: Pick<Customer, ...>` |\n| Subrecord | Owned type | an object-typed property, `shippingAddress: TransactionAddress` |\n| Sublist | Collection navigation | an array-typed property, `@Sublist('item') lines: TransactionLine[]` |\n| Sublist line | Dependent entity | a record type whose `@ParentId()` property points at the parent |\n| Record set | DbSet | `db.salesOrders` |\n\n## Install\n\n```sh\nnpm install @amerilux/netsuite-repository\n```\n\nEnable decorators in the project that authors models:\n\n```json\n{ \"compilerOptions\": { \"experimentalDecorators\": true } }\n```\n\nThe build step needs Node 18 or newer and the `typescript` package of your project.\n\n## Define a model\n\n```ts\n// src/models/SalesOrder.ts\nimport { Field, InternalId, ParentId, RecordType, Sublist, Subrecord } from '@amerilux/netsuite-repository';\nimport type { Customer } from './Customer';\nimport type { InventoryItem } from './InventoryItem';\n\n/** Shared by the shipping and billing addresses; the subrecord field id comes from the property that uses it. */\nexport class TransactionAddress {\n    addr1!: string | null;\n    city!: string | null;\n    @Field({ setFirst: true }) state!: string | null;   // written before zip so address validation accepts it\n    zip!: string | null;\n}\n\n/** A sublist line is a record type. Its @ParentId() property is the field N/query joins the lines through. */\n@RecordType('transactionline')\nexport class TransactionLine {\n    @InternalId() @Field('line', { queryFieldId: 'id' }) id!: number;   // queried as id, written through the sublist field line\n    @ParentId() @Field('transaction', { readOnly: true }) transactionId!: number;\n    @Field('item') itemId!: number;\n    item?: Pick<InventoryItem, 'itemId' | 'displayName'>;    // reference inside the line\n    quantity!: number;\n    rate!: number | null;\n    @Field({ readOnly: true }) amount!: number;\n}\n\n/** Common transaction fields. No @RecordType, so no record set of its own; every transaction type inherits it. */\nexport abstract class Transaction {\n    id!: number;                                             // internal id, read-only\n    @Field('tranid') tranId!: string;                        // renamed field\n    tranDate!: Date;                                         // field 'trandate', type date, writable\n    memo?: string | null;\n    @Field('entity') customerId!: number;                    // select field\n    customer?: Pick<Customer, 'id' | 'companyName'>;          // reference, joined on customerId, two fields only\n    @Field({ queryFieldId: 'status', text: true }) statusText!: string;        // display text, read-only\n    @Field('orderstatus', { queryFieldId: 'status' }) status!: string;        // queried as status, written as orderstatus\n    @Subrecord({ clearListField: 'shipaddresslist' }) shippingAddress!: TransactionAddress;\n    @Subrecord({ clearListField: 'billaddresslist' }) billingAddress?: TransactionAddress;\n}\n\n@RecordType('salesorder')\nexport class SalesOrder extends Transaction {\n    @Field('otherrefnum') poNumber!: string | null;\n    @Field('shipmethod') shipMethodId!: number | null;\n    @Field('foreigntotal', { readOnly: true }) total!: number;\n    /** The item lines: queried from the `transaction` root through `transactionlines` (a `salesorder` root has no join to its lines), without the header line. */\n    @Sublist('item', { queryType: 'transaction', relationship: 'transactionlines', filter: [{ fieldId: 'mainline', operator: 'IS', values: [false] }] }) lines!: TransactionLine[];\n}\n```\n\nThree rules cover most of what you see above:\n\n1. **Every declared property is a mapped field.** `@NotMapped()` opts out; the property stays on the generated type for values you fill in after the query.\n2. **Every body field is writable.** `@Field({ readOnly: true })` opts out. The field id is the lowercased property name and doubles as the N/query field id; `@Field(id)` renames it, `@Field(id, { queryFieldId })` splits the two when NetSuite queries a field under one name and writes it under another.\n3. **A property typed as another model class is a reference, subrecord, or sublist.** The join comes from the declared type and N/query: a subrecord is `autoJoin` on its field, a sublist is `joinFrom` through the line class's `@ParentId()` field, a reference is `joinTo` through its select field and the target's record type. No predicate is ever written by hand.\n\nThe class decorator is only ever `@RecordType`. A sublist line class is a record type like any other; the sublist it belongs to is declared on the property of the parent.\n\n### What the model declares, and what it does not have to\n\n| Concern | Default | Override |\n| --- | --- | --- |\n| Internal id | the property `id`, type integer | `@InternalId()` on another property |\n| Parent of a line | | `@ParentId()` on the line class's property holding the parent's internal id |\n| Field id | lowercased property name | `@Field('x')` |\n| N/query field id | the field id | `@Field('x', { queryFieldId: 'y' })` |\n| Field type | `string`, `number` (float), `boolean`, `Date`, `string[]` / `number[]` (multiselect); the internal id is a `key`, the select field behind a reference and a `@ParentId()` field a `select` | `@Field({ type })` |\n| Select field with no reference | a number or string like any other | `@Field('location', { type: 'select' })`: N/query compares select and key fields through `ANY_OF`, not `EQUAL` |\n| Read-only | the internal id, `text: true` fields, fields of a referenced record | `@Field({ readOnly: true })` |\n| Default select | every mapped field and relation | `@Field({ selectByDefault: false })`, or `{ selectByDefault: false }` on `@Reference`, `@Subrecord`, `@Sublist`; `include()` brings it back per query |\n| Text of a select field | | `@Field({ queryFieldId: 'status', text: true })`, read in DISPLAY context |\n| Field the root does not expose | read on the root | `@Field('subsidiary', { relationship: 'transactionlines', filter: [{ fieldId: 'mainline', operator: 'IS', values: [true] }] })`: read off the joined component, still written through the record's own field |\n| Query type | the record type | `@RecordType('x', { queryType })` |\n| Root filter | none | `@RecordType('x', { filter: [{ fieldId, operator, values }] })` |\n| Reference or subrecord | a plain class is a subrecord; a record class is a reference | `@Reference()`, `@Subrecord()` |\n| Select field of a reference | `<reference>Id` on the same class; when the class declares none, the build step adds it (a shadow select field, see below) | `@Reference('entityId')` names a declared one; `@Field('entity')` on the reference configures the shadow |\n| Reference join | `joinTo` through the select field and the target's record type | `@Reference({ join: 'auto' })` |\n| Reference matched on another field | | `@Reference('code', { targetKey: 'code' })`; always loaded separately |\n| Reference N/query has no join for | | `@Reference('parentId', { load: 'separate' })`: a second query matches the target's internal id against the select field values. A custom List/Record field whose list is Item is one such field: `autoJoin` on it fails with \"Record Join ... was not found\", while custom fields pointing at one custom record or at a standard record such as `shipitem` do join |\n| Subrecord field id | lowercased property name | `@Subrecord('x')` |\n| Subrecord list field to clear | none | `@Subrecord({ clearListField: 'shipaddresslist' })` |\n| Sublist id | lowercased property name | `@Sublist('x')` |\n| Sublist join | `joinFrom` through the line class's `@ParentId()` field | `@Sublist('x', { relationship })` for `autoJoin` on a relationship field; needed when the root has no reverse join for the line's field (a `salesorder` root reaches its lines through `transactionlines`, not through `transactionline.transaction`) |\n| Sublist rows | every row of the line type | `@Sublist('x', { filter: [...] })` |\n| Loading | `join`: read in the parent's query; NetSuite decides inner or outer | `load: 'separate'` on `@Sublist`, `@Subrecord`, `@Reference` |\n| Root of the line query | the owner's query type | `@Sublist('item', { queryType: 'transaction', relationship: 'transactionlines' })` when the lines hang off another record than the owner (a `salesorder` root has no join to its lines; `transaction` has); the lines then run as their own query, matched to the owner by internal id |\n| Has-many keyed by a field on the child | | `@Sublist({ load: 'separate' })` on the owner with `@ParentId()` on the child's field that points back at it (a fulfillment's SPS contents, keyed by their fulfillment field): the children run as their own query on the child's record type, batched on that field with ANY_OF; nothing is joined to reach them |\n| Items more than one join away | | `@Sublist({ relationship: 'nexttransactionlink', through: [{ fieldId: 'nextdoc', target: 'transaction' }] })`: each hop is a relationship field (`autoJoin`) or a select field with the query type it points at (`joinTo`); the items' fields are read on the last hop, and nothing on the way needs a class |\n| Record set name | pluralized camel-case class name | `@RecordType('x', { setName })` |\n\nNothing in the build step knows a NetSuite table, relationship, sublist, or field. What the table does not list is either derived from the class or resolved by N/query when the query runs. A missing declaration the build step needs is a diagnostic naming the decorator that supplies it.\n\n### References project with the declared type\n\nThe declared type of a reference is its projection, so a lookup never pulls the whole record:\n\n```ts\ncustomer?: Pick<Customer, 'id' | 'companyName'>;   // two fields\ncustomer?: Omit<Customer, 'notes'>;                   // everything but one\ncustomer?: CustomerSummary;                            // type CustomerSummary = Pick<Customer, ...>\ncustomer?: Customer;                                   // every mapped field\n```\n\nReferences are read-only; write the select field (`customerId`) instead. A reference that loads its own class without a projection is a build error.\n\n### The select field of a reference\n\nA reference reads the internal id it joins on from `<reference>Id`. Declare that property when you want to control it, or leave it out and the build step adds it to the generated type, like an Entity Framework shadow foreign key:\n\n```ts\n@Field('entity') customerId!: number;                     // declared: its own type and options\ncustomer?: Pick<Customer, 'id' | 'companyName'>;\n\nsubsidiary?: Pick<Subsidiary, 'id' | 'name'>;            // generated type gains subsidiaryId: number | null\n@Field('custbody_approver') approver?: Pick<Employee, 'id'>; // generated type gains approverId, read from custbody_approver\n```\n\n- The shadow is an ordinary field on the generated type: read it, filter on it, write it. Its field id is the reference's lowercased name, its type the referenced record's internal id or `null`.\n- `@Field` on the reference configures the shadow: its field id, `readOnly`, or `relationship` and `filter` to read it off a joined component.\n- A declared property is always the select field, whatever its type; the build step never replaces it, even when it is `@NotMapped()`. `@Field` options on a reference whose select field is declared are a build error: put them on the declared property.\n- A reference that names its select field (`@Reference('entityId')`) or matches on another key (`targetKey`) must declare it.\n- A projection in another model file cannot name a shadow: `Pick<Order, 'subsidiaryId'>` does not compile when the `Order` class does not declare it. Declare the select field when another model projects it.\n\n### Join or separate\n\nN/query has no join-type option: NetSuite decides whether a relationship joins inner or outer. Subrecords come back outer; the line join of a sublist is inner, so a query that reads lines returns only the parents that have matching lines. When parents must come back regardless, load the relation with a query of its own:\n\n```ts\n@Sublist('item', { queryType: 'transaction', relationship: 'transactionlines', filter: [{ fieldId: 'mainline', operator: 'IS', values: [false] }] }) lines!: TransactionLine[];\n```\n\n`separate` runs one query for the parents and one per batch of parent ids for the relation, then stitches the lines in (`[]` when there are none, `null` for a subrecord or reference). Rows never fan out, `limit()` and `page()` count records, and a `where` on a field of the relation narrows the relation's rows rather than the parents. It costs one extra `run` per batch. A reference matched on a field other than the target's internal id (`targetKey`) always loads this way, because N/query joins only through internal ids, and so does a reference whose select field is read through a relationship (see [Fields the root does not expose](#fields-the-root-does-not-expose)).\n\nWhere the second query runs depends on how the relation is joined. A sublist reached through a relationship field (`relationship`) or another root (`queryType`) queries that root and joins the lines; a has-many joined `from` the child's `@ParentId()` field queries the child's own record type, with the sublist filter as a root condition and the child's parent field matched against the owners' ids, and the child's own references (`spsPackage`, `spsPackage.packType`) join inside that query:\n\n```ts\n@RecordType('customrecord_sps_content')\nexport class SpsContent {\n    id!: number;\n    @ParentId() @Field('custrecord_pack_content_fulfillment') fulfillmentId!: number;\n    @Field('custrecord_sps_content_package') packageId!: number | null;\n    @Reference('packageId', { join: 'auto' }) spsPackage?: Pick<SpsPackage, 'id' | 'sscc' | 'packType'>;\n}\n\n@RecordType('itemfulfillment')\nexport class ItemFulfillment {\n    id!: number;\n    @Sublist({ load: 'separate' }) contents!: SpsContent[];   // FROM customrecord_sps_content WHERE custrecord_pack_content_fulfillment ANY_OF [...]\n}\n```\n\nA separate relation inside a separately loaded relation is not planned: compose it in the repository, as the order-processing test project does when it reads the fulfillments of a set of orders and stitches them onto the orders itself.\n\n### Items more than one join away\n\nA has-many whose items sit past further joins from the relationship field declares those joins on the property. Nothing on the way needs a class: only the items do, and a projection of a base class serves. A transaction's related records hang off the link `nexttransactionlink`, and the transaction each link points at off the link's `nextdoc`:\n\n```ts\n@RecordType('salesorder')\nexport class SalesOrder extends Transaction {\n    /** The transactions this order led to: the link off `transaction`, then the transaction it points at. */\n    @Sublist({ queryType: 'transaction', relationship: 'nexttransactionlink', through: [{ fieldId: 'nextdoc', target: 'transaction' }] })\n    relatedTransactions!: Pick<Transaction, 'id' | 'tranId' | 'statusText'>[];\n}\n```\n\n- `relationship` is the first join, `autoJoin` on a relationship field. Each entry of `through` is one more: a string is a relationship field (`autoJoin`), an object a select field with the query type it points at (`joinTo`). A hop may carry its own `filter`; the sublist's `filter` applies to the relationship's component (`linktype ANY_OF [...]` on the link).\n- The build step emits one component per hop, parent to child, and reads the items' fields on the last. Result paths do not spell the hops: `order.relatedTransactions[0].tranId`, and `where('relatedTransactions.tranId', ...)` and `orderBy()` address the items the same way.\n- Loaded separately (`queryType`, or `load: 'separate'`), the second query joins the whole chain from its root and matches the owners by internal id; a `where` on the items narrows the items. Joined (`load: 'join'`, on an owner whose own query type carries the relationship field), the chain joins into the owner's query and a `where` on the items narrows the owners.\n- The items are other records, not lines of the owner, so their fields are read-only.\n- The relationship and hop field ids are what the Records Catalog lists under the record's joins; N/query resolves the predicates.\n\n### Fields the root does not expose\n\nN/query inside SuiteScript rejects some body fields on the root that the record itself has: a transaction's `subsidiary` fails with `NOT_EXPOSED - Field is marked as internal for channel SEARCH`, even though SuiteQL through the REST service reads it. Its main line carries the same value, so the field is read there:\n\n```ts\n@RecordType(NetsuiteRecordType.INVOICE, { queryType: 'transaction', filter: [{ fieldId: 'type', operator: 'ANY_OF', values: ['CustInvc'] }] })\nexport class Invoice {\n    id!: number;\n    @Field('subsidiary', { type: 'select', relationship: 'transactionlines', filter: [{ fieldId: 'mainline', operator: 'IS', values: [true] }] })\n    subsidiaryId!: number;\n    subsidiary?: Pick<Subsidiary, 'id' | 'name'>;   // loaded by a second query, by the id read off the main line\n}\n```\n\n- The build step emits one component per relationship (`autoJoin` with the filter as its conditions) and puts the field on it; the value still lands at the root of the result (`invoice.subsidiaryId`), and `where()` and `orderBy()` on it join the same component.\n- The filter must pick one row per record. `mainline IS true` does, so rows never fan out and `page()` stays a row window.\n- Fields read through one relationship share its join, so they must declare the same filter; the build step reports two that differ, and a filter with no relationship.\n- A reference whose select field is read this way loads separately: a second query reads the target by the ids collected off the component. N/query inside SuiteScript has no join from that component to the target: the join the build step used to emit, from the main line to `subsidiary`, failed in production on 2026-09-25 with `Record Join 'subsidiary^subsidiary' for record 'transactionLine' was not found`. `load: 'join'` on such a reference is a build error, and so is projecting it into another relation, where it cannot load separately.\n- Without `subsidiaryId`, the options go on the reference itself: `@Field({ relationship: 'transactionlines', filter: [...] }) subsidiary?: Pick<Subsidiary, 'id' | 'name'>` reads its shadow select field off the main line and loads the subsidiary separately the same way.\n- Writes are unchanged: the field writes through the record's own field id. Declare it `readOnly: true` when the record has no such field.\n\n### Inheritance\n\nA base class without `@RecordType` is a mapping base whose members are inherited. Each `@RecordType` class queries its own record type, so `salesorder` and `invoice` classes extending one `Transaction` base each read their own fields with no discriminator to declare.\n\n### Decorator reference\n\n| Decorator | Where | Purpose |\n| --- | --- | --- |\n| `@RecordType(id, { queryType?, filter?, setName?, coerce?, updater? })` | class | A queryable record type with a record set on the context. `id` is a native type from `NetsuiteRecordType` (`NetsuiteRecordType.SALES_ORDER`, a runtime copy of N/record's `Type` so the model needs no N/* import) or a custom record id (`'customrecord_x'`). `updater` sets the default `RecordUpdaterOptions` for every write. |\n| `@InternalId()` | property | The internal id when it is not `id`. |\n| `@ParentId()` | property | On a line class: the property holding the parent record's internal id. |\n| `@Field(id?, { queryFieldId?, type?, text?, coerce?, relationship?, filter?, readOnly?, setFirst?, selectByDefault?, transform? })` | property | Renames the field, separates the query field id from the record field id, overrides the inferred type, or reads the field through a relationship when the root does not expose it. On a reference with no declared select field, it configures the shadow select field. The flags: `readOnly` excludes it from writes, `setFirst` writes it before the others, `selectByDefault: false` leaves it out of the default select, and `transform` maps the value read; a transform must be an exported function so the build step can import it by name. |\n| `@Reference(selectFieldProperty?, { targetKey?, load?, join?, selectByDefault? })` | property | A reference: the declared select field behind it when it is not `<reference>Id`, how it joins and loads, and the referenced property to match on when it is not the internal id. |\n| `@Subrecord(fieldId?, { clearListField?, load?, selectByDefault? })` | property | A subrecord: its field id and the list field cleared before an edit. |\n| `@Sublist(sublistId?, { filter?, relationship?, through?, queryType?, load?, selectByDefault? })` | property | A sublist, or any has-many: its id, the conditions that pick its lines, and how the lines are reached (a relationship field, further joins past it, another root, or the line class's `@ParentId()` field). |\n| `@NotMapped()` | property | Leaves the property out of the model; it stays on the generated type for values filled in after the query. |\n\n`@ReadOnly()`, `@SetFirst()`, `@ExcludeFromDefaultSelect()`, and `@Transform(fn)` still work but are deprecated: they are the `readOnly`, `setFirst`, `selectByDefault: false`, and `transform` options above.\n\n## Build step and generated files\n\nAdd a config file at the project root (every key is optional; the file itself is optional too). Relative paths in it resolve against the config file's own directory:\n\n```json\n{\n  \"models\": [\"src/models/**/*.ts\"],\n  \"outDir\": \"src/repositories/generated\",\n  \"context\": { \"name\": \"App\", \"fileName\": \"context.gen.ts\" },\n  \"types\": { \"fileName\": \"types.gen.ts\" },\n  \"tsconfig\": \"tsconfig.json\",\n  \"repositories\": \"none\"\n}\n```\n\n`repositories` is `none` by default. Set it to `classes` to also emit a base repository class per record type and let the context factory accept subclasses (see Repositories).\n\nThen run the build step:\n\n```sh\nnpx netsuite-repository generate     # write the generated files\nnpx netsuite-repository check        # exit non-zero when they are out of date, or when no model files match (CI)\nnpx netsuite-repository watch        # regenerate whenever a model file changes\n```\n\nIt writes:\n\n- `generated/types.gen.ts`, the interface of every exported class (extending its base class's interface) and, for record types, `<Class>Patch` and `<Class>Create`. It holds types only, so a bundler erases any import of it: put DTOs shared with a browser client on it. `types.fileName` renames it, and `types.outDir` moves it to another directory, such as a `common/` workspace the client also compiles; every generated file imports it from there.\n- `generated/<Class>.gen.ts` for every record type, the runtime side: the `<Class>Config` literal the runtime reads, and `<Class>Fields`, a constant whose properties mirror the model and hold its field paths (`SalesOrderFields.lines.item.type` is `'lines.item.type'`) for `where()`, `orderBy()`, and `select()`. It re-exports the class's three types from the types file, so server code can import the type and the fields from one place.\n- `generated/context.gen.ts` with `AppSchema`, the `AppContext` type, `createAppContext()`, and `dbContext`: the record sets for reading without tracking, plus `withTracking()` for a fresh tracking context.\n- With `\"repositories\": \"classes\"`, each record type's file also exports `<Class>RepositoryBase`, a `RecordSet` bound to the config, and the context factory accepts subclasses through `createAppContext({ repositories })`.\n\nThe build step reads the classes with the TypeScript type checker, so it sees every property, its declared type, `Pick` projections, and inheritance. Model files are also evaluated in a sandbox to collect the decorators; they may import the library and other model files by relative path, and nothing else. Anything that cannot be mapped is reported with the file, class, and property.\n\nVite and Rollup accept the plugin directly:\n\n```ts\nimport { netsuiteRepositoryPlugin } from '@amerilux/netsuite-repository/plugin';\n\nexport default { plugins: [netsuiteRepositoryPlugin({ watch: true })] };\n```\n\nOther bundlers can call `createModelWatcher()` from `@amerilux/netsuite-repository/cli` in a few lines.\n\n## Use the context\n\n```ts\nimport { createAppContext } from './repositories/generated/context.gen';\n\nconst db = createAppContext();\n\n// Read\nconst order = db.salesOrders.find(9876);\nconst pending = db.salesOrders.query()\n    .where('status', '=', 'B')\n    .where('customer.companyName', 'LIKE', 'Acme%')\n    .orderByDesc('tranDate')\n    .page(1, 50)\n    .executeTyped();\n\n// One-call writes: load, patch, plan, save; throw on failure\nconst approved = db.salesOrders.update(9876, { memo: 'Auto-approved', shippingAddress: { city: 'Dallas' }, lines: { add: [{ itemId: 1, quantity: 2 }] } });\nconst customer = db.customers.create({ companyName: 'Acme' }); // the same object, with its new id written back\ndb.salesOrders.delete(9876);\n\n// Change tracking: a unit of work across records\norder.memo = 'Auto-approved';\norder.shippingAddress.city = 'Dallas';\norder.lines.push({ itemId: 1, quantity: 2 } as TransactionLine);\nconst result = db.saveChanges();\n\n// The record updater underneath: results instead of exceptions\ndb.salesOrders.submitPatch(9876, { memo: 'x', lines: { add: [{ itemId: 1, quantity: 2 }] } });\ndb.salesOrders.createRecord({ customerId: 12, memo: 'new' });\ndb.salesOrders.deleteRecord(9876);\n```\n\n`getById()`, `list()`, `create()`, `update()`, and `delete()` are the standard operations of every record set. `update()` loads the record, applies the patch to the tracked entity, plans the save, and writes only that entity. `create()` tracks the values object, saves it, and returns it with its id. All three throw: `RecordNotFoundError` when the id does not exist, `SaveChangesError` when NetSuite rejects the save (its `result` lists every entity's outcome). Both take options: `beforeSave` receives the plan before anything is written (log it, or throw to refuse an expensive one), and `updater` passes record updater options such as `requireFastPath` for that write.\n\nA patch is the entity's own shape made partial (`<Model>Patch` in the generated file): scalars replace, `null` clears, `undefined` is skipped, a subrecord merges, and a sublist takes `{ update, add, remove }` where each line patch carries the line's identity (the model's line field, or its match field). A reference cannot be patched; set its select field instead.\n\nEntities returned by `find()`, `getById()`, `list()`, `first()`, and `executeTyped()` are tracked. `saveChanges()` diffs each one against its snapshot and writes the difference through the record updater: body-only changes use `submitFields`, and anything touching a subrecord or sublist loads, mutates, and saves. Lines are matched by their line key. Added entities get their new id written back. `planChanges()` shows what would happen without calling NetSuite, and both take `{ entities }` to work on a subset. Changes to a referenced record's fields are reported as ignored, never written.\n\nUse `asNoTracking()` for reporting reads, and `{ tracking: false }` on `createAppContext()` for contexts that never write. Contexts hold tracked entities strongly, so never keep a tracking one at module scope. The generated `dbContext` is safe there: its record sets (`dbContext.salesOrders`) read through one context that never tracks, and `dbContext.withTracking()` returns a new context each call, so a tracker lives only as long as the function that asked for it. The Layering section below says where that happens.\n\n## Repositories\n\nThe record set on the context is the repository, the way a DbSet is in Entity Framework, and the context is its unit of work: the change tracker that `saveChanges()` writes. Domain queries are built from specifications: plain functions over the query builder that `list`, `first`, `count`, and `exists` apply in order.\n\nThe simplest home for them is a module of functions over the generated `dbContext`: reads through its sets, writes through `withTracking()`:\n\n```ts\n// specifications/salesOrders.ts\nimport type { Specification } from '@amerilux/netsuite-repository';\nimport type { SalesOrder } from '../repositories/generated/SalesOrder.gen';\nimport { SalesOrderFields as so } from '../repositories/generated/SalesOrder.gen';\n\nexport const forCustomer = (customerId: number): Specification<SalesOrder> => (query) => query.where(so.customerId, '=', customerId);\nexport const pendingFulfillment = (): Specification<SalesOrder> => (query) => query.where(so.status, '=', 'SalesOrd:B');\n\n// repositories/salesOrders.ts\nimport { dbContext } from './generated/context.gen';\nimport type { SalesOrder } from './generated/SalesOrder.gen';\nimport { SalesOrderFields as so } from './generated/SalesOrder.gen';\nimport { forCustomer, pendingFulfillment } from '../specifications/salesOrders';\n\nexport function listPendingSalesOrders(customerId: number): SalesOrder[] {\n    return dbContext.salesOrders.list(forCustomer(customerId), pendingFulfillment(), (query) => query.orderByAsc(so.tranDate));\n}\n\nexport function approveSalesOrder(salesOrderId: number, memo: string): SalesOrder {\n    return dbContext.withTracking().salesOrders.update(salesOrderId, { memo }, { beforeSave: (plan) => log.debug('approve plan', plan.entries) });\n}\n\n// services/salesOrders.ts\nimport { approveSalesOrder, listPendingSalesOrders } from '../repositories/salesOrders';\n\nexport function approveOldestPendingSalesOrder(customerId: number): SalesOrder | undefined {\n    const pending = listPendingSalesOrders(customerId);\n    return pending.length > 0 ? approveSalesOrder(pending[0].id, 'Auto-approved') : undefined;\n}\n```\n\nNothing is registered, a function can read several record sets, and the service above never sees the context or the library. Writes wrap the set's `create()`, `update()`, and `delete()` the same way: the module adds the validation and the domain name, the set does the loading, planning, and saving. This is the style to reach for first. Keep the specifications in a module of their own, one per record type, and the query functions in another; the predicates then read as a vocabulary and the functions as sentences built from it.\n\nA flow that reads several records, changes them, and writes them back is one repository function too: it keeps `dbContext.withTracking()` in a local, does the reads through that, mutates the entities, and calls `saveChanges()` on it once before it returns. The tracker is shared by everything inside that function and by nothing outside it, so the service that calls it still sees only a domain name and a result.\n\nIf you prefer the queries on the set itself, set `\"repositories\": \"classes\"` in the build config. The build step then emits a base repository per record type; extend it and register the subclass when the context is created:\n\n```ts\nexport class SalesOrderRepository extends SalesOrderRepositoryBase {\n    listPending(customerId: number): SalesOrder[] {\n        return this.list(forCustomer(customerId), pendingFulfillment());\n    }\n}\n\nconst db = createAppContext({ repositories: { salesOrders: SalesOrderRepository } });\ndb.salesOrders.listPending(12);      // typed as SalesOrderRepository\ndb.customers.find(12);               // every other set is its generated base\n```\n\nEither way:\n\n- Specifications are testable through `describe()` without a context, and compose by being passed together.\n- `first()` and `find()` read every matching row on a model with a joined sublist, so the record comes back with all of its lines; a separately loaded sublist pages over records instead.\n- A registered repository is constructed with the context's change tracker, so the entities it returns are tracked and `saveChanges()` writes them.\n- Repositories and specifications are plain functions and classes with no Node dependencies. They bundle into SuiteScript like the rest of the runtime; only the build step runs in Node.\n\n### Layering\n\nEverything that touches NetSuite data lives in the repositories layer, and the context never leaves it. The layout the defaults assume, and what each layer may import (the `models` globs decide where the model classes live; a project that shares them with a browser client keeps them in a `common/` workspace and points the globs there):\n\n| Folder | Holds | Imports |\n|---|---|---|\n| `src/models/` | The decorated model classes (source, hand-written) | This package's decorators |\n| `src/repositories/generated/` | The build step's output: `<Class>.gen.ts` and `context.gen.ts`, plus `types.gen.ts` unless `types.outDir` moves it into the shared workspace | Never edited |\n| `src/specifications/` | One module per record type of `Specification` builders: the query vocabulary | `generated/`, this package's types |\n| `src/repositories/` | Query and write functions over `dbContext`: reads through its sets, writes through `withTracking()`, saved before the function returns | `generated/`, `specifications/` |\n| `src/services/` | Decisions: interpret the request, call repository functions, shape the result | `repositories/` (functions and model types only) |\n| Endpoints, entry points | Parse the request, call a service, shape the reply | `services/` only |\n\nA read goes through `dbContext.<set>` and tracks nothing. A write calls `dbContext.withTracking()`, keeps the result in a local, and finishes its own write before returning. Two repository functions never share a tracker; when a flow needs one, the flow is a single repository function. The only module-scope state is the read-only context behind `dbContext`, which holds no entities, so lifetimes stay visible in the code and do not depend on how NetSuite instantiates modules.\n\nA repository test mocks the generated `dbContext` with a fake carrying the sets the function reads; a service test mocks the repository module. `N/record`, `N/query`, `N/search`, and `context.gen` are never imported above the repositories layer, which a lint rule can enforce per folder.\n\n## Queries\n\n```ts\ndb.salesOrders.query()\n    .exclude('lines')                                   // leave a relation and its joins out\n    .include('customer')                                // bring one in declared selectByDefault: false\n    .selectFormula('{quantity} * {rate}', 'lineTotal', { type: 'FLOAT', fieldType: 'currency' })\n    .whereGroup((group) => group.where('memo', 'IS NULL').orWhere('memo', '=', ''))\n    .whereFormula('{trandate} + 30 > TRUNC(CURRENT_DATE)')\n    .orderByAsc('lineTotal')\n    .page(2, 25)\n    .executeTyped();\n```\n\n- A query only joins the components its selected fields, conditions, and sorts touch; `exclude()` drops a relation's fields, and a relation declared `selectByDefault: false` waits for `include()`.\n- N/query sorts only on the query's own columns. `orderBy()` on a selected field sorts on that column; on a field that is not selected it adds a hidden column (`__sort0`, …) that the mapped result never shows.\n- `where()`, `orderBy()`, and `select()` are typed against the model: properties and dotted paths into relations (`customer.companyName`, `lines.item.type`) are checked, so a misspelled path is a compile error, inside specifications too. The generated `<Model>Fields` constant spells them for you (`so.lines.item.type`), with completion on every level. An alias declared on the same query by `selectFormula()` is accepted as well.\n- The operators and the value follow the property's declared type, and are translated to N/query's operators. Text takes `=`, `!=`, `LIKE`, `NOT LIKE`, `IN`, and `NOT IN`; a number adds `<`, `<=`, `>`, `>=`, and `BETWEEN`; a `Date` takes the comparisons and `BETWEEN` with `Date` values; a checkbox takes `=` and `!=` with a boolean or NetSuite's `'T'`/`'F'`. Every field takes `IS NULL` and `IS NOT NULL`; `null` is not a comparison value. The N/query operator names follow N/search's: `=` becomes `IS` on text and on a checkbox, `EQUAL` on a number, `ON` on a date, and `ANY_OF` on a select, multiselect, or key field (the internal id, a reference's select field, anything declared `type: 'select'`); `>=` on a date becomes `ON_OR_AFTER`; `LIKE 'Acme%'` becomes `START_WITH`, `'%Acme'` `ENDWITH`, `'%Acme%'` `CONTAIN`, and `'Acme'` `IS`; `IS NULL` becomes `EMPTY`. `IN` becomes `ANY_OF` on a select or key field; on text, numbers, dates, and checkboxes, which have no list operator in N/query, it becomes one equality per value joined with `OR` (`NOT IN`: one negated equality per value joined with `AND`). A select field the model does not mark as one fails at run time with \"Operator EQUAL is not valid\"; declare it with `@Field({ type: 'select' })`. Any N/query operator name is accepted directly on any field (`where(so.tranDate, 'WITHIN', [from, to])`). A `LIKE` pattern with `_` or a `%` in the middle has no N/query operator and is rejected; write it as a formula.\n- With `useText`, and for a `text: true` field, the comparison is against the display text through a `{field#DISPLAY}` formula, so the text operators and string values apply whatever the field. On a joined record the formula names the select field that joins it (`{terms.duerule#DISPLAY}`), never the model's relation name; a joined text field is compared as itself, since it is its own display text and a formula cannot reach every joined record (`{entity.companyname}` is \"not found\": a select field that points at several record types has no formula path).\n- `selectFormula()` and `whereFormula()` are the escape hatch for anything the model does not declare. Formulas use N/query's `{fieldid}` syntax, and `{selectfield.fieldid}` through a select field that points at one record type (`{terms.daysuntilnetdue}`, joined outer); they are sent as written, and values in them are part of the text, so never build a formula from untrusted input.\n- `whereFormula()` with no operator takes a condition (`{trandate} + 30 > TRUNC(CURRENT_DATE)`) and sends it as `CASE WHEN … THEN 1 ELSE 0 END` compared `EQUAL 1`: N/query requires an operator, and fails to render a comparison typed `BOOLEAN`. With an operator, the formula is a value of the given type compared to the value passed (`whereFormula('{custentity_score} * 2', 'FLOAT', 'GREATER', 10)`).\n- N/query's formulas have no `SYSDATE`, and `{today}` is not a field: write `TRUNC(CURRENT_DATE)`. A date plus days (`{trandate} + 30`) works; `ADD_MONTHS(…)` and `TRUNC(CURRENT_DATE) - {trandate}` failed to render in a sandbox (2026-09-28), so pass a date as a condition value instead (`where(so.tranDate, '>=', since)`).\n- `limit()`, `offset()`, and `page()` read a row window through `runPaged`; N/query pages are five to a thousand rows, so a window smaller than five still fetches five and slices. Every query's sort ends with the internal id, so rows that tie on the other sorts never change places between pages. With a joined sublist the rows fan out, so the window applies to mapped records and the query reads every row.\n- A window over a query whose condition pins the internal id to a thousand ids or fewer is cut from one `run()` instead: `find()`, `getById()`, the read inside `update()`, and `first()` or `exists()` on an id. That is 10 units in one call, where `runPaged` spends 10 sizing the result and 10 fetching it.\n- `count()` counts records, distinct by primary key when a component is joined; `exists()` asks for one row.\n- `describe()` returns what the query will ask N/query for as plain data, `describeText()` renders it one clause per line, and `toSQL()` shows the SuiteQL NetSuite would run. None of them executes anything.\n- Read-side coercion turns numeric strings into numbers, `T`/`F` into booleans, and date strings into `Date` through `N/format`, which parses each distinct date text once per read. Generated configs enable it; hand-written configs do not. Override per query with `coerce(false)`, per config with `coerce`, or per field.\n\n### Past N/query's 5,000 rows\n\nN/query's `run()` answers at most 5,000 rows and says nothing when it stops there. `list()`, `all()`, `executeTyped()`, and `execute()` read on past it:\n\n- Every query's sort ends with the internal id (`ORDER BY …, id ASC`), so its order is complete. When an answer comes back 5,000 rows long, the next `run()` asks for the rows after its last one, by the value of each sort and then the id, until an answer comes back shorter. A query under 5,000 rows is still one `run()`.\n- The comparison follows the sort's type. The id, numbers, and dates use their own operators (`GREATER`, `AFTER`, `EQUAL`, `ON`). Text is compared upper-cased in a formula (`UPPER({companyname}) > UPPER('Acme')`): N/query refuses `GREATER` on text, and NetSuite orders text by `UPPER()`. Blanks go where NetSuite sorts them, last ascending and first descending.\n- A query no read can pick up after is read again through `runPaged`, a thousand rows a fetch: one sorted by a select field, a checkbox, a datetime, display text, or a formula; one sorted by text on a record joined through a reference, which reading on compares in a formula that may not reach it (`{entity.companyname}` is \"not found\"); one with a joined sublist, whose rows are lines; one whose config has no internal id. `listPage()` must read on, so it keeps that formula, naming the joined record by the select field that joins it (`{terms.name}`).\n- A separately loaded relation whose batch comes back 5,000 rows long splits the batch in two and reads each half again. One parent with 5,000 related rows fails rather than come back short.\n- One 5,000-row `run()` costs 10 governance units; one `runPaged` read of a thousand costs 20. Before every read past the first, the query checks `N/runtime`'s remaining usage, which costs nothing. When another read like the last would leave less than the reserve (`governanceReserve` on the query options, 100 units by default), `list()` throws a `GovernanceLimitError` carrying `rowsRead`, `remainingUsage`, `readCost`, and `reserve`, instead of NetSuite ending the script halfway through a read.\n\nTo read a list across requests, a page at a time, use `listPage()`:\n\n```ts\nconst first = db.invoices.listPage({ limit: 5000 }, forCustomer(12), open());\n// first.items; first.next is null when nothing follows\nconst second = db.invoices.listPage({ after: first.next, limit: 5000 }, forCustomer(12), open());\n```\n\n- Each page picks up after the previous page's last record by its sort values, so a record changed or deleted between two pages cannot shift the rest, and nothing is counted: `next` is null once the records run out.\n- `next` is a string carrying the last record's sort values and id; hand it back unchanged. A marker from another query is rejected.\n- A page larger than 5,000 is read in several answers. When the script cannot afford another read, the page stops early and `next` says where to continue.\n- Every sort must be one a read can compare (the id, numbers, dates, text), and the model must not join a sublist; load it separately (`load: 'separate'`) to page it.\n\nEach comparison, cost, and blank position above was checked against 17,369 records in a sandbox account on 2026-09-24; the sorts listed as read through `runPaged` were not, which is why they are.\n\n### Grouped reads\n\nA grouped read is SQL's `GROUP BY` over a model: the query's conditions, grouped by one or more keys, with aggregate columns under aliases. The sums run in the database, where they belong: in a sandbox, summing 31,734 open invoices into 577 customer and subsidiary groups took 6.7 to 6.9 seconds through N/query and 5.5 to 5.7 as raw SuiteQL, 10 units each, where reading the rows and summing them in script took 16.5 to 18 seconds and 80 units.\n\n```ts\n// repositories/invoices.ts\nimport { dbContext } from './generated/context.gen';\nimport { InvoiceFields as inv } from './generated/Invoice.gen';\nimport { forCustomer, open } from '../specifications/invoices';\n\nconst dueDate = '{trandate} + NVL(NULLIF({terms.daysuntilnetdue}, 0), 30)';\n\nexport function listOpenInvoiceTotals(customerId?: number) {\n    return dbContext.invoices\n        .groupBy(inv.customerId, inv.subsidiaryId)\n        .aggregateFormula('SUM', `CASE WHEN ${dueDate} < TRUNC(CURRENT_DATE) THEN {foreignamountunpaid} ELSE 0 END`, 'pastDueAmount', { type: 'FLOAT', fieldType: 'currency' })\n        .aggregate('SUM', inv.amountUnpaid, 'unpaidAmount')\n        .aggregate('COUNT', inv.id, 'invoiceCount')\n        .orderByDesc('pastDueAmount')\n        .list(open(), ...(customerId === undefined ? [] : [forCustomer(customerId)]));\n    // { customerId: number; subsidiaryId: number; pastDueAmount: number | null; unpaidAmount: number | null; invoiceCount: number }[]\n}\n```\n\n- `groupBy()` starts a grouped read on the record set, never tracked, or on a query builder, whose conditions it keeps. A key is a field path that holds one value: a body field, a field read through the main line or a joined reference, display text, a sublist's field (each line is a row), or an alias the query declared with `selectFormula()`. A multi-select and a relation itself are not keys, and neither compiles. Each group's row holds its keys at their model paths (`customer.companyName` lands in `customer: { companyName }`) with their declared types.\n- `aggregate(name, field, alias)` takes N/query's own aggregate names. COUNT and COUNT_DISTINCT take any field and answer a number. SUM, AVERAGE, MEDIAN, and their DISTINCT forms take numbers only (over a date, text, or checkbox N/query fails to render them, so those paths do not compile) and answer a number or null; on an integer field N/query types the average and the median as integers, so they come back whole. MINIMUM, MAXIMUM, and their DISTINCT forms take any field and answer its type or null. Values are coerced like any other read: a date comes back as a `Date`.\n- `aggregateFormula(name, formula, alias, { type, fieldType })` aggregates an N/query formula: `type` is its return type, `fieldType` how its value is coerced and typed. A `CASE` formula typed `CURRENCY` fails to render; type a sum of amounts `FLOAT` with `fieldType: 'currency'`. A formula cannot hold an aggregate of its own, so a ratio of two sums is two aggregates divided after the read. Formulas reach a joined record through its select field (`{terms.daysuntilnetdue}`, joined outer, so an invoice without terms still counts).\n- `list(...specifications)` applies specifications the way `RecordSet.list()` does, to a copy, so the same grouped query can run again with others. The model's own filters apply as always.\n- A grouped read selects only its keys and aggregates: no default columns, nothing loaded separately, nothing tracked. A key, an aggregate, or a condition on a relation declared `load: 'separate'` throws, and names the formula that reaches the field instead.\n- The groups come back sorted by their keys. `orderBy()` on a key sorts in N/query; on an aggregate alias it sorts in script once every group is read, since N/query renders a sort on an aggregate without the aggregate and the query fails. Blanks go where NetSuite puts them, last ascending and first descending, and groups that tie keep the keys' order. A page window set on the query (`limit()`, `page()`) is cut after sorting.\n- Every group comes back. `run()` answers at most 5,000 groups; past that the read picks up after the last group by its keys when each key can be compared (the id, numbers, dates, text), and reads every group through `runPaged` otherwise (a select field, display text, a formula, text on a record joined through a reference), under the same governance guard as `list()`.\n- `describe()` and `describeText()` show the grouping (`GROUP BY …`, and `SORT IN SCRIPT BY …` for a sort on an aggregate); `toSQL()` shows what NetSuite renders. The testing double records each column's `groupBy`.\n\nEach behaviour above was checked in a sandbox account on 2026-09-28.\n\n## Writes\n\nThe record updater chooses the cheapest NetSuite path for a change:\n\n- body fields only: one `record.submitFields` call;\n- subrecords or sublists: `record.load`, apply, `record.save`;\n- an address whose list field is set: clear the list field, save, reload, edit the subrecord, save.\n\n`plan()` on any updater describes the calls it would make, and options such as `requireFastPath`, `maxRecordCalls`, `allowLineScans`, and `allowSubrecordReloads` reject expensive plans before they run. `@RecordType('x', { updater })` sets the defaults for a record type; the `updater` option of `create()`, `update()`, and `delete()` and the `updaterOptions` of `saveChanges()` layer over them for one write. The updater trusts the model: a field id NetSuite does not accept surfaces as an `N/record` error at save time.\n\nThe record set exposes the updater in three layers. `create()`, `update()`, and `delete()` go through change tracking and throw. `add()`, `remove()`, and mutation wait for `saveChanges()`, which returns a result per entity. `updater(id)`, `submitPatch()`, `createRecord()`, and `deleteRecord()` call the updater directly with a graph patch and return results without throwing; reach for them when a failure is something to log rather than an error.\n\n## Testing\n\n`@amerilux/netsuite-repository/testing` is an in-memory stand-in for N/query. Map the module to it in jest, queue the rows a query should get, and assert on what was asked:\n\n```js\n// jest.config.js\nmoduleNameMapper: { '^N/query$': '<rootDir>/__tests__/netsuite-stubs/query.ts', ... }\n```\n\n```ts\n// __tests__/netsuite-stubs/query.ts\nimport { fakeNQuery } from '@amerilux/netsuite-repository/testing';\nexport = fakeNQuery;\n```\n\n```ts\nimport { fakeNQuery } from '@amerilux/netsuite-repository/testing';\n\nbeforeEach(() => fakeNQuery.reset());\n\nit('lists open orders for a customer', () => {\n    fakeNQuery.queueRows('salesorder', [{ id: 1, tranid: 'SO1', lines_id: 1, lines_quantity: 2 }]);\n\n    const orders = listOpenSalesOrders(db, 12);\n\n    expect(orders[0].lines).toHaveLength(1);\n    expect(fakeNQuery.calls[0].text).toContain(\"WHERE entity ANY_OF [12]\");\n});\n```\n\n`queueRows` matches the next query by root type, by a joined component, by a substring of the rendered text, or by a predicate; every queued set answers one query unless it repeats (`{ repeat: true }`). The double answers whatever is queued and has no 5,000-row limit: queue exactly 5,000 rows to make a list read on. `fakeNRuntime`, from the same entry point, stands in for `N/runtime`: map `N/runtime` to it and `queueRemainingUsage(1000, 990, 105)` to exercise the governance guard. Where `N/runtime` answers no usage, every read is affordable. `calls` records each query as a `QueryDescription` plus its rendered text and whether it ran, ran paged, or was rendered. The double implements the enums the runtime reads (`Operator`, `FieldContext`, `ReturnType`, `Aggregate`), so the same code runs against it and against NetSuite.\n\nThe double sees only what N/query sees, so it names joined components by the field ids it was given, not by the model's property paths: a sublist joined from `transactionline.transaction` is the component `transactionline.transaction`, a subrecord `shippingaddress` is `shippingaddress`, and a sublist filter lands in the `WHERE` line. `describeText()` on a query builder renders the same plan with the model's names (`lines`, `shippingAddress`) before anything runs; assert on it when the property path is what matters. Row keys are matched case-insensitively against the column aliases (`lines_priceLevelName` or `lines_pricelevelname` both work), and a separately loaded relation's rows carry the parent key under `__parentKey`.\n\n## Advanced: raw config\n\nEverything above compiles to a `QueryConfig`. Hand-written configs still work and can be mixed with generated ones in the same context:\n\n```ts\nexport const VendorConfig = defineQueryConfig<Vendor>({\n    recordType: 'vendor',\n    fields: {\n        id: { queryFieldId: 'id', type: 'integer', isPrimary: true, readonly: true },\n        companyName: { queryFieldId: 'companyname', type: 'string', recordFieldId: 'companyname' },\n    },\n});\n```\n\nA relation is a `components` entry (`path`, `join`, `load`, and any `conditions`), fields under it carry `component` and `nestPath`, and `relationships` describe subrecords, sublists, and references the same way the generated configs do. `fields` may be grouped into `query`, `common`, and `record` sections.\n\n## Package entry points\n\n| Import | Contents |\n| --- | --- |\n| `@amerilux/netsuite-repository` | Everything the runtime needs: decorators, query builder, record updater, context, tracking. |\n| `@amerilux/netsuite-repository/model` | The decorators and the registry the build step reads. |\n| `@amerilux/netsuite-repository/tracking` | `ChangeTracker`, `EntityState`, diff helpers. |\n| `@amerilux/netsuite-repository/testing` | The N/query test double. |\n| `@amerilux/netsuite-repository/cli` | `runGenerate`, `checkGenerated`, `createModelWatcher`, `runCli`, and the model compiler. Node only. |\n| `@amerilux/netsuite-repository/plugin` | `netsuiteRepositoryPlugin`. Node only. |\n\nThe runtime entry points never import Node modules, so the SuiteScript bundle stays free of build tooling.\n\n## Design notes\n\n`docs/entity-conventions-redesign.md` records why the surface looks the way it does: the three rules, the vocabulary, the decisions taken along the way, and the 1.0.0 move to the N/query object model.\n","readmeFilename":"README.md"}