{"_id":"@remix-run/data-table","_rev":"10-7c5323f119e50eb3572d97927e056196","name":"@remix-run/data-table","dist-tags":{"latest":"0.6.0"},"versions":{"0.0.0":{"name":"@remix-run/data-table","version":"0.0.0","keywords":["remix","placeholder"],"license":"MIT","_id":"@remix-run/data-table@0.0.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"},{"name":"ryanflorence","email":"rpflorence@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"dad9f6e07347020fa4579989e0558e01344d66a2","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.0.0.tgz","fileCount":2,"integrity":"sha512-i5W0T8FUjM1Du9TTMqtSTZ+ai4Mom5PyYD/8Kqn7ydRJ9O17iPGK+E7d+Z3phwtdvaUimyk5Do8W9EPn8wxZhg==","signatures":[{"sig":"MEUCIGuljWaCEvS9d11VnW+i4ho/rV0oEq4QHGx5mEXwQV+IAiEAvr44QwZOabUIUBvEql77ml023X5TZBJhUBpMvyWr6Yo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":607},"gitHead":"ef934f523767154caef1b3ab3c4e536f2e390141","private":false,"_npmUser":{"name":"mjackson","email":"mjijackson+npm@gmail.com"},"repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.8.0","description":"Placeholder package for future Remix data-table APIs","directories":{},"_nodeVersion":"24.2.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/data-table_0.0.0_1770854144943_0.08676605231546164","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@remix-run/data-table","version":"0.1.0","keywords":["remix","orm","sql","database","query-builder","relational"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/data-table@0.1.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"ad64dccd5a2266192a2f9e900a8d09d962f5885b","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.1.0.tgz","fileCount":43,"integrity":"sha512-u70INxiF9pThV2LcP7y37G7eq16OXWrA+HXS8qNplQgJJd1zhwZrODOtfF9U+PKLiWQkBj+bEeh7QhOlvGsVDg==","signatures":[{"sig":"MEQCIHPaIuEtxMJFNoHvFXrXXCMs9hgjRUe7H8Ng4N89XFduAiBft/7atfaVMnzaeFTTXFhIGRHJpnRylS1tND+W9/xEgQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":313412},"type":"module","_from":"file:remix-run-data-table-0.1.0.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"test":"node --disable-warning=ExperimentalWarning --test","build":"tsgo -p tsconfig.build.json","clean":"git clean -fdX","typecheck":"tsgo --noEmit","test:coverage":"node --disable-warning=ExperimentalWarning --experimental-test-coverage --test-coverage-include='src/lib/operators.ts' --test-coverage-include='src/lib/references.ts' --test-coverage-include='src/lib/inflection.ts' --test-coverage-include='src/lib/sql.ts' --test-coverage-include='src/lib/errors.ts' --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test ./src/lib/operators.test.ts ./src/lib/references.test.ts ./src/lib/inflection.test.ts ./src/lib/sql.test.ts ./src/lib/errors.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"_resolved":"/tmp/cafb77bfdc970d80b986d943c3b9860c/remix-run-data-table-0.1.0.tgz","_integrity":"sha512-u70INxiF9pThV2LcP7y37G7eq16OXWrA+HXS8qNplQgJJd1zhwZrODOtfF9U+PKLiWQkBj+bEeh7QhOlvGsVDg==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.10.1","description":"A typed, relational query toolkit for Remix","directories":{},"sideEffects":false,"_nodeVersion":"25.7.0","dependencies":{"@remix-run/data-schema":"^0.1.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^24.6.0","better-sqlite3":"^12.4.1","@types/better-sqlite3":"^7.6.13","@remix-run/data-schema":"0.1.0","@typescript/native-preview":"7.0.0-dev.20251125.1"},"_npmOperationalInternal":{"tmp":"tmp/data-table_0.1.0_1772239281205_0.6767360224302701","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@remix-run/data-table","version":"0.2.0","keywords":["remix","orm","sql","database","query-builder","relational"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/data-table@0.2.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"eeea1369a73b4ee51ea4d7b5261f9b505c72b6a9","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.2.0.tgz","fileCount":123,"integrity":"sha512-6QbdlKER0F0rX3XkTHC7ZPBgu/dyGvvgrKRbraBD9iGHTE7AED1emo3/YWsDJUeFzy7INNJaUIpNm9jNcgL8Kg==","signatures":[{"sig":"MEUCIHK1o9NZWZ5+InF49zZiWMV7TLmv1ghV0k/nNernDkwoAiEAuCR94CP/oFcmOAKu0wIDWZ3DUF6EESoDAc0ntUwJhjU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":588345},"type":"module","_from":"file:remix-run-data-table-0.2.0.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./operators":{"types":"./dist/operators.d.ts","default":"./dist/operators.js"},"./migrations":{"types":"./dist/migrations.d.ts","default":"./dist/migrations.js"},"./sql-helpers":{"types":"./dist/sql-helpers.d.ts","default":"./dist/sql-helpers.js"},"./package.json":"./package.json","./migrations/node":{"types":"./dist/migrations/node.d.ts","default":"./dist/migrations/node.js"}},"scripts":{"test":"node --test","build":"tsgo -p tsconfig.build.json","clean":"git clean -fdX","typecheck":"tsgo --noEmit","test:coverage":"pnpm run test:coverage:core && pnpm run test:coverage:migrations","test:coverage:core":"node --experimental-test-coverage --test-coverage-include='src/lib/operators.ts' --test-coverage-include='src/lib/references.ts' --test-coverage-include='src/lib/inflection.ts' --test-coverage-include='src/lib/sql.ts' --test-coverage-include='src/lib/errors.ts' --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test ./src/lib/operators.test.ts ./src/lib/references.test.ts ./src/lib/inflection.test.ts ./src/lib/sql.test.ts ./src/lib/errors.test.ts","test:coverage:migrations":"node --experimental-test-coverage --test-coverage-include='src/lib/migrations.ts' --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test ./src/lib/migrations.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"_resolved":"/tmp/e0d7773001de21c928351b1e561504c4/remix-run-data-table-0.2.0.tgz","_integrity":"sha512-6QbdlKER0F0rX3XkTHC7ZPBgu/dyGvvgrKRbraBD9iGHTE7AED1emo3/YWsDJUeFzy7INNJaUIpNm9jNcgL8Kg==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.9.0","description":"A typed, relational query toolkit for Remix","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^24.6.0","better-sqlite3":"^12.4.1","@types/better-sqlite3":"^7.6.13","@typescript/native-preview":"7.0.0-dev.20251125.1"},"_npmOperationalInternal":{"tmp":"tmp/data-table_0.2.0_1774479806549_0.2967521235054096","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@remix-run/data-table","version":"0.2.1","keywords":["remix","orm","sql","database","query-builder","relational"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/data-table@0.2.1","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"88c3bfcff61571b2493b1a516b797d88b61d0632","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.2.1.tgz","fileCount":123,"integrity":"sha512-p7LFOzgSmRws6U7N2karl+t85J/8jKtUjtKb3XxvCBWEwJk2XZ2nVhhhmamdN5+XEzgFK/KGnNyRtdu114v7sA==","signatures":[{"sig":"MEUCIGpxIyv21+yNJjn1+5SU1s/OTGnsy4hKilePYALEz4TtAiEA8UT4RQ6YOyEhwQIGFFkgoMIRI2TsJYU0BQ81xcZ31bA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":587620},"type":"module","_from":"file:remix-run-data-table-0.2.1.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./operators":{"types":"./dist/operators.d.ts","default":"./dist/operators.js"},"./migrations":{"types":"./dist/migrations.d.ts","default":"./dist/migrations.js"},"./sql-helpers":{"types":"./dist/sql-helpers.d.ts","default":"./dist/sql-helpers.js"},"./package.json":"./package.json","./migrations/node":{"types":"./dist/migrations/node.d.ts","default":"./dist/migrations/node.js"}},"scripts":{"test":"remix-test","build":"tsgo -p tsconfig.build.json","clean":"git clean -fdX","test:bun":"bun x --bun remix-test","typecheck":"tsgo --noEmit","test:coverage":"remix-test --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"_resolved":"/tmp/3e615fca9da2102b8dde1e251ceedbda/remix-run-data-table-0.2.1.tgz","_integrity":"sha512-p7LFOzgSmRws6U7N2karl+t85J/8jKtUjtKb3XxvCBWEwJk2XZ2nVhhhmamdN5+XEzgFK/KGnNyRtdu114v7sA==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.11.0","description":"A typed, relational query toolkit for JavaScript","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^24.6.0","@remix-run/test":"0.3.0","@remix-run/assert":"0.2.0","@typescript/native-preview":"7.0.0-dev.20251125.1"},"_npmOperationalInternal":{"tmp":"tmp/data-table_0.2.1_1777576254313_0.2848604971420432","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@remix-run/data-table","version":"0.3.0","keywords":["remix","orm","sql","database","query-builder","relational"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/data-table@0.3.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"be99e2cc589b6fd0ab25fa8205ea52552fcebad9","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.3.0.tgz","fileCount":119,"integrity":"sha512-2Ikjlm3T2qtMTeWm5v/76iXEP2fqqdJGxjTZ6OFre5b5BFoRyaq/iBmDc0lkXPoOGvkeOxNuRo7L0dRkMTXwJQ==","signatures":[{"sig":"MEUCIG6CfgO4PYsmk8izwTlLUAjcImP9xlkNlHfdRIXL0JlRAiEAvjs8SE/ZATxJjNgZAiYInfNfdy/AK5WKBon+gpEuLIA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":520881},"type":"module","_from":"file:remix-run-data-table-0.3.0.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./operators":{"types":"./dist/operators.d.ts","default":"./dist/operators.js"},"./migrations":{"types":"./dist/migrations.d.ts","default":"./dist/migrations.js"},"./sql-helpers":{"types":"./dist/sql-helpers.d.ts","default":"./dist/sql-helpers.js"},"./package.json":"./package.json","./migrations/node":{"types":"./dist/migrations/node.d.ts","default":"./dist/migrations/node.js"}},"scripts":{"test":"remix-test","build":"tsgo -p tsconfig.build.json","clean":"git clean -fdX","test:bun":"bun x --bun remix-test","typecheck":"tsgo --noEmit","test:coverage":"remix-test --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"_resolved":"/tmp/5fa0047f5e4be80fed574439161d7640/remix-run-data-table-0.3.0.tgz","_integrity":"sha512-2Ikjlm3T2qtMTeWm5v/76iXEP2fqqdJGxjTZ6OFre5b5BFoRyaq/iBmDc0lkXPoOGvkeOxNuRo7L0dRkMTXwJQ==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.12.1","description":"A typed, relational query toolkit for JavaScript","directories":{},"sideEffects":false,"_nodeVersion":"24.15.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"^24.6.0","@remix-run/test":"0.4.0","@remix-run/assert":"0.2.0","@typescript/native-preview":"7.0.0-dev.20251125.1"},"_npmOperationalInternal":{"tmp":"tmp/data-table_0.3.0_1779247242936_0.5939246029413146","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@remix-run/data-table","version":"0.4.0","keywords":["remix","orm","sql","database","query-builder","relational"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/data-table@0.4.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"ba7ccd3bae74ef1531913fc28b5ae5465f69cfda","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.4.0.tgz","fileCount":123,"integrity":"sha512-Flh2PP85hhjNZikxaQvdsrgna3Qv1OWJ/l7RTAKD+K9YdpVL+VnJMOKjyiMzehNBPuCVZB9ST7dF+5A/nfVXtg==","signatures":[{"sig":"MEQCIHaRf+wpAarbn5nvY7sSGrDjbSUqZFt2r/TOVzm73ZBhAiAJVgxR5kGCP+Zw5t4YOUw4KZsDINGoTfGfCOOBO/OIQA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":552949},"type":"module","_from":"file:remix-run-data-table-0.4.0.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist/cli.d.ts","default":"./dist/cli.js"},"./operators":{"types":"./dist/operators.d.ts","default":"./dist/operators.js"},"./migrations":{"types":"./dist/migrations.d.ts","default":"./dist/migrations.js"},"./sql-helpers":{"types":"./dist/sql-helpers.d.ts","default":"./dist/sql-helpers.js"},"./package.json":"./package.json","./migrations/node":{"types":"./dist/migrations/node.d.ts","default":"./dist/migrations/node.js"}},"scripts":{"test":"remix test","build":"tsc -p tsconfig.build.json","clean":"git clean -fdX","test:bun":"bun x --bun remix test","typecheck":"tsc --noEmit","test:coverage":"remix test --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"_resolved":"/tmp/47fcbffb45fe993159840b5e7fccdaa2/remix-run-data-table-0.4.0.tgz","_integrity":"sha512-Flh2PP85hhjNZikxaQvdsrgna3Qv1OWJ/l7RTAKD+K9YdpVL+VnJMOKjyiMzehNBPuCVZB9ST7dF+5A/nfVXtg==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.16.0","description":"A typed, relational query toolkit for JavaScript","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^24.6.0","@remix-run/test":"0.6.0","@remix-run/assert":"0.3.0"},"_npmOperationalInternal":{"tmp":"tmp/data-table_0.4.0_1786666062415_0.2567597029776414","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@remix-run/data-table","version":"0.5.0","keywords":["remix","orm","sql","database","query-builder","relational"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/data-table@0.5.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"7717126015819119663c41719084c8373d8a7435","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.5.0.tgz","fileCount":123,"integrity":"sha512-ntc23qDYnSCA2iq1r2L5+u/9MH+5207mopekOM4NRsVDrMt0e14DVq37JhfVmzcZDCb8lx7iP+D+H8i9a9IiCw==","signatures":[{"sig":"MEQCIFtRC+ytemTUVO3z9Fsif90fXbav9krpDuKMDB65Dw4IAiBsDtmdAHqllhkRCiQ6+/mVySFLPJN15oGqowsCfNtygg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":558475},"type":"module","_from":"file:/tmp/remix-publish-data-table-NKbfEW/remix-run-data-table-0.5.0.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist/cli.d.ts","default":"./dist/cli.js"},"./operators":{"types":"./dist/operators.d.ts","default":"./dist/operators.js"},"./migrations":{"types":"./dist/migrations.d.ts","default":"./dist/migrations.js"},"./sql-helpers":{"types":"./dist/sql-helpers.d.ts","default":"./dist/sql-helpers.js"},"./package.json":"./package.json","./migrations/node":{"types":"./dist/migrations/node.d.ts","default":"./dist/migrations/node.js"}},"scripts":{"test":"remix test","build":"tsc -p tsconfig.build.json","clean":"git clean -fdX","test:bun":"bun x --bun remix test","typecheck":"tsc --noEmit","test:coverage":"remix test --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"_resolved":"/tmp/remix-publish-data-table-NKbfEW/remix-run-data-table-0.5.0.tgz","_integrity":"sha512-ntc23qDYnSCA2iq1r2L5+u/9MH+5207mopekOM4NRsVDrMt0e14DVq37JhfVmzcZDCb8lx7iP+D+H8i9a9IiCw==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.17.0","description":"A typed, relational query toolkit for JavaScript","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^24.6.0","@remix-run/test":"0.6.0","@remix-run/assert":"0.3.0"},"_npmOperationalInternal":{"tmp":"tmp/data-table_0.5.0_1788202623902_0.2833805241954255","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@remix-run/data-table","version":"0.5.1","keywords":["remix","orm","sql","database","query-builder","relational"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","_id":"@remix-run/data-table@0.5.1","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"c1e45c8b201d8ab81bae1abf98a62971622dc785","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.5.1.tgz","fileCount":123,"integrity":"sha512-AbGRuT25urh2l61iVHmaREFmke74GxfGNyxih7wdqKWXHO4zlqfkWE4NuY1BkRWeLeIT7YXdgpsN4jdGAAj5TQ==","signatures":[{"sig":"MEUCIQDwFjllY4y/AjSDaNXo17Mj+fiX6jR7rf1z7J7m5ntPuAIgDejy0fdYC6RtW+aQOEiPakH4XD41044kKk774XOmPHc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIDn7ppQaJZ9GEPMVEyGxpnzSi5ojAIHVM57WNT8cEB0IAiBsO/f+L1jeR0tfgzfH0QzW00aJBIpPYVlcpcGre8OtCQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":559893},"type":"module","_from":"file:/tmp/remix-publish-data-table-jahLUP/remix-run-data-table-0.5.1.tgz","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist/cli.d.ts","default":"./dist/cli.js"},"./operators":{"types":"./dist/operators.d.ts","default":"./dist/operators.js"},"./migrations":{"types":"./dist/migrations.d.ts","default":"./dist/migrations.js"},"./sql-helpers":{"types":"./dist/sql-helpers.d.ts","default":"./dist/sql-helpers.js"},"./package.json":"./package.json","./migrations/node":{"types":"./dist/migrations/node.d.ts","default":"./dist/migrations/node.js"}},"scripts":{"test":"remix test","build":"tsc -p tsconfig.build.json","clean":"git clean -fdX","test:bun":"bun x --bun remix test","typecheck":"tsc --noEmit","test:coverage":"remix test --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"_resolved":"/tmp/remix-publish-data-table-jahLUP/remix-run-data-table-0.5.1.tgz","_integrity":"sha512-AbGRuT25urh2l61iVHmaREFmke74GxfGNyxih7wdqKWXHO4zlqfkWE4NuY1BkRWeLeIT7YXdgpsN4jdGAAj5TQ==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.19.0","description":"A typed, relational query toolkit for JavaScript","directories":{},"sideEffects":false,"_nodeVersion":"24.20.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^24.6.0","@remix-run/test":"0.6.0","@remix-run/assert":"0.3.0"},"_npmOperationalInternal":{"tmp":"tmp/data-table_0.5.1_1788908697758_0.9428925022683436","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"_id":"@remix-run/data-table@0.6.0","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"7ae4f8d6bc425e64f49475e58de2435126931b17","tarball":"https://registry.npmjs.org/@remix-run/data-table/-/data-table-0.6.0.tgz","fileCount":123,"integrity":"sha512-rVF3dg+t6DtQ1W+yRcuuTb6g7TA9qqw5/++ztAK7CFviG0H+1LZnoOQeUonDApXaU9eevCvrZwDrLgb5Gb1p+Q==","signatures":[{"sig":"MEUCIBBUFj7C6eVxayCQCgMWTod8+qQSfMWHyEWngKh+FCHqAiEAyHS9lXWsiU1Bypd/DJXn0YCbtVQFQI5yF5rANiC6Emo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFoiHT6s2wqOhs0J7yBCjAetTH6Y3lv783RwuDCPijIPAiA/LJKMG8RrvRfRluvQQIpERozkS1QJxKOXjGruoUm2nQ=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fdata-table@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":562291},"name":"@remix-run/data-table","type":"module","_from":"file:/tmp/remix-publish-data-table-0i2hAa/remix-run-data-table-0.6.0.tgz","author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./cli":{"types":"./dist/cli.d.ts","default":"./dist/cli.js"},"./operators":{"types":"./dist/operators.d.ts","default":"./dist/operators.js"},"./migrations":{"types":"./dist/migrations.d.ts","default":"./dist/migrations.js"},"./sql-helpers":{"types":"./dist/sql-helpers.d.ts","default":"./dist/sql-helpers.js"},"./package.json":"./package.json","./migrations/node":{"types":"./dist/migrations/node.d.ts","default":"./dist/migrations/node.js"}},"license":"MIT","scripts":{"test":"remix test","build":"tsc -p tsconfig.build.json","clean":"git clean -fdX","test:bun":"bun x --bun remix test","typecheck":"tsc --noEmit","test:coverage":"remix test --coverage"},"version":"0.6.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a893f9c8-8a08-4ceb-9c15-6f2a37bb8360"}},"homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","keywords":["remix","orm","sql","database","query-builder","relational"],"_resolved":"/tmp/remix-publish-data-table-0i2hAa/remix-run-data-table-0.6.0.tgz","_integrity":"sha512-rVF3dg+t6DtQ1W+yRcuuTb6g7TA9qqw5/++ztAK7CFviG0H+1LZnoOQeUonDApXaU9eevCvrZwDrLgb5Gb1p+Q==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"_npmVersion":"11.19.0","description":"A typed, relational query toolkit for JavaScript","directories":{},"maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.20.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^24.6.0","@remix-run/test":"0.6.1","@remix-run/assert":"0.3.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/data-table_0.6.0_1789770807548_0.3122393390622238"}}},"time":{"created":"2026-02-11T23:55:44.825Z","modified":"2026-09-18T22:33:28.008Z","0.0.0":"2026-02-11T23:55:45.082Z","0.1.0":"2026-02-28T00:41:21.357Z","0.2.0":"2026-03-25T23:03:26.682Z","0.2.1":"2026-04-30T19:10:54.496Z","0.3.0":"2026-05-20T03:20:43.129Z","0.4.0":"2026-08-14T00:07:42.562Z","0.5.0":"2026-08-31T18:57:04.083Z","0.5.1":"2026-09-08T23:04:57.872Z","0.6.0":"2026-09-18T22:33:27.644Z"},"bugs":{"url":"https://github.com/remix-run/remix/issues"},"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"license":"MIT","homepage":"https://github.com/remix-run/remix/tree/main/packages/data-table#readme","keywords":["remix","orm","sql","database","query-builder","relational"],"repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/data-table"},"description":"A typed, relational query toolkit for JavaScript","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"readme":"# data-table\n\nTyped relational query toolkit for JavaScript runtimes.\n\n## Features\n\n- **One API Across Databases**: Same query and relation APIs across PostgreSQL, MySQL, and SQLite\n- **One Query API**: Build reusable `Query` objects with `query(table)` and execute them with `db.exec(...)`, or use `db.query(table)` as shorthand\n- **Type-Safe Reads**: Typed `select`, relation loading, and predicate keys\n- **Optional Runtime Validation**: Add `validate(context)` at the table level for create/update validation and coercion\n- **Relation-First Queries**: `hasMany`, `hasOne`, `belongsTo`, `hasManyThrough`, and nested eager loading\n- **Safe Scoped Writes**: `update`/`delete` with `orderBy`/`limit` run safely in a transaction\n- **First-Class Migrations**: Plain SQL `up.sql`/`down.sql` files with a journaling runner and dry-run planning\n- **Database Lifecycle Commands**: Wipe, migrate, rollback, inspect, seed, and reset through `remix db`\n- **Raw SQL Escape Hatch**: Execute SQL directly with `db.exec(sql\\`...\\`)`\n\n`data-table` gives you two complementary APIs:\n\n- [**Query Objects**](#query-objects) for expressive joins, aggregates, eager loading, and scoped writes\n- [**CRUD Helpers**](#crud-helpers) for common create/read/update/delete flows (`find`, `create`, `update`, `delete`)\n\nBoth APIs are type-safe. Runtime validation is opt-in with table-level `validate(context)`.\n\n## Installation\n\n```sh\nnpm i remix\nnpm i pg\n# or\nnpm i mysql2\n# or\n# use the SQLite client built into your runtime\n```\n\n## Setup\n\nDefine tables once, then create a database for your SQL dialect.\n\n```ts\nimport { column as c, hasMany, query, table } from 'remix/data-table'\nimport { createPostgresDatabase } from 'remix/data-table/postgres'\n\nlet users = table({\n  name: 'users',\n  columns: {\n    id: c.uuid(),\n    email: c.varchar(255),\n    role: c.enum(['customer', 'admin']),\n    created_at: c.integer(),\n  },\n})\n\nlet orders = table({\n  name: 'orders',\n  columns: {\n    id: c.uuid(),\n    user_id: c.uuid(),\n    status: c.enum(['pending', 'processing', 'shipped', 'delivered']),\n    total: c.decimal(10, 2),\n    created_at: c.integer(),\n  },\n})\n\nlet userOrders = hasMany(users, orders)\n\nlet db = createPostgresDatabase({ connectionString: process.env.DATABASE_URL })\n```\n\n## Query Objects\n\nUse `query(table)` when you want to build a standalone reusable query object. Execute it later with `db.exec(query)`. Use `db.query(table)` when you want the same chainable `Query` already bound to a database instance.\n\n### Standalone Query Builder\n\n`query(table)` is the primary query-builder API. It gives you an unbound `Query` value that can be composed, stored, reused, and executed against any compatible database instance.\n\n```ts\nimport { eq, ilike, query } from 'remix/data-table'\n\nlet pendingOrdersForExampleUsers = query(orders)\n  .join(users, eq(orders.user_id, users.id))\n  .where({ status: 'pending' })\n  .where(ilike(users.email, '%@example.com'))\n  .select({\n    orderId: orders.id,\n    customerEmail: users.email,\n    total: orders.total,\n    placedAt: orders.created_at,\n  })\n  .orderBy(orders.created_at, 'desc')\n  .limit(20)\n\nlet recentPendingOrders = await db.exec(pendingOrdersForExampleUsers)\n```\n\nUnbound queries stay lazy until you pass them to `db.exec(...)`:\n\n```ts\nlet shippedCustomerQuery = query(users)\n  .where({ role: 'customer' })\n  .with({\n    recentOrders: userOrders.where({ status: 'shipped' }).orderBy('created_at', 'desc').limit(3),\n  })\n\nlet customers = await db.exec(shippedCustomerQuery)\n\n// customers[0].recentOrders is fully typed\n```\n\nThe same standalone query builder also handles terminal read and write operations:\n\n```ts\nlet nextPendingOrder = await db.exec(\n  query(orders).where({ status: 'pending' }).orderBy('created_at', 'asc').first(),\n)\n\nawait db.exec(\n  query(orders)\n    .where({ status: 'pending' })\n    .orderBy('created_at', 'asc')\n    .limit(100)\n    .update({ status: 'processing' }),\n)\n```\n\n### Bound Query Shorthand\n\nIf you already have a `db` instance in hand and do not need a standalone query value, `db.query(table)` returns the same query builder already bound to that database:\n\n```ts\nlet recentPendingOrders = await db\n  .query(orders)\n  .where({ status: 'pending' })\n  .orderBy('created_at', 'desc')\n  .limit(20)\n  .all()\n```\n\n## CRUD Helpers\n\n`data-table` provides helpers for common create/read/update/delete operations. Use these helpers for common operations without building a full query chain.\n\n### Read operations\n\n```ts\nimport { or } from 'remix/data-table'\n\nlet user = await db.find(users, 'u_001')\n\nlet firstPending = await db.findOne(orders, {\n  where: { status: 'pending' },\n  orderBy: ['created_at', 'asc'],\n})\n\nlet page = await db.findMany(orders, {\n  where: or({ status: 'pending' }, { status: 'processing' }),\n  orderBy: [\n    ['status', 'asc'],\n    ['created_at', 'desc'],\n  ],\n  limit: 50,\n  offset: 0,\n})\n```\n\n`where` accepts the same single-table object/predicate inputs as `query().where(...)`, and `orderBy` uses tuple form:\n\n- `['column', 'asc' | 'desc']`\n- `[['columnA', 'asc'], ['columnB', 'desc']]`\n\n### Create helpers\n\n```ts\n// Default: metadata (affectedRows/insertId)\nlet createResult = await db.create(users, {\n  id: 'u_002',\n  email: 'sam@example.com',\n  role: 'customer',\n  created_at: Date.now(),\n})\n\n// Return a typed row (with optional relations)\nlet createdUser = await db.create(\n  users,\n  {\n    id: 'u_003',\n    email: 'pat@example.com',\n    role: 'customer',\n    created_at: Date.now(),\n  },\n  {\n    returnRow: true,\n    with: { recentOrders: userOrders.orderBy('created_at', 'desc').limit(1) },\n  },\n)\n\n// Bulk insert metadata\nlet createManyResult = await db.createMany(orders, [\n  { id: 'o_101', user_id: 'u_002', status: 'pending', total: 24.99, created_at: Date.now() },\n  { id: 'o_102', user_id: 'u_003', status: 'pending', total: 48.5, created_at: Date.now() },\n])\n\n// Return inserted rows (requires database RETURNING support)\nlet insertedRows = await db.createMany(\n  orders,\n  [{ id: 'o_103', user_id: 'u_003', status: 'pending', total: 12, created_at: Date.now() }],\n  { returnRows: true },\n)\n```\n\n`createMany`/`insertMany` throw when every row in the batch is empty (no explicit values).\n\n### Update and delete helpers\n\n```ts\nlet updatedUser = await db.update(users, 'u_003', { role: 'admin' })\n\nlet updateManyResult = await db.updateMany(\n  orders,\n  { status: 'processing' },\n  {\n    where: { status: 'pending' },\n    orderBy: ['created_at', 'asc'],\n    limit: 25,\n  },\n)\n\nlet deletedUser = await db.delete(users, 'u_002')\n\nlet deleteManyResult = await db.deleteMany(orders, {\n  where: { status: 'delivered' },\n  orderBy: [['created_at', 'asc']],\n  limit: 200,\n})\n```\n\n`db.update(...)` throws when the target row cannot be found.\n\nReturn behavior:\n\n- `find`/`findOne` -> row or `null`\n- `findMany` -> rows\n- `create` -> `WriteResult` by default, row when `returnRow: true`\n- `createMany` -> `WriteResult` by default, rows when `returnRows: true` (not supported in MySQL because it doesn't support `RETURNING`)\n- `update` -> updated row (throws when target row is missing)\n- `updateMany`/`deleteMany` -> `WriteResult`\n- `delete` -> `boolean`\n\n### Validation and Lifecycle\n\nValidation is optional and table-scoped. Define `validate(context)` to validate/coerce write payloads, and add lifecycle callbacks when you need custom read/write/delete behavior.\n\n```ts\nimport { column as c, fail, table } from 'remix/data-table'\n\nlet payments = table({\n  name: 'payments',\n  columns: {\n    id: c.uuid(),\n    amount: c.decimal(10, 2),\n  },\n  beforeWrite({ value }) {\n    return {\n      value: {\n        ...value,\n        amount: typeof value.amount === 'string' ? value.amount.trim() : value.amount,\n      },\n    }\n  },\n  validate({ operation, value }) {\n    if (operation === 'create' && typeof value.amount === 'string') {\n      let amount = Number(value.amount)\n\n      if (!Number.isFinite(amount)) {\n        return fail('Expected a numeric amount', ['amount'])\n      }\n\n      return { value: { ...value, amount } }\n    }\n\n    return { value }\n  },\n  beforeDelete({ where }) {\n    if (where.length === 0) {\n      return fail('Refusing unscoped delete')\n    }\n  },\n  afterRead({ value }) {\n    if (!('amount' in value)) {\n      return { value }\n    }\n\n    return {\n      value: {\n        ...value,\n        // Example read-time shaping\n        amount:\n          typeof value.amount === 'number' ? Math.round(value.amount * 100) / 100 : value.amount,\n      },\n    }\n  },\n})\n```\n\nUse `fail(...)` in hooks when you want to return issues without manually building `{ issues: [...] }`.\n\nValidation and lifecycle semantics:\n\n- Write order is `beforeWrite -> validate -> timestamp/default touch -> execute -> afterWrite`\n- `validate` runs for writes (`create`, `createMany`, `insert`, `insertMany`, `update`, `updateMany`, `upsert`)\n- Hook context includes `{ operation: 'create' | 'update', tableName, value }`\n- Write payloads are partial objects\n- Unknown columns fail validation before and after hook processing\n- `beforeDelete` can veto deletes by returning `{ issues }`\n- `afterDelete` runs after successful deletes with `affectedRows`\n- `afterRead` runs for each loaded row (root rows, eager-loaded relation rows, and write-returning rows)\n- `afterRead` receives the current read shape, which may be partial/projection rows; guard field access accordingly\n- Predicate values (`where`, `having`, join predicates) are not runtime-validated\n- Lifecycle callbacks are synchronous; returning a Promise throws a validation error\n- Callback validation errors include `metadata.source` (`beforeWrite`, `validate`, `beforeDelete`, `afterRead`, etc.) for easier debugging\n- Callbacks do not introduce implicit transactions (use `db.transaction(...)` when you need rollback guarantees)\n\n## Transactions\n\n```ts\nawait db.transaction(async (tx) => {\n  let user = await tx.create(\n    users,\n    { id: 'u_010', email: 'new@example.com', role: 'customer', created_at: Date.now() },\n    { returnRow: true },\n  )\n\n  await tx.create(orders, {\n    id: 'o_500',\n    user_id: user.id,\n    status: 'pending',\n    total: 79,\n    created_at: Date.now(),\n  })\n})\n```\n\n## Migrations\n\n`data-table` ships a SQL-first migration system under `remix/data-table/migrations`. Each migration is a directory containing hand-written `up.sql` and (optionally) `down.sql`. The runner journals applied migrations, detects checksum drift and missing applied migrations, and wraps each migration in a transaction when the database supports transactional DDL.\n\n### Example Setup\n\n```txt\napp/\n  db/\n    migrations/\n      20260228090000_create_users/\n        up.sql\n        down.sql\n      20260301113000_add_user_status/\n        up.sql\n        down.sql\n  db.ts\n```\n\n- Keep migration directories in one parent directory (for example `app/db/migrations`).\n- Each directory is named `YYYYMMDDHHmmss_<slug>`.\n- `up.sql` is required. `down.sql` is optional (omit for irreversible migrations).\n- Scripts may contain multiple statements. `id` and `name` are inferred from the directory name.\n\n### Migration File Example\n\n`20260228090000_create_users/up.sql`:\n\n```sql\ncreate table users (\n  id serial primary key,\n  email varchar(255) not null unique,\n  created_at timestamptz not null default now()\n);\n\ncreate unique index users_email_idx on users (email);\n```\n\n`20260228090000_create_users/down.sql`:\n\n```sql\ndrop index if exists users_email_idx;\ndrop table if exists users;\n```\n\n### Multi-Statement Driver Configuration\n\nThe runner sends each migration to the database as a single multi-statement script. Make sure the underlying driver accepts multiple statements:\n\n- `better-sqlite3`: works out of the box (`db.exec`).\n- `pg`: works out of the box when no parameter array is passed.\n- `mysql2`: requires `multipleStatements: true` on the connection/pool.\n\n```ts\nimport { createMysqlDatabase } from 'remix/data-table/mysql'\n\nlet db = createMysqlDatabase({\n  uri: process.env.DATABASE_URL,\n  multipleStatements: true,\n})\n```\n\n### Database Command Configuration\n\nConfigure `remix db` statically in `remix.json`. Connection secrets are read from the named\nenvironment variable at command runtime, and paths are resolved relative to `remix.json`:\n\n```jsonc\n{\n  \"$schema\": \"./node_modules/remix/schema/remix.json\",\n  \"db\": {\n    \"adapter\": {\n      \"type\": \"postgres\",\n      \"connectionString\": { \"env\": \"DATABASE_URL\" },\n    },\n    \"migrations\": {\n      \"directory\": \"./db/migrations\",\n      \"journalTable\": \"app_migrations\",\n    },\n    \"seed\": \"./db/seed.sql\",\n  },\n}\n```\n\nApplication runtime setup remains application-owned. It does not need to expose any special\nexports for the CLI.\n\nRun lifecycle commands through the Remix CLI:\n\n```sh\nremix db status\nremix db migrate\nremix db migrate --to 20260301113000_add_user_status\nremix db rollback\nremix db rollback --step 2\nremix db rollback --to 20260301113000_add_user_status\nremix db rollback --dry-run\nremix db seed\nremix db reset --force\nremix db wipe --force\n```\n\n`rollback` reverts the most recently applied migration by default. Bound it with either `--step <count>` or `--to <migration>`, which reverts through the target migration inclusively. Migration targets accept a bare migration id (`20260301113000`) or the full directory name (`20260301113000_add_user_status`). Use `--dry-run` to report what would be reverted without changing the database.\n\n`remix db status` reports applied migrations whose files are no longer present as `missing`. If the journal table does not exist, it reports every migration as pending without creating the table. Forward migration runs stop before executing SQL when an applied journal entry is missing from the current migration set. Rollbacks skip those orphaned journal entries so migrations that are still present can be reverted.\n\n`wipe` and `reset` are destructive. They require a config-backed database so it can close, recreate, and reconnect to the configured database.\n\n### Programmatic Migrations\n\nLoad migrations and pass the resolved collection directly to the database:\n\n```ts\nimport { loadMigrations } from 'remix/data-table/migrations/node'\n\nlet migrations = await loadMigrations('./db/migrations')\nawait db.migrate(migrations)\n```\n\n`Database.migrate()` supports forward and backward directions, a target or step bound, dry runs,\nand a custom journal table:\n\n```ts\nawait db.migrate(migrations)\nawait db.migrate(migrations, { to: '20260301113000_add_user_status' })\nawait db.migrate(migrations, { step: 1 })\nawait db.migrate(migrations, { direction: 'down' })\nawait db.migrate(migrations, { direction: 'down', to: '20260301113000' })\nawait db.migrate(migrations, { direction: 'down', step: 1 })\nawait db.migrate(migrations, { journalTable: 'app_migrations' })\n\nlet plan = await db.migrate(migrations, { dryRun: true })\nfor (let script of plan.sql) console.log(script)\n```\n\n`to` and `step` are mutually exclusive. Omit `journalTable` to use `data_table_migrations`.\n\nHosts that embed the database CLI can invoke the same rollback behavior through `runRemixDb()`:\n\n```ts\nimport { runRemixDb } from 'remix/data-table/cli'\n\nawait runRemixDb({ command: 'rollback', db, migrations })\nawait runRemixDb({ command: 'rollback', db, migrations, step: 2, dryRun: true })\n```\n\nDatabase drivers with migration locking run the complete migration and journal lifecycle through the\nconnection that owns the lock. This keeps advisory locks correctly paired when the driver uses a\nconnection pool, including pools configured with a single connection.\n\nRead status separately, or rebuild a database with migrations and an optional seed. A seed is a\nfunction that receives the database; `loadSeed()` builds one from a SQL file:\n\n```ts\nimport { loadSeed } from 'remix/data-table/migrations/node'\n\nlet seed = await loadSeed('./db/seed.sql')\n\nlet status = await db.migrationStatus(migrations, { journalTable: 'app_migrations' })\nawait db.reset({ migrations })\nawait db.reset({ migrations, seed })\nawait db.reset({ migrations, seed, journalTable: 'app_migrations' })\n```\n\nWhen a lifecycle command is the last thing a process does, close database-owned connections so\nthe process can exit:\n\n```ts\nawait db.close()\n```\n\n### Transaction Modes\n\nBy default each migration is wrapped in a transaction when the database supports transactional DDL. Override per migration with a directive on the first non-blank line of `up.sql`:\n\n```sql\n-- data-table/transaction: none\ncreate index concurrently users_email_active_idx on users (email) where status = 'active';\n```\n\nSupported modes:\n\n- `auto` (default): wrap when the database supports transactional DDL.\n- `required`: wrap; the runner throws if the database cannot support it.\n- `none`: never wrap. Use this for statements like postgres `CREATE INDEX CONCURRENTLY` that cannot run inside a transaction.\n\nYou can also set `transaction` directly on a `MigrationDescriptor` when registering migrations programmatically.\n\n### Programmatic Registration\n\nFor non-filesystem runtimes, register migrations directly:\n\n```ts\nimport { createMigrationRegistry } from 'remix/data-table/migrations'\n\nlet registry = createMigrationRegistry()\nregistry.register({\n  id: '20260228090000',\n  name: 'create_users',\n  up: 'create table users (id serial primary key, email text not null);',\n  down: 'drop table users;',\n})\n\nawait db.migrate(registry)\n```\n\n## Raw SQL Escape Hatch\n\n```ts\nimport { rawSql, sql } from 'remix/data-table'\n\nawait db.exec(sql`select * from users where id = ${'u_001'}`)\nawait db.exec(rawSql('update users set role = ? where id = ?', ['admin', 'u_001']))\n```\n\nUse `sql` when you need raw SQL plus safe value interpolation:\n\n```ts\nimport { sql } from 'remix/data-table'\n\nlet email = input.email\nlet minCreatedAt = input.minCreatedAt\n\nlet result = await db.exec(sql`\n  select id, email\n  from users\n  where email = ${email}\n    and created_at >= ${minCreatedAt}\n`)\n```\n\n`sql` keeps values parameterized for the database dialect, so you can avoid manual string concatenation.\n\n## Custom Database Drivers\n\nApplications normally use one of the concrete SQLite, PostgreSQL, or MySQL factories. Integration\npackages can add another dialect by implementing `DatabaseDriver` and extending `Database`. This\ncomplete skeleton assumes the underlying client exposes the operations needed by the driver:\n\n```ts\nimport {\n  Database,\n  type DatabaseDriver,\n  type DataManipulationRequest,\n  type DataManipulationResult,\n  type DatabaseOptions,\n  type TableRef,\n  type TransactionOptions,\n  type TransactionToken,\n} from 'remix/data-table'\nimport type { AcmeClient } from 'acme-database'\n\nclass AcmeDriver implements DatabaseDriver<'acme'> {\n  readonly dialect = 'acme'\n  readonly capabilities = {\n    returning: true,\n    savepoints: true,\n    upsert: true,\n    transactionalDdl: true,\n    migrationLock: false,\n  } as const\n\n  #client: AcmeClient\n\n  constructor(client: AcmeClient) {\n    this.#client = client\n  }\n\n  execute(request: DataManipulationRequest): Promise<DataManipulationResult> {\n    return this.#client.execute(request)\n  }\n\n  executeScript(sql: string, transaction?: TransactionToken): Promise<void> {\n    return this.#client.executeScript(sql, transaction)\n  }\n\n  beginTransaction(options?: TransactionOptions): Promise<TransactionToken> {\n    return this.#client.beginTransaction(options)\n  }\n\n  commitTransaction(transaction: TransactionToken): Promise<void> {\n    return this.#client.commitTransaction(transaction)\n  }\n\n  rollbackTransaction(transaction: TransactionToken): Promise<void> {\n    return this.#client.rollbackTransaction(transaction)\n  }\n\n  hasTable(table: TableRef, transaction?: TransactionToken): Promise<boolean> {\n    return this.#client.hasTable(table, transaction)\n  }\n\n  hasColumn(table: TableRef, column: string, transaction?: TransactionToken): Promise<boolean> {\n    return this.#client.hasColumn(table, column, transaction)\n  }\n\n  createSavepoint(transaction: TransactionToken, name: string): Promise<void> {\n    return this.#client.createSavepoint(transaction, name)\n  }\n\n  rollbackToSavepoint(transaction: TransactionToken, name: string): Promise<void> {\n    return this.#client.rollbackToSavepoint(transaction, name)\n  }\n\n  releaseSavepoint(transaction: TransactionToken, name: string): Promise<void> {\n    return this.#client.releaseSavepoint(transaction, name)\n  }\n\n  wipe(): Promise<void> {\n    return this.#client.wipe()\n  }\n\n  close(): void | Promise<void> {\n    return this.#client.close()\n  }\n}\n\nexport class AcmeDatabase extends Database<'acme'> {\n  constructor(client: AcmeClient, options?: DatabaseOptions) {\n    super(new AcmeDriver(client), options)\n  }\n}\n```\n\n`Database` supplies queries, CRUD helpers, relations, transactions, and migrations. The private\ndriver owns SQL execution and connection lifecycle. A driver provides:\n\n- `dialect` and an immutable `capabilities` object\n- `execute()` and `executeScript()`\n- `hasTable()`, `hasColumn()`, `wipe()`, and idempotent `close()`\n- transaction and savepoint lifecycle methods using opaque `TransactionToken` values\n\nDrivers whose capabilities report `migrationLock: true` also implement `withMigrationLock()` and\nrun its callback with a `DatabaseDriver` bound to the connection that owns the lock.\n\n`Database`, `DatabaseDriver`, and the supporting driver protocol types are all exported directly\nfrom `remix/data-table`. Transaction callbacks receive a transaction-scoped `Database`, so custom\nsubclass methods are intentionally unavailable inside the callback.\n\n## Related Packages\n\n- [`data-schema`](https://github.com/remix-run/remix/tree/main/packages/data-schema) - Optional schema parsing you can use inside table-level `validate(...)` hooks\n- [`data-table-postgres`](https://github.com/remix-run/remix/tree/main/packages/data-table-postgres) - PostgreSQL database integration\n- [`data-table-mysql`](https://github.com/remix-run/remix/tree/main/packages/data-table-mysql) - MySQL database integration\n- [`data-table-sqlite`](https://github.com/remix-run/remix/tree/main/packages/data-table-sqlite) - SQLite database integration\n\n## License\n\nSee [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)\n","readmeFilename":"README.md"}