From 51a626ccff482664457c7f1fc7fca7dfc55d2cde Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 03:23:10 -0600 Subject: [PATCH 01/15] test(prisma-next): fix @ts-expect-error placement in multi-line negative type assertions Biome reflows these assertions to multi-line, which strands the directive on the line before the one where tsc reports TS2344. Move the directive inside the generic so it sits directly above the erroring line and survives formatting. Co-Authored-By: Claude Fable 5 --- packages/prisma-next/test/operation-types.types.test-d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/prisma-next/test/operation-types.types.test-d.ts b/packages/prisma-next/test/operation-types.types.test-d.ts index ea9304256..0fae56831 100644 --- a/packages/prisma-next/test/operation-types.types.test-d.ts +++ b/packages/prisma-next/test/operation-types.types.test-d.ts @@ -139,8 +139,8 @@ type _ilike_double_neg = Expect> type _notilike_string_pos = Expect< M<'cipherstashNotIlike', 'cipherstash/string@1'> > -// @ts-expect-error cipherstashNotIlike must not surface on cipherstash/double@1. type _notilike_double_neg = Expect< + // @ts-expect-error cipherstashNotIlike must not surface on cipherstash/double@1. M<'cipherstashNotIlike', 'cipherstash/double@1'> > // @ts-expect-error cipherstashNotIlike must not surface on pg/text@1. @@ -209,8 +209,8 @@ type _between_double_pos = Expect< type _notbetween_date_pos = Expect< M<'cipherstashNotBetween', 'cipherstash/date@1'> > -// @ts-expect-error cipherstashBetween must not surface on cipherstash/boolean@1. type _between_boolean_neg = Expect< + // @ts-expect-error cipherstashBetween must not surface on cipherstash/boolean@1. M<'cipherstashBetween', 'cipherstash/boolean@1'> > // @ts-expect-error cipherstashNotBetween must not surface on pg/text@1. @@ -221,8 +221,8 @@ type _notbetween_text_neg = Expect> type _jpe_json_pos = Expect< M<'cipherstashJsonbPathExists', 'cipherstash/json@1'> > -// @ts-expect-error cipherstashJsonbPathExists must not surface on cipherstash/string@1. type _jpe_string_neg = Expect< + // @ts-expect-error cipherstashJsonbPathExists must not surface on cipherstash/string@1. M<'cipherstashJsonbPathExists', 'cipherstash/string@1'> > // @ts-expect-error cipherstashJsonbPathExists must not surface on pg/text@1. From cad376efbd2377069b8d09055534f8a53ee50c27 Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 03:24:21 -0600 Subject: [PATCH 02/15] chore(prisma-next): add @prisma-next/extension-author-tools for pin checking Co-Authored-By: Claude Fable 5 --- packages/prisma-next/package.json | 1 + pnpm-lock.yaml | 816 +++++++++++++++++++++++++++--- 2 files changed, 749 insertions(+), 68 deletions(-) diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json index fb7f55a13..62933e192 100644 --- a/packages/prisma-next/package.json +++ b/packages/prisma-next/package.json @@ -96,6 +96,7 @@ "@prisma-next/adapter-postgres": "0.8.0", "@prisma-next/cli": "0.8.0", "@prisma-next/driver-postgres": "0.8.0", + "@prisma-next/extension-author-tools": "0.14.0", "@prisma-next/psl-parser": "0.8.0", "@prisma-next/sql-contract-psl": "0.8.0", "@prisma-next/sql-contract-ts": "0.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3023ed143..6ff6bf922 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -146,45 +146,45 @@ importers: specifier: workspace:* version: link:../../packages/stack '@prisma-next/adapter-postgres': - specifier: 0.8.0 - version: 0.8.0(typanion@3.14.0) + specifier: 0.14.0 + version: 0.14.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/contract': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/driver-postgres': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/family-sql': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/framework-components': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/postgres': - specifier: 0.8.0 - version: 0.8.0(typanion@3.14.0) + specifier: 0.14.0 + version: 0.14.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/sql-contract': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-contract-psl': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-orm-client': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-runtime': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/target-postgres': - specifier: 0.8.0 - version: 0.8.0(typanion@3.14.0) + specifier: 0.14.0 + version: 0.14.0(typanion@3.14.0)(typescript@5.9.3) dotenv: specifier: ^17.4.2 version: 17.4.2 devDependencies: '@prisma-next/cli': - specifier: 0.8.0 - version: 0.8.0(typanion@3.14.0) + specifier: 0.14.0 + version: 0.14.0(typanion@3.14.0)(typescript@5.9.3) '@types/node': specifier: ^22.19.19 version: 22.19.19 @@ -450,6 +450,9 @@ importers: '@prisma-next/driver-postgres': specifier: 0.8.0 version: 0.8.0 + '@prisma-next/extension-author-tools': + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/psl-parser': specifier: 0.8.0 version: 0.8.0 @@ -604,7 +607,7 @@ importers: version: 17.4.2 drizzle-orm: specifier: ^0.45.2 - version: 0.45.2(@types/pg@8.20.0)(gel@2.2.0)(mysql2@3.16.0)(pg@8.20.0)(postgres@3.4.9) + version: 0.45.2(@types/pg@8.20.0)(gel@2.2.0)(mysql2@3.16.0)(pg@8.21.0)(postgres@3.4.9) execa: specifier: ^9.5.2 version: 9.6.1 @@ -1814,106 +1817,384 @@ packages: '@posthog/types@1.373.5': resolution: {integrity: sha512-K7STCnRG/WBE1q0BwEkIcrJB5OqECaymsQj6Hp4Ntvaek4dqHkZGfp6hxwIPqQPjlOXwidwPLo+XGsn+CoZUyw==} + '@prisma-next/adapter-postgres@0.14.0': + resolution: {integrity: sha512-c90KFtI74WU+8YH3uhVg1DTmD10T2BpINzzys9F3n1jTM2Am0tXKx53V05Ev+7H1VBrjBoksZPZ92WWxVEXLew==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/adapter-postgres@0.8.0': resolution: {integrity: sha512-YLzaPw+DZX9/FdPi3NK8bBEUpATULW+GTkB6v8BIl/upgVp6XkKxJe1rq2JEeDcxYF71JNhI8le7HCrAsfMoEQ==} + '@prisma-next/cli-telemetry@0.14.0': + resolution: {integrity: sha512-JaP/+09tIL6zH+5WQS507db2HQ0B47HRR2AZD8VHmsw/tMwBDZqKVdIA5KptPMVSR3pcnXDygGvG7E8N+PSPRg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + + '@prisma-next/cli@0.14.0': + resolution: {integrity: sha512-wN4mZc0K1m6wB4NXSEdrA2RCfc8zHBs5yJq0G7XoWQ8J8vvX/hR2kkukJv9QfEYFEIMyOT7vjbSBh2vj4NQ56g==} + engines: {node: '>=24'} + hasBin: true + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/cli@0.8.0': resolution: {integrity: sha512-3tVU24PDPwQFTEcI4K/RC9eC1aEHsSSNCAcg2/zpl3qH1FcjgRLc7XMkzqWnMH35ifPkPrwxuOjlNXp239643w==} hasBin: true + '@prisma-next/config@0.14.0': + resolution: {integrity: sha512-7U4Y5GeicvWy0dcg78J8arzqe13r97Zj8yB/e+Uea6M8LZxV2poW/afHlYnU0VXhLTZ4F29CdlWOUmCAKpl9mg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/config@0.8.0': resolution: {integrity: sha512-RMZk/At00KC6tWrdfLnu2yXcWLjfPljiQgd1Ucr5Xfs2Fw5QA1c0/Rp/nb+2hfnCPLjiZ1Q71cNIzFEb3HNkrQ==} engines: {node: '>=20'} + '@prisma-next/contract-authoring@0.14.0': + resolution: {integrity: sha512-79aLJO04p/XRX2QvIlFMR2ZvbqvI0xFAU5idgYySoevq8bSY9ORGA0XchSbhFl0d6d1VfSEZowRPwzvgl/6lsg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/contract-authoring@0.8.0': resolution: {integrity: sha512-LQ4kLO9U/adM9RdtWDOtNlkdq8wzpGs9Yml0yVun0t8zOoULvQkMFKsK1lHOnU9+xwBgHLUTnewU96ll436I9w==} + '@prisma-next/contract@0.14.0': + resolution: {integrity: sha512-HMeduqWGl1NpRyhEvsswF1T8PSBPfbpvMNYMegiYPt2ck5oClhbl0LHkgAIHsOjBg3IJv2VhRqDhQiuRVbkC4A==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/contract@0.8.0': resolution: {integrity: sha512-n9mgNwBX1PSNHOFWXAhFihOv7osD+ZbAFlY5fVVPjOKKXpTcCUi0/Hqo5SIjhz8JKXpsGqz/Eq4X23c9Y3C99g==} engines: {node: '>=20'} + '@prisma-next/driver-postgres@0.14.0': + resolution: {integrity: sha512-qla7/fAk2YqwomkUPevd9bIqJ5gQZcZTXbEoz8nJaErUunisEosepJOKwlTpquJV2+OAuezODCbrrYtUJ525jg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/driver-postgres@0.8.0': resolution: {integrity: sha512-dg5FQ+hFYC3NwyZPoMN6/ppbLdEWnxN+w1z2XkaVSic5VFC/eXRONxMBlE0YNM69/AhXXbavtBmHklFBw0xduQ==} + '@prisma-next/emitter@0.14.0': + resolution: {integrity: sha512-qdyOe11AsXHMpVmPYulA/NjKEn59VkXekGrtg8lB+Pqd+Dg2HwQinaO6G1PXjymRyVgv31UKRyUU7eLhCQaZ0Q==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/emitter@0.8.0': resolution: {integrity: sha512-GU/WGpwNejOUauIY0diE3OMYsIfw3SuzH9FpzEri8nl5jvmzjG44Sa6gMEc/g22D3MSD9Ufn2rsLc2K5RmC9wA==} + '@prisma-next/errors@0.14.0': + resolution: {integrity: sha512-+VlDHQaP3AAs3rYYYSDoG5Ng5Hkyl+F4oAHAvDsvhFMupsGWaBx7esc5MbBHfnjE/5i/cSybR8/efXrIZJI6zA==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/errors@0.8.0': resolution: {integrity: sha512-/ojKRvq4wYuH2ipTla9BeqGKZRFR6VIloxyXU+G2IILhpcobIepYTiOaafVd1ajx8KgR3Ux/rn8oTemIihdceA==} engines: {node: '>=20'} + '@prisma-next/extension-author-tools@0.14.0': + resolution: {integrity: sha512-7z9RiduSL6czOnB934pS+n0+PpVfzB7ATDK5HN8yBJKQN45ulNOFbxbnvBNDCPml6qDCFKXN3rtxv4bFVyHhOw==} + engines: {node: '>=24'} + hasBin: true + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + + '@prisma-next/family-sql@0.14.0': + resolution: {integrity: sha512-Gx4ByuilDiJzwsOkqWE42CSZrhO9/6uFDpyemD185eZsaJGk5tvEpbLy3UuLNx+6VEsvXfPYGLdGUy2LL8e9Pg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/family-sql@0.8.0': resolution: {integrity: sha512-L2wFXFv2gDFb2E+jm2HIUGSNMNuUwhZRzxDk4YzWEaG4+Xmj1kuv6tdqCLkTzuW9Re1OXMBivqtdIGDS9u4UWA==} + '@prisma-next/framework-components@0.14.0': + resolution: {integrity: sha512-6ZPnesKake4pnRRQZUi67a5qfgs6Pc9cmNN2bAf2bQJYTg3w5b1yOhjg2sFupXT54hq/iE3rXRY5A7ltgSxijA==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/framework-components@0.8.0': resolution: {integrity: sha512-t0RXECncHX+gby7mCT/WbBXGvayQQ6/JmwmE24dArxFqhj+HqDaTCa6OVPN7MRyuPZ3sAXc3g46EpNsQY3Sx9A==} + '@prisma-next/ids@0.14.0': + resolution: {integrity: sha512-E+Nhvgmco/Y24wiv/hWVt6wAf1J9sIS5qupoolBeL0V/BA/+zECewg/vCF2dj92hXk0jJ1WphhQDGrvx5XAPbQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/ids@0.8.0': resolution: {integrity: sha512-Z8LnSHxtdcX0Fj976zb8dM3PmeRKErWBGUdtExBAgu4aU1So0buG3qOQKkx36Ff1AvD6YhNQFAvTYdSJymPjqg==} + '@prisma-next/migration-tools@0.14.0': + resolution: {integrity: sha512-JdFJPO8qFA3N0lP6g/ZsRz4QGeDV/qQpRUWq+9phoAbL/kBaMha838JG5YUb6h8z3tD4/qg+Qs6ESZPURD4AcQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/migration-tools@0.8.0': resolution: {integrity: sha512-MRHV/CcQTHiliG+5jIFgnWrcIC8dfr9/Oh7WgOz9djysNxiytphE0QZAvKwghCC8dCXFTS4giAhimBsNjblv1A==} engines: {node: '>=20'} + '@prisma-next/operations@0.14.0': + resolution: {integrity: sha512-h53frkeEYalkixESJvkCvvuELD5b3YnHdkqkBK1A6XyDoqFusEHpjlP4LMXhpAQlft6QdbgYZfttm9ZXBFBUQw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/operations@0.8.0': resolution: {integrity: sha512-zT0DFdXYSDfenBzyAZpkoMXRamw6fN9gcRQeVkaq3EhtCMCbIIj+gTMMYIFEKRLgbzqvdkRmEl3IV0ouyvvCyQ==} engines: {node: '>=20'} - '@prisma-next/postgres@0.8.0': - resolution: {integrity: sha512-UrUd7SBxgnCGm41+VbeGuYFkWU5ym8IPRO1Uem+M98w4sYk/EYuJCnF/KDpL2scqy3qg22MMc92j1jiz6d2Anw==} - engines: {node: '>=20'} + '@prisma-next/postgres@0.14.0': + resolution: {integrity: sha512-0TXr3+x3INseRhzPUmMn2t6NUhD3DlDht0MHJ8SBvSpkiGXH237IwID161KtZ7xa6ihTTXdjDfIqgmbDtVxfEQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + + '@prisma-next/psl-parser@0.14.0': + resolution: {integrity: sha512-XKumV4Bfy3KHoD3IsWVSDtzFjXKL5zENASbbfIXXpwll3AVgU4rtVKuJIH5fwUOYpcCUdLE8ZaNw6vcwLuse3w==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/psl-parser@0.8.0': resolution: {integrity: sha512-cDsVRqq0SZ3W+du1fFaUOe679dV1Zl35C9wmgSLQQ9LYohiEWsTsWfACDO9oOhLUOzeK6iFJb5WMdEgbvf/6Fg==} + '@prisma-next/psl-printer@0.14.0': + resolution: {integrity: sha512-qVTjwpO9uvITSsMg/mtdk0Py7ITsQRpPjmidSi9ByoPE6OGBGF3/BaUbOjRiZ340i39Npg83jT4rLreH1GOFjg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/psl-printer@0.8.0': resolution: {integrity: sha512-M4jM5ZMVUMYNwmUTnuA3wqtjYVKIRfvh9XA5e26pCPFuBimDtHHN3PdDTciueCTrCqA4/Agl2xRQrACmUPaZ4A==} - '@prisma-next/sql-builder@0.8.0': - resolution: {integrity: sha512-bCCEL7AkfAiGJUX3457YpRqVhfQbjbFbe2BaLD8JyqQPdwQOq7yHyDQuZwtG36zaPgGK9X7VzrFbNfxA+Q+Oww==} - engines: {node: '>=20'} + '@prisma-next/sql-builder@0.14.0': + resolution: {integrity: sha512-11AAASmhf7CZ4M/qQ5JHaHxHl+olBf5TP6jYSKuX8c9COttyp9IN9QRI0+PKR0QSjZgU1VX9WPNRG8hEcmDrZw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + + '@prisma-next/sql-contract-emitter@0.14.0': + resolution: {integrity: sha512-gUgctyFNMKZ94wXNATevMi+FG9R9SK4yYQesYr7/5kZ4ZBbHkaFOFcWwlJZSI+EhCC2J1OgvfaCTtpWpn+VIUw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-contract-emitter@0.8.0': resolution: {integrity: sha512-7OYXFoRrWFN7qfxGnI1CvqqMMwGGffDeazO2JSzOL2rOR8T/SlqQGg8wXRckR6HNgXQisZKFc8aEPTJcLioJew==} + '@prisma-next/sql-contract-psl@0.14.0': + resolution: {integrity: sha512-86Z8MWo0NYhGSX4uOqbvAx46aMOaB2opNyUeFNnntT1br+U48IX2ZPGV62ul6mwCp7DGN7pjvmlBW5OnHXuiyg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/sql-contract-psl@0.8.0': resolution: {integrity: sha512-cJFUOyDWwb8FR3YLm1Ye18luofSoVwJiMO1LPmnjAH9W+da8mbkFpH0OuJbppeHAnKkY4lqKj5HPW9Ow0RZ3Jg==} + '@prisma-next/sql-contract-ts@0.14.0': + resolution: {integrity: sha512-AMTURIMXCsn+M7oW0QOe/9wS7J+Q1siW7+aihgtmBj88lYav9XHyxJO5AJDu6LSDSctorTzccfsPatBAlcxzKQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/sql-contract-ts@0.8.0': resolution: {integrity: sha512-sjfYhT7nyuBJVVxAztlnLppcouXms71BreCXOvvuGp5nw4CzMECD12qjRt8BHcNDdpVSSYeUIhd/TIeMD/qtcQ==} + '@prisma-next/sql-contract@0.14.0': + resolution: {integrity: sha512-/Ibvjd41sEgpi4c3YCZvK6vRCucNffsIZ+isIOYoVqXOt74H0llM4Ux4mR0PShNuo0jgG8xOon1ptSQ6/S7c+w==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/sql-contract@0.8.0': resolution: {integrity: sha512-SJbWPz05GlOrV58Dbk5Tfx88cAbgH7nqd1NWQ66yT5E1OSJ4SGwZCyJbtUnbb9HqLqqphYws6vDBmmMy5NQ77w==} + '@prisma-next/sql-errors@0.14.0': + resolution: {integrity: sha512-zc5U+SzEUqpjPC69w9JejqAkcRtkZ8BSm1a6l6p4pT4b8dk/iXrfQ96oHoy6SYG/dTbGe47W4gCfX9LCzCdUlA==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/sql-errors@0.8.0': resolution: {integrity: sha512-e7KCFVmHEHOXNHYx6XOr0xW/kTCwo0yeyE/zR2EZVTyNjDRGTCS7FOkqOSVWyPwGmUosDogY51ojChZYfn/fdw==} engines: {node: '>=20'} + '@prisma-next/sql-operations@0.14.0': + resolution: {integrity: sha512-0VeVceyh7LAlEGMseFPQrmoBPkNAlBiCOCyWzv1xQjjU0IoPkMlcjFu07wD5aLf+nwBzEUSbdg9EaTdBTYHO9Q==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/sql-operations@0.8.0': resolution: {integrity: sha512-uBTbNPsCjSMQxBw9ZV0K4egON2Vnp/YUe70dVEw/FPczR/w7/NKqiMhZ74qNpfxtFxpkefJ+nfERkMs3bnm1IQ==} engines: {node: '>=20'} - '@prisma-next/sql-orm-client@0.8.0': - resolution: {integrity: sha512-UOCWxxY6uIj3FRJur7xztp/lz9Tm0RWaTw0CLxV0sFnoJ+QTHvjkTY3nIvgc/vvqsgtb4EpkthDDJAQjzK5VlQ==} - engines: {node: '>=20'} + '@prisma-next/sql-orm-client@0.14.0': + resolution: {integrity: sha512-fXyiR/8r3NqLqUKTxrL0o5KcxHW6r91pD0pU1tmnB6kJD3MS++Fhd2miDcbCn/5O0qf71zfmJDafKU74TOJ3dw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + + '@prisma-next/sql-relational-core@0.14.0': + resolution: {integrity: sha512-7H1HaN31lmGD81C9vz07dq/r5n80yKXfFNpYj5XrKKeVpDZQSb/IXtOVt7LyB+q9QqSUW2jD5RGLuTOhsd2kbw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-relational-core@0.8.0': resolution: {integrity: sha512-l+hfL3ChYJI1oBgd9dZqUbf+I1dMpUBqlHzZJG1zyXv5wcTlqXbtOk18EoYe+BXp6jnHBwgQDsiBayLvB+ehpQ==} + '@prisma-next/sql-runtime@0.14.0': + resolution: {integrity: sha512-HV8ZAvNuXtFrAHuxs3/ZEz0JapKFayFYDljQeVDFxExSEj79fPtd9CON9sTTV4T/RKII5knlKJRuKtwWH35Znw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/sql-runtime@0.8.0': resolution: {integrity: sha512-YLqJTzxFcpC5y/2ZPfWqvlHgifqJ/ADAXM8o7PHhrEJ5Z9fivppkEfVUlgnRVDjvPGGwCUY3VbK3FqOL/SbmXw==} + '@prisma-next/sql-schema-ir@0.14.0': + resolution: {integrity: sha512-5buE3jcTz3yyyve4demaACIv52YT09tFPCBEqwmxG1ss8KtybVEgaZg8UUYGmyYbWU0l5RpQ96/vuB5UTFE4nw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/sql-schema-ir@0.8.0': resolution: {integrity: sha512-8xv+leV0bAK8jaFJN5ngEDUeYZGlvXvP/csiCzz8LHC5M4OTouGpbgyDDXFwC9tJThLaCZUK1zVF4XRYP9hftQ==} engines: {node: '>=20'} + '@prisma-next/target-postgres@0.14.0': + resolution: {integrity: sha512-i1H9Jzt7ZGgYQ61rlmalyEPY1RoLFIMk9+qE5WbETjQ8LY9p7iXeTx4bBv25twkYIAHxXtrYxsQ3klUWg0JfVg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/target-postgres@0.8.0': resolution: {integrity: sha512-CXkzmyXhtvcfhaBXBDlyb6Aq2wISQuax2pUbK+tq+e9Acz0idDRZk3FtvCkPUkviKv6ZAPA8EpnB9FT3iP3KGw==} + '@prisma-next/ts-render@0.14.0': + resolution: {integrity: sha512-ub25pEfUe2dLpNu/wR0mlBBMZ87eGmyV5D9GS5KmyHkofjJdiE2Y3BpIhHXf7wjiYp35z/gOyQ2vuPFS4xVBgQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/ts-render@0.8.0': resolution: {integrity: sha512-IYx4NLsTaJxVlUWI+QQyY8LgLEHUOqqxTOwAlhSyXbzbTXiz89DN/I/swtapH3cl1Tk/PAJG9c5vKMl3lfAcng==} engines: {node: '>=20'} + '@prisma-next/utils@0.14.0': + resolution: {integrity: sha512-YIV6FMdKKsJqLkG2h9tQDzEoeAFjtniNXcB0XnyZyD1tkALodiWsRymuQJBGcFZB7S8icrOvRaqdSUNdrrR6yw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true + '@prisma-next/utils@0.8.0': resolution: {integrity: sha512-zpSG3L7/Cwtb/WkUZKZCSd4j5gzLCqNTqDb6hO3hcK7Me+blv72cOuyscyUMiYlkHRdLinnSX7MWcAQnsqNONQ==} engines: {node: '>=20'} @@ -2482,6 +2763,10 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -3397,14 +3682,25 @@ packages: pg-cloudflare@1.3.0: resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} + pg-cloudflare@1.4.0: + resolution: {integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==} + pg-connection-string@2.12.0: resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==} + pg-connection-string@2.14.0: + resolution: {integrity: sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==} + pg-cursor@2.19.0: resolution: {integrity: sha512-J5cF1MUz7LRJ9emOqF/06QjabMHMZy587rSPF0UuA8rCwKeeYl2co8Pp+6k5UU9YrAYHMzWkLxilfZB0hqsWWw==} peerDependencies: pg: ^8 + pg-cursor@2.21.0: + resolution: {integrity: sha512-IYvk/j+Suhtbo/C3uOf4JLsLK/gWxOTUOmYbDsbKnLaVJDq+KwhwK6ngpRfiCk8eDMS3AmGQABZCv0cREEzHQw==} + peerDependencies: + pg: ^8 + pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} @@ -3414,9 +3710,17 @@ packages: peerDependencies: pg: '>=8.0' + pg-pool@3.14.0: + resolution: {integrity: sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==} + peerDependencies: + pg: '>=8.0' + pg-protocol@1.13.0: resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==} + pg-protocol@1.15.0: + resolution: {integrity: sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==} + pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} @@ -3439,6 +3743,15 @@ packages: pg-native: optional: true + pg@8.21.0: + resolution: {integrity: sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} @@ -4888,6 +5201,30 @@ snapshots: '@posthog/types@1.373.5': {} + '@prisma-next/adapter-postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/contract-authoring': 0.14.0(typescript@5.9.3) + '@prisma-next/errors': 0.14.0(typescript@5.9.3) + '@prisma-next/family-sql': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/ids': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract-psl': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.14.0(typescript@5.9.3) + '@prisma-next/target-postgres': 0.14.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - magicast + - typanion + '@prisma-next/adapter-postgres@0.8.0(typanion@3.14.0)': dependencies: '@prisma-next/contract': 0.8.0 @@ -4909,6 +5246,50 @@ snapshots: - magicast - typanion + '@prisma-next/cli-telemetry@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/config': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + c12: 3.3.4 + pathe: 2.0.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - magicast + + '@prisma-next/cli@0.14.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@clack/prompts': 1.4.0 + '@prisma-next/cli-telemetry': 0.14.0(typescript@5.9.3) + '@prisma-next/config': 0.14.0(typescript@5.9.3) + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/emitter': 0.14.0(typescript@5.9.3) + '@prisma-next/errors': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.14.0(typescript@5.9.3) + '@prisma-next/psl-printer': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + c12: 3.3.4 + ci-info: 4.4.0 + clipanion: 4.0.0-rc.4(typanion@3.14.0) + closest-match: 1.3.3 + colorette: 2.0.20 + commander: 14.0.3 + esbuild: 0.28.1 + jsonc-parser: 3.3.1 + package-manager-detector: 1.6.0 + pathe: 2.0.3 + string-width: 8.2.1 + strip-ansi: 7.2.0 + wrap-ansi: 10.0.0 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - magicast + - typanion + '@prisma-next/cli@0.8.0(typanion@3.14.0)': dependencies: '@clack/prompts': 1.3.0 @@ -4938,6 +5319,15 @@ snapshots: - magicast - typanion + '@prisma-next/config@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/config@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -4945,14 +5335,46 @@ snapshots: '@prisma-next/utils': 0.8.0 arktype: 2.2.0 + '@prisma-next/contract-authoring@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/contract-authoring@0.8.0': {} + '@prisma-next/contract@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/contract@0.8.0': dependencies: '@prisma-next/utils': 0.8.0 '@standard-schema/spec': 1.1.0 arktype: 2.2.0 + '@prisma-next/driver-postgres@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/errors': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-errors': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + pg: 8.21.0 + pg-cursor: 2.21.0(pg@8.21.0) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - pg-native + '@prisma-next/driver-postgres@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -4969,6 +5391,18 @@ snapshots: transitivePeerDependencies: - pg-native + '@prisma-next/emitter@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/operations': 0.14.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + prettier: 3.8.3 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/emitter@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -4978,11 +5412,41 @@ snapshots: arktype: 2.2.0 prettier: 3.8.3 + '@prisma-next/errors@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/errors@0.8.0': dependencies: '@prisma-next/framework-components': 0.8.0 '@prisma-next/utils': 0.8.0 + '@prisma-next/extension-author-tools@0.14.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@prisma-next/family-sql@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/emitter': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.14.0(typescript@5.9.3) + '@prisma-next/operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract-emitter': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/family-sql@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -5000,6 +5464,17 @@ snapshots: '@prisma-next/utils': 0.8.0 arktype: 2.2.0 + '@prisma-next/framework-components@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/operations': 0.14.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/framework-components@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -5008,6 +5483,15 @@ snapshots: '@prisma-next/utils': 0.8.0 '@standard-schema/spec': 1.1.0 + '@prisma-next/ids@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + uniku: 0.0.12 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/ids@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -5015,6 +5499,17 @@ snapshots: '@prisma-next/utils': 0.8.0 uniku: 0.0.12 + '@prisma-next/migration-tools@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + pathe: 2.0.3 + prettier: 3.8.3 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/migration-tools@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -5024,49 +5519,81 @@ snapshots: pathe: 2.0.3 prettier: 3.8.3 + '@prisma-next/operations@0.14.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/operations@0.8.0': {} - '@prisma-next/postgres@0.8.0(typanion@3.14.0)': - dependencies: - '@prisma-next/adapter-postgres': 0.8.0(typanion@3.14.0) - '@prisma-next/cli': 0.8.0(typanion@3.14.0) - '@prisma-next/config': 0.8.0 - '@prisma-next/contract': 0.8.0 - '@prisma-next/driver-postgres': 0.8.0 - '@prisma-next/family-sql': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/sql-builder': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-contract-psl': 0.8.0 - '@prisma-next/sql-contract-ts': 0.8.0 - '@prisma-next/sql-orm-client': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 - '@prisma-next/sql-runtime': 0.8.0 - '@prisma-next/target-postgres': 0.8.0(typanion@3.14.0) - '@prisma-next/utils': 0.8.0 + '@prisma-next/postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@prisma-next/adapter-postgres': 0.14.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/cli': 0.14.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/config': 0.14.0(typescript@5.9.3) + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/driver-postgres': 0.14.0(typescript@5.9.3) + '@prisma-next/family-sql': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-builder': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract-psl': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-orm-client': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.14.0(typescript@5.9.3) + '@prisma-next/target-postgres': 0.14.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) pathe: 2.0.3 - pg: 8.20.0 + pg: 8.21.0 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - magicast - pg-native - typanion + '@prisma-next/psl-parser@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/psl-parser@0.8.0': dependencies: '@prisma-next/framework-components': 0.8.0 '@prisma-next/utils': 0.8.0 + '@prisma-next/psl-printer@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/psl-printer@0.8.0': dependencies: '@prisma-next/framework-components': 0.8.0 - '@prisma-next/sql-builder@0.8.0': + '@prisma-next/sql-builder@0.14.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@prisma-next/sql-contract-emitter@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/emitter': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-contract-emitter@0.8.0': dependencies: @@ -5075,6 +5602,19 @@ snapshots: '@prisma-next/sql-contract': 0.8.0 '@prisma-next/utils': 0.8.0 + '@prisma-next/sql-contract-psl@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/config': 0.14.0(typescript@5.9.3) + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/psl-parser': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + pathe: 2.0.3 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/sql-contract-psl@0.8.0': dependencies: '@prisma-next/config': 0.8.0 @@ -5086,6 +5626,20 @@ snapshots: '@prisma-next/utils': 0.8.0 pathe: 2.0.3 + '@prisma-next/sql-contract-ts@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/config': 0.14.0(typescript@5.9.3) + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/contract-authoring': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + pathe: 2.0.3 + ts-toolbelt: 9.6.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/sql-contract-ts@0.8.0': dependencies: '@prisma-next/config': 0.8.0 @@ -5098,30 +5652,68 @@ snapshots: pathe: 2.0.3 ts-toolbelt: 9.6.0 + '@prisma-next/sql-contract@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/sql-contract@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 '@prisma-next/framework-components': 0.8.0 arktype: 2.2.0 + '@prisma-next/sql-errors@0.14.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/sql-errors@0.8.0': {} + '@prisma-next/sql-operations@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/sql-operations@0.8.0': dependencies: '@prisma-next/operations': 0.8.0 '@prisma-next/sql-contract': 0.8.0 arktype: 2.2.0 - '@prisma-next/sql-orm-client@0.8.0': + '@prisma-next/sql-orm-client@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-errors': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@prisma-next/sql-relational-core@0.14.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/operations': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 - '@prisma-next/sql-runtime': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 + ts-toolbelt: 9.6.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-relational-core@0.8.0': dependencies: @@ -5135,6 +5727,20 @@ snapshots: arktype: 2.2.0 ts-toolbelt: 9.6.0 + '@prisma-next/sql-runtime@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/ids': 0.14.0(typescript@5.9.3) + '@prisma-next/operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/sql-runtime@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 @@ -5147,10 +5753,41 @@ snapshots: '@prisma-next/utils': 0.8.0 arktype: 2.2.0 + '@prisma-next/sql-schema-ir@0.14.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/sql-schema-ir@0.8.0': dependencies: '@prisma-next/contract': 0.8.0 + '@prisma-next/target-postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@prisma-next/cli': 0.14.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/errors': 0.14.0(typescript@5.9.3) + '@prisma-next/family-sql': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-errors': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.14.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.14.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) + '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 + pathe: 2.0.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - magicast + - typanion + '@prisma-next/target-postgres@0.8.0(typanion@3.14.0)': dependencies: '@prisma-next/cli': 0.8.0(typanion@3.14.0) @@ -5173,8 +5810,16 @@ snapshots: - magicast - typanion + '@prisma-next/ts-render@0.14.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/ts-render@0.8.0': {} + '@prisma-next/utils@0.14.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + '@prisma-next/utils@0.8.0': {} '@rollup/rollup-android-arm-eabi@4.59.0': @@ -5625,6 +6270,8 @@ snapshots: dependencies: readdirp: 5.0.0 + ci-info@4.4.0: {} + client-only@0.0.1: {} clipanion@4.0.0-rc.4(typanion@3.14.0): @@ -5723,6 +6370,14 @@ snapshots: pg: 8.20.0 postgres: 3.4.9 + drizzle-orm@0.45.2(@types/pg@8.20.0)(gel@2.2.0)(mysql2@3.16.0)(pg@8.21.0)(postgres@3.4.9): + optionalDependencies: + '@types/pg': 8.20.0 + gel: 2.2.0 + mysql2: 3.16.0 + pg: 8.21.0 + postgres: 3.4.9 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -6433,12 +7088,21 @@ snapshots: pg-cloudflare@1.3.0: optional: true + pg-cloudflare@1.4.0: + optional: true + pg-connection-string@2.12.0: {} + pg-connection-string@2.14.0: {} + pg-cursor@2.19.0(pg@8.20.0): dependencies: pg: 8.20.0 + pg-cursor@2.21.0(pg@8.21.0): + dependencies: + pg: 8.21.0 + pg-int8@1.0.1: {} pg-pool@3.13.0(pg@8.13.1): @@ -6449,8 +7113,14 @@ snapshots: dependencies: pg: 8.20.0 + pg-pool@3.14.0(pg@8.21.0): + dependencies: + pg: 8.21.0 + pg-protocol@1.13.0: {} + pg-protocol@1.15.0: {} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 @@ -6479,6 +7149,16 @@ snapshots: optionalDependencies: pg-cloudflare: 1.3.0 + pg@8.21.0: + dependencies: + pg-connection-string: 2.14.0 + pg-pool: 3.14.0(pg@8.21.0) + pg-protocol: 1.15.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.4.0 + pgpass@1.0.5: dependencies: split2: 4.2.0 From 4c2f9a6dda6c997be5018180835a7b0822bf8b2d Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 03:39:27 -0600 Subject: [PATCH 03/15] chore: upgrade @prisma-next/* to 0.9.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-transition changes (extension-author upgrade instructions 0.8→0.9): - strip fromContract/toContract from the seed migration manifest (strip-inline-contracts codemod) Additional 0.9 SPI churn not covered by the instructions: - accept the widened InsertValue union in the bulk-encrypt routing-key stamp (insert rows and update/on-conflict sets now carry PreparedParamRef | DefaultValueExpr variants) - validateContract moved: @prisma-next/sql-contract/validate is gone; use validateSqlContractFully from /validators (single-argument) - SqlStorage requires namespaces: construct test fixture storage via new SqlStorage(...) so the constructor defaults the namespace map - adapter lower() emits structured LoweredParam entries; unwrap {kind:'literal', value} in operator-lowering assertions (literalParamValue helper) - named-type storage entries are stamped kind:'codec-instance' Co-Authored-By: Claude Fable 5 --- .../migration.json | 103 ---- packages/prisma-next/package.json | 36 +- .../src/middleware/bulk-encrypt.ts | 6 +- .../test/cipherstash-codec.test.ts | 6 +- packages/prisma-next/test/helpers.test.ts | 12 +- .../test/operator-lowering-equality.test.ts | 14 +- .../operator-lowering-text-search.test.ts | 3 +- .../test/operator-lowering.helpers.ts | 23 +- .../test/operator-lowering.test.ts | 15 +- .../test/psl-interpretation.test.ts | 5 +- pnpm-lock.yaml | 504 +++++++++--------- 11 files changed, 314 insertions(+), 413 deletions(-) diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json index bf79367bf..1ed73da1a 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json @@ -4,109 +4,6 @@ "labels": [], "providedInvariants": ["cipherstash:install-eql-bundle-v1"], "createdAt": "2026-05-09T03:42:56.902Z", - "fromContract": null, - "toContract": { - "schemaVersion": "1", - "targetFamily": "sql", - "target": "postgres", - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", - "roots": { - "eql_v2_configuration": "EqlV2Configuration" - }, - "models": { - "EqlV2Configuration": { - "fields": { - "data": { - "nullable": false, - "type": { - "codecId": "pg/jsonb@1", - "kind": "scalar" - } - }, - "id": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "state": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - }, - "relations": {}, - "storage": { - "fields": { - "data": { - "column": "data" - }, - "id": { - "column": "id" - }, - "state": { - "column": "state" - } - }, - "table": "eql_v2_configuration" - } - } - }, - "storage": { - "storageHash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": ["id"] - }, - "uniques": [] - } - } - }, - "capabilities": { - "postgres": { - "jsonAgg": true, - "lateral": true, - "limit": true, - "orderBy": true, - "returning": true - }, - "sql": { - "defaultInInsert": true, - "enums": true, - "returning": true - } - }, - "extensionPacks": {}, - "meta": {}, - "_generated": { - "warning": "⚠️ GENERATED FILE - DO NOT EDIT", - "message": "This file is automatically generated by \"prisma-next contract emit\".", - "regenerate": "To regenerate, run: prisma-next contract emit" - } - }, "hints": { "used": [], "applied": [], diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json index 62933e192..50abcc289 100644 --- a/packages/prisma-next/package.json +++ b/packages/prisma-next/package.json @@ -80,28 +80,28 @@ }, "dependencies": { "@cipherstash/stack": "workspace:*", - "@prisma-next/contract": "0.8.0", - "@prisma-next/family-sql": "0.8.0", - "@prisma-next/framework-components": "0.8.0", - "@prisma-next/migration-tools": "0.8.0", - "@prisma-next/sql-contract": "0.8.0", - "@prisma-next/sql-operations": "0.8.0", - "@prisma-next/sql-relational-core": "0.8.0", - "@prisma-next/sql-runtime": "0.8.0", - "@prisma-next/ts-render": "0.8.0", - "@prisma-next/utils": "0.8.0", + "@prisma-next/contract": "0.9.0", + "@prisma-next/family-sql": "0.9.0", + "@prisma-next/framework-components": "0.9.0", + "@prisma-next/migration-tools": "0.9.0", + "@prisma-next/sql-contract": "0.9.0", + "@prisma-next/sql-operations": "0.9.0", + "@prisma-next/sql-relational-core": "0.9.0", + "@prisma-next/sql-runtime": "0.9.0", + "@prisma-next/ts-render": "0.9.0", + "@prisma-next/utils": "0.9.0", "arktype": "^2.1.29" }, "devDependencies": { - "@prisma-next/adapter-postgres": "0.8.0", - "@prisma-next/cli": "0.8.0", - "@prisma-next/driver-postgres": "0.8.0", + "@prisma-next/adapter-postgres": "0.9.0", + "@prisma-next/cli": "0.9.0", + "@prisma-next/driver-postgres": "0.9.0", "@prisma-next/extension-author-tools": "0.14.0", - "@prisma-next/psl-parser": "0.8.0", - "@prisma-next/sql-contract-psl": "0.8.0", - "@prisma-next/sql-contract-ts": "0.8.0", - "@prisma-next/sql-schema-ir": "0.8.0", - "@prisma-next/target-postgres": "0.8.0", + "@prisma-next/psl-parser": "0.9.0", + "@prisma-next/sql-contract-psl": "0.9.0", + "@prisma-next/sql-contract-ts": "0.9.0", + "@prisma-next/sql-schema-ir": "0.9.0", + "@prisma-next/target-postgres": "0.9.0", "pathe": "^2.0.3", "tsup": "catalog:repo", "typescript": "catalog:repo", diff --git a/packages/prisma-next/src/middleware/bulk-encrypt.ts b/packages/prisma-next/src/middleware/bulk-encrypt.ts index 7ee0203ce..1f762f60a 100644 --- a/packages/prisma-next/src/middleware/bulk-encrypt.ts +++ b/packages/prisma-next/src/middleware/bulk-encrypt.ts @@ -57,10 +57,8 @@ import type { AnyQueryAst, - ColumnRef, - DefaultValueExpr, InsertAst, - ParamRef, + InsertValue, UpdateAst, } from '@prisma-next/sql-relational-core/ast' import type { @@ -231,7 +229,7 @@ function stampUpdate(ast: UpdateAst): void { } function stampParamRefIfEnvelope( - value: ColumnRef | ParamRef | DefaultValueExpr, + value: InsertValue, table: string, column: string, ): void { diff --git a/packages/prisma-next/test/cipherstash-codec.test.ts b/packages/prisma-next/test/cipherstash-codec.test.ts index 1978fe674..6327c8095 100644 --- a/packages/prisma-next/test/cipherstash-codec.test.ts +++ b/packages/prisma-next/test/cipherstash-codec.test.ts @@ -26,7 +26,7 @@ import { planFieldEventOperations, } from '@prisma-next/family-sql/control' import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components' -import type { SqlStorage, StorageTable } from '@prisma-next/sql-contract/types' +import { SqlStorage, type StorageTable } from '@prisma-next/sql-contract/types' import { ifDefined } from '@prisma-next/utils/defined' import { describe, expect, it } from 'vitest' import cipherstashExtensionDescriptor from '../src/exports/control' @@ -117,10 +117,10 @@ describe('planFieldEventOperations driving the cipherstash hook', () => { target: 'postgres', targetFamily: 'sql', profileHash: profileHash('sha256:test'), - storage: { + storage: new SqlStorage({ storageHash: 'sha256:test' as StorageHashBase, tables, - }, + }), models: {}, roots: {}, capabilities: {}, diff --git a/packages/prisma-next/test/helpers.test.ts b/packages/prisma-next/test/helpers.test.ts index a876e2cd1..a525e0ff9 100644 --- a/packages/prisma-next/test/helpers.test.ts +++ b/packages/prisma-next/test/helpers.test.ts @@ -24,12 +24,11 @@ import postgresRuntimeAdapter from '@prisma-next/adapter-postgres/runtime' import type { PostgresContract } from '@prisma-next/adapter-postgres/types' -import { emptyCodecLookup } from '@prisma-next/framework-components/codec' import type { RuntimeExtensionDescriptor, RuntimeTargetDescriptor, } from '@prisma-next/framework-components/execution' -import { validateContract } from '@prisma-next/sql-contract/validate' +import { validateSqlContractFully } from '@prisma-next/sql-contract/validators' import { type AnyExpression, ColumnRef, @@ -71,7 +70,7 @@ function emptySdk(): CipherstashSdk { const TABLE = 'user' -const contract = validateContract( +const contract = validateSqlContractFully( { target: 'postgres', targetFamily: 'sql', @@ -130,7 +129,6 @@ const contract = validateContract( }, models: {}, }, - emptyCodecLookup, ) const stubRuntimeTarget: RuntimeTargetDescriptor<'sql', 'postgres'> = { @@ -279,7 +277,9 @@ describe('cipherstashJsonbPathQueryFirst — AST shape and SQL snapshot', () => expect(lowered.sql).toMatchInlineSnapshot( `"SELECT eql_v2.jsonb_path_query_first("user"."payload", $1) AS "first_email" FROM "user""`, ) - expect(lowered.params).toEqual(['$.user.email']) + expect(lowered.params).toEqual([ + { kind: 'literal', value: '$.user.email' }, + ]) }) }) @@ -301,7 +301,7 @@ describe('cipherstashJsonbGet — AST shape and SQL snapshot', () => { expect(lowered.sql).toMatchInlineSnapshot( `"SELECT eql_v2."->"("user"."payload", $1) AS "email_field" FROM "user""`, ) - expect(lowered.params).toEqual(['email']) + expect(lowered.params).toEqual([{ kind: 'literal', value: 'email' }]) }) }) diff --git a/packages/prisma-next/test/operator-lowering-equality.test.ts b/packages/prisma-next/test/operator-lowering-equality.test.ts index da0ed33b8..f74290ffe 100644 --- a/packages/prisma-next/test/operator-lowering-equality.test.ts +++ b/packages/prisma-next/test/operator-lowering-equality.test.ts @@ -25,6 +25,7 @@ import { columnAccessor, contract, getOperator, + literalParamValue, makeAdapter, selectWithWhere, TABLE, @@ -66,7 +67,7 @@ describe('cipherstash operator lowering — cipherstashEq', () => { // `bulk-encrypt.ts:stampRoutingKeysFromAst` does not walk — only // insert/update) still participate in the routing-key grouping. expect(lowered.params).toHaveLength(1) - const envelope = lowered.params[0] + const envelope = literalParamValue(lowered.params[0]) expect(envelope).toBeInstanceOf(EncryptedString) const handle = (envelope as EncryptedString).expose() expect(handle.plaintext).toBe('alice@example.com') @@ -89,7 +90,7 @@ describe('cipherstash operator lowering — cipherstashEq', () => { // The same envelope object flows through; the operator only // augments it with the routing key (write-once-wins semantics — // see `setHandleRoutingKey`). - expect(lowered.params[0]).toBe(userEnvelope) + expect(literalParamValue(lowered.params[0])).toBe(userEnvelope) const handle = userEnvelope.expose() expect(handle.table).toBe(TABLE) expect(handle.column).toBe(COLUMN) @@ -115,7 +116,9 @@ describe('cipherstash operator lowering — equality extensions', () => { `"SELECT "user"."id" AS "id" FROM "user" WHERE NOT eql_v2.eq("user"."email", $1::eql_v2_encrypted)"`, ) expect(lowered.params).toHaveLength(1) - expect(lowered.params[0]).toBeInstanceOf(EncryptedString) + expect(literalParamValue(lowered.params[0])).toBeInstanceOf( + EncryptedString, + ) }) it('lowers cipherstashInArray with a single element to a one-term OR', () => { @@ -164,8 +167,9 @@ describe('cipherstash operator lowering — equality extensions', () => { // Every envelope shares the same `(table, column)` routing key — // the bulk-encrypt grouping invariant for variable-arity ops. for (const param of lowered.params) { - expect(param).toBeInstanceOf(EncryptedString) - const handle = (param as EncryptedString).expose() + const envelope = literalParamValue(param) + expect(envelope).toBeInstanceOf(EncryptedString) + const handle = (envelope as EncryptedString).expose() expect(handle.table).toBe(TABLE) expect(handle.column).toBe(COLUMN) } diff --git a/packages/prisma-next/test/operator-lowering-text-search.test.ts b/packages/prisma-next/test/operator-lowering-text-search.test.ts index bfab62451..2e0d842db 100644 --- a/packages/prisma-next/test/operator-lowering-text-search.test.ts +++ b/packages/prisma-next/test/operator-lowering-text-search.test.ts @@ -22,6 +22,7 @@ import { columnAccessor, contract, getOperator, + literalParamValue, makeAdapter, selectWithWhere, TABLE, @@ -48,7 +49,7 @@ describe('cipherstash operator lowering — cipherstashIlike', () => { const lowered = makeAdapter().lower(ast, { contract }) expect(lowered.params).toHaveLength(1) - const envelope = lowered.params[0] + const envelope = literalParamValue(lowered.params[0]) expect(envelope).toBeInstanceOf(EncryptedString) const handle = (envelope as EncryptedString).expose() expect(handle.plaintext).toBe('%alice%') diff --git a/packages/prisma-next/test/operator-lowering.helpers.ts b/packages/prisma-next/test/operator-lowering.helpers.ts index f4587202c..f5a9d80d2 100644 --- a/packages/prisma-next/test/operator-lowering.helpers.ts +++ b/packages/prisma-next/test/operator-lowering.helpers.ts @@ -25,16 +25,16 @@ import postgresRuntimeAdapter from '@prisma-next/adapter-postgres/runtime' import type { PostgresContract } from '@prisma-next/adapter-postgres/types' -import { emptyCodecLookup } from '@prisma-next/framework-components/codec' import type { RuntimeExtensionDescriptor, RuntimeTargetDescriptor, } from '@prisma-next/framework-components/execution' -import { validateContract } from '@prisma-next/sql-contract/validate' +import { validateSqlContractFully } from '@prisma-next/sql-contract/validators' import type { SqlOperationDescriptor } from '@prisma-next/sql-operations' import { type AnyExpression, ColumnRef, + type LoweredParam, ProjectionItem, SelectAst, TableSource, @@ -67,7 +67,7 @@ export function emptySdk(): CipherstashSdk { export const TABLE = 'user' export const COLUMN = 'email' -export const contract = validateContract( +export const contract = validateSqlContractFully( { target: 'postgres', targetFamily: 'sql', @@ -126,7 +126,6 @@ export const contract = validateContract( }, models: {}, }, - emptyCodecLookup, ) // Stub runtime target — the Postgres adapter only consults `familyId` / @@ -221,3 +220,19 @@ export function selectWithWhere(whereExpr: AnyExpression) { .withProjection([ProjectionItem.of('id', ColumnRef.of(TABLE, 'id'))]) .withWhere(whereExpr) } + +/** + * Unwrap the value carried by a lowered `literal` param. Since 0.9 the + * adapter's `lower` emits structured `LoweredParam` entries + * (`{ kind: 'literal', value } | { kind: 'bind', name }`) instead of raw + * values; the operator-lowering assertions all target the literal + * payload (the cipherstash envelope). + */ +export function literalParamValue(param: LoweredParam): unknown { + if (param.kind !== 'literal') { + throw new Error( + `expected a literal lowered param, got kind '${param.kind}'`, + ) + } + return param.value +} diff --git a/packages/prisma-next/test/operator-lowering.test.ts b/packages/prisma-next/test/operator-lowering.test.ts index 10fd9413b..3ec1038f2 100644 --- a/packages/prisma-next/test/operator-lowering.test.ts +++ b/packages/prisma-next/test/operator-lowering.test.ts @@ -71,6 +71,7 @@ import { contract, emptySdk, getOperator, + literalParamValue, makeAdapter, selectWithWhere, TABLE, @@ -130,7 +131,7 @@ describe('cipherstash operator lowering — per-codec envelope dispatch', () => contract, }) expect(lowered.params).toHaveLength(1) - const envelope = lowered.params[0] + const envelope = literalParamValue(lowered.params[0]) expect(envelope).toBeInstanceOf(EncryptedDouble) }) @@ -144,7 +145,9 @@ describe('cipherstash operator lowering — per-codec envelope dispatch', () => const lowered = makeAdapter().lower(selectWithWhere(predicate), { contract, }) - expect(lowered.params[0]).toBeInstanceOf(EncryptedBigInt) + expect(literalParamValue(lowered.params[0])).toBeInstanceOf( + EncryptedBigInt, + ) }) it('cipherstashGt on a date column wraps the value in EncryptedDate', () => { @@ -157,7 +160,7 @@ describe('cipherstash operator lowering — per-codec envelope dispatch', () => const lowered = makeAdapter().lower(selectWithWhere(predicate), { contract, }) - expect(lowered.params[0]).toBeInstanceOf(EncryptedDate) + expect(literalParamValue(lowered.params[0])).toBeInstanceOf(EncryptedDate) }) it('cipherstashNe on a boolean column wraps the value in EncryptedBoolean', () => { @@ -170,7 +173,9 @@ describe('cipherstash operator lowering — per-codec envelope dispatch', () => const lowered = makeAdapter().lower(selectWithWhere(predicate), { contract, }) - expect(lowered.params[0]).toBeInstanceOf(EncryptedBoolean) + expect(literalParamValue(lowered.params[0])).toBeInstanceOf( + EncryptedBoolean, + ) }) it('cipherstashGt rejects a non-matching plaintext type for the column codec', () => { @@ -202,7 +207,7 @@ describe('cipherstash operator lowering — JSON path predicate', () => { `"SELECT "user"."id" AS "id" FROM "user" WHERE eql_v2.jsonb_path_exists("user"."payload", $1)"`, ) // Path is a plain text bind — no envelope wrapping. - expect(lowered.params).toEqual(['$.k']) + expect(lowered.params.map(literalParamValue)).toEqual(['$.k']) }) it('cipherstashJsonbPathExists rejects non-string path arguments', () => { diff --git a/packages/prisma-next/test/psl-interpretation.test.ts b/packages/prisma-next/test/psl-interpretation.test.ts index 3f73a0f76..03d3811e4 100644 --- a/packages/prisma-next/test/psl-interpretation.test.ts +++ b/packages/prisma-next/test/psl-interpretation.test.ts @@ -315,8 +315,11 @@ model User { // The named type's storage descriptor and the inline column's // codec/nativeType/typeParams must agree byte-for-byte; the inline // column carries `nullable` (and may carry `default`/etc.) which the - // named-type descriptor does not. + // named-type descriptor does not, while the named-type entry is + // stamped with the `kind` discriminator for the polymorphic + // `storage.types` slot. expect(aliasNamedType).toEqual({ + kind: 'codec-instance', codecId: inlineCol['codecId'], nativeType: inlineCol['nativeType'], typeParams: inlineCol['typeParams'], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ff6bf922..e9a38ca7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,66 +408,66 @@ importers: specifier: workspace:* version: link:../stack '@prisma-next/contract': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/family-sql': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/framework-components': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/migration-tools': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/sql-contract': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/sql-operations': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/sql-relational-core': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/sql-runtime': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/ts-render': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/utils': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 arktype: specifier: ^2.1.29 version: 2.2.0 devDependencies: '@prisma-next/adapter-postgres': - specifier: 0.8.0 - version: 0.8.0(typanion@3.14.0) + specifier: 0.9.0 + version: 0.9.0(typanion@3.14.0) '@prisma-next/cli': - specifier: 0.8.0 - version: 0.8.0(typanion@3.14.0) + specifier: 0.9.0 + version: 0.9.0(typanion@3.14.0) '@prisma-next/driver-postgres': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/extension-author-tools': specifier: 0.14.0 version: 0.14.0(typescript@5.9.3) '@prisma-next/psl-parser': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/sql-contract-psl': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/sql-contract-ts': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/sql-schema-ir': - specifier: 0.8.0 - version: 0.8.0 + specifier: 0.9.0 + version: 0.9.0 '@prisma-next/target-postgres': - specifier: 0.8.0 - version: 0.8.0(typanion@3.14.0) + specifier: 0.9.0 + version: 0.9.0(typanion@3.14.0) pathe: specifier: ^2.0.3 version: 2.0.3 @@ -1038,18 +1038,10 @@ packages: '@cipherstash/protect-ffi@0.28.0': resolution: {integrity: sha512-R2L/8HwMREkVKlR5KCcuELIWz4QNButSBQzY+nRDHl1PUXjRqWG1h265FkKVtTXKNKUup7rB4mswu+M+t9KF3A==} - '@clack/core@1.3.0': - resolution: {integrity: sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA==} - engines: {node: '>= 20.12.0'} - '@clack/core@1.3.1': resolution: {integrity: sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA==} engines: {node: '>= 20.12.0'} - '@clack/prompts@1.3.0': - resolution: {integrity: sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw==} - engines: {node: '>= 20.12.0'} - '@clack/prompts@1.4.0': resolution: {integrity: sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA==} engines: {node: '>= 20.12.0'} @@ -1826,8 +1818,8 @@ packages: typescript: optional: true - '@prisma-next/adapter-postgres@0.8.0': - resolution: {integrity: sha512-YLzaPw+DZX9/FdPi3NK8bBEUpATULW+GTkB6v8BIl/upgVp6XkKxJe1rq2JEeDcxYF71JNhI8le7HCrAsfMoEQ==} + '@prisma-next/adapter-postgres@0.9.0': + resolution: {integrity: sha512-KbyquUsBgJB7xpdc6Hz5al784Is9vwNlL7K+nR1SFTbSgWbEDBpjD1hV3qG9nMLeXJ3rDg3/GipXXzcKoa5JEw==} '@prisma-next/cli-telemetry@0.14.0': resolution: {integrity: sha512-JaP/+09tIL6zH+5WQS507db2HQ0B47HRR2AZD8VHmsw/tMwBDZqKVdIA5KptPMVSR3pcnXDygGvG7E8N+PSPRg==} @@ -1848,8 +1840,8 @@ packages: typescript: optional: true - '@prisma-next/cli@0.8.0': - resolution: {integrity: sha512-3tVU24PDPwQFTEcI4K/RC9eC1aEHsSSNCAcg2/zpl3qH1FcjgRLc7XMkzqWnMH35ifPkPrwxuOjlNXp239643w==} + '@prisma-next/cli@0.9.0': + resolution: {integrity: sha512-p66znaSHGjo87YI8nt5hs3NZlyqb7mhrF+9OAmRvfORQ8xXSvKjA1sMmH9hhn4bqKbPYZtjzd7wEMjzWYyWBJg==} hasBin: true '@prisma-next/config@0.14.0': @@ -1861,8 +1853,8 @@ packages: typescript: optional: true - '@prisma-next/config@0.8.0': - resolution: {integrity: sha512-RMZk/At00KC6tWrdfLnu2yXcWLjfPljiQgd1Ucr5Xfs2Fw5QA1c0/Rp/nb+2hfnCPLjiZ1Q71cNIzFEb3HNkrQ==} + '@prisma-next/config@0.9.0': + resolution: {integrity: sha512-JoHkFvKA5cnnjc/IR8AB1/jpCe+6KK4yRwl4t1hL1T6hIHwi70BAOMbVcS6Fsxf9qnUA9N1e6dcnNibsPLmo5Q==} engines: {node: '>=20'} '@prisma-next/contract-authoring@0.14.0': @@ -1874,8 +1866,8 @@ packages: typescript: optional: true - '@prisma-next/contract-authoring@0.8.0': - resolution: {integrity: sha512-LQ4kLO9U/adM9RdtWDOtNlkdq8wzpGs9Yml0yVun0t8zOoULvQkMFKsK1lHOnU9+xwBgHLUTnewU96ll436I9w==} + '@prisma-next/contract-authoring@0.9.0': + resolution: {integrity: sha512-RSKadW4EnmB63uuOSPabZmzBiNedcEHdTXUcd19qCACP0QYJF3+hPO9c7abiEqy26E2O5XcbCqHJwMejH91uMw==} '@prisma-next/contract@0.14.0': resolution: {integrity: sha512-HMeduqWGl1NpRyhEvsswF1T8PSBPfbpvMNYMegiYPt2ck5oClhbl0LHkgAIHsOjBg3IJv2VhRqDhQiuRVbkC4A==} @@ -1886,8 +1878,8 @@ packages: typescript: optional: true - '@prisma-next/contract@0.8.0': - resolution: {integrity: sha512-n9mgNwBX1PSNHOFWXAhFihOv7osD+ZbAFlY5fVVPjOKKXpTcCUi0/Hqo5SIjhz8JKXpsGqz/Eq4X23c9Y3C99g==} + '@prisma-next/contract@0.9.0': + resolution: {integrity: sha512-9BJiFGnJWvIA6V8LQPru0qd7hBl7fbPqoOwVChIpow1YfQOBu12u85R0mRJRjqNJmk3BdNqNm0zU3KNEln80AA==} engines: {node: '>=20'} '@prisma-next/driver-postgres@0.14.0': @@ -1899,8 +1891,8 @@ packages: typescript: optional: true - '@prisma-next/driver-postgres@0.8.0': - resolution: {integrity: sha512-dg5FQ+hFYC3NwyZPoMN6/ppbLdEWnxN+w1z2XkaVSic5VFC/eXRONxMBlE0YNM69/AhXXbavtBmHklFBw0xduQ==} + '@prisma-next/driver-postgres@0.9.0': + resolution: {integrity: sha512-3Ny7pZYG9gWfZRtRoFz4E71NwJTzxIIZ/1jRs8gA0v1jAr8ZotoFetbmjofM7GgDsP0trNyH4DMWWELHMpK/qQ==} '@prisma-next/emitter@0.14.0': resolution: {integrity: sha512-qdyOe11AsXHMpVmPYulA/NjKEn59VkXekGrtg8lB+Pqd+Dg2HwQinaO6G1PXjymRyVgv31UKRyUU7eLhCQaZ0Q==} @@ -1911,8 +1903,8 @@ packages: typescript: optional: true - '@prisma-next/emitter@0.8.0': - resolution: {integrity: sha512-GU/WGpwNejOUauIY0diE3OMYsIfw3SuzH9FpzEri8nl5jvmzjG44Sa6gMEc/g22D3MSD9Ufn2rsLc2K5RmC9wA==} + '@prisma-next/emitter@0.9.0': + resolution: {integrity: sha512-UPsNHHTuR5yBR2CrC282276VLvg7FRwMCscU+rfusrzdHDYaqb+cSlxRYSmBqwZb52XXNvufiFldSBoQ5AovXg==} '@prisma-next/errors@0.14.0': resolution: {integrity: sha512-+VlDHQaP3AAs3rYYYSDoG5Ng5Hkyl+F4oAHAvDsvhFMupsGWaBx7esc5MbBHfnjE/5i/cSybR8/efXrIZJI6zA==} @@ -1923,8 +1915,8 @@ packages: typescript: optional: true - '@prisma-next/errors@0.8.0': - resolution: {integrity: sha512-/ojKRvq4wYuH2ipTla9BeqGKZRFR6VIloxyXU+G2IILhpcobIepYTiOaafVd1ajx8KgR3Ux/rn8oTemIihdceA==} + '@prisma-next/errors@0.9.0': + resolution: {integrity: sha512-3z5zJDIMOnA6mHBdC7Q4sxCb5bzfM5gwBJJXWVjvpc9SdP/ZQfmc0pbSzfEWPPQVuMiMIA1EzBJeWruq/ipQPA==} engines: {node: '>=20'} '@prisma-next/extension-author-tools@0.14.0': @@ -1946,8 +1938,8 @@ packages: typescript: optional: true - '@prisma-next/family-sql@0.8.0': - resolution: {integrity: sha512-L2wFXFv2gDFb2E+jm2HIUGSNMNuUwhZRzxDk4YzWEaG4+Xmj1kuv6tdqCLkTzuW9Re1OXMBivqtdIGDS9u4UWA==} + '@prisma-next/family-sql@0.9.0': + resolution: {integrity: sha512-8S6uaZv/hTzJWmvNmhkubZZMLsH/snx7imw4wQqHCZmDVz4dq2P6XrojJywCB45kQPM3mAwhxz+ckEhcZhBEJg==} '@prisma-next/framework-components@0.14.0': resolution: {integrity: sha512-6ZPnesKake4pnRRQZUi67a5qfgs6Pc9cmNN2bAf2bQJYTg3w5b1yOhjg2sFupXT54hq/iE3rXRY5A7ltgSxijA==} @@ -1958,8 +1950,8 @@ packages: typescript: optional: true - '@prisma-next/framework-components@0.8.0': - resolution: {integrity: sha512-t0RXECncHX+gby7mCT/WbBXGvayQQ6/JmwmE24dArxFqhj+HqDaTCa6OVPN7MRyuPZ3sAXc3g46EpNsQY3Sx9A==} + '@prisma-next/framework-components@0.9.0': + resolution: {integrity: sha512-fAd0w/8PbJ4U+sQGdRhEEHiseytKVnpOHwaVBrIk6ZOjEFI/o8iybE/VUVTF7B56ewV72fd4dM/4ffq3xueGaw==} '@prisma-next/ids@0.14.0': resolution: {integrity: sha512-E+Nhvgmco/Y24wiv/hWVt6wAf1J9sIS5qupoolBeL0V/BA/+zECewg/vCF2dj92hXk0jJ1WphhQDGrvx5XAPbQ==} @@ -1970,8 +1962,8 @@ packages: typescript: optional: true - '@prisma-next/ids@0.8.0': - resolution: {integrity: sha512-Z8LnSHxtdcX0Fj976zb8dM3PmeRKErWBGUdtExBAgu4aU1So0buG3qOQKkx36Ff1AvD6YhNQFAvTYdSJymPjqg==} + '@prisma-next/ids@0.9.0': + resolution: {integrity: sha512-wPuSIcQst057qUUCwuxhHj2V12X5c+YO9gwckU5P5JbFjCOYDS+4cOa3N+TKMxtFpMK79+xjikW99KmGjXxJMg==} '@prisma-next/migration-tools@0.14.0': resolution: {integrity: sha512-JdFJPO8qFA3N0lP6g/ZsRz4QGeDV/qQpRUWq+9phoAbL/kBaMha838JG5YUb6h8z3tD4/qg+Qs6ESZPURD4AcQ==} @@ -1982,8 +1974,8 @@ packages: typescript: optional: true - '@prisma-next/migration-tools@0.8.0': - resolution: {integrity: sha512-MRHV/CcQTHiliG+5jIFgnWrcIC8dfr9/Oh7WgOz9djysNxiytphE0QZAvKwghCC8dCXFTS4giAhimBsNjblv1A==} + '@prisma-next/migration-tools@0.9.0': + resolution: {integrity: sha512-j5f8a3etiUA7PoAHer7VKyPWlMdONVFLt+vHpdMWO+8MwVR/OadOgCfbnQgIPXQCJ9JLQB7IJm5EA0D8gRZuXA==} engines: {node: '>=20'} '@prisma-next/operations@0.14.0': @@ -1995,8 +1987,8 @@ packages: typescript: optional: true - '@prisma-next/operations@0.8.0': - resolution: {integrity: sha512-zT0DFdXYSDfenBzyAZpkoMXRamw6fN9gcRQeVkaq3EhtCMCbIIj+gTMMYIFEKRLgbzqvdkRmEl3IV0ouyvvCyQ==} + '@prisma-next/operations@0.9.0': + resolution: {integrity: sha512-0WQgBDc/lFvvAI5a8M3ES57DGBTtBPk2Z2J4XMWVC8iEwnEiTPHTrpqHAJNyDLJHmlh0H4FIY9OEdWFXqyVJ1Q==} engines: {node: '>=20'} '@prisma-next/postgres@0.14.0': @@ -2017,8 +2009,8 @@ packages: typescript: optional: true - '@prisma-next/psl-parser@0.8.0': - resolution: {integrity: sha512-cDsVRqq0SZ3W+du1fFaUOe679dV1Zl35C9wmgSLQQ9LYohiEWsTsWfACDO9oOhLUOzeK6iFJb5WMdEgbvf/6Fg==} + '@prisma-next/psl-parser@0.9.0': + resolution: {integrity: sha512-b1PG8nG7mR9dpRyWCAjZpHfgFs1qzcJbgx+Jjw59HYnNkMBqZUZWPM99RZj3OYC+VLxVj2s1CcQEl1O6nnYo3w==} '@prisma-next/psl-printer@0.14.0': resolution: {integrity: sha512-qVTjwpO9uvITSsMg/mtdk0Py7ITsQRpPjmidSi9ByoPE6OGBGF3/BaUbOjRiZ340i39Npg83jT4rLreH1GOFjg==} @@ -2029,8 +2021,8 @@ packages: typescript: optional: true - '@prisma-next/psl-printer@0.8.0': - resolution: {integrity: sha512-M4jM5ZMVUMYNwmUTnuA3wqtjYVKIRfvh9XA5e26pCPFuBimDtHHN3PdDTciueCTrCqA4/Agl2xRQrACmUPaZ4A==} + '@prisma-next/psl-printer@0.9.0': + resolution: {integrity: sha512-jpYee/bV3y7jWmivNNuyexkwi59cQqssYYlOeqbEw9Dywvp+Ag+TAS+aXAprhCINJDl8qj/9Wb8Vh1yn6ulejA==} '@prisma-next/sql-builder@0.14.0': resolution: {integrity: sha512-11AAASmhf7CZ4M/qQ5JHaHxHl+olBf5TP6jYSKuX8c9COttyp9IN9QRI0+PKR0QSjZgU1VX9WPNRG8hEcmDrZw==} @@ -2050,8 +2042,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-emitter@0.8.0': - resolution: {integrity: sha512-7OYXFoRrWFN7qfxGnI1CvqqMMwGGffDeazO2JSzOL2rOR8T/SlqQGg8wXRckR6HNgXQisZKFc8aEPTJcLioJew==} + '@prisma-next/sql-contract-emitter@0.9.0': + resolution: {integrity: sha512-e+3+7gbD7aIVwPF+wAf+3uAl6UAzPLzJ4+yOs0fKcij2EARYUSKwg6Hhuv6QVMeT0CA/lgtEb+fv6lsCpYSS9Q==} '@prisma-next/sql-contract-psl@0.14.0': resolution: {integrity: sha512-86Z8MWo0NYhGSX4uOqbvAx46aMOaB2opNyUeFNnntT1br+U48IX2ZPGV62ul6mwCp7DGN7pjvmlBW5OnHXuiyg==} @@ -2062,8 +2054,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-psl@0.8.0': - resolution: {integrity: sha512-cJFUOyDWwb8FR3YLm1Ye18luofSoVwJiMO1LPmnjAH9W+da8mbkFpH0OuJbppeHAnKkY4lqKj5HPW9Ow0RZ3Jg==} + '@prisma-next/sql-contract-psl@0.9.0': + resolution: {integrity: sha512-SUn04doJG1IixPQm9ZP+dzDLy8X+ZcbPNU+URtbwquA+vXa0ePlHs+k0VNGBoZQdEo0MFLtGcoXMVl+Y0KiUUw==} '@prisma-next/sql-contract-ts@0.14.0': resolution: {integrity: sha512-AMTURIMXCsn+M7oW0QOe/9wS7J+Q1siW7+aihgtmBj88lYav9XHyxJO5AJDu6LSDSctorTzccfsPatBAlcxzKQ==} @@ -2074,8 +2066,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-ts@0.8.0': - resolution: {integrity: sha512-sjfYhT7nyuBJVVxAztlnLppcouXms71BreCXOvvuGp5nw4CzMECD12qjRt8BHcNDdpVSSYeUIhd/TIeMD/qtcQ==} + '@prisma-next/sql-contract-ts@0.9.0': + resolution: {integrity: sha512-8dSYxKkvTEUvGJ4vZCWXOW/5GDmLp0/4+wrgSzwRPFw13wFRQct2DYNtJllWoHj9TMzGvtBekrNP4mdejpOUdA==} '@prisma-next/sql-contract@0.14.0': resolution: {integrity: sha512-/Ibvjd41sEgpi4c3YCZvK6vRCucNffsIZ+isIOYoVqXOt74H0llM4Ux4mR0PShNuo0jgG8xOon1ptSQ6/S7c+w==} @@ -2086,8 +2078,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract@0.8.0': - resolution: {integrity: sha512-SJbWPz05GlOrV58Dbk5Tfx88cAbgH7nqd1NWQ66yT5E1OSJ4SGwZCyJbtUnbb9HqLqqphYws6vDBmmMy5NQ77w==} + '@prisma-next/sql-contract@0.9.0': + resolution: {integrity: sha512-ol7R3/rahAsof/34ue7YxSd7yKds1I7XtifDbcEQLqZ41vgqULJp+t5GCr4zNWmZVl2f8QVf4IZ5oxJdR2VqIw==} '@prisma-next/sql-errors@0.14.0': resolution: {integrity: sha512-zc5U+SzEUqpjPC69w9JejqAkcRtkZ8BSm1a6l6p4pT4b8dk/iXrfQ96oHoy6SYG/dTbGe47W4gCfX9LCzCdUlA==} @@ -2098,8 +2090,8 @@ packages: typescript: optional: true - '@prisma-next/sql-errors@0.8.0': - resolution: {integrity: sha512-e7KCFVmHEHOXNHYx6XOr0xW/kTCwo0yeyE/zR2EZVTyNjDRGTCS7FOkqOSVWyPwGmUosDogY51ojChZYfn/fdw==} + '@prisma-next/sql-errors@0.9.0': + resolution: {integrity: sha512-zMWKAHSFGANHiunOKs3txejvGf+pnBpgGYDL+HDJxEFYw5+/rGJEI3Hk0mKpCC0377b4M0BpaH9DCzeVCfdqIw==} engines: {node: '>=20'} '@prisma-next/sql-operations@0.14.0': @@ -2111,8 +2103,8 @@ packages: typescript: optional: true - '@prisma-next/sql-operations@0.8.0': - resolution: {integrity: sha512-uBTbNPsCjSMQxBw9ZV0K4egON2Vnp/YUe70dVEw/FPczR/w7/NKqiMhZ74qNpfxtFxpkefJ+nfERkMs3bnm1IQ==} + '@prisma-next/sql-operations@0.9.0': + resolution: {integrity: sha512-//k//O/CDslU3AHWYBDZL5bvtlUYzzoZHJnU0zz180zdz1jO67yTw+Upp1dLW+jcuCWxtwVo/5j0tp86maOmJg==} engines: {node: '>=20'} '@prisma-next/sql-orm-client@0.14.0': @@ -2133,8 +2125,8 @@ packages: typescript: optional: true - '@prisma-next/sql-relational-core@0.8.0': - resolution: {integrity: sha512-l+hfL3ChYJI1oBgd9dZqUbf+I1dMpUBqlHzZJG1zyXv5wcTlqXbtOk18EoYe+BXp6jnHBwgQDsiBayLvB+ehpQ==} + '@prisma-next/sql-relational-core@0.9.0': + resolution: {integrity: sha512-Fo/0FZAw/5Ay+bxTxJQvSNAErbWntON4cKdxq+99ioZDLx0UVgeL2e3doezmahD+9vOpIMLzNaA8pnaST86gZg==} '@prisma-next/sql-runtime@0.14.0': resolution: {integrity: sha512-HV8ZAvNuXtFrAHuxs3/ZEz0JapKFayFYDljQeVDFxExSEj79fPtd9CON9sTTV4T/RKII5knlKJRuKtwWH35Znw==} @@ -2145,8 +2137,8 @@ packages: typescript: optional: true - '@prisma-next/sql-runtime@0.8.0': - resolution: {integrity: sha512-YLqJTzxFcpC5y/2ZPfWqvlHgifqJ/ADAXM8o7PHhrEJ5Z9fivppkEfVUlgnRVDjvPGGwCUY3VbK3FqOL/SbmXw==} + '@prisma-next/sql-runtime@0.9.0': + resolution: {integrity: sha512-8mj72w4bgoQ7qqD0uGpl18UQPsfWOYbo51/HXBXSls+yI10AijkScCh7x2mHc/G2NPRRpvqrWBT5MOlMZcSmdA==} '@prisma-next/sql-schema-ir@0.14.0': resolution: {integrity: sha512-5buE3jcTz3yyyve4demaACIv52YT09tFPCBEqwmxG1ss8KtybVEgaZg8UUYGmyYbWU0l5RpQ96/vuB5UTFE4nw==} @@ -2157,8 +2149,8 @@ packages: typescript: optional: true - '@prisma-next/sql-schema-ir@0.8.0': - resolution: {integrity: sha512-8xv+leV0bAK8jaFJN5ngEDUeYZGlvXvP/csiCzz8LHC5M4OTouGpbgyDDXFwC9tJThLaCZUK1zVF4XRYP9hftQ==} + '@prisma-next/sql-schema-ir@0.9.0': + resolution: {integrity: sha512-wspzYofwBBxwmwmaZF+TEa/ko3Q4yqKpz8huSjKHR4Xigxa3gAYrsaE6cCH58ukz23/VrmSsKBH5tdbWqjFDgg==} engines: {node: '>=20'} '@prisma-next/target-postgres@0.14.0': @@ -2170,8 +2162,8 @@ packages: typescript: optional: true - '@prisma-next/target-postgres@0.8.0': - resolution: {integrity: sha512-CXkzmyXhtvcfhaBXBDlyb6Aq2wISQuax2pUbK+tq+e9Acz0idDRZk3FtvCkPUkviKv6ZAPA8EpnB9FT3iP3KGw==} + '@prisma-next/target-postgres@0.9.0': + resolution: {integrity: sha512-stKpuRcxvvIdCiJbwNyFcjQYQSZ66mym38Eiq2YeMGGo4ISBz7RV4WMzgX+mhGthF62kcf1fGJYD56Bu0EcP5w==} '@prisma-next/ts-render@0.14.0': resolution: {integrity: sha512-ub25pEfUe2dLpNu/wR0mlBBMZ87eGmyV5D9GS5KmyHkofjJdiE2Y3BpIhHXf7wjiYp35z/gOyQ2vuPFS4xVBgQ==} @@ -2182,8 +2174,8 @@ packages: typescript: optional: true - '@prisma-next/ts-render@0.8.0': - resolution: {integrity: sha512-IYx4NLsTaJxVlUWI+QQyY8LgLEHUOqqxTOwAlhSyXbzbTXiz89DN/I/swtapH3cl1Tk/PAJG9c5vKMl3lfAcng==} + '@prisma-next/ts-render@0.9.0': + resolution: {integrity: sha512-burhx330iW3ytljTYBO3Z1AlryZAjvEfM8tacZMIC/5SQ03w5hYUHexKkiipxcEqcEAgq3TP6xqtkAnTjoyA9Q==} engines: {node: '>=20'} '@prisma-next/utils@0.14.0': @@ -2195,8 +2187,8 @@ packages: typescript: optional: true - '@prisma-next/utils@0.8.0': - resolution: {integrity: sha512-zpSG3L7/Cwtb/WkUZKZCSd4j5gzLCqNTqDb6hO3hcK7Me+blv72cOuyscyUMiYlkHRdLinnSX7MWcAQnsqNONQ==} + '@prisma-next/utils@0.9.0': + resolution: {integrity: sha512-Vh83WXCv9IAAFG1G2+5c8XaOxBkPm2R3I1nmaiPyfxTpZS1MYNoUSL25cHiVpqnPpLxuZx2Ca9qmg/d5C32BAA==} engines: {node: '>=20'} '@rollup/rollup-android-arm-eabi@4.59.0': @@ -3691,11 +3683,6 @@ packages: pg-connection-string@2.14.0: resolution: {integrity: sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==} - pg-cursor@2.19.0: - resolution: {integrity: sha512-J5cF1MUz7LRJ9emOqF/06QjabMHMZy587rSPF0UuA8rCwKeeYl2co8Pp+6k5UU9YrAYHMzWkLxilfZB0hqsWWw==} - peerDependencies: - pg: ^8 - pg-cursor@2.21.0: resolution: {integrity: sha512-IYvk/j+Suhtbo/C3uOf4JLsLK/gWxOTUOmYbDsbKnLaVJDq+KwhwK6ngpRfiCk8eDMS3AmGQABZCv0cREEzHQw==} peerDependencies: @@ -4685,23 +4672,11 @@ snapshots: '@cipherstash/protect-ffi-linux-x64-musl': 0.28.0 '@cipherstash/protect-ffi-win32-x64-msvc': 0.28.0 - '@clack/core@1.3.0': - dependencies: - fast-wrap-ansi: 0.2.0 - sisteransi: 1.0.5 - '@clack/core@1.3.1': dependencies: fast-wrap-ansi: 0.2.0 sisteransi: 1.0.5 - '@clack/prompts@1.3.0': - dependencies: - '@clack/core': 1.3.0 - fast-string-width: 3.0.2 - fast-wrap-ansi: 0.2.0 - sisteransi: 1.0.5 - '@clack/prompts@1.4.0': dependencies: '@clack/core': 1.3.1 @@ -5225,22 +5200,22 @@ snapshots: - magicast - typanion - '@prisma-next/adapter-postgres@0.8.0(typanion@3.14.0)': - dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/contract-authoring': 0.8.0 - '@prisma-next/family-sql': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/ids': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-contract-psl': 0.8.0 - '@prisma-next/sql-contract-ts': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 - '@prisma-next/sql-runtime': 0.8.0 - '@prisma-next/sql-schema-ir': 0.8.0 - '@prisma-next/target-postgres': 0.8.0(typanion@3.14.0) - '@prisma-next/utils': 0.8.0 + '@prisma-next/adapter-postgres@0.9.0(typanion@3.14.0)': + dependencies: + '@prisma-next/contract': 0.9.0 + '@prisma-next/contract-authoring': 0.9.0 + '@prisma-next/family-sql': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/ids': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/sql-contract-psl': 0.9.0 + '@prisma-next/sql-contract-ts': 0.9.0 + '@prisma-next/sql-operations': 0.9.0 + '@prisma-next/sql-relational-core': 0.9.0 + '@prisma-next/sql-runtime': 0.9.0 + '@prisma-next/sql-schema-ir': 0.9.0 + '@prisma-next/target-postgres': 0.9.0(typanion@3.14.0) + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 transitivePeerDependencies: - magicast @@ -5290,18 +5265,18 @@ snapshots: - magicast - typanion - '@prisma-next/cli@0.8.0(typanion@3.14.0)': + '@prisma-next/cli@0.9.0(typanion@3.14.0)': dependencies: - '@clack/prompts': 1.3.0 + '@clack/prompts': 1.4.0 '@dagrejs/dagre': 3.0.0 - '@prisma-next/config': 0.8.0 - '@prisma-next/contract': 0.8.0 - '@prisma-next/emitter': 0.8.0 - '@prisma-next/errors': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/migration-tools': 0.8.0 - '@prisma-next/psl-printer': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/config': 0.9.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/emitter': 0.9.0 + '@prisma-next/errors': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/migration-tools': 0.9.0 + '@prisma-next/psl-printer': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 c12: 3.3.4 clipanion: 4.0.0-rc.4(typanion@3.14.0) @@ -5328,11 +5303,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/config@0.8.0': + '@prisma-next/config@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 '@prisma-next/contract-authoring@0.14.0(typescript@5.9.3)': @@ -5341,7 +5316,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract-authoring@0.8.0': {} + '@prisma-next/contract-authoring@0.9.0': + dependencies: + '@prisma-next/framework-components': 0.9.0 '@prisma-next/contract@0.14.0(typescript@5.9.3)': dependencies: @@ -5351,9 +5328,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract@0.8.0': + '@prisma-next/contract@0.9.0': dependencies: - '@prisma-next/utils': 0.8.0 + '@prisma-next/utils': 0.9.0 '@standard-schema/spec': 1.1.0 arktype: 2.2.0 @@ -5375,19 +5352,19 @@ snapshots: transitivePeerDependencies: - pg-native - '@prisma-next/driver-postgres@0.8.0': + '@prisma-next/driver-postgres@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/errors': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-errors': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/errors': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/sql-errors': 0.9.0 + '@prisma-next/sql-operations': 0.9.0 + '@prisma-next/sql-relational-core': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 pg: 8.20.0 - pg-cursor: 2.19.0(pg@8.20.0) + pg-cursor: 2.21.0(pg@8.20.0) transitivePeerDependencies: - pg-native @@ -5403,12 +5380,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/emitter@0.8.0': + '@prisma-next/emitter@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/operations': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/operations': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 prettier: 3.8.3 @@ -5419,10 +5396,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/errors@0.8.0': + '@prisma-next/errors@0.9.0': dependencies: - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/utils': 0.9.0 '@prisma-next/extension-author-tools@0.14.0(typescript@5.9.3)': optionalDependencies: @@ -5447,21 +5424,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/family-sql@0.8.0': - dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/emitter': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/migration-tools': 0.8.0 - '@prisma-next/operations': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-contract-emitter': 0.8.0 - '@prisma-next/sql-contract-ts': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 - '@prisma-next/sql-runtime': 0.8.0 - '@prisma-next/sql-schema-ir': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/family-sql@0.9.0': + dependencies: + '@prisma-next/contract': 0.9.0 + '@prisma-next/emitter': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/migration-tools': 0.9.0 + '@prisma-next/operations': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/sql-contract-emitter': 0.9.0 + '@prisma-next/sql-contract-ts': 0.9.0 + '@prisma-next/sql-operations': 0.9.0 + '@prisma-next/sql-relational-core': 0.9.0 + '@prisma-next/sql-runtime': 0.9.0 + '@prisma-next/sql-schema-ir': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 '@prisma-next/framework-components@0.14.0(typescript@5.9.3)': @@ -5475,12 +5452,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/framework-components@0.8.0': + '@prisma-next/framework-components@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/operations': 0.8.0 - '@prisma-next/ts-render': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/operations': 0.9.0 + '@prisma-next/ts-render': 0.9.0 + '@prisma-next/utils': 0.9.0 '@standard-schema/spec': 1.1.0 '@prisma-next/ids@0.14.0(typescript@5.9.3)': @@ -5492,11 +5469,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/ids@0.8.0': + '@prisma-next/ids@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/utils': 0.9.0 uniku: 0.0.12 '@prisma-next/migration-tools@0.14.0(typescript@5.9.3)': @@ -5510,11 +5487,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/migration-tools@0.8.0': + '@prisma-next/migration-tools@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 pathe: 2.0.3 prettier: 3.8.3 @@ -5523,7 +5500,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/operations@0.8.0': {} + '@prisma-next/operations@0.9.0': {} '@prisma-next/postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: @@ -5559,10 +5536,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-parser@0.8.0': + '@prisma-next/psl-parser@0.9.0': dependencies: - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/utils': 0.9.0 '@prisma-next/psl-printer@0.14.0(typescript@5.9.3)': dependencies: @@ -5571,9 +5548,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-printer@0.8.0': + '@prisma-next/psl-printer@0.9.0': dependencies: - '@prisma-next/framework-components': 0.8.0 + '@prisma-next/framework-components': 0.9.0 '@prisma-next/sql-builder@0.14.0(typescript@5.9.3)': dependencies: @@ -5595,12 +5572,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-emitter@0.8.0': + '@prisma-next/sql-contract-emitter@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/emitter': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/emitter': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/utils': 0.9.0 '@prisma-next/sql-contract-psl@0.14.0(typescript@5.9.3)': dependencies: @@ -5615,15 +5592,15 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-psl@0.8.0': + '@prisma-next/sql-contract-psl@0.9.0': dependencies: - '@prisma-next/config': 0.8.0 - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/psl-parser': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-contract-ts': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/config': 0.9.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/psl-parser': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/sql-contract-ts': 0.9.0 + '@prisma-next/utils': 0.9.0 pathe: 2.0.3 '@prisma-next/sql-contract-ts@0.14.0(typescript@5.9.3)': @@ -5640,14 +5617,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-ts@0.8.0': + '@prisma-next/sql-contract-ts@0.9.0': dependencies: - '@prisma-next/config': 0.8.0 - '@prisma-next/contract': 0.8.0 - '@prisma-next/contract-authoring': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/config': 0.9.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/contract-authoring': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 pathe: 2.0.3 ts-toolbelt: 9.6.0 @@ -5661,17 +5638,17 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract@0.8.0': + '@prisma-next/sql-contract@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 arktype: 2.2.0 '@prisma-next/sql-errors@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-errors@0.8.0': {} + '@prisma-next/sql-errors@0.9.0': {} '@prisma-next/sql-operations@0.14.0(typescript@5.9.3)': dependencies: @@ -5681,10 +5658,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-operations@0.8.0': + '@prisma-next/sql-operations@0.9.0': dependencies: - '@prisma-next/operations': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 + '@prisma-next/operations': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 arktype: 2.2.0 '@prisma-next/sql-orm-client@0.14.0(typescript@5.9.3)': @@ -5715,14 +5692,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-relational-core@0.8.0': + '@prisma-next/sql-relational-core@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/operations': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/operations': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/sql-operations': 0.9.0 + '@prisma-next/utils': 0.9.0 '@standard-schema/spec': 1.1.0 arktype: 2.2.0 ts-toolbelt: 9.6.0 @@ -5741,16 +5718,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-runtime@0.8.0': + '@prisma-next/sql-runtime@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/ids': 0.8.0 - '@prisma-next/operations': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/ids': 0.9.0 + '@prisma-next/operations': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/sql-operations': 0.9.0 + '@prisma-next/sql-relational-core': 0.9.0 + '@prisma-next/utils': 0.9.0 arktype: 2.2.0 '@prisma-next/sql-schema-ir@0.14.0(typescript@5.9.3)': @@ -5760,9 +5737,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-schema-ir@0.8.0': + '@prisma-next/sql-schema-ir@0.9.0': dependencies: - '@prisma-next/contract': 0.8.0 + '@prisma-next/contract': 0.9.0 + '@prisma-next/framework-components': 0.9.0 '@prisma-next/target-postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: @@ -5788,21 +5766,21 @@ snapshots: - magicast - typanion - '@prisma-next/target-postgres@0.8.0(typanion@3.14.0)': - dependencies: - '@prisma-next/cli': 0.8.0(typanion@3.14.0) - '@prisma-next/contract': 0.8.0 - '@prisma-next/errors': 0.8.0 - '@prisma-next/family-sql': 0.8.0 - '@prisma-next/framework-components': 0.8.0 - '@prisma-next/migration-tools': 0.8.0 - '@prisma-next/sql-contract': 0.8.0 - '@prisma-next/sql-errors': 0.8.0 - '@prisma-next/sql-operations': 0.8.0 - '@prisma-next/sql-relational-core': 0.8.0 - '@prisma-next/sql-schema-ir': 0.8.0 - '@prisma-next/ts-render': 0.8.0 - '@prisma-next/utils': 0.8.0 + '@prisma-next/target-postgres@0.9.0(typanion@3.14.0)': + dependencies: + '@prisma-next/cli': 0.9.0(typanion@3.14.0) + '@prisma-next/contract': 0.9.0 + '@prisma-next/errors': 0.9.0 + '@prisma-next/family-sql': 0.9.0 + '@prisma-next/framework-components': 0.9.0 + '@prisma-next/migration-tools': 0.9.0 + '@prisma-next/sql-contract': 0.9.0 + '@prisma-next/sql-errors': 0.9.0 + '@prisma-next/sql-operations': 0.9.0 + '@prisma-next/sql-relational-core': 0.9.0 + '@prisma-next/sql-schema-ir': 0.9.0 + '@prisma-next/ts-render': 0.9.0 + '@prisma-next/utils': 0.9.0 '@standard-schema/spec': 1.1.0 arktype: 2.2.0 pathe: 2.0.3 @@ -5814,13 +5792,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/ts-render@0.8.0': {} + '@prisma-next/ts-render@0.9.0': {} '@prisma-next/utils@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/utils@0.8.0': {} + '@prisma-next/utils@0.9.0': {} '@rollup/rollup-android-arm-eabi@4.59.0': optional: true @@ -7095,7 +7073,7 @@ snapshots: pg-connection-string@2.14.0: {} - pg-cursor@2.19.0(pg@8.20.0): + pg-cursor@2.21.0(pg@8.20.0): dependencies: pg: 8.20.0 From 14a276773c39920a1fe212bacf30c0c05d7855e6 Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 03:52:34 -0600 Subject: [PATCH 04/15] chore: upgrade @prisma-next/* to 0.10.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-transition changes (extension-author upgrade instructions 0.9→0.10): - storage.types kind-stamping: no-op for this package (no storage.types entries on disk or constructed in source; verified with the codemod's --check mode) Additional 0.10 SPI churn not covered by the instructions: - the storage IR is namespace-enveloped: tables live under storage.namespaces. (late-bound __unbound__ here) instead of flat storage.tables; the structural validator rejects the old shape - deriveStackSchemas walks every namespace's tables - all contract fixtures and storage reads updated to the new shape - FieldEventContext gained a required namespaceId coordinate; all direct codec-hook invocations in tests pass '__unbound__' - re-emitted src/contract.{json,d.ts} via prisma-next contract emit (storageHash changed with the namespace envelope) and re-pinned the baseline migration: end-contract.{json,d.ts}, migration.ts describe() to-hash, self-emitted ops.json/migration.json, migrations/refs/head.json Co-Authored-By: Claude Fable 5 --- .../end-contract.d.ts | 227 ++++--- .../end-contract.json | 62 +- .../migration.json | 10 +- .../migration.ts | 2 +- .../20260601T0000_install_eql_bundle/ops.json | 2 +- .../prisma-next/migrations/refs/head.json | 2 +- packages/prisma-next/package.json | 36 +- packages/prisma-next/src/contract.d.ts | 227 ++++--- packages/prisma-next/src/contract.json | 62 +- .../prisma-next/src/stack/derive-schemas.ts | 20 +- .../test/cipherstash-codec-numeric.test.ts | 3 + .../cipherstash-codec-other-codecs.test.ts | 5 + .../test/cipherstash-codec-string.test.ts | 2 + .../test/cipherstash-codec.test.ts | 2 +- .../prisma-next/test/derive-schemas.test.ts | 36 +- packages/prisma-next/test/descriptor.test.ts | 13 +- .../test/from-stack-divergence.test.ts | 30 +- packages/prisma-next/test/helpers.test.ts | 115 ++-- .../test/operator-lowering.helpers.ts | 111 ++-- .../test/psl-interpretation-numeric.test.ts | 13 +- .../psl-interpretation-other-types.test.ts | 13 +- .../test/psl-interpretation.test.ts | 13 +- pnpm-lock.yaml | 617 +++++++++--------- 23 files changed, 855 insertions(+), 768 deletions(-) diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts index a04e870b3..851a6f7ce 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts @@ -1,161 +1,152 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma-next contract emit'. // To regenerate, run: prisma-next contract emit +import type { CodecTypes as PgTypes } from '@prisma-next/target-postgres/codec-types'; +import type { JsonValue } from '@prisma-next/target-postgres/codec-types'; +import type { Char } from '@prisma-next/target-postgres/codec-types'; +import type { Varchar } from '@prisma-next/target-postgres/codec-types'; +import type { Numeric } from '@prisma-next/target-postgres/codec-types'; +import type { Bit } from '@prisma-next/target-postgres/codec-types'; +import type { VarBit } from '@prisma-next/target-postgres/codec-types'; +import type { Timestamp } from '@prisma-next/target-postgres/codec-types'; +import type { Timestamptz } from '@prisma-next/target-postgres/codec-types'; +import type { Time } from '@prisma-next/target-postgres/codec-types'; +import type { Timetz } from '@prisma-next/target-postgres/codec-types'; +import type { Interval } from '@prisma-next/target-postgres/codec-types'; +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; -import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types' +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, ExecutionHashBase, ProfileHashBase, StorageHashBase, -} from '@prisma-next/contract/types' -import type { - ContractWithTypeMaps, - TypeMaps as TypeMapsType, -} from '@prisma-next/sql-contract/types' -import type { - Bit, - Char, - Interval, - JsonValue, - Numeric, - CodecTypes as PgTypes, - Time, - Timestamp, - Timestamptz, - Timetz, - VarBit, - Varchar, -} from '@prisma-next/target-postgres/codec-types' +} from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4'> -export type ExecutionHash = ExecutionHashBase + StorageHashBase<'sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec'>; +export type ExecutionHash = ExecutionHashBase; export type ProfileHash = - ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'> + ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'>; -export type CodecTypes = PgTypes -export type OperationTypes = Record -export type LaneCodecTypes = CodecTypes -export type QueryOperationTypes = PgAdapterQueryOps -type DefaultLiteralValue< - CodecId extends string, - _Encoded, -> = CodecId extends keyof CodecTypes ? CodecTypes[CodecId]['output'] : _Encoded +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; export type FieldOutputTypes = { readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['output'] - readonly state: CodecTypes['pg/text@1']['output'] - readonly data: CodecTypes['pg/jsonb@1']['output'] - } -} + readonly id: CodecTypes['pg/text@1']['output']; + readonly state: CodecTypes['pg/text@1']['output']; + readonly data: CodecTypes['pg/jsonb@1']['output']; + }; +}; export type FieldInputTypes = { readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['input'] - readonly state: CodecTypes['pg/text@1']['input'] - readonly data: CodecTypes['pg/jsonb@1']['input'] - } -} + readonly id: CodecTypes['pg/text@1']['input']; + readonly state: CodecTypes['pg/text@1']['input']; + readonly data: CodecTypes['pg/jsonb@1']['input']; + }; +}; export type TypeMaps = TypeMapsType< CodecTypes, - OperationTypes, QueryOperationTypes, FieldOutputTypes, FieldInputTypes -> +>; type ContractBase = ContractType< { - readonly tables: { - readonly eql_v2_configuration: { - columns: { - readonly id: { - readonly nativeType: 'text' - readonly codecId: 'pg/text@1' - readonly nullable: false - } - readonly state: { - readonly nativeType: 'text' - readonly codecId: 'pg/text@1' - readonly nullable: false - } - readonly data: { - readonly nativeType: 'jsonb' - readonly codecId: 'pg/jsonb@1' - readonly nullable: false - } - } - primaryKey: { readonly columns: readonly ['id'] } - uniques: readonly [] - indexes: readonly [] - foreignKeys: readonly [] - } - } - readonly types: Record - readonly storageHash: StorageHash + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; }, { readonly EqlV2Configuration: { readonly fields: { readonly id: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/text@1' - } - } + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; readonly state: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/text@1' - } - } + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; readonly data: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/jsonb@1' - } - } - } - readonly relations: Record + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; + }; + }; + readonly relations: Record; readonly storage: { - readonly table: 'eql_v2_configuration' + readonly table: 'eql_v2_configuration'; readonly fields: { - readonly id: { readonly column: 'id' } - readonly state: { readonly column: 'state' } - readonly data: { readonly column: 'data' } - } - } - } + readonly id: { readonly column: 'id' }; + readonly state: { readonly column: 'state' }; + readonly data: { readonly column: 'data' }; + }; + }; + }; } > & { - readonly target: 'postgres' - readonly targetFamily: 'sql' - readonly roots: { readonly eql_v2_configuration: 'EqlV2Configuration' } + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { readonly eql_v2_configuration: 'EqlV2Configuration' }; readonly capabilities: { readonly postgres: { - readonly jsonAgg: true - readonly lateral: true - readonly limit: true - readonly orderBy: true - readonly returning: true - } + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; readonly sql: { - readonly defaultInInsert: true - readonly enums: true - readonly returning: true - } - } - readonly extensionPacks: {} - readonly meta: {} + readonly defaultInInsert: true; + readonly enums: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly meta: {}; - readonly profileHash: ProfileHash -} + readonly profileHash: ProfileHash; +}; -export type Contract = ContractWithTypeMaps +export type Contract = ContractWithTypeMaps; -export type Tables = Contract['storage']['tables'] -export type Models = Contract['models'] +export type Namespaces = Contract['storage']['namespaces']; +export type Models = Contract['models']; diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json index 4b32ae174..2f1cbe180 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json @@ -49,34 +49,42 @@ } }, "storage": { - "storageHash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "eql_v2_configuration": { + "columns": { + "data": { + "codecId": "pg/jsonb@1", + "nativeType": "jsonb", + "nullable": false + }, + "id": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "state": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] + }, + "uniques": [] } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": ["id"] - }, - "uniques": [] + } } - } + }, + "storageHash": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec" }, "capabilities": { "postgres": { @@ -99,4 +107,4 @@ "message": "This file is automatically generated by \"prisma-next contract emit\".", "regenerate": "To regenerate, run: prisma-next contract emit" } -} +} \ No newline at end of file diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json index 1ed73da1a..9f48b0479 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json @@ -1,13 +1,15 @@ { "from": null, - "to": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", + "to": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec", "labels": [], - "providedInvariants": ["cipherstash:install-eql-bundle-v1"], + "providedInvariants": [ + "cipherstash:install-eql-bundle-v1" + ], "createdAt": "2026-05-09T03:42:56.902Z", "hints": { "used": [], "applied": [], "plannerVersion": "2.0.0" }, - "migrationHash": "sha256:76923a92561cdad65c64088ce999bf7afe853b80aac0b787b0d271b0e623abbc" -} + "migrationHash": "sha256:8ca995537c0f89e7064f720471207be4bdd3f4a3186c544adbeba9f54f713a1c" +} \ No newline at end of file diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts index e43c50839..f8593b655 100755 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts @@ -36,7 +36,7 @@ export default class M extends Migration { override describe() { return { from: null, - to: 'sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4', + to: 'sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec', } } diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/ops.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/ops.json index c124cd04e..47ee0ca65 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/ops.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/ops.json @@ -25,4 +25,4 @@ } ] } -] +] \ No newline at end of file diff --git a/packages/prisma-next/migrations/refs/head.json b/packages/prisma-next/migrations/refs/head.json index 78f580898..0faf88aa6 100644 --- a/packages/prisma-next/migrations/refs/head.json +++ b/packages/prisma-next/migrations/refs/head.json @@ -1,4 +1,4 @@ { - "hash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", + "hash": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec", "invariants": ["cipherstash:install-eql-bundle-v1"] } diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json index 50abcc289..bae7c37ca 100644 --- a/packages/prisma-next/package.json +++ b/packages/prisma-next/package.json @@ -80,28 +80,28 @@ }, "dependencies": { "@cipherstash/stack": "workspace:*", - "@prisma-next/contract": "0.9.0", - "@prisma-next/family-sql": "0.9.0", - "@prisma-next/framework-components": "0.9.0", - "@prisma-next/migration-tools": "0.9.0", - "@prisma-next/sql-contract": "0.9.0", - "@prisma-next/sql-operations": "0.9.0", - "@prisma-next/sql-relational-core": "0.9.0", - "@prisma-next/sql-runtime": "0.9.0", - "@prisma-next/ts-render": "0.9.0", - "@prisma-next/utils": "0.9.0", + "@prisma-next/contract": "0.10.0", + "@prisma-next/family-sql": "0.10.0", + "@prisma-next/framework-components": "0.10.0", + "@prisma-next/migration-tools": "0.10.0", + "@prisma-next/sql-contract": "0.10.0", + "@prisma-next/sql-operations": "0.10.0", + "@prisma-next/sql-relational-core": "0.10.0", + "@prisma-next/sql-runtime": "0.10.0", + "@prisma-next/ts-render": "0.10.0", + "@prisma-next/utils": "0.10.0", "arktype": "^2.1.29" }, "devDependencies": { - "@prisma-next/adapter-postgres": "0.9.0", - "@prisma-next/cli": "0.9.0", - "@prisma-next/driver-postgres": "0.9.0", + "@prisma-next/adapter-postgres": "0.10.0", + "@prisma-next/cli": "0.10.0", + "@prisma-next/driver-postgres": "0.10.0", "@prisma-next/extension-author-tools": "0.14.0", - "@prisma-next/psl-parser": "0.9.0", - "@prisma-next/sql-contract-psl": "0.9.0", - "@prisma-next/sql-contract-ts": "0.9.0", - "@prisma-next/sql-schema-ir": "0.9.0", - "@prisma-next/target-postgres": "0.9.0", + "@prisma-next/psl-parser": "0.10.0", + "@prisma-next/sql-contract-psl": "0.10.0", + "@prisma-next/sql-contract-ts": "0.10.0", + "@prisma-next/sql-schema-ir": "0.10.0", + "@prisma-next/target-postgres": "0.10.0", "pathe": "^2.0.3", "tsup": "catalog:repo", "typescript": "catalog:repo", diff --git a/packages/prisma-next/src/contract.d.ts b/packages/prisma-next/src/contract.d.ts index a04e870b3..851a6f7ce 100644 --- a/packages/prisma-next/src/contract.d.ts +++ b/packages/prisma-next/src/contract.d.ts @@ -1,161 +1,152 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma-next contract emit'. // To regenerate, run: prisma-next contract emit +import type { CodecTypes as PgTypes } from '@prisma-next/target-postgres/codec-types'; +import type { JsonValue } from '@prisma-next/target-postgres/codec-types'; +import type { Char } from '@prisma-next/target-postgres/codec-types'; +import type { Varchar } from '@prisma-next/target-postgres/codec-types'; +import type { Numeric } from '@prisma-next/target-postgres/codec-types'; +import type { Bit } from '@prisma-next/target-postgres/codec-types'; +import type { VarBit } from '@prisma-next/target-postgres/codec-types'; +import type { Timestamp } from '@prisma-next/target-postgres/codec-types'; +import type { Timestamptz } from '@prisma-next/target-postgres/codec-types'; +import type { Time } from '@prisma-next/target-postgres/codec-types'; +import type { Timetz } from '@prisma-next/target-postgres/codec-types'; +import type { Interval } from '@prisma-next/target-postgres/codec-types'; +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; -import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types' +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, ExecutionHashBase, ProfileHashBase, StorageHashBase, -} from '@prisma-next/contract/types' -import type { - ContractWithTypeMaps, - TypeMaps as TypeMapsType, -} from '@prisma-next/sql-contract/types' -import type { - Bit, - Char, - Interval, - JsonValue, - Numeric, - CodecTypes as PgTypes, - Time, - Timestamp, - Timestamptz, - Timetz, - VarBit, - Varchar, -} from '@prisma-next/target-postgres/codec-types' +} from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4'> -export type ExecutionHash = ExecutionHashBase + StorageHashBase<'sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec'>; +export type ExecutionHash = ExecutionHashBase; export type ProfileHash = - ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'> + ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'>; -export type CodecTypes = PgTypes -export type OperationTypes = Record -export type LaneCodecTypes = CodecTypes -export type QueryOperationTypes = PgAdapterQueryOps -type DefaultLiteralValue< - CodecId extends string, - _Encoded, -> = CodecId extends keyof CodecTypes ? CodecTypes[CodecId]['output'] : _Encoded +export type CodecTypes = PgTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; export type FieldOutputTypes = { readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['output'] - readonly state: CodecTypes['pg/text@1']['output'] - readonly data: CodecTypes['pg/jsonb@1']['output'] - } -} + readonly id: CodecTypes['pg/text@1']['output']; + readonly state: CodecTypes['pg/text@1']['output']; + readonly data: CodecTypes['pg/jsonb@1']['output']; + }; +}; export type FieldInputTypes = { readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['input'] - readonly state: CodecTypes['pg/text@1']['input'] - readonly data: CodecTypes['pg/jsonb@1']['input'] - } -} + readonly id: CodecTypes['pg/text@1']['input']; + readonly state: CodecTypes['pg/text@1']['input']; + readonly data: CodecTypes['pg/jsonb@1']['input']; + }; +}; export type TypeMaps = TypeMapsType< CodecTypes, - OperationTypes, QueryOperationTypes, FieldOutputTypes, FieldInputTypes -> +>; type ContractBase = ContractType< { - readonly tables: { - readonly eql_v2_configuration: { - columns: { - readonly id: { - readonly nativeType: 'text' - readonly codecId: 'pg/text@1' - readonly nullable: false - } - readonly state: { - readonly nativeType: 'text' - readonly codecId: 'pg/text@1' - readonly nullable: false - } - readonly data: { - readonly nativeType: 'jsonb' - readonly codecId: 'pg/jsonb@1' - readonly nullable: false - } - } - primaryKey: { readonly columns: readonly ['id'] } - uniques: readonly [] - indexes: readonly [] - foreignKeys: readonly [] - } - } - readonly types: Record - readonly storageHash: StorageHash + readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; }, { readonly EqlV2Configuration: { readonly fields: { readonly id: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/text@1' - } - } + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; readonly state: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/text@1' - } - } + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; readonly data: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/jsonb@1' - } - } - } - readonly relations: Record + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; + }; + }; + readonly relations: Record; readonly storage: { - readonly table: 'eql_v2_configuration' + readonly table: 'eql_v2_configuration'; readonly fields: { - readonly id: { readonly column: 'id' } - readonly state: { readonly column: 'state' } - readonly data: { readonly column: 'data' } - } - } - } + readonly id: { readonly column: 'id' }; + readonly state: { readonly column: 'state' }; + readonly data: { readonly column: 'data' }; + }; + }; + }; } > & { - readonly target: 'postgres' - readonly targetFamily: 'sql' - readonly roots: { readonly eql_v2_configuration: 'EqlV2Configuration' } + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { readonly eql_v2_configuration: 'EqlV2Configuration' }; readonly capabilities: { readonly postgres: { - readonly jsonAgg: true - readonly lateral: true - readonly limit: true - readonly orderBy: true - readonly returning: true - } + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; readonly sql: { - readonly defaultInInsert: true - readonly enums: true - readonly returning: true - } - } - readonly extensionPacks: {} - readonly meta: {} + readonly defaultInInsert: true; + readonly enums: true; + readonly returning: true; + }; + }; + readonly extensionPacks: {}; + readonly meta: {}; - readonly profileHash: ProfileHash -} + readonly profileHash: ProfileHash; +}; -export type Contract = ContractWithTypeMaps +export type Contract = ContractWithTypeMaps; -export type Tables = Contract['storage']['tables'] -export type Models = Contract['models'] +export type Namespaces = Contract['storage']['namespaces']; +export type Models = Contract['models']; diff --git a/packages/prisma-next/src/contract.json b/packages/prisma-next/src/contract.json index 4b32ae174..2f1cbe180 100644 --- a/packages/prisma-next/src/contract.json +++ b/packages/prisma-next/src/contract.json @@ -49,34 +49,42 @@ } }, "storage": { - "storageHash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false + "namespaces": { + "__unbound__": { + "id": "__unbound__", + "kind": "postgres-unbound-schema", + "tables": { + "eql_v2_configuration": { + "columns": { + "data": { + "codecId": "pg/jsonb@1", + "nativeType": "jsonb", + "nullable": false + }, + "id": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "state": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] + }, + "uniques": [] } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": ["id"] - }, - "uniques": [] + } } - } + }, + "storageHash": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec" }, "capabilities": { "postgres": { @@ -99,4 +107,4 @@ "message": "This file is automatically generated by \"prisma-next contract emit\".", "regenerate": "To regenerate, run: prisma-next contract emit" } -} +} \ No newline at end of file diff --git a/packages/prisma-next/src/stack/derive-schemas.ts b/packages/prisma-next/src/stack/derive-schemas.ts index 8b48d486f..95084fd63 100644 --- a/packages/prisma-next/src/stack/derive-schemas.ts +++ b/packages/prisma-next/src/stack/derive-schemas.ts @@ -6,7 +6,8 @@ * framework knows about — its physical table name (after `@map(...)` * collapsing), its physical column name, its codec id * (`cipherstash/@1`), and its per-flag search-mode `typeParams`. - * `deriveStackSchemas` walks `storage.tables` and returns one + * `deriveStackSchemas` walks the tables of every `storage.namespaces` + * entry and returns one * `EncryptedTable` per table with at least one cipherstash-codec'd * column, ready to pass to `Encryption({ schemas })`. Skipping the * hand-written second declaration removes a runtime-correctness @@ -41,7 +42,14 @@ import { */ export interface ContractStorageView { readonly storage?: { - readonly tables?: Readonly> + readonly namespaces?: Readonly< + Record< + string, + { + readonly tables?: Readonly> + } + > + > } } @@ -86,8 +94,12 @@ type CipherstashFlag = keyof typeof FLAG_DISPATCH export function deriveStackSchemas( contractJson: ContractStorageView, ): ReadonlyArray> { - const tables = contractJson.storage?.tables - if (!tables) return [] + const namespaces = contractJson.storage?.namespaces + if (!namespaces) return [] + const tables: Record = {} + for (const namespace of Object.values(namespaces)) { + Object.assign(tables, namespace.tables) + } const result: EncryptedTable[] = [] diff --git a/packages/prisma-next/test/cipherstash-codec-numeric.test.ts b/packages/prisma-next/test/cipherstash-codec-numeric.test.ts index 7a4847aed..195c9265f 100644 --- a/packages/prisma-next/test/cipherstash-codec-numeric.test.ts +++ b/packages/prisma-next/test/cipherstash-codec-numeric.test.ts @@ -31,6 +31,7 @@ describe('cipherstashDoubleCodecHooks — flag → index mapping', () => { next?: Partial | undefined codecId: string }): { + readonly namespaceId: string readonly tableName: string readonly fieldName: string readonly priorField?: StorageColumn @@ -42,6 +43,7 @@ describe('cipherstashDoubleCodecHooks — flag → index mapping', () => { nullable: false, } return { + namespaceId: '__unbound__', tableName: TABLE, fieldName: FIELD, ...(args.prior !== undefined @@ -124,6 +126,7 @@ describe('cipherstashDoubleCodecHooks — flag → index mapping', () => { describe('cipherstashBigIntCodecHooks — cast_as=big_int', () => { it("emits add_search_config(unique) with cast_as='big_int' when equality flips on", () => { const ctxArg = { + namespaceId: '__unbound__', tableName: TABLE, fieldName: FIELD, newField: { diff --git a/packages/prisma-next/test/cipherstash-codec-other-codecs.test.ts b/packages/prisma-next/test/cipherstash-codec-other-codecs.test.ts index 815acde92..103ecc3e5 100644 --- a/packages/prisma-next/test/cipherstash-codec-other-codecs.test.ts +++ b/packages/prisma-next/test/cipherstash-codec-other-codecs.test.ts @@ -32,6 +32,7 @@ const FIELD = 'email' describe('cipherstashDateCodecHooks — cast_as=date', () => { it("emits add_search_config(unique) with cast_as='date' when equality flips on", () => { const ctxArg = { + namespaceId: '__unbound__', tableName: TABLE, fieldName: FIELD, newField: { @@ -55,6 +56,7 @@ describe('cipherstashDateCodecHooks — cast_as=date', () => { describe('cipherstashBooleanCodecHooks — equality-only, cast_as=boolean', () => { it('emits a single add_search_config(unique) with cast_as=boolean when equality flips on', () => { const ctxArg = { + namespaceId: '__unbound__', tableName: TABLE, fieldName: FIELD, newField: { @@ -74,6 +76,7 @@ describe('cipherstashBooleanCodecHooks — equality-only, cast_as=boolean', () = it('does not emit ore ops — booleans have no orderAndRange flag', () => { const ctxArg = { + namespaceId: '__unbound__', tableName: TABLE, fieldName: FIELD, newField: { @@ -94,6 +97,7 @@ describe('cipherstashBooleanCodecHooks — equality-only, cast_as=boolean', () = describe('cipherstashJsonCodecHooks — searchableJson → ste_vec, cast_as=jsonb', () => { it('emits add_search_config(ste_vec) with cast_as=jsonb when searchableJson flips on', () => { const ctxArg = { + namespaceId: '__unbound__', tableName: TABLE, fieldName: FIELD, newField: { @@ -113,6 +117,7 @@ describe('cipherstashJsonCodecHooks — searchableJson → ste_vec, cast_as=json it('emits remove_search_config(ste_vec) on drop when searchableJson was previously enabled', () => { const ctxArg = { + namespaceId: '__unbound__', tableName: TABLE, fieldName: FIELD, priorField: { diff --git a/packages/prisma-next/test/cipherstash-codec-string.test.ts b/packages/prisma-next/test/cipherstash-codec-string.test.ts index ad1eca0b9..52cc22bab 100644 --- a/packages/prisma-next/test/cipherstash-codec-string.test.ts +++ b/packages/prisma-next/test/cipherstash-codec-string.test.ts @@ -40,6 +40,7 @@ function ctx(args: { tableName?: string fieldName?: string }): { + readonly namespaceId: string readonly tableName: string readonly fieldName: string readonly priorField?: StorageColumn @@ -51,6 +52,7 @@ function ctx(args: { nullable: false, } return { + namespaceId: '__unbound__', tableName: args.tableName ?? TABLE, fieldName: args.fieldName ?? FIELD, ...(args.prior !== undefined diff --git a/packages/prisma-next/test/cipherstash-codec.test.ts b/packages/prisma-next/test/cipherstash-codec.test.ts index 6327c8095..d90c0e858 100644 --- a/packages/prisma-next/test/cipherstash-codec.test.ts +++ b/packages/prisma-next/test/cipherstash-codec.test.ts @@ -119,7 +119,7 @@ describe('planFieldEventOperations driving the cipherstash hook', () => { profileHash: profileHash('sha256:test'), storage: new SqlStorage({ storageHash: 'sha256:test' as StorageHashBase, - tables, + namespaces: { __unbound__: { id: '__unbound__', tables } }, }), models: {}, roots: {}, diff --git a/packages/prisma-next/test/derive-schemas.test.ts b/packages/prisma-next/test/derive-schemas.test.ts index 89d893fef..b1a7e5c4e 100644 --- a/packages/prisma-next/test/derive-schemas.test.ts +++ b/packages/prisma-next/test/derive-schemas.test.ts @@ -28,17 +28,24 @@ function makeContract( ) { return { storage: { - tables: Object.fromEntries( - Object.entries(tables).map(([name, cols]) => [ - name, - { - columns: cols as Record< - string, - { codecId: string; typeParams?: Record | null } - >, - }, - ]), - ), + namespaces: { + __unbound__: { + tables: Object.fromEntries( + Object.entries(tables).map(([name, cols]) => [ + name, + { + columns: cols as Record< + string, + { + codecId: string + typeParams?: Record | null + } + >, + }, + ]), + ), + }, + }, }, } } @@ -47,7 +54,12 @@ describe('deriveStackSchemas', () => { it('returns an empty array when contract has no storage tables', () => { expect(deriveStackSchemas({})).toEqual([]) expect(deriveStackSchemas({ storage: {} })).toEqual([]) - expect(deriveStackSchemas({ storage: { tables: {} } })).toEqual([]) + expect(deriveStackSchemas({ storage: { namespaces: {} } })).toEqual([]) + expect( + deriveStackSchemas({ + storage: { namespaces: { __unbound__: { tables: {} } } }, + }), + ).toEqual([]) }) it('skips tables with no cipherstash columns', () => { diff --git a/packages/prisma-next/test/descriptor.test.ts b/packages/prisma-next/test/descriptor.test.ts index 71eca486a..c85ad4d8b 100644 --- a/packages/prisma-next/test/descriptor.test.ts +++ b/packages/prisma-next/test/descriptor.test.ts @@ -46,9 +46,16 @@ describe('cipherstash extension descriptor (contract-space package layout)', () it('exposes a contractSpace declaring the eql_v2_configuration table', () => { const space = cipherstashExtensionDescriptor.contractSpace expect(space).toBeDefined() - expect(Object.keys(space!.contractJson.storage.tables)).toEqual([ - EQL_V2_CONFIGURATION_TABLE, - ]) + // Since 0.10 the storage IR is namespace-enveloped; the extension's + // sole table lives in the late-bound `__unbound__` namespace. + const namespaces = space!.contractJson.storage.namespaces as Record< + string, + { tables?: Record } + > + const tables = Object.values(namespaces).flatMap((ns) => + Object.keys(ns.tables ?? {}), + ) + expect(tables).toEqual([EQL_V2_CONFIGURATION_TABLE]) }) it('publishes one baseline migration sourced from the on-disk emit pipeline', () => { diff --git a/packages/prisma-next/test/from-stack-divergence.test.ts b/packages/prisma-next/test/from-stack-divergence.test.ts index e19842c0b..223de8597 100644 --- a/packages/prisma-next/test/from-stack-divergence.test.ts +++ b/packages/prisma-next/test/from-stack-divergence.test.ts @@ -24,16 +24,20 @@ import { cipherstashFromStack } from '../src/stack/from-stack' function makeContract() { return { storage: { - tables: { - users: { - columns: { - email: { - codecId: CIPHERSTASH_STRING_CODEC_ID, - typeParams: { equality: true, freeTextSearch: true }, - }, - verified: { - codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, - typeParams: { equality: true }, + namespaces: { + __unbound__: { + tables: { + users: { + columns: { + email: { + codecId: CIPHERSTASH_STRING_CODEC_ID, + typeParams: { equality: true, freeTextSearch: true }, + }, + verified: { + codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, + typeParams: { equality: true }, + }, + }, }, }, }, @@ -104,7 +108,11 @@ describe('cipherstashFromStack — divergence check', () => { }) it('throws when the contract has no cipherstash columns and no override is supplied', async () => { - const emptyContract = { storage: { tables: { users: { columns: {} } } } } + const emptyContract = { + storage: { + namespaces: { __unbound__: { tables: { users: { columns: {} } } } }, + }, + } await expect( cipherstashFromStack({ contractJson: emptyContract }), ).rejects.toThrow(/no cipherstash columns found/) diff --git a/packages/prisma-next/test/helpers.test.ts b/packages/prisma-next/test/helpers.test.ts index a525e0ff9..03e99dead 100644 --- a/packages/prisma-next/test/helpers.test.ts +++ b/packages/prisma-next/test/helpers.test.ts @@ -70,66 +70,69 @@ function emptySdk(): CipherstashSdk { const TABLE = 'user' -const contract = validateSqlContractFully( - { - target: 'postgres', - targetFamily: 'sql', - profileHash: 'sha256:cipherstash-helpers-test', - roots: {}, - capabilities: {}, - extensionPacks: {}, - meta: {}, - storage: { - storageHash: 'sha256:cipherstash-helpers-test-storage', - tables: { - [TABLE]: { - columns: { - id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false }, - email: { - codecId: CIPHERSTASH_STRING_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - score: { - codecId: CIPHERSTASH_DOUBLE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - amount: { - codecId: CIPHERSTASH_BIGINT_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - birthday: { - codecId: CIPHERSTASH_DATE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - enabled: { - codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - payload: { - codecId: CIPHERSTASH_JSON_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - plain: { - codecId: 'pg/text@1', - nativeType: 'text', - nullable: false, +const contract = validateSqlContractFully({ + target: 'postgres', + targetFamily: 'sql', + profileHash: 'sha256:cipherstash-helpers-test', + roots: {}, + capabilities: {}, + extensionPacks: {}, + meta: {}, + storage: { + storageHash: 'sha256:cipherstash-helpers-test-storage', + namespaces: { + __unbound__: { + id: '__unbound__', + tables: { + [TABLE]: { + columns: { + id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false }, + email: { + codecId: CIPHERSTASH_STRING_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + score: { + codecId: CIPHERSTASH_DOUBLE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + amount: { + codecId: CIPHERSTASH_BIGINT_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + birthday: { + codecId: CIPHERSTASH_DATE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + enabled: { + codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + payload: { + codecId: CIPHERSTASH_JSON_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + plain: { + codecId: 'pg/text@1', + nativeType: 'text', + nullable: false, + }, }, + uniques: [], + indexes: [], + foreignKeys: [], }, - uniques: [], - indexes: [], - foreignKeys: [], }, }, }, - models: {}, }, -) + models: {}, +}) const stubRuntimeTarget: RuntimeTargetDescriptor<'sql', 'postgres'> = { kind: 'target', @@ -277,9 +280,7 @@ describe('cipherstashJsonbPathQueryFirst — AST shape and SQL snapshot', () => expect(lowered.sql).toMatchInlineSnapshot( `"SELECT eql_v2.jsonb_path_query_first("user"."payload", $1) AS "first_email" FROM "user""`, ) - expect(lowered.params).toEqual([ - { kind: 'literal', value: '$.user.email' }, - ]) + expect(lowered.params).toEqual([{ kind: 'literal', value: '$.user.email' }]) }) }) diff --git a/packages/prisma-next/test/operator-lowering.helpers.ts b/packages/prisma-next/test/operator-lowering.helpers.ts index f5a9d80d2..d217411d4 100644 --- a/packages/prisma-next/test/operator-lowering.helpers.ts +++ b/packages/prisma-next/test/operator-lowering.helpers.ts @@ -67,66 +67,69 @@ export function emptySdk(): CipherstashSdk { export const TABLE = 'user' export const COLUMN = 'email' -export const contract = validateSqlContractFully( - { - target: 'postgres', - targetFamily: 'sql', - profileHash: 'sha256:cipherstash-operator-lowering-test', - roots: {}, - capabilities: {}, - extensionPacks: {}, - meta: {}, - storage: { - storageHash: 'sha256:cipherstash-operator-lowering-test-storage', - tables: { - [TABLE]: { - columns: { - id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false }, - [COLUMN]: { - codecId: CIPHERSTASH_STRING_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - // Per-codec columns so the trait-dispatched operators - // can be exercised against each column type (the - // postgres renderer reads `nativeType` from the codec - // descriptor at lower time; the column is what gives - // the renderer the codec id to look up). - score: { - codecId: CIPHERSTASH_DOUBLE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - amount: { - codecId: CIPHERSTASH_BIGINT_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - birthday: { - codecId: CIPHERSTASH_DATE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - enabled: { - codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - payload: { - codecId: CIPHERSTASH_JSON_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, +export const contract = validateSqlContractFully({ + target: 'postgres', + targetFamily: 'sql', + profileHash: 'sha256:cipherstash-operator-lowering-test', + roots: {}, + capabilities: {}, + extensionPacks: {}, + meta: {}, + storage: { + storageHash: 'sha256:cipherstash-operator-lowering-test-storage', + namespaces: { + __unbound__: { + id: '__unbound__', + tables: { + [TABLE]: { + columns: { + id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false }, + [COLUMN]: { + codecId: CIPHERSTASH_STRING_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + // Per-codec columns so the trait-dispatched operators + // can be exercised against each column type (the + // postgres renderer reads `nativeType` from the codec + // descriptor at lower time; the column is what gives + // the renderer the codec id to look up). + score: { + codecId: CIPHERSTASH_DOUBLE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + amount: { + codecId: CIPHERSTASH_BIGINT_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + birthday: { + codecId: CIPHERSTASH_DATE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + enabled: { + codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + payload: { + codecId: CIPHERSTASH_JSON_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, }, + uniques: [], + indexes: [], + foreignKeys: [], }, - uniques: [], - indexes: [], - foreignKeys: [], }, }, }, - models: {}, }, -) + models: {}, +}) // Stub runtime target — the Postgres adapter only consults `familyId` / // `targetId` on the target during `create`. Replicates the helper at diff --git a/packages/prisma-next/test/psl-interpretation-numeric.test.ts b/packages/prisma-next/test/psl-interpretation-numeric.test.ts index b24984c75..cfc8ce181 100644 --- a/packages/prisma-next/test/psl-interpretation-numeric.test.ts +++ b/packages/prisma-next/test/psl-interpretation-numeric.test.ts @@ -55,7 +55,18 @@ type StorageView = { > readonly types?: Record> } -const asStorage = (storage: unknown): StorageView => storage as StorageView +// Since 0.10 the storage IR is namespace-enveloped: tables live under +// `storage.namespaces.__unbound__`, while `types` stays at the root. +const asStorage = (storage: unknown): StorageView => { + const s = storage as { + readonly namespaces?: Record + readonly types?: StorageView['types'] + } + return { + tables: s.namespaces?.['__unbound__']?.tables ?? {}, + ...(s.types !== undefined ? { types: s.types } : {}), + } +} describe('PSL interpretation: cipherstash.EncryptedDouble constructor', () => { it('lowers full args to a column with cipherstash/double@1 codec, eql_v2_encrypted nativeType', () => { diff --git a/packages/prisma-next/test/psl-interpretation-other-types.test.ts b/packages/prisma-next/test/psl-interpretation-other-types.test.ts index ead66a703..2f4d2ed05 100644 --- a/packages/prisma-next/test/psl-interpretation-other-types.test.ts +++ b/packages/prisma-next/test/psl-interpretation-other-types.test.ts @@ -56,7 +56,18 @@ type StorageView = { > readonly types?: Record> } -const asStorage = (storage: unknown): StorageView => storage as StorageView +// Since 0.10 the storage IR is namespace-enveloped: tables live under +// `storage.namespaces.__unbound__`, while `types` stays at the root. +const asStorage = (storage: unknown): StorageView => { + const s = storage as { + readonly namespaces?: Record + readonly types?: StorageView['types'] + } + return { + tables: s.namespaces?.['__unbound__']?.tables ?? {}, + ...(s.types !== undefined ? { types: s.types } : {}), + } +} describe('PSL interpretation: cipherstash.EncryptedDate constructor', () => { it('lowers full args to a column with cipherstash/date@1 codec, eql_v2_encrypted nativeType', () => { diff --git a/packages/prisma-next/test/psl-interpretation.test.ts b/packages/prisma-next/test/psl-interpretation.test.ts index 03d3811e4..0d6f4a0ee 100644 --- a/packages/prisma-next/test/psl-interpretation.test.ts +++ b/packages/prisma-next/test/psl-interpretation.test.ts @@ -72,7 +72,18 @@ type StorageView = { > readonly types?: Record> } -const asStorage = (storage: unknown): StorageView => storage as StorageView +// Since 0.10 the storage IR is namespace-enveloped: tables live under +// `storage.namespaces.__unbound__`, while `types` stays at the root. +const asStorage = (storage: unknown): StorageView => { + const s = storage as { + readonly namespaces?: Record + readonly types?: StorageView['types'] + } + return { + tables: s.namespaces?.['__unbound__']?.tables ?? {}, + ...(s.types !== undefined ? { types: s.types } : {}), + } +} describe('PSL interpretation: cipherstash.EncryptedString constructor', () => { it('lowers full args to a column with codecId, nativeType, typeParams', () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9a38ca7f..713d473fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,66 +408,66 @@ importers: specifier: workspace:* version: link:../stack '@prisma-next/contract': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/family-sql': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/framework-components': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/migration-tools': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/sql-contract': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/sql-operations': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/sql-relational-core': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/sql-runtime': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/ts-render': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/utils': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 arktype: specifier: ^2.1.29 version: 2.2.0 devDependencies: '@prisma-next/adapter-postgres': - specifier: 0.9.0 - version: 0.9.0(typanion@3.14.0) + specifier: 0.10.0 + version: 0.10.0(typanion@3.14.0) '@prisma-next/cli': - specifier: 0.9.0 - version: 0.9.0(typanion@3.14.0) + specifier: 0.10.0 + version: 0.10.0(typanion@3.14.0) '@prisma-next/driver-postgres': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/extension-author-tools': specifier: 0.14.0 version: 0.14.0(typescript@5.9.3) '@prisma-next/psl-parser': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/sql-contract-psl': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/sql-contract-ts': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/sql-schema-ir': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.10.0 + version: 0.10.0 '@prisma-next/target-postgres': - specifier: 0.9.0 - version: 0.9.0(typanion@3.14.0) + specifier: 0.10.0 + version: 0.10.0(typanion@3.14.0) pathe: specifier: ^2.0.3 version: 2.0.3 @@ -1809,6 +1809,9 @@ packages: '@posthog/types@1.373.5': resolution: {integrity: sha512-K7STCnRG/WBE1q0BwEkIcrJB5OqECaymsQj6Hp4Ntvaek4dqHkZGfp6hxwIPqQPjlOXwidwPLo+XGsn+CoZUyw==} + '@prisma-next/adapter-postgres@0.10.0': + resolution: {integrity: sha512-fKTWIGTWmaczeK6KO9tJJqmu/kZ6RQr+BWU4/JkN+emKeGKdvza93+gXdqrSGNRTQlCAL3qeCyEHX9j/pZPyHg==} + '@prisma-next/adapter-postgres@0.14.0': resolution: {integrity: sha512-c90KFtI74WU+8YH3uhVg1DTmD10T2BpINzzys9F3n1jTM2Am0tXKx53V05Ev+7H1VBrjBoksZPZ92WWxVEXLew==} engines: {node: '>=24'} @@ -1818,9 +1821,6 @@ packages: typescript: optional: true - '@prisma-next/adapter-postgres@0.9.0': - resolution: {integrity: sha512-KbyquUsBgJB7xpdc6Hz5al784Is9vwNlL7K+nR1SFTbSgWbEDBpjD1hV3qG9nMLeXJ3rDg3/GipXXzcKoa5JEw==} - '@prisma-next/cli-telemetry@0.14.0': resolution: {integrity: sha512-JaP/+09tIL6zH+5WQS507db2HQ0B47HRR2AZD8VHmsw/tMwBDZqKVdIA5KptPMVSR3pcnXDygGvG7E8N+PSPRg==} engines: {node: '>=24'} @@ -1830,6 +1830,10 @@ packages: typescript: optional: true + '@prisma-next/cli@0.10.0': + resolution: {integrity: sha512-f8WBdK1pnF9gXMw+iSu6vJfHtW5ETcXXCZ4aDZDcO5ynufTzIAkQxI4yvap7dORUhH0e37Mtl4/YylUr/a1OOA==} + hasBin: true + '@prisma-next/cli@0.14.0': resolution: {integrity: sha512-wN4mZc0K1m6wB4NXSEdrA2RCfc8zHBs5yJq0G7XoWQ8J8vvX/hR2kkukJv9QfEYFEIMyOT7vjbSBh2vj4NQ56g==} engines: {node: '>=24'} @@ -1840,9 +1844,9 @@ packages: typescript: optional: true - '@prisma-next/cli@0.9.0': - resolution: {integrity: sha512-p66znaSHGjo87YI8nt5hs3NZlyqb7mhrF+9OAmRvfORQ8xXSvKjA1sMmH9hhn4bqKbPYZtjzd7wEMjzWYyWBJg==} - hasBin: true + '@prisma-next/config@0.10.0': + resolution: {integrity: sha512-NVd+lHqGiopgZsqJZh9FAfSZHdzKtnCnokWgiuvrEC4uCK24G1erPRmRskkpg7fIeW8ZtZEs/24yjLbxYhj7Vg==} + engines: {node: '>=20'} '@prisma-next/config@0.14.0': resolution: {integrity: sha512-7U4Y5GeicvWy0dcg78J8arzqe13r97Zj8yB/e+Uea6M8LZxV2poW/afHlYnU0VXhLTZ4F29CdlWOUmCAKpl9mg==} @@ -1853,9 +1857,8 @@ packages: typescript: optional: true - '@prisma-next/config@0.9.0': - resolution: {integrity: sha512-JoHkFvKA5cnnjc/IR8AB1/jpCe+6KK4yRwl4t1hL1T6hIHwi70BAOMbVcS6Fsxf9qnUA9N1e6dcnNibsPLmo5Q==} - engines: {node: '>=20'} + '@prisma-next/contract-authoring@0.10.0': + resolution: {integrity: sha512-qC0wenYjOrdgzXOBpAvjjteijsVWQ3EeGz/3nLUiEPTEJ7QFxavOvYGqLLlBaVBikR4stHO09SVUqpWx3koS8w==} '@prisma-next/contract-authoring@0.14.0': resolution: {integrity: sha512-79aLJO04p/XRX2QvIlFMR2ZvbqvI0xFAU5idgYySoevq8bSY9ORGA0XchSbhFl0d6d1VfSEZowRPwzvgl/6lsg==} @@ -1866,8 +1869,9 @@ packages: typescript: optional: true - '@prisma-next/contract-authoring@0.9.0': - resolution: {integrity: sha512-RSKadW4EnmB63uuOSPabZmzBiNedcEHdTXUcd19qCACP0QYJF3+hPO9c7abiEqy26E2O5XcbCqHJwMejH91uMw==} + '@prisma-next/contract@0.10.0': + resolution: {integrity: sha512-tqLXa2nt5mfuIj2BlmyvfUYl2mUlfBQMYRMHiUiBtvTYukVQLCQmH3IrhjU4+FX1Y5ZmCw14WztJomFOzM1H1w==} + engines: {node: '>=20'} '@prisma-next/contract@0.14.0': resolution: {integrity: sha512-HMeduqWGl1NpRyhEvsswF1T8PSBPfbpvMNYMegiYPt2ck5oClhbl0LHkgAIHsOjBg3IJv2VhRqDhQiuRVbkC4A==} @@ -1878,9 +1882,8 @@ packages: typescript: optional: true - '@prisma-next/contract@0.9.0': - resolution: {integrity: sha512-9BJiFGnJWvIA6V8LQPru0qd7hBl7fbPqoOwVChIpow1YfQOBu12u85R0mRJRjqNJmk3BdNqNm0zU3KNEln80AA==} - engines: {node: '>=20'} + '@prisma-next/driver-postgres@0.10.0': + resolution: {integrity: sha512-+gyl4gU2K6xSoC+3rJX+hbhGVeitst6ZkVWE4CsYcKvT1L9WRlvZfy5LJaV3+lcsDwB3Gd7KvDDG0+i+XYsguQ==} '@prisma-next/driver-postgres@0.14.0': resolution: {integrity: sha512-qla7/fAk2YqwomkUPevd9bIqJ5gQZcZTXbEoz8nJaErUunisEosepJOKwlTpquJV2+OAuezODCbrrYtUJ525jg==} @@ -1891,8 +1894,8 @@ packages: typescript: optional: true - '@prisma-next/driver-postgres@0.9.0': - resolution: {integrity: sha512-3Ny7pZYG9gWfZRtRoFz4E71NwJTzxIIZ/1jRs8gA0v1jAr8ZotoFetbmjofM7GgDsP0trNyH4DMWWELHMpK/qQ==} + '@prisma-next/emitter@0.10.0': + resolution: {integrity: sha512-Bek5fWE7QWHzzwKsTvq/gmLYJPk+p7NisYNd0229IZSMfWzo6OMJT2/pHvDRyU6ykFK/c2rYdxvqrBdwGR5vfw==} '@prisma-next/emitter@0.14.0': resolution: {integrity: sha512-qdyOe11AsXHMpVmPYulA/NjKEn59VkXekGrtg8lB+Pqd+Dg2HwQinaO6G1PXjymRyVgv31UKRyUU7eLhCQaZ0Q==} @@ -1903,8 +1906,9 @@ packages: typescript: optional: true - '@prisma-next/emitter@0.9.0': - resolution: {integrity: sha512-UPsNHHTuR5yBR2CrC282276VLvg7FRwMCscU+rfusrzdHDYaqb+cSlxRYSmBqwZb52XXNvufiFldSBoQ5AovXg==} + '@prisma-next/errors@0.10.0': + resolution: {integrity: sha512-EYFw4isfWMvme3euuji1KAslSZ5REp+37Gw8QPwZ6+gpJjqpTh7LWtTTE4zmexv/Nc4KNYfuH/hVOQcPSfHVMQ==} + engines: {node: '>=20'} '@prisma-next/errors@0.14.0': resolution: {integrity: sha512-+VlDHQaP3AAs3rYYYSDoG5Ng5Hkyl+F4oAHAvDsvhFMupsGWaBx7esc5MbBHfnjE/5i/cSybR8/efXrIZJI6zA==} @@ -1915,10 +1919,6 @@ packages: typescript: optional: true - '@prisma-next/errors@0.9.0': - resolution: {integrity: sha512-3z5zJDIMOnA6mHBdC7Q4sxCb5bzfM5gwBJJXWVjvpc9SdP/ZQfmc0pbSzfEWPPQVuMiMIA1EzBJeWruq/ipQPA==} - engines: {node: '>=20'} - '@prisma-next/extension-author-tools@0.14.0': resolution: {integrity: sha512-7z9RiduSL6czOnB934pS+n0+PpVfzB7ATDK5HN8yBJKQN45ulNOFbxbnvBNDCPml6qDCFKXN3rtxv4bFVyHhOw==} engines: {node: '>=24'} @@ -1929,6 +1929,9 @@ packages: typescript: optional: true + '@prisma-next/family-sql@0.10.0': + resolution: {integrity: sha512-gYzBMNvENpWQKCCAta/wiCmsn5JBwx8uhVEouuTrYqRWGTHrZ6bgwv/vVxpcY73GtoNd/tShGHPc6JoNlrP0vw==} + '@prisma-next/family-sql@0.14.0': resolution: {integrity: sha512-Gx4ByuilDiJzwsOkqWE42CSZrhO9/6uFDpyemD185eZsaJGk5tvEpbLy3UuLNx+6VEsvXfPYGLdGUy2LL8e9Pg==} engines: {node: '>=24'} @@ -1938,8 +1941,8 @@ packages: typescript: optional: true - '@prisma-next/family-sql@0.9.0': - resolution: {integrity: sha512-8S6uaZv/hTzJWmvNmhkubZZMLsH/snx7imw4wQqHCZmDVz4dq2P6XrojJywCB45kQPM3mAwhxz+ckEhcZhBEJg==} + '@prisma-next/framework-components@0.10.0': + resolution: {integrity: sha512-tdcGLgrN2wsuSsfjA5rTqO3pOFaQiypn28f0i/uQ9KwoDFEDoq7XwFwhimqqrrpeePAVAt/la8Tw60ThE/AsDQ==} '@prisma-next/framework-components@0.14.0': resolution: {integrity: sha512-6ZPnesKake4pnRRQZUi67a5qfgs6Pc9cmNN2bAf2bQJYTg3w5b1yOhjg2sFupXT54hq/iE3rXRY5A7ltgSxijA==} @@ -1950,8 +1953,8 @@ packages: typescript: optional: true - '@prisma-next/framework-components@0.9.0': - resolution: {integrity: sha512-fAd0w/8PbJ4U+sQGdRhEEHiseytKVnpOHwaVBrIk6ZOjEFI/o8iybE/VUVTF7B56ewV72fd4dM/4ffq3xueGaw==} + '@prisma-next/ids@0.10.0': + resolution: {integrity: sha512-kNGze4NksyRe5nEo6LznqFuy5hWUqEvRdzjAyvRJbP0Z01EGIcueJNsc+i288FvIAC1A8PCB7MWYs87eSQtQaw==} '@prisma-next/ids@0.14.0': resolution: {integrity: sha512-E+Nhvgmco/Y24wiv/hWVt6wAf1J9sIS5qupoolBeL0V/BA/+zECewg/vCF2dj92hXk0jJ1WphhQDGrvx5XAPbQ==} @@ -1962,8 +1965,9 @@ packages: typescript: optional: true - '@prisma-next/ids@0.9.0': - resolution: {integrity: sha512-wPuSIcQst057qUUCwuxhHj2V12X5c+YO9gwckU5P5JbFjCOYDS+4cOa3N+TKMxtFpMK79+xjikW99KmGjXxJMg==} + '@prisma-next/migration-tools@0.10.0': + resolution: {integrity: sha512-WJJ8LMHCXlewSrtC4aJkxi0yI7/m9JbvnLVlTRzA4D94RkiEDbQg77LQhp3osF26eQqXu3Jc+LQMQ6nAItEYfQ==} + engines: {node: '>=20'} '@prisma-next/migration-tools@0.14.0': resolution: {integrity: sha512-JdFJPO8qFA3N0lP6g/ZsRz4QGeDV/qQpRUWq+9phoAbL/kBaMha838JG5YUb6h8z3tD4/qg+Qs6ESZPURD4AcQ==} @@ -1974,8 +1978,8 @@ packages: typescript: optional: true - '@prisma-next/migration-tools@0.9.0': - resolution: {integrity: sha512-j5f8a3etiUA7PoAHer7VKyPWlMdONVFLt+vHpdMWO+8MwVR/OadOgCfbnQgIPXQCJ9JLQB7IJm5EA0D8gRZuXA==} + '@prisma-next/operations@0.10.0': + resolution: {integrity: sha512-R6qhJB9CJuT2xyQbTIkJ4wXqclLaiJz5h6aVkRBUXr+Vt/EYA8SfGzzA6vO62gBbLHvDr+nZXEolxsSi5LOOIw==} engines: {node: '>=20'} '@prisma-next/operations@0.14.0': @@ -1987,10 +1991,6 @@ packages: typescript: optional: true - '@prisma-next/operations@0.9.0': - resolution: {integrity: sha512-0WQgBDc/lFvvAI5a8M3ES57DGBTtBPk2Z2J4XMWVC8iEwnEiTPHTrpqHAJNyDLJHmlh0H4FIY9OEdWFXqyVJ1Q==} - engines: {node: '>=20'} - '@prisma-next/postgres@0.14.0': resolution: {integrity: sha512-0TXr3+x3INseRhzPUmMn2t6NUhD3DlDht0MHJ8SBvSpkiGXH237IwID161KtZ7xa6ihTTXdjDfIqgmbDtVxfEQ==} engines: {node: '>=24'} @@ -2000,6 +2000,9 @@ packages: typescript: optional: true + '@prisma-next/psl-parser@0.10.0': + resolution: {integrity: sha512-+4HwCLoEsqK+MXJjG9CumdfLlKeRsSh8VSQ5yFz5Dy0Q9srbYyOooFMiZ8+85bAU65xz/pB2/59MN+tpWrMFrA==} + '@prisma-next/psl-parser@0.14.0': resolution: {integrity: sha512-XKumV4Bfy3KHoD3IsWVSDtzFjXKL5zENASbbfIXXpwll3AVgU4rtVKuJIH5fwUOYpcCUdLE8ZaNw6vcwLuse3w==} engines: {node: '>=24'} @@ -2009,8 +2012,8 @@ packages: typescript: optional: true - '@prisma-next/psl-parser@0.9.0': - resolution: {integrity: sha512-b1PG8nG7mR9dpRyWCAjZpHfgFs1qzcJbgx+Jjw59HYnNkMBqZUZWPM99RZj3OYC+VLxVj2s1CcQEl1O6nnYo3w==} + '@prisma-next/psl-printer@0.10.0': + resolution: {integrity: sha512-aeE9udDT9tNJ3SMDNYQhND7Sv8q3XpLaDbX83ZG2tynQKNvZP+RsbA03FYTs5UdEDm2pY2EjpGQ68nnPpTXY6g==} '@prisma-next/psl-printer@0.14.0': resolution: {integrity: sha512-qVTjwpO9uvITSsMg/mtdk0Py7ITsQRpPjmidSi9ByoPE6OGBGF3/BaUbOjRiZ340i39Npg83jT4rLreH1GOFjg==} @@ -2021,9 +2024,6 @@ packages: typescript: optional: true - '@prisma-next/psl-printer@0.9.0': - resolution: {integrity: sha512-jpYee/bV3y7jWmivNNuyexkwi59cQqssYYlOeqbEw9Dywvp+Ag+TAS+aXAprhCINJDl8qj/9Wb8Vh1yn6ulejA==} - '@prisma-next/sql-builder@0.14.0': resolution: {integrity: sha512-11AAASmhf7CZ4M/qQ5JHaHxHl+olBf5TP6jYSKuX8c9COttyp9IN9QRI0+PKR0QSjZgU1VX9WPNRG8hEcmDrZw==} engines: {node: '>=24'} @@ -2033,6 +2033,9 @@ packages: typescript: optional: true + '@prisma-next/sql-contract-emitter@0.10.0': + resolution: {integrity: sha512-pep5zFhyYs6FWQsJR229i7Xjj9K+XLGWZwI0l3eW7G+XJsPtef2zz0SapJCPcCB/CEbVUWx3kVxz0DtvKGwDbA==} + '@prisma-next/sql-contract-emitter@0.14.0': resolution: {integrity: sha512-gUgctyFNMKZ94wXNATevMi+FG9R9SK4yYQesYr7/5kZ4ZBbHkaFOFcWwlJZSI+EhCC2J1OgvfaCTtpWpn+VIUw==} engines: {node: '>=24'} @@ -2042,8 +2045,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-emitter@0.9.0': - resolution: {integrity: sha512-e+3+7gbD7aIVwPF+wAf+3uAl6UAzPLzJ4+yOs0fKcij2EARYUSKwg6Hhuv6QVMeT0CA/lgtEb+fv6lsCpYSS9Q==} + '@prisma-next/sql-contract-psl@0.10.0': + resolution: {integrity: sha512-vgzQMC80Oc1wvY/xO1xoFi9ZdPDjbtjxeeVeVHoimTx/Q62skZ1TiyU1t/IP8A9PtXBXTgFgR+Zs1xRbzKn5bg==} '@prisma-next/sql-contract-psl@0.14.0': resolution: {integrity: sha512-86Z8MWo0NYhGSX4uOqbvAx46aMOaB2opNyUeFNnntT1br+U48IX2ZPGV62ul6mwCp7DGN7pjvmlBW5OnHXuiyg==} @@ -2054,8 +2057,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-psl@0.9.0': - resolution: {integrity: sha512-SUn04doJG1IixPQm9ZP+dzDLy8X+ZcbPNU+URtbwquA+vXa0ePlHs+k0VNGBoZQdEo0MFLtGcoXMVl+Y0KiUUw==} + '@prisma-next/sql-contract-ts@0.10.0': + resolution: {integrity: sha512-ejOEM3FJ1MjdsD2fD9eHtdtT0cHvHR8Vx9fSf7wV/9iR8FqlDvYT6V1/eT38R5rVWyRviXWZlyB/KaoifIgKhg==} '@prisma-next/sql-contract-ts@0.14.0': resolution: {integrity: sha512-AMTURIMXCsn+M7oW0QOe/9wS7J+Q1siW7+aihgtmBj88lYav9XHyxJO5AJDu6LSDSctorTzccfsPatBAlcxzKQ==} @@ -2066,8 +2069,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-ts@0.9.0': - resolution: {integrity: sha512-8dSYxKkvTEUvGJ4vZCWXOW/5GDmLp0/4+wrgSzwRPFw13wFRQct2DYNtJllWoHj9TMzGvtBekrNP4mdejpOUdA==} + '@prisma-next/sql-contract@0.10.0': + resolution: {integrity: sha512-kgrjCzAN6Cqz7Y5jIFP5yTf53eNxNWUO6kCdmP90zJuzPBBRPnzhPGZdAEbfoT6K/gjip3k68lsFXA+xle8tVw==} '@prisma-next/sql-contract@0.14.0': resolution: {integrity: sha512-/Ibvjd41sEgpi4c3YCZvK6vRCucNffsIZ+isIOYoVqXOt74H0llM4Ux4mR0PShNuo0jgG8xOon1ptSQ6/S7c+w==} @@ -2078,8 +2081,9 @@ packages: typescript: optional: true - '@prisma-next/sql-contract@0.9.0': - resolution: {integrity: sha512-ol7R3/rahAsof/34ue7YxSd7yKds1I7XtifDbcEQLqZ41vgqULJp+t5GCr4zNWmZVl2f8QVf4IZ5oxJdR2VqIw==} + '@prisma-next/sql-errors@0.10.0': + resolution: {integrity: sha512-MhppTzW3Visg0RRJILvRgOKZjhOJA8UnSJqEH3zc/9wr9qVyjf9DXDIJKH6TZH9jkWTbXwqkTx55vWzI3vkCTA==} + engines: {node: '>=20'} '@prisma-next/sql-errors@0.14.0': resolution: {integrity: sha512-zc5U+SzEUqpjPC69w9JejqAkcRtkZ8BSm1a6l6p4pT4b8dk/iXrfQ96oHoy6SYG/dTbGe47W4gCfX9LCzCdUlA==} @@ -2090,8 +2094,8 @@ packages: typescript: optional: true - '@prisma-next/sql-errors@0.9.0': - resolution: {integrity: sha512-zMWKAHSFGANHiunOKs3txejvGf+pnBpgGYDL+HDJxEFYw5+/rGJEI3Hk0mKpCC0377b4M0BpaH9DCzeVCfdqIw==} + '@prisma-next/sql-operations@0.10.0': + resolution: {integrity: sha512-w615a6D0znJfQayGjOUb1ylc/1DX4/Nib1I+qK20xgYvSG3fM5YLILkUFcuvKcHhkFHp2a4ynNantfsVVLfx5w==} engines: {node: '>=20'} '@prisma-next/sql-operations@0.14.0': @@ -2103,10 +2107,6 @@ packages: typescript: optional: true - '@prisma-next/sql-operations@0.9.0': - resolution: {integrity: sha512-//k//O/CDslU3AHWYBDZL5bvtlUYzzoZHJnU0zz180zdz1jO67yTw+Upp1dLW+jcuCWxtwVo/5j0tp86maOmJg==} - engines: {node: '>=20'} - '@prisma-next/sql-orm-client@0.14.0': resolution: {integrity: sha512-fXyiR/8r3NqLqUKTxrL0o5KcxHW6r91pD0pU1tmnB6kJD3MS++Fhd2miDcbCn/5O0qf71zfmJDafKU74TOJ3dw==} engines: {node: '>=24'} @@ -2116,6 +2116,9 @@ packages: typescript: optional: true + '@prisma-next/sql-relational-core@0.10.0': + resolution: {integrity: sha512-W0n6F5axdeezfm079bUIEUNwqwBWerLtfb2LPMJlmSxA9BamKk4c0H2XftP++iCwvjW0g/xG9sBbtOz9d5egkQ==} + '@prisma-next/sql-relational-core@0.14.0': resolution: {integrity: sha512-7H1HaN31lmGD81C9vz07dq/r5n80yKXfFNpYj5XrKKeVpDZQSb/IXtOVt7LyB+q9QqSUW2jD5RGLuTOhsd2kbw==} engines: {node: '>=24'} @@ -2125,8 +2128,8 @@ packages: typescript: optional: true - '@prisma-next/sql-relational-core@0.9.0': - resolution: {integrity: sha512-Fo/0FZAw/5Ay+bxTxJQvSNAErbWntON4cKdxq+99ioZDLx0UVgeL2e3doezmahD+9vOpIMLzNaA8pnaST86gZg==} + '@prisma-next/sql-runtime@0.10.0': + resolution: {integrity: sha512-fk0+ZaVSkF+kogY19O2iLLK5kMZd8p5RXiTIHlKujalmo3mQ2f+gVNL0co2uA3Nh5RyHSf+t7oq2GK5ZQsZ63g==} '@prisma-next/sql-runtime@0.14.0': resolution: {integrity: sha512-HV8ZAvNuXtFrAHuxs3/ZEz0JapKFayFYDljQeVDFxExSEj79fPtd9CON9sTTV4T/RKII5knlKJRuKtwWH35Znw==} @@ -2137,8 +2140,9 @@ packages: typescript: optional: true - '@prisma-next/sql-runtime@0.9.0': - resolution: {integrity: sha512-8mj72w4bgoQ7qqD0uGpl18UQPsfWOYbo51/HXBXSls+yI10AijkScCh7x2mHc/G2NPRRpvqrWBT5MOlMZcSmdA==} + '@prisma-next/sql-schema-ir@0.10.0': + resolution: {integrity: sha512-OlzfkVkxA0BNCQphb5hgnPWO8AqJI3NmKAYLI0xBHrUqlHDOetpbDJuBFSzN5BE5uLPQEQ/B1Ecd6efoH+aa1A==} + engines: {node: '>=20'} '@prisma-next/sql-schema-ir@0.14.0': resolution: {integrity: sha512-5buE3jcTz3yyyve4demaACIv52YT09tFPCBEqwmxG1ss8KtybVEgaZg8UUYGmyYbWU0l5RpQ96/vuB5UTFE4nw==} @@ -2149,9 +2153,8 @@ packages: typescript: optional: true - '@prisma-next/sql-schema-ir@0.9.0': - resolution: {integrity: sha512-wspzYofwBBxwmwmaZF+TEa/ko3Q4yqKpz8huSjKHR4Xigxa3gAYrsaE6cCH58ukz23/VrmSsKBH5tdbWqjFDgg==} - engines: {node: '>=20'} + '@prisma-next/target-postgres@0.10.0': + resolution: {integrity: sha512-MWqMguwu4Fxanw2xOAVL8HijJJt9h7cEiP9alSCyBr0NVist7l0a4Aw9h4z0AGeCKnxnLDV35GWyG/BowNP3PA==} '@prisma-next/target-postgres@0.14.0': resolution: {integrity: sha512-i1H9Jzt7ZGgYQ61rlmalyEPY1RoLFIMk9+qE5WbETjQ8LY9p7iXeTx4bBv25twkYIAHxXtrYxsQ3klUWg0JfVg==} @@ -2162,8 +2165,9 @@ packages: typescript: optional: true - '@prisma-next/target-postgres@0.9.0': - resolution: {integrity: sha512-stKpuRcxvvIdCiJbwNyFcjQYQSZ66mym38Eiq2YeMGGo4ISBz7RV4WMzgX+mhGthF62kcf1fGJYD56Bu0EcP5w==} + '@prisma-next/ts-render@0.10.0': + resolution: {integrity: sha512-8hKW3krALe5aPtUZnZWyTp25qqdshQ1JPqtDGWKEwRhFXIxeOWzJoajxJw0hBAGhFVimTcF0y1PaXU0gSVD0eQ==} + engines: {node: '>=20'} '@prisma-next/ts-render@0.14.0': resolution: {integrity: sha512-ub25pEfUe2dLpNu/wR0mlBBMZ87eGmyV5D9GS5KmyHkofjJdiE2Y3BpIhHXf7wjiYp35z/gOyQ2vuPFS4xVBgQ==} @@ -2174,8 +2178,8 @@ packages: typescript: optional: true - '@prisma-next/ts-render@0.9.0': - resolution: {integrity: sha512-burhx330iW3ytljTYBO3Z1AlryZAjvEfM8tacZMIC/5SQ03w5hYUHexKkiipxcEqcEAgq3TP6xqtkAnTjoyA9Q==} + '@prisma-next/utils@0.10.0': + resolution: {integrity: sha512-MLBGskruFbGiyjHs78gyBRS/6D0cpJeAVN4UxxVxq/qm2m4dS9saXeI3OZz5sn4cHgc10vU47Ssibo6cSWpc1g==} engines: {node: '>=20'} '@prisma-next/utils@0.14.0': @@ -2187,10 +2191,6 @@ packages: typescript: optional: true - '@prisma-next/utils@0.9.0': - resolution: {integrity: sha512-Vh83WXCv9IAAFG1G2+5c8XaOxBkPm2R3I1nmaiPyfxTpZS1MYNoUSL25cHiVpqnPpLxuZx2Ca9qmg/d5C32BAA==} - engines: {node: '>=20'} - '@rollup/rollup-android-arm-eabi@4.59.0': resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] @@ -5176,6 +5176,27 @@ snapshots: '@posthog/types@1.373.5': {} + '@prisma-next/adapter-postgres@0.10.0(typanion@3.14.0)': + dependencies: + '@prisma-next/contract': 0.10.0 + '@prisma-next/contract-authoring': 0.10.0 + '@prisma-next/family-sql': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/ids': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/sql-contract-psl': 0.10.0 + '@prisma-next/sql-contract-ts': 0.10.0 + '@prisma-next/sql-operations': 0.10.0 + '@prisma-next/sql-relational-core': 0.10.0 + '@prisma-next/sql-runtime': 0.10.0 + '@prisma-next/sql-schema-ir': 0.10.0 + '@prisma-next/target-postgres': 0.10.0(typanion@3.14.0) + '@prisma-next/utils': 0.10.0 + arktype: 2.2.0 + transitivePeerDependencies: + - magicast + - typanion + '@prisma-next/adapter-postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5200,27 +5221,6 @@ snapshots: - magicast - typanion - '@prisma-next/adapter-postgres@0.9.0(typanion@3.14.0)': - dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/contract-authoring': 0.9.0 - '@prisma-next/family-sql': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/ids': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/sql-contract-psl': 0.9.0 - '@prisma-next/sql-contract-ts': 0.9.0 - '@prisma-next/sql-operations': 0.9.0 - '@prisma-next/sql-relational-core': 0.9.0 - '@prisma-next/sql-runtime': 0.9.0 - '@prisma-next/sql-schema-ir': 0.9.0 - '@prisma-next/target-postgres': 0.9.0(typanion@3.14.0) - '@prisma-next/utils': 0.9.0 - arktype: 2.2.0 - transitivePeerDependencies: - - magicast - - typanion - '@prisma-next/cli-telemetry@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/config': 0.14.0(typescript@5.9.3) @@ -5233,21 +5233,20 @@ snapshots: transitivePeerDependencies: - magicast - '@prisma-next/cli@0.14.0(typanion@3.14.0)(typescript@5.9.3)': + '@prisma-next/cli@0.10.0(typanion@3.14.0)': dependencies: '@clack/prompts': 1.4.0 - '@prisma-next/cli-telemetry': 0.14.0(typescript@5.9.3) - '@prisma-next/config': 0.14.0(typescript@5.9.3) - '@prisma-next/contract': 0.14.0(typescript@5.9.3) - '@prisma-next/emitter': 0.14.0(typescript@5.9.3) - '@prisma-next/errors': 0.14.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) - '@prisma-next/migration-tools': 0.14.0(typescript@5.9.3) - '@prisma-next/psl-printer': 0.14.0(typescript@5.9.3) - '@prisma-next/utils': 0.14.0(typescript@5.9.3) + '@dagrejs/dagre': 3.0.0 + '@prisma-next/config': 0.10.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/emitter': 0.10.0 + '@prisma-next/errors': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/migration-tools': 0.10.0 + '@prisma-next/psl-printer': 0.10.0 + '@prisma-next/utils': 0.10.0 arktype: 2.2.0 c12: 3.3.4 - ci-info: 4.4.0 clipanion: 4.0.0-rc.4(typanion@3.14.0) closest-match: 1.3.3 colorette: 2.0.20 @@ -5259,26 +5258,25 @@ snapshots: string-width: 8.2.1 strip-ansi: 7.2.0 wrap-ansi: 10.0.0 - optionalDependencies: - typescript: 5.9.3 transitivePeerDependencies: - magicast - typanion - '@prisma-next/cli@0.9.0(typanion@3.14.0)': + '@prisma-next/cli@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@clack/prompts': 1.4.0 - '@dagrejs/dagre': 3.0.0 - '@prisma-next/config': 0.9.0 - '@prisma-next/contract': 0.9.0 - '@prisma-next/emitter': 0.9.0 - '@prisma-next/errors': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/migration-tools': 0.9.0 - '@prisma-next/psl-printer': 0.9.0 - '@prisma-next/utils': 0.9.0 + '@prisma-next/cli-telemetry': 0.14.0(typescript@5.9.3) + '@prisma-next/config': 0.14.0(typescript@5.9.3) + '@prisma-next/contract': 0.14.0(typescript@5.9.3) + '@prisma-next/emitter': 0.14.0(typescript@5.9.3) + '@prisma-next/errors': 0.14.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.14.0(typescript@5.9.3) + '@prisma-next/psl-printer': 0.14.0(typescript@5.9.3) + '@prisma-next/utils': 0.14.0(typescript@5.9.3) arktype: 2.2.0 c12: 3.3.4 + ci-info: 4.4.0 clipanion: 4.0.0-rc.4(typanion@3.14.0) closest-match: 1.3.3 colorette: 2.0.20 @@ -5290,10 +5288,19 @@ snapshots: string-width: 8.2.1 strip-ansi: 7.2.0 wrap-ansi: 10.0.0 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - magicast - typanion + '@prisma-next/config@0.10.0': + dependencies: + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/utils': 0.10.0 + arktype: 2.2.0 + '@prisma-next/config@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5303,12 +5310,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/config@0.9.0': + '@prisma-next/contract-authoring@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/utils': 0.9.0 - arktype: 2.2.0 + '@prisma-next/framework-components': 0.10.0 '@prisma-next/contract-authoring@0.14.0(typescript@5.9.3)': dependencies: @@ -5316,9 +5320,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract-authoring@0.9.0': + '@prisma-next/contract@0.10.0': dependencies: - '@prisma-next/framework-components': 0.9.0 + '@prisma-next/utils': 0.10.0 + '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 '@prisma-next/contract@0.14.0(typescript@5.9.3)': dependencies: @@ -5328,11 +5334,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract@0.9.0': + '@prisma-next/driver-postgres@0.10.0': dependencies: - '@prisma-next/utils': 0.9.0 - '@standard-schema/spec': 1.1.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/errors': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/sql-errors': 0.10.0 + '@prisma-next/sql-operations': 0.10.0 + '@prisma-next/sql-relational-core': 0.10.0 + '@prisma-next/utils': 0.10.0 arktype: 2.2.0 + pg: 8.20.0 + pg-cursor: 2.21.0(pg@8.20.0) + transitivePeerDependencies: + - pg-native '@prisma-next/driver-postgres@0.14.0(typescript@5.9.3)': dependencies: @@ -5352,21 +5368,14 @@ snapshots: transitivePeerDependencies: - pg-native - '@prisma-next/driver-postgres@0.9.0': + '@prisma-next/emitter@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/errors': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/sql-errors': 0.9.0 - '@prisma-next/sql-operations': 0.9.0 - '@prisma-next/sql-relational-core': 0.9.0 - '@prisma-next/utils': 0.9.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/operations': 0.10.0 + '@prisma-next/utils': 0.10.0 arktype: 2.2.0 - pg: 8.20.0 - pg-cursor: 2.21.0(pg@8.20.0) - transitivePeerDependencies: - - pg-native + prettier: 3.8.3 '@prisma-next/emitter@0.14.0(typescript@5.9.3)': dependencies: @@ -5380,14 +5389,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/emitter@0.9.0': + '@prisma-next/errors@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/operations': 0.9.0 - '@prisma-next/utils': 0.9.0 - arktype: 2.2.0 - prettier: 3.8.3 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/utils': 0.10.0 '@prisma-next/errors@0.14.0(typescript@5.9.3)': dependencies: @@ -5396,15 +5401,27 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/errors@0.9.0': - dependencies: - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/utils': 0.9.0 - '@prisma-next/extension-author-tools@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 + '@prisma-next/family-sql@0.10.0': + dependencies: + '@prisma-next/contract': 0.10.0 + '@prisma-next/emitter': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/migration-tools': 0.10.0 + '@prisma-next/operations': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/sql-contract-emitter': 0.10.0 + '@prisma-next/sql-contract-ts': 0.10.0 + '@prisma-next/sql-operations': 0.10.0 + '@prisma-next/sql-relational-core': 0.10.0 + '@prisma-next/sql-runtime': 0.10.0 + '@prisma-next/sql-schema-ir': 0.10.0 + '@prisma-next/utils': 0.10.0 + arktype: 2.2.0 + '@prisma-next/family-sql@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5424,22 +5441,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/family-sql@0.9.0': - dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/emitter': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/migration-tools': 0.9.0 - '@prisma-next/operations': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/sql-contract-emitter': 0.9.0 - '@prisma-next/sql-contract-ts': 0.9.0 - '@prisma-next/sql-operations': 0.9.0 - '@prisma-next/sql-relational-core': 0.9.0 - '@prisma-next/sql-runtime': 0.9.0 - '@prisma-next/sql-schema-ir': 0.9.0 - '@prisma-next/utils': 0.9.0 - arktype: 2.2.0 + '@prisma-next/framework-components@0.10.0': + dependencies: + '@prisma-next/contract': 0.10.0 + '@prisma-next/operations': 0.10.0 + '@prisma-next/ts-render': 0.10.0 + '@prisma-next/utils': 0.10.0 + '@standard-schema/spec': 1.1.0 '@prisma-next/framework-components@0.14.0(typescript@5.9.3)': dependencies: @@ -5452,13 +5460,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/framework-components@0.9.0': + '@prisma-next/ids@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/operations': 0.9.0 - '@prisma-next/ts-render': 0.9.0 - '@prisma-next/utils': 0.9.0 - '@standard-schema/spec': 1.1.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/utils': 0.10.0 + uniku: 0.0.12 '@prisma-next/ids@0.14.0(typescript@5.9.3)': dependencies: @@ -5469,12 +5476,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/ids@0.9.0': + '@prisma-next/migration-tools@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/utils': 0.9.0 - uniku: 0.0.12 + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/utils': 0.10.0 + arktype: 2.2.0 + pathe: 2.0.3 + prettier: 3.8.3 '@prisma-next/migration-tools@0.14.0(typescript@5.9.3)': dependencies: @@ -5487,21 +5496,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/migration-tools@0.9.0': - dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/utils': 0.9.0 - arktype: 2.2.0 - pathe: 2.0.3 - prettier: 3.8.3 + '@prisma-next/operations@0.10.0': {} '@prisma-next/operations@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/operations@0.9.0': {} - '@prisma-next/postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@prisma-next/adapter-postgres': 0.14.0(typanion@3.14.0)(typescript@5.9.3) @@ -5529,6 +5529,11 @@ snapshots: - pg-native - typanion + '@prisma-next/psl-parser@0.10.0': + dependencies: + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/utils': 0.10.0 + '@prisma-next/psl-parser@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) @@ -5536,10 +5541,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-parser@0.9.0': + '@prisma-next/psl-printer@0.10.0': dependencies: - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/utils': 0.9.0 + '@prisma-next/framework-components': 0.10.0 '@prisma-next/psl-printer@0.14.0(typescript@5.9.3)': dependencies: @@ -5548,10 +5552,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-printer@0.9.0': - dependencies: - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/sql-builder@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5562,6 +5562,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 + '@prisma-next/sql-contract-emitter@0.10.0': + dependencies: + '@prisma-next/contract': 0.10.0 + '@prisma-next/emitter': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/utils': 0.10.0 + '@prisma-next/sql-contract-emitter@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5572,12 +5580,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-emitter@0.9.0': + '@prisma-next/sql-contract-psl@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/emitter': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/utils': 0.9.0 + '@prisma-next/config': 0.10.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/psl-parser': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/sql-contract-ts': 0.10.0 + '@prisma-next/utils': 0.10.0 + pathe: 2.0.3 '@prisma-next/sql-contract-psl@0.14.0(typescript@5.9.3)': dependencies: @@ -5592,16 +5604,17 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-psl@0.9.0': + '@prisma-next/sql-contract-ts@0.10.0': dependencies: - '@prisma-next/config': 0.9.0 - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/psl-parser': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/sql-contract-ts': 0.9.0 - '@prisma-next/utils': 0.9.0 + '@prisma-next/config': 0.10.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/contract-authoring': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/utils': 0.10.0 + arktype: 2.2.0 pathe: 2.0.3 + ts-toolbelt: 9.6.0 '@prisma-next/sql-contract-ts@0.14.0(typescript@5.9.3)': dependencies: @@ -5617,17 +5630,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-ts@0.9.0': + '@prisma-next/sql-contract@0.10.0': dependencies: - '@prisma-next/config': 0.9.0 - '@prisma-next/contract': 0.9.0 - '@prisma-next/contract-authoring': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/utils': 0.9.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 arktype: 2.2.0 - pathe: 2.0.3 - ts-toolbelt: 9.6.0 '@prisma-next/sql-contract@0.14.0(typescript@5.9.3)': dependencies: @@ -5638,17 +5645,17 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract@0.9.0': - dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - arktype: 2.2.0 + '@prisma-next/sql-errors@0.10.0': {} '@prisma-next/sql-errors@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-errors@0.9.0': {} + '@prisma-next/sql-operations@0.10.0': + dependencies: + '@prisma-next/operations': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + arktype: 2.2.0 '@prisma-next/sql-operations@0.14.0(typescript@5.9.3)': dependencies: @@ -5658,12 +5665,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-operations@0.9.0': - dependencies: - '@prisma-next/operations': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - arktype: 2.2.0 - '@prisma-next/sql-orm-client@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5678,6 +5679,18 @@ snapshots: optionalDependencies: typescript: 5.9.3 + '@prisma-next/sql-relational-core@0.10.0': + dependencies: + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/operations': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/sql-operations': 0.10.0 + '@prisma-next/utils': 0.10.0 + '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 + ts-toolbelt: 9.6.0 + '@prisma-next/sql-relational-core@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5692,17 +5705,17 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-relational-core@0.9.0': + '@prisma-next/sql-runtime@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/operations': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/sql-operations': 0.9.0 - '@prisma-next/utils': 0.9.0 - '@standard-schema/spec': 1.1.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/ids': 0.10.0 + '@prisma-next/operations': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/sql-operations': 0.10.0 + '@prisma-next/sql-relational-core': 0.10.0 + '@prisma-next/utils': 0.10.0 arktype: 2.2.0 - ts-toolbelt: 9.6.0 '@prisma-next/sql-runtime@0.14.0(typescript@5.9.3)': dependencies: @@ -5718,17 +5731,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-runtime@0.9.0': + '@prisma-next/sql-schema-ir@0.10.0': dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/ids': 0.9.0 - '@prisma-next/operations': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/sql-operations': 0.9.0 - '@prisma-next/sql-relational-core': 0.9.0 - '@prisma-next/utils': 0.9.0 - arktype: 2.2.0 + '@prisma-next/contract': 0.10.0 + '@prisma-next/framework-components': 0.10.0 '@prisma-next/sql-schema-ir@0.14.0(typescript@5.9.3)': dependencies: @@ -5737,10 +5743,27 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-schema-ir@0.9.0': - dependencies: - '@prisma-next/contract': 0.9.0 - '@prisma-next/framework-components': 0.9.0 + '@prisma-next/target-postgres@0.10.0(typanion@3.14.0)': + dependencies: + '@prisma-next/cli': 0.10.0(typanion@3.14.0) + '@prisma-next/contract': 0.10.0 + '@prisma-next/errors': 0.10.0 + '@prisma-next/family-sql': 0.10.0 + '@prisma-next/framework-components': 0.10.0 + '@prisma-next/migration-tools': 0.10.0 + '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/sql-errors': 0.10.0 + '@prisma-next/sql-operations': 0.10.0 + '@prisma-next/sql-relational-core': 0.10.0 + '@prisma-next/sql-schema-ir': 0.10.0 + '@prisma-next/ts-render': 0.10.0 + '@prisma-next/utils': 0.10.0 + '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 + pathe: 2.0.3 + transitivePeerDependencies: + - magicast + - typanion '@prisma-next/target-postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: @@ -5766,40 +5789,18 @@ snapshots: - magicast - typanion - '@prisma-next/target-postgres@0.9.0(typanion@3.14.0)': - dependencies: - '@prisma-next/cli': 0.9.0(typanion@3.14.0) - '@prisma-next/contract': 0.9.0 - '@prisma-next/errors': 0.9.0 - '@prisma-next/family-sql': 0.9.0 - '@prisma-next/framework-components': 0.9.0 - '@prisma-next/migration-tools': 0.9.0 - '@prisma-next/sql-contract': 0.9.0 - '@prisma-next/sql-errors': 0.9.0 - '@prisma-next/sql-operations': 0.9.0 - '@prisma-next/sql-relational-core': 0.9.0 - '@prisma-next/sql-schema-ir': 0.9.0 - '@prisma-next/ts-render': 0.9.0 - '@prisma-next/utils': 0.9.0 - '@standard-schema/spec': 1.1.0 - arktype: 2.2.0 - pathe: 2.0.3 - transitivePeerDependencies: - - magicast - - typanion + '@prisma-next/ts-render@0.10.0': {} '@prisma-next/ts-render@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/ts-render@0.9.0': {} + '@prisma-next/utils@0.10.0': {} '@prisma-next/utils@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/utils@0.9.0': {} - '@rollup/rollup-android-arm-eabi@4.59.0': optional: true From cc05c22f36451210584ac5af0044c48f39b21c22 Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 03:54:04 -0600 Subject: [PATCH 05/15] chore: upgrade @prisma-next/* to 0.11.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All five 0.10→0.11 extension-author changes are no-ops for this package (no handcrafted Contract type literals, no facade, no Mongo, no .insert()/.withValues() call sites). Additional 0.11 SPI churn: update/on-conflict set values widened to AnyExpression; the bulk-encrypt routing-key stamp accepts the wider union (it only acts on kind === 'param-ref'). Co-Authored-By: Claude Fable 5 --- packages/prisma-next/package.json | 36 +- .../src/middleware/bulk-encrypt.ts | 3 +- pnpm-lock.yaml | 493 +++++++++--------- 3 files changed, 275 insertions(+), 257 deletions(-) diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json index bae7c37ca..2333697ea 100644 --- a/packages/prisma-next/package.json +++ b/packages/prisma-next/package.json @@ -80,28 +80,28 @@ }, "dependencies": { "@cipherstash/stack": "workspace:*", - "@prisma-next/contract": "0.10.0", - "@prisma-next/family-sql": "0.10.0", - "@prisma-next/framework-components": "0.10.0", - "@prisma-next/migration-tools": "0.10.0", - "@prisma-next/sql-contract": "0.10.0", - "@prisma-next/sql-operations": "0.10.0", - "@prisma-next/sql-relational-core": "0.10.0", - "@prisma-next/sql-runtime": "0.10.0", - "@prisma-next/ts-render": "0.10.0", - "@prisma-next/utils": "0.10.0", + "@prisma-next/contract": "0.11.0", + "@prisma-next/family-sql": "0.11.0", + "@prisma-next/framework-components": "0.11.0", + "@prisma-next/migration-tools": "0.11.0", + "@prisma-next/sql-contract": "0.11.0", + "@prisma-next/sql-operations": "0.11.0", + "@prisma-next/sql-relational-core": "0.11.0", + "@prisma-next/sql-runtime": "0.11.0", + "@prisma-next/ts-render": "0.11.0", + "@prisma-next/utils": "0.11.0", "arktype": "^2.1.29" }, "devDependencies": { - "@prisma-next/adapter-postgres": "0.10.0", - "@prisma-next/cli": "0.10.0", - "@prisma-next/driver-postgres": "0.10.0", + "@prisma-next/adapter-postgres": "0.11.0", + "@prisma-next/cli": "0.11.0", + "@prisma-next/driver-postgres": "0.11.0", "@prisma-next/extension-author-tools": "0.14.0", - "@prisma-next/psl-parser": "0.10.0", - "@prisma-next/sql-contract-psl": "0.10.0", - "@prisma-next/sql-contract-ts": "0.10.0", - "@prisma-next/sql-schema-ir": "0.10.0", - "@prisma-next/target-postgres": "0.10.0", + "@prisma-next/psl-parser": "0.11.0", + "@prisma-next/sql-contract-psl": "0.11.0", + "@prisma-next/sql-contract-ts": "0.11.0", + "@prisma-next/sql-schema-ir": "0.11.0", + "@prisma-next/target-postgres": "0.11.0", "pathe": "^2.0.3", "tsup": "catalog:repo", "typescript": "catalog:repo", diff --git a/packages/prisma-next/src/middleware/bulk-encrypt.ts b/packages/prisma-next/src/middleware/bulk-encrypt.ts index 1f762f60a..23143852f 100644 --- a/packages/prisma-next/src/middleware/bulk-encrypt.ts +++ b/packages/prisma-next/src/middleware/bulk-encrypt.ts @@ -56,6 +56,7 @@ */ import type { + AnyExpression, AnyQueryAst, InsertAst, InsertValue, @@ -229,7 +230,7 @@ function stampUpdate(ast: UpdateAst): void { } function stampParamRefIfEnvelope( - value: InsertValue, + value: AnyExpression | InsertValue, table: string, column: string, ): void { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 713d473fe..92f724a7e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,66 +408,66 @@ importers: specifier: workspace:* version: link:../stack '@prisma-next/contract': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/family-sql': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/framework-components': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/migration-tools': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/sql-contract': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/sql-operations': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/sql-relational-core': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/sql-runtime': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/ts-render': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/utils': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 arktype: specifier: ^2.1.29 version: 2.2.0 devDependencies: '@prisma-next/adapter-postgres': - specifier: 0.10.0 - version: 0.10.0(typanion@3.14.0) + specifier: 0.11.0 + version: 0.11.0(typanion@3.14.0) '@prisma-next/cli': - specifier: 0.10.0 - version: 0.10.0(typanion@3.14.0) + specifier: 0.11.0 + version: 0.11.0(typanion@3.14.0) '@prisma-next/driver-postgres': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/extension-author-tools': specifier: 0.14.0 version: 0.14.0(typescript@5.9.3) '@prisma-next/psl-parser': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/sql-contract-psl': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/sql-contract-ts': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/sql-schema-ir': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.11.0 + version: 0.11.0 '@prisma-next/target-postgres': - specifier: 0.10.0 - version: 0.10.0(typanion@3.14.0) + specifier: 0.11.0 + version: 0.11.0(typanion@3.14.0) pathe: specifier: ^2.0.3 version: 2.0.3 @@ -1809,8 +1809,8 @@ packages: '@posthog/types@1.373.5': resolution: {integrity: sha512-K7STCnRG/WBE1q0BwEkIcrJB5OqECaymsQj6Hp4Ntvaek4dqHkZGfp6hxwIPqQPjlOXwidwPLo+XGsn+CoZUyw==} - '@prisma-next/adapter-postgres@0.10.0': - resolution: {integrity: sha512-fKTWIGTWmaczeK6KO9tJJqmu/kZ6RQr+BWU4/JkN+emKeGKdvza93+gXdqrSGNRTQlCAL3qeCyEHX9j/pZPyHg==} + '@prisma-next/adapter-postgres@0.11.0': + resolution: {integrity: sha512-L5nx2kLzBD+0HZ8olvOlVMJohAo0lRcspmPMKNLIFmqdPTQxfdmAHrVbM1tTTw0Q8DcRvO2243oP6ir9eSkoCw==} '@prisma-next/adapter-postgres@0.14.0': resolution: {integrity: sha512-c90KFtI74WU+8YH3uhVg1DTmD10T2BpINzzys9F3n1jTM2Am0tXKx53V05Ev+7H1VBrjBoksZPZ92WWxVEXLew==} @@ -1821,6 +1821,9 @@ packages: typescript: optional: true + '@prisma-next/cli-telemetry@0.11.0': + resolution: {integrity: sha512-Vbhl/4gcch7fMFcuaVSJdhsaqt2aC8uyBZ2q9ZPjAU9QZ2IrG+w2YwfQdC9AXe2sf5BEJ4I/4cXyvV5bCCSYDA==} + '@prisma-next/cli-telemetry@0.14.0': resolution: {integrity: sha512-JaP/+09tIL6zH+5WQS507db2HQ0B47HRR2AZD8VHmsw/tMwBDZqKVdIA5KptPMVSR3pcnXDygGvG7E8N+PSPRg==} engines: {node: '>=24'} @@ -1830,8 +1833,8 @@ packages: typescript: optional: true - '@prisma-next/cli@0.10.0': - resolution: {integrity: sha512-f8WBdK1pnF9gXMw+iSu6vJfHtW5ETcXXCZ4aDZDcO5ynufTzIAkQxI4yvap7dORUhH0e37Mtl4/YylUr/a1OOA==} + '@prisma-next/cli@0.11.0': + resolution: {integrity: sha512-iFtlGtZ+6bb3JrSajnn/2gdnbMWl6d2aj4TkoqMHUyqYRXwPXGYQX/dL3+O1t+K2jnqah0M4tWj7zmJL9Be57w==} hasBin: true '@prisma-next/cli@0.14.0': @@ -1844,8 +1847,8 @@ packages: typescript: optional: true - '@prisma-next/config@0.10.0': - resolution: {integrity: sha512-NVd+lHqGiopgZsqJZh9FAfSZHdzKtnCnokWgiuvrEC4uCK24G1erPRmRskkpg7fIeW8ZtZEs/24yjLbxYhj7Vg==} + '@prisma-next/config@0.11.0': + resolution: {integrity: sha512-Yza+AB0wps5vP8TozZl1qinY/fm04kb9E7i1FJts+pEDDElZH04iaTNv+NeOcWNCXVNkdPnPSJnXXi1XekFxfw==} engines: {node: '>=20'} '@prisma-next/config@0.14.0': @@ -1857,8 +1860,8 @@ packages: typescript: optional: true - '@prisma-next/contract-authoring@0.10.0': - resolution: {integrity: sha512-qC0wenYjOrdgzXOBpAvjjteijsVWQ3EeGz/3nLUiEPTEJ7QFxavOvYGqLLlBaVBikR4stHO09SVUqpWx3koS8w==} + '@prisma-next/contract-authoring@0.11.0': + resolution: {integrity: sha512-yJ/CKtr51Xa33ojs5GfdpVk0RvO8MFa/nuiEOr5G1BdQO4H7r8qg448SzEimyjpQy/D8AIPzY5rWIT10P66HlQ==} '@prisma-next/contract-authoring@0.14.0': resolution: {integrity: sha512-79aLJO04p/XRX2QvIlFMR2ZvbqvI0xFAU5idgYySoevq8bSY9ORGA0XchSbhFl0d6d1VfSEZowRPwzvgl/6lsg==} @@ -1869,8 +1872,8 @@ packages: typescript: optional: true - '@prisma-next/contract@0.10.0': - resolution: {integrity: sha512-tqLXa2nt5mfuIj2BlmyvfUYl2mUlfBQMYRMHiUiBtvTYukVQLCQmH3IrhjU4+FX1Y5ZmCw14WztJomFOzM1H1w==} + '@prisma-next/contract@0.11.0': + resolution: {integrity: sha512-PDddWjEtp9E5txyxavUxQ1fjH2pasjh57NZp6103swe9EatJP8LKqz9mBKo6zhuhF3xJasNgPBbNCMc4x4I0gg==} engines: {node: '>=20'} '@prisma-next/contract@0.14.0': @@ -1882,8 +1885,8 @@ packages: typescript: optional: true - '@prisma-next/driver-postgres@0.10.0': - resolution: {integrity: sha512-+gyl4gU2K6xSoC+3rJX+hbhGVeitst6ZkVWE4CsYcKvT1L9WRlvZfy5LJaV3+lcsDwB3Gd7KvDDG0+i+XYsguQ==} + '@prisma-next/driver-postgres@0.11.0': + resolution: {integrity: sha512-iNQEeh/3XwGr/MO1nTjos69bgZSfTZsWP2b4aktejMEmev3FicW/MMHNZjbIeQzscWy0XrGEFLmUPAZ0OvjA8A==} '@prisma-next/driver-postgres@0.14.0': resolution: {integrity: sha512-qla7/fAk2YqwomkUPevd9bIqJ5gQZcZTXbEoz8nJaErUunisEosepJOKwlTpquJV2+OAuezODCbrrYtUJ525jg==} @@ -1894,8 +1897,8 @@ packages: typescript: optional: true - '@prisma-next/emitter@0.10.0': - resolution: {integrity: sha512-Bek5fWE7QWHzzwKsTvq/gmLYJPk+p7NisYNd0229IZSMfWzo6OMJT2/pHvDRyU6ykFK/c2rYdxvqrBdwGR5vfw==} + '@prisma-next/emitter@0.11.0': + resolution: {integrity: sha512-ehcN/PpgWVeNjRpBBDCFuRuPfOcTEIiHiNZCrB3U+wpXfaekqLLzDuN6sh4Jj+qp5JuLAMyw+zXvUx6J9+Ualw==} '@prisma-next/emitter@0.14.0': resolution: {integrity: sha512-qdyOe11AsXHMpVmPYulA/NjKEn59VkXekGrtg8lB+Pqd+Dg2HwQinaO6G1PXjymRyVgv31UKRyUU7eLhCQaZ0Q==} @@ -1906,8 +1909,8 @@ packages: typescript: optional: true - '@prisma-next/errors@0.10.0': - resolution: {integrity: sha512-EYFw4isfWMvme3euuji1KAslSZ5REp+37Gw8QPwZ6+gpJjqpTh7LWtTTE4zmexv/Nc4KNYfuH/hVOQcPSfHVMQ==} + '@prisma-next/errors@0.11.0': + resolution: {integrity: sha512-XBqO7Igiym6bL0361KJLpFflcGPXsjBaRAklR3vtH+Js1E1MS0OWRVB+l5AT5E6REN2aVJstafykdaVgMq1B6Q==} engines: {node: '>=20'} '@prisma-next/errors@0.14.0': @@ -1929,8 +1932,8 @@ packages: typescript: optional: true - '@prisma-next/family-sql@0.10.0': - resolution: {integrity: sha512-gYzBMNvENpWQKCCAta/wiCmsn5JBwx8uhVEouuTrYqRWGTHrZ6bgwv/vVxpcY73GtoNd/tShGHPc6JoNlrP0vw==} + '@prisma-next/family-sql@0.11.0': + resolution: {integrity: sha512-bLAsWdbTRLn7m6K4J+tj+E4F+4/OjRvnAbg6MKnBN1WOULnZN/SFPSbxIDt+l3RE16lsZmyVS04FjnprRLtesQ==} '@prisma-next/family-sql@0.14.0': resolution: {integrity: sha512-Gx4ByuilDiJzwsOkqWE42CSZrhO9/6uFDpyemD185eZsaJGk5tvEpbLy3UuLNx+6VEsvXfPYGLdGUy2LL8e9Pg==} @@ -1941,8 +1944,8 @@ packages: typescript: optional: true - '@prisma-next/framework-components@0.10.0': - resolution: {integrity: sha512-tdcGLgrN2wsuSsfjA5rTqO3pOFaQiypn28f0i/uQ9KwoDFEDoq7XwFwhimqqrrpeePAVAt/la8Tw60ThE/AsDQ==} + '@prisma-next/framework-components@0.11.0': + resolution: {integrity: sha512-yrBN3BOZKBIaKSgTR791ZC8vBLkQRppaAWFm2w5rVseGYElu4x1JVew33exnC2OikR6/AQeRswqA78Kpqiej0w==} '@prisma-next/framework-components@0.14.0': resolution: {integrity: sha512-6ZPnesKake4pnRRQZUi67a5qfgs6Pc9cmNN2bAf2bQJYTg3w5b1yOhjg2sFupXT54hq/iE3rXRY5A7ltgSxijA==} @@ -1953,8 +1956,8 @@ packages: typescript: optional: true - '@prisma-next/ids@0.10.0': - resolution: {integrity: sha512-kNGze4NksyRe5nEo6LznqFuy5hWUqEvRdzjAyvRJbP0Z01EGIcueJNsc+i288FvIAC1A8PCB7MWYs87eSQtQaw==} + '@prisma-next/ids@0.11.0': + resolution: {integrity: sha512-5Z3hCDibcuMEGd/Jb8bqhd65x5lLFdExhj9IletPj+b9et3xY/HMDN0ZbUWSiMR97KvvvgcDiI82i/15T1QiWA==} '@prisma-next/ids@0.14.0': resolution: {integrity: sha512-E+Nhvgmco/Y24wiv/hWVt6wAf1J9sIS5qupoolBeL0V/BA/+zECewg/vCF2dj92hXk0jJ1WphhQDGrvx5XAPbQ==} @@ -1965,8 +1968,8 @@ packages: typescript: optional: true - '@prisma-next/migration-tools@0.10.0': - resolution: {integrity: sha512-WJJ8LMHCXlewSrtC4aJkxi0yI7/m9JbvnLVlTRzA4D94RkiEDbQg77LQhp3osF26eQqXu3Jc+LQMQ6nAItEYfQ==} + '@prisma-next/migration-tools@0.11.0': + resolution: {integrity: sha512-QU9QGA1EbNMsi9LC6EMPeeBc9KBI0V3x4ptBoLPuocI6QG7d7fQlmu7qXS2qQUO6DmqMiDptK5InRKLB3uki8A==} engines: {node: '>=20'} '@prisma-next/migration-tools@0.14.0': @@ -1978,8 +1981,8 @@ packages: typescript: optional: true - '@prisma-next/operations@0.10.0': - resolution: {integrity: sha512-R6qhJB9CJuT2xyQbTIkJ4wXqclLaiJz5h6aVkRBUXr+Vt/EYA8SfGzzA6vO62gBbLHvDr+nZXEolxsSi5LOOIw==} + '@prisma-next/operations@0.11.0': + resolution: {integrity: sha512-viAM+bmj9dVWdeO3R24TXorKC3v+6SyKyIQjQKmTSe4x1iwj6TjODxHbd6y6VUum/pph5Q79y3Flji+5vPDIcw==} engines: {node: '>=20'} '@prisma-next/operations@0.14.0': @@ -2000,8 +2003,8 @@ packages: typescript: optional: true - '@prisma-next/psl-parser@0.10.0': - resolution: {integrity: sha512-+4HwCLoEsqK+MXJjG9CumdfLlKeRsSh8VSQ5yFz5Dy0Q9srbYyOooFMiZ8+85bAU65xz/pB2/59MN+tpWrMFrA==} + '@prisma-next/psl-parser@0.11.0': + resolution: {integrity: sha512-fMNQJAYsdm+r9MAmdAGzMoGqIqzu9U5LIvXK4OuFNtUispJObhhxprrVvJVLL+sJREbgVhug95+Ahko6XngHxg==} '@prisma-next/psl-parser@0.14.0': resolution: {integrity: sha512-XKumV4Bfy3KHoD3IsWVSDtzFjXKL5zENASbbfIXXpwll3AVgU4rtVKuJIH5fwUOYpcCUdLE8ZaNw6vcwLuse3w==} @@ -2012,8 +2015,8 @@ packages: typescript: optional: true - '@prisma-next/psl-printer@0.10.0': - resolution: {integrity: sha512-aeE9udDT9tNJ3SMDNYQhND7Sv8q3XpLaDbX83ZG2tynQKNvZP+RsbA03FYTs5UdEDm2pY2EjpGQ68nnPpTXY6g==} + '@prisma-next/psl-printer@0.11.0': + resolution: {integrity: sha512-OP6ipK83ycC6OgtXkcaQgxJvvDPZZvrhO8w4ILhYQdYXdTjUZXTDa0+bJxr9p+KgM3FDYR0oxRKyBFEDNokf4w==} '@prisma-next/psl-printer@0.14.0': resolution: {integrity: sha512-qVTjwpO9uvITSsMg/mtdk0Py7ITsQRpPjmidSi9ByoPE6OGBGF3/BaUbOjRiZ340i39Npg83jT4rLreH1GOFjg==} @@ -2033,8 +2036,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-emitter@0.10.0': - resolution: {integrity: sha512-pep5zFhyYs6FWQsJR229i7Xjj9K+XLGWZwI0l3eW7G+XJsPtef2zz0SapJCPcCB/CEbVUWx3kVxz0DtvKGwDbA==} + '@prisma-next/sql-contract-emitter@0.11.0': + resolution: {integrity: sha512-39iWRUR2dZ9NIbv7QAOMUV9qK3HnGweFdTEXnutWJdsGvyDnkxt59FgJZbNwBy01xuIhmh3ZA+l7g+IxyYI/OA==} '@prisma-next/sql-contract-emitter@0.14.0': resolution: {integrity: sha512-gUgctyFNMKZ94wXNATevMi+FG9R9SK4yYQesYr7/5kZ4ZBbHkaFOFcWwlJZSI+EhCC2J1OgvfaCTtpWpn+VIUw==} @@ -2045,8 +2048,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-psl@0.10.0': - resolution: {integrity: sha512-vgzQMC80Oc1wvY/xO1xoFi9ZdPDjbtjxeeVeVHoimTx/Q62skZ1TiyU1t/IP8A9PtXBXTgFgR+Zs1xRbzKn5bg==} + '@prisma-next/sql-contract-psl@0.11.0': + resolution: {integrity: sha512-fooOPbsnfEPJwxW+jt5fVEyOn6sT3Q6S5DtmxWwIqwkuJRZk8zAh/FoPq1daTiJT5u7XjJjysdztUzJ0uUK56g==} '@prisma-next/sql-contract-psl@0.14.0': resolution: {integrity: sha512-86Z8MWo0NYhGSX4uOqbvAx46aMOaB2opNyUeFNnntT1br+U48IX2ZPGV62ul6mwCp7DGN7pjvmlBW5OnHXuiyg==} @@ -2057,8 +2060,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-ts@0.10.0': - resolution: {integrity: sha512-ejOEM3FJ1MjdsD2fD9eHtdtT0cHvHR8Vx9fSf7wV/9iR8FqlDvYT6V1/eT38R5rVWyRviXWZlyB/KaoifIgKhg==} + '@prisma-next/sql-contract-ts@0.11.0': + resolution: {integrity: sha512-30YBhrj4A4COsOC6Ogz50ONU383sy7NZ/vP/Mb2iw/+6SlVKymg812gSw9+w8a668MzJnDzribJ/stXqPXdXsw==} '@prisma-next/sql-contract-ts@0.14.0': resolution: {integrity: sha512-AMTURIMXCsn+M7oW0QOe/9wS7J+Q1siW7+aihgtmBj88lYav9XHyxJO5AJDu6LSDSctorTzccfsPatBAlcxzKQ==} @@ -2069,8 +2072,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract@0.10.0': - resolution: {integrity: sha512-kgrjCzAN6Cqz7Y5jIFP5yTf53eNxNWUO6kCdmP90zJuzPBBRPnzhPGZdAEbfoT6K/gjip3k68lsFXA+xle8tVw==} + '@prisma-next/sql-contract@0.11.0': + resolution: {integrity: sha512-rztm5R0nmumIp6IKPmmQPLFnyzXgq4geOna0/tMRNqNOxvzvtpmVusFOq049Q39Bc05Y9l9NbJ5nq2E0PArWYA==} '@prisma-next/sql-contract@0.14.0': resolution: {integrity: sha512-/Ibvjd41sEgpi4c3YCZvK6vRCucNffsIZ+isIOYoVqXOt74H0llM4Ux4mR0PShNuo0jgG8xOon1ptSQ6/S7c+w==} @@ -2081,8 +2084,8 @@ packages: typescript: optional: true - '@prisma-next/sql-errors@0.10.0': - resolution: {integrity: sha512-MhppTzW3Visg0RRJILvRgOKZjhOJA8UnSJqEH3zc/9wr9qVyjf9DXDIJKH6TZH9jkWTbXwqkTx55vWzI3vkCTA==} + '@prisma-next/sql-errors@0.11.0': + resolution: {integrity: sha512-FCr89Hofj8dBQ40zaQ+dzCqkrJSQIf7DM6HOm9KdwOjNke7fJLCAwsRGmiaet61MWEYikQjBletRAahyKli+9w==} engines: {node: '>=20'} '@prisma-next/sql-errors@0.14.0': @@ -2094,8 +2097,8 @@ packages: typescript: optional: true - '@prisma-next/sql-operations@0.10.0': - resolution: {integrity: sha512-w615a6D0znJfQayGjOUb1ylc/1DX4/Nib1I+qK20xgYvSG3fM5YLILkUFcuvKcHhkFHp2a4ynNantfsVVLfx5w==} + '@prisma-next/sql-operations@0.11.0': + resolution: {integrity: sha512-ktjE5aLXGGiFjtFXPAQyo9S8+9FilintKGgEy+EJxB7sHKqRezUhT41v329EyZY5WzapCgq2LtCRpCtFBuoeLA==} engines: {node: '>=20'} '@prisma-next/sql-operations@0.14.0': @@ -2116,8 +2119,8 @@ packages: typescript: optional: true - '@prisma-next/sql-relational-core@0.10.0': - resolution: {integrity: sha512-W0n6F5axdeezfm079bUIEUNwqwBWerLtfb2LPMJlmSxA9BamKk4c0H2XftP++iCwvjW0g/xG9sBbtOz9d5egkQ==} + '@prisma-next/sql-relational-core@0.11.0': + resolution: {integrity: sha512-D8LRwBWXZgYGcbMp85LQcecv4KO5ADeg/vgvXXiM1+kisYYrC9lbIEKCUIhmGPWqQ27N335OckqoFceLpoDf2w==} '@prisma-next/sql-relational-core@0.14.0': resolution: {integrity: sha512-7H1HaN31lmGD81C9vz07dq/r5n80yKXfFNpYj5XrKKeVpDZQSb/IXtOVt7LyB+q9QqSUW2jD5RGLuTOhsd2kbw==} @@ -2128,8 +2131,8 @@ packages: typescript: optional: true - '@prisma-next/sql-runtime@0.10.0': - resolution: {integrity: sha512-fk0+ZaVSkF+kogY19O2iLLK5kMZd8p5RXiTIHlKujalmo3mQ2f+gVNL0co2uA3Nh5RyHSf+t7oq2GK5ZQsZ63g==} + '@prisma-next/sql-runtime@0.11.0': + resolution: {integrity: sha512-UFwHj69jM0XDlB0YVuXOp+7PayYI9xTGG2l/ecE3aUg4pz79rtKpZ6JqL7IatiISaId6KsRGWtG1/YobwX+XzQ==} '@prisma-next/sql-runtime@0.14.0': resolution: {integrity: sha512-HV8ZAvNuXtFrAHuxs3/ZEz0JapKFayFYDljQeVDFxExSEj79fPtd9CON9sTTV4T/RKII5knlKJRuKtwWH35Znw==} @@ -2140,8 +2143,8 @@ packages: typescript: optional: true - '@prisma-next/sql-schema-ir@0.10.0': - resolution: {integrity: sha512-OlzfkVkxA0BNCQphb5hgnPWO8AqJI3NmKAYLI0xBHrUqlHDOetpbDJuBFSzN5BE5uLPQEQ/B1Ecd6efoH+aa1A==} + '@prisma-next/sql-schema-ir@0.11.0': + resolution: {integrity: sha512-EcNDZ21o2z3pCe4nSedYjhSAXcSBQ5eK6K33IkGoL+C0Tns62Ks0Sj6oBHHdlu7iWrzdiEs9kMoZ0moh5h9wEg==} engines: {node: '>=20'} '@prisma-next/sql-schema-ir@0.14.0': @@ -2153,8 +2156,8 @@ packages: typescript: optional: true - '@prisma-next/target-postgres@0.10.0': - resolution: {integrity: sha512-MWqMguwu4Fxanw2xOAVL8HijJJt9h7cEiP9alSCyBr0NVist7l0a4Aw9h4z0AGeCKnxnLDV35GWyG/BowNP3PA==} + '@prisma-next/target-postgres@0.11.0': + resolution: {integrity: sha512-ZILsX/yJ99WCUZPi9Dlj3Ww2uvdrFF0BQ6f8pSKOR130WTSyniGTKY176/1zG4aNnZ+dCOqkjL/1eaZ+2/zZ5Q==} '@prisma-next/target-postgres@0.14.0': resolution: {integrity: sha512-i1H9Jzt7ZGgYQ61rlmalyEPY1RoLFIMk9+qE5WbETjQ8LY9p7iXeTx4bBv25twkYIAHxXtrYxsQ3klUWg0JfVg==} @@ -2165,8 +2168,8 @@ packages: typescript: optional: true - '@prisma-next/ts-render@0.10.0': - resolution: {integrity: sha512-8hKW3krALe5aPtUZnZWyTp25qqdshQ1JPqtDGWKEwRhFXIxeOWzJoajxJw0hBAGhFVimTcF0y1PaXU0gSVD0eQ==} + '@prisma-next/ts-render@0.11.0': + resolution: {integrity: sha512-OX//hWyh9KMd2440KjA0R4vkDCZIQbzpgdirEeHnNxOuQ4QKgHCFSjg/3sqIeSRCBG0WnzAtrVBpsFV1qSHBsg==} engines: {node: '>=20'} '@prisma-next/ts-render@0.14.0': @@ -2178,8 +2181,8 @@ packages: typescript: optional: true - '@prisma-next/utils@0.10.0': - resolution: {integrity: sha512-MLBGskruFbGiyjHs78gyBRS/6D0cpJeAVN4UxxVxq/qm2m4dS9saXeI3OZz5sn4cHgc10vU47Ssibo6cSWpc1g==} + '@prisma-next/utils@0.11.0': + resolution: {integrity: sha512-QUA2q5Qw0tFqXea5bK3M0jGVFpFpQ3kgAW51RhENkiShnIyWh0Jjn2TKBrvWBXCzvfF1kJI20/Hr7X8xC9p+3g==} engines: {node: '>=20'} '@prisma-next/utils@0.14.0': @@ -5176,22 +5179,23 @@ snapshots: '@posthog/types@1.373.5': {} - '@prisma-next/adapter-postgres@0.10.0(typanion@3.14.0)': - dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/contract-authoring': 0.10.0 - '@prisma-next/family-sql': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/ids': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/sql-contract-psl': 0.10.0 - '@prisma-next/sql-contract-ts': 0.10.0 - '@prisma-next/sql-operations': 0.10.0 - '@prisma-next/sql-relational-core': 0.10.0 - '@prisma-next/sql-runtime': 0.10.0 - '@prisma-next/sql-schema-ir': 0.10.0 - '@prisma-next/target-postgres': 0.10.0(typanion@3.14.0) - '@prisma-next/utils': 0.10.0 + '@prisma-next/adapter-postgres@0.11.0(typanion@3.14.0)': + dependencies: + '@prisma-next/contract': 0.11.0 + '@prisma-next/contract-authoring': 0.11.0 + '@prisma-next/errors': 0.11.0 + '@prisma-next/family-sql': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/ids': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/sql-contract-psl': 0.11.0 + '@prisma-next/sql-contract-ts': 0.11.0 + '@prisma-next/sql-operations': 0.11.0 + '@prisma-next/sql-relational-core': 0.11.0 + '@prisma-next/sql-runtime': 0.11.0 + '@prisma-next/sql-schema-ir': 0.11.0 + '@prisma-next/target-postgres': 0.11.0(typanion@3.14.0) + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 transitivePeerDependencies: - magicast @@ -5221,6 +5225,16 @@ snapshots: - magicast - typanion + '@prisma-next/cli-telemetry@0.11.0': + dependencies: + '@prisma-next/config': 0.11.0 + '@prisma-next/utils': 0.11.0 + arktype: 2.2.0 + c12: 3.3.4 + pathe: 2.0.3 + transitivePeerDependencies: + - magicast + '@prisma-next/cli-telemetry@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/config': 0.14.0(typescript@5.9.3) @@ -5233,20 +5247,22 @@ snapshots: transitivePeerDependencies: - magicast - '@prisma-next/cli@0.10.0(typanion@3.14.0)': + '@prisma-next/cli@0.11.0(typanion@3.14.0)': dependencies: '@clack/prompts': 1.4.0 '@dagrejs/dagre': 3.0.0 - '@prisma-next/config': 0.10.0 - '@prisma-next/contract': 0.10.0 - '@prisma-next/emitter': 0.10.0 - '@prisma-next/errors': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/migration-tools': 0.10.0 - '@prisma-next/psl-printer': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/cli-telemetry': 0.11.0 + '@prisma-next/config': 0.11.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/emitter': 0.11.0 + '@prisma-next/errors': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/migration-tools': 0.11.0 + '@prisma-next/psl-printer': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 c12: 3.3.4 + ci-info: 4.4.0 clipanion: 4.0.0-rc.4(typanion@3.14.0) closest-match: 1.3.3 colorette: 2.0.20 @@ -5294,11 +5310,11 @@ snapshots: - magicast - typanion - '@prisma-next/config@0.10.0': + '@prisma-next/config@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 '@prisma-next/config@0.14.0(typescript@5.9.3)': @@ -5310,9 +5326,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract-authoring@0.10.0': + '@prisma-next/contract-authoring@0.11.0': dependencies: - '@prisma-next/framework-components': 0.10.0 + '@prisma-next/framework-components': 0.11.0 '@prisma-next/contract-authoring@0.14.0(typescript@5.9.3)': dependencies: @@ -5320,9 +5336,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract@0.10.0': + '@prisma-next/contract@0.11.0': dependencies: - '@prisma-next/utils': 0.10.0 + '@prisma-next/utils': 0.11.0 '@standard-schema/spec': 1.1.0 arktype: 2.2.0 @@ -5334,16 +5350,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/driver-postgres@0.10.0': + '@prisma-next/driver-postgres@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/errors': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/sql-errors': 0.10.0 - '@prisma-next/sql-operations': 0.10.0 - '@prisma-next/sql-relational-core': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/errors': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/sql-errors': 0.11.0 + '@prisma-next/sql-operations': 0.11.0 + '@prisma-next/sql-relational-core': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 pg: 8.20.0 pg-cursor: 2.21.0(pg@8.20.0) @@ -5368,12 +5384,12 @@ snapshots: transitivePeerDependencies: - pg-native - '@prisma-next/emitter@0.10.0': + '@prisma-next/emitter@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/operations': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/operations': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 prettier: 3.8.3 @@ -5389,10 +5405,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/errors@0.10.0': + '@prisma-next/errors@0.11.0': dependencies: - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/utils': 0.11.0 '@prisma-next/errors@0.14.0(typescript@5.9.3)': dependencies: @@ -5405,21 +5421,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/family-sql@0.10.0': - dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/emitter': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/migration-tools': 0.10.0 - '@prisma-next/operations': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/sql-contract-emitter': 0.10.0 - '@prisma-next/sql-contract-ts': 0.10.0 - '@prisma-next/sql-operations': 0.10.0 - '@prisma-next/sql-relational-core': 0.10.0 - '@prisma-next/sql-runtime': 0.10.0 - '@prisma-next/sql-schema-ir': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/family-sql@0.11.0': + dependencies: + '@prisma-next/contract': 0.11.0 + '@prisma-next/emitter': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/migration-tools': 0.11.0 + '@prisma-next/operations': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/sql-contract-emitter': 0.11.0 + '@prisma-next/sql-contract-ts': 0.11.0 + '@prisma-next/sql-operations': 0.11.0 + '@prisma-next/sql-relational-core': 0.11.0 + '@prisma-next/sql-runtime': 0.11.0 + '@prisma-next/sql-schema-ir': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 '@prisma-next/family-sql@0.14.0(typescript@5.9.3)': @@ -5441,13 +5457,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/framework-components@0.10.0': + '@prisma-next/framework-components@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/operations': 0.10.0 - '@prisma-next/ts-render': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/operations': 0.11.0 + '@prisma-next/ts-render': 0.11.0 + '@prisma-next/utils': 0.11.0 '@standard-schema/spec': 1.1.0 + arktype: 2.2.0 '@prisma-next/framework-components@0.14.0(typescript@5.9.3)': dependencies: @@ -5460,11 +5477,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/ids@0.10.0': + '@prisma-next/ids@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/utils': 0.11.0 uniku: 0.0.12 '@prisma-next/ids@0.14.0(typescript@5.9.3)': @@ -5476,11 +5493,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/migration-tools@0.10.0': + '@prisma-next/migration-tools@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 pathe: 2.0.3 prettier: 3.8.3 @@ -5496,7 +5513,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/operations@0.10.0': {} + '@prisma-next/operations@0.11.0': {} '@prisma-next/operations@0.14.0(typescript@5.9.3)': optionalDependencies: @@ -5529,10 +5546,10 @@ snapshots: - pg-native - typanion - '@prisma-next/psl-parser@0.10.0': + '@prisma-next/psl-parser@0.11.0': dependencies: - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/utils': 0.11.0 '@prisma-next/psl-parser@0.14.0(typescript@5.9.3)': dependencies: @@ -5541,9 +5558,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-printer@0.10.0': + '@prisma-next/psl-printer@0.11.0': dependencies: - '@prisma-next/framework-components': 0.10.0 + '@prisma-next/framework-components': 0.11.0 '@prisma-next/psl-printer@0.14.0(typescript@5.9.3)': dependencies: @@ -5562,13 +5579,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-emitter@0.10.0': + '@prisma-next/sql-contract-emitter@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/emitter': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/emitter': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/utils': 0.11.0 '@prisma-next/sql-contract-emitter@0.14.0(typescript@5.9.3)': dependencies: @@ -5580,15 +5597,15 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-psl@0.10.0': + '@prisma-next/sql-contract-psl@0.11.0': dependencies: - '@prisma-next/config': 0.10.0 - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/psl-parser': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/sql-contract-ts': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/config': 0.11.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/psl-parser': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/sql-contract-ts': 0.11.0 + '@prisma-next/utils': 0.11.0 pathe: 2.0.3 '@prisma-next/sql-contract-psl@0.14.0(typescript@5.9.3)': @@ -5604,14 +5621,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-ts@0.10.0': + '@prisma-next/sql-contract-ts@0.11.0': dependencies: - '@prisma-next/config': 0.10.0 - '@prisma-next/contract': 0.10.0 - '@prisma-next/contract-authoring': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/config': 0.11.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/contract-authoring': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 pathe: 2.0.3 ts-toolbelt: 9.6.0 @@ -5630,10 +5647,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract@0.10.0': + '@prisma-next/sql-contract@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 arktype: 2.2.0 '@prisma-next/sql-contract@0.14.0(typescript@5.9.3)': @@ -5645,16 +5662,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-errors@0.10.0': {} + '@prisma-next/sql-errors@0.11.0': {} '@prisma-next/sql-errors@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-operations@0.10.0': + '@prisma-next/sql-operations@0.11.0': dependencies: - '@prisma-next/operations': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 + '@prisma-next/operations': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 arktype: 2.2.0 '@prisma-next/sql-operations@0.14.0(typescript@5.9.3)': @@ -5679,14 +5696,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-relational-core@0.10.0': + '@prisma-next/sql-relational-core@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/operations': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/sql-operations': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/operations': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/sql-operations': 0.11.0 + '@prisma-next/utils': 0.11.0 '@standard-schema/spec': 1.1.0 arktype: 2.2.0 ts-toolbelt: 9.6.0 @@ -5705,16 +5722,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-runtime@0.10.0': + '@prisma-next/sql-runtime@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/ids': 0.10.0 - '@prisma-next/operations': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/sql-operations': 0.10.0 - '@prisma-next/sql-relational-core': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/ids': 0.11.0 + '@prisma-next/operations': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/sql-operations': 0.11.0 + '@prisma-next/sql-relational-core': 0.11.0 + '@prisma-next/utils': 0.11.0 arktype: 2.2.0 '@prisma-next/sql-runtime@0.14.0(typescript@5.9.3)': @@ -5731,10 +5748,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-schema-ir@0.10.0': + '@prisma-next/sql-schema-ir@0.11.0': dependencies: - '@prisma-next/contract': 0.10.0 - '@prisma-next/framework-components': 0.10.0 + '@prisma-next/contract': 0.11.0 + '@prisma-next/framework-components': 0.11.0 '@prisma-next/sql-schema-ir@0.14.0(typescript@5.9.3)': dependencies: @@ -5743,21 +5760,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/target-postgres@0.10.0(typanion@3.14.0)': - dependencies: - '@prisma-next/cli': 0.10.0(typanion@3.14.0) - '@prisma-next/contract': 0.10.0 - '@prisma-next/errors': 0.10.0 - '@prisma-next/family-sql': 0.10.0 - '@prisma-next/framework-components': 0.10.0 - '@prisma-next/migration-tools': 0.10.0 - '@prisma-next/sql-contract': 0.10.0 - '@prisma-next/sql-errors': 0.10.0 - '@prisma-next/sql-operations': 0.10.0 - '@prisma-next/sql-relational-core': 0.10.0 - '@prisma-next/sql-schema-ir': 0.10.0 - '@prisma-next/ts-render': 0.10.0 - '@prisma-next/utils': 0.10.0 + '@prisma-next/target-postgres@0.11.0(typanion@3.14.0)': + dependencies: + '@prisma-next/cli': 0.11.0(typanion@3.14.0) + '@prisma-next/contract': 0.11.0 + '@prisma-next/errors': 0.11.0 + '@prisma-next/family-sql': 0.11.0 + '@prisma-next/framework-components': 0.11.0 + '@prisma-next/migration-tools': 0.11.0 + '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/sql-errors': 0.11.0 + '@prisma-next/sql-operations': 0.11.0 + '@prisma-next/sql-relational-core': 0.11.0 + '@prisma-next/sql-schema-ir': 0.11.0 + '@prisma-next/ts-render': 0.11.0 + '@prisma-next/utils': 0.11.0 '@standard-schema/spec': 1.1.0 arktype: 2.2.0 pathe: 2.0.3 @@ -5789,13 +5806,13 @@ snapshots: - magicast - typanion - '@prisma-next/ts-render@0.10.0': {} + '@prisma-next/ts-render@0.11.0': {} '@prisma-next/ts-render@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/utils@0.10.0': {} + '@prisma-next/utils@0.11.0': {} '@prisma-next/utils@0.14.0(typescript@5.9.3)': optionalDependencies: From c2e7e1c1d7436a084950eb7c2c374a5abc8cfb98 Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:08:08 -0600 Subject: [PATCH 06/15] chore: upgrade @prisma-next/* to 0.12.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-transition changes (extension-author upgrade instructions 0.11→0.12): - strip-migration-labels-hints: codemod removed labels/hints from the seed migration manifest and recomputed migrationHash - extension-public-default-baseline: re-emitted the contract space — the default namespace flips __unbound__/postgres-unbound-schema → public/postgres-schema — and re-pinned the baseline migration (end-contract.{json,d.ts}, migration.ts to-hash, self-emitted ops.json/migration.json, refs/head.json) - domain plane: contract.json now carries domain.namespaces instead of flat models; roots entries are { model, namespace } objects; all handcrafted contract fixtures replace models:{} with domain.namespaces..models Additional 0.12 SPI churn not covered by the instructions: - SqlMiddlewareContext (via RuntimeMiddlewareContext) gained a required planExecutionId — middleware test contexts stamp a fixed id - SqlStorageInput.namespaces is now Readonly>; test fixtures construct entries via buildSqlNamespace(...) - the PSL interpreter emits authored tables into the target-owned default namespace (public on Postgres); asStorage test helpers walk all namespaces instead of hard-coding __unbound__ - assertDescriptorSelfConsistency now recomputes through the family canonicalization hooks; the descriptor test passes sqlContractCanonicalizationHooks explicitly Co-Authored-By: Claude Fable 5 --- .../end-contract.d.ts | 179 +++-- .../end-contract.json | 97 +-- .../migration.json | 10 +- .../migration.ts | 2 +- .../prisma-next/migrations/refs/head.json | 2 +- packages/prisma-next/package.json | 36 +- packages/prisma-next/src/contract.d.ts | 179 +++-- packages/prisma-next/src/contract.json | 97 +-- packages/prisma-next/test/abort.test.ts | 1 + .../test/bulk-encrypt-middleware.test.ts | 1 + .../test/cipherstash-codec.test.ts | 12 +- packages/prisma-next/test/descriptor.test.ts | 5 + packages/prisma-next/test/helpers.test.ts | 2 +- .../test/operator-lowering.helpers.ts | 2 +- .../test/psl-interpretation-numeric.test.ts | 9 +- .../psl-interpretation-other-types.test.ts | 9 +- .../test/psl-interpretation.test.ts | 9 +- pnpm-lock.yaml | 726 ++++++++++++------ 18 files changed, 866 insertions(+), 512 deletions(-) diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts index 851a6f7ce..7e9b1072f 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts @@ -1,19 +1,21 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma-next contract emit'. // To regenerate, run: prisma-next contract emit -import type { CodecTypes as PgTypes } from '@prisma-next/target-postgres/codec-types'; -import type { JsonValue } from '@prisma-next/target-postgres/codec-types'; -import type { Char } from '@prisma-next/target-postgres/codec-types'; -import type { Varchar } from '@prisma-next/target-postgres/codec-types'; -import type { Numeric } from '@prisma-next/target-postgres/codec-types'; -import type { Bit } from '@prisma-next/target-postgres/codec-types'; -import type { VarBit } from '@prisma-next/target-postgres/codec-types'; -import type { Timestamp } from '@prisma-next/target-postgres/codec-types'; -import type { Timestamptz } from '@prisma-next/target-postgres/codec-types'; -import type { Time } from '@prisma-next/target-postgres/codec-types'; -import type { Timetz } from '@prisma-next/target-postgres/codec-types'; -import type { Interval } from '@prisma-next/target-postgres/codec-types'; import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; import type { ContractWithTypeMaps, @@ -21,16 +23,18 @@ import type { } from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, + ContractModelsMap, ExecutionHashBase, + NamespaceId, ProfileHashBase, StorageHashBase, } from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec'>; + StorageHashBase<'sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = - ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'>; + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; export type CodecTypes = PgTypes; export type LaneCodecTypes = CodecTypes; @@ -60,74 +64,116 @@ export type TypeMaps = TypeMapsType< FieldInputTypes >; -type ContractBase = ContractType< - { +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly EqlV2Configuration: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly state: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly data: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'eql_v2_configuration'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly state: { readonly column: 'state' }; + readonly data: { readonly column: 'data' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly eql_v2_configuration: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'EqlV2Configuration'; + }; + }; + readonly domain: { readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'sql-namespace'; - readonly tables: { - readonly eql_v2_configuration: { - columns: { + readonly public: { + readonly models: { + readonly EqlV2Configuration: { + readonly fields: { readonly id: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; }; readonly state: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; }; readonly data: { - readonly nativeType: 'jsonb'; - readonly codecId: 'pg/jsonb@1'; readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'eql_v2_configuration'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly state: { readonly column: 'state' }; + readonly data: { readonly column: 'data' }; }; }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; }; }; }; }; - readonly storageHash: StorageHash; - }, - { - readonly EqlV2Configuration: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly state: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly data: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'eql_v2_configuration'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly state: { readonly column: 'state' }; - readonly data: { readonly column: 'data' }; - }; - }; - }; - } -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { readonly eql_v2_configuration: 'EqlV2Configuration' }; + }; readonly capabilities: { readonly postgres: { + readonly distinctOn: true; readonly jsonAgg: true; readonly lateral: true; readonly limit: true; @@ -137,6 +183,7 @@ type ContractBase = ContractType< readonly sql: { readonly defaultInInsert: true; readonly enums: true; + readonly lateral: true; readonly returning: true; }; }; @@ -149,4 +196,4 @@ type ContractBase = ContractType< export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; -export type Models = Contract['models']; +export type Models = ContractModelsMap; diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json index 2f1cbe180..ec5e42fdd 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json @@ -2,57 +2,66 @@ "schemaVersion": "1", "targetFamily": "sql", "target": "postgres", - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", "roots": { - "eql_v2_configuration": "EqlV2Configuration" + "eql_v2_configuration": { + "model": "EqlV2Configuration", + "namespace": "public" + } }, - "models": { - "EqlV2Configuration": { - "fields": { - "data": { - "nullable": false, - "type": { - "codecId": "pg/jsonb@1", - "kind": "scalar" - } - }, - "id": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "state": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" + "domain": { + "namespaces": { + "public": { + "models": { + "EqlV2Configuration": { + "fields": { + "data": { + "nullable": false, + "type": { + "codecId": "pg/jsonb@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "state": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "data": { + "column": "data" + }, + "id": { + "column": "id" + }, + "state": { + "column": "state" + } + }, + "table": "eql_v2_configuration" + } } } - }, - "relations": {}, - "storage": { - "fields": { - "data": { - "column": "data" - }, - "id": { - "column": "id" - }, - "state": { - "column": "state" - } - }, - "table": "eql_v2_configuration" } } }, "storage": { "namespaces": { - "__unbound__": { - "id": "__unbound__", - "kind": "postgres-unbound-schema", + "public": { + "id": "public", + "kind": "postgres-schema", "tables": { "eql_v2_configuration": { "columns": { @@ -84,10 +93,11 @@ } } }, - "storageHash": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec" + "storageHash": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2" }, "capabilities": { "postgres": { + "distinctOn": true, "jsonAgg": true, "lateral": true, "limit": true, @@ -97,6 +107,7 @@ "sql": { "defaultInInsert": true, "enums": true, + "lateral": true, "returning": true } }, diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json index 9f48b0479..1115c219e 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json @@ -1,15 +1,9 @@ { "from": null, - "to": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec", - "labels": [], + "to": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2", "providedInvariants": [ "cipherstash:install-eql-bundle-v1" ], "createdAt": "2026-05-09T03:42:56.902Z", - "hints": { - "used": [], - "applied": [], - "plannerVersion": "2.0.0" - }, - "migrationHash": "sha256:8ca995537c0f89e7064f720471207be4bdd3f4a3186c544adbeba9f54f713a1c" + "migrationHash": "sha256:2faa77ff326090cecfc1babbeca884201f9d509c87641cd77b1c08afcd5451e1" } \ No newline at end of file diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts index f8593b655..5879c7a5e 100755 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts @@ -36,7 +36,7 @@ export default class M extends Migration { override describe() { return { from: null, - to: 'sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec', + to: 'sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2', } } diff --git a/packages/prisma-next/migrations/refs/head.json b/packages/prisma-next/migrations/refs/head.json index 0faf88aa6..7fb48ef3f 100644 --- a/packages/prisma-next/migrations/refs/head.json +++ b/packages/prisma-next/migrations/refs/head.json @@ -1,4 +1,4 @@ { - "hash": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec", + "hash": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2", "invariants": ["cipherstash:install-eql-bundle-v1"] } diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json index 2333697ea..32cecbbd4 100644 --- a/packages/prisma-next/package.json +++ b/packages/prisma-next/package.json @@ -80,28 +80,28 @@ }, "dependencies": { "@cipherstash/stack": "workspace:*", - "@prisma-next/contract": "0.11.0", - "@prisma-next/family-sql": "0.11.0", - "@prisma-next/framework-components": "0.11.0", - "@prisma-next/migration-tools": "0.11.0", - "@prisma-next/sql-contract": "0.11.0", - "@prisma-next/sql-operations": "0.11.0", - "@prisma-next/sql-relational-core": "0.11.0", - "@prisma-next/sql-runtime": "0.11.0", - "@prisma-next/ts-render": "0.11.0", - "@prisma-next/utils": "0.11.0", + "@prisma-next/contract": "0.12.0", + "@prisma-next/family-sql": "0.12.0", + "@prisma-next/framework-components": "0.12.0", + "@prisma-next/migration-tools": "0.12.0", + "@prisma-next/sql-contract": "0.12.0", + "@prisma-next/sql-operations": "0.12.0", + "@prisma-next/sql-relational-core": "0.12.0", + "@prisma-next/sql-runtime": "0.12.0", + "@prisma-next/ts-render": "0.12.0", + "@prisma-next/utils": "0.12.0", "arktype": "^2.1.29" }, "devDependencies": { - "@prisma-next/adapter-postgres": "0.11.0", - "@prisma-next/cli": "0.11.0", - "@prisma-next/driver-postgres": "0.11.0", + "@prisma-next/adapter-postgres": "0.12.0", + "@prisma-next/cli": "0.12.0", + "@prisma-next/driver-postgres": "0.12.0", "@prisma-next/extension-author-tools": "0.14.0", - "@prisma-next/psl-parser": "0.11.0", - "@prisma-next/sql-contract-psl": "0.11.0", - "@prisma-next/sql-contract-ts": "0.11.0", - "@prisma-next/sql-schema-ir": "0.11.0", - "@prisma-next/target-postgres": "0.11.0", + "@prisma-next/psl-parser": "0.12.0", + "@prisma-next/sql-contract-psl": "0.12.0", + "@prisma-next/sql-contract-ts": "0.12.0", + "@prisma-next/sql-schema-ir": "0.12.0", + "@prisma-next/target-postgres": "0.12.0", "pathe": "^2.0.3", "tsup": "catalog:repo", "typescript": "catalog:repo", diff --git a/packages/prisma-next/src/contract.d.ts b/packages/prisma-next/src/contract.d.ts index 851a6f7ce..7e9b1072f 100644 --- a/packages/prisma-next/src/contract.d.ts +++ b/packages/prisma-next/src/contract.d.ts @@ -1,19 +1,21 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma-next contract emit'. // To regenerate, run: prisma-next contract emit -import type { CodecTypes as PgTypes } from '@prisma-next/target-postgres/codec-types'; -import type { JsonValue } from '@prisma-next/target-postgres/codec-types'; -import type { Char } from '@prisma-next/target-postgres/codec-types'; -import type { Varchar } from '@prisma-next/target-postgres/codec-types'; -import type { Numeric } from '@prisma-next/target-postgres/codec-types'; -import type { Bit } from '@prisma-next/target-postgres/codec-types'; -import type { VarBit } from '@prisma-next/target-postgres/codec-types'; -import type { Timestamp } from '@prisma-next/target-postgres/codec-types'; -import type { Timestamptz } from '@prisma-next/target-postgres/codec-types'; -import type { Time } from '@prisma-next/target-postgres/codec-types'; -import type { Timetz } from '@prisma-next/target-postgres/codec-types'; -import type { Interval } from '@prisma-next/target-postgres/codec-types'; import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; import type { ContractWithTypeMaps, @@ -21,16 +23,18 @@ import type { } from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, + ContractModelsMap, ExecutionHashBase, + NamespaceId, ProfileHashBase, StorageHashBase, } from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec'>; + StorageHashBase<'sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = - ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'>; + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; export type CodecTypes = PgTypes; export type LaneCodecTypes = CodecTypes; @@ -60,74 +64,116 @@ export type TypeMaps = TypeMapsType< FieldInputTypes >; -type ContractBase = ContractType< - { +type ContractBase = Omit< + ContractType< + { + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'sql-namespace'; + readonly tables: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }, + { + readonly EqlV2Configuration: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly state: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly data: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'eql_v2_configuration'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly state: { readonly column: 'state' }; + readonly data: { readonly column: 'data' }; + }; + }; + }; + } + >, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly eql_v2_configuration: { + readonly namespace: 'public' & NamespaceId; + readonly model: 'EqlV2Configuration'; + }; + }; + readonly domain: { readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'sql-namespace'; - readonly tables: { - readonly eql_v2_configuration: { - columns: { + readonly public: { + readonly models: { + readonly EqlV2Configuration: { + readonly fields: { readonly id: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; }; readonly state: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; }; readonly data: { - readonly nativeType: 'jsonb'; - readonly codecId: 'pg/jsonb@1'; readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'eql_v2_configuration'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly state: { readonly column: 'state' }; + readonly data: { readonly column: 'data' }; }; }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; }; }; }; }; - readonly storageHash: StorageHash; - }, - { - readonly EqlV2Configuration: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly state: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly data: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'eql_v2_configuration'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly state: { readonly column: 'state' }; - readonly data: { readonly column: 'data' }; - }; - }; - }; - } -> & { - readonly target: 'postgres'; - readonly targetFamily: 'sql'; - readonly roots: { readonly eql_v2_configuration: 'EqlV2Configuration' }; + }; readonly capabilities: { readonly postgres: { + readonly distinctOn: true; readonly jsonAgg: true; readonly lateral: true; readonly limit: true; @@ -137,6 +183,7 @@ type ContractBase = ContractType< readonly sql: { readonly defaultInInsert: true; readonly enums: true; + readonly lateral: true; readonly returning: true; }; }; @@ -149,4 +196,4 @@ type ContractBase = ContractType< export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; -export type Models = Contract['models']; +export type Models = ContractModelsMap; diff --git a/packages/prisma-next/src/contract.json b/packages/prisma-next/src/contract.json index 2f1cbe180..ec5e42fdd 100644 --- a/packages/prisma-next/src/contract.json +++ b/packages/prisma-next/src/contract.json @@ -2,57 +2,66 @@ "schemaVersion": "1", "targetFamily": "sql", "target": "postgres", - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", "roots": { - "eql_v2_configuration": "EqlV2Configuration" + "eql_v2_configuration": { + "model": "EqlV2Configuration", + "namespace": "public" + } }, - "models": { - "EqlV2Configuration": { - "fields": { - "data": { - "nullable": false, - "type": { - "codecId": "pg/jsonb@1", - "kind": "scalar" - } - }, - "id": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "state": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" + "domain": { + "namespaces": { + "public": { + "models": { + "EqlV2Configuration": { + "fields": { + "data": { + "nullable": false, + "type": { + "codecId": "pg/jsonb@1", + "kind": "scalar" + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "state": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + } + }, + "relations": {}, + "storage": { + "fields": { + "data": { + "column": "data" + }, + "id": { + "column": "id" + }, + "state": { + "column": "state" + } + }, + "table": "eql_v2_configuration" + } } } - }, - "relations": {}, - "storage": { - "fields": { - "data": { - "column": "data" - }, - "id": { - "column": "id" - }, - "state": { - "column": "state" - } - }, - "table": "eql_v2_configuration" } } }, "storage": { "namespaces": { - "__unbound__": { - "id": "__unbound__", - "kind": "postgres-unbound-schema", + "public": { + "id": "public", + "kind": "postgres-schema", "tables": { "eql_v2_configuration": { "columns": { @@ -84,10 +93,11 @@ } } }, - "storageHash": "sha256:5fde95fc595b8f1a040f5e6dd986cd913d07c018319d6fa18fdbeb6d507489ec" + "storageHash": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2" }, "capabilities": { "postgres": { + "distinctOn": true, "jsonAgg": true, "lateral": true, "limit": true, @@ -97,6 +107,7 @@ "sql": { "defaultInInsert": true, "enums": true, + "lateral": true, "returning": true } }, diff --git a/packages/prisma-next/test/abort.test.ts b/packages/prisma-next/test/abort.test.ts index 5e9bcacae..2a40b0097 100644 --- a/packages/prisma-next/test/abort.test.ts +++ b/packages/prisma-next/test/abort.test.ts @@ -127,6 +127,7 @@ function makeMiddlewareCtx( now: () => Date.now(), log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, contentHash: async () => 'mock-hash', + planExecutionId: 'test-plan-execution', ...(signal === undefined ? {} : { signal }), } } diff --git a/packages/prisma-next/test/bulk-encrypt-middleware.test.ts b/packages/prisma-next/test/bulk-encrypt-middleware.test.ts index ad6359322..7a4efc004 100644 --- a/packages/prisma-next/test/bulk-encrypt-middleware.test.ts +++ b/packages/prisma-next/test/bulk-encrypt-middleware.test.ts @@ -69,6 +69,7 @@ function createCtx( error: vi.fn(), }, contentHash: async () => 'mock-hash', + planExecutionId: 'test-plan-execution', ...overrides, } } diff --git a/packages/prisma-next/test/cipherstash-codec.test.ts b/packages/prisma-next/test/cipherstash-codec.test.ts index d90c0e858..8a9e611f7 100644 --- a/packages/prisma-next/test/cipherstash-codec.test.ts +++ b/packages/prisma-next/test/cipherstash-codec.test.ts @@ -26,7 +26,11 @@ import { planFieldEventOperations, } from '@prisma-next/family-sql/control' import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components' -import { SqlStorage, type StorageTable } from '@prisma-next/sql-contract/types' +import { + buildSqlNamespace, + SqlStorage, + type StorageTable, +} from '@prisma-next/sql-contract/types' import { ifDefined } from '@prisma-next/utils/defined' import { describe, expect, it } from 'vitest' import cipherstashExtensionDescriptor from '../src/exports/control' @@ -119,9 +123,11 @@ describe('planFieldEventOperations driving the cipherstash hook', () => { profileHash: profileHash('sha256:test'), storage: new SqlStorage({ storageHash: 'sha256:test' as StorageHashBase, - namespaces: { __unbound__: { id: '__unbound__', tables } }, + namespaces: { + __unbound__: buildSqlNamespace({ id: '__unbound__', tables }), + }, }), - models: {}, + domain: { namespaces: { __unbound__: { models: {} } } }, roots: {}, capabilities: {}, extensionPacks: {}, diff --git a/packages/prisma-next/test/descriptor.test.ts b/packages/prisma-next/test/descriptor.test.ts index c85ad4d8b..66afb4681 100644 --- a/packages/prisma-next/test/descriptor.test.ts +++ b/packages/prisma-next/test/descriptor.test.ts @@ -23,6 +23,7 @@ */ import { assertDescriptorSelfConsistency } from '@prisma-next/migration-tools/spaces' +import { sqlContractCanonicalizationHooks } from '@prisma-next/sql-contract/canonicalization-hooks' import { describe, expect, it } from 'vitest' import cipherstashExtensionDescriptor from '../src/exports/control' import { @@ -116,6 +117,10 @@ describe('cipherstash extension descriptor (contract-space package layout)', () unknown >, headRefHash: space.headRef.hash, + // The emit pipeline hashes through the SQL family's + // canonicalization hooks; the recompute must use the same ones. + shouldPreserveEmpty: sqlContractCanonicalizationHooks.shouldPreserveEmpty, + sortStorage: sqlContractCanonicalizationHooks.sortStorage, }), ).not.toThrow() }) diff --git a/packages/prisma-next/test/helpers.test.ts b/packages/prisma-next/test/helpers.test.ts index 03e99dead..374927165 100644 --- a/packages/prisma-next/test/helpers.test.ts +++ b/packages/prisma-next/test/helpers.test.ts @@ -131,7 +131,7 @@ const contract = validateSqlContractFully({ }, }, }, - models: {}, + domain: { namespaces: { __unbound__: { models: {} } } }, }) const stubRuntimeTarget: RuntimeTargetDescriptor<'sql', 'postgres'> = { diff --git a/packages/prisma-next/test/operator-lowering.helpers.ts b/packages/prisma-next/test/operator-lowering.helpers.ts index d217411d4..745ecbbdc 100644 --- a/packages/prisma-next/test/operator-lowering.helpers.ts +++ b/packages/prisma-next/test/operator-lowering.helpers.ts @@ -128,7 +128,7 @@ export const contract = validateSqlContractFully({ }, }, }, - models: {}, + domain: { namespaces: { __unbound__: { models: {} } } }, }) // Stub runtime target — the Postgres adapter only consults `familyId` / diff --git a/packages/prisma-next/test/psl-interpretation-numeric.test.ts b/packages/prisma-next/test/psl-interpretation-numeric.test.ts index cfc8ce181..47b018b41 100644 --- a/packages/prisma-next/test/psl-interpretation-numeric.test.ts +++ b/packages/prisma-next/test/psl-interpretation-numeric.test.ts @@ -56,14 +56,19 @@ type StorageView = { readonly types?: Record> } // Since 0.10 the storage IR is namespace-enveloped: tables live under -// `storage.namespaces.__unbound__`, while `types` stays at the root. +// `storage.namespaces.` (the target-owned default namespace — +// `public` for Postgres since 0.12), while `types` stays at the root. const asStorage = (storage: unknown): StorageView => { const s = storage as { readonly namespaces?: Record readonly types?: StorageView['types'] } + const tables: StorageView['tables'] = {} + for (const ns of Object.values(s.namespaces ?? {})) { + Object.assign(tables, ns.tables) + } return { - tables: s.namespaces?.['__unbound__']?.tables ?? {}, + tables, ...(s.types !== undefined ? { types: s.types } : {}), } } diff --git a/packages/prisma-next/test/psl-interpretation-other-types.test.ts b/packages/prisma-next/test/psl-interpretation-other-types.test.ts index 2f4d2ed05..2beda1ec1 100644 --- a/packages/prisma-next/test/psl-interpretation-other-types.test.ts +++ b/packages/prisma-next/test/psl-interpretation-other-types.test.ts @@ -57,14 +57,19 @@ type StorageView = { readonly types?: Record> } // Since 0.10 the storage IR is namespace-enveloped: tables live under -// `storage.namespaces.__unbound__`, while `types` stays at the root. +// `storage.namespaces.` (the target-owned default namespace — +// `public` for Postgres since 0.12), while `types` stays at the root. const asStorage = (storage: unknown): StorageView => { const s = storage as { readonly namespaces?: Record readonly types?: StorageView['types'] } + const tables: StorageView['tables'] = {} + for (const ns of Object.values(s.namespaces ?? {})) { + Object.assign(tables, ns.tables) + } return { - tables: s.namespaces?.['__unbound__']?.tables ?? {}, + tables, ...(s.types !== undefined ? { types: s.types } : {}), } } diff --git a/packages/prisma-next/test/psl-interpretation.test.ts b/packages/prisma-next/test/psl-interpretation.test.ts index 0d6f4a0ee..5da5de7f5 100644 --- a/packages/prisma-next/test/psl-interpretation.test.ts +++ b/packages/prisma-next/test/psl-interpretation.test.ts @@ -73,14 +73,19 @@ type StorageView = { readonly types?: Record> } // Since 0.10 the storage IR is namespace-enveloped: tables live under -// `storage.namespaces.__unbound__`, while `types` stays at the root. +// `storage.namespaces.` (the target-owned default namespace — +// `public` for Postgres since 0.12), while `types` stays at the root. const asStorage = (storage: unknown): StorageView => { const s = storage as { readonly namespaces?: Record readonly types?: StorageView['types'] } + const tables: StorageView['tables'] = {} + for (const ns of Object.values(s.namespaces ?? {})) { + Object.assign(tables, ns.tables) + } return { - tables: s.namespaces?.['__unbound__']?.tables ?? {}, + tables, ...(s.types !== undefined ? { types: s.types } : {}), } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92f724a7e..50be45e15 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,66 +408,66 @@ importers: specifier: workspace:* version: link:../stack '@prisma-next/contract': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/family-sql': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/framework-components': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/migration-tools': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/sql-contract': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/sql-operations': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/sql-relational-core': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/sql-runtime': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/ts-render': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/utils': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) arktype: specifier: ^2.1.29 version: 2.2.0 devDependencies: '@prisma-next/adapter-postgres': - specifier: 0.11.0 - version: 0.11.0(typanion@3.14.0) + specifier: 0.12.0 + version: 0.12.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/cli': - specifier: 0.11.0 - version: 0.11.0(typanion@3.14.0) + specifier: 0.12.0 + version: 0.12.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/driver-postgres': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/extension-author-tools': specifier: 0.14.0 version: 0.14.0(typescript@5.9.3) '@prisma-next/psl-parser': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/sql-contract-psl': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/sql-contract-ts': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/sql-schema-ir': - specifier: 0.11.0 - version: 0.11.0 + specifier: 0.12.0 + version: 0.12.0(typescript@5.9.3) '@prisma-next/target-postgres': - specifier: 0.11.0 - version: 0.11.0(typanion@3.14.0) + specifier: 0.12.0 + version: 0.12.0(typanion@3.14.0)(typescript@5.9.3) pathe: specifier: ^2.0.3 version: 2.0.3 @@ -1809,8 +1809,14 @@ packages: '@posthog/types@1.373.5': resolution: {integrity: sha512-K7STCnRG/WBE1q0BwEkIcrJB5OqECaymsQj6Hp4Ntvaek4dqHkZGfp6hxwIPqQPjlOXwidwPLo+XGsn+CoZUyw==} - '@prisma-next/adapter-postgres@0.11.0': - resolution: {integrity: sha512-L5nx2kLzBD+0HZ8olvOlVMJohAo0lRcspmPMKNLIFmqdPTQxfdmAHrVbM1tTTw0Q8DcRvO2243oP6ir9eSkoCw==} + '@prisma-next/adapter-postgres@0.12.0': + resolution: {integrity: sha512-2PvyY92pncqieSsxAMNYsxL4SynK9gaNxbdcKqnJhS068WDfPnsuTDq7L5O+fs+PPmq9x0g+QYo3DGU31XzpDA==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/adapter-postgres@0.14.0': resolution: {integrity: sha512-c90KFtI74WU+8YH3uhVg1DTmD10T2BpINzzys9F3n1jTM2Am0tXKx53V05Ev+7H1VBrjBoksZPZ92WWxVEXLew==} @@ -1821,8 +1827,14 @@ packages: typescript: optional: true - '@prisma-next/cli-telemetry@0.11.0': - resolution: {integrity: sha512-Vbhl/4gcch7fMFcuaVSJdhsaqt2aC8uyBZ2q9ZPjAU9QZ2IrG+w2YwfQdC9AXe2sf5BEJ4I/4cXyvV5bCCSYDA==} + '@prisma-next/cli-telemetry@0.12.0': + resolution: {integrity: sha512-0ifr8cuKldEibs7clP9Z/v2+ZgEbraxFmahil+ejgmz0y/RAIdlVCbHS5Nve6mI+0AbdhBDF3Kjaoda/oSrRZA==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/cli-telemetry@0.14.0': resolution: {integrity: sha512-JaP/+09tIL6zH+5WQS507db2HQ0B47HRR2AZD8VHmsw/tMwBDZqKVdIA5KptPMVSR3pcnXDygGvG7E8N+PSPRg==} @@ -1833,9 +1845,15 @@ packages: typescript: optional: true - '@prisma-next/cli@0.11.0': - resolution: {integrity: sha512-iFtlGtZ+6bb3JrSajnn/2gdnbMWl6d2aj4TkoqMHUyqYRXwPXGYQX/dL3+O1t+K2jnqah0M4tWj7zmJL9Be57w==} + '@prisma-next/cli@0.12.0': + resolution: {integrity: sha512-xItdeDd+9sUSvVCqTDIJWC9TTkROMUFYTrECMc2iLk+6AzIzms19ASkqIWqM/2yfGwXIQxwZiFizdqt4sG45UQ==} + engines: {node: '>=24'} hasBin: true + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/cli@0.14.0': resolution: {integrity: sha512-wN4mZc0K1m6wB4NXSEdrA2RCfc8zHBs5yJq0G7XoWQ8J8vvX/hR2kkukJv9QfEYFEIMyOT7vjbSBh2vj4NQ56g==} @@ -1847,9 +1865,14 @@ packages: typescript: optional: true - '@prisma-next/config@0.11.0': - resolution: {integrity: sha512-Yza+AB0wps5vP8TozZl1qinY/fm04kb9E7i1FJts+pEDDElZH04iaTNv+NeOcWNCXVNkdPnPSJnXXi1XekFxfw==} - engines: {node: '>=20'} + '@prisma-next/config@0.12.0': + resolution: {integrity: sha512-cGU3jMKEG17ceTbEKWivThcejyYqpXtUdG6HsXi6yJedw27nxs5SrbaQIbDi1lizzSTEDtOUZGRIU3fSlaoj/g==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/config@0.14.0': resolution: {integrity: sha512-7U4Y5GeicvWy0dcg78J8arzqe13r97Zj8yB/e+Uea6M8LZxV2poW/afHlYnU0VXhLTZ4F29CdlWOUmCAKpl9mg==} @@ -1860,8 +1883,14 @@ packages: typescript: optional: true - '@prisma-next/contract-authoring@0.11.0': - resolution: {integrity: sha512-yJ/CKtr51Xa33ojs5GfdpVk0RvO8MFa/nuiEOr5G1BdQO4H7r8qg448SzEimyjpQy/D8AIPzY5rWIT10P66HlQ==} + '@prisma-next/contract-authoring@0.12.0': + resolution: {integrity: sha512-YH3Uuu+DeETyhd6SDxdoLEuWSVRDZ0AtKQgeD7D+NndnLncsTiEa2i+l0ezrYxP+nA4S6GkrB8+FrFuqZczbhA==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/contract-authoring@0.14.0': resolution: {integrity: sha512-79aLJO04p/XRX2QvIlFMR2ZvbqvI0xFAU5idgYySoevq8bSY9ORGA0XchSbhFl0d6d1VfSEZowRPwzvgl/6lsg==} @@ -1872,9 +1901,14 @@ packages: typescript: optional: true - '@prisma-next/contract@0.11.0': - resolution: {integrity: sha512-PDddWjEtp9E5txyxavUxQ1fjH2pasjh57NZp6103swe9EatJP8LKqz9mBKo6zhuhF3xJasNgPBbNCMc4x4I0gg==} - engines: {node: '>=20'} + '@prisma-next/contract@0.12.0': + resolution: {integrity: sha512-DqRglAsfsby1wclnxdtKU/YuihyEYdl/YCDs2Kt5RCnWTxfTNViIoFoz5E7hiwdkHLDl4fwTXJPzreA35qhT8A==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/contract@0.14.0': resolution: {integrity: sha512-HMeduqWGl1NpRyhEvsswF1T8PSBPfbpvMNYMegiYPt2ck5oClhbl0LHkgAIHsOjBg3IJv2VhRqDhQiuRVbkC4A==} @@ -1885,8 +1919,14 @@ packages: typescript: optional: true - '@prisma-next/driver-postgres@0.11.0': - resolution: {integrity: sha512-iNQEeh/3XwGr/MO1nTjos69bgZSfTZsWP2b4aktejMEmev3FicW/MMHNZjbIeQzscWy0XrGEFLmUPAZ0OvjA8A==} + '@prisma-next/driver-postgres@0.12.0': + resolution: {integrity: sha512-keYGuIkBBdSiQUbFRXQwAlShbmGNogztJOVYL9aCS46UoAKB+bjoylOheImyAQWK+3HtupOmiOUCSbBhUhtdOQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/driver-postgres@0.14.0': resolution: {integrity: sha512-qla7/fAk2YqwomkUPevd9bIqJ5gQZcZTXbEoz8nJaErUunisEosepJOKwlTpquJV2+OAuezODCbrrYtUJ525jg==} @@ -1897,8 +1937,14 @@ packages: typescript: optional: true - '@prisma-next/emitter@0.11.0': - resolution: {integrity: sha512-ehcN/PpgWVeNjRpBBDCFuRuPfOcTEIiHiNZCrB3U+wpXfaekqLLzDuN6sh4Jj+qp5JuLAMyw+zXvUx6J9+Ualw==} + '@prisma-next/emitter@0.12.0': + resolution: {integrity: sha512-kpJ0pPSVUpsAJExkQaMEinhsh7Dwr+MAAKcp0P0C9MbnkvYE5hOUv1FUr+RWlta5bz6ioR73elfp+C+dSAAFng==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/emitter@0.14.0': resolution: {integrity: sha512-qdyOe11AsXHMpVmPYulA/NjKEn59VkXekGrtg8lB+Pqd+Dg2HwQinaO6G1PXjymRyVgv31UKRyUU7eLhCQaZ0Q==} @@ -1909,9 +1955,14 @@ packages: typescript: optional: true - '@prisma-next/errors@0.11.0': - resolution: {integrity: sha512-XBqO7Igiym6bL0361KJLpFflcGPXsjBaRAklR3vtH+Js1E1MS0OWRVB+l5AT5E6REN2aVJstafykdaVgMq1B6Q==} - engines: {node: '>=20'} + '@prisma-next/errors@0.12.0': + resolution: {integrity: sha512-vDeTeUs2Uncpxdme+fjRF/fAN1DSc6TstujwEVmnyjTrKu5tAcwPJagAtG2ewN+nz9aEBu+m060PJxvg+VmbCg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/errors@0.14.0': resolution: {integrity: sha512-+VlDHQaP3AAs3rYYYSDoG5Ng5Hkyl+F4oAHAvDsvhFMupsGWaBx7esc5MbBHfnjE/5i/cSybR8/efXrIZJI6zA==} @@ -1932,8 +1983,14 @@ packages: typescript: optional: true - '@prisma-next/family-sql@0.11.0': - resolution: {integrity: sha512-bLAsWdbTRLn7m6K4J+tj+E4F+4/OjRvnAbg6MKnBN1WOULnZN/SFPSbxIDt+l3RE16lsZmyVS04FjnprRLtesQ==} + '@prisma-next/family-sql@0.12.0': + resolution: {integrity: sha512-WE2Mg01Qm6uaTu0m8VxIH6ylPqWmsqz2836VuJeQFAeZpUZesiSlgZzCbT8qXIsJf55WYk6Ke1auX9g7dO/LLg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/family-sql@0.14.0': resolution: {integrity: sha512-Gx4ByuilDiJzwsOkqWE42CSZrhO9/6uFDpyemD185eZsaJGk5tvEpbLy3UuLNx+6VEsvXfPYGLdGUy2LL8e9Pg==} @@ -1944,8 +2001,14 @@ packages: typescript: optional: true - '@prisma-next/framework-components@0.11.0': - resolution: {integrity: sha512-yrBN3BOZKBIaKSgTR791ZC8vBLkQRppaAWFm2w5rVseGYElu4x1JVew33exnC2OikR6/AQeRswqA78Kpqiej0w==} + '@prisma-next/framework-components@0.12.0': + resolution: {integrity: sha512-rN/x6DUHzhm8jQEBadY8byVUxT9ps1fMWZ2iW0vkJeDyZKMh3mtAEqT59wqS1PEN5CQ6jJsq5FV0AYM1lqHMKw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/framework-components@0.14.0': resolution: {integrity: sha512-6ZPnesKake4pnRRQZUi67a5qfgs6Pc9cmNN2bAf2bQJYTg3w5b1yOhjg2sFupXT54hq/iE3rXRY5A7ltgSxijA==} @@ -1956,8 +2019,14 @@ packages: typescript: optional: true - '@prisma-next/ids@0.11.0': - resolution: {integrity: sha512-5Z3hCDibcuMEGd/Jb8bqhd65x5lLFdExhj9IletPj+b9et3xY/HMDN0ZbUWSiMR97KvvvgcDiI82i/15T1QiWA==} + '@prisma-next/ids@0.12.0': + resolution: {integrity: sha512-tn48qFBlVtn2/6ep48fkiWzAgN/mJXHFuihjzTZ0krB8Vfz6Kw9xu0/BikUf17Ewd653A2LSmtjnmaWslLju9Q==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/ids@0.14.0': resolution: {integrity: sha512-E+Nhvgmco/Y24wiv/hWVt6wAf1J9sIS5qupoolBeL0V/BA/+zECewg/vCF2dj92hXk0jJ1WphhQDGrvx5XAPbQ==} @@ -1968,9 +2037,14 @@ packages: typescript: optional: true - '@prisma-next/migration-tools@0.11.0': - resolution: {integrity: sha512-QU9QGA1EbNMsi9LC6EMPeeBc9KBI0V3x4ptBoLPuocI6QG7d7fQlmu7qXS2qQUO6DmqMiDptK5InRKLB3uki8A==} - engines: {node: '>=20'} + '@prisma-next/migration-tools@0.12.0': + resolution: {integrity: sha512-k9FlvZbcvwWVqcN9/isFnD6RoIesvDxXI0aLvFOGejZb5iAS+7nh+zZUNx0e7cf0XP48g3IZVw+1qsFPU3pheg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/migration-tools@0.14.0': resolution: {integrity: sha512-JdFJPO8qFA3N0lP6g/ZsRz4QGeDV/qQpRUWq+9phoAbL/kBaMha838JG5YUb6h8z3tD4/qg+Qs6ESZPURD4AcQ==} @@ -1981,9 +2055,14 @@ packages: typescript: optional: true - '@prisma-next/operations@0.11.0': - resolution: {integrity: sha512-viAM+bmj9dVWdeO3R24TXorKC3v+6SyKyIQjQKmTSe4x1iwj6TjODxHbd6y6VUum/pph5Q79y3Flji+5vPDIcw==} - engines: {node: '>=20'} + '@prisma-next/operations@0.12.0': + resolution: {integrity: sha512-ARJtBZ6YrHscg27dnX19soeJs97NsKyk/A13J4DmUdMEPLMs/dYFxxt0MZ2OPyTIWW/ihp+sszGAlRa6+74tGA==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/operations@0.14.0': resolution: {integrity: sha512-h53frkeEYalkixESJvkCvvuELD5b3YnHdkqkBK1A6XyDoqFusEHpjlP4LMXhpAQlft6QdbgYZfttm9ZXBFBUQw==} @@ -2003,8 +2082,14 @@ packages: typescript: optional: true - '@prisma-next/psl-parser@0.11.0': - resolution: {integrity: sha512-fMNQJAYsdm+r9MAmdAGzMoGqIqzu9U5LIvXK4OuFNtUispJObhhxprrVvJVLL+sJREbgVhug95+Ahko6XngHxg==} + '@prisma-next/psl-parser@0.12.0': + resolution: {integrity: sha512-7w4nrcDQLvnvSx6RjrVsIsB1jXSR8TumzYZYRZ2/QNkVxdHPd5/VOysZWxv7rCLxdZ1G0PvoMmGYy2LWGnzBsg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/psl-parser@0.14.0': resolution: {integrity: sha512-XKumV4Bfy3KHoD3IsWVSDtzFjXKL5zENASbbfIXXpwll3AVgU4rtVKuJIH5fwUOYpcCUdLE8ZaNw6vcwLuse3w==} @@ -2015,8 +2100,14 @@ packages: typescript: optional: true - '@prisma-next/psl-printer@0.11.0': - resolution: {integrity: sha512-OP6ipK83ycC6OgtXkcaQgxJvvDPZZvrhO8w4ILhYQdYXdTjUZXTDa0+bJxr9p+KgM3FDYR0oxRKyBFEDNokf4w==} + '@prisma-next/psl-printer@0.12.0': + resolution: {integrity: sha512-qnL9vbe8Nhc49fCioBs4KoWOYRJbt09KJCl1g/KkU5de9MG0kkgHhZpMAdRHvZv7n20k24usXKs7RqPQ4XDymw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/psl-printer@0.14.0': resolution: {integrity: sha512-qVTjwpO9uvITSsMg/mtdk0Py7ITsQRpPjmidSi9ByoPE6OGBGF3/BaUbOjRiZ340i39Npg83jT4rLreH1GOFjg==} @@ -2036,8 +2127,14 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-emitter@0.11.0': - resolution: {integrity: sha512-39iWRUR2dZ9NIbv7QAOMUV9qK3HnGweFdTEXnutWJdsGvyDnkxt59FgJZbNwBy01xuIhmh3ZA+l7g+IxyYI/OA==} + '@prisma-next/sql-contract-emitter@0.12.0': + resolution: {integrity: sha512-sSVD3KysoxaqNOMahVCgcf3wSlluf9MKrh74TTtvPgb0UV/M2iW0XW56ZJGrtrrH6YDhGgysnoievwFCCL+Gvg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-contract-emitter@0.14.0': resolution: {integrity: sha512-gUgctyFNMKZ94wXNATevMi+FG9R9SK4yYQesYr7/5kZ4ZBbHkaFOFcWwlJZSI+EhCC2J1OgvfaCTtpWpn+VIUw==} @@ -2048,8 +2145,14 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-psl@0.11.0': - resolution: {integrity: sha512-fooOPbsnfEPJwxW+jt5fVEyOn6sT3Q6S5DtmxWwIqwkuJRZk8zAh/FoPq1daTiJT5u7XjJjysdztUzJ0uUK56g==} + '@prisma-next/sql-contract-psl@0.12.0': + resolution: {integrity: sha512-I760s2H3DNUf+u0hPh3eYCOqzcKTrZOOUJmwj6ROwqy7OoxExv4ffYeIW8sU9SqHhFxGm1HHiiJZ/v7LdXcpRg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-contract-psl@0.14.0': resolution: {integrity: sha512-86Z8MWo0NYhGSX4uOqbvAx46aMOaB2opNyUeFNnntT1br+U48IX2ZPGV62ul6mwCp7DGN7pjvmlBW5OnHXuiyg==} @@ -2060,8 +2163,14 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-ts@0.11.0': - resolution: {integrity: sha512-30YBhrj4A4COsOC6Ogz50ONU383sy7NZ/vP/Mb2iw/+6SlVKymg812gSw9+w8a668MzJnDzribJ/stXqPXdXsw==} + '@prisma-next/sql-contract-ts@0.12.0': + resolution: {integrity: sha512-FQEBLzQwlkmmx/lxSaM5yUpopt6vJVp2aAoe15hxybLRudHwtBwIz7JqKgkMzI2zN/Gquu9D07WDuo2G1h8//Q==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-contract-ts@0.14.0': resolution: {integrity: sha512-AMTURIMXCsn+M7oW0QOe/9wS7J+Q1siW7+aihgtmBj88lYav9XHyxJO5AJDu6LSDSctorTzccfsPatBAlcxzKQ==} @@ -2072,8 +2181,14 @@ packages: typescript: optional: true - '@prisma-next/sql-contract@0.11.0': - resolution: {integrity: sha512-rztm5R0nmumIp6IKPmmQPLFnyzXgq4geOna0/tMRNqNOxvzvtpmVusFOq049Q39Bc05Y9l9NbJ5nq2E0PArWYA==} + '@prisma-next/sql-contract@0.12.0': + resolution: {integrity: sha512-lGkowImeBFLKMry8FNUY2roZIN3eNnQsjt9rbQvzaiwuIM5XbURkmjX+rLb+MHAOrcCJaJVxPyZoJfFCeB7s3w==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-contract@0.14.0': resolution: {integrity: sha512-/Ibvjd41sEgpi4c3YCZvK6vRCucNffsIZ+isIOYoVqXOt74H0llM4Ux4mR0PShNuo0jgG8xOon1ptSQ6/S7c+w==} @@ -2084,9 +2199,14 @@ packages: typescript: optional: true - '@prisma-next/sql-errors@0.11.0': - resolution: {integrity: sha512-FCr89Hofj8dBQ40zaQ+dzCqkrJSQIf7DM6HOm9KdwOjNke7fJLCAwsRGmiaet61MWEYikQjBletRAahyKli+9w==} - engines: {node: '>=20'} + '@prisma-next/sql-errors@0.12.0': + resolution: {integrity: sha512-g83tk8EYvmU8RWvzIR+G/7LsvO1GwXUGo4jVe4G908GEIdPtsWT48WjiaB5CkNiuEVGegZ/FgFeNWGcBklTBRw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-errors@0.14.0': resolution: {integrity: sha512-zc5U+SzEUqpjPC69w9JejqAkcRtkZ8BSm1a6l6p4pT4b8dk/iXrfQ96oHoy6SYG/dTbGe47W4gCfX9LCzCdUlA==} @@ -2097,9 +2217,14 @@ packages: typescript: optional: true - '@prisma-next/sql-operations@0.11.0': - resolution: {integrity: sha512-ktjE5aLXGGiFjtFXPAQyo9S8+9FilintKGgEy+EJxB7sHKqRezUhT41v329EyZY5WzapCgq2LtCRpCtFBuoeLA==} - engines: {node: '>=20'} + '@prisma-next/sql-operations@0.12.0': + resolution: {integrity: sha512-HdsT2URngE3rJ1RA+WZUzaYsp0plwXdHzt40cKRgLhVon25yVWsYJDxUdAIQtLSl0+Ce49bKMYr5LsdYrDmU8g==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-operations@0.14.0': resolution: {integrity: sha512-0VeVceyh7LAlEGMseFPQrmoBPkNAlBiCOCyWzv1xQjjU0IoPkMlcjFu07wD5aLf+nwBzEUSbdg9EaTdBTYHO9Q==} @@ -2119,8 +2244,14 @@ packages: typescript: optional: true - '@prisma-next/sql-relational-core@0.11.0': - resolution: {integrity: sha512-D8LRwBWXZgYGcbMp85LQcecv4KO5ADeg/vgvXXiM1+kisYYrC9lbIEKCUIhmGPWqQ27N335OckqoFceLpoDf2w==} + '@prisma-next/sql-relational-core@0.12.0': + resolution: {integrity: sha512-MKkOuLyAKvzubwUPvLpcm/VGokuFYfHb8cllSD3xFR8gdeUksG4xib+vLswxSeE+UM3KcmhXPGHsm5KxpgCYvQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-relational-core@0.14.0': resolution: {integrity: sha512-7H1HaN31lmGD81C9vz07dq/r5n80yKXfFNpYj5XrKKeVpDZQSb/IXtOVt7LyB+q9QqSUW2jD5RGLuTOhsd2kbw==} @@ -2131,8 +2262,14 @@ packages: typescript: optional: true - '@prisma-next/sql-runtime@0.11.0': - resolution: {integrity: sha512-UFwHj69jM0XDlB0YVuXOp+7PayYI9xTGG2l/ecE3aUg4pz79rtKpZ6JqL7IatiISaId6KsRGWtG1/YobwX+XzQ==} + '@prisma-next/sql-runtime@0.12.0': + resolution: {integrity: sha512-YPb4Ntw53WhSyIy6FIaP0HbL2SCaQc/jgTRANKqlckGa+vKVeHIScehT5j6B49nL+6SsshuKEE5+YvyT0roX6Q==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-runtime@0.14.0': resolution: {integrity: sha512-HV8ZAvNuXtFrAHuxs3/ZEz0JapKFayFYDljQeVDFxExSEj79fPtd9CON9sTTV4T/RKII5knlKJRuKtwWH35Znw==} @@ -2143,9 +2280,14 @@ packages: typescript: optional: true - '@prisma-next/sql-schema-ir@0.11.0': - resolution: {integrity: sha512-EcNDZ21o2z3pCe4nSedYjhSAXcSBQ5eK6K33IkGoL+C0Tns62Ks0Sj6oBHHdlu7iWrzdiEs9kMoZ0moh5h9wEg==} - engines: {node: '>=20'} + '@prisma-next/sql-schema-ir@0.12.0': + resolution: {integrity: sha512-/gvvkx1PHb52gxR4ePRtW/UQVWdDjGaN0liUBKZU8qMdY7UBUuf/UI9HtNIzO0TERWIWYZKCHuh9u9rTMz10XQ==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/sql-schema-ir@0.14.0': resolution: {integrity: sha512-5buE3jcTz3yyyve4demaACIv52YT09tFPCBEqwmxG1ss8KtybVEgaZg8UUYGmyYbWU0l5RpQ96/vuB5UTFE4nw==} @@ -2156,8 +2298,14 @@ packages: typescript: optional: true - '@prisma-next/target-postgres@0.11.0': - resolution: {integrity: sha512-ZILsX/yJ99WCUZPi9Dlj3Ww2uvdrFF0BQ6f8pSKOR130WTSyniGTKY176/1zG4aNnZ+dCOqkjL/1eaZ+2/zZ5Q==} + '@prisma-next/target-postgres@0.12.0': + resolution: {integrity: sha512-cozTQwhU+ky3JFby5/Q2kDscEX/Zfi3LnJ9Ol7QLTB+nvMErfzykoqLhZZdiPNxY1z0DktNohWJsXpj0oQJhFw==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/target-postgres@0.14.0': resolution: {integrity: sha512-i1H9Jzt7ZGgYQ61rlmalyEPY1RoLFIMk9+qE5WbETjQ8LY9p7iXeTx4bBv25twkYIAHxXtrYxsQ3klUWg0JfVg==} @@ -2168,9 +2316,14 @@ packages: typescript: optional: true - '@prisma-next/ts-render@0.11.0': - resolution: {integrity: sha512-OX//hWyh9KMd2440KjA0R4vkDCZIQbzpgdirEeHnNxOuQ4QKgHCFSjg/3sqIeSRCBG0WnzAtrVBpsFV1qSHBsg==} - engines: {node: '>=20'} + '@prisma-next/ts-render@0.12.0': + resolution: {integrity: sha512-rEN8aylBGSI6lCDQLSyIEpXLnJmTeS/vp48EdsMc8ZIUD0FrwDLUxsqkOBjQmCtmBmSw2qjIkXokjsmvM+/k9A==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/ts-render@0.14.0': resolution: {integrity: sha512-ub25pEfUe2dLpNu/wR0mlBBMZ87eGmyV5D9GS5KmyHkofjJdiE2Y3BpIhHXf7wjiYp35z/gOyQ2vuPFS4xVBgQ==} @@ -2181,9 +2334,14 @@ packages: typescript: optional: true - '@prisma-next/utils@0.11.0': - resolution: {integrity: sha512-QUA2q5Qw0tFqXea5bK3M0jGVFpFpQ3kgAW51RhENkiShnIyWh0Jjn2TKBrvWBXCzvfF1kJI20/Hr7X8xC9p+3g==} - engines: {node: '>=20'} + '@prisma-next/utils@0.12.0': + resolution: {integrity: sha512-Bdgoqc5VSSKRPl4uWJvXwUkpQHvVUj9ZCLav82DDypKITmt4hRZ/o7+YzuwACGhOcbNaBtFNOPaasPFXtNyipg==} + engines: {node: '>=24'} + peerDependencies: + typescript: '>=5.9' + peerDependenciesMeta: + typescript: + optional: true '@prisma-next/utils@0.14.0': resolution: {integrity: sha512-YIV6FMdKKsJqLkG2h9tQDzEoeAFjtniNXcB0XnyZyD1tkALodiWsRymuQJBGcFZB7S8icrOvRaqdSUNdrrR6yw==} @@ -5179,24 +5337,26 @@ snapshots: '@posthog/types@1.373.5': {} - '@prisma-next/adapter-postgres@0.11.0(typanion@3.14.0)': - dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/contract-authoring': 0.11.0 - '@prisma-next/errors': 0.11.0 - '@prisma-next/family-sql': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/ids': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/sql-contract-psl': 0.11.0 - '@prisma-next/sql-contract-ts': 0.11.0 - '@prisma-next/sql-operations': 0.11.0 - '@prisma-next/sql-relational-core': 0.11.0 - '@prisma-next/sql-runtime': 0.11.0 - '@prisma-next/sql-schema-ir': 0.11.0 - '@prisma-next/target-postgres': 0.11.0(typanion@3.14.0) - '@prisma-next/utils': 0.11.0 + '@prisma-next/adapter-postgres@0.12.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/contract-authoring': 0.12.0(typescript@5.9.3) + '@prisma-next/errors': 0.12.0(typescript@5.9.3) + '@prisma-next/family-sql': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/ids': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract-psl': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.12.0(typescript@5.9.3) + '@prisma-next/target-postgres': 0.12.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - magicast - typanion @@ -5225,13 +5385,15 @@ snapshots: - magicast - typanion - '@prisma-next/cli-telemetry@0.11.0': + '@prisma-next/cli-telemetry@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/config': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/config': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 c12: 3.3.4 pathe: 2.0.3 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - magicast @@ -5247,19 +5409,19 @@ snapshots: transitivePeerDependencies: - magicast - '@prisma-next/cli@0.11.0(typanion@3.14.0)': + '@prisma-next/cli@0.12.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@clack/prompts': 1.4.0 '@dagrejs/dagre': 3.0.0 - '@prisma-next/cli-telemetry': 0.11.0 - '@prisma-next/config': 0.11.0 - '@prisma-next/contract': 0.11.0 - '@prisma-next/emitter': 0.11.0 - '@prisma-next/errors': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/migration-tools': 0.11.0 - '@prisma-next/psl-printer': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/cli-telemetry': 0.12.0(typescript@5.9.3) + '@prisma-next/config': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/emitter': 0.12.0(typescript@5.9.3) + '@prisma-next/errors': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.12.0(typescript@5.9.3) + '@prisma-next/psl-printer': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 c12: 3.3.4 ci-info: 4.4.0 @@ -5274,6 +5436,8 @@ snapshots: string-width: 8.2.1 strip-ansi: 7.2.0 wrap-ansi: 10.0.0 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - magicast - typanion @@ -5310,12 +5474,14 @@ snapshots: - magicast - typanion - '@prisma-next/config@0.11.0': + '@prisma-next/config@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/config@0.14.0(typescript@5.9.3)': dependencies: @@ -5326,9 +5492,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract-authoring@0.11.0': + '@prisma-next/contract-authoring@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.11.0 + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 '@prisma-next/contract-authoring@0.14.0(typescript@5.9.3)': dependencies: @@ -5336,11 +5504,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract@0.11.0': + '@prisma-next/contract@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/utils': 0.11.0 + '@prisma-next/utils': 0.12.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/contract@0.14.0(typescript@5.9.3)': dependencies: @@ -5350,19 +5520,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/driver-postgres@0.11.0': + '@prisma-next/driver-postgres@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/errors': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/sql-errors': 0.11.0 - '@prisma-next/sql-operations': 0.11.0 - '@prisma-next/sql-relational-core': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/errors': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-errors': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 pg: 8.20.0 pg-cursor: 2.21.0(pg@8.20.0) + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - pg-native @@ -5384,14 +5556,17 @@ snapshots: transitivePeerDependencies: - pg-native - '@prisma-next/emitter@0.11.0': + '@prisma-next/emitter@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/operations': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/operations': 0.12.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 prettier: 3.8.3 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/emitter@0.14.0(typescript@5.9.3)': dependencies: @@ -5405,10 +5580,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/errors@0.11.0': + '@prisma-next/errors@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 '@prisma-next/errors@0.14.0(typescript@5.9.3)': dependencies: @@ -5421,22 +5598,24 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/family-sql@0.11.0': - dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/emitter': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/migration-tools': 0.11.0 - '@prisma-next/operations': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/sql-contract-emitter': 0.11.0 - '@prisma-next/sql-contract-ts': 0.11.0 - '@prisma-next/sql-operations': 0.11.0 - '@prisma-next/sql-relational-core': 0.11.0 - '@prisma-next/sql-runtime': 0.11.0 - '@prisma-next/sql-schema-ir': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/family-sql@0.12.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/emitter': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.12.0(typescript@5.9.3) + '@prisma-next/operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract-emitter': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/family-sql@0.14.0(typescript@5.9.3)': dependencies: @@ -5457,14 +5636,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/framework-components@0.11.0': + '@prisma-next/framework-components@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/operations': 0.11.0 - '@prisma-next/ts-render': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/operations': 0.12.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/framework-components@0.14.0(typescript@5.9.3)': dependencies: @@ -5477,12 +5658,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/ids@0.11.0': + '@prisma-next/ids@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) uniku: 0.0.12 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/ids@0.14.0(typescript@5.9.3)': dependencies: @@ -5493,14 +5676,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/migration-tools@0.11.0': + '@prisma-next/migration-tools@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 pathe: 2.0.3 prettier: 3.8.3 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/migration-tools@0.14.0(typescript@5.9.3)': dependencies: @@ -5513,7 +5698,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/operations@0.11.0': {} + '@prisma-next/operations@0.12.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 '@prisma-next/operations@0.14.0(typescript@5.9.3)': optionalDependencies: @@ -5546,10 +5733,12 @@ snapshots: - pg-native - typanion - '@prisma-next/psl-parser@0.11.0': + '@prisma-next/psl-parser@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 '@prisma-next/psl-parser@0.14.0(typescript@5.9.3)': dependencies: @@ -5558,9 +5747,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-printer@0.11.0': + '@prisma-next/psl-printer@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.11.0 + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 '@prisma-next/psl-printer@0.14.0(typescript@5.9.3)': dependencies: @@ -5579,13 +5770,15 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-emitter@0.11.0': + '@prisma-next/sql-contract-emitter@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/emitter': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/emitter': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-contract-emitter@0.14.0(typescript@5.9.3)': dependencies: @@ -5597,16 +5790,18 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-psl@0.11.0': + '@prisma-next/sql-contract-psl@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/config': 0.11.0 - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/psl-parser': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/sql-contract-ts': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/config': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/psl-parser': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) pathe: 2.0.3 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-contract-psl@0.14.0(typescript@5.9.3)': dependencies: @@ -5621,17 +5816,19 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-ts@0.11.0': + '@prisma-next/sql-contract-ts@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/config': 0.11.0 - '@prisma-next/contract': 0.11.0 - '@prisma-next/contract-authoring': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/config': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/contract-authoring': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 pathe: 2.0.3 ts-toolbelt: 9.6.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-contract-ts@0.14.0(typescript@5.9.3)': dependencies: @@ -5647,11 +5844,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract@0.11.0': + '@prisma-next/sql-contract@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-contract@0.14.0(typescript@5.9.3)': dependencies: @@ -5662,17 +5862,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-errors@0.11.0': {} + '@prisma-next/sql-errors@0.12.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-errors@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-operations@0.11.0': + '@prisma-next/sql-operations@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/operations': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 + '@prisma-next/operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-operations@0.14.0(typescript@5.9.3)': dependencies: @@ -5696,17 +5900,19 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-relational-core@0.11.0': + '@prisma-next/sql-relational-core@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/operations': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/sql-operations': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 ts-toolbelt: 9.6.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-relational-core@0.14.0(typescript@5.9.3)': dependencies: @@ -5722,17 +5928,19 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-runtime@0.11.0': + '@prisma-next/sql-runtime@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/ids': 0.11.0 - '@prisma-next/operations': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/sql-operations': 0.11.0 - '@prisma-next/sql-relational-core': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/ids': 0.12.0(typescript@5.9.3) + '@prisma-next/operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) arktype: 2.2.0 + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-runtime@0.14.0(typescript@5.9.3)': dependencies: @@ -5748,10 +5956,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-schema-ir@0.11.0': + '@prisma-next/sql-schema-ir@0.12.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.11.0 - '@prisma-next/framework-components': 0.11.0 + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 '@prisma-next/sql-schema-ir@0.14.0(typescript@5.9.3)': dependencies: @@ -5760,24 +5970,26 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/target-postgres@0.11.0(typanion@3.14.0)': - dependencies: - '@prisma-next/cli': 0.11.0(typanion@3.14.0) - '@prisma-next/contract': 0.11.0 - '@prisma-next/errors': 0.11.0 - '@prisma-next/family-sql': 0.11.0 - '@prisma-next/framework-components': 0.11.0 - '@prisma-next/migration-tools': 0.11.0 - '@prisma-next/sql-contract': 0.11.0 - '@prisma-next/sql-errors': 0.11.0 - '@prisma-next/sql-operations': 0.11.0 - '@prisma-next/sql-relational-core': 0.11.0 - '@prisma-next/sql-schema-ir': 0.11.0 - '@prisma-next/ts-render': 0.11.0 - '@prisma-next/utils': 0.11.0 + '@prisma-next/target-postgres@0.12.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@prisma-next/cli': 0.12.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/contract': 0.12.0(typescript@5.9.3) + '@prisma-next/errors': 0.12.0(typescript@5.9.3) + '@prisma-next/family-sql': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-errors': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.12.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.12.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 pathe: 2.0.3 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - magicast - typanion @@ -5806,13 +6018,17 @@ snapshots: - magicast - typanion - '@prisma-next/ts-render@0.11.0': {} + '@prisma-next/ts-render@0.12.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 '@prisma-next/ts-render@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/utils@0.11.0': {} + '@prisma-next/utils@0.12.0(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 '@prisma-next/utils@0.14.0(typescript@5.9.3)': optionalDependencies: From 982f75c86fa761ba8de7c9b415c768ec12acbb41 Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:12:07 -0600 Subject: [PATCH 07/15] chore: upgrade @prisma-next/* to 0.13.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-transition changes (extension-author upgrade instructions 0.12→0.13): - storage-namespace-envelope-re-emit: re-emitted the contract space — storage entries move under storage.namespaces..entries. — and re-pinned the baseline migration (end-contract.{json,d.ts}, migration.ts to-hash, self-emitted ops.json/migration.json, refs/head.json). The 0.13 emitter also injects an empty __unbound__ namespace slot alongside public; 0.14 removes it (TML-2916). - sqlite-create-table-method, strip-empty-type-params, and codec-ref-resolver namespaceId threading: no-ops for this package (no SQLite migrations, no empty typeParams, no direct codec-ref construction) Additional 0.13 SPI churn not covered by the instructions: - SqlNamespaceTablesInput moved tables into the entries envelope: buildSqlNamespace({ id, entries: { table } }); contract fixtures and namespace reads updated - TargetPackRef gained required defaultNamespaceId ('public' for the Postgres stub in the PSL interpreter tests) - interpretPslDocumentToSqlContract requires composedExtensionContracts; tests pass the cipherstash contract keyed by its space id Co-Authored-By: Claude Fable 5 --- .../end-contract.d.ts | 58 +- .../end-contract.json | 68 ++- .../migration.json | 4 +- .../migration.ts | 2 +- .../prisma-next/migrations/refs/head.json | 2 +- packages/prisma-next/package.json | 36 +- packages/prisma-next/src/contract.d.ts | 58 +- packages/prisma-next/src/contract.json | 68 ++- .../test/cipherstash-codec.test.ts | 2 +- packages/prisma-next/test/descriptor.test.ts | 10 +- packages/prisma-next/test/helpers.test.ts | 88 +-- .../test/operator-lowering.helpers.ts | 88 +-- .../test/psl-interpretation-numeric.test.ts | 15 +- .../psl-interpretation-other-types.test.ts | 15 +- .../test/psl-interpretation.test.ts | 15 +- pnpm-lock.yaml | 516 +++++++++--------- 16 files changed, 558 insertions(+), 487 deletions(-) diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts index 7e9b1072f..b1997c23a 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts @@ -23,7 +23,7 @@ import type { } from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, - ContractModelsMap, + ContractModelDefinitions, ExecutionHashBase, NamespaceId, ProfileHashBase, @@ -31,7 +31,7 @@ import type { } from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2'>; + StorageHashBase<'sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; @@ -68,32 +68,39 @@ type ContractBase = Omit< ContractType< { readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly entries: { readonly table: {} }; + }; readonly public: { readonly id: 'public'; readonly kind: 'sql-namespace'; - readonly tables: { - readonly eql_v2_configuration: { - columns: { - readonly id: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly state: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly data: { - readonly nativeType: 'jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: false; + readonly entries: { + readonly table: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; + }; }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; }; }; }; @@ -119,6 +126,7 @@ type ContractBase = Omit< readonly relations: Record; readonly storage: { readonly table: 'eql_v2_configuration'; + readonly namespaceId: 'public'; readonly fields: { readonly id: { readonly column: 'id' }; readonly state: { readonly column: 'state' }; @@ -160,6 +168,7 @@ type ContractBase = Omit< readonly relations: Record; readonly storage: { readonly table: 'eql_v2_configuration'; + readonly namespaceId: 'public'; readonly fields: { readonly id: { readonly column: 'id' }; readonly state: { readonly column: 'state' }; @@ -193,7 +202,8 @@ type ContractBase = Omit< readonly profileHash: ProfileHash; }; +export type Models = ContractModelDefinitions; + export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; -export type Models = ContractModelsMap; diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json index ec5e42fdd..f38b207be 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json @@ -50,6 +50,7 @@ "column": "state" } }, + "namespaceId": "public", "table": "eql_v2_configuration" } } @@ -59,41 +60,50 @@ }, "storage": { "namespaces": { + "__unbound__": { + "entries": { + "table": {} + }, + "id": "__unbound__", + "kind": "postgres-unbound-schema" + }, "public": { - "id": "public", - "kind": "postgres-schema", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false + "entries": { + "table": { + "eql_v2_configuration": { + "columns": { + "data": { + "codecId": "pg/jsonb@1", + "nativeType": "jsonb", + "nullable": false + }, + "id": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "state": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + } }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": [ - "id" - ] - }, - "uniques": [] + "uniques": [] + } } - } + }, + "id": "public", + "kind": "postgres-schema" } }, - "storageHash": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2" + "storageHash": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0" }, "capabilities": { "postgres": { diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json index 1115c219e..058115bf1 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json @@ -1,9 +1,9 @@ { "from": null, - "to": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2", + "to": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0", "providedInvariants": [ "cipherstash:install-eql-bundle-v1" ], "createdAt": "2026-05-09T03:42:56.902Z", - "migrationHash": "sha256:2faa77ff326090cecfc1babbeca884201f9d509c87641cd77b1c08afcd5451e1" + "migrationHash": "sha256:f4fb2bcb8619643b7c1cf7d9c81c156bc589bef99214c95a4d73127d5276d007" } \ No newline at end of file diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts index 5879c7a5e..d6a1f4ed7 100755 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts @@ -36,7 +36,7 @@ export default class M extends Migration { override describe() { return { from: null, - to: 'sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2', + to: 'sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0', } } diff --git a/packages/prisma-next/migrations/refs/head.json b/packages/prisma-next/migrations/refs/head.json index 7fb48ef3f..b53e474ff 100644 --- a/packages/prisma-next/migrations/refs/head.json +++ b/packages/prisma-next/migrations/refs/head.json @@ -1,4 +1,4 @@ { - "hash": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2", + "hash": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0", "invariants": ["cipherstash:install-eql-bundle-v1"] } diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json index 32cecbbd4..a4891d300 100644 --- a/packages/prisma-next/package.json +++ b/packages/prisma-next/package.json @@ -80,28 +80,28 @@ }, "dependencies": { "@cipherstash/stack": "workspace:*", - "@prisma-next/contract": "0.12.0", - "@prisma-next/family-sql": "0.12.0", - "@prisma-next/framework-components": "0.12.0", - "@prisma-next/migration-tools": "0.12.0", - "@prisma-next/sql-contract": "0.12.0", - "@prisma-next/sql-operations": "0.12.0", - "@prisma-next/sql-relational-core": "0.12.0", - "@prisma-next/sql-runtime": "0.12.0", - "@prisma-next/ts-render": "0.12.0", - "@prisma-next/utils": "0.12.0", + "@prisma-next/contract": "0.13.0", + "@prisma-next/family-sql": "0.13.0", + "@prisma-next/framework-components": "0.13.0", + "@prisma-next/migration-tools": "0.13.0", + "@prisma-next/sql-contract": "0.13.0", + "@prisma-next/sql-operations": "0.13.0", + "@prisma-next/sql-relational-core": "0.13.0", + "@prisma-next/sql-runtime": "0.13.0", + "@prisma-next/ts-render": "0.13.0", + "@prisma-next/utils": "0.13.0", "arktype": "^2.1.29" }, "devDependencies": { - "@prisma-next/adapter-postgres": "0.12.0", - "@prisma-next/cli": "0.12.0", - "@prisma-next/driver-postgres": "0.12.0", + "@prisma-next/adapter-postgres": "0.13.0", + "@prisma-next/cli": "0.13.0", + "@prisma-next/driver-postgres": "0.13.0", "@prisma-next/extension-author-tools": "0.14.0", - "@prisma-next/psl-parser": "0.12.0", - "@prisma-next/sql-contract-psl": "0.12.0", - "@prisma-next/sql-contract-ts": "0.12.0", - "@prisma-next/sql-schema-ir": "0.12.0", - "@prisma-next/target-postgres": "0.12.0", + "@prisma-next/psl-parser": "0.13.0", + "@prisma-next/sql-contract-psl": "0.13.0", + "@prisma-next/sql-contract-ts": "0.13.0", + "@prisma-next/sql-schema-ir": "0.13.0", + "@prisma-next/target-postgres": "0.13.0", "pathe": "^2.0.3", "tsup": "catalog:repo", "typescript": "catalog:repo", diff --git a/packages/prisma-next/src/contract.d.ts b/packages/prisma-next/src/contract.d.ts index 7e9b1072f..b1997c23a 100644 --- a/packages/prisma-next/src/contract.d.ts +++ b/packages/prisma-next/src/contract.d.ts @@ -23,7 +23,7 @@ import type { } from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, - ContractModelsMap, + ContractModelDefinitions, ExecutionHashBase, NamespaceId, ProfileHashBase, @@ -31,7 +31,7 @@ import type { } from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2'>; + StorageHashBase<'sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; @@ -68,32 +68,39 @@ type ContractBase = Omit< ContractType< { readonly namespaces: { + readonly __unbound__: { + readonly id: '__unbound__'; + readonly kind: 'sql-namespace'; + readonly entries: { readonly table: {} }; + }; readonly public: { readonly id: 'public'; readonly kind: 'sql-namespace'; - readonly tables: { - readonly eql_v2_configuration: { - columns: { - readonly id: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly state: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly data: { - readonly nativeType: 'jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: false; + readonly entries: { + readonly table: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; + }; }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; }; }; }; @@ -119,6 +126,7 @@ type ContractBase = Omit< readonly relations: Record; readonly storage: { readonly table: 'eql_v2_configuration'; + readonly namespaceId: 'public'; readonly fields: { readonly id: { readonly column: 'id' }; readonly state: { readonly column: 'state' }; @@ -160,6 +168,7 @@ type ContractBase = Omit< readonly relations: Record; readonly storage: { readonly table: 'eql_v2_configuration'; + readonly namespaceId: 'public'; readonly fields: { readonly id: { readonly column: 'id' }; readonly state: { readonly column: 'state' }; @@ -193,7 +202,8 @@ type ContractBase = Omit< readonly profileHash: ProfileHash; }; +export type Models = ContractModelDefinitions; + export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; -export type Models = ContractModelsMap; diff --git a/packages/prisma-next/src/contract.json b/packages/prisma-next/src/contract.json index ec5e42fdd..f38b207be 100644 --- a/packages/prisma-next/src/contract.json +++ b/packages/prisma-next/src/contract.json @@ -50,6 +50,7 @@ "column": "state" } }, + "namespaceId": "public", "table": "eql_v2_configuration" } } @@ -59,41 +60,50 @@ }, "storage": { "namespaces": { + "__unbound__": { + "entries": { + "table": {} + }, + "id": "__unbound__", + "kind": "postgres-unbound-schema" + }, "public": { - "id": "public", - "kind": "postgres-schema", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false + "entries": { + "table": { + "eql_v2_configuration": { + "columns": { + "data": { + "codecId": "pg/jsonb@1", + "nativeType": "jsonb", + "nullable": false + }, + "id": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "state": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + } }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": [ - "id" - ] - }, - "uniques": [] + "uniques": [] + } } - } + }, + "id": "public", + "kind": "postgres-schema" } }, - "storageHash": "sha256:3c0ac95bd99fd4105d62aab17cbc79e81c75ee6bc46435b6c86ce65e5e39fcd2" + "storageHash": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0" }, "capabilities": { "postgres": { diff --git a/packages/prisma-next/test/cipherstash-codec.test.ts b/packages/prisma-next/test/cipherstash-codec.test.ts index 8a9e611f7..020e0ba67 100644 --- a/packages/prisma-next/test/cipherstash-codec.test.ts +++ b/packages/prisma-next/test/cipherstash-codec.test.ts @@ -124,7 +124,7 @@ describe('planFieldEventOperations driving the cipherstash hook', () => { storage: new SqlStorage({ storageHash: 'sha256:test' as StorageHashBase, namespaces: { - __unbound__: buildSqlNamespace({ id: '__unbound__', tables }), + __unbound__: buildSqlNamespace({ id: '__unbound__', entries: { table: tables } }), }, }), domain: { namespaces: { __unbound__: { models: {} } } }, diff --git a/packages/prisma-next/test/descriptor.test.ts b/packages/prisma-next/test/descriptor.test.ts index 66afb4681..6956128cd 100644 --- a/packages/prisma-next/test/descriptor.test.ts +++ b/packages/prisma-next/test/descriptor.test.ts @@ -47,14 +47,16 @@ describe('cipherstash extension descriptor (contract-space package layout)', () it('exposes a contractSpace declaring the eql_v2_configuration table', () => { const space = cipherstashExtensionDescriptor.contractSpace expect(space).toBeDefined() - // Since 0.10 the storage IR is namespace-enveloped; the extension's - // sole table lives in the late-bound `__unbound__` namespace. + // Since 0.10 the storage IR is namespace-enveloped (tables under + // `storage.namespaces..entries.table` since 0.13); the + // extension's sole table lives in the target-owned default + // namespace (`public`). const namespaces = space!.contractJson.storage.namespaces as Record< string, - { tables?: Record } + { entries?: { table?: Record } } > const tables = Object.values(namespaces).flatMap((ns) => - Object.keys(ns.tables ?? {}), + Object.keys(ns.entries?.table ?? {}), ) expect(tables).toEqual([EQL_V2_CONFIGURATION_TABLE]) }) diff --git a/packages/prisma-next/test/helpers.test.ts b/packages/prisma-next/test/helpers.test.ts index 374927165..0ca5aa527 100644 --- a/packages/prisma-next/test/helpers.test.ts +++ b/packages/prisma-next/test/helpers.test.ts @@ -83,49 +83,55 @@ const contract = validateSqlContractFully({ namespaces: { __unbound__: { id: '__unbound__', - tables: { - [TABLE]: { - columns: { - id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false }, - email: { - codecId: CIPHERSTASH_STRING_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - score: { - codecId: CIPHERSTASH_DOUBLE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - amount: { - codecId: CIPHERSTASH_BIGINT_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - birthday: { - codecId: CIPHERSTASH_DATE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - enabled: { - codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - payload: { - codecId: CIPHERSTASH_JSON_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - plain: { - codecId: 'pg/text@1', - nativeType: 'text', - nullable: false, + entries: { + table: { + [TABLE]: { + columns: { + id: { + codecId: 'pg/text@1', + nativeType: 'text', + nullable: false, + }, + email: { + codecId: CIPHERSTASH_STRING_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + score: { + codecId: CIPHERSTASH_DOUBLE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + amount: { + codecId: CIPHERSTASH_BIGINT_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + birthday: { + codecId: CIPHERSTASH_DATE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + enabled: { + codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + payload: { + codecId: CIPHERSTASH_JSON_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + plain: { + codecId: 'pg/text@1', + nativeType: 'text', + nullable: false, + }, }, + uniques: [], + indexes: [], + foreignKeys: [], }, - uniques: [], - indexes: [], - foreignKeys: [], }, }, }, diff --git a/packages/prisma-next/test/operator-lowering.helpers.ts b/packages/prisma-next/test/operator-lowering.helpers.ts index 745ecbbdc..faa68f762 100644 --- a/packages/prisma-next/test/operator-lowering.helpers.ts +++ b/packages/prisma-next/test/operator-lowering.helpers.ts @@ -80,49 +80,55 @@ export const contract = validateSqlContractFully({ namespaces: { __unbound__: { id: '__unbound__', - tables: { - [TABLE]: { - columns: { - id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false }, - [COLUMN]: { - codecId: CIPHERSTASH_STRING_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - // Per-codec columns so the trait-dispatched operators - // can be exercised against each column type (the - // postgres renderer reads `nativeType` from the codec - // descriptor at lower time; the column is what gives - // the renderer the codec id to look up). - score: { - codecId: CIPHERSTASH_DOUBLE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - amount: { - codecId: CIPHERSTASH_BIGINT_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - birthday: { - codecId: CIPHERSTASH_DATE_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - enabled: { - codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, - }, - payload: { - codecId: CIPHERSTASH_JSON_CODEC_ID, - nativeType: EQL_V2_ENCRYPTED_TYPE, - nullable: true, + entries: { + table: { + [TABLE]: { + columns: { + id: { + codecId: 'pg/text@1', + nativeType: 'text', + nullable: false, + }, + [COLUMN]: { + codecId: CIPHERSTASH_STRING_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + // Per-codec columns so the trait-dispatched operators + // can be exercised against each column type (the + // postgres renderer reads `nativeType` from the codec + // descriptor at lower time; the column is what gives + // the renderer the codec id to look up). + score: { + codecId: CIPHERSTASH_DOUBLE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + amount: { + codecId: CIPHERSTASH_BIGINT_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + birthday: { + codecId: CIPHERSTASH_DATE_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + enabled: { + codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, + payload: { + codecId: CIPHERSTASH_JSON_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + nullable: true, + }, }, + uniques: [], + indexes: [], + foreignKeys: [], }, - uniques: [], - indexes: [], - foreignKeys: [], }, }, }, diff --git a/packages/prisma-next/test/psl-interpretation-numeric.test.ts b/packages/prisma-next/test/psl-interpretation-numeric.test.ts index 47b018b41..8769f97ca 100644 --- a/packages/prisma-next/test/psl-interpretation-numeric.test.ts +++ b/packages/prisma-next/test/psl-interpretation-numeric.test.ts @@ -11,6 +11,7 @@ * byte-for-byte (PSL/TS parity). */ +import type { Contract } from '@prisma-next/contract/types' import { parsePslDocument } from '@prisma-next/psl-parser' import { interpretPslDocumentToSqlContract } from '@prisma-next/sql-contract-psl' import { describe, expect, it } from 'vitest' @@ -21,6 +22,7 @@ const postgresTarget = { kind: 'target' as const, familyId: 'sql' as const, targetId: 'postgres' as const, + defaultNamespaceId: 'public', id: 'postgres', version: '0.0.1', capabilities: {}, @@ -38,6 +40,12 @@ function interpret(schema: string) { target: postgresTarget, scalarTypeDescriptors: postgresScalarTypeDescriptors, composedExtensionPacks: [cipherstashControl.id], + composedExtensionContracts: new Map([ + [ + cipherstashControl.id, + cipherstashControl.contractSpace!.contractJson as unknown as Contract, + ], + ]), authoringContributions: { type: cipherstashPack.authoring.type, field: {} }, }) } @@ -60,12 +68,15 @@ type StorageView = { // `public` for Postgres since 0.12), while `types` stays at the root. const asStorage = (storage: unknown): StorageView => { const s = storage as { - readonly namespaces?: Record + readonly namespaces?: Record< + string, + { entries?: { table?: StorageView['tables'] } } + > readonly types?: StorageView['types'] } const tables: StorageView['tables'] = {} for (const ns of Object.values(s.namespaces ?? {})) { - Object.assign(tables, ns.tables) + Object.assign(tables, ns.entries?.table) } return { tables, diff --git a/packages/prisma-next/test/psl-interpretation-other-types.test.ts b/packages/prisma-next/test/psl-interpretation-other-types.test.ts index 2beda1ec1..f1a7ab212 100644 --- a/packages/prisma-next/test/psl-interpretation-other-types.test.ts +++ b/packages/prisma-next/test/psl-interpretation-other-types.test.ts @@ -12,6 +12,7 @@ * `true` in every case. */ +import type { Contract } from '@prisma-next/contract/types' import { parsePslDocument } from '@prisma-next/psl-parser' import { interpretPslDocumentToSqlContract } from '@prisma-next/sql-contract-psl' import { describe, expect, it } from 'vitest' @@ -22,6 +23,7 @@ const postgresTarget = { kind: 'target' as const, familyId: 'sql' as const, targetId: 'postgres' as const, + defaultNamespaceId: 'public', id: 'postgres', version: '0.0.1', capabilities: {}, @@ -39,6 +41,12 @@ function interpret(schema: string) { target: postgresTarget, scalarTypeDescriptors: postgresScalarTypeDescriptors, composedExtensionPacks: [cipherstashControl.id], + composedExtensionContracts: new Map([ + [ + cipherstashControl.id, + cipherstashControl.contractSpace!.contractJson as unknown as Contract, + ], + ]), authoringContributions: { type: cipherstashPack.authoring.type, field: {} }, }) } @@ -61,12 +69,15 @@ type StorageView = { // `public` for Postgres since 0.12), while `types` stays at the root. const asStorage = (storage: unknown): StorageView => { const s = storage as { - readonly namespaces?: Record + readonly namespaces?: Record< + string, + { entries?: { table?: StorageView['tables'] } } + > readonly types?: StorageView['types'] } const tables: StorageView['tables'] = {} for (const ns of Object.values(s.namespaces ?? {})) { - Object.assign(tables, ns.tables) + Object.assign(tables, ns.entries?.table) } return { tables, diff --git a/packages/prisma-next/test/psl-interpretation.test.ts b/packages/prisma-next/test/psl-interpretation.test.ts index 5da5de7f5..aa4b4e4d9 100644 --- a/packages/prisma-next/test/psl-interpretation.test.ts +++ b/packages/prisma-next/test/psl-interpretation.test.ts @@ -28,6 +28,7 @@ * (`EncryptedDate`, `EncryptedBoolean`, `EncryptedJson`) */ +import type { Contract } from '@prisma-next/contract/types' import { parsePslDocument } from '@prisma-next/psl-parser' import { interpretPslDocumentToSqlContract } from '@prisma-next/sql-contract-psl' import { describe, expect, it } from 'vitest' @@ -38,6 +39,7 @@ const postgresTarget = { kind: 'target' as const, familyId: 'sql' as const, targetId: 'postgres' as const, + defaultNamespaceId: 'public', id: 'postgres', version: '0.0.1', capabilities: {}, @@ -55,6 +57,12 @@ function interpret(schema: string) { target: postgresTarget, scalarTypeDescriptors: postgresScalarTypeDescriptors, composedExtensionPacks: [cipherstashControl.id], + composedExtensionContracts: new Map([ + [ + cipherstashControl.id, + cipherstashControl.contractSpace!.contractJson as unknown as Contract, + ], + ]), authoringContributions: { type: cipherstashPack.authoring.type, field: {} }, }) } @@ -77,12 +85,15 @@ type StorageView = { // `public` for Postgres since 0.12), while `types` stays at the root. const asStorage = (storage: unknown): StorageView => { const s = storage as { - readonly namespaces?: Record + readonly namespaces?: Record< + string, + { entries?: { table?: StorageView['tables'] } } + > readonly types?: StorageView['types'] } const tables: StorageView['tables'] = {} for (const ns of Object.values(s.namespaces ?? {})) { - Object.assign(tables, ns.tables) + Object.assign(tables, ns.entries?.table) } return { tables, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50be45e15..216c8019a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,66 +408,66 @@ importers: specifier: workspace:* version: link:../stack '@prisma-next/contract': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/family-sql': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/framework-components': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/migration-tools': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/sql-contract': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/sql-operations': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/sql-relational-core': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/sql-runtime': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/ts-render': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/utils': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) arktype: specifier: ^2.1.29 version: 2.2.0 devDependencies: '@prisma-next/adapter-postgres': - specifier: 0.12.0 - version: 0.12.0(typanion@3.14.0)(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/cli': - specifier: 0.12.0 - version: 0.12.0(typanion@3.14.0)(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/driver-postgres': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/extension-author-tools': specifier: 0.14.0 version: 0.14.0(typescript@5.9.3) '@prisma-next/psl-parser': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/sql-contract-psl': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/sql-contract-ts': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/sql-schema-ir': - specifier: 0.12.0 - version: 0.12.0(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typescript@5.9.3) '@prisma-next/target-postgres': - specifier: 0.12.0 - version: 0.12.0(typanion@3.14.0)(typescript@5.9.3) + specifier: 0.13.0 + version: 0.13.0(typanion@3.14.0)(typescript@5.9.3) pathe: specifier: ^2.0.3 version: 2.0.3 @@ -1077,12 +1077,6 @@ packages: react-dom: optional: true - '@dagrejs/dagre@3.0.0': - resolution: {integrity: sha512-ZzhnTy1rfuoew9Ez3EIw4L2znPGnYYhfn8vc9c4oB8iw6QAsszbiU0vRhlxWPFnmmNSFAkrYeF1PhM5m4lAN0Q==} - - '@dagrejs/graphlib@4.0.1': - resolution: {integrity: sha512-IvcV6FduIIAmLwnH+yun+QtV36SC7mERqa86aClNqmMN09WhmPPYU8ckHrZBozErf+UvHPWOTJYaGYiIcs0DgA==} - '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} @@ -1809,8 +1803,8 @@ packages: '@posthog/types@1.373.5': resolution: {integrity: sha512-K7STCnRG/WBE1q0BwEkIcrJB5OqECaymsQj6Hp4Ntvaek4dqHkZGfp6hxwIPqQPjlOXwidwPLo+XGsn+CoZUyw==} - '@prisma-next/adapter-postgres@0.12.0': - resolution: {integrity: sha512-2PvyY92pncqieSsxAMNYsxL4SynK9gaNxbdcKqnJhS068WDfPnsuTDq7L5O+fs+PPmq9x0g+QYo3DGU31XzpDA==} + '@prisma-next/adapter-postgres@0.13.0': + resolution: {integrity: sha512-C3eYTekLZcRYSBS6VUgurgGYHKmuIYO0P5jRUWyWbELHJf4aMyKrRWJWkvog3+A/SMZUQsLaL0CFL/x7cWgNmA==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1827,8 +1821,8 @@ packages: typescript: optional: true - '@prisma-next/cli-telemetry@0.12.0': - resolution: {integrity: sha512-0ifr8cuKldEibs7clP9Z/v2+ZgEbraxFmahil+ejgmz0y/RAIdlVCbHS5Nve6mI+0AbdhBDF3Kjaoda/oSrRZA==} + '@prisma-next/cli-telemetry@0.13.0': + resolution: {integrity: sha512-D7efYTh+XRLZNu4Sj3MQZaffn3odfikvGxQCkK/H3vf1Z9hlNZXi6b9LAVUNODgfp+M0L25lrvQeXoOnXYuy9w==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1845,8 +1839,8 @@ packages: typescript: optional: true - '@prisma-next/cli@0.12.0': - resolution: {integrity: sha512-xItdeDd+9sUSvVCqTDIJWC9TTkROMUFYTrECMc2iLk+6AzIzms19ASkqIWqM/2yfGwXIQxwZiFizdqt4sG45UQ==} + '@prisma-next/cli@0.13.0': + resolution: {integrity: sha512-bzVvhNlbS/Fh5WvTtUa0+L4wG/HmV4MugaTCDpZO4+MBbm5S8HdgloOP1dvjvchp4emZYLN4nu8x8Objqz3kEg==} engines: {node: '>=24'} hasBin: true peerDependencies: @@ -1865,8 +1859,8 @@ packages: typescript: optional: true - '@prisma-next/config@0.12.0': - resolution: {integrity: sha512-cGU3jMKEG17ceTbEKWivThcejyYqpXtUdG6HsXi6yJedw27nxs5SrbaQIbDi1lizzSTEDtOUZGRIU3fSlaoj/g==} + '@prisma-next/config@0.13.0': + resolution: {integrity: sha512-M9RmCMGS0K6bPvMwjgszntRQa+9z7t0ZZGcyYP36CJ1w3/IUeSGZ0VJJCzKE/8XFGi+QxD6lULK8tzJkXQvgzQ==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1883,8 +1877,8 @@ packages: typescript: optional: true - '@prisma-next/contract-authoring@0.12.0': - resolution: {integrity: sha512-YH3Uuu+DeETyhd6SDxdoLEuWSVRDZ0AtKQgeD7D+NndnLncsTiEa2i+l0ezrYxP+nA4S6GkrB8+FrFuqZczbhA==} + '@prisma-next/contract-authoring@0.13.0': + resolution: {integrity: sha512-n+SwaTNJ+E/2cdQyShn67U5jJHMW/7CxaZf27oJDXqRzhNX2DthDJh955LGIbrQGQDc3zPvCL5nOD5oTU1MVpA==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1901,8 +1895,8 @@ packages: typescript: optional: true - '@prisma-next/contract@0.12.0': - resolution: {integrity: sha512-DqRglAsfsby1wclnxdtKU/YuihyEYdl/YCDs2Kt5RCnWTxfTNViIoFoz5E7hiwdkHLDl4fwTXJPzreA35qhT8A==} + '@prisma-next/contract@0.13.0': + resolution: {integrity: sha512-QnLFmvHL6Z96J2ZRylo9YADjsY+yQUq2fVK/T3HWChbp9QaN9G2dymeugSJFjs72gbxtHJ86nsWONBZ8uAJUjg==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1919,8 +1913,8 @@ packages: typescript: optional: true - '@prisma-next/driver-postgres@0.12.0': - resolution: {integrity: sha512-keYGuIkBBdSiQUbFRXQwAlShbmGNogztJOVYL9aCS46UoAKB+bjoylOheImyAQWK+3HtupOmiOUCSbBhUhtdOQ==} + '@prisma-next/driver-postgres@0.13.0': + resolution: {integrity: sha512-RClCCsjCq3iDtMzDCXwEdyX3fQakagZlBMOVyw230qhkRE8dBUUAmv6M1YBuGx6TVvgj1Q+PP7a+K10XXUCNJw==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1937,8 +1931,8 @@ packages: typescript: optional: true - '@prisma-next/emitter@0.12.0': - resolution: {integrity: sha512-kpJ0pPSVUpsAJExkQaMEinhsh7Dwr+MAAKcp0P0C9MbnkvYE5hOUv1FUr+RWlta5bz6ioR73elfp+C+dSAAFng==} + '@prisma-next/emitter@0.13.0': + resolution: {integrity: sha512-oaELZkVVxYtO8r9SaFOb2ZrkQfhlMCnGOM8ZBLc7WbDSCvVjKxYmUCnSVQ2OVrrn5dadPRmn5s/XVNnD9t51AA==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1955,8 +1949,8 @@ packages: typescript: optional: true - '@prisma-next/errors@0.12.0': - resolution: {integrity: sha512-vDeTeUs2Uncpxdme+fjRF/fAN1DSc6TstujwEVmnyjTrKu5tAcwPJagAtG2ewN+nz9aEBu+m060PJxvg+VmbCg==} + '@prisma-next/errors@0.13.0': + resolution: {integrity: sha512-dspObTEVD5FvDAg0KC3qDPdcsi1WprrAU733VOfD3sR3La5n9/KFACQPpleKUADvqQwex+sKuo+woMS4yiOjtQ==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -1983,8 +1977,8 @@ packages: typescript: optional: true - '@prisma-next/family-sql@0.12.0': - resolution: {integrity: sha512-WE2Mg01Qm6uaTu0m8VxIH6ylPqWmsqz2836VuJeQFAeZpUZesiSlgZzCbT8qXIsJf55WYk6Ke1auX9g7dO/LLg==} + '@prisma-next/family-sql@0.13.0': + resolution: {integrity: sha512-PL2KNL05y4UcElFFYW1uPH9dPkRjqMzBArtbFjXP2XaTOiaRF8cX5/GNy3onPWq9ZhDbrmL1+vKYPfUls9yrrw==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2001,8 +1995,8 @@ packages: typescript: optional: true - '@prisma-next/framework-components@0.12.0': - resolution: {integrity: sha512-rN/x6DUHzhm8jQEBadY8byVUxT9ps1fMWZ2iW0vkJeDyZKMh3mtAEqT59wqS1PEN5CQ6jJsq5FV0AYM1lqHMKw==} + '@prisma-next/framework-components@0.13.0': + resolution: {integrity: sha512-jyaU+A8ZMgowt+Hqb68slwh8jsn76HJ9eCFSbTGt5do88SvoSHWnhAkXVJy6oeprI15o0V/CmXOJ6a0ZtHRKNg==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2019,8 +2013,8 @@ packages: typescript: optional: true - '@prisma-next/ids@0.12.0': - resolution: {integrity: sha512-tn48qFBlVtn2/6ep48fkiWzAgN/mJXHFuihjzTZ0krB8Vfz6Kw9xu0/BikUf17Ewd653A2LSmtjnmaWslLju9Q==} + '@prisma-next/ids@0.13.0': + resolution: {integrity: sha512-SMb6qFTiS22cK3npJaaFaXYJu8+/kGdhblitOq63hzeBWS7hxkf3LNNy8ugyS7Tep9x3q+k312qcqOVKiJRB2Q==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2037,8 +2031,8 @@ packages: typescript: optional: true - '@prisma-next/migration-tools@0.12.0': - resolution: {integrity: sha512-k9FlvZbcvwWVqcN9/isFnD6RoIesvDxXI0aLvFOGejZb5iAS+7nh+zZUNx0e7cf0XP48g3IZVw+1qsFPU3pheg==} + '@prisma-next/migration-tools@0.13.0': + resolution: {integrity: sha512-csvsFLurOb8DzTpRgp2TIfl+orf+jtnvkEFNuK0A02bL2b/aN4G+qtqKpvHl4KAfF5DTsPuzf+l8sj2AarzWBw==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2055,8 +2049,8 @@ packages: typescript: optional: true - '@prisma-next/operations@0.12.0': - resolution: {integrity: sha512-ARJtBZ6YrHscg27dnX19soeJs97NsKyk/A13J4DmUdMEPLMs/dYFxxt0MZ2OPyTIWW/ihp+sszGAlRa6+74tGA==} + '@prisma-next/operations@0.13.0': + resolution: {integrity: sha512-Wj3SPPYAZGgJ8thpeRoshqhlJSjOTruYgppjSy/eHgU91NgFMB1smeUXZAlkZK7062mGDJQabZBPoiZdRlcO5A==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2082,8 +2076,8 @@ packages: typescript: optional: true - '@prisma-next/psl-parser@0.12.0': - resolution: {integrity: sha512-7w4nrcDQLvnvSx6RjrVsIsB1jXSR8TumzYZYRZ2/QNkVxdHPd5/VOysZWxv7rCLxdZ1G0PvoMmGYy2LWGnzBsg==} + '@prisma-next/psl-parser@0.13.0': + resolution: {integrity: sha512-HhdtdeL7gE5Pq1vj+o/zJBzeZ1te9vOECtkg+w7gcOrTLyagQSrC+SMg04wT5vnehCiQPq43Knd7i3GrCucX/w==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2100,8 +2094,8 @@ packages: typescript: optional: true - '@prisma-next/psl-printer@0.12.0': - resolution: {integrity: sha512-qnL9vbe8Nhc49fCioBs4KoWOYRJbt09KJCl1g/KkU5de9MG0kkgHhZpMAdRHvZv7n20k24usXKs7RqPQ4XDymw==} + '@prisma-next/psl-printer@0.13.0': + resolution: {integrity: sha512-LijrxXOnY6dHBHl1kSNcJL+CmXsZL61Z4VLXqbev/C+shtBzqcIlesG8DuUwv8Zv07eIKYc4SUpSqxs+NiSMKQ==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2127,8 +2121,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-emitter@0.12.0': - resolution: {integrity: sha512-sSVD3KysoxaqNOMahVCgcf3wSlluf9MKrh74TTtvPgb0UV/M2iW0XW56ZJGrtrrH6YDhGgysnoievwFCCL+Gvg==} + '@prisma-next/sql-contract-emitter@0.13.0': + resolution: {integrity: sha512-JcsvIKEoRToxAQ15h7onVqfnbiPJrU0WYJ+qude4rPY6ZVirhT/gpK9tXuNH2zIYKK4XPTthvOwlcOnZQcR1Aw==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2145,8 +2139,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-psl@0.12.0': - resolution: {integrity: sha512-I760s2H3DNUf+u0hPh3eYCOqzcKTrZOOUJmwj6ROwqy7OoxExv4ffYeIW8sU9SqHhFxGm1HHiiJZ/v7LdXcpRg==} + '@prisma-next/sql-contract-psl@0.13.0': + resolution: {integrity: sha512-Z85MB7IgmHJ6fIwPfpNj6dLhT3zZGClAIunTvpRhvcnlEkP2BpzBDsTFY8FrdFwUQXYVrRY9qNsimlj7k4nNBA==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2163,8 +2157,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-ts@0.12.0': - resolution: {integrity: sha512-FQEBLzQwlkmmx/lxSaM5yUpopt6vJVp2aAoe15hxybLRudHwtBwIz7JqKgkMzI2zN/Gquu9D07WDuo2G1h8//Q==} + '@prisma-next/sql-contract-ts@0.13.0': + resolution: {integrity: sha512-zgsdxm2i8CuIQdW3MBo4kKlBy0T0ARDLmbLE/l75rm20AdEgdUUdyPZrdtRaFm/4PAUCeyct14sAqG05HqBzDw==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2181,8 +2175,8 @@ packages: typescript: optional: true - '@prisma-next/sql-contract@0.12.0': - resolution: {integrity: sha512-lGkowImeBFLKMry8FNUY2roZIN3eNnQsjt9rbQvzaiwuIM5XbURkmjX+rLb+MHAOrcCJaJVxPyZoJfFCeB7s3w==} + '@prisma-next/sql-contract@0.13.0': + resolution: {integrity: sha512-ZlFIpdndckKR3Vm//JNXqe7LTDg7JY4i7iHyy9ri9D6FDDHgr1jg+BEPxduvW2VjqVOBoZKw3hIxpM4ZBLP0Lg==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2199,8 +2193,8 @@ packages: typescript: optional: true - '@prisma-next/sql-errors@0.12.0': - resolution: {integrity: sha512-g83tk8EYvmU8RWvzIR+G/7LsvO1GwXUGo4jVe4G908GEIdPtsWT48WjiaB5CkNiuEVGegZ/FgFeNWGcBklTBRw==} + '@prisma-next/sql-errors@0.13.0': + resolution: {integrity: sha512-0chEMpBWFNCHSUmnkP2iGo3CTN/lfTYy4eVV6DMg6Np9CFmGk+VMkwrieN7bRpbtTNc27cjT4s+4EK3UY5B71A==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2217,8 +2211,8 @@ packages: typescript: optional: true - '@prisma-next/sql-operations@0.12.0': - resolution: {integrity: sha512-HdsT2URngE3rJ1RA+WZUzaYsp0plwXdHzt40cKRgLhVon25yVWsYJDxUdAIQtLSl0+Ce49bKMYr5LsdYrDmU8g==} + '@prisma-next/sql-operations@0.13.0': + resolution: {integrity: sha512-+7Kr/99kePZ1mxLZLVo5N9awm39lG3mqln6H2d8Gn2poQP3j+oRJVXAVF4LjHvFyfT219NwKREr42+42sh/UaA==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2244,8 +2238,8 @@ packages: typescript: optional: true - '@prisma-next/sql-relational-core@0.12.0': - resolution: {integrity: sha512-MKkOuLyAKvzubwUPvLpcm/VGokuFYfHb8cllSD3xFR8gdeUksG4xib+vLswxSeE+UM3KcmhXPGHsm5KxpgCYvQ==} + '@prisma-next/sql-relational-core@0.13.0': + resolution: {integrity: sha512-uTfH2jPfZWS78SgIYu7ofEEWHYQUXiCwSITQPB4Hl9QEkzWY7WjRCOtHNBjO1rH7VXw678adzPDHk6Hl70HZtw==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2262,8 +2256,8 @@ packages: typescript: optional: true - '@prisma-next/sql-runtime@0.12.0': - resolution: {integrity: sha512-YPb4Ntw53WhSyIy6FIaP0HbL2SCaQc/jgTRANKqlckGa+vKVeHIScehT5j6B49nL+6SsshuKEE5+YvyT0roX6Q==} + '@prisma-next/sql-runtime@0.13.0': + resolution: {integrity: sha512-+sy4pf83HzkGU/6SrAtC9KbhuqjGqgdcZa87wFIN+oMFgqMnvz2sPFxKg3SEQIwNOBS9hFiAiX9AZqFgBjlOhQ==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2280,8 +2274,8 @@ packages: typescript: optional: true - '@prisma-next/sql-schema-ir@0.12.0': - resolution: {integrity: sha512-/gvvkx1PHb52gxR4ePRtW/UQVWdDjGaN0liUBKZU8qMdY7UBUuf/UI9HtNIzO0TERWIWYZKCHuh9u9rTMz10XQ==} + '@prisma-next/sql-schema-ir@0.13.0': + resolution: {integrity: sha512-v8CJKAcaqmBaBcq0tamf7YxDL87AMPRAUhFIZA59EoJjvQSkSa1anlhT1vKqkON7Y03DK4powm/8fC9UhWkhzw==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2298,8 +2292,8 @@ packages: typescript: optional: true - '@prisma-next/target-postgres@0.12.0': - resolution: {integrity: sha512-cozTQwhU+ky3JFby5/Q2kDscEX/Zfi3LnJ9Ol7QLTB+nvMErfzykoqLhZZdiPNxY1z0DktNohWJsXpj0oQJhFw==} + '@prisma-next/target-postgres@0.13.0': + resolution: {integrity: sha512-3AyuAOusVy9/bUwovgkoFOBf0h8G50O3SI6+FEead6BD9teCMUE2oWJ/3rnSYQbE0bljE0n9eW7sHQ67iP4XSQ==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2316,8 +2310,8 @@ packages: typescript: optional: true - '@prisma-next/ts-render@0.12.0': - resolution: {integrity: sha512-rEN8aylBGSI6lCDQLSyIEpXLnJmTeS/vp48EdsMc8ZIUD0FrwDLUxsqkOBjQmCtmBmSw2qjIkXokjsmvM+/k9A==} + '@prisma-next/ts-render@0.13.0': + resolution: {integrity: sha512-rDOJocWAKUSoheEH1PWZFAxQyHRWbq6bAqKDkA3M5QTGDK1iBFNdgA3Ep9kkUt50vMHHJt1A5+h/FBCgpGgROA==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -2334,8 +2328,8 @@ packages: typescript: optional: true - '@prisma-next/utils@0.12.0': - resolution: {integrity: sha512-Bdgoqc5VSSKRPl4uWJvXwUkpQHvVUj9ZCLav82DDypKITmt4hRZ/o7+YzuwACGhOcbNaBtFNOPaasPFXtNyipg==} + '@prisma-next/utils@0.13.0': + resolution: {integrity: sha512-ldrgWOIMf3bYXRmtLDNzPSXo5lVluJZ+p1Ly6cKHhJo5RDM5TrFfa6hDC5E/fB+gbUlh1mVZ45iZKjqqRqVr7w==} engines: {node: '>=24'} peerDependencies: typescript: '>=5.9' @@ -4883,12 +4877,6 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - '@dagrejs/dagre@3.0.0': - dependencies: - '@dagrejs/graphlib': 4.0.1 - - '@dagrejs/graphlib@4.0.1': {} - '@drizzle-team/brocli@0.10.2': {} '@emnapi/runtime@1.7.1': @@ -5337,23 +5325,23 @@ snapshots: '@posthog/types@1.373.5': {} - '@prisma-next/adapter-postgres@0.12.0(typanion@3.14.0)(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/contract-authoring': 0.12.0(typescript@5.9.3) - '@prisma-next/errors': 0.12.0(typescript@5.9.3) - '@prisma-next/family-sql': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/ids': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract-psl': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract-ts': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-runtime': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-schema-ir': 0.12.0(typescript@5.9.3) - '@prisma-next/target-postgres': 0.12.0(typanion@3.14.0)(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/adapter-postgres@0.13.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/contract-authoring': 0.13.0(typescript@5.9.3) + '@prisma-next/errors': 0.13.0(typescript@5.9.3) + '@prisma-next/family-sql': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/ids': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract-psl': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.13.0(typescript@5.9.3) + '@prisma-next/target-postgres': 0.13.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 optionalDependencies: typescript: 5.9.3 @@ -5385,10 +5373,10 @@ snapshots: - magicast - typanion - '@prisma-next/cli-telemetry@0.12.0(typescript@5.9.3)': + '@prisma-next/cli-telemetry@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/config': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/config': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 c12: 3.3.4 pathe: 2.0.3 @@ -5409,19 +5397,18 @@ snapshots: transitivePeerDependencies: - magicast - '@prisma-next/cli@0.12.0(typanion@3.14.0)(typescript@5.9.3)': + '@prisma-next/cli@0.13.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@clack/prompts': 1.4.0 - '@dagrejs/dagre': 3.0.0 - '@prisma-next/cli-telemetry': 0.12.0(typescript@5.9.3) - '@prisma-next/config': 0.12.0(typescript@5.9.3) - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/emitter': 0.12.0(typescript@5.9.3) - '@prisma-next/errors': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/migration-tools': 0.12.0(typescript@5.9.3) - '@prisma-next/psl-printer': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/cli-telemetry': 0.13.0(typescript@5.9.3) + '@prisma-next/config': 0.13.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/emitter': 0.13.0(typescript@5.9.3) + '@prisma-next/errors': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.13.0(typescript@5.9.3) + '@prisma-next/psl-printer': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 c12: 3.3.4 ci-info: 4.4.0 @@ -5474,11 +5461,11 @@ snapshots: - magicast - typanion - '@prisma-next/config@0.12.0(typescript@5.9.3)': + '@prisma-next/config@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 optionalDependencies: typescript: 5.9.3 @@ -5492,9 +5479,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract-authoring@0.12.0(typescript@5.9.3)': + '@prisma-next/contract-authoring@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5504,9 +5491,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract@0.12.0(typescript@5.9.3)': + '@prisma-next/contract@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 optionalDependencies: @@ -5520,19 +5507,19 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/driver-postgres@0.12.0(typescript@5.9.3)': + '@prisma-next/driver-postgres@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/errors': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-errors': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/errors': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-errors': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 - pg: 8.20.0 - pg-cursor: 2.21.0(pg@8.20.0) + pg: 8.21.0 + pg-cursor: 2.21.0(pg@8.21.0) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -5556,13 +5543,13 @@ snapshots: transitivePeerDependencies: - pg-native - '@prisma-next/emitter@0.12.0(typescript@5.9.3)': + '@prisma-next/emitter@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/operations': 0.12.0(typescript@5.9.3) - '@prisma-next/ts-render': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/operations': 0.13.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 prettier: 3.8.3 optionalDependencies: @@ -5580,10 +5567,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/errors@0.12.0(typescript@5.9.3)': + '@prisma-next/errors@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5598,21 +5585,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/family-sql@0.12.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/emitter': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/migration-tools': 0.12.0(typescript@5.9.3) - '@prisma-next/operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract-emitter': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract-ts': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-runtime': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-schema-ir': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/family-sql@0.13.0(typescript@5.9.3)': + dependencies: + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/emitter': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.13.0(typescript@5.9.3) + '@prisma-next/operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract-emitter': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-runtime': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 optionalDependencies: typescript: 5.9.3 @@ -5636,12 +5623,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/framework-components@0.12.0(typescript@5.9.3)': + '@prisma-next/framework-components@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/operations': 0.12.0(typescript@5.9.3) - '@prisma-next/ts-render': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/operations': 0.13.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 optionalDependencies: @@ -5658,11 +5645,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/ids@0.12.0(typescript@5.9.3)': + '@prisma-next/ids@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) uniku: 0.0.12 optionalDependencies: typescript: 5.9.3 @@ -5676,11 +5663,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/migration-tools@0.12.0(typescript@5.9.3)': + '@prisma-next/migration-tools@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 pathe: 2.0.3 prettier: 3.8.3 @@ -5698,7 +5685,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/operations@0.12.0(typescript@5.9.3)': + '@prisma-next/operations@0.13.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -5733,10 +5720,10 @@ snapshots: - pg-native - typanion - '@prisma-next/psl-parser@0.12.0(typescript@5.9.3)': + '@prisma-next/psl-parser@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5747,9 +5734,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-printer@0.12.0(typescript@5.9.3)': + '@prisma-next/psl-printer@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5770,13 +5758,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-emitter@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-contract-emitter@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/emitter': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/emitter': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5790,15 +5778,15 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-psl@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-contract-psl@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/config': 0.12.0(typescript@5.9.3) - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/psl-parser': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract-ts': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/config': 0.13.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/psl-parser': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract-ts': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) pathe: 2.0.3 optionalDependencies: typescript: 5.9.3 @@ -5816,14 +5804,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-ts@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-contract-ts@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/config': 0.12.0(typescript@5.9.3) - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/contract-authoring': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/config': 0.13.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/contract-authoring': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 pathe: 2.0.3 ts-toolbelt: 9.6.0 @@ -5844,11 +5832,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-contract@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 optionalDependencies: typescript: 5.9.3 @@ -5862,7 +5850,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-errors@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-errors@0.13.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -5870,10 +5858,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-operations@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-operations@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) + '@prisma-next/operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) arktype: 2.2.0 optionalDependencies: typescript: 5.9.3 @@ -5900,14 +5888,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-relational-core@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-relational-core@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 ts-toolbelt: 9.6.0 @@ -5928,16 +5916,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-runtime@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-runtime@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/ids': 0.12.0(typescript@5.9.3) - '@prisma-next/operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/ids': 0.13.0(typescript@5.9.3) + '@prisma-next/operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) arktype: 2.2.0 optionalDependencies: typescript: 5.9.3 @@ -5956,10 +5944,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-schema-ir@0.12.0(typescript@5.9.3)': + '@prisma-next/sql-schema-ir@0.13.0(typescript@5.9.3)': dependencies: - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -5970,21 +5958,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/target-postgres@0.12.0(typanion@3.14.0)(typescript@5.9.3)': - dependencies: - '@prisma-next/cli': 0.12.0(typanion@3.14.0)(typescript@5.9.3) - '@prisma-next/contract': 0.12.0(typescript@5.9.3) - '@prisma-next/errors': 0.12.0(typescript@5.9.3) - '@prisma-next/family-sql': 0.12.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.12.0(typescript@5.9.3) - '@prisma-next/migration-tools': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-errors': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.12.0(typescript@5.9.3) - '@prisma-next/sql-schema-ir': 0.12.0(typescript@5.9.3) - '@prisma-next/ts-render': 0.12.0(typescript@5.9.3) - '@prisma-next/utils': 0.12.0(typescript@5.9.3) + '@prisma-next/target-postgres@0.13.0(typanion@3.14.0)(typescript@5.9.3)': + dependencies: + '@prisma-next/cli': 0.13.0(typanion@3.14.0)(typescript@5.9.3) + '@prisma-next/contract': 0.13.0(typescript@5.9.3) + '@prisma-next/errors': 0.13.0(typescript@5.9.3) + '@prisma-next/family-sql': 0.13.0(typescript@5.9.3) + '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) + '@prisma-next/migration-tools': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-errors': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) + '@prisma-next/sql-schema-ir': 0.13.0(typescript@5.9.3) + '@prisma-next/ts-render': 0.13.0(typescript@5.9.3) + '@prisma-next/utils': 0.13.0(typescript@5.9.3) '@standard-schema/spec': 1.1.0 arktype: 2.2.0 pathe: 2.0.3 @@ -6018,7 +6006,7 @@ snapshots: - magicast - typanion - '@prisma-next/ts-render@0.12.0(typescript@5.9.3)': + '@prisma-next/ts-render@0.13.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -6026,7 +6014,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/utils@0.12.0(typescript@5.9.3)': + '@prisma-next/utils@0.13.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -7307,10 +7295,6 @@ snapshots: pg-connection-string@2.14.0: {} - pg-cursor@2.21.0(pg@8.20.0): - dependencies: - pg: 8.20.0 - pg-cursor@2.21.0(pg@8.21.0): dependencies: pg: 8.21.0 From a4d1a3b9e50e1360dfdb69f651830b94cf66b8f1 Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:17:19 -0600 Subject: [PATCH 08/15] chore: upgrade @prisma-next/* to 0.14.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-transition changes (extension-author upgrade instructions 0.13→0.14): - migration-op-factories-to-methods: ran the codemod; the seed migration's only op factory is rawSql, which survives in 0.14 — import churn only - create-runtime-removed: code was unaffected (createRuntime appeared only in doc comments, now updated to new PostgresRuntimeImpl) - re-emitted the contract space (the 0.14 emitter drops the spurious empty __unbound__ namespace slot, TML-2916, and no longer emits ContractModelDefinitions) and re-pinned the baseline migration - uuid presets, flat builder accessors, namespace-entries dot access, enum surface, namespaced TypeMaps/TableProxy, ContractModelDefinitions: no-ops for this package - planFieldEventOperations op calls can lower lazily; descriptor tests await Promise.all(ops.map(c => c.toOp())) Co-Authored-By: Claude Fable 5 --- .../end-contract.d.ts | 120 ++-- .../end-contract.json | 9 +- .../migration.json | 4 +- .../migration.ts | 2 +- .../prisma-next/migrations/refs/head.json | 2 +- packages/prisma-next/package.json | 36 +- packages/prisma-next/src/contract.d.ts | 120 ++-- packages/prisma-next/src/contract.json | 9 +- .../prisma-next/src/exports/middleware.ts | 3 +- packages/prisma-next/src/exports/migration.ts | 20 +- packages/prisma-next/src/exports/runtime.ts | 5 +- .../test/cipherstash-codec.test.ts | 10 +- pnpm-lock.yaml | 646 +----------------- 13 files changed, 170 insertions(+), 816 deletions(-) diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts index b1997c23a..6443a1f58 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts @@ -23,7 +23,6 @@ import type { } from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, - ContractModelDefinitions, ExecutionHashBase, NamespaceId, ProfileHashBase, @@ -31,7 +30,7 @@ import type { } from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0'>; + StorageHashBase<'sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; @@ -44,17 +43,21 @@ type DefaultLiteralValue = CodecId extends key : _Encoded; export type FieldOutputTypes = { - readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['output']; - readonly state: CodecTypes['pg/text@1']['output']; - readonly data: CodecTypes['pg/jsonb@1']['output']; + readonly public: { + readonly EqlV2Configuration: { + readonly id: CodecTypes['pg/text@1']['output']; + readonly state: CodecTypes['pg/text@1']['output']; + readonly data: CodecTypes['pg/jsonb@1']['output']; + }; }; }; export type FieldInputTypes = { - readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['input']; - readonly state: CodecTypes['pg/text@1']['input']; - readonly data: CodecTypes['pg/jsonb@1']['input']; + readonly public: { + readonly EqlV2Configuration: { + readonly id: CodecTypes['pg/text@1']['input']; + readonly state: CodecTypes['pg/text@1']['input']; + readonly data: CodecTypes['pg/jsonb@1']['input']; + }; }; }; export type TypeMaps = TypeMapsType< @@ -65,77 +68,42 @@ export type TypeMaps = TypeMapsType< >; type ContractBase = Omit< - ContractType< - { - readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'sql-namespace'; - readonly entries: { readonly table: {} }; - }; - readonly public: { - readonly id: 'public'; - readonly kind: 'sql-namespace'; - readonly entries: { - readonly table: { - readonly eql_v2_configuration: { - columns: { - readonly id: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly state: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly data: { - readonly nativeType: 'jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: false; - }; + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'sql-namespace'; + readonly entries: { + readonly table: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; }; }; }; }; - readonly storageHash: StorageHash; - }, - { - readonly EqlV2Configuration: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly state: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly data: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'eql_v2_configuration'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly state: { readonly column: 'state' }; - readonly data: { readonly column: 'data' }; - }; - }; - }; - } - >, + }; + readonly storageHash: StorageHash; + }>, 'roots' | 'domain' > & { readonly target: 'postgres'; @@ -202,8 +170,6 @@ type ContractBase = Omit< readonly profileHash: ProfileHash; }; -export type Models = ContractModelDefinitions; - export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json index f38b207be..8dc56139f 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json @@ -60,13 +60,6 @@ }, "storage": { "namespaces": { - "__unbound__": { - "entries": { - "table": {} - }, - "id": "__unbound__", - "kind": "postgres-unbound-schema" - }, "public": { "entries": { "table": { @@ -103,7 +96,7 @@ "kind": "postgres-schema" } }, - "storageHash": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0" + "storageHash": "sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7" }, "capabilities": { "postgres": { diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json index 058115bf1..4bf75c2ac 100644 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json @@ -1,9 +1,9 @@ { "from": null, - "to": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0", + "to": "sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7", "providedInvariants": [ "cipherstash:install-eql-bundle-v1" ], "createdAt": "2026-05-09T03:42:56.902Z", - "migrationHash": "sha256:f4fb2bcb8619643b7c1cf7d9c81c156bc589bef99214c95a4d73127d5276d007" + "migrationHash": "sha256:ae84404848d090c24f4094331c14972474a9695ffd45d29a21c5f5a8cfed9df7" } \ No newline at end of file diff --git a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts index d6a1f4ed7..4588bea3d 100755 --- a/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts +++ b/packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts @@ -36,7 +36,7 @@ export default class M extends Migration { override describe() { return { from: null, - to: 'sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0', + to: 'sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7', } } diff --git a/packages/prisma-next/migrations/refs/head.json b/packages/prisma-next/migrations/refs/head.json index b53e474ff..e79526b6f 100644 --- a/packages/prisma-next/migrations/refs/head.json +++ b/packages/prisma-next/migrations/refs/head.json @@ -1,4 +1,4 @@ { - "hash": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0", + "hash": "sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7", "invariants": ["cipherstash:install-eql-bundle-v1"] } diff --git a/packages/prisma-next/package.json b/packages/prisma-next/package.json index a4891d300..36937fada 100644 --- a/packages/prisma-next/package.json +++ b/packages/prisma-next/package.json @@ -80,28 +80,28 @@ }, "dependencies": { "@cipherstash/stack": "workspace:*", - "@prisma-next/contract": "0.13.0", - "@prisma-next/family-sql": "0.13.0", - "@prisma-next/framework-components": "0.13.0", - "@prisma-next/migration-tools": "0.13.0", - "@prisma-next/sql-contract": "0.13.0", - "@prisma-next/sql-operations": "0.13.0", - "@prisma-next/sql-relational-core": "0.13.0", - "@prisma-next/sql-runtime": "0.13.0", - "@prisma-next/ts-render": "0.13.0", - "@prisma-next/utils": "0.13.0", + "@prisma-next/contract": "0.14.0", + "@prisma-next/family-sql": "0.14.0", + "@prisma-next/framework-components": "0.14.0", + "@prisma-next/migration-tools": "0.14.0", + "@prisma-next/sql-contract": "0.14.0", + "@prisma-next/sql-operations": "0.14.0", + "@prisma-next/sql-relational-core": "0.14.0", + "@prisma-next/sql-runtime": "0.14.0", + "@prisma-next/ts-render": "0.14.0", + "@prisma-next/utils": "0.14.0", "arktype": "^2.1.29" }, "devDependencies": { - "@prisma-next/adapter-postgres": "0.13.0", - "@prisma-next/cli": "0.13.0", - "@prisma-next/driver-postgres": "0.13.0", + "@prisma-next/adapter-postgres": "0.14.0", + "@prisma-next/cli": "0.14.0", + "@prisma-next/driver-postgres": "0.14.0", "@prisma-next/extension-author-tools": "0.14.0", - "@prisma-next/psl-parser": "0.13.0", - "@prisma-next/sql-contract-psl": "0.13.0", - "@prisma-next/sql-contract-ts": "0.13.0", - "@prisma-next/sql-schema-ir": "0.13.0", - "@prisma-next/target-postgres": "0.13.0", + "@prisma-next/psl-parser": "0.14.0", + "@prisma-next/sql-contract-psl": "0.14.0", + "@prisma-next/sql-contract-ts": "0.14.0", + "@prisma-next/sql-schema-ir": "0.14.0", + "@prisma-next/target-postgres": "0.14.0", "pathe": "^2.0.3", "tsup": "catalog:repo", "typescript": "catalog:repo", diff --git a/packages/prisma-next/src/contract.d.ts b/packages/prisma-next/src/contract.d.ts index b1997c23a..6443a1f58 100644 --- a/packages/prisma-next/src/contract.d.ts +++ b/packages/prisma-next/src/contract.d.ts @@ -23,7 +23,6 @@ import type { } from '@prisma-next/sql-contract/types'; import type { Contract as ContractType, - ContractModelDefinitions, ExecutionHashBase, NamespaceId, ProfileHashBase, @@ -31,7 +30,7 @@ import type { } from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0'>; + StorageHashBase<'sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; @@ -44,17 +43,21 @@ type DefaultLiteralValue = CodecId extends key : _Encoded; export type FieldOutputTypes = { - readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['output']; - readonly state: CodecTypes['pg/text@1']['output']; - readonly data: CodecTypes['pg/jsonb@1']['output']; + readonly public: { + readonly EqlV2Configuration: { + readonly id: CodecTypes['pg/text@1']['output']; + readonly state: CodecTypes['pg/text@1']['output']; + readonly data: CodecTypes['pg/jsonb@1']['output']; + }; }; }; export type FieldInputTypes = { - readonly EqlV2Configuration: { - readonly id: CodecTypes['pg/text@1']['input']; - readonly state: CodecTypes['pg/text@1']['input']; - readonly data: CodecTypes['pg/jsonb@1']['input']; + readonly public: { + readonly EqlV2Configuration: { + readonly id: CodecTypes['pg/text@1']['input']; + readonly state: CodecTypes['pg/text@1']['input']; + readonly data: CodecTypes['pg/jsonb@1']['input']; + }; }; }; export type TypeMaps = TypeMapsType< @@ -65,77 +68,42 @@ export type TypeMaps = TypeMapsType< >; type ContractBase = Omit< - ContractType< - { - readonly namespaces: { - readonly __unbound__: { - readonly id: '__unbound__'; - readonly kind: 'sql-namespace'; - readonly entries: { readonly table: {} }; - }; - readonly public: { - readonly id: 'public'; - readonly kind: 'sql-namespace'; - readonly entries: { - readonly table: { - readonly eql_v2_configuration: { - columns: { - readonly id: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly state: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; - }; - readonly data: { - readonly nativeType: 'jsonb'; - readonly codecId: 'pg/jsonb@1'; - readonly nullable: false; - }; + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'sql-namespace'; + readonly entries: { + readonly table: { + readonly eql_v2_configuration: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly state: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly data: { + readonly nativeType: 'jsonb'; + readonly codecId: 'pg/jsonb@1'; + readonly nullable: false; }; - primaryKey: { readonly columns: readonly ['id'] }; - uniques: readonly []; - indexes: readonly []; - foreignKeys: readonly []; }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; }; }; }; }; - readonly storageHash: StorageHash; - }, - { - readonly EqlV2Configuration: { - readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly state: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; - }; - readonly data: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/jsonb@1' }; - }; - }; - readonly relations: Record; - readonly storage: { - readonly table: 'eql_v2_configuration'; - readonly namespaceId: 'public'; - readonly fields: { - readonly id: { readonly column: 'id' }; - readonly state: { readonly column: 'state' }; - readonly data: { readonly column: 'data' }; - }; - }; - }; - } - >, + }; + readonly storageHash: StorageHash; + }>, 'roots' | 'domain' > & { readonly target: 'postgres'; @@ -202,8 +170,6 @@ type ContractBase = Omit< readonly profileHash: ProfileHash; }; -export type Models = ContractModelDefinitions; - export type Contract = ContractWithTypeMaps; export type Namespaces = Contract['storage']['namespaces']; diff --git a/packages/prisma-next/src/contract.json b/packages/prisma-next/src/contract.json index f38b207be..8dc56139f 100644 --- a/packages/prisma-next/src/contract.json +++ b/packages/prisma-next/src/contract.json @@ -60,13 +60,6 @@ }, "storage": { "namespaces": { - "__unbound__": { - "entries": { - "table": {} - }, - "id": "__unbound__", - "kind": "postgres-unbound-schema" - }, "public": { "entries": { "table": { @@ -103,7 +96,7 @@ "kind": "postgres-schema" } }, - "storageHash": "sha256:d2c4bb1e486ca28485b18f9ead0f8d7d5ffa7ee5f5758aa49a70be4fcb2ee5c0" + "storageHash": "sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7" }, "capabilities": { "postgres": { diff --git a/packages/prisma-next/src/exports/middleware.ts b/packages/prisma-next/src/exports/middleware.ts index 5f7a1c171..20bdfd854 100644 --- a/packages/prisma-next/src/exports/middleware.ts +++ b/packages/prisma-next/src/exports/middleware.ts @@ -6,10 +6,11 @@ * encrypted in batches before encode runs: * * ```ts + * import { PostgresRuntimeImpl } from '@prisma-next/postgres/runtime'; * import { createCipherstashRuntimeDescriptor } from '@prisma-next/extension-cipherstash/runtime'; * import { bulkEncryptMiddleware } from '@prisma-next/extension-cipherstash/middleware'; * - * const runtime = createRuntime({ + * const runtime = new PostgresRuntimeImpl({ * extensionPacks: [createCipherstashRuntimeDescriptor({ sdk })], * middleware: [bulkEncryptMiddleware(sdk)], * }); diff --git a/packages/prisma-next/src/exports/migration.ts b/packages/prisma-next/src/exports/migration.ts index 510bbfe95..cad360528 100644 --- a/packages/prisma-next/src/exports/migration.ts +++ b/packages/prisma-next/src/exports/migration.ts @@ -6,16 +6,20 @@ * to wire EQL search-config rows alongside structural DDL: * * ```ts - * import { Migration, MigrationCLI, createTable } from '@prisma-next/target-postgres/migration'; + * import { Migration, MigrationCLI } from '@prisma-next/target-postgres/migration'; * import { cipherstashAddSearchConfig } from '@cipherstash/prisma-next/migration'; * * export default class M extends Migration { * override get operations() { * return [ - * createTable('public', 'user', [ - * { name: 'email', typeSql: 'eql_v2_encrypted', defaultSql: '', nullable: false }, - * { name: 'id', typeSql: 'text', defaultSql: '', nullable: false }, - * ]), + * this.createTable({ + * schema: 'public', + * table: 'user', + * columns: [ + * { name: 'email', typeSql: 'eql_v2_encrypted', defaultSql: '', nullable: false }, + * { name: 'id', typeSql: 'text', defaultSql: '', nullable: false }, + * ], + * }), * cipherstashAddSearchConfig({ table: 'user', column: 'email', index: 'unique' }), * ]; * } @@ -24,8 +28,10 @@ * MigrationCLI.run(import.meta.url, M); * ``` * - * Identical ergonomics to `createTable` / `setNotNull` etc. from - * `@prisma-next/target-postgres/migration`. The codec lifecycle hook + * Identical ergonomics to the `this.createTable` / `this.setNotNull` + * methods on the `Migration` base class from + * `@prisma-next/target-postgres/migration` (the bare op factory + * functions were removed in Prisma Next 0.14). The codec lifecycle hook * for `Encrypted` columns calls these factories automatically * when planning a contract diff. */ diff --git a/packages/prisma-next/src/exports/runtime.ts b/packages/prisma-next/src/exports/runtime.ts index 928e55e7e..5c3e14982 100644 --- a/packages/prisma-next/src/exports/runtime.ts +++ b/packages/prisma-next/src/exports/runtime.ts @@ -20,8 +20,9 @@ * pgvector's `runtime.ts` precedent. The bulk-encrypt middleware ships * separately at `@prisma-next/extension-cipherstash/middleware` because * `SqlRuntimeExtensionDescriptor` does not own a middleware slot; - * consumers register it via `createRuntime({ middleware: - * [bulkEncryptMiddleware(sdk)] })`. + * consumers register it via `new PostgresRuntimeImpl({ middleware: + * [bulkEncryptMiddleware(sdk)] })` (or their target facade's + * `middleware` option). */ import type { SqlRuntimeExtensionDescriptor } from '@prisma-next/sql-runtime' diff --git a/packages/prisma-next/test/cipherstash-codec.test.ts b/packages/prisma-next/test/cipherstash-codec.test.ts index 020e0ba67..07896f6f6 100644 --- a/packages/prisma-next/test/cipherstash-codec.test.ts +++ b/packages/prisma-next/test/cipherstash-codec.test.ts @@ -142,7 +142,7 @@ describe('planFieldEventOperations driving the cipherstash hook', () => { >, ]) - it('inlines per-flag add ops on first emit (priorContract null) when flags are enabled', () => { + it('inlines per-flag add ops on first emit (priorContract null) when flags are enabled', async () => { const ops = planFieldEventOperations({ priorContract: null, newContract: build({ @@ -151,14 +151,15 @@ describe('planFieldEventOperations driving the cipherstash hook', () => { codecHooks, }) expect(ops).toHaveLength(2) - const ids = ops.map((c) => c.toOp().invariantId).sort() + const lowered = await Promise.all(ops.map((c) => c.toOp())) + const ids = lowered.map((op) => op.invariantId).sort() expect(ids).toEqual([ 'cipherstash-codec:User.email:add-search-config:match@v1', 'cipherstash-codec:User.email:add-search-config:unique@v1', ]) }) - it('inlines per-flag remove ops when previously-flagged column is dropped', () => { + it('inlines per-flag remove ops when previously-flagged column is dropped', async () => { const prior = build({ User: userTable({ equality: true, freeTextSearch: true }), }) @@ -171,7 +172,8 @@ describe('planFieldEventOperations driving the cipherstash hook', () => { codecHooks, }) expect(ops).toHaveLength(2) - const ids = ops.map((c) => c.toOp().invariantId).sort() + const lowered = await Promise.all(ops.map((c) => c.toOp())) + const ids = lowered.map((op) => op.invariantId).sort() expect(ids).toEqual([ 'cipherstash-codec:User.email:remove-search-config:match@v1', 'cipherstash-codec:User.email:remove-search-config:unique@v1', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 216c8019a..97b2c71c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,66 +408,66 @@ importers: specifier: workspace:* version: link:../stack '@prisma-next/contract': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/family-sql': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/framework-components': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/migration-tools': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-contract': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-operations': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-relational-core': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-runtime': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/ts-render': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/utils': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) arktype: specifier: ^2.1.29 version: 2.2.0 devDependencies: '@prisma-next/adapter-postgres': - specifier: 0.13.0 - version: 0.13.0(typanion@3.14.0)(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/cli': - specifier: 0.13.0 - version: 0.13.0(typanion@3.14.0)(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typanion@3.14.0)(typescript@5.9.3) '@prisma-next/driver-postgres': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/extension-author-tools': specifier: 0.14.0 version: 0.14.0(typescript@5.9.3) '@prisma-next/psl-parser': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-contract-psl': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-contract-ts': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/sql-schema-ir': - specifier: 0.13.0 - version: 0.13.0(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typescript@5.9.3) '@prisma-next/target-postgres': - specifier: 0.13.0 - version: 0.13.0(typanion@3.14.0)(typescript@5.9.3) + specifier: 0.14.0 + version: 0.14.0(typanion@3.14.0)(typescript@5.9.3) pathe: specifier: ^2.0.3 version: 2.0.3 @@ -1803,15 +1803,6 @@ packages: '@posthog/types@1.373.5': resolution: {integrity: sha512-K7STCnRG/WBE1q0BwEkIcrJB5OqECaymsQj6Hp4Ntvaek4dqHkZGfp6hxwIPqQPjlOXwidwPLo+XGsn+CoZUyw==} - '@prisma-next/adapter-postgres@0.13.0': - resolution: {integrity: sha512-C3eYTekLZcRYSBS6VUgurgGYHKmuIYO0P5jRUWyWbELHJf4aMyKrRWJWkvog3+A/SMZUQsLaL0CFL/x7cWgNmA==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/adapter-postgres@0.14.0': resolution: {integrity: sha512-c90KFtI74WU+8YH3uhVg1DTmD10T2BpINzzys9F3n1jTM2Am0tXKx53V05Ev+7H1VBrjBoksZPZ92WWxVEXLew==} engines: {node: '>=24'} @@ -1821,15 +1812,6 @@ packages: typescript: optional: true - '@prisma-next/cli-telemetry@0.13.0': - resolution: {integrity: sha512-D7efYTh+XRLZNu4Sj3MQZaffn3odfikvGxQCkK/H3vf1Z9hlNZXi6b9LAVUNODgfp+M0L25lrvQeXoOnXYuy9w==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/cli-telemetry@0.14.0': resolution: {integrity: sha512-JaP/+09tIL6zH+5WQS507db2HQ0B47HRR2AZD8VHmsw/tMwBDZqKVdIA5KptPMVSR3pcnXDygGvG7E8N+PSPRg==} engines: {node: '>=24'} @@ -1839,16 +1821,6 @@ packages: typescript: optional: true - '@prisma-next/cli@0.13.0': - resolution: {integrity: sha512-bzVvhNlbS/Fh5WvTtUa0+L4wG/HmV4MugaTCDpZO4+MBbm5S8HdgloOP1dvjvchp4emZYLN4nu8x8Objqz3kEg==} - engines: {node: '>=24'} - hasBin: true - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/cli@0.14.0': resolution: {integrity: sha512-wN4mZc0K1m6wB4NXSEdrA2RCfc8zHBs5yJq0G7XoWQ8J8vvX/hR2kkukJv9QfEYFEIMyOT7vjbSBh2vj4NQ56g==} engines: {node: '>=24'} @@ -1859,15 +1831,6 @@ packages: typescript: optional: true - '@prisma-next/config@0.13.0': - resolution: {integrity: sha512-M9RmCMGS0K6bPvMwjgszntRQa+9z7t0ZZGcyYP36CJ1w3/IUeSGZ0VJJCzKE/8XFGi+QxD6lULK8tzJkXQvgzQ==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/config@0.14.0': resolution: {integrity: sha512-7U4Y5GeicvWy0dcg78J8arzqe13r97Zj8yB/e+Uea6M8LZxV2poW/afHlYnU0VXhLTZ4F29CdlWOUmCAKpl9mg==} engines: {node: '>=24'} @@ -1877,15 +1840,6 @@ packages: typescript: optional: true - '@prisma-next/contract-authoring@0.13.0': - resolution: {integrity: sha512-n+SwaTNJ+E/2cdQyShn67U5jJHMW/7CxaZf27oJDXqRzhNX2DthDJh955LGIbrQGQDc3zPvCL5nOD5oTU1MVpA==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/contract-authoring@0.14.0': resolution: {integrity: sha512-79aLJO04p/XRX2QvIlFMR2ZvbqvI0xFAU5idgYySoevq8bSY9ORGA0XchSbhFl0d6d1VfSEZowRPwzvgl/6lsg==} engines: {node: '>=24'} @@ -1895,15 +1849,6 @@ packages: typescript: optional: true - '@prisma-next/contract@0.13.0': - resolution: {integrity: sha512-QnLFmvHL6Z96J2ZRylo9YADjsY+yQUq2fVK/T3HWChbp9QaN9G2dymeugSJFjs72gbxtHJ86nsWONBZ8uAJUjg==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/contract@0.14.0': resolution: {integrity: sha512-HMeduqWGl1NpRyhEvsswF1T8PSBPfbpvMNYMegiYPt2ck5oClhbl0LHkgAIHsOjBg3IJv2VhRqDhQiuRVbkC4A==} engines: {node: '>=24'} @@ -1913,15 +1858,6 @@ packages: typescript: optional: true - '@prisma-next/driver-postgres@0.13.0': - resolution: {integrity: sha512-RClCCsjCq3iDtMzDCXwEdyX3fQakagZlBMOVyw230qhkRE8dBUUAmv6M1YBuGx6TVvgj1Q+PP7a+K10XXUCNJw==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/driver-postgres@0.14.0': resolution: {integrity: sha512-qla7/fAk2YqwomkUPevd9bIqJ5gQZcZTXbEoz8nJaErUunisEosepJOKwlTpquJV2+OAuezODCbrrYtUJ525jg==} engines: {node: '>=24'} @@ -1931,15 +1867,6 @@ packages: typescript: optional: true - '@prisma-next/emitter@0.13.0': - resolution: {integrity: sha512-oaELZkVVxYtO8r9SaFOb2ZrkQfhlMCnGOM8ZBLc7WbDSCvVjKxYmUCnSVQ2OVrrn5dadPRmn5s/XVNnD9t51AA==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/emitter@0.14.0': resolution: {integrity: sha512-qdyOe11AsXHMpVmPYulA/NjKEn59VkXekGrtg8lB+Pqd+Dg2HwQinaO6G1PXjymRyVgv31UKRyUU7eLhCQaZ0Q==} engines: {node: '>=24'} @@ -1949,15 +1876,6 @@ packages: typescript: optional: true - '@prisma-next/errors@0.13.0': - resolution: {integrity: sha512-dspObTEVD5FvDAg0KC3qDPdcsi1WprrAU733VOfD3sR3La5n9/KFACQPpleKUADvqQwex+sKuo+woMS4yiOjtQ==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/errors@0.14.0': resolution: {integrity: sha512-+VlDHQaP3AAs3rYYYSDoG5Ng5Hkyl+F4oAHAvDsvhFMupsGWaBx7esc5MbBHfnjE/5i/cSybR8/efXrIZJI6zA==} engines: {node: '>=24'} @@ -1977,15 +1895,6 @@ packages: typescript: optional: true - '@prisma-next/family-sql@0.13.0': - resolution: {integrity: sha512-PL2KNL05y4UcElFFYW1uPH9dPkRjqMzBArtbFjXP2XaTOiaRF8cX5/GNy3onPWq9ZhDbrmL1+vKYPfUls9yrrw==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/family-sql@0.14.0': resolution: {integrity: sha512-Gx4ByuilDiJzwsOkqWE42CSZrhO9/6uFDpyemD185eZsaJGk5tvEpbLy3UuLNx+6VEsvXfPYGLdGUy2LL8e9Pg==} engines: {node: '>=24'} @@ -1995,15 +1904,6 @@ packages: typescript: optional: true - '@prisma-next/framework-components@0.13.0': - resolution: {integrity: sha512-jyaU+A8ZMgowt+Hqb68slwh8jsn76HJ9eCFSbTGt5do88SvoSHWnhAkXVJy6oeprI15o0V/CmXOJ6a0ZtHRKNg==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/framework-components@0.14.0': resolution: {integrity: sha512-6ZPnesKake4pnRRQZUi67a5qfgs6Pc9cmNN2bAf2bQJYTg3w5b1yOhjg2sFupXT54hq/iE3rXRY5A7ltgSxijA==} engines: {node: '>=24'} @@ -2013,15 +1913,6 @@ packages: typescript: optional: true - '@prisma-next/ids@0.13.0': - resolution: {integrity: sha512-SMb6qFTiS22cK3npJaaFaXYJu8+/kGdhblitOq63hzeBWS7hxkf3LNNy8ugyS7Tep9x3q+k312qcqOVKiJRB2Q==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/ids@0.14.0': resolution: {integrity: sha512-E+Nhvgmco/Y24wiv/hWVt6wAf1J9sIS5qupoolBeL0V/BA/+zECewg/vCF2dj92hXk0jJ1WphhQDGrvx5XAPbQ==} engines: {node: '>=24'} @@ -2031,15 +1922,6 @@ packages: typescript: optional: true - '@prisma-next/migration-tools@0.13.0': - resolution: {integrity: sha512-csvsFLurOb8DzTpRgp2TIfl+orf+jtnvkEFNuK0A02bL2b/aN4G+qtqKpvHl4KAfF5DTsPuzf+l8sj2AarzWBw==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/migration-tools@0.14.0': resolution: {integrity: sha512-JdFJPO8qFA3N0lP6g/ZsRz4QGeDV/qQpRUWq+9phoAbL/kBaMha838JG5YUb6h8z3tD4/qg+Qs6ESZPURD4AcQ==} engines: {node: '>=24'} @@ -2049,15 +1931,6 @@ packages: typescript: optional: true - '@prisma-next/operations@0.13.0': - resolution: {integrity: sha512-Wj3SPPYAZGgJ8thpeRoshqhlJSjOTruYgppjSy/eHgU91NgFMB1smeUXZAlkZK7062mGDJQabZBPoiZdRlcO5A==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/operations@0.14.0': resolution: {integrity: sha512-h53frkeEYalkixESJvkCvvuELD5b3YnHdkqkBK1A6XyDoqFusEHpjlP4LMXhpAQlft6QdbgYZfttm9ZXBFBUQw==} engines: {node: '>=24'} @@ -2076,15 +1949,6 @@ packages: typescript: optional: true - '@prisma-next/psl-parser@0.13.0': - resolution: {integrity: sha512-HhdtdeL7gE5Pq1vj+o/zJBzeZ1te9vOECtkg+w7gcOrTLyagQSrC+SMg04wT5vnehCiQPq43Knd7i3GrCucX/w==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/psl-parser@0.14.0': resolution: {integrity: sha512-XKumV4Bfy3KHoD3IsWVSDtzFjXKL5zENASbbfIXXpwll3AVgU4rtVKuJIH5fwUOYpcCUdLE8ZaNw6vcwLuse3w==} engines: {node: '>=24'} @@ -2094,15 +1958,6 @@ packages: typescript: optional: true - '@prisma-next/psl-printer@0.13.0': - resolution: {integrity: sha512-LijrxXOnY6dHBHl1kSNcJL+CmXsZL61Z4VLXqbev/C+shtBzqcIlesG8DuUwv8Zv07eIKYc4SUpSqxs+NiSMKQ==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/psl-printer@0.14.0': resolution: {integrity: sha512-qVTjwpO9uvITSsMg/mtdk0Py7ITsQRpPjmidSi9ByoPE6OGBGF3/BaUbOjRiZ340i39Npg83jT4rLreH1GOFjg==} engines: {node: '>=24'} @@ -2121,15 +1976,6 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-emitter@0.13.0': - resolution: {integrity: sha512-JcsvIKEoRToxAQ15h7onVqfnbiPJrU0WYJ+qude4rPY6ZVirhT/gpK9tXuNH2zIYKK4XPTthvOwlcOnZQcR1Aw==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-contract-emitter@0.14.0': resolution: {integrity: sha512-gUgctyFNMKZ94wXNATevMi+FG9R9SK4yYQesYr7/5kZ4ZBbHkaFOFcWwlJZSI+EhCC2J1OgvfaCTtpWpn+VIUw==} engines: {node: '>=24'} @@ -2139,15 +1985,6 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-psl@0.13.0': - resolution: {integrity: sha512-Z85MB7IgmHJ6fIwPfpNj6dLhT3zZGClAIunTvpRhvcnlEkP2BpzBDsTFY8FrdFwUQXYVrRY9qNsimlj7k4nNBA==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-contract-psl@0.14.0': resolution: {integrity: sha512-86Z8MWo0NYhGSX4uOqbvAx46aMOaB2opNyUeFNnntT1br+U48IX2ZPGV62ul6mwCp7DGN7pjvmlBW5OnHXuiyg==} engines: {node: '>=24'} @@ -2157,15 +1994,6 @@ packages: typescript: optional: true - '@prisma-next/sql-contract-ts@0.13.0': - resolution: {integrity: sha512-zgsdxm2i8CuIQdW3MBo4kKlBy0T0ARDLmbLE/l75rm20AdEgdUUdyPZrdtRaFm/4PAUCeyct14sAqG05HqBzDw==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-contract-ts@0.14.0': resolution: {integrity: sha512-AMTURIMXCsn+M7oW0QOe/9wS7J+Q1siW7+aihgtmBj88lYav9XHyxJO5AJDu6LSDSctorTzccfsPatBAlcxzKQ==} engines: {node: '>=24'} @@ -2175,15 +2003,6 @@ packages: typescript: optional: true - '@prisma-next/sql-contract@0.13.0': - resolution: {integrity: sha512-ZlFIpdndckKR3Vm//JNXqe7LTDg7JY4i7iHyy9ri9D6FDDHgr1jg+BEPxduvW2VjqVOBoZKw3hIxpM4ZBLP0Lg==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-contract@0.14.0': resolution: {integrity: sha512-/Ibvjd41sEgpi4c3YCZvK6vRCucNffsIZ+isIOYoVqXOt74H0llM4Ux4mR0PShNuo0jgG8xOon1ptSQ6/S7c+w==} engines: {node: '>=24'} @@ -2193,15 +2012,6 @@ packages: typescript: optional: true - '@prisma-next/sql-errors@0.13.0': - resolution: {integrity: sha512-0chEMpBWFNCHSUmnkP2iGo3CTN/lfTYy4eVV6DMg6Np9CFmGk+VMkwrieN7bRpbtTNc27cjT4s+4EK3UY5B71A==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-errors@0.14.0': resolution: {integrity: sha512-zc5U+SzEUqpjPC69w9JejqAkcRtkZ8BSm1a6l6p4pT4b8dk/iXrfQ96oHoy6SYG/dTbGe47W4gCfX9LCzCdUlA==} engines: {node: '>=24'} @@ -2211,15 +2021,6 @@ packages: typescript: optional: true - '@prisma-next/sql-operations@0.13.0': - resolution: {integrity: sha512-+7Kr/99kePZ1mxLZLVo5N9awm39lG3mqln6H2d8Gn2poQP3j+oRJVXAVF4LjHvFyfT219NwKREr42+42sh/UaA==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-operations@0.14.0': resolution: {integrity: sha512-0VeVceyh7LAlEGMseFPQrmoBPkNAlBiCOCyWzv1xQjjU0IoPkMlcjFu07wD5aLf+nwBzEUSbdg9EaTdBTYHO9Q==} engines: {node: '>=24'} @@ -2238,15 +2039,6 @@ packages: typescript: optional: true - '@prisma-next/sql-relational-core@0.13.0': - resolution: {integrity: sha512-uTfH2jPfZWS78SgIYu7ofEEWHYQUXiCwSITQPB4Hl9QEkzWY7WjRCOtHNBjO1rH7VXw678adzPDHk6Hl70HZtw==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-relational-core@0.14.0': resolution: {integrity: sha512-7H1HaN31lmGD81C9vz07dq/r5n80yKXfFNpYj5XrKKeVpDZQSb/IXtOVt7LyB+q9QqSUW2jD5RGLuTOhsd2kbw==} engines: {node: '>=24'} @@ -2256,15 +2048,6 @@ packages: typescript: optional: true - '@prisma-next/sql-runtime@0.13.0': - resolution: {integrity: sha512-+sy4pf83HzkGU/6SrAtC9KbhuqjGqgdcZa87wFIN+oMFgqMnvz2sPFxKg3SEQIwNOBS9hFiAiX9AZqFgBjlOhQ==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-runtime@0.14.0': resolution: {integrity: sha512-HV8ZAvNuXtFrAHuxs3/ZEz0JapKFayFYDljQeVDFxExSEj79fPtd9CON9sTTV4T/RKII5knlKJRuKtwWH35Znw==} engines: {node: '>=24'} @@ -2274,15 +2057,6 @@ packages: typescript: optional: true - '@prisma-next/sql-schema-ir@0.13.0': - resolution: {integrity: sha512-v8CJKAcaqmBaBcq0tamf7YxDL87AMPRAUhFIZA59EoJjvQSkSa1anlhT1vKqkON7Y03DK4powm/8fC9UhWkhzw==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/sql-schema-ir@0.14.0': resolution: {integrity: sha512-5buE3jcTz3yyyve4demaACIv52YT09tFPCBEqwmxG1ss8KtybVEgaZg8UUYGmyYbWU0l5RpQ96/vuB5UTFE4nw==} engines: {node: '>=24'} @@ -2292,15 +2066,6 @@ packages: typescript: optional: true - '@prisma-next/target-postgres@0.13.0': - resolution: {integrity: sha512-3AyuAOusVy9/bUwovgkoFOBf0h8G50O3SI6+FEead6BD9teCMUE2oWJ/3rnSYQbE0bljE0n9eW7sHQ67iP4XSQ==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/target-postgres@0.14.0': resolution: {integrity: sha512-i1H9Jzt7ZGgYQ61rlmalyEPY1RoLFIMk9+qE5WbETjQ8LY9p7iXeTx4bBv25twkYIAHxXtrYxsQ3klUWg0JfVg==} engines: {node: '>=24'} @@ -2310,15 +2075,6 @@ packages: typescript: optional: true - '@prisma-next/ts-render@0.13.0': - resolution: {integrity: sha512-rDOJocWAKUSoheEH1PWZFAxQyHRWbq6bAqKDkA3M5QTGDK1iBFNdgA3Ep9kkUt50vMHHJt1A5+h/FBCgpGgROA==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/ts-render@0.14.0': resolution: {integrity: sha512-ub25pEfUe2dLpNu/wR0mlBBMZ87eGmyV5D9GS5KmyHkofjJdiE2Y3BpIhHXf7wjiYp35z/gOyQ2vuPFS4xVBgQ==} engines: {node: '>=24'} @@ -2328,15 +2084,6 @@ packages: typescript: optional: true - '@prisma-next/utils@0.13.0': - resolution: {integrity: sha512-ldrgWOIMf3bYXRmtLDNzPSXo5lVluJZ+p1Ly6cKHhJo5RDM5TrFfa6hDC5E/fB+gbUlh1mVZ45iZKjqqRqVr7w==} - engines: {node: '>=24'} - peerDependencies: - typescript: '>=5.9' - peerDependenciesMeta: - typescript: - optional: true - '@prisma-next/utils@0.14.0': resolution: {integrity: sha512-YIV6FMdKKsJqLkG2h9tQDzEoeAFjtniNXcB0XnyZyD1tkALodiWsRymuQJBGcFZB7S8icrOvRaqdSUNdrrR6yw==} engines: {node: '>=24'} @@ -5325,30 +5072,6 @@ snapshots: '@posthog/types@1.373.5': {} - '@prisma-next/adapter-postgres@0.13.0(typanion@3.14.0)(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/contract-authoring': 0.13.0(typescript@5.9.3) - '@prisma-next/errors': 0.13.0(typescript@5.9.3) - '@prisma-next/family-sql': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/ids': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract-psl': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract-ts': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-runtime': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-schema-ir': 0.13.0(typescript@5.9.3) - '@prisma-next/target-postgres': 0.13.0(typanion@3.14.0)(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - magicast - - typanion - '@prisma-next/adapter-postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5373,18 +5096,6 @@ snapshots: - magicast - typanion - '@prisma-next/cli-telemetry@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/config': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - c12: 3.3.4 - pathe: 2.0.3 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - magicast - '@prisma-next/cli-telemetry@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/config': 0.14.0(typescript@5.9.3) @@ -5397,38 +5108,6 @@ snapshots: transitivePeerDependencies: - magicast - '@prisma-next/cli@0.13.0(typanion@3.14.0)(typescript@5.9.3)': - dependencies: - '@clack/prompts': 1.4.0 - '@prisma-next/cli-telemetry': 0.13.0(typescript@5.9.3) - '@prisma-next/config': 0.13.0(typescript@5.9.3) - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/emitter': 0.13.0(typescript@5.9.3) - '@prisma-next/errors': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/migration-tools': 0.13.0(typescript@5.9.3) - '@prisma-next/psl-printer': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - c12: 3.3.4 - ci-info: 4.4.0 - clipanion: 4.0.0-rc.4(typanion@3.14.0) - closest-match: 1.3.3 - colorette: 2.0.20 - commander: 14.0.3 - esbuild: 0.28.1 - jsonc-parser: 3.3.1 - package-manager-detector: 1.6.0 - pathe: 2.0.3 - string-width: 8.2.1 - strip-ansi: 7.2.0 - wrap-ansi: 10.0.0 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - magicast - - typanion - '@prisma-next/cli@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@clack/prompts': 1.4.0 @@ -5461,15 +5140,6 @@ snapshots: - magicast - typanion - '@prisma-next/config@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/config@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5479,26 +5149,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract-authoring@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/contract-authoring@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@prisma-next/contract@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - '@standard-schema/spec': 1.1.0 - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/contract@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/utils': 0.14.0(typescript@5.9.3) @@ -5507,24 +5163,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/driver-postgres@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/errors': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-errors': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - pg: 8.21.0 - pg-cursor: 2.21.0(pg@8.21.0) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - pg-native - '@prisma-next/driver-postgres@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5543,18 +5181,6 @@ snapshots: transitivePeerDependencies: - pg-native - '@prisma-next/emitter@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/operations': 0.13.0(typescript@5.9.3) - '@prisma-next/ts-render': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - prettier: 3.8.3 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/emitter@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5567,13 +5193,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/errors@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/errors@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) @@ -5585,25 +5204,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/family-sql@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/emitter': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/migration-tools': 0.13.0(typescript@5.9.3) - '@prisma-next/operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract-emitter': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract-ts': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-runtime': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-schema-ir': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/family-sql@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5623,17 +5223,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/framework-components@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/operations': 0.13.0(typescript@5.9.3) - '@prisma-next/ts-render': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - '@standard-schema/spec': 1.1.0 - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/framework-components@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5645,15 +5234,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/ids@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - uniku: 0.0.12 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/ids@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5663,17 +5243,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/migration-tools@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - pathe: 2.0.3 - prettier: 3.8.3 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/migration-tools@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5685,10 +5254,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/operations@0.13.0(typescript@5.9.3)': - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/operations@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -5720,13 +5285,6 @@ snapshots: - pg-native - typanion - '@prisma-next/psl-parser@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/psl-parser@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) @@ -5734,13 +5292,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/psl-printer@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/psl-printer@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/framework-components': 0.14.0(typescript@5.9.3) @@ -5758,16 +5309,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-emitter@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/emitter': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-contract-emitter@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5778,19 +5319,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-psl@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/config': 0.13.0(typescript@5.9.3) - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/psl-parser': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract-ts': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - pathe: 2.0.3 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-contract-psl@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/config': 0.14.0(typescript@5.9.3) @@ -5804,20 +5332,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract-ts@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/config': 0.13.0(typescript@5.9.3) - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/contract-authoring': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - pathe: 2.0.3 - ts-toolbelt: 9.6.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-contract-ts@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/config': 0.14.0(typescript@5.9.3) @@ -5832,15 +5346,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-contract@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-contract@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5850,22 +5355,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-errors@0.13.0(typescript@5.9.3)': - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-errors@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-operations@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-operations@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/operations': 0.14.0(typescript@5.9.3) @@ -5888,20 +5381,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-relational-core@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - '@standard-schema/spec': 1.1.0 - arktype: 2.2.0 - ts-toolbelt: 9.6.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-relational-core@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5916,20 +5395,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-runtime@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/ids': 0.13.0(typescript@5.9.3) - '@prisma-next/operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - arktype: 2.2.0 - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-runtime@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5944,13 +5409,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/sql-schema-ir@0.13.0(typescript@5.9.3)': - dependencies: - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/sql-schema-ir@0.14.0(typescript@5.9.3)': dependencies: '@prisma-next/contract': 0.14.0(typescript@5.9.3) @@ -5958,30 +5416,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@prisma-next/target-postgres@0.13.0(typanion@3.14.0)(typescript@5.9.3)': - dependencies: - '@prisma-next/cli': 0.13.0(typanion@3.14.0)(typescript@5.9.3) - '@prisma-next/contract': 0.13.0(typescript@5.9.3) - '@prisma-next/errors': 0.13.0(typescript@5.9.3) - '@prisma-next/family-sql': 0.13.0(typescript@5.9.3) - '@prisma-next/framework-components': 0.13.0(typescript@5.9.3) - '@prisma-next/migration-tools': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-contract': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-errors': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-operations': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-relational-core': 0.13.0(typescript@5.9.3) - '@prisma-next/sql-schema-ir': 0.13.0(typescript@5.9.3) - '@prisma-next/ts-render': 0.13.0(typescript@5.9.3) - '@prisma-next/utils': 0.13.0(typescript@5.9.3) - '@standard-schema/spec': 1.1.0 - arktype: 2.2.0 - pathe: 2.0.3 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - magicast - - typanion - '@prisma-next/target-postgres@0.14.0(typanion@3.14.0)(typescript@5.9.3)': dependencies: '@prisma-next/cli': 0.14.0(typanion@3.14.0)(typescript@5.9.3) @@ -6006,18 +5440,10 @@ snapshots: - magicast - typanion - '@prisma-next/ts-render@0.13.0(typescript@5.9.3)': - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/ts-render@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@prisma-next/utils@0.13.0(typescript@5.9.3)': - optionalDependencies: - typescript: 5.9.3 - '@prisma-next/utils@0.14.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 From d6d23bebbf803bb3e8a44ca985f4c759aea124bc Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:18:23 -0600 Subject: [PATCH 09/15] docs(prisma-next): namespace-qualified ORM accessors in README + changeset for the 0.14 upgrade Prisma Next 0.14 removed the flat orm. accessors; the README examples now go through db.orm.public.User. Co-Authored-By: Claude Fable 5 --- .changeset/prisma-next-0-14.md | 12 ++++++++++++ packages/prisma-next/README.md | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/prisma-next-0-14.md diff --git a/.changeset/prisma-next-0-14.md b/.changeset/prisma-next-0-14.md new file mode 100644 index 000000000..f1ad6d5a5 --- /dev/null +++ b/.changeset/prisma-next-0-14.md @@ -0,0 +1,12 @@ +--- +'@cipherstash/prisma-next': minor +--- + +Upgrade to Prisma Next 0.14.0 (from 0.8.0). Every `@prisma-next/*` dependency is now pinned at 0.14.0; consuming apps must run Prisma Next 0.14 to use this release. + +Highlights of the upgrade: + +- The extension contract space is re-emitted in the 0.14 canonical shape: storage is namespace-enveloped (`storage.namespaces.public.entries.table`), the domain plane replaces flat `models`, and the baseline EQL-install migration is re-pinned to the new storage hash. The vendored EQL bundle SQL is unchanged byte-for-byte. +- `deriveStackSchemas` reads the namespace-enveloped contract shape emitted by Prisma Next 0.10+. +- The bulk-encrypt middleware accepts the widened insert/update AST value unions introduced through 0.9–0.11. +- README examples use the namespace-qualified ORM accessors (`db.orm.public.User`) required since Prisma Next 0.14. diff --git a/packages/prisma-next/README.md b/packages/prisma-next/README.md index ba82820ce..6795240c2 100644 --- a/packages/prisma-next/README.md +++ b/packages/prisma-next/README.md @@ -71,13 +71,13 @@ npx prisma-next migration apply # installs EQL bundle + your schema ```typescript import { EncryptedString, decryptAll } from "@cipherstash/prisma-next/runtime" -await db.orm.User.create({ +await db.orm.public.User.create({ id: "user-0", email: EncryptedString.from("alice@example.com"), // ... }) -const rows = await db.orm.User +const rows = await db.orm.public.User .where((u) => u.email.cipherstashIlike("%@example.com")) .all() From 44d21a3bb26cfc5e254622a84fd2a8bed8d5bedb Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 9 Jul 2026 18:45:46 +1000 Subject: [PATCH 10/15] docs(prisma-next): EQL v3 concrete per-domain design (design A) Design for authoring EQL v3 columns in @cipherstash/prisma-next via concrete per-domain constructors that map 1:1 to the public.* Postgres domains, with capabilities encoded in the type system (no boolean-option surface, no resolver). Reflects protect-ffi 0.28: public.* domains + eql_v3.* operator functions, first-class BigInt family, DOMAIN_REGISTRY as source of truth, separate v2/v3 entry points, and concrete per-domain types threaded end-to-end. --- .../2026-07-08-eql-v3-prisma-next-design.md | 466 ++++++++++++++++++ 1 file changed, 466 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md diff --git a/docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md b/docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md new file mode 100644 index 000000000..bb294fea1 --- /dev/null +++ b/docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md @@ -0,0 +1,466 @@ +# EQL v3 Prisma-next Default Design + +Status: proposed +Date: 2026-07-08 +Revised: 2026-07-08 (design A — capabilities encoded in the type system; per-domain constructors; boolean-option resolver removed) +Revised: 2026-07-09 (audit vs `feat/eql-v3-supabase-adapter`: domains are `public.*` / operator functions stay `eql_v3.*`; BigInt is now a first-class v3 family; boolean domain is `public.boolean`; catalog derived from `DOMAIN_REGISTRY`) +Revised: 2026-07-09b (review remediation: **concrete per-domain types are the spine** — see [Type Discipline]; v2/v3 are **separate entry points**, no in-one-client mixing (bare names are v3, `*V2` is legacy — a breaking major, no transition guard); encrypted ORDER BY (`asc`/`desc` → `eql_v3.ord_term`) in scope; `between` parenthesisation + `bigintSafeReplacer` are correctness invariants; framework-neutral primitives (codec, gate index-sets, fn-schema/op-map/paren-rule) lifted to `eql/v3/`; tests require a plaintext oracle, real properties, full-string operator pins, and mandatory `*.test-d.ts`) +Target package: `@cipherstash/prisma-next` +Reference branches: +- `feat/eql-v3-supabase-adapter` (base) for the current FFI 0.28 concrete domain catalog (`public.*` domains, `eql_v3.*` operator functions), the `DOMAIN_REGISTRY` source of truth, and the canonical v3 adapter shape (Supabase is PostgREST, so its operator model does not port; use it for catalog/derivation/wire-format, not for SQL operator lowering). +- `eql-v3-drizzle-concrete-types` for the canonical **SQL** integration shape, operator lowering (two-arg `eql_v3.*(...::jsonb)`), and plain-jsonb v3 wire format — this is the model prisma-next mirrors. +- Operator SQL is proven live in `packages/stack/__tests__/v3-matrix/matrix-live-pg.test.ts` and `schema-v3-pg.test.ts`. + +## Goal + +Change `@cipherstash/prisma-next` so that EQL v3 columns are authored through **concrete per-domain constructors** that map 1:1 to the `eql_v3.*` Postgres domains. Capability is encoded in the type system: the constructor you choose *is* the capability set. There is no boolean-option surface and no runtime domain-resolution stage. + +This is the original intent of the v3 work. EQL v3 makes each capability combination its own Postgres domain (`public.text_eq`, `public.text_search`, …), so capability is a property of the type, not runtime configuration. The authoring surface mirrors that: one constructor per exposed domain, each carrying a static codec id. + +> **Schema note (protect-ffi 0.28).** Two different Postgres schemas are in play and must not be conflated: +> - **Domains / native types live in `public`** — `public.text_eq`, `public.integer_ord`, `public.boolean`, `public.bigint_ord`. The stack catalog pins this at the type level: `eqlType: \`public.${string}\``. This is the column's `nativeType`. +> - **Operator functions live in `eql_v3`** — `eql_v3.eq(...)`, `eql_v3.gt(...)`, `eql_v3.contains(...)`, `eql_v3.ord_term(...)`. This is the SQL the operators lower to. +> +> Wherever this spec refers to a *domain* it is `public.*`; wherever it refers to an *operator function* it is `eql_v3.*`. The `cipherstash/eql-v3/...` codec-id namespace is a logical version tag, unrelated to either Postgres schema. + +EQL v2 remains available only through explicit legacy `*V2` names where a compatibility path is needed. + +### Why not the boolean-option surface + +The v2 authoring surface (`EncryptedString({ equality, freeTextSearch, orderAndRange })`) exists because v2 has a **single** native type (`eql_v2_encrypted`) shared by all codecs, with searchability layered on at migration time via `add_search_config` / `remove_search_config`. In v2, capability is runtime configuration applied to an opaque encrypted blob, and the booleans are the front-end for that config. + +v3 deletes that model: query behavior is encoded by the concrete domain, and v3 columns must **not** emit `add_search_config` / `remove_search_config` (see [Migrations](#migrations)). Carrying the boolean surface into v3 would require a resolution stage to translate booleans into a domain and to reject the impossible combinations at runtime — reintroducing a v2 configuration model on top of a v3 type model, and degrading compile-time guarantees into runtime throws. Design A drops the booleans so that: + +- **Illegal capability combinations are unrepresentable**, not runtime errors. There is no `EncryptedTextEqMatch()` constructor because there is no `public.text_eq_match` domain, so the combination simply cannot be authored. +- **The codec id is static and known at authoring time.** `(codec id) ⟺ (eql_v3.* domain) ⟺ capabilities` is bijective; nothing is resolved. +- **Autocomplete lists the valid domains.** Discoverability comes from the constructor set, not from a checkbox matrix where most combinations throw. + +## Type Discipline (the spine) + +Concrete per-domain types are the **core** of EQL v3 and MUST be preserved end-to-end — from the imported catalog, through authoring and the contract descriptor, into derivation and the stack client. This is the load-bearing invariant of the whole design, not a convenience mirrored from another adapter. A v3 implementation that widens columns to a single `AnyEncryptedV3Column`, or descriptors to `{ codecId: string; capabilities: unknown }`, has discarded the exact thing v3 exists to encode. Every task must be specified so the types flow; "derive it like Drizzle" is not a specification. + +Requirements (each is independently type-tested): + +- **Distinct type per domain.** `encryptedBigIntOrd()` and `encryptedText()` have *different* static types. Two factories for two different domains never share a type. +- **Literal codec id.** A descriptor's `codecId` is a member of the generated literal union `CipherstashV3CodecId` (e.g. `'cipherstash/eql-v3/text_eq@1'`), never `string`. +- **Literal native type.** `nativeType` is the concrete `` `public.${Domain}` `` literal (e.g. `'public.text_eq'`), never widened to `string` or bare `` `public.${string}` ``. +- **Concrete capabilities.** `capabilities` is the domain's literal `QueryCapabilities` (e.g. `{ equality: true; orderAndRange: false; freeTextSearch: false }`), never `unknown` or a widened `QueryCapabilities` that loses the boolean literals. +- **Derivation preserves identity.** `deriveStackSchemasV3` maps each column to its concrete stack factory + column class (`types.TextEq` → `EncryptedTextEqColumn`) and threads that concrete type into `encryptedTableV3`. It MUST NOT widen to `AnyEncryptedV3Column` before building the table — that erases the per-column plaintext / capability inference the stack factories exist to provide. +- **Type-level tests are mandatory** (`*.test-d.ts`), not optional. They pin that the factories are mutually non-assignable, that `codecId` / `nativeType` / `capabilities` are the literal types (not their widenings), and that derivation round-trips the concrete column type. A runtime-only suite does not protect this invariant. + +The stack already carries these types — each `types.*` factory returns a concrete `EncryptedXColumn` with literal `eqlType` / `castAs` / `capabilities`. prisma-next's job is to *thread them through generically*, never to collapse them to a uniform descriptor. + +## Non-Goals + +- Do not add a boolean-capability authoring surface or a domain-resolution stage. Capability is chosen by constructor identity. +- Do not expose `*_ord_ore` variants in this release. The catalog defines them (`EncryptedTextOrdOreColumn`, etc.), but prisma-next surfaces only the `_ord` order domains. `_ord_ore` remains unexposed. +- Do not use `encryptQuery` or v3 term-only operands in Prisma-next. The canonical Drizzle branch uses full encrypted operands and public two-arg SQL functions. +- Do not invent domains that the FFI catalog does not expose. Searchable JSON is not in the current v3 catalog, so there is **no** v3 JSON constructor. (BigInt **is** in the catalog as of protect-ffi 0.28 — `public.bigint` / `bigint_eq` / `bigint_ord` / `bigint_ord_ore` — and is exposed as a first-class v3 family; see [Constructor Inventory](#constructor-inventory).) +- Do not hand-maintain a local copy of the v3 domain catalog. Import it and derive the constructor set, codec ids, capabilities, and cast kinds from it (see [Source of Truth](#source-of-truth)). +- Do not cross the v2 and v3 implementations. v3 is a self-contained namespace; do not branch v2 code paths with `if (isV3)` wire-format switches (see [Namespace Separation](#namespace-separation)). + +## Namespace Separation + +v3 is implemented as a parallel, self-contained namespace, not as v3-aware branches grafted onto the v2 code. This is a hard constraint, not a stylistic preference: the two versions have different wire formats, clients, codecs, migrations, and query lowering, and entangling them is exactly how cross-version leaks (e.g. an `instanceof` dispatch matching the wrong version) and ambiguous contracts arise. + +Rules: + +- **Separate module subtree.** All v3 implementation lives under a dedicated namespace — `src/v3/` for codecs/middleware/operators/adapter and `extension-metadata/constants-v3.ts` for ids/traits/invariants. v3 modules must not import v2 codec, wire (`encodeEqlV2EncryptedWire` / composite-literal), middleware, or adapter modules. +- **Separate everything on the wire/runtime path.** Distinct v3 codecs, v3 bulk-encrypt middleware, v3 operator lowering, v3 SDK adapter, and the v3 `EncryptionV3` client. No v2 function gains a v3 branch. +- **Separate entry points, not a shared dispatch.** v2 and v3 are distinct extension factories with distinct extension ids, registered one-per-client (the framework `OperationRegistry` is a flat method-keyed map that disallows override, so two descriptors sharing `cipherstash*` method names cannot co-register). There is no runtime codec-id-namespace router inside a single client, and mixed v2+v3 columns in one client are not supported this release (see [Schema Derivation](#schema-derivation-and-stack-adapter)). Each version flows through fully isolated code from authoring to client. +- **Permitted shared primitives.** Only genuinely version-neutral, presentation-layer primitives may be shared: the `EncryptedEnvelopeBase` class and framework-level authoring/trait infrastructure. Where a v3 column reuses a user-facing value type, it is surfaced through the v3 barrel and must not pull in any v2 wire/codec code. + +## Source of Truth + +The v3 domain catalog comes from `@cipherstash/stack/eql/v3` after the FFI sync. Prisma-next must **import** this catalog and derive the constructor set, domain names, plaintext cast kinds, capabilities, and codec ids from it. Do not duplicate it as a local table. + +The catalog exports a `types` namespace of per-domain factories (`packages/stack/src/eql/v3/types.ts`), each backed by a concrete column class, plus a `DOMAIN_REGISTRY` (`packages/stack/src/eql/v3/domain-registry.ts`) mapping the **bare** (unqualified) Postgres domain name to its factory: + +``` +types.Text, types.TextEq, types.TextMatch, types.TextOrd, types.TextOrdOre, types.TextSearch +types.Integer, types.IntegerEq, types.IntegerOrd, types.IntegerOrdOre +types.Smallint, types.SmallintEq, types.SmallintOrd, types.SmallintOrdOre +types.Bigint, types.BigintEq, types.BigintOrd, types.BigintOrdOre +types.Numeric, types.NumericEq, types.NumericOrd, types.NumericOrdOre +types.Real, types.RealEq, types.RealOrd, types.RealOrdOre +types.Double, types.DoubleEq, types.DoubleOrd, types.DoubleOrdOre +types.Date, types.DateEq, types.DateOrd, types.DateOrdOre +types.Timestamp, types.TimestampEq, types.TimestampOrd, types.TimestampOrdOre +types.Boolean +``` + +Catalog constraints: + +1. There is no searchable v3 JSON domain. BigInt **is** present (`public.bigint` / `bigint_eq` / `bigint_ord` / `bigint_ord_ore`, `cast_as: 'bigint'`, plaintext = JS `bigint`). +2. `public.boolean` (`types.Boolean`) is storage-only. It does not advertise equality; there is only one boolean domain (no `boolean_eq`). +3. The `*OrdOre` factories exist but are **not** exposed by prisma-next this release (this now includes `types.BigintOrdOre`). + +Reuse technique: import `DOMAIN_REGISTRY` / `factoryForDomain` / `stripDomainSchema` from `@cipherstash/stack/eql/v3` and iterate the registry. Each registry key is the bare domain name; each value is a factory that builds a concrete column whose `getEqlType()` returns the `public.`-qualified type, `getQueryCapabilities()` returns `{ equality, orderAndRange, freeTextSearch }`, and `build()` returns `{ cast_as, indexes }`. Derive the codec id from the bare name (`cipherstash/eql-v3/${bareName}@1`); derive `nativeType` from `getEqlType()` (already `public.*`); use `stripDomainSchema()` when you need the bare name from a `public.`-qualified type. The derivation *is* the catalog; there is no separate local table to pin. Retain one drift test only for the FFI-catalog invariants that are not structural (no searchable JSON domain; `public.boolean` is storage-only; `*OrdOre` intentionally unexposed). + +> Note: `getEqlType()` is metadata only — `build()` emits `cast_as` + `indexes` and never the domain name. `integer_ord` and `integer_ord_ore` build byte-identically; they differ **only** in `getEqlType()` (the `public.*` domain). This is why override/derivation validation must compare exact domain identity, not just `cast_as` + index keys. + +## Public Authoring Surface + +One constructor per exposed v3 domain, mirroring the catalog `types.*` names with an `Encrypted` prefix. Each constructor has a static codec id; the constructor name is the capability. + +### Constructor Inventory + +All constructors below are **new** — none of the v3 constructors reuses a v2 name for v3 behavior. Capability is read from the constructor: + +| Family | Storage-only | `+ equality` | `+ equality + order/range` | `+ free-text` | full | +| --- | --- | --- | --- | --- | --- | +| Text | `EncryptedText` | `EncryptedTextEq` | `EncryptedTextOrd` | `EncryptedTextMatch` | `EncryptedTextSearch` | +| Integer | `EncryptedInteger` | `EncryptedIntegerEq` | `EncryptedIntegerOrd` | — | — | +| Smallint | `EncryptedSmallint` | `EncryptedSmallintEq` | `EncryptedSmallintOrd` | — | — | +| BigInt | `EncryptedBigInt` | `EncryptedBigIntEq` | `EncryptedBigIntOrd` | — | — | +| Numeric | `EncryptedNumeric` | `EncryptedNumericEq` | `EncryptedNumericOrd` | — | — | +| Real | `EncryptedReal` | `EncryptedRealEq` | `EncryptedRealOrd` | — | — | +| Double | `EncryptedDouble` | `EncryptedDoubleEq` | `EncryptedDoubleOrd` | — | — | +| Date | `EncryptedDate` | `EncryptedDateEq` | `EncryptedDateOrd` | — | — | +| Timestamp | `EncryptedTimestamp` | `EncryptedTimestampEq` | `EncryptedTimestampOrd` | — | — | +| Boolean | `EncryptedBoolean` (storage-only) | — | — | — | — | + +Notes: + +- `EncryptedTextOrd` maps to `public.text_ord` (equality + order/range). `EncryptedTextSearch` maps to `public.text_search` (equality + free-text + order/range). `public.text_ord_ore` is not exposed. +- Scalar `*Ord` (including `EncryptedBigIntOrd`) maps to the `public.*_ord` domain (equality + order/range); `*_ord_ore` is not exposed. +- `EncryptedBigInt*` maps to `public.bigint` / `bigint_eq` / `bigint_ord` (`cast_as: 'bigint'`, plaintext = JS `bigint`). `EncryptedBigIntOrdOre` is not exposed. +- `EncryptedBoolean` maps to storage-only `public.boolean`. There is no boolean equality constructor until the FFI exposes a boolean equality domain. +- There is **no** `EncryptedJson` v3 constructor. The name does not exist on the v3 surface; use `EncryptedJsonV2` (see [JSON](#json)). + +The constructor set is **derived from the catalog**, not hand-listed: iterate the exposed `types.*` factories (excluding `*OrdOre`) and generate one authoring constructor per factory. Adding a domain to the catalog adds a constructor automatically; removing one removes it. + +### PSL / TS surface + +```prisma +model User { + email cipherstash.EncryptedTextSearch() // eq + free-text + order/range + name cipherstash.EncryptedTextEq() // equality only + notes cipherstash.EncryptedText() // storage only + age cipherstash.EncryptedIntegerOrd() // eq + order/range + tier cipherstash.EncryptedInteger() // storage only + balance cipherstash.EncryptedBigIntOrd() // eq + order/range (JS bigint) + birthday cipherstash.EncryptedDateEq() // equality only + createdAt cipherstash.EncryptedTimestampOrd() // eq + order/range + active cipherstash.EncryptedBoolean() // storage only +} +``` + +The TS factories mirror the PSL constructors 1:1 (camelCase): + +```ts +encryptedTextSearch() +encryptedTextEq() +encryptedText() +encryptedIntegerOrd() +encryptedInteger() +encryptedBigIntOrd() +encryptedDateEq() +encryptedTimestampOrd() +encryptedBoolean() +``` + +Legacy v2 is explicit and unchanged: + +```prisma +legacyEmail cipherstash.EncryptedStringV2() +legacyData cipherstash.EncryptedJsonV2() +``` + +```ts +encryptedStringV2() +encryptedJsonV2() +``` + +### Capability semantics per domain + +The capability a constructor grants is defined by its domain's `getQueryCapabilities()`, not by any local flag: + +- **Storage-only** (`EncryptedText`, `EncryptedInteger`, …, `EncryptedBoolean`): encrypt/store/decrypt only. Every search operator is rejected at operator time. +- **`*Eq`**: equality (`=`, `IN`) only. +- **`*Ord`**: equality plus comparison / range / ordering (`<`, `>`, `BETWEEN`, `ORDER BY`). +- **`EncryptedTextMatch`**: free-text token containment only. +- **`EncryptedTextSearch`**: equality + free-text + order/range. + +There is no combination to validate at authoring time: unrepresentable combinations have no constructor. The only capability enforcement at runtime is **operator gating** (see [Operators](#operators)) — rejecting an operator a column's domain does not support — which reads the domain's capabilities directly. + +### JSON + +`EncryptedJson` has **no** v3 constructor. The v3 barrel does not export it, so `cipherstash.EncryptedJson()` is an unresolved name at authoring time (PSL / TS "unknown constructor"), not a runtime throw. Documentation and the changeset must direct users to the v2 alias: + +- searchable JSON → `EncryptedJsonV2` / `encryptedJsonV2`. + +BigInt, by contrast, **is** a first-class v3 family as of protect-ffi 0.28: `EncryptedBigInt` / `EncryptedBigIntEq` / `EncryptedBigIntOrd` (`cast_as: 'bigint'`, plaintext = JS `bigint`, backed by `public.bigint` / `bigint_eq` / `bigint_ord`). The legacy `EncryptedBigIntV2` / `encryptedBigIntV2` remain available only for explicit v2 compatibility. + +`EncryptedTimestamp*` maps to `cast_as: "timestamp"` and preserves time of day. `EncryptedDate*` maps to `cast_as: "date"` and reconstructs to `Date`. + +## Contract Representation + +Each v3 column descriptor is **static at authoring** — there is no post-authoring rewrite. The constructor emits its concrete descriptor directly: + +- `codecId`: the concrete per-domain v3 codec id, **derived from the domain**, not a reused v2 codec id. +- `nativeType`: the concrete `public.*` domain (from `getEqlType()`). +- `typeParams`: + - `castAs` + - `capabilities` + +### The codec id is a projection of the domain, not independent data + +In v2, one native type (`eql_v2_encrypted`) is shared by all six codecs, so the codec id is the only discriminator and is genuinely independent data. In v3 the relationship inverts: each domain is its own Postgres domain, so `(codec id) ⟺ (public.* domain)` is bijective. The codec id therefore carries no discriminating information the domain does not already have. Its two remaining jobs are: + +1. **Serialization handle.** The contract is JSON; the domain *type* exists only at compile time. The codec id is the string key that reconstitutes the runtime codec/envelope/trait bundle from persisted JSON. +2. **Version axis.** The `@N` suffix (which the bare domain name lacks) is exactly how v2 and v3 — same logical type, different wire format — stay distinguishable, and how a future wire-format change to a domain would be versioned. + +Consequences for the design: + +- **Derive, do not author.** Codec ids are generated from the catalog as `cipherstash/eql-v3/${bareDomain}@1`, not hand-listed, where `bareDomain` is the `DOMAIN_REGISTRY` key (equivalently `stripDomainSchema(getEqlType())` — the domain with the `public.` prefix removed). Iterate `DOMAIN_REGISTRY` rather than re-deriving. +- **Single source for the type.** `nativeType` is the single source; do not also carry a redundant `typeParams.eqlType`. +- **Type the ids as a generated closed union.** Produce `CIPHERSTASH_V3_CODEC_IDS` (const tuple) → `CipherstashV3CodecId` (union) → set + guard, mirroring `CIPHERSTASH_CODEC_IDS` / `CipherstashCodecId` / `isCipherstashCodecId` (`extension-metadata/constants.ts:88-123`) so every codec-id-keyed dispatch table is compile-time-exhaustive. +- **No id reuse.** v3 must not reuse v2 codec ids such as `cipherstash/string@1`. Tests must assert the v3 and v2 id sets are disjoint. + +Type the descriptor fields with the imported stack types rather than free strings: `nativeType` as `` `public.${string}` `` / `EqlTypeForColumn`, `capabilities` as the exported `QueryCapabilities` (`{ equality, orderAndRange, freeTextSearch }`), `castAs` restricted to `PlaintextKind` (`'string' | 'number' | 'bigint' | 'boolean' | 'date' | 'timestamp'`) / `DateLikeCast`. + +Legacy v2 aliases keep the existing v2 codec ids and `eql_v2_encrypted` native type. + +Place v3 ids/traits/invariants in a dedicated `extension-metadata/constants-v3.ts` (or `v3/` subtree) rather than swelling the v2 `constants.ts`. + +## Runtime Codecs + +V3 columns are PostgreSQL domains over `jsonb`. Runtime encoding and decoding must use plain JSONB, matching `v3ToDriver` / `v3FromDriver` from the Drizzle integration: + +```ts +// `bigintSafeReplacer` is load-bearing and MUST be copied with the codec: a +// well-formed envelope never carries a bigint (bigint plaintext is encrypted to +// ciphertext strings first), but a malformed envelope with a stray bigint would +// otherwise throw `TypeError: Do not know how to serialize a BigInt`. A copy +// that drops it is a regression (see `eql/v3/drizzle/codec.ts:18,28`). +function bigintSafeReplacer(_key: string, value: unknown): unknown { + return typeof value === 'bigint' ? value.toString() : value +} + +function v3ToDriver(value: unknown): string | null { + if (value === null || value === undefined) return null + return JSON.stringify(value, bigintSafeReplacer) +} +``` + +Per [Operators](#operators), this codec (`v3ToDriver` / `v3FromDriver` / `bigintSafeReplacer`) is a **shared** framework-neutral primitive lifted to `eql/v3/codec.ts` and imported by both the Drizzle and prisma-next adapters — not re-implemented here. + +v3 codecs live in the v3 subtree and depend only on the neutral `EncryptedEnvelopeBase`; they must not import the v2 composite/wire codec. The v3 codec factory reuses the version-neutral user-facing value classes where the type already matches (surfaced through the v3 barrel) and introduces a number envelope for number-backed v3 domains: + +- text -> `EncryptedString` +- integer, smallint, numeric, real, double -> `EncryptedNumber` +- bigint -> `EncryptedBigInt` (bigint-backed, **not** `EncryptedNumber`) +- date, timestamp -> `EncryptedDate` +- boolean -> `EncryptedBoolean` + +`EncryptedNumber` and `EncryptedDouble` must be **siblings**, each extending `EncryptedEnvelopeBase` with its own distinct `typeName`. Do **not** alias or subclass one from the other: `typeName` drives the `$encryptedX` placeholder marker that `decryptAll` keys on (`envelope-base.ts:87-99`), so aliasing renders the wrong marker, and subclassing makes `value instanceof EncryptedNumber` true for a v2 `EncryptedDouble` — a cross-version leak in any coercer that dispatches on `instanceof` (`execution/operators.ts:202-233`). Each concrete v3 number domain sets `renderOutputType` to `'EncryptedNumber'`. `EncryptedDouble` (the value class) remains available for legacy v2 compatibility only; v3 integer/numeric constructors must not render as `EncryptedDouble`. + +`EncryptedBigInt` is a **separate** value envelope extending `EncryptedEnvelopeBase` with its own `typeName` — the `bigint` `cast_as` domains (`public.bigint*`) carry JS `bigint` plaintext, which is not a `number`, so they must not reuse the `EncryptedNumber` envelope. `bigint` values are lossless across the FFI boundary; the runtime wire codec still `JSON.stringify`s the *ciphertext envelope* (never the raw `bigint`), so JSON's lack of native `bigint` support does not arise on the wire path. + +The runtime codec descriptor should: + +- advertise the concrete domain in `targetTypes` +- use plain JSONB for parameter wire encoding +- never use the v2 composite-literal codec +- emit `[REDACTED]` placeholders through the existing envelope base + +## Middleware + +Add a **separate** v3 bulk-encrypt middleware under the v3 subtree. Do not generalize the v2 middleware with a wire-format branch (see [Namespace Separation](#namespace-separation)). The v3 middleware may reuse the neutral routing-key stamping *utility*, but must not share the v2 encode path. + +Required behavior: + +1. Filter parameters by the v3 codec id set. +2. Stamp `(table, column)` routing keys from INSERT/UPDATE ASTs, reusing the neutral `stampRoutingKeysFromAst` / `setHandleRoutingKey` utility (`bulk-encrypt.ts:80-81, 198-231`) — a version-neutral primitive, not the v2 encode path. +3. For search operands produced by operators, rely on operator-time routing-key stamping (`execution/operators.ts:147-158`). +4. Group by routing key. +5. Call `sdk.bulkEncrypt`, not `sdk.bulkEncryptQuery`. (`bulkEncrypt` is already the established prisma-next path — `bulk-encrypt.ts:121`.) +6. Replace parameter values with plain JSONB encoded ciphertext payloads (not the v2 composite from `encodeEqlV2EncryptedWire`). + +There is no query/storage split in this design. Search operands are full encrypted payloads because the canonical Drizzle branch lowers to public two-arg functions that accept `$::jsonb` full operands. + +## Operators + +Keep the existing cipherstash-prefixed methods: + +- `cipherstashEq`, `cipherstashNe` +- `cipherstashInArray`, `cipherstashNotInArray` +- `cipherstashGt`, `cipherstashGte`, `cipherstashLt`, `cipherstashLte` +- `cipherstashBetween`, `cipherstashNotBetween` +- `cipherstashIlike`, `cipherstashNotIlike` +- `cipherstashAsc`, `cipherstashDesc` — encrypted ordering, lowering to `eql_v3.ord_term({{self}})`, gated on an `ore` index. In scope this release (Drizzle parity: `operators.ts:497-502`). Storage-only / non-`ore` columns reject ordering with `EncryptionOperatorError`. + +For v3 columns, lower to the same SQL shape as the canonical Drizzle branch. The prisma-next lowering mechanism is a template with `{{self}}` / `{{arg0}}` placeholders (`execution/operators.ts:302-326`); the v3 templates must carry the `::jsonb` cast, which the v2 templates do not: + +``` +eql_v3.eq({{self}}, {{arg0}}::jsonb) +eql_v3.neq({{self}}, {{arg0}}::jsonb) +eql_v3.gt({{self}}, {{arg0}}::jsonb) +eql_v3.gte({{self}}, {{arg0}}::jsonb) +eql_v3.lt({{self}}, {{arg0}}::jsonb) +eql_v3.lte({{self}}, {{arg0}}::jsonb) +eql_v3.contains({{self}}, {{arg0}}::jsonb) +eql_v3.ord_term({{self}}) +``` + +**`cipherstashBetween` / `cipherstashNotBetween` parenthesisation is a correctness invariant, not a style choice.** The range template MUST be a single self-contained, parenthesised conjunction: + +``` +(eql_v3.gte({{self}}, {{arg0}}::jsonb) AND eql_v3.lte({{self}}, {{arg1}}::jsonb)) +``` + +Postgres binds `NOT` tighter than `AND`. Emitted bare, `NOT (between)` parses as `(NOT gte) AND lte` — selecting rows *below the lower bound* instead of the complement of the range (this is the exact class of bug fixed in `9c82f50e`; the reference documents it at `sql-dialect.ts:26-37`). `cipherstashNotBetween` then emits `NOT `. A test MUST pin the full parenthesised string for both, and assert `not(between)` wraps the whole conjunction. Emitting the two calls unwrapped is a defect even though each half is individually correct. + +The operator **functions** live in the `eql_v3` schema (unchanged by 0.28) even though the **domains** they operate on are `public.*`; do not "correct" these to `public.*`. This exact two-arg `::jsonb` shape is proven end-to-end against real domains in `packages/stack/__tests__/v3-matrix/matrix-live-pg.test.ts` and `schema-v3-pg.test.ts` (e.g. `eql_v3.eq("col", $::jsonb)`, `eql_v3.contains("col", $::jsonb)`, range = `eql_v3.gte(...) AND eql_v3.lte(...)`). The `::jsonb` template change and the middleware wire-format branch (plain JSON, not the v2 composite) must be implemented together — the template consumes exactly what the middleware produces. + +The operand is the **full encrypted envelope** (every index term), not a narrowed query term: the `encryptQuery` scalar-term path is unsupported in protect-ffi 0.28 (`EQL_V3_QUERY_UNSUPPORTED`), and the SQL function chosen — not a `queryType` — selects which term is compared. On a pure-ORE domain (no `unique`/`hm` index) `eql_v3.eq` resolves via `ord_term` (equality-via-ORE); this is why the equality gate accepts `unique` **or** `ore`. `eql_v3.contained_by` also exists as the dual of `contains`, but prisma-next exposes only `contains` (via `cipherstashIlike`) this release. + +`cipherstashIlike` / `cipherstashNotIlike` lower to `eql_v3.contains` (bloom-filter token containment, not SQL `LIKE`). Carry this caveat into user docs so wildcard semantics are not implied. (The Drizzle reference names this operator `contains`; prisma-next keeps its `ilike` method name — an intentional surface divergence.) + +Capability gating must read the domain's built `indexes` (from `column.build().indexes`), not any authoring flag. The gate is a disjunction — any listed index grants the capability: + +- equality (`eq`, `ne`, `IN`, `NOT IN`) requires `unique` **or** `ore` +- comparison / range / ordering (`gt`/`gte`/`lt`/`lte`, `between`/`notBetween`, `asc`/`desc`) requires `ore` +- free-text search (`ilike`) requires `match` +- storage-only domains (no index) reject every search operator + +This gating is the **only** runtime capability enforcement in the design — there is no authoring-time combination validation because impossible combinations have no constructor. + +**Shared, framework-neutral primitives — lift to `packages/stack/src/eql/v3/`, do not re-copy.** Three things the Drizzle adapter currently keeps under its own subtree have **no** Drizzle dependency and are a correctness surface that must not diverge between adapters: + +1. The **wire codec** `v3ToDriver` / `v3FromDriver` **including `bigintSafeReplacer`** (currently `eql/v3/drizzle/codec.ts`, but it imports only `Encrypted` from `@/types`). Move to `eql/v3/codec.ts`; both adapters import it. +2. The **gate index-sets** `EQUALITY_INDEXES = ['unique','ore']`, `ORE_INDEXES = ['ore']`, `MATCH_INDEXES = ['match']` and the `requireIndex` disjunction rule (currently `eql/v3/drizzle/operators.ts:205-221`). These decide which operators a domain may answer — two divergent copies is a correctness bug, not a DRY nit. +3. The **function-schema + op→function-name map + parenthesisation rule**: `EQL_V3_FN_SCHEMA = 'eql_v3'`, the `eq→eq`/`ne→neq`/`gt→gt`/… name map, and the "range is a parenthesised conjunction" rule (currently `eql/v3/drizzle/sql-dialect.ts`). The *fragment builders* can't be shared verbatim (Drizzle emits `sql` fragments; prisma-next emits `{{self}}`/`{{arg0}}` template strings), but the schema, the name map, and the paren rule can and must be. Had they been shared, the between bug above could not exist in two places. + +This expands scope to touch the existing Drizzle adapter (it re-imports from the lifted location). That is intended. + +Add a real exported error class `EncryptionOperatorError` carrying a `context: { columnName?, tableName?, operator? }` (mirroring `eql/v3/drizzle/operators.ts:66-78`). The two adapters keep **separate** `EncryptionOperatorError` classes by deliberate fork (they are independently-versioned entry points — see the "INTENTIONAL FORK" note in the reference); this is the one place duplication is correct. Prisma-next operators currently throw plain `TypeError` / `Error`; "structured like" is not enough — the class must exist and be exported. + +Per-domain trait derivation: each concrete v3 codec descriptor derives its `cipherstash:*` traits from the domain's `QueryCapabilities` keys (`equality → cipherstash:equality`, etc.), a single mapping reused across domains. + +Do not attach built-in framework equality traits that would re-enable plain SQL `=`. CipherStash operators stay in the `cipherstash:*` trait namespace (regression-pinned by `test/equality-trait-removal.test.ts`). + +## Schema Derivation and Stack Adapter + +**v2 and v3 are separate entry points — they are never co-registered in one Prisma-next client.** The framework's `OperationRegistry` is a flat, method-name-keyed map that disallows override; two descriptors both defining `cipherstashEq` collide at registration regardless of extension id. Rather than overload one gated descriptor, v3 ships as its **own** extension factory with its **own** distinct extension id/version, keeping the **same** `cipherstash*` operator method names. A given client is either v2 or v3. + +Consequences: + +- **No single-dispatch-boundary / codec-id-namespace routing inside one client.** There is no shared `deriveStackSchemas` that branches v2 vs v3 at runtime, and no per-client registry that holds both versions. Delete that machinery. +- **Mixed v2 + v3 columns in a single client are not supported this release.** A schema is authored for one version. (A codebase can still run a v2 client and a v3 client side by side; they do not share a registry, descriptor, or dispatch.) +- **`deriveStackSchemasV3(contractJson)` is a v3-only derivation.** It reads the v3 codec ids and builds concrete `@cipherstash/stack/eql/v3` schemas, preserving the concrete column type (see [Type Discipline](#type-discipline-the-spine)): + +```ts +// nativeType 'public.text_eq' -> concrete factory types.TextEq -> EncryptedTextEqColumn +encryptedTableV3(table, { [column]: types.TextEq(column) }) +``` + +It MUST NOT widen the built column to `AnyEncryptedV3Column` before constructing the table. A v3 contract containing a v2 codec id is a hard error (wrong entry point), not a silently-derived v2 column. + +- **v3 client.** `cipherstashFromStackV3` builds a single `EncryptionV3({ schemas })` client from the v3-derived schemas. v3 bulk-encrypt goes through `EncryptionV3(...)`, never the base v2 `bulkEncrypt`. The v3 SDK adapter/registry accepts the v3 concrete columns; the v2 registry (`sdk-adapter.ts:136`) is untouched and unaware of v3. + +Override validation for v3 columns lives in the v3 derivation path and must compare **exact v3 domain identity**, not only `cast_as` and index keys (`from-stack.ts:149-176` cannot distinguish `integer_ord` from `integer_ord_ore` — they have identical `cast_as` and built indexes and differ only in `nativeType`). Two columns with the same `cast_as` but different domains are not equivalent. v2 override validation is unchanged. + +## Migrations + +Add a v3 bundle baseline migration after the existing v2 baseline. Keep the v2 baseline while legacy v2 aliases exist. + +V3 columns must not emit v2 `add_search_config` / `remove_search_config` lifecycle operations. Their query behavior is encoded by the concrete PostgreSQL domain. + +The migration package must include: + +- EQL v3 install SQL sourced from `@cipherstash/eql/sql` (`releaseManifest`, `@cipherstash/eql@3.0.0-alpha.3` — the same source `packages/stack/scripts/install-eql-v3.ts` / `installEqlV3IfNeeded` use), **not** a hand-vendored FFI fixture. This install creates the `public.*` domains and the `eql_v3.*` operator functions. +- new invariant id `cipherstash:install-eql-v3-bundle-v1` +- migration directory `20260601T0100_install_eql_v3_bundle`, which sorts after the v2 install (`20260601T0000_install_eql_bundle` / `cipherstash:install-eql-bundle-v1`) + +## Testing + +Target three levels — unit, property, and integration. + +### Unit / authoring + +Add or update tests in `packages/prisma-next/test` (each maps to an existing file that gains v3 cases unless noted): + +1. Authoring: each v3 constructor emits its concrete per-domain codec id and concrete `public.*` native type, statically — no resolution pass (`authoring.test.ts`). +2. Constructor↔domain 1:1: the generated constructor set exactly matches the exposed catalog `types.*` factories (excluding `*OrdOre`); every constructor's codec id, `nativeType`, and `capabilities` equal the catalog values for that domain (`authoring.test.ts` / `column-types.test.ts`). +3. Legacy: `*V2` constructors emit current v2 codec ids and `eql_v2_encrypted` (`authoring.test.ts` / `psl-interpretation*.test.ts`). +4. Unexposed / absent surface: no `*OrdOre` constructor is exported (including `EncryptedBigIntOrdOre`); no `EncryptedJson` v3 constructor exists (the name is unresolved on the v3 barrel); `EncryptedBoolean` is storage-only and exposes no equality constructor. Conversely, `EncryptedBigInt` / `EncryptedBigIntEq` / `EncryptedBigIntOrd` **are** exported and emit `public.bigint*` with `cast_as: 'bigint'` (`column-types.test.ts`, `psl-interpretation*.test.ts`). +5. Catalog invariants (new): a drift test asserting no searchable JSON v3 domain is exposed, `public.boolean` is storage-only, `*OrdOre` (incl. `bigint_ord_ore`) is excluded from the generated surface, and the BigInt family IS exposed. Assert native types are `public.*` while operator SQL functions are `eql_v3.*` (the schema split is not accidentally collapsed). Structural derivation from the imported `DOMAIN_REGISTRY` covers the rest — no hand-maintained table to pin. +6. Runtime codec: v3 encodes/decodes plain JSONB and never emits v2 composite literals (`codec-runtime.test.ts`, `cipherstash-codec*.test.ts`). +7. Middleware: v3 parameters are bulk-encrypted with `sdk.bulkEncrypt` and JSONB encoded (`bulk-encrypt-middleware.test.ts`). This suite MUST cover the edge cases the implementation itself handles (the Drizzle reference covers all four — `eql/v3/drizzle/operators.test.ts`): (a) a bulk-encrypt response whose length ≠ the operand count is **rejected**, not truncated (a short response would silently widen `inArray` / narrow `notInArray`); (b) partial-failure is wrapped in the structured error with `{ operator, columnName }`; (c) returned terms are positionally aligned index-for-index with the operands; (d) a `null` in a value list is rejected **before** any crypto crossing. +8. Operator SQL lowering: **pin the full lowered string with `.toBe()` for every operator** (`eq`, `ne`, `inArray`, `notInArray`, `gt`, `gte`, `lt`, `lte`, `between`, `notBetween`, `ilike`, `notIlike`, `asc`, `desc`) — not `.toContain('eql_v3.eq(')`, which would not catch the `between` paren bug. Include an explicit test that `cipherstashNotBetween` (and `not(between)`) emits `NOT (eql_v3.gte(...) AND eql_v3.lte(...))` with the conjunction parenthesised (`operator-lowering*.test.ts`). +9. Operator gating: for each domain, the gate allows exactly the operators the domain's built indexes support and rejects the rest with `EncryptionOperatorError`; storage-only domains reject every search operator **and** `asc`/`desc` (`operator-lowering*.test.ts`). +10. Trait: framework built-in equality traits are not reintroduced (`equality-trait-removal.test.ts`). +11. Derivation: v3 contracts derive exact `@cipherstash/stack/eql/v3` concrete schemas (`derive-schemas.test.ts`). +12. Divergence: override schemas with the specific **same-`cast_as`, different-domain** mismatch fail (`from-stack-divergence.test.ts`). +13. Bundling: v3 codecs and bundle assets do not pull the v2 composite codec path unnecessarily (`bundling-isolation.test.ts`). +14. Codec-id disjointness: the v3 codec-id set and the v2 codec-id set are disjoint. + +### Type-level (`*.test-d.ts`) — mandatory + +These enforce [Type Discipline](#type-discipline-the-spine); a runtime-only suite does not. Add `*.test-d.ts` files (as the Drizzle side does — `packages/stack/__tests__/drizzle-v3/types.test-d.ts`): + +15. **Factories are mutually non-assignable.** `encryptedBigIntOrd()` is *not* assignable to the type of `encryptedText()` (and vice-versa); a representative off-diagonal set is checked so an accidental collapse to one shared descriptor type fails to compile. +16. **Descriptor field types are literal, not widened.** For a sample of domains, `codecId` is the literal union member (e.g. `'cipherstash/eql-v3/text_eq@1'`, not `string`), `nativeType` is the `` `public.text_eq` `` literal (not `string`), and `capabilities` is the concrete literal `QueryCapabilities` (booleans preserved, not `unknown`). +17. **Derivation preserves the concrete column type.** `deriveStackSchemasV3` over a known contract yields the concrete `EncryptedTextEqColumn` / `EncryptedBigintOrdColumn` types into `encryptedTableV3`, not `AnyEncryptedV3Column`. + +### Property-based + +`fast-check ^4.8.0` is already in the monorepo (`packages/drizzle/package.json`) but is not yet a prisma-next devDependency — add it. Required properties: + +These must be genuine properties over generated inputs — not `fc.constantFrom(...EXPOSED_DOMAIN_ENTRIES)` table-tests wearing a fast-check costume. Iterating the closed catalog is a table test; keep those as `it.each`, and reserve `fc` for real input spaces: + +- **JSONB round-trip**: over `fc.jsonValue()`, `v3FromDriver(v3ToDriver(x))` deep-equals `x`; `null`/`undefined → null`. +- **BigInt losslessness**: over `fc.bigInt()`, a v3 bigint value survives encode→decode / the codec path and reconstructs to the identical JS `bigint` (this is the property `9c82f50e`'s oracle needed and the current plan lacks). +- **Codec round-trip per `castAs`**: for each `castAs`, an arbitrary plaintext of that kind round-trips through the cell codec to an equal value (the current JSON-only round-trip does not exercise the number/bigint/date/boolean envelopes). +- **`[REDACTED]` invariant**: for arbitrary user values the serialized envelope never contains the plaintext and always renders the placeholder. +- **ORE order-preservation (metamorphic)**: over arbitrary ordered pairs of same-typed plaintexts, the emitted ordering relation agrees with the plaintext comparison — the property that also makes the live oracle rigorous. +- **Operator-gating equivalence**: for arbitrary `(domain, operator)`, the gate allows the operator iff the pure capability predicate holds. +- **Derivation round-trip**: arbitrary v3 contract column → `deriveStackSchemasV3` → derived domain identity equals the original. + +### Integration / live-PG + +prisma-next has no live-PG tests today; add suites mirroring the Drizzle branch, reusing `describeLivePg` / `LIVE_EQL_V3_PG_ENABLED` / `installEqlV3IfNeeded` (self-skip locally, run in CI with `DATABASE_URL` + `CS_*`): + +The live suite MUST assert against a **plaintext-computed oracle**, not hand-picked rows (mirror the Drizzle oracle `eql/v3/drizzle/operators-live-pg.test.ts` — `expectedKeysFor` / `sortedKeysFor` / `comparePlain`, which now includes a `bigint` branch after `9c82f50e`). Asserting "the expected row is selected" over a couple of rows would pass against a `between` that matches everything — the oracle is exactly what caught the paren bug. + +- **operators-live-pg**: per capability tier (per constructor), encrypt → INSERT → query via each `cipherstash*` operator → decrypt against real `public.*` domains; assert the selected key set **equals the oracle's `expectedKeysFor`** over a seeded plaintext population. +- **range boundaries (the paren oracle)**: a `cipherstashBetween` with a single-row-wide window selects exactly that row, and a `cipherstashNotBetween` anchored at the **lower** bound excludes it (and selects the rest) — the specific shape that fails when `NOT (gte AND lte)` degrades to `(NOT gte) AND lte`. +- **ordering live**: `cipherstashAsc` / `cipherstashDesc` over a seeded population return rows in the exact plaintext order (`sortedKeysFor`); a non-`ore` column rejects ordering. +- **operators-null-live-pg**: null operands and null columns round-trip. +- **boolean storage-only live**: `public.boolean` survives INSERT/SELECT/decrypt and rejects every search operator (and ordering). +- **bigint round-trip live**: `public.bigint` / `bigint_eq` / `bigint_ord` encrypt → INSERT → query (eq / order-range) → decrypt to a JS `bigint`, ordering asserted via `comparePlain`'s bigint branch. +- **migration application live**: apply `20260601T0100_install_eql_v3_bundle`; assert the concrete domains exist, invariant `cipherstash:install-eql-v3-bundle-v1` is registered, and v3 columns emit no `add_search_config` / `remove_search_config`. +- **real bulkEncrypt**: middleware → real `EncryptionClient.bulkEncrypt` (not the fake) → INSERT, proving routing-key stamping and JSONB payloads end-to-end. + +(No "mixed v2 + v3 in one client" live test — that mode is unsupported per [Schema Derivation](#schema-derivation-and-stack-adapter). A v2 client and a v3 client running side by side may be exercised separately.) + +### Commands + +```bash +pnpm --filter @cipherstash/prisma-next test +pnpm --filter @cipherstash/prisma-next build +``` + +If implementation touches `@cipherstash/stack` public exports, also run the relevant stack v3 tests. + +## Release Notes + +This is a breaking public behavior change for `@cipherstash/prisma-next`: v3 columns are authored through new per-domain constructors, and the legacy boolean-option surface is not carried into v3. + +Required release metadata: + +- Add a changeset for `@cipherstash/prisma-next`. +- Mark the change as major if the package is considered published/stable. +- If the package is still experimental and versioning policy allows a minor, the changeset body must still call out the new authoring surface explicitly. + +User-facing migration guidance: + +- EQL v3 columns are authored with concrete per-domain constructors — the constructor is the capability. Examples: `EncryptedTextSearch()` (eq + free-text + order/range), `EncryptedTextEq()` (equality), `EncryptedIntegerOrd()` (eq + order/range), `EncryptedText()` / `EncryptedInteger()` (storage-only), `EncryptedBoolean()` (storage-only). +- There is no boolean-option surface (`EncryptedString({ equality, ... })`) in v3. Choose the constructor whose name matches the capability you need. +- Impossible capability combinations have no constructor (e.g. text equality + free-text without order/range) — they are unrepresentable rather than runtime errors. +- Use `*V2` constructors to keep old v2 columns. +- BigInt is a first-class v3 family (`EncryptedBigInt` / `EncryptedBigIntEq` / `EncryptedBigIntOrd`, JS `bigint` plaintext). Searchable JSON has no v3 constructor; use `EncryptedJsonV2`. +- Boolean v3 is storage-only; there is no boolean equality constructor. +- Regenerate contracts and migrations after changing constructors. From a655cefe16aa47c834068930b712cd15876d5825 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Thu, 9 Jul 2026 18:56:12 +1000 Subject: [PATCH 11/15] docs(prisma-next): EQL v3 implementation plan + review remediation Add the design-A implementation plan and remediate the review findings. Real bugs fixed: - cipherstashBetween is now a self-parenthesised conjunction '(gte AND lte)' so any generic NOT/OR composition is safe (Postgres binds NOT tighter than AND; the 9c82f50e bug class). Operator-lowering tests pin the full parenthesised skeleton for all operators and assert NOT (gte AND lte). - v3ToDriver restores bigintSafeReplacer; a stray bigint in a malformed envelope serialises instead of throwing. Pinned by a test. - codec-runtime encode test now exercises the real envelope/.expose() contract (the old test fed a plain object with no .expose(), so it never passed). - constants-v3 drift test replaced the masking cast with a pinned literal tuple (as const satisfies) + bidirectional Exclude guard + runtime equality check. Type spine (the core of v3): - Authoring factories are typed per-domain via v3Authored(types.X): distinct return type per domain, literal codecId/nativeType/capabilities (never string/unknown). Mandatory column-types.test-d.ts pins non-assignability + literal field types. Derivation preserves the concrete column type. Separate v2/v3 entry points (decision 1b): - v3 registers its own extension descriptor with a distinct CIPHERSTASH_V3_SPACE_ID/VERSION, keeping the same cipherstash* method names, and is never co-registered with v2 (the flat OperationRegistry rejects the duplicate method). Task 7 replaces the two-client dispatch with a v3-only cipherstashFromStackV3; a v2 codec id in a v3 contract is a hard error. Plan under docs/superpowers/plans/ (locally git-excluded; force-added). Remaining (follow-up): encrypted ORDER BY (asc/desc -> eql_v3.ord_term), shared-primitive lift to eql/v3/, bulk-middleware edge-case tests, live plaintext-oracle suite, genuine property tests, minor type cleanups. --- .../plans/2026-07-08-eql-v3-prisma-next.md | 2859 +++++++++++++++++ 1 file changed, 2859 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md diff --git a/docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md b/docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md new file mode 100644 index 000000000..0f9f172d5 --- /dev/null +++ b/docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md @@ -0,0 +1,2859 @@ +# EQL v3 Prisma-next Implementation Plan (design A — per-domain constructors) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make `@cipherstash/prisma-next` author EQL v3 columns through **concrete per-domain constructors** that map 1:1 to the `public.*` Postgres domains, each carrying a static codec id + concrete `public.*` native type + `{ castAs, capabilities }` typeParams — no boolean-option surface, no runtime domain-resolution stage — wired end-to-end through a self-contained `src/v3/` namespace, with legacy `*V2` aliases preserved. + +**Architecture:** v3 is a **parallel, self-contained namespace** — never v3 branches grafted onto v2 code. Capability is encoded by constructor identity: the constructor you choose *is* the capability set, and it emits its concrete descriptor statically (`(codec id) ⟺ (public.* domain) ⟺ capabilities` is bijective — nothing is resolved). The **domains / native types live in `public`** (`public.text_eq`, `public.integer_ord`, `public.boolean`, `public.bigint_ord`); the **operator functions live in `eql_v3`** (`eql_v3.eq`, `eql_v3.gt`, `eql_v3.contains`, `eql_v3.ord_term`). These two schemas must never be conflated. The constructor set, codec ids, capabilities, cast kinds, and native types are all **derived from the imported `DOMAIN_REGISTRY`** in `@cipherstash/stack/eql/v3` — there is no hand-maintained local table. Concrete per-domain types are threaded end-to-end (literal `codecId` / `nativeType` / `capabilities`; see Task 3's `V3ColumnDescriptor` and the `.test-d.ts`). **v2 and v3 are separate entry points** (decision 1b): v3 is its own extension factory with its own distinct extension id, keeping the same `cipherstash*` method names, and is **never co-registered** with v2 in one client (the flat `OperationRegistry` disallows the duplicate-method override). A client is v2 or v3; mixed v2+v3 columns in one client are unsupported this release. + +**Tech Stack:** TypeScript, `@cipherstash/stack` (`/eql/v3` catalog + a v3 `EncryptionV3` client), `@cipherstash/eql@3.0.0-alpha.3` (`/sql` → `readInstallSql` / `releaseManifest`), `@prisma-next/*` framework, `arktype`, `vitest` (`vitest run`), `fast-check ^4.8.0` (added here), `biome` (lint), `tsup` (build), `postgres` (live tests). + +## Global Constraints + +- **Schema split (hard — the #1 thing to get right).** Two Postgres schemas are in play and must not be conflated: + - **Domains / native types → `public`.** `public.text_eq`, `public.integer_ord`, `public.boolean`, `public.bigint_ord`, … A column's `getEqlType()` returns this `public.*` string; it is the column's `nativeType`. The stack type pins it as `` `public.${string}` ``. + - **Operator functions → `eql_v3`.** `eql_v3.eq(...)`, `eql_v3.gt(...)`, `eql_v3.contains(...)`, `eql_v3.ord_term(...)`. This is the SQL the operators lower to. Do NOT "correct" these to `public.*`. +- **Namespace separation (hard).** All v3 implementation lives under `packages/prisma-next/src/v3/` plus `packages/prisma-next/src/extension-metadata/constants-v3.ts`. v3 modules MUST NOT import v2 codec/wire (`encodeEqlV2EncryptedWire`), the v2 bulk-encrypt middleware, the v2 codec-runtime, or the v2 SDK adapter. The only permitted shared primitives are the version-neutral value-envelope classes (`EncryptedEnvelopeBase`, `EncryptedString`, `EncryptedDate`, `EncryptedBoolean`, `EncryptedBigInt`) + the routing-key stamping utilities and framework authoring/trait infrastructure. No v2 module gains an `if (isV3)` wire-format branch. +- **Source of truth = the imported `DOMAIN_REGISTRY`.** Derive domain names, `castAs`, capabilities, indexes, native types, and codec ids from `@cipherstash/stack/eql/v3` by iterating `DOMAIN_REGISTRY` (keys are bare domain names → factory). Do NOT hand-maintain a local domain table. The stack v3 layer defines NO codec-id strings; `codecId` is purely a prisma-next concept. +- **Codec id scheme.** Concrete per-domain id = `` `cipherstash/eql-v3/${bareDomain}@1` `` where `bareDomain` is the domain with the `public.` prefix stripped (e.g. `public.text_search` → `cipherstash/eql-v3/text_search@1`). Strip `public.` (never `eql_v3.`). The `eql-v3` token is a logical version tag, not a schema. +- **castAs set.** `'string' | 'number' | 'bigint' | 'boolean' | 'date' | 'timestamp'`. `bigint` domains (`public.bigint*`) carry JS `bigint` plaintext. +- **No id reuse.** v3 ids MUST NOT reuse v2 ids (`cipherstash/string@1`, …). The v3 and v2 id sets MUST be disjoint (pinned by a test). +- **Legacy aliases.** `*V2` constructors keep the existing v2 codec ids and `eql_v2_encrypted` native type — unchanged behavior. The v2 constructors that used unqualified names (`EncryptedString`, `EncryptedDouble`, `EncryptedBigInt`, `EncryptedDate`, `EncryptedBoolean`, `EncryptedJson`) are renamed to their `*V2` forms; the unqualified names are taken over by the v3 surface (except `EncryptedString`/`EncryptedJson`, which have no v3 constructor — text v3 uses `EncryptedText*`, and JSON has no v3 domain). +- **Contract representation.** Each v3 column carries `codecId` (concrete per-domain id), `nativeType` (concrete `public.*` domain), `typeParams: { castAs, capabilities }`. There is NO redundant `eqlType` field — `nativeType` is the single source. +- **`*_ord_ore` not exposed.** The registry defines `*_ord_ore` domains; prisma-next surfaces only the `_ord` order domains. `_ord_ore` gets no constructor. (The catalog META/codec-id set may still include them so runtime decode/derivation is robust; only the *authoring constructor set* excludes them.) +- **No searchable JSON v3 domain, no `EncryptedJson` v3 constructor.** BigInt IS a first-class v3 family (`public.bigint` / `bigint_eq` / `bigint_ord`). +- **Wire format.** v3 columns are PG domains over `jsonb`; encode = `JSON.stringify`, decode = `JSON.parse` (object passthrough). Never the v2 composite literal. +- **Operators.** v3 lowers to the two-arg `eql_v3.*(...::jsonb)` functions, byte-for-byte matching the canonical Drizzle branch. No built-in framework equality traits; CipherStash operators stay in the `cipherstash:*` trait namespace. +- **Migrations.** v3 install SQL is sourced from `@cipherstash/eql/sql` (`readInstallSql()` / `releaseManifest.eqlVersion`, `@cipherstash/eql@3.0.0-alpha.3`), NOT a hand-vendored FFI fixture. Invariant `cipherstash:install-eql-v3-bundle-v1`; directory `20260601T0100_install_eql_v3_bundle` (sorts after the v2 `20260601T0000_install_eql_bundle`). v3 columns emit NO `add_search_config` / `remove_search_config`. +- **Commands.** Test: `pnpm --filter @cipherstash/prisma-next test`. Build: `pnpm --filter @cipherstash/prisma-next build`. Lint: `pnpm --filter @cipherstash/prisma-next lint`. If a task touches `@cipherstash/stack` public exports, also run `pnpm --filter @cipherstash/stack test`. Every task ends green on `test`. + +## Ground-truth notes (verified against the worktree `.worktrees/feat/eql-v3-supabase-adapter`) + +1. **`DOMAIN_REGISTRY` / `factoryForDomain` / `stripDomainSchema` are NOT yet re-exported** from the `@cipherstash/stack/eql/v3` barrel (`packages/stack/src/eql/v3/index.ts`); they live in `domain-registry.ts` as internal. Task 1 Step 1 adds them to the barrel (touches stack public exports → run stack tests). +2. **`EncryptedBigInt`, `EncryptedString`, `EncryptedDate`, `EncryptedBoolean`, `EncryptedDouble` value envelopes already exist** in `packages/prisma-next/src/execution/envelope-*.ts` (each with its own `typeName`). **`EncryptedNumber` does NOT exist and must be created** (a sibling of `EncryptedDouble`). `EncryptedBigInt` is REUSED (not recreated) and surfaced through the v3 barrel. +3. **`stampRoutingKeysFromAst` is module-private** in `src/middleware/bulk-encrypt.ts`. Task 5 exports it (a version-neutral primitive). +4. **`CipherstashSdk`** (`src/execution/sdk.ts`): `decrypt(args) => Promise`, `bulkEncrypt({ routingKey, values, signal? }) => Promise>`, `bulkDecrypt({ routingKey, ciphertexts, signal? }) => Promise>`; `CipherstashRoutingKey { readonly table: string; readonly column: string }`. +5. **`encryptedTable(name, columns)`** (stack `eql/v3/table.ts`) returns `EncryptedTable & T`; instance members: `tableName`, `columnBuilders`, `build() => { tableName, columns }`, `buildColumnKeyMap()`. `AnyV3Table = EncryptedTable`. +6. **`@cipherstash/eql/sql`** exports `readInstallSql(): string` and `releaseManifest` (`.eqlVersion`). `installEqlV3IfNeeded(sql)` lives in `packages/stack/__tests__/helpers/eql-v3.ts` (advisory lock `3_733_003`; staleness via `eql_v3.version()`). +7. **The Drizzle reference operator/gating shapes** live on branch `eql-v3-drizzle-concrete-types` (not the working tree). Their shapes are reproduced inline in the relevant tasks. Operator SQL is proven live in `packages/stack/__tests__/v3-matrix/matrix-live-pg.test.ts` and `schema-v3-pg.test.ts`. +8. **Operator method names + the flat registry (resolved: decision 1b).** The framework `OperationRegistry` is a flat, method-name-keyed map that disallows override, so two descriptors both defining `cipherstashEq` collide at registration regardless of extension id. Resolution: v2 and v3 are **separate entry points** — v3 registers its own extension descriptor (its own distinct `id`/`version`, same `cipherstash*` method names) and is **never co-registered** with the v2 descriptor. There is no single-shared-gated-descriptor and no in-one-client mixing. This is not a fallback; it is the design. + +## File Structure + +New / modified files, by responsibility: + +``` +packages/stack/ + src/eql/v3/index.ts (MODIFY) re-export DOMAIN_REGISTRY, factoryForDomain, stripDomainSchema, V3ColumnFactory +packages/prisma-next/ + src/ + extension-metadata/ + constants-v3.ts (NEW) v3 codec ids (derived closed union), traits map, invariants, migration name + v3/ + catalog.ts (NEW) derive domain meta + codec ids + native-type→factory map from DOMAIN_REGISTRY + envelope-number.ts (NEW) EncryptedNumber envelope (sibling of EncryptedDouble) + wire-v3.ts (NEW) v3ToDriver / v3FromDriver (plain JSONB) + codec-runtime-v3.ts (NEW) v3 cell codec (JSONB), per-domain factories, metadata + bulk-encrypt-v3.ts (NEW) bulkEncryptMiddlewareV3(sdk): JSONB payloads, neutral routing-key stamp + operators-v3.ts (NEW) cipherstashV3QueryOperations(), EncryptionOperatorError, capability gating + derive-schemas-v3.ts (NEW) deriveStackSchemasV3(contractJson) -> AnyV3Table[] + sdk-adapter-v3.ts (NEW) createCipherstashV3Sdk(EncryptionV3 client, v3 schemas) + runtime-v3.ts (NEW) createCipherstashV3RuntimeDescriptor({ sdk }) + from-stack-v3-validate.ts (NEW) assertV3SchemasAgree (exact domain identity) + barrel.ts (NEW) v3 user-facing value types (reuse neutral classes + EncryptedNumber) + contract-authoring.ts (MODIFY) v3 concrete per-domain constructors (derived) + *V2 aliases + exports/column-types.ts (MODIFY) TS factories mirror the PSL constructors + *V2 + middleware/bulk-encrypt.ts (MODIFY) export stampRoutingKeysFromAst as neutral util + stack/from-stack.ts (MODIFY) dispatch v2/v3, build Encryption + EncryptionV3 clients + migration/ + eql-bundle-v3.ts (NEW) re-export readInstallSql / releaseManifest from @cipherstash/eql/sql + migrations/20260601T0100_install_eql_v3_bundle/ + migration.ts (NEW) rawSql install op, invariant cipherstash:install-eql-v3-bundle-v1 + migration.json / ops.json / end-contract.json (GENERATED by migration.ts) + exports/control.ts (MODIFY) add the v3 baseline to the migrations array + exports/runtime.ts (MODIFY) export v3 runtime surface + exports/stack.ts (MODIFY) export v3 derivation/adapter + test/ + v3/ (new suites — see tasks) + live/helpers/live-gate.ts, eql-v3.ts (NEW) mirror stack live helpers + package.json (MODIFY) add fast-check + @cipherstash/eql devDeps; ./v3 subpath exports +.changeset/eql-v3-prisma-next.md (NEW) +``` + +--- + +### Task 1: Export `DOMAIN_REGISTRY` from stack + derive the v3 catalog (meta + codec ids) + +Re-export the registry from the stack v3 barrel, then derive every v3 domain's `{ nativeType, bareDomain, castAs, capabilities, indexes }` and its concrete codec id by iterating `DOMAIN_REGISTRY`. This is the single runtime source of truth; every later task reads from it. + +**Files:** +- Modify: `packages/stack/src/eql/v3/index.ts` +- Create: `packages/prisma-next/src/v3/catalog.ts` +- Test: `packages/prisma-next/test/v3/catalog.test.ts` + +**Interfaces:** +- Consumes: `@cipherstash/stack/eql/v3` — after Step 1: `DOMAIN_REGISTRY` (`Record AnyEncryptedV3Column>`), `stripDomainSchema(eqlType): string`, `type V3ColumnFactory`; already public: `type QueryCapabilities`, `type AnyEncryptedV3Column`, `type EqlTypeForColumn`. Each column exposes `getEqlType(): \`public.${string}\``, `getQueryCapabilities(): QueryCapabilities`, `build(): { cast_as: string; indexes: Record }`. +- Produces: + - `type V3CastAs = 'string' | 'number' | 'bigint' | 'boolean' | 'date' | 'timestamp'` + - `interface V3DomainMeta { readonly nativeType: \`public.${string}\`; readonly bareDomain: string; readonly castAs: V3CastAs; readonly capabilities: QueryCapabilities; readonly indexes: Readonly> }` + - `function toV3CodecId(bareDomain: string): string` → `` `cipherstash/eql-v3/${bareDomain}@1` `` + - `const V3_DOMAIN_META_BY_CODEC_ID: ReadonlyMap` (all registry domains, keyed by concrete codec id) + - `const V3_CODEC_IDS: readonly string[]` + - `const V3_FACTORY_BY_NATIVE_TYPE: ReadonlyMap AnyEncryptedV3Column>` (keyed by `public.*` native type) + - `function isOrdOreDomain(bareDomain: string): boolean` + - `const EXPOSED_DOMAIN_ENTRIES: ReadonlyArray` (registry minus `*_ord_ore`) + - `function envelopeTypeNameForCastAs(castAs: V3CastAs): 'EncryptedString' | 'EncryptedNumber' | 'EncryptedBigInt' | 'EncryptedDate' | 'EncryptedBoolean'` + +- [ ] **Step 1: Re-export the registry from the stack v3 barrel** + +In `packages/stack/src/eql/v3/index.ts`, add: + +```ts +export { + DOMAIN_REGISTRY, + factoryForDomain, + stripDomainSchema, + type V3ColumnFactory, +} from './domain-registry' +``` + +Run: `pnpm --filter @cipherstash/stack build && pnpm --filter @cipherstash/stack test` +Expected: PASS (pure additive export; no behavior change). + +- [ ] **Step 2: Write the failing test** + +Create `packages/prisma-next/test/v3/catalog.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { + EXPOSED_DOMAIN_ENTRIES, + envelopeTypeNameForCastAs, + isOrdOreDomain, + toV3CodecId, + V3_CODEC_IDS, + V3_DOMAIN_META_BY_CODEC_ID, + V3_FACTORY_BY_NATIVE_TYPE, +} from '../../src/v3/catalog' + +describe('v3 catalog derivation', () => { + it('derives a concrete codec id per domain by stripping the public. prefix', () => { + expect(toV3CodecId('text_search')).toBe('cipherstash/eql-v3/text_search@1') + expect(toV3CodecId('boolean')).toBe('cipherstash/eql-v3/boolean@1') + }) + + it('records the public.* native type (never eql_v3.*) for text_search with full caps', () => { + const meta = V3_DOMAIN_META_BY_CODEC_ID.get('cipherstash/eql-v3/text_search@1') + expect(meta?.nativeType).toBe('public.text_search') + expect(meta?.nativeType.startsWith('public.')).toBe(true) + expect(meta?.castAs).toBe('string') + expect(meta?.capabilities).toEqual({ equality: true, orderAndRange: true, freeTextSearch: true }) + expect(Object.keys(meta?.indexes ?? {}).sort()).toEqual(['match', 'ore', 'unique']) + }) + + it('marks public.boolean as storage-only (no indexes, all capabilities false)', () => { + const meta = V3_DOMAIN_META_BY_CODEC_ID.get('cipherstash/eql-v3/boolean@1') + expect(meta?.nativeType).toBe('public.boolean') + expect(meta?.castAs).toBe('boolean') + expect(meta?.capabilities).toEqual({ equality: false, orderAndRange: false, freeTextSearch: false }) + expect(Object.keys(meta?.indexes ?? {})).toEqual([]) + }) + + it('exposes bigint as a first-class family with bigint castAs', () => { + expect(V3_DOMAIN_META_BY_CODEC_ID.get('cipherstash/eql-v3/bigint@1')?.castAs).toBe('bigint') + expect(V3_DOMAIN_META_BY_CODEC_ID.get('cipherstash/eql-v3/bigint_ord@1')?.nativeType).toBe('public.bigint_ord') + }) + + it('derives timestamp castAs distinctly from date', () => { + expect(V3_DOMAIN_META_BY_CODEC_ID.get('cipherstash/eql-v3/timestamp@1')?.castAs).toBe('timestamp') + expect(V3_DOMAIN_META_BY_CODEC_ID.get('cipherstash/eql-v3/date@1')?.castAs).toBe('date') + }) + + it('maps native type back to its concrete factory', () => { + const factory = V3_FACTORY_BY_NATIVE_TYPE.get('public.integer_ord') + expect(factory).toBeDefined() + expect(factory?.('score').getEqlType()).toBe('public.integer_ord') + }) + + it('maps castAs to the envelope type name (bigint distinct from number)', () => { + expect(envelopeTypeNameForCastAs('string')).toBe('EncryptedString') + expect(envelopeTypeNameForCastAs('number')).toBe('EncryptedNumber') + expect(envelopeTypeNameForCastAs('bigint')).toBe('EncryptedBigInt') + expect(envelopeTypeNameForCastAs('date')).toBe('EncryptedDate') + expect(envelopeTypeNameForCastAs('timestamp')).toBe('EncryptedDate') + expect(envelopeTypeNameForCastAs('boolean')).toBe('EncryptedBoolean') + }) + + it('includes *_ord_ore in the full meta but excludes it from the exposed entries', () => { + expect(V3_DOMAIN_META_BY_CODEC_ID.has('cipherstash/eql-v3/integer_ord_ore@1')).toBe(true) + expect(isOrdOreDomain('integer_ord_ore')).toBe(true) + const exposedBare = new Set(EXPOSED_DOMAIN_ENTRIES.map(([, m]) => m.bareDomain)) + expect([...exposedBare].some((b) => b.endsWith('_ord_ore'))).toBe(false) + expect(exposedBare.has('integer_ord')).toBe(true) + expect(exposedBare.has('bigint')).toBe(true) + }) + + it('every codec id is v3-namespaced and unique', () => { + expect(new Set(V3_CODEC_IDS).size).toBe(V3_CODEC_IDS.length) + for (const id of V3_CODEC_IDS) expect(id.startsWith('cipherstash/eql-v3/')).toBe(true) + }) +}) +``` + +- [ ] **Step 3: Run test to verify it fails** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/catalog` +Expected: FAIL — `Cannot find module '../../src/v3/catalog'`. + +- [ ] **Step 4: Write minimal implementation** + +Create `packages/prisma-next/src/v3/catalog.ts`: + +```ts +/** + * v3 domain catalog — derived (never hand-maintained) from the imported + * `DOMAIN_REGISTRY`. For each bare domain name we probe its factory and read + * `getEqlType()` (the `public.*` native type), `getQueryCapabilities()`, and + * `build()` (`cast_as` + `indexes`) off the instance. Codec id is + * `cipherstash/eql-v3/${bareDomain}@1` — the `public.` prefix is stripped, and + * the `eql-v3` token is a logical version tag, NOT the operator schema. + */ +import { + type AnyEncryptedV3Column, + DOMAIN_REGISTRY, + type QueryCapabilities, + type V3ColumnFactory, +} from '@cipherstash/stack/eql/v3' + +export type V3CastAs = 'string' | 'number' | 'bigint' | 'boolean' | 'date' | 'timestamp' + +export interface V3DomainMeta { + readonly nativeType: `public.${string}` + readonly bareDomain: string + readonly castAs: V3CastAs + readonly capabilities: QueryCapabilities + readonly indexes: Readonly> +} + +export function toV3CodecId(bareDomain: string): string { + return `cipherstash/eql-v3/${bareDomain}@1` +} + +const PROBE = '__probe__' + +function metaFor(bareDomain: string, factory: V3ColumnFactory): V3DomainMeta { + const column = factory(PROBE) + const built = column.build() + return { + nativeType: column.getEqlType(), + bareDomain, + castAs: built.cast_as as V3CastAs, + capabilities: column.getQueryCapabilities(), + indexes: built.indexes, + } +} + +const registryEntries: ReadonlyArray = + Object.entries(DOMAIN_REGISTRY) + +const metaEntries: Array = registryEntries.map( + ([bareDomain, factory]) => [toV3CodecId(bareDomain), metaFor(bareDomain, factory)] as const, +) + +export const V3_DOMAIN_META_BY_CODEC_ID: ReadonlyMap = new Map(metaEntries) + +export const V3_CODEC_IDS: readonly string[] = metaEntries.map(([id]) => id) + +export const V3_FACTORY_BY_NATIVE_TYPE: ReadonlyMap< + string, + (name: string) => AnyEncryptedV3Column +> = new Map(registryEntries.map(([, factory]) => [factory(PROBE).getEqlType(), factory] as const)) + +export function isOrdOreDomain(bareDomain: string): boolean { + return bareDomain.endsWith('_ord_ore') +} + +export const EXPOSED_DOMAIN_ENTRIES: ReadonlyArray = + metaEntries.filter(([, meta]) => !isOrdOreDomain(meta.bareDomain)) + +export function envelopeTypeNameForCastAs( + castAs: V3CastAs, +): 'EncryptedString' | 'EncryptedNumber' | 'EncryptedBigInt' | 'EncryptedDate' | 'EncryptedBoolean' { + switch (castAs) { + case 'string': + return 'EncryptedString' + case 'number': + return 'EncryptedNumber' + case 'bigint': + return 'EncryptedBigInt' + case 'date': + case 'timestamp': + return 'EncryptedDate' + case 'boolean': + return 'EncryptedBoolean' + } +} +``` + +- [ ] **Step 5: Run test to verify it passes** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/catalog` +Expected: PASS (9 tests). + +- [ ] **Step 6: Commit** + +```bash +git add packages/stack/src/eql/v3/index.ts packages/prisma-next/src/v3/catalog.ts packages/prisma-next/test/v3/catalog.test.ts +git commit -m "feat(prisma-next): derive v3 domain catalog + codec ids from stack DOMAIN_REGISTRY" +``` + +--- + +### Task 2: `constants-v3.ts` — codec-id closed union, traits, invariants, drift test + +Produce the generated closed union (`CIPHERSTASH_V3_CODEC_IDS` → `CipherstashV3CodecId` → set + guard), the per-capability trait mapping, the FFI-catalog invariant drift test, and the migration name/invariant constants. Mirrors the v2 `constants.ts` shape. There are NO family ids and NO resolver in design A. + +**Files:** +- Create: `packages/prisma-next/src/extension-metadata/constants-v3.ts` +- Test: `packages/prisma-next/test/v3/constants-v3.test.ts`, `packages/prisma-next/test/v3/catalog-invariants.test.ts` + +**Interfaces:** +- Consumes: Task 1 `V3_CODEC_IDS`, `V3_DOMAIN_META_BY_CODEC_ID`, `EXPOSED_DOMAIN_ENTRIES`; `@cipherstash/stack/eql/v3` types `EqlTypeForColumn`, `AnyEncryptedV3Column`, `QueryCapabilities`; existing v2 `CIPHERSTASH_CODEC_IDS`, `CIPHERSTASH_TRAIT_EQUALITY`, `CIPHERSTASH_TRAIT_ORDER_AND_RANGE`, `CIPHERSTASH_TRAIT_FREE_TEXT_SEARCH` (`./constants`). +- Produces: + - `type CipherstashV3CodecId` (compile-time closed union, one id per catalog domain) + - `const CIPHERSTASH_V3_CODEC_IDS: readonly CipherstashV3CodecId[]` + - `const CIPHERSTASH_V3_CODEC_ID_SET: ReadonlySet` + - `function isCipherstashV3CodecId(id: string): id is CipherstashV3CodecId` + - re-exported trait constants; `function v3TraitsForCapabilities(caps: QueryCapabilities): readonly string[]` + - `const CIPHERSTASH_V3_BASELINE_MIGRATION_NAME = '20260601T0100_install_eql_v3_bundle'` + - `const CIPHERSTASH_V3_INVARIANTS = { installBundle: 'cipherstash:install-eql-v3-bundle-v1' } as const` + - `const CIPHERSTASH_V3_SPACE_ID` / `const CIPHERSTASH_V3_EXTENSION_VERSION` — v3's own distinct extension identity (decision 1b), asserted ≠ the v2 id/version. + +- [ ] **Step 1: Write the failing test** + +Create `packages/prisma-next/test/v3/constants-v3.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { CIPHERSTASH_CODEC_IDS } from '../../src/extension-metadata/constants' +import { V3_CODEC_IDS } from '../../src/v3/catalog' +import { + CIPHERSTASH_V3_BASELINE_MIGRATION_NAME, + CIPHERSTASH_V3_CODEC_ID_SET, + CIPHERSTASH_V3_CODEC_IDS, + CIPHERSTASH_V3_INVARIANTS, + isCipherstashV3CodecId, + v3TraitsForCapabilities, +} from '../../src/extension-metadata/constants-v3' + +describe('constants-v3', () => { + it('the PINNED codec-id tuple equals the registry-derived set (drift guard, runtime side)', () => { + // The compile-time `satisfies` + `Exclude` guards catch type drift; this + // catches derivation drift (e.g. the registry iteration silently dropping + // or reordering a domain). Both directions must hold. + expect(new Set(CIPHERSTASH_V3_CODEC_IDS)).toEqual(new Set(V3_CODEC_IDS)) + expect(CIPHERSTASH_V3_CODEC_IDS.length).toBe(V3_CODEC_IDS.length) + expect(new Set(CIPHERSTASH_V3_CODEC_IDS).size).toBe(CIPHERSTASH_V3_CODEC_IDS.length) + }) + + it('v3 and v2 codec-id sets are disjoint', () => { + const v2 = new Set(CIPHERSTASH_CODEC_IDS) + for (const id of CIPHERSTASH_V3_CODEC_IDS) expect(v2.has(id)).toBe(false) + }) + + it('guard narrows only v3 ids', () => { + expect(isCipherstashV3CodecId('cipherstash/eql-v3/text_eq@1')).toBe(true) + expect(isCipherstashV3CodecId('cipherstash/string@1')).toBe(false) + expect(CIPHERSTASH_V3_CODEC_ID_SET.has('cipherstash/eql-v3/text_eq@1')).toBe(true) + }) + + it('derives cipherstash-namespaced traits from capabilities', () => { + expect( + v3TraitsForCapabilities({ equality: true, orderAndRange: true, freeTextSearch: true }).sort(), + ).toEqual(['cipherstash:equality', 'cipherstash:free-text-search', 'cipherstash:order-and-range']) + expect(v3TraitsForCapabilities({ equality: false, orderAndRange: false, freeTextSearch: false })).toEqual([]) + }) + + it('pins the migration name + invariant', () => { + expect(CIPHERSTASH_V3_BASELINE_MIGRATION_NAME).toBe('20260601T0100_install_eql_v3_bundle') + expect(CIPHERSTASH_V3_INVARIANTS.installBundle).toBe('cipherstash:install-eql-v3-bundle-v1') + }) +}) +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/constants-v3` +Expected: FAIL — module not found. + +- [ ] **Step 3: Write minimal implementation** + +Create `packages/prisma-next/src/extension-metadata/constants-v3.ts`: + +```ts +/** + * v3 codec ids, traits, and migration invariants. Parallel to the v2 + * `constants.ts`; imports NO v2 codec/wire code. Codec ids are the generated + * closed union derived from the imported catalog: `(codec id) ⟺ (public.* + * domain)` is bijective, so the id set IS the catalog. + */ +import type { + AnyEncryptedV3Column, + EqlTypeForColumn, + QueryCapabilities, +} from '@cipherstash/stack/eql/v3' +import { V3_CODEC_IDS } from '../v3/catalog' +import { + CIPHERSTASH_TRAIT_EQUALITY, + CIPHERSTASH_TRAIT_FREE_TEXT_SEARCH, + CIPHERSTASH_TRAIT_ORDER_AND_RANGE, +} from './constants' + +export { + CIPHERSTASH_TRAIT_EQUALITY, + CIPHERSTASH_TRAIT_FREE_TEXT_SEARCH, + CIPHERSTASH_TRAIT_ORDER_AND_RANGE, +} + +/** Strip the `public.` schema qualifier at the type level. */ +type StripSchema = T extends `public.${infer D}` ? D : never + +/** Compile-time closed union: one concrete codec id per catalog domain. A + * catalog change that adds/drops a domain surfaces as a type error at the + * `satisfies` line below. */ +export type CipherstashV3CodecId = + `cipherstash/eql-v3/${StripSchema>}@1` + +/** + * PINNED literal tuple — the compile-time source of truth for the v3 codec-id + * set. Hand-listed, one entry per catalog domain (INCLUDING the `*_ord_ore` + * variants that authoring does not expose but the codec/derivation layer still + * decodes). This is deliberately NOT `V3_CODEC_IDS as readonly CipherstashV3CodecId[]` + * — that is a masking cast that asserts the derived array IS the union and so + * catches NO drift. Here, catalog drift fails to COMPILE in both directions: + * - `satisfies readonly CipherstashV3CodecId[]` fails if a listed id is no + * longer a catalog domain (a domain removed / renamed). + * - `_NoUnpinnedDomain` (below) fails if the catalog gained a domain not + * pinned here. + * The Step-1 runtime test additionally asserts this equals the registry-derived + * `V3_CODEC_IDS` (guarding the derivation itself). + */ +export const CIPHERSTASH_V3_CODEC_IDS = [ + 'cipherstash/eql-v3/integer@1', + 'cipherstash/eql-v3/integer_eq@1', + 'cipherstash/eql-v3/integer_ord_ore@1', + 'cipherstash/eql-v3/integer_ord@1', + 'cipherstash/eql-v3/smallint@1', + 'cipherstash/eql-v3/smallint_eq@1', + 'cipherstash/eql-v3/smallint_ord_ore@1', + 'cipherstash/eql-v3/smallint_ord@1', + 'cipherstash/eql-v3/bigint@1', + 'cipherstash/eql-v3/bigint_eq@1', + 'cipherstash/eql-v3/bigint_ord_ore@1', + 'cipherstash/eql-v3/bigint_ord@1', + 'cipherstash/eql-v3/date@1', + 'cipherstash/eql-v3/date_eq@1', + 'cipherstash/eql-v3/date_ord_ore@1', + 'cipherstash/eql-v3/date_ord@1', + 'cipherstash/eql-v3/timestamp@1', + 'cipherstash/eql-v3/timestamp_eq@1', + 'cipherstash/eql-v3/timestamp_ord_ore@1', + 'cipherstash/eql-v3/timestamp_ord@1', + 'cipherstash/eql-v3/numeric@1', + 'cipherstash/eql-v3/numeric_eq@1', + 'cipherstash/eql-v3/numeric_ord_ore@1', + 'cipherstash/eql-v3/numeric_ord@1', + 'cipherstash/eql-v3/text@1', + 'cipherstash/eql-v3/text_eq@1', + 'cipherstash/eql-v3/text_match@1', + 'cipherstash/eql-v3/text_ord_ore@1', + 'cipherstash/eql-v3/text_ord@1', + 'cipherstash/eql-v3/text_search@1', + 'cipherstash/eql-v3/boolean@1', + 'cipherstash/eql-v3/real@1', + 'cipherstash/eql-v3/real_eq@1', + 'cipherstash/eql-v3/real_ord_ore@1', + 'cipherstash/eql-v3/real_ord@1', + 'cipherstash/eql-v3/double@1', + 'cipherstash/eql-v3/double_eq@1', + 'cipherstash/eql-v3/double_ord_ore@1', + 'cipherstash/eql-v3/double_ord@1', +] as const satisfies readonly CipherstashV3CodecId[] + +// Bidirectional drift guard — the direction `satisfies` cannot catch. If the +// catalog gains a domain that is not pinned above, `Exclude<...>` is non-`never` +// and this assignment fails to compile with the offending id in the error. +type _PinnedId = (typeof CIPHERSTASH_V3_CODEC_IDS)[number] +type _NoUnpinnedDomain = [Exclude] extends [never] + ? true + : ['catalog gained an unpinned codec id', Exclude] +const _driftGuard: _NoUnpinnedDomain = true +void _driftGuard + +export const CIPHERSTASH_V3_CODEC_ID_SET: ReadonlySet = new Set(CIPHERSTASH_V3_CODEC_IDS) + +export function isCipherstashV3CodecId(id: string): id is CipherstashV3CodecId { + return CIPHERSTASH_V3_CODEC_ID_SET.has(id) +} + +export function v3TraitsForCapabilities(caps: QueryCapabilities): readonly string[] { + const traits: string[] = [] + if (caps.equality) traits.push(CIPHERSTASH_TRAIT_EQUALITY) + if (caps.orderAndRange) traits.push(CIPHERSTASH_TRAIT_ORDER_AND_RANGE) + if (caps.freeTextSearch) traits.push(CIPHERSTASH_TRAIT_FREE_TEXT_SEARCH) + return traits +} + +export const CIPHERSTASH_V3_BASELINE_MIGRATION_NAME = '20260601T0100_install_eql_v3_bundle' + +export const CIPHERSTASH_V3_INVARIANTS = { + installBundle: 'cipherstash:install-eql-v3-bundle-v1', +} as const + +// v3's OWN extension identity (decision 1b) — DISTINCT from the v2 +// CIPHERSTASH_SPACE_ID / CIPHERSTASH_EXTENSION_VERSION. v3 and v2 are separate +// entry points, never co-registered; the distinct id keeps the two extension +// descriptors cleanly separate. Confirm the exact SPACE_ID shape the framework +// expects against the v2 `constants.ts` (e.g. a namespaced string / UUID) and +// mirror it under the `eql-v3` namespace. +export const CIPHERSTASH_V3_SPACE_ID = 'cipherstash/eql-v3' as const +export const CIPHERSTASH_V3_EXTENSION_VERSION = 1 as const +``` + +Add a test asserting the v3 extension id/version are **not equal** to the v2 `CIPHERSTASH_SPACE_ID` / `CIPHERSTASH_EXTENSION_VERSION` (imported from `../../src/extension-metadata/constants`), so the separation can never silently regress. + +- [ ] **Step 4: Run test to verify it passes** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/constants-v3` +Expected: PASS (4 tests). + +- [ ] **Step 5: Add the catalog-invariant drift test** + +Create `packages/prisma-next/test/v3/catalog-invariants.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { DOMAIN_REGISTRY } from '@cipherstash/stack/eql/v3' +import { EXPOSED_DOMAIN_ENTRIES, V3_DOMAIN_META_BY_CODEC_ID } from '../../src/v3/catalog' + +describe('FFI catalog invariants (non-structural — pinned)', () => { + const bareDomains = Object.keys(DOMAIN_REGISTRY) + + it('exposes no searchable JSON v3 domain', () => { + expect(bareDomains.some((d) => d.includes('json'))).toBe(false) + }) + + it('public.boolean is storage-only (advertises no equality, no indexes)', () => { + const bool = V3_DOMAIN_META_BY_CODEC_ID.get('cipherstash/eql-v3/boolean@1') + expect(bool?.nativeType).toBe('public.boolean') + expect(bool?.capabilities.equality).toBe(false) + expect(Object.keys(bool?.indexes ?? {})).toEqual([]) + // there is only one boolean domain (no boolean_eq) + expect(bareDomains.filter((d) => d.startsWith('boolean'))).toEqual(['boolean']) + }) + + it('the BigInt family IS exposed (base/_eq/_ord), *OrdOre is not', () => { + const exposedBare = EXPOSED_DOMAIN_ENTRIES.map(([, m]) => m.bareDomain) + expect(exposedBare).toEqual(expect.arrayContaining(['bigint', 'bigint_eq', 'bigint_ord'])) + expect(exposedBare).not.toContain('bigint_ord_ore') + expect(exposedBare.some((b) => b.endsWith('_ord_ore'))).toBe(false) + }) + + it('native types are public.* while operator SQL is eql_v3.* (schema split not collapsed)', () => { + for (const [, meta] of V3_DOMAIN_META_BY_CODEC_ID) { + expect(meta.nativeType.startsWith('public.')).toBe(true) + expect(meta.nativeType.startsWith('eql_v3.')).toBe(false) + } + }) +}) +``` + +- [ ] **Step 6: Run and commit** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/constants-v3 v3/catalog-invariants` +Expected: PASS. + +```bash +git add packages/prisma-next/src/extension-metadata/constants-v3.ts packages/prisma-next/test/v3/constants-v3.test.ts packages/prisma-next/test/v3/catalog-invariants.test.ts +git commit -m "feat(prisma-next): v3 codec-id closed union, traits, invariants, drift test" +``` + +--- + +### Task 3: Authoring surface — derived per-domain constructors + `*V2` aliases + +Rewrite `contract-authoring.ts` (PSL) and `exports/column-types.ts` (TS factories) so every exposed v3 domain gets one concrete constructor (static codec id + `public.*` native type + `{ castAs, capabilities }` typeParams, NO options), derived from `EXPOSED_DOMAIN_ENTRIES`. Rename the six pre-existing v2 constructors to their `*V2` forms (keeping v2 codec ids + `eql_v2_encrypted`). The PSL constructor name and the TS factory name are the mechanical `Encrypted` / `encrypted` transform of the bare domain. + +**Files:** +- Modify: `packages/prisma-next/src/contract-authoring.ts` +- Modify: `packages/prisma-next/src/exports/column-types.ts` +- Test: `packages/prisma-next/test/authoring.test.ts` (add/retarget v3 + V2 cases), `packages/prisma-next/test/column-types.test.ts` (add v3 + V2 cases) + +**Interfaces:** +- Consumes: Task 1 `EXPOSED_DOMAIN_ENTRIES`, `V3DomainMeta`; existing v2 `CIPHERSTASH_STRING_CODEC_ID`, `CIPHERSTASH_DOUBLE_CODEC_ID`, `CIPHERSTASH_BIGINT_CODEC_ID`, `CIPHERSTASH_DATE_CODEC_ID`, `CIPHERSTASH_BOOLEAN_CODEC_ID`, `CIPHERSTASH_JSON_CODEC_ID`, `EQL_V2_ENCRYPTED_TYPE` (`./extension-metadata/constants`); framework `AuthoringTypeNamespace` (`@prisma-next/framework-components/authoring`). +- Produces: + - `function v3PascalName(bareDomain: string): string` / `function v3CamelName(bareDomain: string): string` (shared name transform; exported from `contract-authoring.ts` for reuse by `column-types.ts` and tests). + - PSL: `cipherstashAuthoringTypes.cipherstash` carrying one constructor per exposed domain (`EncryptedText`, `EncryptedTextEq`, `EncryptedTextOrd`, `EncryptedTextMatch`, `EncryptedTextSearch`, `EncryptedInteger`/`Eq`/`Ord`, `EncryptedSmallint`/`Eq`/`Ord`, `EncryptedBigInt`/`Eq`/`Ord`, `EncryptedNumeric`/`Eq`/`Ord`, `EncryptedReal`/`Eq`/`Ord`, `EncryptedDouble`/`Eq`/`Ord`, `EncryptedDate`/`Eq`/`Ord`, `EncryptedTimestamp`/`Eq`/`Ord`, `EncryptedBoolean`) plus v2 aliases `EncryptedStringV2`, `EncryptedDoubleV2`, `EncryptedBigIntV2`, `EncryptedDateV2`, `EncryptedBooleanV2`, `EncryptedJsonV2`. + - TS: one factory per exposed domain (`encryptedTextSearch`, `encryptedIntegerOrd`, `encryptedBigIntOrd`, `encryptedBoolean`, …) returning `{ codecId, nativeType, typeParams: { castAs, capabilities } }`, plus `encryptedStringV2`, `encryptedDoubleV2`, `encryptedBigIntV2`, `encryptedDateV2`, `encryptedBooleanV2`, `encryptedJsonV2`. + +- [ ] **Step 0: Confirm the framework accepts static (non-arg) typeParams** + +The v2 constructors use `{ kind: 'arg', ... }` typeParam nodes because they take options. v3 constructors take no options, so their `typeParams` are static literal values (`castAs: 'string'`, `capabilities: { equality: true, ... }`). Confirm the framework serializes static literal typeParam values: + +Run: `sed -n '1,80p' packages/prisma-next/node_modules/@prisma-next/framework-components/dist/authoring.d.ts 2>/dev/null || grep -rn "typeParams" packages/prisma-next/src/contract-authoring.ts` +Expected: `typeParams` values are `unknown`/JSON — static literals are allowed. If the framework requires `{ kind: 'arg' | 'literal', ... }` node wrappers, wrap each static value as `{ kind: 'literal', value }` in Step 2 (adjust both the PSL emit and the Step-1 test expectation together). Record the confirmed shape in a comment atop `contract-authoring.ts`. + +- [ ] **Step 1: Write the failing test (PSL authoring outputs, derived 1:1)** + +Replace the v2 authoring assertions in `packages/prisma-next/test/authoring.test.ts` with the v3 + V2 surface: + +```ts +import { EXPOSED_DOMAIN_ENTRIES } from '../src/v3/catalog' +import { cipherstashAuthoringTypes, v3PascalName } from '../src/contract-authoring' + +describe('cipherstash v3 authoring (concrete per-domain, static descriptors)', () => { + const ns = cipherstashAuthoringTypes.cipherstash as Record }> + + it('emits exactly one constructor per exposed domain, matching the catalog values', () => { + for (const [codecId, meta] of EXPOSED_DOMAIN_ENTRIES) { + const name = v3PascalName(meta.bareDomain) + const ctor = ns[name] + expect(ctor, `missing constructor ${name}`).toBeDefined() + expect(ctor.output).toMatchObject({ + codecId, + nativeType: meta.nativeType, + typeParams: { castAs: meta.castAs, capabilities: meta.capabilities }, + }) + } + }) + + it('EncryptedTextSearch → public.text_search with full capabilities', () => { + expect(ns.EncryptedTextSearch.output).toMatchObject({ + codecId: 'cipherstash/eql-v3/text_search@1', + nativeType: 'public.text_search', + typeParams: { + castAs: 'string', + capabilities: { equality: true, orderAndRange: true, freeTextSearch: true }, + }, + }) + }) + + it('EncryptedBoolean → storage-only public.boolean (no equality constructor exists)', () => { + expect(ns.EncryptedBoolean.output).toMatchObject({ + codecId: 'cipherstash/eql-v3/boolean@1', + nativeType: 'public.boolean', + typeParams: { castAs: 'boolean', capabilities: { equality: false, orderAndRange: false, freeTextSearch: false } }, + }) + expect(ns.EncryptedBooleanEq).toBeUndefined() + }) + + it('BigInt family is present (public.bigint*, cast_as bigint); no *OrdOre, no v3 Json/String', () => { + expect(ns.EncryptedBigInt.output).toMatchObject({ nativeType: 'public.bigint', typeParams: { castAs: 'bigint' } }) + expect(ns.EncryptedBigIntEq.output).toMatchObject({ nativeType: 'public.bigint_eq' }) + expect(ns.EncryptedBigIntOrd.output).toMatchObject({ nativeType: 'public.bigint_ord' }) + expect(ns.EncryptedBigIntOrdOre).toBeUndefined() + expect(ns.EncryptedIntegerOrdOre).toBeUndefined() + expect(ns.EncryptedTextOrdOre).toBeUndefined() + expect(ns.EncryptedJson).toBeUndefined() // no v3 searchable JSON + expect(ns.EncryptedString).toBeUndefined() // v3 text uses EncryptedText* + }) + + it('*V2 aliases keep v2 codec ids + eql_v2_encrypted', () => { + expect(ns.EncryptedStringV2.output).toMatchObject({ codecId: 'cipherstash/string@1', nativeType: 'eql_v2_encrypted' }) + expect(ns.EncryptedJsonV2.output).toMatchObject({ codecId: 'cipherstash/json@1', nativeType: 'eql_v2_encrypted' }) + expect(ns.EncryptedBigIntV2.output).toMatchObject({ codecId: 'cipherstash/bigint@1', nativeType: 'eql_v2_encrypted' }) + }) +}) +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `pnpm --filter @cipherstash/prisma-next test -- authoring` +Expected: FAIL — `v3PascalName` / constructors undefined. + +- [ ] **Step 3: Write the PSL implementation (derived)** + +Replace `packages/prisma-next/src/contract-authoring.ts` with: + +```ts +import type { AuthoringTypeNamespace } from '@prisma-next/framework-components/authoring' +import { + CIPHERSTASH_BIGINT_CODEC_ID, + CIPHERSTASH_BOOLEAN_CODEC_ID, + CIPHERSTASH_DATE_CODEC_ID, + CIPHERSTASH_DOUBLE_CODEC_ID, + CIPHERSTASH_JSON_CODEC_ID, + CIPHERSTASH_STRING_CODEC_ID, + EQL_V2_ENCRYPTED_TYPE, +} from './extension-metadata/constants' +import { EXPOSED_DOMAIN_ENTRIES, type V3DomainMeta } from './v3/catalog' + +// The stem of a bare domain maps to a JS-friendly display label; the suffix +// after the first `_` names the capability tier. `bigint` deliberately renders +// as `BigInt` (capital I) while `smallint` stays `Smallint` — this table is the +// single source for both PSL (`Encrypted…`) and TS (`encrypted…`) names. +const STEM_LABEL: Record = { + text: 'Text', + integer: 'Integer', + smallint: 'Smallint', + bigint: 'BigInt', + numeric: 'Numeric', + real: 'Real', + double: 'Double', + date: 'Date', + timestamp: 'Timestamp', + boolean: 'Boolean', +} +const SUFFIX_LABEL: Record = { + '': '', + eq: 'Eq', + ord: 'Ord', + match: 'Match', + search: 'Search', +} + +function coreName(bareDomain: string): string { + const i = bareDomain.indexOf('_') + const stem = i === -1 ? bareDomain : bareDomain.slice(0, i) + const suffix = i === -1 ? '' : bareDomain.slice(i + 1) + const stemLabel = STEM_LABEL[stem] + const suffixLabel = SUFFIX_LABEL[suffix] + if (stemLabel === undefined || suffixLabel === undefined) { + throw new Error( + `contract-authoring: cannot name exposed domain "${bareDomain}" — extend STEM_LABEL/SUFFIX_LABEL.`, + ) + } + return `${stemLabel}${suffixLabel}` +} + +/** `text_search` → `EncryptedTextSearch`, `bigint_ord` → `EncryptedBigIntOrd`. */ +export function v3PascalName(bareDomain: string): string { + return `Encrypted${coreName(bareDomain)}` +} +/** `text_search` → `encryptedTextSearch`, `bigint_ord` → `encryptedBigIntOrd`. */ +export function v3CamelName(bareDomain: string): string { + return `encrypted${coreName(bareDomain)}` +} + +function v3Constructor(codecId: string, meta: V3DomainMeta) { + return { + kind: 'typeConstructor', + args: [], + output: { + codecId, + nativeType: meta.nativeType, + // Static, not `{ kind: 'arg' }`: there are no options — the constructor + // IS the capability set. (If the framework requires literal-node + // wrappers, wrap these two values per Step 0.) + typeParams: { castAs: meta.castAs, capabilities: meta.capabilities }, + }, + } as const +} + +// --- v2 legacy aliases (verbatim old v2 outputs, renamed to *V2) --- +const v2StringOptions = { + kind: 'object', + name: 'options', + optional: true, + properties: { + equality: { kind: 'boolean', optional: true }, + freeTextSearch: { kind: 'boolean', optional: true }, + orderAndRange: { kind: 'boolean', optional: true }, + }, +} as const +const argTrue = (path: string) => ({ kind: 'arg', index: 0, path: [path], default: true }) as const + +const v2String = { + kind: 'typeConstructor', + args: [v2StringOptions], + output: { + codecId: CIPHERSTASH_STRING_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + typeParams: { + equality: argTrue('equality'), + freeTextSearch: argTrue('freeTextSearch'), + orderAndRange: argTrue('orderAndRange'), + }, + }, +} as const + +const v2Scalar = (codecId: string) => + ({ + kind: 'typeConstructor', + args: [ + { + kind: 'object', + name: 'options', + optional: true, + properties: { + equality: { kind: 'boolean', optional: true }, + orderAndRange: { kind: 'boolean', optional: true }, + }, + }, + ], + output: { + codecId, + nativeType: EQL_V2_ENCRYPTED_TYPE, + typeParams: { equality: argTrue('equality'), orderAndRange: argTrue('orderAndRange') }, + }, + }) as const + +const v2Boolean = { + kind: 'typeConstructor', + args: [ + { kind: 'object', name: 'options', optional: true, properties: { equality: { kind: 'boolean', optional: true } } }, + ], + output: { + codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + typeParams: { equality: argTrue('equality') }, + }, +} as const + +const v2Json = { + kind: 'typeConstructor', + args: [ + { + kind: 'object', + name: 'options', + optional: true, + properties: { searchableJson: { kind: 'boolean', optional: true } }, + }, + ], + output: { + codecId: CIPHERSTASH_JSON_CODEC_ID, + nativeType: EQL_V2_ENCRYPTED_TYPE, + typeParams: { searchableJson: argTrue('searchableJson') }, + }, +} as const + +const v3Constructors: Record = Object.fromEntries( + EXPOSED_DOMAIN_ENTRIES.map(([codecId, meta]) => [v3PascalName(meta.bareDomain), v3Constructor(codecId, meta)]), +) + +export const cipherstashAuthoringTypes = { + cipherstash: { + ...v3Constructors, + // v2 legacy aliases + EncryptedStringV2: v2String, + EncryptedDoubleV2: v2Scalar(CIPHERSTASH_DOUBLE_CODEC_ID), + EncryptedBigIntV2: v2Scalar(CIPHERSTASH_BIGINT_CODEC_ID), + EncryptedDateV2: v2Scalar(CIPHERSTASH_DATE_CODEC_ID), + EncryptedBooleanV2: v2Boolean, + EncryptedJsonV2: v2Json, + }, +} as unknown as AuthoringTypeNamespace +``` + +> Dynamic construction loses the literal object type that `as const satisfies AuthoringTypeNamespace` gave the old file; the `as unknown as AuthoringTypeNamespace` cast is deliberate, and the 1:1 test in Step 1 is what enforces the shape at runtime. + +- [ ] **Step 4: Run the PSL authoring test** + +Run: `pnpm --filter @cipherstash/prisma-next test -- authoring` +Expected: PASS for the v3 + V2 cases. + +- [ ] **Step 5: Retarget pre-existing v2 assertions in `psl-interpretation*.test.ts`** + +Any assertion expecting the unqualified `EncryptedString`/`EncryptedDouble`/`EncryptedBigInt`/`EncryptedDate`/`EncryptedBoolean`/`EncryptedJson` to lower to `cipherstash/*@1` + `eql_v2_encrypted` must be retargeted to the `*V2` name (or moved to a v3 expectation where the unqualified name is now a v3 domain). + +Run: `pnpm --filter @cipherstash/prisma-next test -- psl-interpretation` +Expected: the failing assertions name the exact lines; update each to `EncryptedStringV2` / `EncryptedDoubleV2` / etc. + +- [ ] **Step 6: Write the failing TS-factory test** + +Add to `packages/prisma-next/test/column-types.test.ts`: + +```ts +import * as columnTypes from '../src/exports/column-types' +import { EXPOSED_DOMAIN_ENTRIES } from '../src/v3/catalog' +import { v3CamelName } from '../src/contract-authoring' + +describe('v3 TS factories', () => { + it('one factory per exposed domain, returning the concrete descriptor', () => { + for (const [codecId, meta] of EXPOSED_DOMAIN_ENTRIES) { + const fn = (columnTypes as Record unknown>)[v3CamelName(meta.bareDomain)] + expect(fn, `missing factory ${v3CamelName(meta.bareDomain)}`).toBeTypeOf('function') + expect(fn()).toEqual({ + codecId, + nativeType: meta.nativeType, + typeParams: { castAs: meta.castAs, capabilities: meta.capabilities }, + }) + } + }) + + it('encryptedBigIntOrd() emits public.bigint_ord with bigint castAs', () => { + expect(columnTypes.encryptedBigIntOrd()).toMatchObject({ + codecId: 'cipherstash/eql-v3/bigint_ord@1', + nativeType: 'public.bigint_ord', + typeParams: { castAs: 'bigint' }, + }) + }) + + it('encryptedStringV2() keeps the v2 codec id', () => { + expect(columnTypes.encryptedStringV2()).toEqual({ + codecId: 'cipherstash/string@1', + nativeType: 'eql_v2_encrypted', + typeParams: { equality: true, freeTextSearch: true, orderAndRange: true }, + }) + }) +}) +``` + +- [ ] **Step 7: Implement the TS factories** + +In `packages/prisma-next/src/exports/column-types.ts`, derive a descriptor record and expose one named factory per exposed domain plus the `*V2` aliases. Add the imports and this block; keep the existing option/descriptor interfaces for the `*V2` return shapes. + +```ts +import { + type AnyEncryptedV3Column, + type PlaintextKind, + stripDomainSchema, + types, +} from '@cipherstash/stack/eql/v3' + +type StripPublic = T extends `public.${infer D}` ? D : never + +/** + * Literal-preserving authoring descriptor, generic over the concrete stack + * column `C`. `codecId`, `nativeType`, and `capabilities` are the domain's + * LITERAL types (not `string` / `unknown`), so two domains' factories are + * mutually non-assignable — this is the type spine (design "Type Discipline"). + * `castAs` is the one field the stack widens (`build(): ColumnSchema`), which is + * harmless: `nativeType` already discriminates every domain. Pinned by the + * `column-types.test-d.ts` type-level tests (Task: type-level). + */ +export interface V3ColumnDescriptor { + readonly codecId: `cipherstash/eql-v3/${StripPublic>}@1` + readonly nativeType: ReturnType + readonly typeParams: { + readonly castAs: PlaintextKind + readonly capabilities: ReturnType + } +} + +/** + * Build one authoring factory from a concrete stack `types.*` factory. VALUES + * are read from the column (`getEqlType` / `getQueryCapabilities` / `build`) — + * nothing is hardcoded — while the literal TYPES flow from `C`. The factory set + * is a typed 1:1 mirror of the exposed catalog domains, which is how "derive + * from the catalog" and "distinct static type per domain" coexist: TypeScript + * cannot hand distinct static types to values pulled from a runtime record, so + * each factory names its stack factory. Drift is caught by the Step-1 1:1 test + * (exposed factory set === registry minus `*_ord_ore`) and Task 2's drift test. + */ +function v3Authored( + make: (name: string) => C, +): () => V3ColumnDescriptor { + const probe = make('__probe__') + const nativeType = probe.getEqlType() + const descriptor = { + codecId: `cipherstash/eql-v3/${stripDomainSchema(nativeType)}@1`, + nativeType, + typeParams: { + castAs: probe.build().cast_as as PlaintextKind, + capabilities: probe.getQueryCapabilities(), + }, + } as V3ColumnDescriptor + return () => descriptor +} + +// Each factory has a DISTINCT return type (V3ColumnDescriptor); +// `encryptedBigIntOrd()` is not assignable to `encryptedText()` and vice-versa. +// Note the stack factory names: bigint is `types.Bigint*` (not `BigInt`). +// text family +export const encryptedText = v3Authored(types.Text) +export const encryptedTextEq = v3Authored(types.TextEq) +export const encryptedTextOrd = v3Authored(types.TextOrd) +export const encryptedTextMatch = v3Authored(types.TextMatch) +export const encryptedTextSearch = v3Authored(types.TextSearch) +// integer +export const encryptedInteger = v3Authored(types.Integer) +export const encryptedIntegerEq = v3Authored(types.IntegerEq) +export const encryptedIntegerOrd = v3Authored(types.IntegerOrd) +// smallint +export const encryptedSmallint = v3Authored(types.Smallint) +export const encryptedSmallintEq = v3Authored(types.SmallintEq) +export const encryptedSmallintOrd = v3Authored(types.SmallintOrd) +// bigint +export const encryptedBigInt = v3Authored(types.Bigint) +export const encryptedBigIntEq = v3Authored(types.BigintEq) +export const encryptedBigIntOrd = v3Authored(types.BigintOrd) +// numeric +export const encryptedNumeric = v3Authored(types.Numeric) +export const encryptedNumericEq = v3Authored(types.NumericEq) +export const encryptedNumericOrd = v3Authored(types.NumericOrd) +// real +export const encryptedReal = v3Authored(types.Real) +export const encryptedRealEq = v3Authored(types.RealEq) +export const encryptedRealOrd = v3Authored(types.RealOrd) +// double +export const encryptedDouble = v3Authored(types.Double) +export const encryptedDoubleEq = v3Authored(types.DoubleEq) +export const encryptedDoubleOrd = v3Authored(types.DoubleOrd) +// date +export const encryptedDate = v3Authored(types.Date) +export const encryptedDateEq = v3Authored(types.DateEq) +export const encryptedDateOrd = v3Authored(types.DateOrd) +// timestamp +export const encryptedTimestamp = v3Authored(types.Timestamp) +export const encryptedTimestampEq = v3Authored(types.TimestampEq) +export const encryptedTimestampOrd = v3Authored(types.TimestampOrd) +// boolean (storage-only) +export const encryptedBoolean = v3Authored(types.Boolean) +``` + +> Note: `V3ColumnDescriptor` widens to the framework's `ColumnTypeDescriptor` (whose `codecId` is `string`) at the framework authoring boundary — that widening is fine at the edge; prisma-next's own factories and the `.test-d.ts` retain the literals. If `PlaintextKind` is not re-exported from `@cipherstash/stack/eql/v3`, add it to that barrel (it is the `build().cast_as` union) rather than falling back to `string`. + +Add the MANDATORY type-level test (design "Type Discipline" — a runtime suite does not protect the spine). Create `packages/prisma-next/test/v3/column-types.test-d.ts`: + +```ts +import { expectTypeOf } from 'vitest' +import type { QueryCapabilities } from '@cipherstash/stack/eql/v3' +import { + encryptedBigIntOrd, + encryptedText, + encryptedTextEq, +} from '../../src/exports/column-types' + +// 1. Factories are mutually non-assignable — distinct type per domain. +expectTypeOf(encryptedTextEq()).not.toMatchTypeOf(encryptedText()) +expectTypeOf(encryptedBigIntOrd()).not.toMatchTypeOf(encryptedText()) +expectTypeOf(encryptedText()).not.toMatchTypeOf(encryptedBigIntOrd()) + +// 2. codecId is the literal union member, not `string`. +expectTypeOf(encryptedTextEq().codecId).toEqualTypeOf<'cipherstash/eql-v3/text_eq@1'>() +expectTypeOf(encryptedBigIntOrd().codecId).toEqualTypeOf<'cipherstash/eql-v3/bigint_ord@1'>() + +// 3. nativeType is the `public.*` literal, not `string`. +expectTypeOf(encryptedTextEq().nativeType).toEqualTypeOf<'public.text_eq'>() +expectTypeOf(encryptedBigIntOrd().nativeType).toEqualTypeOf<'public.bigint_ord'>() + +// 4. capabilities is (at least) the concrete QueryCapabilities, never `unknown`. +// (If the stack types each domain's capabilities as literal booleans, this +// also narrows further — the point is it is NOT `unknown`.) +expectTypeOf(encryptedTextEq().typeParams.capabilities).toMatchTypeOf() +expectTypeOf(encryptedTextEq().typeParams.capabilities).not.toBeUnknown() +``` + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/column-types.test-d` (vitest typecheck) → PASS. + +Rename the six existing factories to their `*V2` names, keeping their previous bodies verbatim (they still return `{ codecId: CIPHERSTASH_*_CODEC_ID, nativeType: EQL_V2_ENCRYPTED_TYPE, typeParams: {...} }`): + +```ts +export function encryptedStringV2(options: EncryptedStringOptions = {}) { /* previous encryptedString body */ } +export function encryptedDoubleV2(options: EncryptedNumericOptions = {}) { /* previous encryptedDouble body */ } +export function encryptedBigIntV2(options: EncryptedNumericOptions = {}) { /* previous encryptedBigInt body */ } +export function encryptedDateV2(options: EncryptedDateOptions = {}) { /* previous encryptedDate body */ } +export function encryptedBooleanV2(options: EncryptedBooleanOptions = {}) { /* previous encryptedBoolean body */ } +export function encryptedJsonV2(options: EncryptedJsonOptions = {}) { /* previous encryptedJson body */ } +``` + +If any barrel (`exports/index.ts`) re-exported the old `encryptedString`/`encryptedDouble`/… by name, update those re-exports to the `*V2` names (and add the new v3 names). Run the build to surface any dangling re-export. + +- [ ] **Step 8: Run and commit** + +Run: `pnpm --filter @cipherstash/prisma-next test -- authoring psl-interpretation column-types && pnpm --filter @cipherstash/prisma-next build` +Expected: PASS + build clean. + +```bash +git add packages/prisma-next/src/contract-authoring.ts packages/prisma-next/src/exports/column-types.ts packages/prisma-next/test/authoring.test.ts packages/prisma-next/test/column-types.test.ts packages/prisma-next/test/psl-interpretation*.test.ts +git commit -m "feat(prisma-next): derived v3 per-domain constructors + V2 aliases" +``` + +--- + +### Task 4: Runtime codecs — `EncryptedNumber` + plain-JSONB v3 cell codec + +Create the `EncryptedNumber` envelope (a **sibling** of `EncryptedDouble`, distinct `typeName`), the plain-JSONB `v3ToDriver`/`v3FromDriver`, the SDK-bound v3 cell codec + per-domain descriptors (native type = the concrete `public.*` domain), and the v3 barrel that surfaces the neutral value classes (`EncryptedString`, `EncryptedDate`, `EncryptedBoolean`, the existing `EncryptedBigInt`) plus `EncryptedNumber`. v3 codecs depend only on the neutral `EncryptedEnvelopeBase`; they never import the v2 composite/wire codec. + +**Files:** +- Create: `packages/prisma-next/src/v3/envelope-number.ts`, `packages/prisma-next/src/v3/wire-v3.ts`, `packages/prisma-next/src/v3/codec-runtime-v3.ts`, `packages/prisma-next/src/v3/barrel.ts` +- Test: `packages/prisma-next/test/v3/envelope-number.test.ts`, `packages/prisma-next/test/v3/wire-v3.test.ts`, `packages/prisma-next/test/v3/codec-runtime-v3.test.ts` + +**Interfaces:** +- Consumes: `EncryptedEnvelopeBase`, `EncryptedEnvelopeFromInternalArgs`, `EncryptedEnvelopeHandle` (`../execution/envelope-base`); neutral value classes `EncryptedString` (`../execution/envelope-string`), `EncryptedDate` (`../execution/envelope-date`), `EncryptedBoolean` (`../execution/envelope-boolean`), `EncryptedBigInt` (`../execution/envelope-bigint`); Task 1 `V3_DOMAIN_META_BY_CODEC_ID`, `V3DomainMeta`, `V3CastAs`, `envelopeTypeNameForCastAs`; Task 2 `CipherstashV3CodecId`, `v3TraitsForCapabilities`; `CipherstashSdk` (`../execution/sdk`); framework codec infra. +- Produces: + - `class EncryptedNumber extends EncryptedEnvelopeBase` (`typeName = 'EncryptedNumber'`, `static from`, `static fromInternal`). + - `function v3ToDriver(value: unknown): string | null`; `function v3FromDriver(value: string | object | null | undefined): T`. + - `class CipherstashV3CellCodec`; `function createV3CodecDescriptors(sdk): ReadonlyArray>` (one per catalog domain; `nativeType`/`targetTypes` = the concrete `public.*` domain; traits from capabilities). + - `barrel.ts` re-exporting `EncryptedNumber`, `EncryptedString`, `EncryptedDate`, `EncryptedBoolean`, `EncryptedBigInt`. + +- [ ] **Step 1: Write the failing test for `EncryptedNumber`** + +Create `packages/prisma-next/test/v3/envelope-number.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { EncryptedDouble } from '../../src/execution/envelope-double' +import { EncryptedNumber } from '../../src/v3/envelope-number' + +describe('EncryptedNumber', () => { + it('is a distinct sibling of EncryptedDouble (no cross-instanceof leak)', () => { + const n = EncryptedNumber.from(42) + expect(n).toBeInstanceOf(EncryptedNumber) + expect(n instanceof EncryptedDouble).toBe(false) + expect(EncryptedDouble.from(42) instanceof EncryptedNumber).toBe(false) + }) + it('renders the $encryptedNumber placeholder marker (distinct typeName)', () => { + expect(JSON.stringify(EncryptedNumber.from(7))).toBe('{"$encryptedNumber":""}') + }) + it('redacts and round-trips plaintext via decrypt() fast path', async () => { + const n = EncryptedNumber.from(3.14) + expect(String(n)).toBe('[REDACTED]') + expect(await n.decrypt()).toBe(3.14) + }) +}) +``` + +- [ ] **Step 2: Run to verify it fails** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/envelope-number` +Expected: FAIL — module not found. + +- [ ] **Step 3: Implement `EncryptedNumber`** + +Create `packages/prisma-next/src/v3/envelope-number.ts` (mirror `execution/envelope-double.ts` byte-for-byte, changing only the class name + `typeName`): + +```ts +import { + EncryptedEnvelopeBase, + type EncryptedEnvelopeFromInternalArgs, + type EncryptedEnvelopeHandle, +} from '../execution/envelope-base' + +export type EncryptedNumberHandle = EncryptedEnvelopeHandle +export type EncryptedNumberFromInternalArgs = EncryptedEnvelopeFromInternalArgs + +/** + * v3 number envelope. A SIBLING of `EncryptedDouble` — NOT a subclass or alias. + * Its distinct `typeName` drives the `$encryptedNumber` marker; keeping the + * class separate prevents `value instanceof EncryptedNumber` from matching a v2 + * `EncryptedDouble` (a cross-version leak). Integer/smallint/numeric/real/double + * v3 domains all render as `EncryptedNumber`. + */ +export class EncryptedNumber extends EncryptedEnvelopeBase { + protected override get typeName(): string { + return 'EncryptedNumber' + } + + static from(plaintext: number): EncryptedNumber { + return new EncryptedNumber({ plaintext, ciphertext: undefined, table: undefined, column: undefined, sdk: undefined }) + } + + static fromInternal(args: EncryptedNumberFromInternalArgs): EncryptedNumber { + return new EncryptedNumber({ + plaintext: undefined, + ciphertext: args.ciphertext, + table: args.table, + column: args.column, + sdk: args.sdk, + }) + } +} +``` + +> Confirm the `typeName` accessor visibility + the `from`/`fromInternal` argument object against the actual `envelope-double.ts` before finalizing; match it exactly. + +- [ ] **Step 4: Run to verify it passes** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/envelope-number` +Expected: PASS (3 tests). + +- [ ] **Step 5: Write + implement `wire-v3.ts` (TDD)** + +Create `packages/prisma-next/test/v3/wire-v3.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { v3FromDriver, v3ToDriver } from '../../src/v3/wire-v3' + +describe('v3 wire (plain JSONB)', () => { + it('serialises to JSON text, never the v2 composite literal', () => { + expect(v3ToDriver({ a: 1 })).toBe('{"a":1}') + expect(v3ToDriver({ a: 1 })).not.toMatch(/^\(/) + }) + it('null/undefined -> null', () => { + expect(v3ToDriver(null)).toBeNull() + expect(v3ToDriver(undefined)).toBeNull() + }) + it('bigintSafeReplacer: a stray bigint in a malformed envelope serialises to its decimal string, not a throw', () => { + // A well-formed envelope never carries a bigint; this pins the defensive + // guard so the codec never throws "Do not know how to serialize a BigInt". + expect(v3ToDriver({ c: 10n })).toBe('{"c":"10"}') + expect(() => v3ToDriver({ c: 9007199254740993n })).not.toThrow() + }) + it('fromDriver parses text, passes objects through, preserves null/undefined', () => { + expect(v3FromDriver('{"a":1}')).toEqual({ a: 1 }) + expect(v3FromDriver({ a: 1 } as object)).toEqual({ a: 1 }) + expect(v3FromDriver(null)).toBeNull() + expect(v3FromDriver(undefined)).toBeUndefined() + }) +}) +``` + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/wire-v3` → FAIL. Then create `packages/prisma-next/src/v3/wire-v3.ts`: + +```ts +/** + * v3 columns are `CREATE DOMAIN ... AS jsonb`, so they serialise as plain jsonb + * — distinct from v2's composite-literal wire. + * + * NOTE: this is a thin re-export of the SHARED, framework-neutral codec lifted + * to `@cipherstash/stack/eql/v3` (`v3ToDriver` / `v3FromDriver` / + * `bigintSafeReplacer`) — see the shared-primitive-lift task. It is reproduced + * here only to show the exact behaviour under test; the implementation must + * `import { v3ToDriver, v3FromDriver } from '@cipherstash/stack/eql/v3'` rather + * than fork a second copy. + */ + +// `bigintSafeReplacer` is LOAD-BEARING and must not be dropped: a well-formed +// envelope never carries a bigint (bigint plaintext is encrypted to ciphertext +// strings first), but a malformed envelope with a stray bigint would otherwise +// throw `TypeError: Do not know how to serialize a BigInt`. Dropping it is a +// regression (ref `eql/v3/drizzle/codec.ts:18,28`). +function bigintSafeReplacer(_key: string, value: unknown): unknown { + return typeof value === 'bigint' ? value.toString() : value +} + +export function v3ToDriver(value: unknown): string | null { + if (value === null || value === undefined) return null + return JSON.stringify(value, bigintSafeReplacer) +} + +export function v3FromDriver(value: string | object | null | undefined): T { + if (value === null || value === undefined) return value as T + if (typeof value === 'object') return value as T + return JSON.parse(value) as T +} +``` + +Run again → PASS. + +- [ ] **Step 6: Write + implement the v3 cell codec + descriptors (TDD)** + +Create `packages/prisma-next/test/v3/codec-runtime-v3.test.ts`: + +```ts +import { describe, expect, it, vi } from 'vitest' +import type { CipherstashSdk } from '../../src/execution/sdk' +import { createV3CodecDescriptors } from '../../src/v3/codec-runtime-v3' + +const emptySdk = (): CipherstashSdk => ({ + decrypt: vi.fn(), + bulkEncrypt: vi.fn(), + bulkDecrypt: vi.fn(), +}) + +describe('createV3CodecDescriptors', () => { + it('emits one descriptor per catalog domain; native type = the public.* domain', () => { + const ds = createV3CodecDescriptors(emptySdk()) + const textSearch = ds.find((d) => d.codecId === 'cipherstash/eql-v3/text_search@1') + expect(textSearch).toBeDefined() + expect(textSearch?.targetTypes).toEqual(['public.text_search']) + expect(textSearch?.meta).toEqual({ db: { sql: { postgres: { nativeType: 'public.text_search' } } } }) + expect(textSearch?.renderOutputType?.({} as never)).toBe('EncryptedString') + }) + + it('storage-only public.boolean declares no traits; text_search declares all three', () => { + const ds = createV3CodecDescriptors(emptySdk()) + expect(ds.find((d) => d.codecId === 'cipherstash/eql-v3/boolean@1')?.traits).toEqual([]) + expect([...(ds.find((d) => d.codecId === 'cipherstash/eql-v3/text_search@1')?.traits ?? [])].sort()).toEqual([ + 'cipherstash:equality', + 'cipherstash:free-text-search', + 'cipherstash:order-and-range', + ]) + }) + + it('number domains render EncryptedNumber; bigint domains render EncryptedBigInt', () => { + const ds = createV3CodecDescriptors(emptySdk()) + expect(ds.find((d) => d.codecId === 'cipherstash/eql-v3/integer_ord@1')?.renderOutputType?.({} as never)).toBe( + 'EncryptedNumber', + ) + expect(ds.find((d) => d.codecId === 'cipherstash/eql-v3/bigint_ord@1')?.renderOutputType?.({} as never)).toBe( + 'EncryptedBigInt', + ) + }) + + it('encode serialises an envelope\'s ciphertext to plain JSONB text', async () => { + const ds = createV3CodecDescriptors(emptySdk()) + const desc = ds.find((d) => d.codecId === 'cipherstash/eql-v3/text_eq@1')! + const codec = desc.factory({} as never)({} as never) + // `encode` operates on an ENVELOPE (with `.expose()`), not a plain object. + // A set ciphertext serialises via v3ToDriver to JSONB text. + const envelope = { expose: () => ({ ciphertext: { c: 'abc' } }) } + const encoded = await codec.encode(envelope as never, {} as never) + expect(encoded).toBe('{"c":"abc"}') + }) + + it('encode passes a pre-serialised JSONB string through unchanged (middleware two-pass sentinel)', async () => { + const ds = createV3CodecDescriptors(emptySdk()) + const codec = ds.find((d) => d.codecId === 'cipherstash/eql-v3/text_eq@1')!.factory({} as never)({} as never) + expect(await codec.encode('{"c":"enc"}' as never, {} as never)).toBe('{"c":"enc"}') + }) + + it('encode returns the value unchanged when the envelope has no ciphertext yet (pre-encrypt)', async () => { + const ds = createV3CodecDescriptors(emptySdk()) + const codec = ds.find((d) => d.codecId === 'cipherstash/eql-v3/text_eq@1')!.factory({} as never)({} as never) + const preEncrypt = { expose: () => ({ ciphertext: undefined }) } + expect(await codec.encode(preEncrypt as never, {} as never)).toBe(preEncrypt) + }) +}) +``` + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/codec-runtime-v3` → FAIL. Then create `packages/prisma-next/src/v3/codec-runtime-v3.ts`: + +```ts +import type { JsonValue } from '@prisma-next/contract/types' +import { + type AnyCodecDescriptor, + CodecImpl, + type CodecInstanceContext, + type CodecTrait, +} from '@prisma-next/framework-components/codec' +import { runtimeError } from '@prisma-next/framework-components/runtime' +import type { Codec, SqlCodecCallContext } from '@prisma-next/sql-relational-core/ast' +import type { RuntimeParameterizedCodecDescriptor } from '@prisma-next/sql-runtime' +import { type as arktype } from 'arktype' +import { type CipherstashV3CodecId, v3TraitsForCapabilities } from '../extension-metadata/constants-v3' +import { EncryptedBigInt } from '../execution/envelope-bigint' +import type { EncryptedEnvelopeBase } from '../execution/envelope-base' +import { EncryptedBoolean } from '../execution/envelope-boolean' +import { EncryptedDate } from '../execution/envelope-date' +import { EncryptedString } from '../execution/envelope-string' +import type { CipherstashSdk } from '../execution/sdk' +import { + envelopeTypeNameForCastAs, + type V3CastAs, + type V3DomainMeta, + V3_DOMAIN_META_BY_CODEC_ID, +} from './catalog' +import { EncryptedNumber } from './envelope-number' +import { v3FromDriver, v3ToDriver } from './wire-v3' + +type FromInternal = (args: { + ciphertext: unknown + table: string + column: string + sdk: CipherstashSdk +}) => EncryptedEnvelopeBase + +function fromInternalForCastAs(castAs: V3CastAs): FromInternal { + switch (castAs) { + case 'string': + return EncryptedString.fromInternal + case 'number': + return EncryptedNumber.fromInternal + case 'bigint': + return EncryptedBigInt.fromInternal + case 'date': + case 'timestamp': + return EncryptedDate.fromInternal + case 'boolean': + return EncryptedBoolean.fromInternal + } +} + +export class CipherstashV3CellCodec> extends CodecImpl< + string, + readonly CodecTrait[], + unknown, + E +> { + readonly #fromInternal: FromInternal + readonly #typeName: string + constructor(descriptor: AnyCodecDescriptor, private readonly sdk: CipherstashSdk, typeName: string, fromInternal: FromInternal) { + super(descriptor) + this.#fromInternal = fromInternal + this.#typeName = typeName + } + + async encode(value: E | string, _ctx: SqlCodecCallContext): Promise { + // Two-pass: the v3 bulk-encrypt middleware replaces the param with the JSONB + // text before the driver reads it. If we still hold an envelope, serialise + // its stamped ciphertext (or return itself as the pre-encrypt sentinel). + if (typeof value === 'string') return value + if (value === null || value === undefined) return value + const handle = (value as E).expose() + if (handle.ciphertext === undefined) return value + return v3ToDriver(handle.ciphertext) + } + + async decode(wire: unknown, ctx: SqlCodecCallContext): Promise { + const column = ctx.column + if (!column) { + throw runtimeError( + 'RUNTIME.DECODE_FAILED', + `cipherstash ${this.descriptor.codecId}: decode requires the projected-column routing context.`, + { codecId: this.descriptor.codecId, reason: 'cipherstash-v3-decode-column-context-missing' }, + ) + } + return this.#fromInternal({ + ciphertext: v3FromDriver(wire as string | object | null | undefined), + table: column.table, + column: column.name, + sdk: this.sdk, + }) as E + } + + encodeJson(_v: E): JsonValue { + const marker = `$${this.#typeName.charAt(0).toLowerCase()}${this.#typeName.slice(1)}` + return { [marker]: '' } as JsonValue + } + decodeJson(): E { + throw new Error('cipherstash v3 codec: decodeJson is not supported') + } +} + +const V3_PARAMS_SCHEMA = arktype({ castAs: 'string', capabilities: 'object' }) + +function makeV3Descriptor( + sdk: CipherstashSdk, + codecId: CipherstashV3CodecId, + meta: V3DomainMeta, +): RuntimeParameterizedCodecDescriptor { + const typeName = envelopeTypeNameForCastAs(meta.castAs) + const descriptorMeta: AnyCodecDescriptor = { + codecId, + traits: v3TraitsForCapabilities(meta.capabilities) as unknown as readonly CodecTrait[], + targetTypes: [meta.nativeType], + meta: { db: { sql: { postgres: { nativeType: meta.nativeType } } } }, + paramsSchema: V3_PARAMS_SCHEMA, + isParameterized: true, + renderOutputType: () => typeName, + factory: () => () => codec, + } + const codec = new CipherstashV3CellCodec(descriptorMeta, sdk, typeName, fromInternalForCastAs(meta.castAs)) as CipherstashV3CellCodec< + EncryptedEnvelopeBase + > & + Codec> + return { + codecId, + traits: descriptorMeta.traits, + targetTypes: descriptorMeta.targetTypes, + meta: descriptorMeta.meta, + paramsSchema: V3_PARAMS_SCHEMA, + isParameterized: true as const, + renderOutputType: () => typeName, + factory: (_p: unknown) => (_ctx: CodecInstanceContext) => codec, + } +} + +export function createV3CodecDescriptors( + sdk: CipherstashSdk, +): ReadonlyArray> { + return [...V3_DOMAIN_META_BY_CODEC_ID.entries()].map(([codecId, meta]) => + makeV3Descriptor(sdk, codecId as CipherstashV3CodecId, meta), + ) +} +``` + +Then create `packages/prisma-next/src/v3/barrel.ts`: + +```ts +/** + * v3 user-facing value types, surfaced through the v3 subtree. Re-exports the + * version-neutral value classes plus the v3-only `EncryptedNumber`. Pulls in NO + * v2 wire/codec code. + */ +export { EncryptedBigInt } from '../execution/envelope-bigint' +export { EncryptedBoolean } from '../execution/envelope-boolean' +export { EncryptedDate } from '../execution/envelope-date' +export { EncryptedString } from '../execution/envelope-string' +export { EncryptedNumber } from './envelope-number' +``` + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/codec-runtime-v3` → PASS. (If arktype's `paramsSchema` type differs from the framework's `~standard` shape, mirror the exact `paramsSchema` object used in v2 `parameterized.ts`. Confirm `CodecImpl` / `AnyCodecDescriptor` / `runtimeError` import paths against the v2 codec-runtime module before finalizing.) + +- [ ] **Step 7: Commit** + +```bash +git add packages/prisma-next/src/v3/envelope-number.ts packages/prisma-next/src/v3/wire-v3.ts packages/prisma-next/src/v3/codec-runtime-v3.ts packages/prisma-next/src/v3/barrel.ts packages/prisma-next/test/v3/envelope-number.test.ts packages/prisma-next/test/v3/wire-v3.test.ts packages/prisma-next/test/v3/codec-runtime-v3.test.ts +git commit -m "feat(prisma-next): v3 EncryptedNumber + plain-JSONB cell codec + per-domain descriptors (public.* native types)" +``` + +--- + +### Task 5: v3 bulk-encrypt middleware (JSONB payloads) + +A separate v3 middleware that filters by the v3 codec-id set, stamps `(table, column)` routing keys via the **neutral** AST utility (reused from the v2 module — not the v2 encode path), groups by routing key, calls `sdk.bulkEncrypt`, and replaces params with **plain JSONB** ciphertext (never the v2 composite). + +**Files:** +- Modify: `packages/prisma-next/src/middleware/bulk-encrypt.ts` (export `stampRoutingKeysFromAst`) +- Create: `packages/prisma-next/src/v3/bulk-encrypt-v3.ts` +- Test: `packages/prisma-next/test/v3/bulk-encrypt-v3.test.ts` + +**Interfaces:** +- Consumes: neutral `stampRoutingKeysFromAst` (now exported from `../middleware/bulk-encrypt`), `setHandleCiphertext`, `EncryptedEnvelopeBase` (`../execution/envelope-base`); `groupByRoutingKey`, `BulkEncryptTarget` (`../execution/routing`); `CipherstashSdk` (`../execution/sdk`); `markBulkEncryptMiddlewareRegistered` (`../execution/middleware-registry`); Task 2 `CIPHERSTASH_V3_CODEC_ID_SET`; Task 4 `v3ToDriver`; abort helpers (`../execution/abort`). +- Produces: `function bulkEncryptMiddlewareV3(sdk: CipherstashSdk): SqlMiddleware` — `name: 'cipherstash.bulk-encrypt-v3'`, `familyId: 'sql'`. + +- [ ] **Step 1: Export the neutral AST-stamping util (refactor, no behavior change)** + +In `packages/prisma-next/src/middleware/bulk-encrypt.ts`, change `function stampRoutingKeysFromAst(...)` to `export function stampRoutingKeysFromAst(...)`. No other change. + +Run: `pnpm --filter @cipherstash/prisma-next test -- bulk-encrypt-middleware` +Expected: PASS (unchanged v2 behavior). + +- [ ] **Step 2: Write the failing test** + +Create `packages/prisma-next/test/v3/bulk-encrypt-v3.test.ts` (reuse the v2 harness; if `buildInsertPlan`/`createCtx`/`createSqlParamRefMutator` are not already exported from a shared helper, extract them into `packages/prisma-next/test/bulk-encrypt-middleware.helpers.ts` as a first sub-step and re-import from the v2 test too — pure refactor, run the v2 suite green afterward; `buildInsertPlan` gains a 3rd arg = the codec id stamped on each `ParamRef.of(value, { codec: { codecId } })`): + +```ts +import { describe, expect, it, vi } from 'vitest' +import type { CipherstashSdk } from '../../src/execution/sdk' +import { EncryptedString } from '../../src/execution/envelope-string' +import { bulkEncryptMiddlewareV3 } from '../../src/v3/bulk-encrypt-v3' +import { buildInsertPlan, createCtx, createSqlParamRefMutator } from '../bulk-encrypt-middleware.helpers' + +const V3_TEXT_SEARCH = 'cipherstash/eql-v3/text_search@1' + +function makeSdk(): CipherstashSdk & { calls: unknown[] } { + const calls: unknown[] = [] + return { + calls, + decrypt: vi.fn(), + bulkDecrypt: vi.fn(), + bulkEncrypt(args) { + calls.push(args) + return Promise.resolve(args.values.map((v) => ({ c: `enc:${String(v)}` }))) + }, + } +} + +describe('bulkEncryptMiddlewareV3', () => { + it('has the v3 identity', () => { + const m = bulkEncryptMiddlewareV3(makeSdk()) + expect(m.name).toBe('cipherstash.bulk-encrypt-v3') + expect(m.familyId).toBe('sql') + }) + + it('bulk-encrypts v3 params and writes plain JSONB (not the v2 composite)', async () => { + const sdk = makeSdk() + const m = bulkEncryptMiddlewareV3(sdk) + const env = EncryptedString.from('alice@example.com') + const plan = buildInsertPlan('user', [{ email: env }], V3_TEXT_SEARCH) + const params = createSqlParamRefMutator(plan) + await m.beforeExecute?.(plan, createCtx(), params) + expect(sdk.calls).toHaveLength(1) + const only = params.currentParams()[0] + expect(only).toBe('{"c":"enc:alice@example.com"}') + expect(only).not.toMatch(/^\(/) // never the composite literal + }) + + it('ignores v2-codec params (jurisdiction is the v3 id set only)', async () => { + const sdk = makeSdk() + const m = bulkEncryptMiddlewareV3(sdk) + const env = EncryptedString.from('x') + const plan = buildInsertPlan('user', [{ email: env }], 'cipherstash/string@1') + const params = createSqlParamRefMutator(plan) + await m.beforeExecute?.(plan, createCtx(), params) + expect(sdk.calls).toHaveLength(0) + }) +}) +``` + +- [ ] **Step 3: Run to verify it fails** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/bulk-encrypt-v3` +Expected: FAIL — module not found. + +- [ ] **Step 4: Implement `bulk-encrypt-v3.ts`** + +Create `packages/prisma-next/src/v3/bulk-encrypt-v3.ts` (structure mirrors v2 `bulk-encrypt.ts`, but filters on the v3 set and writes `v3ToDriver`): + +```ts +import type { ParamRefHandle, SqlParamRefMutator } from '@prisma-next/sql-relational-core/middleware' +import type { SqlMiddleware } from '@prisma-next/sql-runtime' +import { ifDefined } from '@prisma-next/utils/defined' +import { CIPHERSTASH_V3_CODEC_ID_SET } from '../extension-metadata/constants-v3' +import { checkCipherstashAborted, raceCipherstashAbort } from '../execution/abort' +import { EncryptedEnvelopeBase, setHandleCiphertext } from '../execution/envelope-base' +import { markBulkEncryptMiddlewareRegistered } from '../execution/middleware-registry' +import { type BulkEncryptTarget, groupByRoutingKey } from '../execution/routing' +import type { CipherstashSdk } from '../execution/sdk' +// NEUTRAL util — reused, not the v2 encode path. +import { stampRoutingKeysFromAst } from '../middleware/bulk-encrypt' +import { v3ToDriver } from './wire-v3' + +export function bulkEncryptMiddlewareV3(sdk: CipherstashSdk): SqlMiddleware { + markBulkEncryptMiddlewareRegistered(sdk) + return { + name: 'cipherstash.bulk-encrypt-v3', + familyId: 'sql', + async beforeExecute(plan, ctx, params) { + if (!params) return + stampRoutingKeysFromAst(plan.ast) + const targets = collectV3Targets(params) + if (targets.length === 0) return + for (const [groupKey, group] of groupByRoutingKey(targets)) { + const first = group[0] + if (!first) continue + checkCipherstashAborted(ctx.signal, 'bulk-encrypt') + const ciphertexts = await raceCipherstashAbort( + sdk.bulkEncrypt({ + routingKey: first.routingKey, + values: group.map((t) => t.plaintext), + ...ifDefined('signal', ctx.signal), + }), + ctx.signal, + 'bulk-encrypt', + ) + if (ciphertexts.length !== group.length) { + throw new Error( + `cipherstash v3 bulk-encrypt: SDK returned ${ciphertexts.length} ciphertexts for routing key ${groupKey} but ${group.length} were requested.`, + ) + } + params.replaceValues( + group.map((t, i) => { + const ciphertext = ciphertexts[i] + setHandleCiphertext(t.envelope, ciphertext) + return { ref: t.ref, newValue: v3ToDriver(ciphertext) } + }), + ) + } + }, + } +} + +function collectV3Targets(params: SqlParamRefMutator): BulkEncryptTarget>[] { + const targets: BulkEncryptTarget>[] = [] + for (const entry of params.entries()) { + if (entry.codecId === undefined || !CIPHERSTASH_V3_CODEC_ID_SET.has(entry.codecId)) continue + const value = entry.value + if (!(value instanceof EncryptedEnvelopeBase)) continue + const handle = value.expose() + if (handle.plaintext === undefined) { + throw new Error( + 'cipherstash v3 bulk-encrypt: encountered a write-path envelope with no plaintext. Use `Encrypted*.from(plaintext)`.', + ) + } + if (handle.table === undefined || handle.column === undefined) { + throw new Error( + 'cipherstash v3 bulk-encrypt: envelope reached the bulk-encrypt phase without a (table, column) routing context.', + ) + } + targets.push({ + ref: entry.ref, + plaintext: handle.plaintext, + envelope: value, + routingKey: { table: handle.table, column: handle.column }, + }) + } + return targets +} +``` + +- [ ] **Step 5: Run to verify it passes** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/bulk-encrypt-v3 bulk-encrypt-middleware` +Expected: PASS (v3 suite + unchanged v2 suite). + +- [ ] **Step 6: Commit** + +```bash +git add packages/prisma-next/src/middleware/bulk-encrypt.ts packages/prisma-next/src/v3/bulk-encrypt-v3.ts packages/prisma-next/test/v3/bulk-encrypt-v3.test.ts packages/prisma-next/test/bulk-encrypt-middleware.helpers.ts packages/prisma-next/test/bulk-encrypt-middleware.test.ts +git commit -m "feat(prisma-next): v3 bulk-encrypt middleware with JSONB payloads" +``` + +--- + +### Task 6: v3 operators — `::jsonb` two-arg lowering + capability gating + `EncryptionOperatorError` + +Keep the `cipherstash*` method names but lower v3 columns to the two-arg `eql_v3.*(...::jsonb)` **functions** (in the `eql_v3` schema — the domains they operate on are `public.*`; do NOT conflate), gate by the concrete domain's index set, and throw a real exported `EncryptionOperatorError`. `cipherstashIlike`/`cipherstashNotIlike` lower to `eql_v3.contains` (documented bloom-filter caveat). No built-in equality traits are attached. + +**Files:** +- Create: `packages/prisma-next/src/v3/operators-v3.ts` +- Test: `packages/prisma-next/test/v3/operator-lowering-v3.test.ts`, `packages/prisma-next/test/v3/operator-gating-v3.test.ts`, `packages/prisma-next/test/v3/operator-lowering-v3.helpers.ts` + +**Interfaces:** +- Consumes: framework operator infra (`buildOperation`, `codecOf`, `toExpr`, `ParamRef`, `Expression`, `ScopeField`, `CodecRef`, `SqlOperationDescriptor(s)` — same imports as v2 `execution/operators.ts`); Task 1 `V3_DOMAIN_META_BY_CODEC_ID`, `V3DomainMeta`; Task 2 trait constants; Task 4 `EncryptedNumber` + neutral `EncryptedString`/`EncryptedDate`/`EncryptedBoolean`/`EncryptedBigInt`; `setHandleRoutingKey` (`../execution/envelope-base`). +- Produces: + - `class EncryptionOperatorError extends Error` (name `'EncryptionOperatorError'`) carrying `{ tableName?, columnName?, operator? }`. + - `function cipherstashV3QueryOperations(): SqlOperationDescriptors` with `cipherstashEq`, `cipherstashNe`, `cipherstashInArray`, `cipherstashNotInArray`, `cipherstashGt`, `cipherstashGte`, `cipherstashLt`, `cipherstashLte`, `cipherstashBetween`, `cipherstashNotBetween`, `cipherstashIlike`, `cipherstashNotIlike`. + - Templates: `eql_v3.eq({{self}}, {{arg0}}::jsonb)`, `eql_v3.neq(...)`, `eql_v3.gt/gte/lt/lte(...)`, range = `eql_v3.gte({{self}}, {{arg0}}::jsonb) AND eql_v3.lte({{self}}, {{arg1}}::jsonb)`, `eql_v3.contains({{self}}, {{arg0}}::jsonb)`. + +- [ ] **Step 1: Write the failing lowering test + helpers** + +Create `packages/prisma-next/test/v3/operator-lowering-v3.helpers.ts` mirroring the v2 `operator-lowering.helpers.ts`, but composing the v3 runtime (`createCipherstashV3RuntimeDescriptor({ sdk: emptySdk() })` from Task 7 — until Task 7 lands, compose a minimal adapter directly from `createV3CodecDescriptors` + `cipherstashV3QueryOperations`) and v3 codec ids in `columnAccessorV3` / `contractV3`. Export `callOperator`, `columnAccessorV3`, `contractV3`, `makeV3Adapter`, `selectWithWhere`, `TABLE`, `emptySdk`. + +Create `packages/prisma-next/test/v3/operator-lowering-v3.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { cipherstashV3QueryOperations } from '../../src/v3/operators-v3' +import { callOperator, columnAccessorV3, contractV3, makeV3Adapter, selectWithWhere, TABLE } from './operator-lowering-v3.helpers' + +// Extract just the operator fragment from the lowered WHERE clause and +// normalise the framework's param placeholders (`$1`/`$2`/…) to `$` so the +// assertion pins the FULL function-call skeleton — parentheses included — +// without coupling to placeholder numbering. A `.toContain('eql_v3.eq(')` +// check (the previous form) does NOT catch the `between` paren bug; these do. +// NOTE TO IMPLEMENTER: once the helper's exact `lowered.sql` format is +// confirmed against the v2 `operator-lowering.helpers.ts`, tighten each +// `whereFragment(...)` assertion to a full-string `.toBe(...)`. +function whereFragment(sql: string): string { + return sql.slice(sql.toUpperCase().indexOf('WHERE ') + 6).replace(/\$\d+/g, '$').trim() +} + +const TEXT = 'cipherstash/eql-v3/text_search@1' +const ORD = 'cipherstash/eql-v3/integer_ord@1' +const lowerOf = (predicate: unknown) => + whereFragment(makeV3Adapter().lower(selectWithWhere(predicate), { contract: contractV3 }).sql) + +describe('v3 operator lowering (::jsonb two-arg eql_v3.* functions)', () => { + const ops = cipherstashV3QueryOperations() + + it.each([ + ['cipherstashEq', ORD, ['x'], 'eql_v3.eq($, $::jsonb)'], + ['cipherstashNe', ORD, ['x'], 'eql_v3.neq($, $::jsonb)'], + ['cipherstashGt', ORD, ['x'], 'eql_v3.gt($, $::jsonb)'], + ['cipherstashGte', ORD, ['x'], 'eql_v3.gte($, $::jsonb)'], + ['cipherstashLt', ORD, ['x'], 'eql_v3.lt($, $::jsonb)'], + ['cipherstashLte', ORD, ['x'], 'eql_v3.lte($, $::jsonb)'], + ['cipherstashIlike', TEXT, ['x'], 'eql_v3.contains($, $::jsonb)'], + ['cipherstashNotIlike', TEXT, ['x'], 'NOT eql_v3.contains($, $::jsonb)'], + ] as const)('%s lowers to the exact ::jsonb skeleton', (method, codecId, args, expected) => { + const predicate = callOperator(ops[method], columnAccessorV3(TABLE, 'c', codecId), ...args) + expect(lowerOf(predicate)).toBe(expected) + }) + + it('cipherstashBetween is a SELF-PARENTHESISED conjunction (composition-safe)', () => { + const predicate = callOperator(ops.cipherstashBetween, columnAccessorV3(TABLE, 'c', ORD), 1, 9) + // The leading/trailing parens are the fix: without them a generic NOT/OR + // over `between` mis-parses. Pin the whole shape. + expect(lowerOf(predicate)).toBe('(eql_v3.gte($, $::jsonb) AND eql_v3.lte($, $::jsonb))') + }) + + it('cipherstashNotBetween wraps the WHOLE conjunction: NOT (gte AND lte)', () => { + const predicate = callOperator(ops.cipherstashNotBetween, columnAccessorV3(TABLE, 'c', ORD), 1, 9) + const frag = lowerOf(predicate) + expect(frag).toBe('NOT (eql_v3.gte($, $::jsonb) AND eql_v3.lte($, $::jsonb))') + // Regression guard on the 9c82f50e degradation `NOT gte AND lte`: + expect(frag).not.toMatch(/NOT eql_v3\.gte/) + }) + + it.each([ + ['cipherstashInArray', '(eql_v3.eq($, $::jsonb) OR eql_v3.eq($, $::jsonb))'], + ['cipherstashNotInArray', 'NOT (eql_v3.eq($, $::jsonb) OR eql_v3.eq($, $::jsonb))'], + ] as const)('%s ORs/NOT-ORs one eq term per value, parenthesised', (method, expected) => { + const predicate = callOperator(ops[method], columnAccessorV3(TABLE, 'c', ORD), ['a', 'b']) + expect(lowerOf(predicate)).toBe(expected) + }) +}) +``` + +- [ ] **Step 2: Write the failing gating test** + +Create `packages/prisma-next/test/v3/operator-gating-v3.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { cipherstashV3QueryOperations, EncryptionOperatorError } from '../../src/v3/operators-v3' +import { callOperator, columnAccessorV3, TABLE } from './operator-lowering-v3.helpers' + +describe('v3 operator capability gating', () => { + it('equality requires unique or ore — storage-only public.text rejects cipherstashEq', () => { + const op = cipherstashV3QueryOperations().cipherstashEq + expect(() => callOperator(op, columnAccessorV3(TABLE, 'note', 'cipherstash/eql-v3/text@1'), 'x')).toThrow( + EncryptionOperatorError, + ) + }) + it('comparison requires ore — text_eq rejects cipherstashGt', () => { + const op = cipherstashV3QueryOperations().cipherstashGt + expect(() => callOperator(op, columnAccessorV3(TABLE, 'email', 'cipherstash/eql-v3/text_eq@1'), 'x')).toThrow(/order\/range/) + }) + it('free-text requires match — text_eq rejects cipherstashIlike', () => { + const op = cipherstashV3QueryOperations().cipherstashIlike + expect(() => callOperator(op, columnAccessorV3(TABLE, 'email', 'cipherstash/eql-v3/text_eq@1'), 'x')).toThrow(/free-text/) + }) + it('storage-only public.boolean rejects every search operator', () => { + const op = cipherstashV3QueryOperations().cipherstashEq + expect(() => callOperator(op, columnAccessorV3(TABLE, 'active', 'cipherstash/eql-v3/boolean@1'), true)).toThrow( + EncryptionOperatorError, + ) + }) +}) +``` + +- [ ] **Step 3: Run to verify both fail** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/operator` +Expected: FAIL — module not found. + +- [ ] **Step 4: Implement `operators-v3.ts`** + +Create `packages/prisma-next/src/v3/operators-v3.ts` (structure mirrors v2 `execution/operators.ts`, coercing to v3 envelopes, resolving domain meta from the codec id, gating on `meta.indexes`, using `::jsonb` templates): + +```ts +import type { CodecTrait } from '@prisma-next/framework-components/codec' +import type { SqlOperationDescriptor, SqlOperationDescriptors } from '@prisma-next/sql-operations' +import { type AnyExpression, type ColumnRef, type CodecRef, ParamRef } from '@prisma-next/sql-relational-core/ast' +import { buildOperation, codecOf, type Expression, type ScopeField, toExpr } from '@prisma-next/sql-relational-core/expression' +import { + CIPHERSTASH_TRAIT_EQUALITY, + CIPHERSTASH_TRAIT_FREE_TEXT_SEARCH, + CIPHERSTASH_TRAIT_ORDER_AND_RANGE, +} from '../extension-metadata/constants-v3' +import { EncryptedBigInt } from '../execution/envelope-bigint' +import { setHandleRoutingKey } from '../execution/envelope-base' +import { EncryptedBoolean } from '../execution/envelope-boolean' +import { EncryptedDate } from '../execution/envelope-date' +import { EncryptedString } from '../execution/envelope-string' +import { type V3DomainMeta, V3_DOMAIN_META_BY_CODEC_ID } from './catalog' +import { EncryptedNumber } from './envelope-number' + +const PG_BOOL_CODEC_ID = 'pg/bool@1' as const +type PgBoolReturn = { readonly codecId: typeof PG_BOOL_CODEC_ID; readonly nullable: false } + +export class EncryptionOperatorError extends Error { + constructor( + message: string, + public readonly context?: { columnName?: string; tableName?: string; operator?: string }, + ) { + super(message) + this.name = 'EncryptionOperatorError' + } +} + +function coerceV3(meta: V3DomainMeta, value: unknown) { + switch (meta.castAs) { + case 'string': + if (value instanceof EncryptedString) return value + if (typeof value === 'string') return EncryptedString.from(value) + break + case 'number': + if (value instanceof EncryptedNumber) return value + if (typeof value === 'number') return EncryptedNumber.from(value) + break + case 'bigint': + if (value instanceof EncryptedBigInt) return value + if (typeof value === 'bigint') return EncryptedBigInt.from(value) + break + case 'date': + case 'timestamp': + if (value instanceof EncryptedDate) return value + if (value instanceof Date) return EncryptedDate.from(value) + break + case 'boolean': + if (value instanceof EncryptedBoolean) return value + if (typeof value === 'boolean') return EncryptedBoolean.from(value) + break + } + throw new EncryptionOperatorError(`cipherstash v3 operator: value not assignable to castAs "${meta.castAs}".`) +} + +function requireSelfCodec(self: Expression, method: string): CodecRef { + const codec = codecOf(self) + if (codec === undefined) { + throw new EncryptionOperatorError( + `cipherstash ${method}: self expression is missing a CodecRef. Reach the operator through the ORM model accessor.`, + { operator: method }, + ) + } + return codec +} + +function metaFor(codecId: string, method: string): V3DomainMeta { + const meta = V3_DOMAIN_META_BY_CODEC_ID.get(codecId) + if (!meta) { + throw new EncryptionOperatorError(`cipherstash ${method}: column codec id "${codecId}" is not a public.* v3 domain.`, { + operator: method, + }) + } + return meta +} + +function extractColumnRef(selfAst: AnyExpression): ColumnRef | undefined { + if (selfAst.kind === 'column-ref') return selfAst + try { + return selfAst.baseColumnRef() + } catch { + return undefined + } +} + +function asEncryptedParam(selfAst: AnyExpression, selfCodec: CodecRef, meta: V3DomainMeta, value: unknown): ParamRef { + const envelope = coerceV3(meta, value) + const columnRef = extractColumnRef(selfAst) + if (columnRef) setHandleRoutingKey(envelope, columnRef.table, columnRef.column) + return ParamRef.of(envelope, { codec: selfCodec }) +} + +type Gate = (meta: V3DomainMeta) => boolean +// equality is answered via `unique` (HMAC) OR `ore` (equality-via-ORE on a +// pure-order domain); comparison/range/order require `ore`; free-text `match`. +const hasEquality: Gate = (m) => !!(m.indexes.unique || m.indexes.ore) +const hasOre: Gate = (m) => !!m.indexes.ore +const hasMatch: Gate = (m) => !!m.indexes.match + +function gate(meta: V3DomainMeta, ok: Gate, method: string, capability: string): void { + if (!ok(meta)) { + throw new EncryptionOperatorError( + `Operator "${method}" requires ${capability} on this column (domain ${meta.nativeType} does not support it).`, + { operator: method }, + ) + } +} + +function fixedArity(method: string, trait: string, arity: number, template: string, gateFn: Gate, capability: string): SqlOperationDescriptor { + return { + self: { traits: [trait] as unknown as readonly CodecTrait[] }, + impl: (self: Expression, ...userArgs: unknown[]): Expression => { + if (userArgs.length !== arity) { + throw new EncryptionOperatorError(`cipherstash ${method}: expected ${arity} argument(s), got ${userArgs.length}.`, { + operator: method, + }) + } + const selfCodec = requireSelfCodec(self, method) + const meta = metaFor(selfCodec.codecId, method) + gate(meta, gateFn, method, capability) + const selfAst = toExpr(self, selfCodec) + const argRefs = userArgs.map((v) => asEncryptedParam(selfAst, selfCodec, meta, v)) + return buildOperation({ + method, + args: [selfAst, ...argRefs], + returns: { codecId: PG_BOOL_CODEC_ID, nullable: false }, + lowering: { targetFamily: 'sql', strategy: 'function', template }, + }) + }, + } +} + +function variableArity(method: string, trait: string, build: (n: number) => string): SqlOperationDescriptor { + return { + self: { traits: [trait] as unknown as readonly CodecTrait[] }, + impl: (self: Expression, values: unknown): Expression => { + if (!Array.isArray(values)) throw new EncryptionOperatorError(`cipherstash ${method}: expected an array.`, { operator: method }) + if (values.length === 0) throw new EncryptionOperatorError(`cipherstash ${method}: empty array is not supported.`, { operator: method }) + const selfCodec = requireSelfCodec(self, method) + const meta = metaFor(selfCodec.codecId, method) + gate(meta, hasEquality, method, 'equality') + const selfAst = toExpr(self, selfCodec) + const argRefs = values.map((v) => asEncryptedParam(selfAst, selfCodec, meta, v)) + return buildOperation({ + method, + args: [selfAst, ...argRefs], + returns: { codecId: PG_BOOL_CODEC_ID, nullable: false }, + lowering: { targetFamily: 'sql', strategy: 'function', template: build(values.length) }, + }) + }, + } +} + +const inArrayTemplate = (n: number) => + `(${Array.from({ length: n }, (_, i) => `eql_v3.eq({{self}}, {{arg${i}}}::jsonb)`).join(' OR ')})` +const notInArrayTemplate = (n: number) => `NOT ${inArrayTemplate(n)}` + +export function cipherstashV3QueryOperations(): SqlOperationDescriptors { + return { + cipherstashEq: fixedArity('cipherstashEq', CIPHERSTASH_TRAIT_EQUALITY, 1, 'eql_v3.eq({{self}}, {{arg0}}::jsonb)', hasEquality, 'equality'), + cipherstashNe: fixedArity('cipherstashNe', CIPHERSTASH_TRAIT_EQUALITY, 1, 'eql_v3.neq({{self}}, {{arg0}}::jsonb)', hasEquality, 'equality'), + cipherstashInArray: variableArity('cipherstashInArray', CIPHERSTASH_TRAIT_EQUALITY, inArrayTemplate), + cipherstashNotInArray: variableArity('cipherstashNotInArray', CIPHERSTASH_TRAIT_EQUALITY, notInArrayTemplate), + cipherstashGt: fixedArity('cipherstashGt', CIPHERSTASH_TRAIT_ORDER_AND_RANGE, 1, 'eql_v3.gt({{self}}, {{arg0}}::jsonb)', hasOre, 'order/range'), + cipherstashGte: fixedArity('cipherstashGte', CIPHERSTASH_TRAIT_ORDER_AND_RANGE, 1, 'eql_v3.gte({{self}}, {{arg0}}::jsonb)', hasOre, 'order/range'), + cipherstashLt: fixedArity('cipherstashLt', CIPHERSTASH_TRAIT_ORDER_AND_RANGE, 1, 'eql_v3.lt({{self}}, {{arg0}}::jsonb)', hasOre, 'order/range'), + cipherstashLte: fixedArity('cipherstashLte', CIPHERSTASH_TRAIT_ORDER_AND_RANGE, 1, 'eql_v3.lte({{self}}, {{arg0}}::jsonb)', hasOre, 'order/range'), + // between's template is a SELF-CONTAINED PARENTHESISED conjunction. The + // parens are load-bearing, not cosmetic: Postgres binds NOT tighter than + // AND, so an unparenthesised `gte AND lte` under any generic negation (a + // framework `not(between(...))`, an OR composition, etc.) parses as + // `(NOT gte) AND lte` — rows BELOW the lower bound instead of the range + // complement (the 9c82f50e bug class). Parenthesising here makes every + // composition safe instead of relying on each caller to remember. + cipherstashBetween: fixedArity('cipherstashBetween', CIPHERSTASH_TRAIT_ORDER_AND_RANGE, 2, '(eql_v3.gte({{self}}, {{arg0}}::jsonb) AND eql_v3.lte({{self}}, {{arg1}}::jsonb))', hasOre, 'order/range'), + cipherstashNotBetween: fixedArity('cipherstashNotBetween', CIPHERSTASH_TRAIT_ORDER_AND_RANGE, 2, 'NOT (eql_v3.gte({{self}}, {{arg0}}::jsonb) AND eql_v3.lte({{self}}, {{arg1}}::jsonb))', hasOre, 'order/range'), + // ilike/notIlike lower to eql_v3.contains (bloom-filter token containment, NOT SQL LIKE — carry this caveat into user docs). + cipherstashIlike: fixedArity('cipherstashIlike', CIPHERSTASH_TRAIT_FREE_TEXT_SEARCH, 1, 'eql_v3.contains({{self}}, {{arg0}}::jsonb)', hasMatch, 'free-text search'), + cipherstashNotIlike: fixedArity('cipherstashNotIlike', CIPHERSTASH_TRAIT_FREE_TEXT_SEARCH, 1, 'NOT eql_v3.contains({{self}}, {{arg0}}::jsonb)', hasMatch, 'free-text search'), + } +} +``` + +- [ ] **Step 5: Run to verify both suites pass** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/operator` +Expected: PASS. + +- [ ] **Step 6: Pin the trait-removal regression for v3** + +Add to `packages/prisma-next/test/equality-trait-removal.test.ts` (or a v3 sibling): every descriptor from `createV3CodecDescriptors(emptySdk())` has traits ⊆ `cipherstash:*` and never contains the framework built-in `'equality'`. + +```ts +import { createV3CodecDescriptors } from '../src/v3/codec-runtime-v3' +it('v3 codec descriptors never advertise the framework built-in equality trait', () => { + for (const d of createV3CodecDescriptors({ decrypt: vi.fn(), bulkEncrypt: vi.fn(), bulkDecrypt: vi.fn() })) { + for (const t of d.traits) expect(String(t).startsWith('cipherstash:')).toBe(true) + expect(d.traits).not.toContain('equality') + } +}) +``` + +- [ ] **Step 7: Verify no method-name collision across v2+v3 descriptors** + +Confirm the **v3 descriptor stands alone**: `createCipherstashV3RuntimeDescriptor({ sdk })` (Task 7) registers its `cipherstash*` operations under v3's own distinct extension id and builds cleanly. Do NOT co-register it with the v2 descriptor — decision 1b makes v2 and v3 separate entry points precisely because the flat `OperationRegistry` rejects two descriptors sharing the `cipherstashEq` method name. Add a test asserting a v3-only adapter builds, and (optionally) that attempting to register v2 and v3 descriptors together throws — documenting why they must not be combined. + +- [ ] **Step 8: Commit** + +```bash +git add packages/prisma-next/src/v3/operators-v3.ts packages/prisma-next/test/v3/operator-lowering-v3.test.ts packages/prisma-next/test/v3/operator-lowering-v3.helpers.ts packages/prisma-next/test/v3/operator-gating-v3.test.ts packages/prisma-next/test/equality-trait-removal.test.ts +git commit -m "feat(prisma-next): v3 operators with eql_v3.* ::jsonb lowering, capability gating, EncryptionOperatorError" +``` + +--- + +### Task 7: v3 schema derivation, SDK adapter, runtime descriptor, and the v3 entry point (`cipherstashFromStackV3`) + +Derive `@cipherstash/stack/eql/v3` schemas from v3 contract columns (mapping `nativeType` (`public.*`) → the matching concrete factory, preserving the concrete column type — no widening to `AnyEncryptedV3Column`), adapt the `EncryptionV3` client to the `CipherstashSdk` shape, compose the v3 runtime descriptor (under v3's **own distinct extension id/version**), and add a **v3-only** entry point `cipherstashFromStackV3` that builds a single `EncryptionV3` client. Decision **1b**: v2 and v3 are separate entry points — the v2 `cipherstashFromStack` is left unchanged, there is no two-client dispatch, and a v3 contract carrying a v2 codec id is a hard error. v3 override validation compares exact domain identity (`integer_ord` ≠ `integer_ord_ore`). + +**Files:** +- Create: `packages/prisma-next/src/v3/derive-schemas-v3.ts`, `packages/prisma-next/src/v3/sdk-adapter-v3.ts`, `packages/prisma-next/src/v3/runtime-v3.ts`, `packages/prisma-next/src/v3/from-stack-v3-validate.ts` +- Modify: `packages/prisma-next/src/stack/from-stack.ts`, `packages/prisma-next/src/exports/stack.ts`, `packages/prisma-next/src/exports/runtime.ts` +- Test: `packages/prisma-next/test/v3/derive-schemas-v3.test.ts`, `packages/prisma-next/test/v3/sdk-adapter-v3.test.ts`, `packages/prisma-next/test/v3/from-stack-divergence-v3.test.ts` + +**Interfaces:** +- Consumes: `@cipherstash/stack/eql/v3` — `types`, `encryptedTable`, `AnyV3Table`, `AnyEncryptedV3Column`; Task 1 `V3_FACTORY_BY_NATIVE_TYPE`; Task 2 `isCipherstashV3CodecId`; `EncryptionV3` (from the stack v3 client entry — see Step 4 note); `CipherstashSdk`, `CipherstashRoutingKey` (`../execution/sdk`); Task 4 `createV3CodecDescriptors`; Task 6 `cipherstashV3QueryOperations`; Task 5 `bulkEncryptMiddlewareV3`; existing v2 `deriveStackSchemas`, `createCipherstashSdk`, `createCipherstashRuntimeDescriptor`, `bulkEncryptMiddleware`, `Encryption`. +- Produces: + - `function deriveStackSchemasV3(contractJson): ReadonlyArray` + - `function createCipherstashV3Sdk(client, schemas): CipherstashSdk` + - `function createCipherstashV3RuntimeDescriptor(opts: { sdk: CipherstashSdk }): SqlRuntimeExtensionDescriptor<'postgres'>` + - `function assertV3SchemasAgree(derived: AnyV3Table, override: AnyV3Table): void` + - `function cipherstashFromStackV3(opts: CipherstashFromStackV3Options): Promise` — the v3-only entry point (decision 1b). The v2 `cipherstashFromStack` is **unchanged** and not touched by this task. + - `interface V3ContractShape` (structural view of the contract storage): `{ storage?: { tables?: Record }> } }` (exported for reuse by tests/properties). + +- [ ] **Step 1: Write the failing derivation test** + +Create `packages/prisma-next/test/v3/derive-schemas-v3.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { deriveStackSchemasV3 } from '../../src/v3/derive-schemas-v3' + +function contract(columns: Record) { + return { storage: { tables: { user: { columns } } } } +} + +describe('deriveStackSchemasV3', () => { + it('derives one v3 EncryptedTable, mapping public.* nativeType -> the concrete factory', () => { + const [table] = deriveStackSchemasV3( + contract({ + email: { codecId: 'cipherstash/eql-v3/text_search@1', nativeType: 'public.text_search' }, + score: { codecId: 'cipherstash/eql-v3/integer_ord@1', nativeType: 'public.integer_ord' }, + balance: { codecId: 'cipherstash/eql-v3/bigint_ord@1', nativeType: 'public.bigint_ord' }, + }), + ) + const built = table!.build() + expect(built.tableName).toBe('user') + expect(Object.keys(built.columns.email!.indexes).sort()).toEqual(['match', 'ore', 'unique']) + expect(Object.keys(built.columns.score!.indexes)).toEqual(['ore']) + expect(built.columns.email!.cast_as).toBe('string') + expect(built.columns.score!.cast_as).toBe('number') + expect(built.columns.balance!.cast_as).toBe('bigint') + }) + + it('skips tables with no v3 columns', () => { + expect( + deriveStackSchemasV3(contract({ x: { codecId: 'cipherstash/string@1', nativeType: 'eql_v2_encrypted' } })), + ).toHaveLength(0) + }) +}) +``` + +- [ ] **Step 2: Run to verify it fails, then implement** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/derive-schemas-v3` → FAIL. Create `packages/prisma-next/src/v3/derive-schemas-v3.ts`: + +```ts +import { type AnyEncryptedV3Column, type AnyV3Table, encryptedTable } from '@cipherstash/stack/eql/v3' +import { isCipherstashV3CodecId } from '../extension-metadata/constants-v3' +import { V3_FACTORY_BY_NATIVE_TYPE } from './catalog' + +export interface V3ContractShape { + storage?: { + tables?: Record< + string, + { columns?: Record } + > + } +} + +export function deriveStackSchemasV3(contractJson: V3ContractShape): ReadonlyArray { + const tables = contractJson.storage?.tables + if (!tables) return [] + const result: AnyV3Table[] = [] + for (const [tableName, table] of Object.entries(tables)) { + const columns: Record = {} + for (const [columnName, column] of Object.entries(table.columns ?? {})) { + const codecId = column.codecId + if (codecId == null || !isCipherstashV3CodecId(codecId)) continue + const nativeType = column.nativeType + const factory = nativeType ? V3_FACTORY_BY_NATIVE_TYPE.get(nativeType) : undefined + if (!factory) { + throw new Error( + `deriveStackSchemasV3: column "${tableName}"."${columnName}" has v3 codec id "${codecId}" but nativeType "${nativeType}" maps to no eql/v3 factory. Re-emit the contract with a current @cipherstash/stack.`, + ) + } + columns[columnName] = factory(columnName) + } + if (Object.keys(columns).length === 0) continue + result.push(encryptedTable(tableName, columns)) + } + return result +} +``` + +Run again → PASS. + +- [ ] **Step 3: Implement the exact-domain override validator (TDD)** + +Create `packages/prisma-next/test/v3/from-stack-divergence-v3.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import { encryptedTable, types } from '@cipherstash/stack/eql/v3' +import { assertV3SchemasAgree } from '../../src/v3/from-stack-v3-validate' + +describe('v3 override divergence — same cast_as, different domain', () => { + it('rejects integer_ord vs integer_ord_ore (both cast_as number)', () => { + const derived = encryptedTable('user', { score: types.IntegerOrd('score') }) + const override = encryptedTable('user', { score: types.IntegerOrdOre('score') }) + expect(() => assertV3SchemasAgree(derived, override)).toThrow(/domain/) + }) + it('accepts an exact domain match', () => { + const derived = encryptedTable('user', { score: types.IntegerOrd('score') }) + const override = encryptedTable('user', { score: types.IntegerOrd('score') }) + expect(() => assertV3SchemasAgree(derived, override)).not.toThrow() + }) +}) +``` + +Create `packages/prisma-next/src/v3/from-stack-v3-validate.ts`: + +```ts +import type { AnyV3Table } from '@cipherstash/stack/eql/v3' + +/** v3 override validation compares EXACT domain identity (getEqlType() → + * public.*), not just cast_as + index keys — integer_ord and integer_ord_ore + * share cast_as and index families but are different domains. */ +export function assertV3SchemasAgree(derived: AnyV3Table, override: AnyV3Table): void { + const d = Object.fromEntries(Object.values(derived.columnBuilders).map((c) => [c.getName(), c.getEqlType()])) + const o = Object.fromEntries(Object.values(override.columnBuilders).map((c) => [c.getName(), c.getEqlType()])) + for (const col of new Set([...Object.keys(d), ...Object.keys(o)])) { + if (d[col] !== o[col]) { + throw new Error( + `cipherstashFromStack (v3): schema divergence on column "${derived.tableName}"."${col}". Contract domain "${d[col] ?? '(missing)'}" but override domain "${o[col] ?? '(missing)'}". Fix prisma/schema.prisma and re-emit rather than overriding.`, + ) + } + } +} +``` + +> Confirm the `columnBuilders` property name against `packages/stack/src/eql/v3/table.ts` (`EncryptedTable`); if the concrete-column map is exposed under another name, adjust here and in the SDK adapter (Step 4). + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/from-stack-divergence-v3` → PASS. + +- [ ] **Step 4: Implement the v3 SDK adapter (TDD)** + +Create `packages/prisma-next/test/v3/sdk-adapter-v3.test.ts` asserting `createCipherstashV3Sdk` routes `(table, column)` to the v3 typed objects and calls the client's `bulkEncrypt`, returning a bare array (matching `CipherstashSdk.bulkEncrypt: Promise>`). Then create `packages/prisma-next/src/v3/sdk-adapter-v3.ts`: + +```ts +import type { AnyEncryptedV3Column, AnyV3Table } from '@cipherstash/stack/eql/v3' +import type { CipherstashRoutingKey, CipherstashSdk } from '../execution/sdk' + +/** Minimal structural view of the stack `EncryptionV3` client. Confirm the real + * result shapes against `packages/stack/src/encryption/v3.ts` at implementation + * time; if a method returns a chainable operation, `await` it directly (it is a + * PromiseLike) and keep this adapter's `CipherstashSdk` output shape unchanged. */ +interface V3Client { + bulkEncrypt( + payload: ReadonlyArray<{ plaintext: unknown }>, + opts: { table: AnyV3Table; column: AnyEncryptedV3Column }, + ): PromiseLike<{ data?: Array<{ data: unknown }>; failure?: { message: string } }> + decrypt(encrypted: unknown): PromiseLike<{ data?: unknown; failure?: { message: string } }> + bulkDecrypt( + payloads: ReadonlyArray<{ data: unknown }>, + ): PromiseLike<{ data?: Array<{ data?: unknown; error?: unknown }>; failure?: { message: string } }> +} + +interface V3RegistryEntry { + readonly table: AnyV3Table + readonly columns: ReadonlyMap +} + +export function createCipherstashV3Sdk(client: V3Client, schemas: ReadonlyArray): CipherstashSdk { + const registry = new Map() + for (const table of schemas) { + const columns = new Map() + for (const value of Object.values(table.columnBuilders)) { + columns.set((value as AnyEncryptedV3Column).getName(), value as AnyEncryptedV3Column) + } + registry.set(table.tableName, { table, columns }) + } + + function lookup(rk: CipherstashRoutingKey) { + const entry = registry.get(rk.table) + if (!entry) throw new Error(`cipherstash v3 SDK adapter: routing-key table "${rk.table}" is not in the v3 schemas.`) + const column = entry.columns.get(rk.column) + if (!column) throw new Error(`cipherstash v3 SDK adapter: routing-key column "${rk.column}" is not on v3 table "${rk.table}".`) + return { table: entry.table, column } + } + + return { + async bulkEncrypt({ values, routingKey }) { + const { table, column } = lookup(routingKey) + const result = await client.bulkEncrypt(values.map((plaintext) => ({ plaintext })), { table, column }) + if (result.failure) throw new Error(`cipherstash v3 bulkEncrypt failed: ${result.failure.message}`) + return (result.data ?? []).map((e) => e.data) + }, + async decrypt({ ciphertext }) { + const result = await client.decrypt(ciphertext) + if (result.failure) throw new Error(`cipherstash v3 decrypt failed: ${result.failure.message}`) + return result.data as string + }, + async bulkDecrypt({ ciphertexts }) { + const result = await client.bulkDecrypt(ciphertexts.map((data) => ({ data }))) + if (result.failure) throw new Error(`cipherstash v3 bulkDecrypt failed: ${result.failure.message}`) + return (result.data ?? []).map((entry) => { + if (entry.error !== undefined) throw new Error(`cipherstash v3 bulkDecrypt entry failed: ${String(entry.error)}`) + return entry.data as string + }) + }, + } +} +``` + +- [ ] **Step 5: Implement the v3 runtime descriptor** + +Create `packages/prisma-next/src/v3/runtime-v3.ts`: + +```ts +import type { SqlRuntimeExtensionDescriptor } from '@prisma-next/sql-runtime' +// v3 uses its OWN extension id/version (decision 1b) — NOT the v2 +// CIPHERSTASH_SPACE_ID / CIPHERSTASH_EXTENSION_VERSION. Sharing the v2 id while +// registering the same `cipherstash*` method names is the collision that makes +// v2+v3 co-registration throw; distinct ids keep the two entry points cleanly +// separate. (Method names stay identical — they are never co-registered.) +import { + CIPHERSTASH_V3_EXTENSION_VERSION, + CIPHERSTASH_V3_SPACE_ID, +} from '../extension-metadata/constants-v3' +import type { CipherstashSdk } from '../execution/sdk' +import { createV3CodecDescriptors } from './codec-runtime-v3' +import { cipherstashV3QueryOperations } from './operators-v3' + +export function createCipherstashV3RuntimeDescriptor(opts: { sdk: CipherstashSdk }): SqlRuntimeExtensionDescriptor<'postgres'> { + const descriptors = createV3CodecDescriptors(opts.sdk) + return { + kind: 'extension' as const, + id: CIPHERSTASH_V3_SPACE_ID, + version: CIPHERSTASH_V3_EXTENSION_VERSION, + familyId: 'sql' as const, + targetId: 'postgres' as const, + types: { codecTypes: { codecDescriptors: descriptors } }, + codecs: () => descriptors, + queryOperations: () => cipherstashV3QueryOperations(), + create() { + return { familyId: 'sql' as const, targetId: 'postgres' as const } + }, + } +} +``` + +> Match the exact `SqlRuntimeExtensionDescriptor` field set against the v2 `createCipherstashRuntimeDescriptor` in `runtime.ts`; copy any additional required fields verbatim. + +- [ ] **Step 6: v3 entry point — `cipherstashFromStackV3` (separate from v2, never co-registered)** + +Decision **1b**: v2 and v3 are **separate entry points** and are **never co-registered** in one client. The framework `OperationRegistry` is a flat, method-name-keyed map that disallows override, so pushing both the v2 and v3 runtime descriptors (both defining `cipherstashEq`) into one `extensions[]` collides at registration. Do **NOT** extend `cipherstashFromStack` to build both. Leave the v2 `cipherstashFromStack` **unchanged**, and add a v3-only entry point. A given client is v2 or v3; mixed v2+v3 columns in one client are unsupported this release. + +Create `packages/prisma-next/src/stack/from-stack-v3.ts`: + +```ts +import type { ClientConfig } from '@cipherstash/stack' +import { EncryptionV3 } from '@cipherstash/stack/v3' // confirm the v3 client entry path (Step 8 note) +import { isCipherstashV3CodecId } from '../extension-metadata/constants-v3' +import { bulkEncryptMiddlewareV3 } from '../v3/bulk-encrypt-v3' +import { assertV3SchemasAgree } from '../v3/from-stack-v3-validate' +import { deriveStackSchemasV3 } from '../v3/derive-schemas-v3' +import { createCipherstashV3RuntimeDescriptor } from '../v3/runtime-v3' +import { createCipherstashV3Sdk } from '../v3/sdk-adapter-v3' + +export interface CipherstashFromStackV3Options { + readonly contractJson: unknown + readonly schemasV3?: ReadonlyArray + readonly encryptionConfig?: ClientConfig +} + +/** + * v3-only stack entry point. A v3 client is v3-only: a contract carrying a v2 + * codec id is the WRONG entry point (mixed v2+v3 in one client is unsupported — + * decision 1b), and is a hard error rather than a silently-derived v2 column. + */ +export async function cipherstashFromStackV3( + opts: CipherstashFromStackV3Options, +): Promise { + const foreignV2 = collectV2CodecIds(opts.contractJson) // any codecId that is NOT isCipherstashV3CodecId + if (foreignV2.length > 0) { + throw new Error( + `cipherstashFromStackV3: contract.json contains v2 codec ids [${foreignV2.join(', ')}]. ` + + 'A v3 client is v3-only; use cipherstashFromStack for a v2 contract. Mixed v2+v3 in one client is unsupported.', + ) + } + + const derivedV3 = deriveStackSchemasV3(opts.contractJson) + if (derivedV3.length === 0) { + throw new Error( + 'cipherstashFromStackV3: no v3 cipherstash columns in contract.json. Declare at least one v3 cipherstash.Encrypted*() column and re-emit.', + ) + } + + if (opts.schemasV3) { + const overrideByName = new Map(opts.schemasV3.map((t) => [t.tableName, t])) + for (const dt of derivedV3) { + const ot = overrideByName.get(dt.tableName) + if (ot) assertV3SchemasAgree(dt, ot) // exact domain identity: integer_ord ≠ integer_ord_ore + } + } + + const [f, ...r] = derivedV3 + const clientV3 = await EncryptionV3({ + schemas: [f!, ...r] as never, + ...(opts.encryptionConfig ? { config: opts.encryptionConfig } : {}), + }) + const sdkV3 = createCipherstashV3Sdk(clientV3 as never, derivedV3) + return { + extensions: [createCipherstashV3RuntimeDescriptor({ sdk: sdkV3 })], + middleware: [bulkEncryptMiddlewareV3(sdkV3)], + encryptionClient: clientV3 as never, + } +} + +/** Walk the contract columns and collect every codecId that is not a v3 id. */ +function collectV2CodecIds(contractJson: unknown): string[] { + const ids = new Set() + for (const col of iterateContractColumns(contractJson)) { + if (typeof col.codecId === 'string' && col.codecId.startsWith('cipherstash/') && !isCipherstashV3CodecId(col.codecId)) { + ids.add(col.codecId) + } + } + return [...ids] +} +``` + +(`iterateContractColumns` is the same contract walker `deriveStackSchemasV3` uses.) The v2 `cipherstashFromStack` keeps its signature and body untouched. + +- [ ] **Step 7: Export the v3 surface** + +In `packages/prisma-next/src/exports/runtime.ts` add: + +```ts +export { EncryptedNumber } from '../v3/envelope-number' +export { createCipherstashV3RuntimeDescriptor } from '../v3/runtime-v3' +export { createV3CodecDescriptors } from '../v3/codec-runtime-v3' +export { cipherstashV3QueryOperations, EncryptionOperatorError } from '../v3/operators-v3' +export { bulkEncryptMiddlewareV3 } from '../v3/bulk-encrypt-v3' +export { v3FromDriver, v3ToDriver } from '../v3/wire-v3' +``` + +In `packages/prisma-next/src/exports/stack.ts` add: + +```ts +export { deriveStackSchemasV3 } from '../v3/derive-schemas-v3' +export { createCipherstashV3Sdk } from '../v3/sdk-adapter-v3' +export { assertV3SchemasAgree } from '../v3/from-stack-v3-validate' +``` + +- [ ] **Step 8: Run and commit** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/derive-schemas-v3 v3/sdk-adapter-v3 v3/from-stack-divergence-v3 && pnpm --filter @cipherstash/prisma-next build` +Expected: PASS + build clean. (If the stack v3 client is exported from a subpath other than `@cipherstash/stack/v3`, grep `packages/stack/package.json` `exports` for the `EncryptionV3` entry and adjust the import in Step 6.) + +```bash +git add packages/prisma-next/src/v3/derive-schemas-v3.ts packages/prisma-next/src/v3/sdk-adapter-v3.ts packages/prisma-next/src/v3/runtime-v3.ts packages/prisma-next/src/v3/from-stack-v3-validate.ts packages/prisma-next/src/stack/from-stack.ts packages/prisma-next/src/exports/stack.ts packages/prisma-next/src/exports/runtime.ts packages/prisma-next/test/v3/derive-schemas-v3.test.ts packages/prisma-next/test/v3/sdk-adapter-v3.test.ts packages/prisma-next/test/v3/from-stack-divergence-v3.test.ts +git commit -m "feat(prisma-next): v3 derivation, SDK adapter, runtime descriptor, two-client from-stack dispatch" +``` + +--- + +### Task 8: v3 bundle baseline migration (SQL from `@cipherstash/eql/sql`) + +Add a v3 baseline migration `20260601T0100_install_eql_v3_bundle` with invariant `cipherstash:install-eql-v3-bundle-v1`, whose install SQL is sourced at emit time from `@cipherstash/eql/sql` (`readInstallSql()` / `releaseManifest.eqlVersion`, `@cipherstash/eql@3.0.0-alpha.3` — the same source `packages/stack/scripts/install-eql-v3.ts` / `installEqlV3IfNeeded` use), NOT a hand-vendored FFI fixture. Wire it into the control descriptor after the v2 baseline. v3 columns emit **no** `add_search_config`/`remove_search_config`. + +**Files:** +- Modify: `packages/prisma-next/package.json` (add `@cipherstash/eql` devDependency) +- Create: `packages/prisma-next/src/migration/eql-bundle-v3.ts`, `packages/prisma-next/migrations/20260601T0100_install_eql_v3_bundle/migration.ts` +- Generated by running `migration.ts`: `migration.json`, `ops.json`, `end-contract.json`, updated `migrations/refs/head.json` +- Modify: `packages/prisma-next/src/exports/control.ts` +- Test: `packages/prisma-next/test/v3/migration-v3.test.ts` + +**Interfaces:** +- Consumes: `@cipherstash/eql/sql` `readInstallSql()`, `releaseManifest`; Task 2 `CIPHERSTASH_V3_INVARIANTS`, `CIPHERSTASH_V3_BASELINE_MIGRATION_NAME`; framework `Migration`, `MigrationCLI`, `rawSql` (`@prisma-next/target-postgres/migration`); `contractSpaceFromJson`. +- Produces: `eql-bundle-v3.ts` (re-export of `readInstallSql` / `releaseManifest`); the migration module + emitted artifacts. + +- [ ] **Step 1: Add the `@cipherstash/eql` devDependency** + +Edit `packages/prisma-next/package.json` — add to `devDependencies`: + +```json +"@cipherstash/eql": "3.0.0-alpha.3", +``` + +Run: `pnpm install` (repo root). Expected: resolves to the version already in the monorepo (stack's devDep). The SQL is baked into `ops.json` at emit time, so `@cipherstash/eql` stays a devDependency (not a runtime dependency of the published package). + +- [ ] **Step 2: Create the bundle re-export** + +Create `packages/prisma-next/src/migration/eql-bundle-v3.ts`: + +```ts +/** + * CipherStash EQL v3 install SQL, sourced from `@cipherstash/eql/sql` — the same + * source the stack install script uses. `readInstallSql()` returns the full + * bundle that creates the `public.*` domains and the `eql_v3.*` operator + * functions; `releaseManifest.eqlVersion` identifies the pinned release. The SQL + * is read at migration EMIT time and baked into `ops.json`. + */ +export { readInstallSql, releaseManifest } from '@cipherstash/eql/sql' +``` + +- [ ] **Step 3: Create the migration module** + +Create `packages/prisma-next/migrations/20260601T0100_install_eql_v3_bundle/migration.ts` (mirror the v2 `20260601T0000_install_eql_bundle/migration.ts`, but v3 invariant, v3 SQL, and a v3 postcheck — a concrete `public.*` domain + the `eql_v3` operator schema exist, and NO `add_search_config`): + +```ts +#!/usr/bin/env -S node +import { Migration, MigrationCLI, rawSql } from '@prisma-next/target-postgres/migration' +import { CIPHERSTASH_V3_INVARIANTS } from '../../src/extension-metadata/constants-v3' +import { readInstallSql, releaseManifest } from '../../src/migration/eql-bundle-v3' + +const INSTALL_LABEL = `Install EQL v3 bundle (${releaseManifest.eqlVersion}: public.* domains + eql_v3.* functions)` + +export default class M extends Migration { + override describe() { + return { from: null, to: 'sha256:REPLACE_WITH_EMITTED_HASH' } + } + override get operations() { + return [ + rawSql({ + id: 'cipherstash.install-eql-v3-bundle', + label: INSTALL_LABEL, + operationClass: 'additive', + invariantId: CIPHERSTASH_V3_INVARIANTS.installBundle, + target: { id: 'postgres' }, + precheck: [], + execute: [{ description: INSTALL_LABEL, sql: readInstallSql() }], + postcheck: [ + { + description: 'verify the eql_v3 operator schema exists', + sql: "SELECT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'eql_v3')", + }, + { + description: 'verify the concrete domain public.text_search exists', + sql: "SELECT to_regtype('public.text_search') IS NOT NULL", + }, + { + description: 'verify the eql_v3.eq operator function exists', + sql: "SELECT to_regprocedure('eql_v3.eq(public.eql_encrypted, jsonb)') IS NOT NULL OR to_regproc('eql_v3.eq') IS NOT NULL", + }, + ], + }), + ] + } +} + +MigrationCLI.run(import.meta.url, M) +``` + +> Match the exact `rawSql` field names + the `Migration` subclass shape against the v2 `migration.ts`. In particular confirm whether it uses `get operations()` or `override operations()` and the postcheck object keys; copy the v2 shape verbatim, changing only id/label/invariant/sql/postcheck. + +- [ ] **Step 4: Emit the migration artifacts** + +Run: `node packages/prisma-next/migrations/20260601T0100_install_eql_v3_bundle/migration.ts` +Expected: writes `migration.json`, `ops.json`, `end-contract.json` and prints the `to` contract hash. Copy that hash into `describe().to` (replacing `REPLACE_WITH_EMITTED_HASH`) and re-run once so the artifacts are internally consistent. If the CLI manages `migrations/refs/head.json`, let it; otherwise append `cipherstash:install-eql-v3-bundle-v1` to its `invariants` and update `hash` per the CLI's printed guidance. + +- [ ] **Step 5: Wire the v3 baseline into control.ts** + +In `packages/prisma-next/src/exports/control.ts`: + +```ts +import v3BaselineMetadata from '../../migrations/20260601T0100_install_eql_v3_bundle/migration.json' with { type: 'json' } +import v3BaselineOps from '../../migrations/20260601T0100_install_eql_v3_bundle/ops.json' with { type: 'json' } +import { CIPHERSTASH_V3_BASELINE_MIGRATION_NAME } from '../extension-metadata/constants-v3' +// ... + migrations: [ + { dirName: CIPHERSTASH_BASELINE_MIGRATION_NAME, metadata: baselineMetadata, ops: baselineOps }, + { dirName: CIPHERSTASH_V3_BASELINE_MIGRATION_NAME, metadata: v3BaselineMetadata, ops: v3BaselineOps }, + ], +``` + +Do **not** register any v3 codec ids in `controlPlaneHooks` — that omission is what guarantees v3 columns emit no `add_search_config`/`remove_search_config`. + +- [ ] **Step 6: Write the migration assertion test** + +Create `packages/prisma-next/test/v3/migration-v3.test.ts`: + +```ts +import { describe, expect, it } from 'vitest' +import v3Ops from '../../migrations/20260601T0100_install_eql_v3_bundle/ops.json' + +describe('v3 baseline migration', () => { + it('installs under the v3 invariant with an additive rawSql op', () => { + const op = (v3Ops as Array>)[0]! + expect(op.invariantId).toBe('cipherstash:install-eql-v3-bundle-v1') + expect(op.operationClass).toBe('additive') + }) + it('emits no add_search_config / remove_search_config ops', () => { + const json = JSON.stringify(v3Ops) + expect(json).not.toContain('add_search_config') + expect(json).not.toContain('remove_search_config') + }) + it('sorts after the v2 baseline', () => { + expect('20260601T0100_install_eql_v3_bundle' > '20260601T0000_install_eql_bundle').toBe(true) + }) +}) +``` + +- [ ] **Step 7: Run and commit** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/migration-v3 descriptor && pnpm --filter @cipherstash/prisma-next build` +Expected: PASS (including the existing `descriptor.test.ts` self-consistency check with the second migration wired). + +```bash +git add packages/prisma-next/package.json packages/prisma-next/src/migration/eql-bundle-v3.ts packages/prisma-next/migrations/20260601T0100_install_eql_v3_bundle packages/prisma-next/migrations/refs/head.json packages/prisma-next/src/exports/control.ts packages/prisma-next/test/v3/migration-v3.test.ts ../../pnpm-lock.yaml +git commit -m "feat(prisma-next): v3 bundle baseline migration from @cipherstash/eql/sql (install-eql-v3-bundle-v1)" +``` + +--- + +### Task 9: Property-based tests (`fast-check`) + +Add `fast-check ^4.8.0` as a prisma-next devDependency and cover the five required properties. (There is no resolver, so there is no resolver-totality property — the constructor↔domain totality property replaces it.) + +**Files:** +- Modify: `packages/prisma-next/package.json` (devDependencies) +- Create: `packages/prisma-next/test/v3/properties.test.ts` + +**Interfaces:** +- Consumes: `fast-check`; Task 4 `v3ToDriver`/`v3FromDriver`, `EncryptedNumber`; Task 1 `V3_DOMAIN_META_BY_CODEC_ID`, `EXPOSED_DOMAIN_ENTRIES`; Task 3 `v3PascalName`; Task 6 `EncryptionOperatorError`; Task 7 `deriveStackSchemasV3`; `cipherstashAuthoringTypes`. + +- [ ] **Step 1: Add the devDependency** + +Edit `packages/prisma-next/package.json` — add to `devDependencies`: + +```json +"fast-check": "^4.8.0", +``` + +Run: `pnpm install` (repo root). Expected: resolves to the monorepo catalog version already present in `packages/drizzle`. + +- [ ] **Step 2: Write the five properties** + +Create `packages/prisma-next/test/v3/properties.test.ts`: + +```ts +import fc from 'fast-check' +import { describe, expect, it } from 'vitest' +import { cipherstashAuthoringTypes, v3PascalName } from '../../src/contract-authoring' +import { EncryptedNumber } from '../../src/v3/envelope-number' +import { v3FromDriver, v3ToDriver } from '../../src/v3/wire-v3' +import { EXPOSED_DOMAIN_ENTRIES, V3_DOMAIN_META_BY_CODEC_ID } from '../../src/v3/catalog' +import { deriveStackSchemasV3 } from '../../src/v3/derive-schemas-v3' + +describe('property: JSONB round-trip', () => { + it('v3FromDriver(v3ToDriver(x)) deep-equals x; null/undefined -> null', () => { + fc.assert( + fc.property(fc.jsonValue(), (x) => { + expect(v3FromDriver(v3ToDriver(x))).toEqual(x) + }), + ) + expect(v3ToDriver(null)).toBeNull() + expect(v3ToDriver(undefined)).toBeNull() + }) +}) + +describe('property: [REDACTED] invariant', () => { + it('serialized envelope never contains the plaintext and always renders the placeholder', () => { + fc.assert( + fc.property(fc.double({ noNaN: true }), (n) => { + const env = EncryptedNumber.from(n) + expect(JSON.stringify(env)).toBe('{"$encryptedNumber":""}') + expect(String(env)).toBe('[REDACTED]') + expect(`${env}`).not.toContain(String(n)) + }), + ) + }) +}) + +describe('property: constructor↔domain totality (public.*)', () => { + const ns = cipherstashAuthoringTypes.cipherstash as Record }> + it('every exposed domain has exactly one constructor whose descriptor equals the catalog values', () => { + fc.assert( + fc.property(fc.constantFrom(...EXPOSED_DOMAIN_ENTRIES), ([codecId, meta]) => { + const ctor = ns[v3PascalName(meta.bareDomain)] + expect(ctor).toBeDefined() + expect(ctor.output.codecId).toBe(codecId) + expect(ctor.output.nativeType).toBe(meta.nativeType) + expect(meta.nativeType.startsWith('public.')).toBe(true) + expect((ctor.output.typeParams as { capabilities: unknown }).capabilities).toEqual(meta.capabilities) + }), + ) + }) + it('no constructor maps off-catalog', () => { + const validNames = new Set(EXPOSED_DOMAIN_ENTRIES.map(([, m]) => v3PascalName(m.bareDomain))) + for (const name of Object.keys(ns)) { + if (name.endsWith('V2')) continue + expect(validNames.has(name), `constructor ${name} is not in the exposed catalog`).toBe(true) + } + }) +}) + +describe('property: operator-gating equivalence', () => { + it('the equality gate allows a domain iff it has a unique or ore index', () => { + fc.assert( + fc.property(fc.constantFrom(...V3_DOMAIN_META_BY_CODEC_ID.keys()), (codecId) => { + const meta = V3_DOMAIN_META_BY_CODEC_ID.get(codecId)! + const gateAllows = !!(meta.indexes.unique || meta.indexes.ore) + const predicate = meta.capabilities.equality || meta.capabilities.orderAndRange + expect(gateAllows).toBe(predicate) + }), + ) + }) +}) + +describe('property: derivation round-trip', () => { + it('arbitrary v3 contract column -> deriveStackSchemasV3 -> derived domain identity == original', () => { + fc.assert( + fc.property(fc.constantFrom(...V3_DOMAIN_META_BY_CODEC_ID.entries()), ([codecId, meta]) => { + const [table] = deriveStackSchemasV3({ + storage: { tables: { user: { columns: { c: { codecId, nativeType: meta.nativeType } } } } }, + }) + const col = Object.values(table!.columnBuilders)[0]! + expect((col as { getEqlType(): string }).getEqlType()).toBe(meta.nativeType) + }), + ) + }) +}) +``` + +- [ ] **Step 3: Run and commit** + +Run: `pnpm --filter @cipherstash/prisma-next test -- v3/properties` +Expected: PASS (5 property blocks). + +```bash +git add packages/prisma-next/package.json packages/prisma-next/test/v3/properties.test.ts ../../pnpm-lock.yaml +git commit -m "test(prisma-next): property-based coverage for v3 wire, constructor totality, gating, derivation" +``` + +--- + +### Task 10: Bundling isolation + live-PG integration suites + +Pin the namespace-separation invariant at the bundle level (v3 entry never pulls the v2 composite codec), and add the seven live-PG suites mirroring the Drizzle branch, installing v3 via `@cipherstash/eql/sql` `readInstallSql()` behind an advisory lock. + +**Files:** +- Modify: `packages/prisma-next/test/bundling-isolation.test.ts` (add v3 assertions) +- Create: `packages/prisma-next/test/live/helpers/live-gate.ts`, `packages/prisma-next/test/live/helpers/eql-v3.ts`, and seven suites under `packages/prisma-next/test/live/` +- Modify: `packages/prisma-next/package.json` (optional `test:live` script) + +**Interfaces:** +- Consumes: `@cipherstash/eql/sql` `readInstallSql` / `releaseManifest`; `postgres`; the v3 runtime/middleware from Tasks 4–7; the stack `EncryptionV3` client. +- Produces: `LIVE_EQL_V3_PG_ENABLED`, `describeLivePg`, `installEqlV3IfNeeded(sql)` local helpers. + +- [ ] **Step 1: Bundling isolation — add v3 forbidden-substring assertions** + +In `packages/prisma-next/test/bundling-isolation.test.ts`, assert that no v3-only chunk reachable from the v3 barrel/runtime re-exports includes the v2 composite encoder (`encodeEqlV2EncryptedWire`), reusing the file's existing `collectGraph`/`readChunk` helpers: + +```ts +it('v3 codec runtime does not pull the v2 composite-literal codec', () => { + const v3Chunks = [...collectGraph('runtime.js').entries()].filter( + ([name]) => name.includes('v3') || name.includes('codec-runtime-v3'), + ) + for (const [, chunk] of v3Chunks) { + expect(chunk.body.includes('encodeEqlV2EncryptedWire')).toBe(false) + } +}) +``` + +> The chunk-name predicate depends on tsup's output; adjust the `.filter` after inspecting `dist/` names. + +Run: `pnpm --filter @cipherstash/prisma-next build && pnpm --filter @cipherstash/prisma-next test -- bundling-isolation` +Expected: PASS. + +- [ ] **Step 2: Create the local live-PG helpers** + +Create `packages/prisma-next/test/live/helpers/live-gate.ts`: + +```ts +import { describe } from 'vitest' + +export const LIVE_CIPHERSTASH_ENABLED = Boolean( + process.env.CS_WORKSPACE_CRN && process.env.CS_CLIENT_ID && process.env.CS_CLIENT_KEY && process.env.CS_CLIENT_ACCESS_KEY, +) +export const LIVE_EQL_V3_PG_ENABLED = Boolean(process.env.DATABASE_URL && LIVE_CIPHERSTASH_ENABLED) +export const describeLivePg = LIVE_EQL_V3_PG_ENABLED ? describe : describe.skip +``` + +Create `packages/prisma-next/test/live/helpers/eql-v3.ts` (mirror `packages/stack/__tests__/helpers/eql-v3.ts` — advisory lock + `eql_v3.version()` staleness probe + `readInstallSql()`): + +```ts +import { readInstallSql, releaseManifest } from '@cipherstash/eql/sql' +import type postgres from 'postgres' + +const EQL_V3_ADVISORY_LOCK_ID = 3_733_003 + +async function readEqlV3Version(sql: postgres.Sql): Promise { + const [probe] = await sql<{ present: boolean }[]>`SELECT to_regprocedure('eql_v3.version()') IS NOT NULL AS present` + if (!probe?.present) return null + const [row] = await sql<{ version: string }[]>`SELECT eql_v3.version() AS version` + return row?.version ?? null +} + +export async function installEqlV3IfNeeded(sql: postgres.Sql): Promise { + const reserved = await sql.reserve() + try { + await reserved`SELECT pg_advisory_lock(${EQL_V3_ADVISORY_LOCK_ID})` + try { + if ((await readEqlV3Version(reserved)) === releaseManifest.eqlVersion) return + await reserved.unsafe(readInstallSql()) + const installed = await readEqlV3Version(reserved) + if (installed !== releaseManifest.eqlVersion) { + throw new Error(`EQL v3 installation did not yield ${releaseManifest.eqlVersion}, got ${installed ?? 'none'}`) + } + } finally { + await reserved`SELECT pg_advisory_unlock(${EQL_V3_ADVISORY_LOCK_ID})` + } + } finally { + reserved.release() + } +} +``` + +- [ ] **Step 3: Write the seven live suites (self-skipping)** + +Create the following under `packages/prisma-next/test/live/`, each guarded by `describeLivePg`, installing v3 in `beforeAll`, and driving real INSERT/SELECT/decrypt against the `public.*` domains + `eql_v3.*` operators via `cipherstashFromStack` + a real `EncryptionV3` client. Shared skeleton: + +```ts +import { afterAll, beforeAll, expect, it } from 'vitest' +import postgres from 'postgres' +import { describeLivePg, LIVE_EQL_V3_PG_ENABLED } from './helpers/live-gate' +import { installEqlV3IfNeeded } from './helpers/eql-v3' + +const sql = LIVE_EQL_V3_PG_ENABLED ? postgres(process.env.DATABASE_URL!, { prepare: false }) : undefined +beforeAll(async () => { if (LIVE_EQL_V3_PG_ENABLED && sql) await installEqlV3IfNeeded(sql) }) +afterAll(async () => { if (sql) await sql.end() }) +``` + +1. `operators-live-pg.test.ts` — per capability tier (`text_search`, `text_eq`, `integer_ord`, `date_ord`, `timestamp_ord`): encrypt → INSERT → query via each `cipherstash*` operator → decrypt; assert the **expected row is selected** (not just an SQL string). +2. `operators-null-live-pg.test.ts` — null operands and null columns round-trip (insert NULL, select, decrypt to null). +3. `boolean-storage-live-pg.test.ts` — `public.boolean` survives INSERT/SELECT/decrypt and every `cipherstash*` search operator throws `EncryptionOperatorError` (storage-only). +4. `bigint-live-pg.test.ts` — `public.bigint` / `bigint_eq` / `bigint_ord` encrypt → INSERT → query (eq / order-range) → decrypt to a JS `bigint`; assert `typeof decrypted === 'bigint'`, lossless value, and ORE ordering over real domains. +5. `migration-apply-live-pg.test.ts` — apply the v3 bundle (`installEqlV3IfNeeded`); assert `to_regtype('public.text_search')`/`public.integer_ord` are non-null, the `eql_v3` schema exists, the invariant `cipherstash:install-eql-v3-bundle-v1` is recorded (via the migration ops fixture or the runner's marker table), and no `add_search_config` was executed. +6. `bulk-encrypt-live-pg.test.ts` — drive the v3 middleware against a **real** `EncryptionV3(...)` client (not a fake) → INSERT; assert the stored cell parses as JSON and decrypts to the original, proving routing-key stamping + JSONB payloads end-to-end. +7. `mixed-v2-v3-live-pg.test.ts` — one table with a legacy `*V2` column and v3 columns; insert/query/decrypt both, proving mixed-mode derivation + two-client routing against a real DB. + +- [ ] **Step 4: Verify self-skip locally, then commit** + +Run (no live env): `pnpm --filter @cipherstash/prisma-next test` +Expected: the seven live suites report `skipped`; every non-live suite passes. + +```bash +git add packages/prisma-next/test/bundling-isolation.test.ts packages/prisma-next/test/live packages/prisma-next/package.json +git commit -m "test(prisma-next): v3 bundling-isolation + live-PG integration suites" +``` + +--- + +### Task 11: Release notes / changeset + +**Files:** +- Create: `.changeset/eql-v3-prisma-next.md` + +- [ ] **Step 1: Write the changeset** + +Create `.changeset/eql-v3-prisma-next.md` (major if the package is treated as stable; the body must call out the new authoring surface regardless — see repo `.changeset/config.json` for the bump policy): + +```md +--- +"@cipherstash/prisma-next": major +--- + +**Breaking:** EQL v3 columns are now authored through **concrete per-domain constructors** — the constructor you choose *is* the capability set. The legacy boolean-option surface (`EncryptedString({ equality, freeTextSearch, orderAndRange })`) is not carried into v3. + +- New per-domain constructors, one per exposed `public.*` domain: + - Text: `EncryptedText` (storage), `EncryptedTextEq`, `EncryptedTextOrd` (eq + order/range), `EncryptedTextMatch` (free-text), `EncryptedTextSearch` (eq + free-text + order/range). + - Scalars (Integer, Smallint, BigInt, Numeric, Real, Double, Date, Timestamp): `Encrypted` (storage), `EncryptedEq`, `EncryptedOrd`. + - `EncryptedBoolean` — storage-only (`public.boolean`); there is no boolean equality constructor. +- **Impossible capability combinations have no constructor** (e.g. text equality + free-text without order/range) — they are unrepresentable, not runtime errors. +- **BigInt is a first-class v3 family** (`EncryptedBigInt` / `EncryptedBigIntEq` / `EncryptedBigIntOrd`, JS `bigint` plaintext, backed by `public.bigint*`). +- **Searchable JSON has no v3 constructor.** Use `EncryptedJsonV2`. +- Use the `*V2` constructors (`EncryptedStringV2`, `EncryptedDoubleV2`, `EncryptedBigIntV2`, `EncryptedDateV2`, `EncryptedBooleanV2`, `EncryptedJsonV2`) to keep EQL v2 columns. +- Query operators (`cipherstashEq`, `cipherstashGt`, …) lower to the two-arg `eql_v3.*(...::jsonb)` functions for v3 columns; `cipherstashIlike` maps to `eql_v3.contains` (bloom-filter token containment, not SQL `LIKE`). The domains are `public.*`; the operator functions live in `eql_v3`. +- A new baseline migration `20260601T0100_install_eql_v3_bundle` (invariant `cipherstash:install-eql-v3-bundle-v1`) installs the `public.*` domains and `eql_v3.*` functions. Regenerate contracts and run migrations after changing constructors. +``` + +- [ ] **Step 2: Full green + commit** + +Run: `pnpm --filter @cipherstash/prisma-next test && pnpm --filter @cipherstash/prisma-next build && pnpm --filter @cipherstash/prisma-next lint` +Expected: all PASS. Because Task 1 touched `@cipherstash/stack` public exports, also run: `pnpm --filter @cipherstash/stack test`. + +```bash +git add .changeset/eql-v3-prisma-next.md +git commit -m "docs(prisma-next): changeset for EQL v3 per-domain authoring surface" +``` + +--- + +## Self-Review + +**1. Spec coverage** — each spec section maps to a task: + +| Spec section | Task(s) | +| --- | --- | +| Goal / Public Authoring Surface (per-domain constructors + `*V2`) | 3 | +| Namespace Separation (dedicated `src/v3/`, separate v2/v3 entry points — decision 1b) | 1–10 (structural); v3 entry point in 7 | +| Source of Truth (import `DOMAIN_REGISTRY`, derive; codec id strips `public.`) | 1, 2 | +| Schema split (native types `public.*`; operator functions `eql_v3.*`) | 1 (meta), 4 (codec native type), 6 (operator templates); pinned by 2 drift test | +| Constructor Inventory (Encrypted prefix, exclude `*OrdOre`, BigInt first-class, no v3 JSON, boolean storage-only) | 3 | +| Capability semantics per domain | 3 (typeParams), 6 (gating) | +| Contract Representation (concrete codec id, `public.*` nativeType, typeParams castAs/capabilities; drop redundant eqlType; generated closed union; disjoint ids) | 2 (union/disjoint), 3 (representation) | +| Runtime Codecs (plain JSONB; EncryptedNumber sibling; EncryptedBigInt distinct envelope; `[REDACTED]`) | 4 | +| Middleware (separate v3, `sdk.bulkEncrypt`, JSONB, neutral routing-key stamp) | 5 | +| Operators (`eql_v3.* ::jsonb` templates; capability gating; `EncryptionOperatorError`; ilike→contains; no built-in equality traits; per-domain traits) | 6 | +| Schema Derivation + Stack Adapter (v3-only `cipherstashFromStackV3`; single `EncryptionV3` client; distinct extension id; v2 contract = hard error; exact-domain override validation) | 7 | +| Migrations (v3 baseline from `@cipherstash/eql/sql`; invariant; no add_search_config) | 8 | +| Testing → Unit/authoring (14 items) | 1,2,3,4,5,6,7,8 (mapping below) | +| Testing → Property-based (5) | 9 | +| Testing → Integration/live-PG (7) | 10 | +| Release Notes / changeset | 11 | + +**14 unit tests → tasks:** (1) authoring static per-domain codec id + `public.*` native type = 3; (2) constructor↔domain 1:1 = 3; (3) `*V2` legacy = 3; (4) unexposed/absent surface (`*OrdOre`, no v3 JSON, boolean storage-only, BigInt present) = 3 + 2 (drift); (5) catalog invariants + `public.*` vs `eql_v3.*` = 2; (6) runtime codec plain JSONB / no composite = 4; (7) middleware `sdk.bulkEncrypt` + JSONB = 5; (8) operator SQL lowering = 6; (9) operator gating = 6; (10) trait removal = 6; (11) derivation exact schemas = 7; (12) same-cast_as different-domain divergence = 7; (13) bundling isolation = 10; (14) codec-id disjointness = 2. **All 14 present.** **5 properties** all in Task 9. **7 live suites** all in Task 10. + +**Removed vs. the old (stale) plan:** +- **Old Task 4** (`resolveV3Domain` pure resolver) and **old Task 5** (`cipherstashV3ContractSource` / `source.load` wrapper) are **deleted** — design A has no resolution stage. The "family codec id / Stage 1 / Stage 2 / boolean-option" model and the pinned boolean/bigint/json error strings are gone. +- Old family constructors + boolean-option authoring replaced by derived per-domain constructors (Task 3). +- Old `eql_v3.*` **native types** corrected to `public.*` throughout (catalog, codec descriptors, derivation, override validation); operator **functions** stay `eql_v3.*`. +- Old hand-vendored FFI install fixture replaced by `@cipherstash/eql/sql` `readInstallSql()` (Task 8). +- Boolean domain corrected from `bool` → `boolean` (`public.boolean`, codec id `cipherstash/eql-v3/boolean@1`). +- BigInt promoted from "throws / unavailable" to a first-class family; `EncryptedBigInt` envelope reused (not recreated). + +**2. Placeholder scan** — no `TBD`/`TODO`/"add error handling"/"similar to Task N" left. Every code step shows real code. The remaining "confirm at implementation time" notes (framework static-typeParams shape in Task 3 Step 0; `envelope-double` shape in Task 4 Step 3; codec import paths in Task 4 Step 6; `columnBuilders` name in Task 7 Step 3; `EncryptionV3` entry path in Task 7 Step 6/8; `rawSql` field names in Task 8 Step 3; tsup chunk names in Task 10 Step 1) are each a concrete verification against a named file with a stated fallback, not a deferred design decision. + +**3. Type consistency** — names verified stable across tasks: +- Task 1 → `V3CastAs`, `V3DomainMeta`, `toV3CodecId`, `V3_DOMAIN_META_BY_CODEC_ID`, `V3_CODEC_IDS`, `V3_FACTORY_BY_NATIVE_TYPE`, `EXPOSED_DOMAIN_ENTRIES`, `isOrdOreDomain`, `envelopeTypeNameForCastAs` — consumed unchanged in 2,3,4,6,7,9. +- Task 2 → `CipherstashV3CodecId`, `CIPHERSTASH_V3_CODEC_IDS`, `CIPHERSTASH_V3_CODEC_ID_SET`, `isCipherstashV3CodecId`, `v3TraitsForCapabilities`, trait constants, `CIPHERSTASH_V3_INVARIANTS`, `CIPHERSTASH_V3_BASELINE_MIGRATION_NAME` — consumed in 3,4,5,6,7,8. +- Task 3 → `v3PascalName` / `v3CamelName` — consumed in 3 tests, 9. +- Task 4 → `EncryptedNumber` (`typeName='EncryptedNumber'`, distinct from `EncryptedDouble`), `v3ToDriver`/`v3FromDriver`, `createV3CodecDescriptors`, `CipherstashV3CellCodec` — consumed in 5,6,7,9,10. +- Task 5 → `bulkEncryptMiddlewareV3`; exported `stampRoutingKeysFromAst` — consumed in 7. +- Task 6 → `cipherstashV3QueryOperations`, `EncryptionOperatorError` — consumed in 7,9,10. +- Task 7 → `deriveStackSchemasV3`, `V3ContractShape`, `createCipherstashV3Sdk`, `createCipherstashV3RuntimeDescriptor`, `assertV3SchemasAgree` — consumed in 9 (derivation), 10 (live). +- `EncryptedBigInt` is REUSED from `../execution/envelope-bigint` in 4 and 6 (never recreated); `EncryptedString`/`EncryptedDate`/`EncryptedBoolean` reused identically. + +No signature drift found. Plan is internally consistent. + +--- + +**Plan complete and saved to `docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md`. Two execution options:** + +**1. Subagent-Driven (recommended)** — a fresh subagent per task, review between tasks, fast iteration. + +**2. Inline Execution** — execute tasks in this session using executing-plans, batch execution with checkpoints. + +**Which approach?** From 20a5aff02f40266846717f1c85d56681f2942b07 Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:29:58 -0600 Subject: [PATCH 12/15] docs(prisma-next): re-align EQL v3 plan with Prisma Next 0.14 The plan and spec were drafted against @prisma-next/* 0.8.0; the package is now pinned at 0.14.0. Changes: - new 'Prisma Next 0.14 ground truth' section: namespace-enveloped contract shape (storage.namespaces..entries.table, domain plane, public default namespace), the manual contract-emit/re-pin loop (self- emit writes only ops.json + migration.json), current head hash, validateSqlContractFully fixture shape, structured LoweredParam, PSL-interpreter harness requirements (defaultNamespaceId, composedExtensionContracts), FieldEventContext namespaceId, lazily awaitable toOp(), planExecutionId on middleware contexts, createRuntime removal (rawSql survives), canonicalization hooks for descriptor self-consistency - Task 7 + Task 9: V3ContractShape / deriveStackSchemasV3 / fixtures walk the 0.14 namespace envelope instead of flat storage.tables - Task 8: v3 baseline describe() chains from the v2 head hash; Step 4 rewritten as the real 0.14 emit loop with the invariant-only-edge question called out for verification - Task 3/6: point new tests at the 0.14-shaped harnesses on this branch - Task 10: replace the mixed-v2-v3-in-one-client live test (contradicted decision 1b and the spec) with side-by-side v2/v3 clients; the live suites go through cipherstashFromStackV3 - spec: revision pointer 2026-07-09c Co-Authored-By: Claude Fable 5 --- .../plans/2026-07-08-eql-v3-prisma-next.md | 112 +++++++++++++++--- .../2026-07-08-eql-v3-prisma-next-design.md | 1 + 2 files changed, 95 insertions(+), 18 deletions(-) diff --git a/docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md b/docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md index 0f9f172d5..11f2be297 100644 --- a/docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md +++ b/docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md @@ -6,7 +6,7 @@ **Architecture:** v3 is a **parallel, self-contained namespace** — never v3 branches grafted onto v2 code. Capability is encoded by constructor identity: the constructor you choose *is* the capability set, and it emits its concrete descriptor statically (`(codec id) ⟺ (public.* domain) ⟺ capabilities` is bijective — nothing is resolved). The **domains / native types live in `public`** (`public.text_eq`, `public.integer_ord`, `public.boolean`, `public.bigint_ord`); the **operator functions live in `eql_v3`** (`eql_v3.eq`, `eql_v3.gt`, `eql_v3.contains`, `eql_v3.ord_term`). These two schemas must never be conflated. The constructor set, codec ids, capabilities, cast kinds, and native types are all **derived from the imported `DOMAIN_REGISTRY`** in `@cipherstash/stack/eql/v3` — there is no hand-maintained local table. Concrete per-domain types are threaded end-to-end (literal `codecId` / `nativeType` / `capabilities`; see Task 3's `V3ColumnDescriptor` and the `.test-d.ts`). **v2 and v3 are separate entry points** (decision 1b): v3 is its own extension factory with its own distinct extension id, keeping the same `cipherstash*` method names, and is **never co-registered** with v2 in one client (the flat `OperationRegistry` disallows the duplicate-method override). A client is v2 or v3; mixed v2+v3 columns in one client are unsupported this release. -**Tech Stack:** TypeScript, `@cipherstash/stack` (`/eql/v3` catalog + a v3 `EncryptionV3` client), `@cipherstash/eql@3.0.0-alpha.3` (`/sql` → `readInstallSql` / `releaseManifest`), `@prisma-next/*` framework, `arktype`, `vitest` (`vitest run`), `fast-check ^4.8.0` (added here), `biome` (lint), `tsup` (build), `postgres` (live tests). +**Tech Stack:** TypeScript, `@cipherstash/stack` (`/eql/v3` catalog + a v3 `EncryptionV3` client), `@cipherstash/eql@3.0.0-alpha.3` (`/sql` → `readInstallSql` / `releaseManifest`), `@prisma-next/*` framework **pinned at 0.14.0** (see the "Prisma Next 0.14 ground truth" notes below — this plan was originally drafted against 0.8.0 and has been re-aligned), `arktype`, `vitest` (`vitest run`), `fast-check ^4.8.0` (added here), `biome` (lint), `tsup` (build), `postgres` (live tests). ## Global Constraints @@ -38,6 +38,31 @@ 7. **The Drizzle reference operator/gating shapes** live on branch `eql-v3-drizzle-concrete-types` (not the working tree). Their shapes are reproduced inline in the relevant tasks. Operator SQL is proven live in `packages/stack/__tests__/v3-matrix/matrix-live-pg.test.ts` and `schema-v3-pg.test.ts`. 8. **Operator method names + the flat registry (resolved: decision 1b).** The framework `OperationRegistry` is a flat, method-name-keyed map that disallows override, so two descriptors both defining `cipherstashEq` collide at registration regardless of extension id. Resolution: v2 and v3 are **separate entry points** — v3 registers its own extension descriptor (its own distinct `id`/`version`, same `cipherstash*` method names) and is **never co-registered** with the v2 descriptor. There is no single-shared-gated-descriptor and no in-one-client mixing. This is not a fallback; it is the design. +## Prisma Next 0.14 ground truth (re-alignment, 2026-07-09) + +This plan was drafted while `packages/prisma-next` pinned `@prisma-next/*` at **0.8.0**. The package has since been upgraded to **0.14.0** (branch `chore/prisma-next-0.14-upgrade`, one commit per minor). Everything below is verified against that branch and **overrides** any 0.8-era shape the original draft assumed. Tasks whose steps this affects carry an inline `> **0.14:**` note. + +1. **Contract JSON shape (affects every contract fixture and every contract walker).** `contract.json` is namespace-enveloped at both planes: + - Storage: `storage.namespaces..entries.table.` (the `entries.` envelope landed in 0.13). There is **no** flat `storage.tables` — the structural validator rejects it. + - Domain: `domain.namespaces..models` replaces flat `models`; `roots` entries are `{ model, namespace }` objects. + - The Postgres default namespace is **`public`** (`kind: 'postgres-schema'`) since 0.12; the emitted extension contract carries the sole namespace `public` (0.14 dropped the spurious empty `__unbound__` slot, TML-2916). + - Reference implementation: the v2 `deriveStackSchemas` (`src/stack/derive-schemas.ts`) already walks `storage.namespaces..tables`-era → 0.14 `entries`-shaped views on the branch; `deriveStackSchemasV3` must walk the same envelope (Task 7 has been updated accordingly). +2. **Handcrafted contract fixtures** (`test/operator-lowering.helpers.ts`, `test/helpers.test.ts`) are validated with `validateSqlContractFully(value)` — single argument, imported from `@prisma-next/sql-contract/validators` (`@prisma-next/sql-contract/validate` and the two-arg `validateContract` are gone). Fixtures carry `storage.namespaces.__unbound__ = { id, entries: { table: {...} } }` and `domain: { namespaces: { __unbound__: { models: {} } } }`. Mirror this shape for `contractV3` (Task 6). +3. **Lowered params are structured.** `adapter.lower(...)` emits `LoweredParam` entries (`{ kind: 'literal', value } | { kind: 'bind', name }`), not raw values. The v2 helpers export `literalParamValue(param)` for unwrapping — reuse it in v3 lowering tests that assert on `lowered.params`. +4. **PSL interpreter harness.** `interpretPslDocumentToSqlContract` requires the target ref to carry `defaultNamespaceId` (`'public'`) and requires `composedExtensionContracts: ReadonlyMap` (the composed cipherstash contract must be in the map or interpretation fails with `PSL_UNKNOWN_CONTRACT_SPACE`). Interpreted tables land under the `public` namespace at `storage.namespaces.public.entries.table`. The `psl-interpretation*.test.ts` harnesses (`interpret()`, `asStorage()`) are already 0.14-shaped on the branch — new v3 cases extend them, don't fork them. +5. **Contract-space emit / re-pin loop (Task 8's backbone).** The self-emit (`pnpm exec tsx migrations//migration.ts`) writes **only** `ops.json` + `migration.json`. It does not write `end-contract.json`, does not print a contract hash, and does not touch `migrations/refs/head.json`. The full maintainer loop, exercised four times on the upgrade branch, is: + 1. `pnpm exec prisma-next contract emit` (from `packages/prisma-next/`) → rewrites `src/contract.{json,d.ts}`; the new hash is `src/contract.json` → `storage.storageHash`. + 2. Copy `src/contract.json` → `migrations//end-contract.json` and `src/contract.d.ts` → `migrations//end-contract.d.ts`. + 3. Hand-edit the migration's `describe()` hash literal(s). + 4. Self-emit: `pnpm exec tsx migrations//migration.ts` → regenerates `ops.json` + `migration.json` (0.12+ manifests carry no `labels`/`hints` and no inlined `fromContract`/`toContract`). + 5. Hand-edit `migrations/refs/head.json` (`{ hash, invariants }`). + - Current head after the 0.14 upgrade: `hash: sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7`, `invariants: ["cipherstash:install-eql-bundle-v1"]`. The v2 baseline's `describe()` is `{ from: null, to: }`. +6. **Descriptor self-consistency requires the family canonicalization hooks.** `assertDescriptorSelfConsistency` must be passed `sqlContractCanonicalizationHooks.shouldPreserveEmpty` / `.sortStorage` (from `@prisma-next/sql-contract/canonicalization-hooks`) or the recomputed hash will not match the emitted one. `test/descriptor.test.ts` already does this on the branch; Task 8's "descriptor test passes with the second migration wired" step inherits it. +7. **Codec control hooks carry a namespace coordinate.** `FieldEventContext` has a required `namespaceId` (plus optional `priorTable`/`newTable`), and `planFieldEventOperations` walks `storage.namespaces.` — relevant only to v2 hooks (v3 registers none), but any new test that invokes a hook directly must pass `namespaceId`. +8. **Plan ops can lower lazily.** `OpFactoryCall.toOp()` may return a `Promise`; tests await `Promise.all(ops.map((c) => c.toOp()))` (see `test/cipherstash-codec.test.ts` on the branch). +9. **Middleware context requires `planExecutionId`.** `SqlMiddlewareContext` (via `RuntimeMiddlewareContext`) has a required `planExecutionId: string`; the v2 test `createCtx()` fixtures already stamp one — the extracted shared helper (Task 5) inherits it. +10. **`createRuntime` is removed** from `@prisma-next/sql-runtime` (construct `new PostgresRuntimeImpl(...)` from `@prisma-next/postgres/runtime`), the bare migration op factories are methods on the `Migration` base class (`this.createTable({...})` etc.) — but **`rawSql` survives as a free function** in 0.14 and the v2 baseline still uses it; Task 8's migration module is unaffected. ORM access is namespace-qualified (`db.orm.public.User`) — relevant to any live-suite example code. + ## File Structure New / modified files, by responsibility: @@ -69,7 +94,8 @@ packages/prisma-next/ eql-bundle-v3.ts (NEW) re-export readInstallSql / releaseManifest from @cipherstash/eql/sql migrations/20260601T0100_install_eql_v3_bundle/ migration.ts (NEW) rawSql install op, invariant cipherstash:install-eql-v3-bundle-v1 - migration.json / ops.json / end-contract.json (GENERATED by migration.ts) + migration.json / ops.json (GENERATED by the migration.ts self-emit) + end-contract.{json,d.ts} (COPIED from src/contract.{json,d.ts} after `prisma-next contract emit`) exports/control.ts (MODIFY) add the v3 baseline to the migrations array exports/runtime.ts (MODIFY) export v3 runtime surface exports/stack.ts (MODIFY) export v3 derivation/adapter @@ -856,6 +882,8 @@ Expected: PASS for the v3 + V2 cases. Any assertion expecting the unqualified `EncryptedString`/`EncryptedDouble`/`EncryptedBigInt`/`EncryptedDate`/`EncryptedBoolean`/`EncryptedJson` to lower to `cipherstash/*@1` + `eql_v2_encrypted` must be retargeted to the `*V2` name (or moved to a v3 expectation where the unqualified name is now a v3 domain). +> **0.14:** extend the existing `interpret()` / `asStorage()` harnesses in `psl-interpretation*.test.ts` — they already carry the 0.14 requirements (target ref with `defaultNamespaceId: 'public'`, the `composedExtensionContracts` map keyed by the cipherstash space id, and namespace-envelope reads via `entries.table`). Interpreted tables land under the `public` namespace (ground-truth note 4). Do not fork a pre-0.13 harness shape for the v3 cases. + Run: `pnpm --filter @cipherstash/prisma-next test -- psl-interpretation` Expected: the failing assertions name the exact lines; update each to `EncryptedStringV2` / `EncryptedDoubleV2` / etc. @@ -1675,6 +1703,8 @@ Keep the `cipherstash*` method names but lower v3 columns to the two-arg `eql_v3 Create `packages/prisma-next/test/v3/operator-lowering-v3.helpers.ts` mirroring the v2 `operator-lowering.helpers.ts`, but composing the v3 runtime (`createCipherstashV3RuntimeDescriptor({ sdk: emptySdk() })` from Task 7 — until Task 7 lands, compose a minimal adapter directly from `createV3CodecDescriptors` + `cipherstashV3QueryOperations`) and v3 codec ids in `columnAccessorV3` / `contractV3`. Export `callOperator`, `columnAccessorV3`, `contractV3`, `makeV3Adapter`, `selectWithWhere`, `TABLE`, `emptySdk`. +> **0.14:** the v2 helpers file being mirrored is already 0.14-shaped on the branch — `contractV3` must follow it: `validateSqlContractFully(value)` (single arg, from `@prisma-next/sql-contract/validators`), storage as `namespaces.__unbound__ = { id: '__unbound__', entries: { table: {...} } }`, a `domain: { namespaces: { __unbound__: { models: {} } } }` plane, and per-column `nativeType` set to the concrete `public.*` domain. If any v3 lowering assertion reads `lowered.params`, unwrap the structured `LoweredParam` entries with the exported `literalParamValue` helper (ground-truth notes 2–3). + Create `packages/prisma-next/test/v3/operator-lowering-v3.test.ts`: ```ts @@ -2018,7 +2048,7 @@ Derive `@cipherstash/stack/eql/v3` schemas from v3 contract columns (mapping `na - `function createCipherstashV3RuntimeDescriptor(opts: { sdk: CipherstashSdk }): SqlRuntimeExtensionDescriptor<'postgres'>` - `function assertV3SchemasAgree(derived: AnyV3Table, override: AnyV3Table): void` - `function cipherstashFromStackV3(opts: CipherstashFromStackV3Options): Promise` — the v3-only entry point (decision 1b). The v2 `cipherstashFromStack` is **unchanged** and not touched by this task. - - `interface V3ContractShape` (structural view of the contract storage): `{ storage?: { tables?: Record }> } }` (exported for reuse by tests/properties). + - `interface V3ContractShape` (structural view of the contract storage — the 0.14 namespace envelope, mirroring the v2 `ContractStorageView` in `src/stack/derive-schemas.ts`): `{ storage?: { namespaces?: Record }> } }> } }` (exported for reuse by tests/properties). - [ ] **Step 1: Write the failing derivation test** @@ -2028,8 +2058,14 @@ Create `packages/prisma-next/test/v3/derive-schemas-v3.test.ts`: import { describe, expect, it } from 'vitest' import { deriveStackSchemasV3 } from '../../src/v3/derive-schemas-v3' +// 0.14 contract shape: tables live under storage.namespaces..entries.table +// (Postgres default namespace is `public`). function contract(columns: Record) { - return { storage: { tables: { user: { columns } } } } + return { + storage: { + namespaces: { public: { entries: { table: { user: { columns } } } } }, + }, + } } describe('deriveStackSchemasV3', () => { @@ -2067,18 +2103,28 @@ import { type AnyEncryptedV3Column, type AnyV3Table, encryptedTable } from '@cip import { isCipherstashV3CodecId } from '../extension-metadata/constants-v3' import { V3_FACTORY_BY_NATIVE_TYPE } from './catalog' +interface V3TableView { + readonly columns?: Record< + string, + { codecId?: string; nativeType?: string; typeParams?: unknown } + > +} + +// 0.14 storage envelope: namespaces..entries.table. (see the +// "Prisma Next 0.14 ground truth" notes; mirrors the v2 ContractStorageView). export interface V3ContractShape { storage?: { - tables?: Record< - string, - { columns?: Record } - > + namespaces?: Record } }> } } export function deriveStackSchemasV3(contractJson: V3ContractShape): ReadonlyArray { - const tables = contractJson.storage?.tables - if (!tables) return [] + const namespaces = contractJson.storage?.namespaces + if (!namespaces) return [] + const tables: Record = {} + for (const namespace of Object.values(namespaces)) { + Object.assign(tables, namespace.entries?.table) + } const result: AnyV3Table[] = [] for (const [tableName, table] of Object.entries(tables)) { const columns: Record = {} @@ -2427,7 +2473,15 @@ const INSTALL_LABEL = `Install EQL v3 bundle (${releaseManifest.eqlVersion}: pub export default class M extends Migration { override describe() { - return { from: null, to: 'sha256:REPLACE_WITH_EMITTED_HASH' } + // The v3 baseline is the SECOND migration in the cipherstash contract + // space: `from` is the v2 baseline's `to` (the current head hash — see + // "Prisma Next 0.14 ground truth" note 5; after the 0.14 upgrade it is + // sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7). + // `to` is the storage hash after this migration (see Step 4). + return { + from: 'sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7', + to: 'sha256:REPLACE_WITH_EMITTED_HASH', + } } override get operations() { return [ @@ -2462,11 +2516,25 @@ MigrationCLI.run(import.meta.url, M) ``` > Match the exact `rawSql` field names + the `Migration` subclass shape against the v2 `migration.ts`. In particular confirm whether it uses `get operations()` or `override operations()` and the postcheck object keys; copy the v2 shape verbatim, changing only id/label/invariant/sql/postcheck. +> +> **0.14:** the bare structural op factories (`createTable`, `setNotNull`, …) became protected methods on the `Migration` base class in Prisma Next 0.14, but **`rawSql` survives as a free function** and the v2 baseline still imports it — the import above is correct as written (ground-truth note 10). + +- [ ] **Step 4: Emit the migration artifacts (0.14 loop — self-emit writes `ops.json` + `migration.json` ONLY)** + +> **0.14:** the self-emit does **not** write `end-contract.json`, does **not** print a contract hash, and does **not** touch `migrations/refs/head.json` — all three are manual (ground-truth note 5). The loop below was exercised four times on the upgrade branch. -- [ ] **Step 4: Emit the migration artifacts** +First decide the `to` hash — it depends on whether this migration changes the extension's declared contract storage: -Run: `node packages/prisma-next/migrations/20260601T0100_install_eql_v3_bundle/migration.ts` -Expected: writes `migration.json`, `ops.json`, `end-contract.json` and prints the `to` contract hash. Copy that hash into `describe().to` (replacing `REPLACE_WITH_EMITTED_HASH`) and re-run once so the artifacts are internally consistent. If the CLI manages `migrations/refs/head.json`, let it; otherwise append `cipherstash:install-eql-v3-bundle-v1` to its `invariants` and update `hash` per the CLI's printed guidance. +- **If `src/contract.prisma` is unchanged** (the v3 bundle only creates `public.*` domains + `eql_v3.*` functions, and no config table is added to the contract space), the storage hash does not move: `to` equals `from` (the current head hash) and the edge is **invariant-only**. VERIFY the migration graph (`reconstructGraph` / `readMigrationsDir` in `@prisma-next/migration-tools`) accepts a `from === to` edge before committing to this shape; if it rejects self-loop edges, declare the v3 configuration state in `src/contract.prisma` (mirroring how the v2 space declares `eql_v2_configuration`) so the hash genuinely moves. +- **If the contract space gains v3-visible storage** (e.g. a v3 config table), run the full re-pin loop and use the new hash. + +The loop (all from `packages/prisma-next/`): + +1. If the contract changed: `pnpm exec prisma-next contract emit` → rewrites `src/contract.{json,d.ts}`; read the new hash from `src/contract.json` → `storage.storageHash`. +2. Copy `src/contract.json` → `migrations/20260601T0100_install_eql_v3_bundle/end-contract.json` and `src/contract.d.ts` → `.../end-contract.d.ts` (for an invariant-only edge these equal the v2 baseline's end-contract — copy them anyway; every on-disk migration package carries its destination snapshot). +3. Replace `REPLACE_WITH_EMITTED_HASH` in `describe().to` with the hash from (1) (or the `from` hash for an invariant-only edge). +4. Self-emit: `pnpm exec tsx migrations/20260601T0100_install_eql_v3_bundle/migration.ts` → writes `ops.json` + `migration.json`. +5. Hand-edit `migrations/refs/head.json`: set `hash` to the v3 `to` hash and append `cipherstash:install-eql-v3-bundle-v1` to `invariants` (keeping `cipherstash:install-eql-bundle-v1`). - [ ] **Step 5: Wire the v3 baseline into control.ts** @@ -2513,7 +2581,7 @@ describe('v3 baseline migration', () => { - [ ] **Step 7: Run and commit** Run: `pnpm --filter @cipherstash/prisma-next test -- v3/migration-v3 descriptor && pnpm --filter @cipherstash/prisma-next build` -Expected: PASS (including the existing `descriptor.test.ts` self-consistency check with the second migration wired). +Expected: PASS (including the existing `descriptor.test.ts` self-consistency check with the second migration wired — note it already passes `sqlContractCanonicalizationHooks` to `assertDescriptorSelfConsistency`, required since 0.12, and its "head ref tracks the latest migration's `to`" assertion will now point at the v3 baseline; ground-truth notes 5–6). ```bash git add packages/prisma-next/package.json packages/prisma-next/src/migration/eql-bundle-v3.ts packages/prisma-next/migrations/20260601T0100_install_eql_v3_bundle packages/prisma-next/migrations/refs/head.json packages/prisma-next/src/exports/control.ts packages/prisma-next/test/v3/migration-v3.test.ts ../../pnpm-lock.yaml @@ -2622,7 +2690,15 @@ describe('property: derivation round-trip', () => { fc.assert( fc.property(fc.constantFrom(...V3_DOMAIN_META_BY_CODEC_ID.entries()), ([codecId, meta]) => { const [table] = deriveStackSchemasV3({ - storage: { tables: { user: { columns: { c: { codecId, nativeType: meta.nativeType } } } } }, + storage: { + namespaces: { + public: { + entries: { + table: { user: { columns: { c: { codecId, nativeType: meta.nativeType } } } }, + }, + }, + }, + }, }) const col = Object.values(table!.columnBuilders)[0]! expect((col as { getEqlType(): string }).getEqlType()).toBe(meta.nativeType) @@ -2728,7 +2804,7 @@ export async function installEqlV3IfNeeded(sql: postgres.Sql): Promise { - [ ] **Step 3: Write the seven live suites (self-skipping)** -Create the following under `packages/prisma-next/test/live/`, each guarded by `describeLivePg`, installing v3 in `beforeAll`, and driving real INSERT/SELECT/decrypt against the `public.*` domains + `eql_v3.*` operators via `cipherstashFromStack` + a real `EncryptionV3` client. Shared skeleton: +Create the following under `packages/prisma-next/test/live/`, each guarded by `describeLivePg`, installing v3 in `beforeAll`, and driving real INSERT/SELECT/decrypt against the `public.*` domains + `eql_v3.*` operators via `cipherstashFromStackV3` (the v3-only entry point — decision 1b) + a real `EncryptionV3` client. Shared skeleton: ```ts import { afterAll, beforeAll, expect, it } from 'vitest' @@ -2747,7 +2823,7 @@ afterAll(async () => { if (sql) await sql.end() }) 4. `bigint-live-pg.test.ts` — `public.bigint` / `bigint_eq` / `bigint_ord` encrypt → INSERT → query (eq / order-range) → decrypt to a JS `bigint`; assert `typeof decrypted === 'bigint'`, lossless value, and ORE ordering over real domains. 5. `migration-apply-live-pg.test.ts` — apply the v3 bundle (`installEqlV3IfNeeded`); assert `to_regtype('public.text_search')`/`public.integer_ord` are non-null, the `eql_v3` schema exists, the invariant `cipherstash:install-eql-v3-bundle-v1` is recorded (via the migration ops fixture or the runner's marker table), and no `add_search_config` was executed. 6. `bulk-encrypt-live-pg.test.ts` — drive the v3 middleware against a **real** `EncryptionV3(...)` client (not a fake) → INSERT; assert the stored cell parses as JSON and decrypts to the original, proving routing-key stamping + JSONB payloads end-to-end. -7. `mixed-v2-v3-live-pg.test.ts` — one table with a legacy `*V2` column and v3 columns; insert/query/decrypt both, proving mixed-mode derivation + two-client routing against a real DB. +7. `side-by-side-clients-live-pg.test.ts` — a v2 client (`cipherstashFromStack`, v2-only contract/table) and a v3 client (`cipherstashFromStackV3`, v3-only contract/table) running in the same process against the same database; insert/query/decrypt through each, proving the two entry points coexist without sharing a registry, descriptor, or dispatch. (Mixed v2+v3 columns in ONE client are unsupported per decision 1b — the spec explicitly excludes a mixed-client live test; this replaces the earlier `mixed-v2-v3-live-pg.test.ts` item, which contradicted it.) - [ ] **Step 4: Verify self-skip locally, then commit** diff --git a/docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md b/docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md index bb294fea1..63d1f5f05 100644 --- a/docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md +++ b/docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md @@ -4,6 +4,7 @@ Status: proposed Date: 2026-07-08 Revised: 2026-07-08 (design A — capabilities encoded in the type system; per-domain constructors; boolean-option resolver removed) Revised: 2026-07-09 (audit vs `feat/eql-v3-supabase-adapter`: domains are `public.*` / operator functions stay `eql_v3.*`; BigInt is now a first-class v3 family; boolean domain is `public.boolean`; catalog derived from `DOMAIN_REGISTRY`) +Revised: 2026-07-09c (Prisma Next 0.14 alignment: `packages/prisma-next` now pins `@prisma-next/*` at 0.14.0 — contract storage/domain shapes, the emit/re-pin loop, and test-harness requirements changed between 0.8 and 0.14; the operational details live in the companion plan's "Prisma Next 0.14 ground truth" section. Source line references in this spec are 0.8-era anchors and may have drifted by a few lines. The plan's live-suite list also replaces the mixed-client test with side-by-side v2/v3 clients per decision 1b.) Revised: 2026-07-09b (review remediation: **concrete per-domain types are the spine** — see [Type Discipline]; v2/v3 are **separate entry points**, no in-one-client mixing (bare names are v3, `*V2` is legacy — a breaking major, no transition guard); encrypted ORDER BY (`asc`/`desc` → `eql_v3.ord_term`) in scope; `between` parenthesisation + `bigintSafeReplacer` are correctness invariants; framework-neutral primitives (codec, gate index-sets, fn-schema/op-map/paren-rule) lifted to `eql/v3/`; tests require a plaintext oracle, real properties, full-string operator pins, and mandatory `*.test-d.ts`) Target package: `@cipherstash/prisma-next` Reference branches: From 35a49b02c68ee4f83fca3bdb37b5acd56385e29f Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:46:00 -0600 Subject: [PATCH 13/15] fix(prisma-next): read tables from the 0.13+ entries envelope in deriveStackSchemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 0.12→0.13 upgrade step moved storage namespaces to the entries. envelope but deriveStackSchemas kept reading namespace.tables — its unit fixtures were updated in lockstep, so the suite stayed green while derivation returned [] against a real 0.14-emitted contract.json (cipherstashFromStack would throw 'no cipherstash columns found'). Caught by typechecking the example app against its re-emitted contract. Fixtures now mirror the real emitted shape. Co-Authored-By: Claude Fable 5 --- .../prisma-next/src/stack/derive-schemas.ts | 6 ++-- .../prisma-next/test/derive-schemas.test.ts | 32 ++++++++++--------- .../test/from-stack-divergence.test.ts | 26 ++++++++------- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/packages/prisma-next/src/stack/derive-schemas.ts b/packages/prisma-next/src/stack/derive-schemas.ts index 95084fd63..785a05c40 100644 --- a/packages/prisma-next/src/stack/derive-schemas.ts +++ b/packages/prisma-next/src/stack/derive-schemas.ts @@ -46,7 +46,9 @@ export interface ContractStorageView { Record< string, { - readonly tables?: Readonly> + readonly entries?: { + readonly table?: Readonly> + } } > > @@ -98,7 +100,7 @@ export function deriveStackSchemas( if (!namespaces) return [] const tables: Record = {} for (const namespace of Object.values(namespaces)) { - Object.assign(tables, namespace.tables) + Object.assign(tables, namespace.entries?.table) } const result: EncryptedTable[] = [] diff --git a/packages/prisma-next/test/derive-schemas.test.ts b/packages/prisma-next/test/derive-schemas.test.ts index b1a7e5c4e..4f3dd6409 100644 --- a/packages/prisma-next/test/derive-schemas.test.ts +++ b/packages/prisma-next/test/derive-schemas.test.ts @@ -30,20 +30,22 @@ function makeContract( storage: { namespaces: { __unbound__: { - tables: Object.fromEntries( - Object.entries(tables).map(([name, cols]) => [ - name, - { - columns: cols as Record< - string, - { - codecId: string - typeParams?: Record | null - } - >, - }, - ]), - ), + entries: { + table: Object.fromEntries( + Object.entries(tables).map(([name, cols]) => [ + name, + { + columns: cols as Record< + string, + { + codecId: string + typeParams?: Record | null + } + >, + }, + ]), + ), + }, }, }, }, @@ -57,7 +59,7 @@ describe('deriveStackSchemas', () => { expect(deriveStackSchemas({ storage: { namespaces: {} } })).toEqual([]) expect( deriveStackSchemas({ - storage: { namespaces: { __unbound__: { tables: {} } } }, + storage: { namespaces: { __unbound__: { entries: { table: {} } } } }, }), ).toEqual([]) }) diff --git a/packages/prisma-next/test/from-stack-divergence.test.ts b/packages/prisma-next/test/from-stack-divergence.test.ts index 223de8597..42c34316c 100644 --- a/packages/prisma-next/test/from-stack-divergence.test.ts +++ b/packages/prisma-next/test/from-stack-divergence.test.ts @@ -26,16 +26,18 @@ function makeContract() { storage: { namespaces: { __unbound__: { - tables: { - users: { - columns: { - email: { - codecId: CIPHERSTASH_STRING_CODEC_ID, - typeParams: { equality: true, freeTextSearch: true }, - }, - verified: { - codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, - typeParams: { equality: true }, + entries: { + table: { + users: { + columns: { + email: { + codecId: CIPHERSTASH_STRING_CODEC_ID, + typeParams: { equality: true, freeTextSearch: true }, + }, + verified: { + codecId: CIPHERSTASH_BOOLEAN_CODEC_ID, + typeParams: { equality: true }, + }, }, }, }, @@ -110,7 +112,9 @@ describe('cipherstashFromStack — divergence check', () => { it('throws when the contract has no cipherstash columns and no override is supplied', async () => { const emptyContract = { storage: { - namespaces: { __unbound__: { tables: { users: { columns: {} } } } }, + namespaces: { + __unbound__: { entries: { table: { users: { columns: {} } } } }, + }, }, } await expect( From 90e72922f209049a0e8f498fd04d4efa5269859f Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:46:18 -0600 Subject: [PATCH 14/15] chore(prisma-example): upgrade the example app to Prisma Next 0.14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - deps: every @prisma-next/* pinned at 0.14.0 - prisma-next.config.ts: prismaContract takes the target PACK ref (@prisma-next/target-postgres/pack, carrying defaultNamespaceId) instead of the control descriptor - contract re-emitted in the 0.14 shape (public namespace, entries envelope, domain plane); migrations regenerated from scratch — fresh app initial migration + the cipherstash extension space re-materialised from the upgraded workspace package (head sha256:1e86a016…) - ORM/builder access namespace-qualified: db.orm.public.User, db.sql.public.users (src/index.ts + every e2e suite) - the CLI renamed 'migration apply' to 'migrate': package.json script, e2e global-setup, and READMEs updated (--yes for non-interactive runs) - e2e walkthrough test (e2e/tests/prisma-example-readme.e2e.test.ts): snapshot/restore examples/prisma/.env instead of deleting it in teardown — the old teardown destroyed a developer's real credentials file Full example e2e suite green against the docker harness: 36 passed, 1 skipped (known jsonbPathExists limitation). Co-Authored-By: Claude Fable 5 --- e2e/tests/prisma-example-readme.e2e.test.ts | 12 +- examples/prisma/README.md | 2 +- .../20260513T1735_initial/end-contract.d.ts | 533 ------------ .../app/20260513T1735_initial/migration.json | 475 ----------- .../app/20260513T1735_initial/migration.ts | 130 --- .../20260709T1034_initial/end-contract.d.ts | 513 ++++++++++++ .../end-contract.json | 392 +++++---- .../app/20260709T1034_initial/migration.json | 19 + .../app/20260709T1034_initial/migration.ts | 114 +++ .../ops.json | 15 +- .../contract.json | 79 -- .../migration.json | 119 +-- .../20260601T0000_install_eql_bundle/ops.json | 2 +- .../migrations/cipherstash/contract.d.ts | 4 +- .../migrations/cipherstash/contract.json | 80 +- .../migrations/cipherstash/refs/head.json | 5 +- examples/prisma/package.json | 26 +- examples/prisma/prisma-next.config.ts | 5 +- examples/prisma/src/index.ts | 18 +- examples/prisma/src/prisma/contract.d.ts | 788 +++++++++--------- examples/prisma/src/prisma/contract.json | 392 +++++---- examples/prisma/test/e2e/README.md | 2 +- examples/prisma/test/e2e/bigint.e2e.test.ts | 14 +- examples/prisma/test/e2e/bool.e2e.test.ts | 12 +- examples/prisma/test/e2e/date.e2e.test.ts | 12 +- examples/prisma/test/e2e/global-setup.ts | 4 +- examples/prisma/test/e2e/json.e2e.test.ts | 12 +- examples/prisma/test/e2e/mixed.e2e.test.ts | 8 +- examples/prisma/test/e2e/num.e2e.test.ts | 18 +- .../prisma/test/e2e/str-range.e2e.test.ts | 12 +- 30 files changed, 1565 insertions(+), 2252 deletions(-) delete mode 100644 examples/prisma/migrations/app/20260513T1735_initial/end-contract.d.ts delete mode 100644 examples/prisma/migrations/app/20260513T1735_initial/migration.json delete mode 100755 examples/prisma/migrations/app/20260513T1735_initial/migration.ts create mode 100644 examples/prisma/migrations/app/20260709T1034_initial/end-contract.d.ts rename examples/prisma/migrations/app/{20260513T1735_initial => 20260709T1034_initial}/end-contract.json (53%) create mode 100644 examples/prisma/migrations/app/20260709T1034_initial/migration.json create mode 100755 examples/prisma/migrations/app/20260709T1034_initial/migration.ts rename examples/prisma/migrations/app/{20260513T1735_initial => 20260709T1034_initial}/ops.json (95%) delete mode 100644 examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.json diff --git a/e2e/tests/prisma-example-readme.e2e.test.ts b/e2e/tests/prisma-example-readme.e2e.test.ts index f43966d55..76ea909f6 100644 --- a/e2e/tests/prisma-example-readme.e2e.test.ts +++ b/e2e/tests/prisma-example-readme.e2e.test.ts @@ -62,10 +62,15 @@ function runStep(commandLine: string, timeoutMs: number): StepResult { } } +// `.env` is included so a developer's real credentials file survives the +// walkthrough: the README's `cp .env.example .env` step overwrites it, and +// without the snapshot the teardown would delete it outright. The snapshot +// captures it before the run and the restore puts the original back. const TRANSIENT_PATHS = [ 'migrations/app', 'src/prisma/contract.json', 'src/prisma/contract.d.ts', + '.env', ] as const async function snapshotTransientOutputs(): Promise { @@ -170,10 +175,11 @@ describe.skipIf(!authConfigured)( afterAll(async () => { // Teardown the bundled Postgres container regardless of outcome. runStep('docker compose down -v', 60_000) - // Restore the transient outputs from snapshot so the working tree is clean. + // Restore the transient outputs from snapshot so the working tree is + // clean. `.env` is part of the snapshot: the walkthrough's + // `cp .env.example .env` overwrote it, and the restore brings back the + // developer's original (or removes the copy when none existed before). await restoreTransientOutputs(snapDir) - // Remove the .env we copied in the walkthrough (not tracked anyway). - rmSync(join(EXAMPLE_DIR, '.env'), { force: true }) }, 120_000) // Per-step exit-zero assertion, registered once per non-skipped README line. diff --git a/examples/prisma/README.md b/examples/prisma/README.md index 4dfdffa77..67eafbfa1 100644 --- a/examples/prisma/README.md +++ b/examples/prisma/README.md @@ -33,7 +33,7 @@ docker compose up -d pnpm install pnpm emit # PSL → contract.{json,d.ts} pnpm migration:plan --name initial -pnpm migration:apply # installs EQL bundle + your app schema in one sweep +pnpm migration:apply # installs EQL bundle + your app schema in one sweep (runs `prisma-next migrate`) pnpm start # runs the demo ``` diff --git a/examples/prisma/migrations/app/20260513T1735_initial/end-contract.d.ts b/examples/prisma/migrations/app/20260513T1735_initial/end-contract.d.ts deleted file mode 100644 index 9bd67b083..000000000 --- a/examples/prisma/migrations/app/20260513T1735_initial/end-contract.d.ts +++ /dev/null @@ -1,533 +0,0 @@ -// ⚠️ GENERATED FILE - DO NOT EDIT -// This file is automatically generated by 'prisma-next contract emit'. -// To regenerate, run: prisma-next contract emit - -import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types' -import type { - Contract as ContractType, - ExecutionHashBase, - ProfileHashBase, - StorageHashBase, -} from '@prisma-next/contract/types' -import type { CodecTypes as CipherstashTypes } from '@prisma-next/extension-cipherstash/codec-types' -import type { QueryOperationTypes as CipherstashQueryOperationTypes } from '@prisma-next/extension-cipherstash/operation-types' -import type { - EncryptedBigInt, - EncryptedBoolean, - EncryptedDate, - EncryptedDouble, - EncryptedJson, - EncryptedString, -} from '@prisma-next/extension-cipherstash/runtime' -import type { - ContractWithTypeMaps, - TypeMaps as TypeMapsType, -} from '@prisma-next/sql-contract/types' -import type { - Bit, - Char, - Interval, - JsonValue, - Numeric, - CodecTypes as PgTypes, - Time, - Timestamp, - Timestamptz, - Timetz, - VarBit, - Varchar, -} from '@prisma-next/target-postgres/codec-types' - -export type StorageHash = - StorageHashBase<'sha256:7475191ce0d78258ce5586265bcdfd12202f5daf90690b902890e58eb7508373'> -export type ExecutionHash = ExecutionHashBase -export type ProfileHash = - ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'> - -export type CodecTypes = PgTypes & CipherstashTypes -export type LaneCodecTypes = CodecTypes -export type QueryOperationTypes = PgAdapterQueryOps & - CipherstashQueryOperationTypes -type DefaultLiteralValue< - CodecId extends string, - _Encoded, -> = CodecId extends keyof CodecTypes ? CodecTypes[CodecId]['output'] : _Encoded - -export type FieldOutputTypes = { - readonly User: { - readonly id: CodecTypes['pg/text@1']['output'] - readonly email: CodecTypes['cipherstash/string@1']['output'] - readonly salary: CodecTypes['cipherstash/double@1']['output'] - readonly accountId: CodecTypes['cipherstash/bigint@1']['output'] - readonly birthday: CodecTypes['cipherstash/date@1']['output'] - readonly emailVerified: CodecTypes['cipherstash/boolean@1']['output'] - readonly preferences: CodecTypes['cipherstash/json@1']['output'] - } -} -export type FieldInputTypes = { - readonly User: { - readonly id: CodecTypes['pg/text@1']['input'] - readonly email: CodecTypes['cipherstash/string@1']['input'] - readonly salary: CodecTypes['cipherstash/double@1']['input'] - readonly accountId: CodecTypes['cipherstash/bigint@1']['input'] - readonly birthday: CodecTypes['cipherstash/date@1']['input'] - readonly emailVerified: CodecTypes['cipherstash/boolean@1']['input'] - readonly preferences: CodecTypes['cipherstash/json@1']['input'] - } -} -export type TypeMaps = TypeMapsType< - CodecTypes, - QueryOperationTypes, - FieldOutputTypes, - FieldInputTypes -> - -type ContractBase = ContractType< - { - readonly tables: { - readonly users: { - columns: { - readonly id: { - readonly nativeType: 'text' - readonly codecId: 'pg/text@1' - readonly nullable: false - } - readonly email: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/string@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly freeTextSearch: true - readonly orderAndRange: true - } - } - readonly salary: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/double@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - readonly accountid: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/bigint@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - readonly birthday: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/date@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - readonly emailverified: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/boolean@1' - readonly nullable: false - readonly typeParams: { readonly equality: true } - } - readonly preferences: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/json@1' - readonly nullable: false - readonly typeParams: { readonly searchableJson: true } - } - } - primaryKey: { readonly columns: readonly ['id'] } - uniques: readonly [] - indexes: readonly [] - foreignKeys: readonly [] - } - } - readonly types: Record - readonly storageHash: StorageHash - }, - { - readonly User: { - readonly fields: { - readonly id: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/text@1' - } - } - readonly email: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/string@1' - readonly typeParams: { - readonly equality: true - readonly freeTextSearch: true - readonly orderAndRange: true - } - } - } - readonly salary: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/double@1' - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - } - readonly accountId: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/bigint@1' - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - } - readonly birthday: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/date@1' - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - } - readonly emailVerified: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/boolean@1' - readonly typeParams: { readonly equality: true } - } - } - readonly preferences: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/json@1' - readonly typeParams: { readonly searchableJson: true } - } - } - } - readonly relations: Record - readonly storage: { - readonly table: 'users' - readonly fields: { - readonly id: { readonly column: 'id' } - readonly email: { readonly column: 'email' } - readonly salary: { readonly column: 'salary' } - readonly accountId: { readonly column: 'accountid' } - readonly birthday: { readonly column: 'birthday' } - readonly emailVerified: { readonly column: 'emailverified' } - readonly preferences: { readonly column: 'preferences' } - } - } - } - } -> & { - readonly target: 'postgres' - readonly targetFamily: 'sql' - readonly roots: { readonly users: 'User' } - readonly capabilities: { - readonly postgres: { - readonly jsonAgg: true - readonly lateral: true - readonly limit: true - readonly orderBy: true - readonly returning: true - } - readonly sql: { - readonly defaultInInsert: true - readonly enums: true - readonly returning: true - } - } - readonly extensionPacks: { - readonly cipherstash: { - readonly familyId: 'sql' - readonly id: 'cipherstash' - readonly kind: 'extension' - readonly targetId: 'postgres' - readonly types: { - readonly codecTypes: { - readonly codecInstances: readonly [ - { - readonly descriptor: { - readonly codecId: 'cipherstash/string@1' - readonly factory: unknown - readonly isParameterized: false - readonly meta: { - readonly db: { - readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } - readonly paramsSchema: { - readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly [ - 'cipherstash:equality', - 'cipherstash:free-text-search', - 'cipherstash:order-and-range', - ] - } - }, - { - readonly descriptor: { - readonly codecId: 'cipherstash/double@1' - readonly factory: unknown - readonly isParameterized: false - readonly meta: { - readonly db: { - readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } - readonly paramsSchema: { - readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly [ - 'cipherstash:equality', - 'cipherstash:order-and-range', - ] - } - }, - { - readonly descriptor: { - readonly codecId: 'cipherstash/bigint@1' - readonly factory: unknown - readonly isParameterized: false - readonly meta: { - readonly db: { - readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } - readonly paramsSchema: { - readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly [ - 'cipherstash:equality', - 'cipherstash:order-and-range', - ] - } - }, - { - readonly descriptor: { - readonly codecId: 'cipherstash/date@1' - readonly factory: unknown - readonly isParameterized: false - readonly meta: { - readonly db: { - readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } - readonly paramsSchema: { - readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly [ - 'cipherstash:equality', - 'cipherstash:order-and-range', - ] - } - }, - { - readonly descriptor: { - readonly codecId: 'cipherstash/boolean@1' - readonly factory: unknown - readonly isParameterized: false - readonly meta: { - readonly db: { - readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } - readonly paramsSchema: { - readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly ['cipherstash:equality'] - } - }, - { - readonly descriptor: { - readonly codecId: 'cipherstash/json@1' - readonly factory: unknown - readonly isParameterized: false - readonly meta: { - readonly db: { - readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } - readonly paramsSchema: { - readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly ['cipherstash:searchable-json'] - } - }, - ] - readonly import: { - readonly alias: 'CipherstashTypes' - readonly named: 'CodecTypes' - readonly package: '@prisma-next/extension-cipherstash/codec-types' - } - readonly typeImports: readonly [ - { - readonly alias: 'EncryptedString' - readonly named: 'EncryptedString' - readonly package: '@prisma-next/extension-cipherstash/runtime' - }, - { - readonly alias: 'EncryptedDouble' - readonly named: 'EncryptedDouble' - readonly package: '@prisma-next/extension-cipherstash/runtime' - }, - { - readonly alias: 'EncryptedBigInt' - readonly named: 'EncryptedBigInt' - readonly package: '@prisma-next/extension-cipherstash/runtime' - }, - { - readonly alias: 'EncryptedDate' - readonly named: 'EncryptedDate' - readonly package: '@prisma-next/extension-cipherstash/runtime' - }, - { - readonly alias: 'EncryptedBoolean' - readonly named: 'EncryptedBoolean' - readonly package: '@prisma-next/extension-cipherstash/runtime' - }, - { - readonly alias: 'EncryptedJson' - readonly named: 'EncryptedJson' - readonly package: '@prisma-next/extension-cipherstash/runtime' - }, - ] - } - readonly queryOperationTypes: { - readonly import: { - readonly alias: 'CipherstashQueryOperationTypes' - readonly named: 'QueryOperationTypes' - readonly package: '@prisma-next/extension-cipherstash/operation-types' - } - } - readonly storage: readonly [ - { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/string@1' - }, - { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/double@1' - }, - { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/bigint@1' - }, - { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/date@1' - }, - { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/boolean@1' - }, - { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/json@1' - }, - ] - } - readonly version: '0.0.1' - } - } - readonly meta: {} - - readonly profileHash: ProfileHash -} - -export type Contract = ContractWithTypeMaps - -export type Tables = Contract['storage']['tables'] -export type Models = Contract['models'] diff --git a/examples/prisma/migrations/app/20260513T1735_initial/migration.json b/examples/prisma/migrations/app/20260513T1735_initial/migration.json deleted file mode 100644 index ac2f39c6e..000000000 --- a/examples/prisma/migrations/app/20260513T1735_initial/migration.json +++ /dev/null @@ -1,475 +0,0 @@ -{ - "from": null, - "to": "sha256:7475191ce0d78258ce5586265bcdfd12202f5daf90690b902890e58eb7508373", - "fromContract": null, - "toContract": { - "schemaVersion": "1", - "targetFamily": "sql", - "target": "postgres", - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", - "roots": { - "users": "User" - }, - "models": { - "User": { - "fields": { - "accountId": { - "nullable": false, - "type": { - "codecId": "cipherstash/bigint@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - }, - "birthday": { - "nullable": false, - "type": { - "codecId": "cipherstash/date@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - }, - "email": { - "nullable": false, - "type": { - "codecId": "cipherstash/string@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "freeTextSearch": true, - "orderAndRange": true - } - } - }, - "emailVerified": { - "nullable": false, - "type": { - "codecId": "cipherstash/boolean@1", - "kind": "scalar", - "typeParams": { - "equality": true - } - } - }, - "id": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "preferences": { - "nullable": false, - "type": { - "codecId": "cipherstash/json@1", - "kind": "scalar", - "typeParams": { - "searchableJson": true - } - } - }, - "salary": { - "nullable": false, - "type": { - "codecId": "cipherstash/double@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - } - }, - "relations": {}, - "storage": { - "fields": { - "accountId": { - "column": "accountid" - }, - "birthday": { - "column": "birthday" - }, - "email": { - "column": "email" - }, - "emailVerified": { - "column": "emailverified" - }, - "id": { - "column": "id" - }, - "preferences": { - "column": "preferences" - }, - "salary": { - "column": "salary" - } - }, - "table": "users" - } - } - }, - "storage": { - "storageHash": "sha256:7475191ce0d78258ce5586265bcdfd12202f5daf90690b902890e58eb7508373", - "tables": { - "users": { - "columns": { - "accountid": { - "codecId": "cipherstash/bigint@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true - } - }, - "birthday": { - "codecId": "cipherstash/date@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true - } - }, - "email": { - "codecId": "cipherstash/string@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "freeTextSearch": true, - "orderAndRange": true - } - }, - "emailverified": { - "codecId": "cipherstash/boolean@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true - } - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "preferences": { - "codecId": "cipherstash/json@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "searchableJson": true - } - }, - "salary": { - "codecId": "cipherstash/double@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": ["id"] - }, - "uniques": [] - } - } - }, - "capabilities": { - "postgres": { - "jsonAgg": true, - "lateral": true, - "limit": true, - "orderBy": true, - "returning": true - }, - "sql": { - "defaultInInsert": true, - "enums": true, - "returning": true - } - }, - "extensionPacks": { - "cipherstash": { - "familyId": "sql", - "id": "cipherstash", - "kind": "extension", - "targetId": "postgres", - "types": { - "codecTypes": { - "codecInstances": [ - { - "descriptor": { - "codecId": "cipherstash/string@1", - "meta": { - "db": { - "sql": { - "postgres": { - "nativeType": "eql_v2_encrypted" - } - } - } - }, - "paramsSchema": { - "~standard": { - "vendor": "cipherstash", - "version": 1 - } - }, - "targetTypes": ["eql_v2_encrypted"], - "traits": [ - "cipherstash:equality", - "cipherstash:free-text-search", - "cipherstash:order-and-range" - ] - } - }, - { - "descriptor": { - "codecId": "cipherstash/double@1", - "meta": { - "db": { - "sql": { - "postgres": { - "nativeType": "eql_v2_encrypted" - } - } - } - }, - "paramsSchema": { - "~standard": { - "vendor": "cipherstash", - "version": 1 - } - }, - "targetTypes": ["eql_v2_encrypted"], - "traits": [ - "cipherstash:equality", - "cipherstash:order-and-range" - ] - } - }, - { - "descriptor": { - "codecId": "cipherstash/bigint@1", - "meta": { - "db": { - "sql": { - "postgres": { - "nativeType": "eql_v2_encrypted" - } - } - } - }, - "paramsSchema": { - "~standard": { - "vendor": "cipherstash", - "version": 1 - } - }, - "targetTypes": ["eql_v2_encrypted"], - "traits": [ - "cipherstash:equality", - "cipherstash:order-and-range" - ] - } - }, - { - "descriptor": { - "codecId": "cipherstash/date@1", - "meta": { - "db": { - "sql": { - "postgres": { - "nativeType": "eql_v2_encrypted" - } - } - } - }, - "paramsSchema": { - "~standard": { - "vendor": "cipherstash", - "version": 1 - } - }, - "targetTypes": ["eql_v2_encrypted"], - "traits": [ - "cipherstash:equality", - "cipherstash:order-and-range" - ] - } - }, - { - "descriptor": { - "codecId": "cipherstash/boolean@1", - "meta": { - "db": { - "sql": { - "postgres": { - "nativeType": "eql_v2_encrypted" - } - } - } - }, - "paramsSchema": { - "~standard": { - "vendor": "cipherstash", - "version": 1 - } - }, - "targetTypes": ["eql_v2_encrypted"], - "traits": ["cipherstash:equality"] - } - }, - { - "descriptor": { - "codecId": "cipherstash/json@1", - "meta": { - "db": { - "sql": { - "postgres": { - "nativeType": "eql_v2_encrypted" - } - } - } - }, - "paramsSchema": { - "~standard": { - "vendor": "cipherstash", - "version": 1 - } - }, - "targetTypes": ["eql_v2_encrypted"], - "traits": ["cipherstash:searchable-json"] - } - } - ], - "import": { - "alias": "CipherstashTypes", - "named": "CodecTypes", - "package": "@prisma-next/extension-cipherstash/codec-types" - }, - "typeImports": [ - { - "alias": "EncryptedString", - "named": "EncryptedString", - "package": "@prisma-next/extension-cipherstash/runtime" - }, - { - "alias": "EncryptedDouble", - "named": "EncryptedDouble", - "package": "@prisma-next/extension-cipherstash/runtime" - }, - { - "alias": "EncryptedBigInt", - "named": "EncryptedBigInt", - "package": "@prisma-next/extension-cipherstash/runtime" - }, - { - "alias": "EncryptedDate", - "named": "EncryptedDate", - "package": "@prisma-next/extension-cipherstash/runtime" - }, - { - "alias": "EncryptedBoolean", - "named": "EncryptedBoolean", - "package": "@prisma-next/extension-cipherstash/runtime" - }, - { - "alias": "EncryptedJson", - "named": "EncryptedJson", - "package": "@prisma-next/extension-cipherstash/runtime" - } - ] - }, - "queryOperationTypes": { - "import": { - "alias": "CipherstashQueryOperationTypes", - "named": "QueryOperationTypes", - "package": "@prisma-next/extension-cipherstash/operation-types" - } - }, - "storage": [ - { - "familyId": "sql", - "nativeType": "eql_v2_encrypted", - "targetId": "postgres", - "typeId": "cipherstash/string@1" - }, - { - "familyId": "sql", - "nativeType": "eql_v2_encrypted", - "targetId": "postgres", - "typeId": "cipherstash/double@1" - }, - { - "familyId": "sql", - "nativeType": "eql_v2_encrypted", - "targetId": "postgres", - "typeId": "cipherstash/bigint@1" - }, - { - "familyId": "sql", - "nativeType": "eql_v2_encrypted", - "targetId": "postgres", - "typeId": "cipherstash/date@1" - }, - { - "familyId": "sql", - "nativeType": "eql_v2_encrypted", - "targetId": "postgres", - "typeId": "cipherstash/boolean@1" - }, - { - "familyId": "sql", - "nativeType": "eql_v2_encrypted", - "targetId": "postgres", - "typeId": "cipherstash/json@1" - } - ] - }, - "version": "0.0.1" - } - }, - "meta": {}, - "_generated": { - "warning": "⚠️ GENERATED FILE - DO NOT EDIT", - "message": "This file is automatically generated by \"prisma-next contract emit\".", - "regenerate": "To regenerate, run: prisma-next contract emit" - } - }, - "hints": { - "used": [], - "applied": [], - "plannerVersion": "2.0.0" - }, - "labels": [], - "createdAt": "2026-05-13T17:35:47.440Z", - "providedInvariants": [ - "cipherstash-codec:users.accountid:add-search-config:ore@v1", - "cipherstash-codec:users.accountid:add-search-config:unique@v1", - "cipherstash-codec:users.birthday:add-search-config:ore@v1", - "cipherstash-codec:users.birthday:add-search-config:unique@v1", - "cipherstash-codec:users.email:add-search-config:match@v1", - "cipherstash-codec:users.email:add-search-config:ore@v1", - "cipherstash-codec:users.email:add-search-config:unique@v1", - "cipherstash-codec:users.emailverified:add-search-config:unique@v1", - "cipherstash-codec:users.preferences:add-search-config:ste_vec@v1", - "cipherstash-codec:users.salary:add-search-config:ore@v1", - "cipherstash-codec:users.salary:add-search-config:unique@v1" - ], - "migrationHash": "sha256:9ea9b8e790665ce11265339be522ed4baba54d446036386c80fa589196d5f645" -} diff --git a/examples/prisma/migrations/app/20260513T1735_initial/migration.ts b/examples/prisma/migrations/app/20260513T1735_initial/migration.ts deleted file mode 100755 index 7d9c9baff..000000000 --- a/examples/prisma/migrations/app/20260513T1735_initial/migration.ts +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env -S node -import { cipherstashAddSearchConfig } from '@prisma-next/extension-cipherstash/migration' -import { - createTable, - Migration, - MigrationCLI, -} from '@prisma-next/target-postgres/migration' - -export default class M extends Migration { - override describe() { - return { - from: null, - to: 'sha256:7475191ce0d78258ce5586265bcdfd12202f5daf90690b902890e58eb7508373', - } - } - - override get operations() { - return [ - createTable( - 'public', - 'users', - [ - { - name: 'accountid', - typeSql: 'eql_v2_encrypted', - defaultSql: '', - nullable: false, - }, - { - name: 'birthday', - typeSql: 'eql_v2_encrypted', - defaultSql: '', - nullable: false, - }, - { - name: 'email', - typeSql: 'eql_v2_encrypted', - defaultSql: '', - nullable: false, - }, - { - name: 'emailverified', - typeSql: 'eql_v2_encrypted', - defaultSql: '', - nullable: false, - }, - { name: 'id', typeSql: 'text', defaultSql: '', nullable: false }, - { - name: 'preferences', - typeSql: 'eql_v2_encrypted', - defaultSql: '', - nullable: false, - }, - { - name: 'salary', - typeSql: 'eql_v2_encrypted', - defaultSql: '', - nullable: false, - }, - ], - { columns: ['id'] }, - ), - cipherstashAddSearchConfig({ - table: 'users', - column: 'accountid', - index: 'unique', - castAs: 'big_int', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'accountid', - index: 'ore', - castAs: 'big_int', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'birthday', - index: 'unique', - castAs: 'date', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'birthday', - index: 'ore', - castAs: 'date', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'email', - index: 'unique', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'email', - index: 'match', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'email', - index: 'ore', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'emailverified', - index: 'unique', - castAs: 'boolean', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'preferences', - index: 'ste_vec', - castAs: 'jsonb', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'salary', - index: 'unique', - castAs: 'double', - }), - cipherstashAddSearchConfig({ - table: 'users', - column: 'salary', - index: 'ore', - castAs: 'double', - }), - ] - } -} - -MigrationCLI.run(import.meta.url, M) diff --git a/examples/prisma/migrations/app/20260709T1034_initial/end-contract.d.ts b/examples/prisma/migrations/app/20260709T1034_initial/end-contract.d.ts new file mode 100644 index 000000000..d6c3d2b94 --- /dev/null +++ b/examples/prisma/migrations/app/20260709T1034_initial/end-contract.d.ts @@ -0,0 +1,513 @@ +// ⚠️ GENERATED FILE - DO NOT EDIT +// This file is automatically generated by 'prisma-next contract emit'. +// To regenerate, run: prisma-next contract emit +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { CodecTypes as CipherstashTypes } from '@prisma-next/extension-cipherstash/codec-types'; +import type { QueryOperationTypes as CipherstashQueryOperationTypes } from '@prisma-next/extension-cipherstash/operation-types'; +import type { + EncryptedBigInt, + EncryptedBoolean, + EncryptedDate, + EncryptedDouble, + EncryptedJson, + EncryptedString, +} from '@prisma-next/extension-cipherstash/runtime'; +import type { + Bit, + Char, + CodecTypes as PgTypes, + Interval, + JsonValue, + Numeric, + Time, + Timestamp, + Timestamptz, + Timetz, + VarBit, + Varchar, +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; + +export type StorageHash = + StorageHashBase<'sha256:b8c4febc9397cf1b68293cdb3b2afe9f568db6967f428e3f207e32575a2bc2fa'>; +export type ExecutionHash = ExecutionHashBase; +export type ProfileHash = + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; + +export type CodecTypes = PgTypes & CipherstashTypes; +export type LaneCodecTypes = CodecTypes; +export type QueryOperationTypes = PgAdapterQueryOps & + CipherstashQueryOperationTypes; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; + +export type FieldOutputTypes = { + readonly public: { + readonly User: { + readonly id: CodecTypes['pg/text@1']['output']; + readonly email: CodecTypes['cipherstash/string@1']['output']; + readonly salary: CodecTypes['cipherstash/double@1']['output']; + readonly accountId: CodecTypes['cipherstash/bigint@1']['output']; + readonly birthday: CodecTypes['cipherstash/date@1']['output']; + readonly emailVerified: CodecTypes['cipherstash/boolean@1']['output']; + readonly preferences: CodecTypes['cipherstash/json@1']['output']; + }; + }; +}; +export type FieldInputTypes = { + readonly public: { + readonly User: { + readonly id: CodecTypes['pg/text@1']['input']; + readonly email: CodecTypes['cipherstash/string@1']['input']; + readonly salary: CodecTypes['cipherstash/double@1']['input']; + readonly accountId: CodecTypes['cipherstash/bigint@1']['input']; + readonly birthday: CodecTypes['cipherstash/date@1']['input']; + readonly emailVerified: CodecTypes['cipherstash/boolean@1']['input']; + readonly preferences: CodecTypes['cipherstash/json@1']['input']; + }; + }; +}; +export type TypeMaps = TypeMapsType< + CodecTypes, + QueryOperationTypes, + FieldOutputTypes, + FieldInputTypes +>; + +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'sql-namespace'; + readonly entries: { + readonly table: { + readonly users: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly email: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/string@1'; + readonly nullable: false; + readonly typeParams: { + readonly equality: true; + readonly freeTextSearch: true; + readonly orderAndRange: true; + }; + }; + readonly salary: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/double@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + readonly accountid: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/bigint@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + readonly birthday: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/date@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + readonly emailverified: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/boolean@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true }; + }; + readonly preferences: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/json@1'; + readonly nullable: false; + readonly typeParams: { readonly searchableJson: true }; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' +> & { + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly users: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly User: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/string@1'; + readonly typeParams: { + readonly equality: true; + readonly freeTextSearch: true; + readonly orderAndRange: true; + }; + }; + }; + readonly salary: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/double@1'; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + }; + readonly accountId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/bigint@1'; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + }; + readonly birthday: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/date@1'; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + }; + readonly emailVerified: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/boolean@1'; + readonly typeParams: { readonly equality: true }; + }; + }; + readonly preferences: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/json@1'; + readonly typeParams: { readonly searchableJson: true }; + }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'users'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly salary: { readonly column: 'salary' }; + readonly accountId: { readonly column: 'accountid' }; + readonly birthday: { readonly column: 'birthday' }; + readonly emailVerified: { readonly column: 'emailverified' }; + readonly preferences: { readonly column: 'preferences' }; + }; + }; + }; + }; + }; + }; + }; + readonly capabilities: { + readonly postgres: { + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; + readonly sql: { + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; + readonly extensionPacks: { + readonly cipherstash: { + readonly familyId: 'sql'; + readonly id: 'cipherstash'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; + readonly types: { + readonly codecTypes: { + readonly codecInstances: readonly [ + { + readonly descriptor: { + readonly codecId: 'cipherstash/string@1'; + readonly factory: unknown; + readonly isParameterized: false; + readonly meta: { + readonly db: { + readonly sql: { + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; + readonly paramsSchema: { + readonly '~standard': { + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly [ + 'cipherstash:equality', + 'cipherstash:free-text-search', + 'cipherstash:order-and-range', + ]; + }; + }, + { + readonly descriptor: { + readonly codecId: 'cipherstash/double@1'; + readonly factory: unknown; + readonly isParameterized: false; + readonly meta: { + readonly db: { + readonly sql: { + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; + readonly paramsSchema: { + readonly '~standard': { + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality', 'cipherstash:order-and-range']; + }; + }, + { + readonly descriptor: { + readonly codecId: 'cipherstash/bigint@1'; + readonly factory: unknown; + readonly isParameterized: false; + readonly meta: { + readonly db: { + readonly sql: { + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; + readonly paramsSchema: { + readonly '~standard': { + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality', 'cipherstash:order-and-range']; + }; + }, + { + readonly descriptor: { + readonly codecId: 'cipherstash/date@1'; + readonly factory: unknown; + readonly isParameterized: false; + readonly meta: { + readonly db: { + readonly sql: { + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; + readonly paramsSchema: { + readonly '~standard': { + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality', 'cipherstash:order-and-range']; + }; + }, + { + readonly descriptor: { + readonly codecId: 'cipherstash/boolean@1'; + readonly factory: unknown; + readonly isParameterized: false; + readonly meta: { + readonly db: { + readonly sql: { + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; + readonly paramsSchema: { + readonly '~standard': { + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality']; + }; + }, + { + readonly descriptor: { + readonly codecId: 'cipherstash/json@1'; + readonly factory: unknown; + readonly isParameterized: false; + readonly meta: { + readonly db: { + readonly sql: { + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; + readonly paramsSchema: { + readonly '~standard': { + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:searchable-json']; + }; + }, + ]; + readonly import: { + readonly alias: 'CipherstashTypes'; + readonly named: 'CodecTypes'; + readonly package: '@prisma-next/extension-cipherstash/codec-types'; + }; + readonly typeImports: readonly [ + { + readonly alias: 'EncryptedString'; + readonly named: 'EncryptedString'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; + }, + { + readonly alias: 'EncryptedDouble'; + readonly named: 'EncryptedDouble'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; + }, + { + readonly alias: 'EncryptedBigInt'; + readonly named: 'EncryptedBigInt'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; + }, + { + readonly alias: 'EncryptedDate'; + readonly named: 'EncryptedDate'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; + }, + { + readonly alias: 'EncryptedBoolean'; + readonly named: 'EncryptedBoolean'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; + }, + { + readonly alias: 'EncryptedJson'; + readonly named: 'EncryptedJson'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; + }, + ]; + }; + readonly queryOperationTypes: { + readonly import: { + readonly alias: 'CipherstashQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@prisma-next/extension-cipherstash/operation-types'; + }; + }; + readonly storage: readonly [ + { + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/string@1'; + }, + { + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/double@1'; + }, + { + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/bigint@1'; + }, + { + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/date@1'; + }, + { + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/boolean@1'; + }, + { + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/json@1'; + }, + ]; + }; + readonly version: '0.0.1'; + }; + }; + readonly meta: {}; + + readonly profileHash: ProfileHash; +}; + +export type Contract = ContractWithTypeMaps; + +export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma/migrations/app/20260513T1735_initial/end-contract.json b/examples/prisma/migrations/app/20260709T1034_initial/end-contract.json similarity index 53% rename from examples/prisma/migrations/app/20260513T1735_initial/end-contract.json rename to examples/prisma/migrations/app/20260709T1034_initial/end-contract.json index 98c97b1fa..9a06c3eba 100644 --- a/examples/prisma/migrations/app/20260513T1735_initial/end-contract.json +++ b/examples/prisma/migrations/app/20260709T1034_initial/end-contract.json @@ -2,190 +2,211 @@ "schemaVersion": "1", "targetFamily": "sql", "target": "postgres", - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", "roots": { - "users": "User" + "users": { + "model": "User", + "namespace": "public" + } }, - "models": { - "User": { - "fields": { - "accountId": { - "nullable": false, - "type": { - "codecId": "cipherstash/bigint@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - }, - "birthday": { - "nullable": false, - "type": { - "codecId": "cipherstash/date@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - }, - "email": { - "nullable": false, - "type": { - "codecId": "cipherstash/string@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "freeTextSearch": true, - "orderAndRange": true - } - } - }, - "emailVerified": { - "nullable": false, - "type": { - "codecId": "cipherstash/boolean@1", - "kind": "scalar", - "typeParams": { - "equality": true - } - } - }, - "id": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "preferences": { - "nullable": false, - "type": { - "codecId": "cipherstash/json@1", - "kind": "scalar", - "typeParams": { - "searchableJson": true - } - } - }, - "salary": { - "nullable": false, - "type": { - "codecId": "cipherstash/double@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true + "domain": { + "namespaces": { + "public": { + "models": { + "User": { + "fields": { + "accountId": { + "nullable": false, + "type": { + "codecId": "cipherstash/bigint@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + }, + "birthday": { + "nullable": false, + "type": { + "codecId": "cipherstash/date@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "cipherstash/string@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "freeTextSearch": true, + "orderAndRange": true + } + } + }, + "emailVerified": { + "nullable": false, + "type": { + "codecId": "cipherstash/boolean@1", + "kind": "scalar", + "typeParams": { + "equality": true + } + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "preferences": { + "nullable": false, + "type": { + "codecId": "cipherstash/json@1", + "kind": "scalar", + "typeParams": { + "searchableJson": true + } + } + }, + "salary": { + "nullable": false, + "type": { + "codecId": "cipherstash/double@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "accountId": { + "column": "accountid" + }, + "birthday": { + "column": "birthday" + }, + "email": { + "column": "email" + }, + "emailVerified": { + "column": "emailverified" + }, + "id": { + "column": "id" + }, + "preferences": { + "column": "preferences" + }, + "salary": { + "column": "salary" + } + }, + "namespaceId": "public", + "table": "users" } } } - }, - "relations": {}, - "storage": { - "fields": { - "accountId": { - "column": "accountid" - }, - "birthday": { - "column": "birthday" - }, - "email": { - "column": "email" - }, - "emailVerified": { - "column": "emailverified" - }, - "id": { - "column": "id" - }, - "preferences": { - "column": "preferences" - }, - "salary": { - "column": "salary" - } - }, - "table": "users" } } }, "storage": { - "storageHash": "sha256:7475191ce0d78258ce5586265bcdfd12202f5daf90690b902890e58eb7508373", - "tables": { - "users": { - "columns": { - "accountid": { - "codecId": "cipherstash/bigint@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true - } - }, - "birthday": { - "codecId": "cipherstash/date@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true - } - }, - "email": { - "codecId": "cipherstash/string@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "freeTextSearch": true, - "orderAndRange": true - } - }, - "emailverified": { - "codecId": "cipherstash/boolean@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true - } - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "preferences": { - "codecId": "cipherstash/json@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "searchableJson": true - } - }, - "salary": { - "codecId": "cipherstash/double@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true + "namespaces": { + "public": { + "entries": { + "table": { + "users": { + "columns": { + "accountid": { + "codecId": "cipherstash/bigint@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "orderAndRange": true + } + }, + "birthday": { + "codecId": "cipherstash/date@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "orderAndRange": true + } + }, + "email": { + "codecId": "cipherstash/string@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "freeTextSearch": true, + "orderAndRange": true + } + }, + "emailverified": { + "codecId": "cipherstash/boolean@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true + } + }, + "id": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "preferences": { + "codecId": "cipherstash/json@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "searchableJson": true + } + }, + "salary": { + "codecId": "cipherstash/double@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] + }, + "uniques": [] } } }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": ["id"] - }, - "uniques": [] + "id": "public", + "kind": "postgres-schema" } - } + }, + "storageHash": "sha256:b8c4febc9397cf1b68293cdb3b2afe9f568db6967f428e3f207e32575a2bc2fa" }, "capabilities": { "postgres": { + "distinctOn": true, "jsonAgg": true, "lateral": true, "limit": true, @@ -195,6 +216,7 @@ "sql": { "defaultInInsert": true, "enums": true, + "lateral": true, "returning": true } }, @@ -225,7 +247,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:free-text-search", @@ -251,7 +275,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:order-and-range" @@ -276,7 +302,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:order-and-range" @@ -301,7 +329,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:order-and-range" @@ -326,8 +356,12 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], - "traits": ["cipherstash:equality"] + "targetTypes": [ + "eql_v2_encrypted" + ], + "traits": [ + "cipherstash:equality" + ] } }, { @@ -348,8 +382,12 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], - "traits": ["cipherstash:searchable-json"] + "targetTypes": [ + "eql_v2_encrypted" + ], + "traits": [ + "cipherstash:searchable-json" + ] } } ], @@ -446,4 +484,4 @@ "message": "This file is automatically generated by \"prisma-next contract emit\".", "regenerate": "To regenerate, run: prisma-next contract emit" } -} +} \ No newline at end of file diff --git a/examples/prisma/migrations/app/20260709T1034_initial/migration.json b/examples/prisma/migrations/app/20260709T1034_initial/migration.json new file mode 100644 index 000000000..14b8ed399 --- /dev/null +++ b/examples/prisma/migrations/app/20260709T1034_initial/migration.json @@ -0,0 +1,19 @@ +{ + "from": null, + "to": "sha256:b8c4febc9397cf1b68293cdb3b2afe9f568db6967f428e3f207e32575a2bc2fa", + "providedInvariants": [ + "cipherstash-codec:users.accountid:add-search-config:ore@v1", + "cipherstash-codec:users.accountid:add-search-config:unique@v1", + "cipherstash-codec:users.birthday:add-search-config:ore@v1", + "cipherstash-codec:users.birthday:add-search-config:unique@v1", + "cipherstash-codec:users.email:add-search-config:match@v1", + "cipherstash-codec:users.email:add-search-config:ore@v1", + "cipherstash-codec:users.email:add-search-config:unique@v1", + "cipherstash-codec:users.emailverified:add-search-config:unique@v1", + "cipherstash-codec:users.preferences:add-search-config:ste_vec@v1", + "cipherstash-codec:users.salary:add-search-config:ore@v1", + "cipherstash-codec:users.salary:add-search-config:unique@v1" + ], + "createdAt": "2026-07-09T10:34:08.902Z", + "migrationHash": "sha256:4cce7dab1306568bb00184c15d1b82f6c5a1f61adb6b37a211b917adc4e71200" +} \ No newline at end of file diff --git a/examples/prisma/migrations/app/20260709T1034_initial/migration.ts b/examples/prisma/migrations/app/20260709T1034_initial/migration.ts new file mode 100755 index 000000000..11c035c3a --- /dev/null +++ b/examples/prisma/migrations/app/20260709T1034_initial/migration.ts @@ -0,0 +1,114 @@ +#!/usr/bin/env -S node +import { cipherstashAddSearchConfig } from '@prisma-next/extension-cipherstash/migration'; +import { Migration, MigrationCLI, col, primaryKey } from '@prisma-next/postgres/migration'; + +export default class M extends Migration { + override describe() { + return { + from: null, + to: 'sha256:b8c4febc9397cf1b68293cdb3b2afe9f568db6967f428e3f207e32575a2bc2fa', + }; + } + + override get operations() { + return [ + this.createTable({ + schema: 'public', + table: 'users', + columns: [ + col('accountid', 'eql_v2_encrypted', { + notNull: true, + codecRef: { + codecId: 'cipherstash/bigint@1', + typeParams: { equality: true, orderAndRange: true }, + }, + }), + col('birthday', 'eql_v2_encrypted', { + notNull: true, + codecRef: { + codecId: 'cipherstash/date@1', + typeParams: { equality: true, orderAndRange: true }, + }, + }), + col('email', 'eql_v2_encrypted', { + notNull: true, + codecRef: { + codecId: 'cipherstash/string@1', + typeParams: { equality: true, freeTextSearch: true, orderAndRange: true }, + }, + }), + col('emailverified', 'eql_v2_encrypted', { + notNull: true, + codecRef: { codecId: 'cipherstash/boolean@1', typeParams: { equality: true } }, + }), + col('id', 'text', { notNull: true, codecRef: { codecId: 'pg/text@1' } }), + col('preferences', 'eql_v2_encrypted', { + notNull: true, + codecRef: { codecId: 'cipherstash/json@1', typeParams: { searchableJson: true } }, + }), + col('salary', 'eql_v2_encrypted', { + notNull: true, + codecRef: { + codecId: 'cipherstash/double@1', + typeParams: { equality: true, orderAndRange: true }, + }, + }), + ], + constraints: [primaryKey(['id'])], + }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'accountid', + index: 'unique', + castAs: 'big_int', + }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'accountid', + index: 'ore', + castAs: 'big_int', + }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'birthday', + index: 'unique', + castAs: 'date', + }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'birthday', + index: 'ore', + castAs: 'date', + }), + cipherstashAddSearchConfig({ table: 'users', column: 'email', index: 'unique' }), + cipherstashAddSearchConfig({ table: 'users', column: 'email', index: 'match' }), + cipherstashAddSearchConfig({ table: 'users', column: 'email', index: 'ore' }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'emailverified', + index: 'unique', + castAs: 'boolean', + }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'preferences', + index: 'ste_vec', + castAs: 'jsonb', + }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'salary', + index: 'unique', + castAs: 'double', + }), + cipherstashAddSearchConfig({ + table: 'users', + column: 'salary', + index: 'ore', + castAs: 'double', + }), + ]; + } +} + +MigrationCLI.run(import.meta.url, M); diff --git a/examples/prisma/migrations/app/20260513T1735_initial/ops.json b/examples/prisma/migrations/app/20260709T1034_initial/ops.json similarity index 95% rename from examples/prisma/migrations/app/20260513T1735_initial/ops.json rename to examples/prisma/migrations/app/20260709T1034_initial/ops.json index e985c872c..72baac1a5 100644 --- a/examples/prisma/migrations/app/20260513T1735_initial/ops.json +++ b/examples/prisma/migrations/app/20260709T1034_initial/ops.json @@ -15,19 +15,26 @@ "precheck": [ { "description": "ensure table \"users\" does not exist", - "sql": "SELECT to_regclass('\"public\".\"users\"') IS NULL" + "sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", + "params": [ + "\"public\".\"users\"" + ] } ], "execute": [ { "description": "create table \"users\"", - "sql": "CREATE TABLE \"public\".\"users\" (\n \"accountid\" eql_v2_encrypted NOT NULL,\n \"birthday\" eql_v2_encrypted NOT NULL,\n \"email\" eql_v2_encrypted NOT NULL,\n \"emailverified\" eql_v2_encrypted NOT NULL,\n \"id\" text NOT NULL,\n \"preferences\" eql_v2_encrypted NOT NULL,\n \"salary\" eql_v2_encrypted NOT NULL,\n PRIMARY KEY (\"id\")\n)" + "sql": "CREATE TABLE \"public\".\"users\" (\n \"accountid\" eql_v2_encrypted NOT NULL,\n \"birthday\" eql_v2_encrypted NOT NULL,\n \"email\" eql_v2_encrypted NOT NULL,\n \"emailverified\" eql_v2_encrypted NOT NULL,\n \"id\" text NOT NULL,\n \"preferences\" eql_v2_encrypted NOT NULL,\n \"salary\" eql_v2_encrypted NOT NULL,\n PRIMARY KEY (\"id\")\n)", + "params": [] } ], "postcheck": [ { "description": "verify table \"users\" exists", - "sql": "SELECT to_regclass('\"public\".\"users\"') IS NOT NULL" + "sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"", + "params": [ + "\"public\".\"users\"" + ] } ] }, @@ -218,4 +225,4 @@ ], "postcheck": [] } -] +] \ No newline at end of file diff --git a/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.json b/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.json deleted file mode 100644 index 0eed3abc3..000000000 --- a/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "_generated": { - "message": "This file is automatically generated by \"prisma-next contract emit\".", - "regenerate": "To regenerate, run: prisma-next contract emit", - "warning": "⚠️ GENERATED FILE - DO NOT EDIT" - }, - "capabilities": { - "postgres": { - "jsonAgg": true, - "lateral": true, - "limit": true, - "orderBy": true, - "returning": true - }, - "sql": { "defaultInInsert": true, "enums": true, "returning": true } - }, - "extensionPacks": {}, - "meta": {}, - "models": { - "EqlV2Configuration": { - "fields": { - "data": { - "nullable": false, - "type": { "codecId": "pg/jsonb@1", "kind": "scalar" } - }, - "id": { - "nullable": false, - "type": { "codecId": "pg/text@1", "kind": "scalar" } - }, - "state": { - "nullable": false, - "type": { "codecId": "pg/text@1", "kind": "scalar" } - } - }, - "relations": {}, - "storage": { - "fields": { - "data": { "column": "data" }, - "id": { "column": "id" }, - "state": { "column": "state" } - }, - "table": "eql_v2_configuration" - } - } - }, - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", - "roots": { "eql_v2_configuration": "EqlV2Configuration" }, - "schemaVersion": "1", - "storage": { - "storageHash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { "columns": ["id"] }, - "uniques": [] - } - } - }, - "target": "postgres", - "targetFamily": "sql" -} diff --git a/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json b/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json index bf79367bf..4bf75c2ac 100644 --- a/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json +++ b/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json @@ -1,116 +1,9 @@ { "from": null, - "to": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "labels": [], - "providedInvariants": ["cipherstash:install-eql-bundle-v1"], + "to": "sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7", + "providedInvariants": [ + "cipherstash:install-eql-bundle-v1" + ], "createdAt": "2026-05-09T03:42:56.902Z", - "fromContract": null, - "toContract": { - "schemaVersion": "1", - "targetFamily": "sql", - "target": "postgres", - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", - "roots": { - "eql_v2_configuration": "EqlV2Configuration" - }, - "models": { - "EqlV2Configuration": { - "fields": { - "data": { - "nullable": false, - "type": { - "codecId": "pg/jsonb@1", - "kind": "scalar" - } - }, - "id": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "state": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - } - }, - "relations": {}, - "storage": { - "fields": { - "data": { - "column": "data" - }, - "id": { - "column": "id" - }, - "state": { - "column": "state" - } - }, - "table": "eql_v2_configuration" - } - } - }, - "storage": { - "storageHash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": ["id"] - }, - "uniques": [] - } - } - }, - "capabilities": { - "postgres": { - "jsonAgg": true, - "lateral": true, - "limit": true, - "orderBy": true, - "returning": true - }, - "sql": { - "defaultInInsert": true, - "enums": true, - "returning": true - } - }, - "extensionPacks": {}, - "meta": {}, - "_generated": { - "warning": "⚠️ GENERATED FILE - DO NOT EDIT", - "message": "This file is automatically generated by \"prisma-next contract emit\".", - "regenerate": "To regenerate, run: prisma-next contract emit" - } - }, - "hints": { - "used": [], - "applied": [], - "plannerVersion": "2.0.0" - }, - "migrationHash": "sha256:76923a92561cdad65c64088ce999bf7afe853b80aac0b787b0d271b0e623abbc" -} + "migrationHash": "sha256:ae84404848d090c24f4094331c14972474a9695ffd45d29a21c5f5a8cfed9df7" +} \ No newline at end of file diff --git a/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json b/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json index c124cd04e..47ee0ca65 100644 --- a/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json +++ b/examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json @@ -25,4 +25,4 @@ } ] } -] +] \ No newline at end of file diff --git a/examples/prisma/migrations/cipherstash/contract.d.ts b/examples/prisma/migrations/cipherstash/contract.d.ts index e301b06e2..181d93f81 100644 --- a/examples/prisma/migrations/cipherstash/contract.d.ts +++ b/examples/prisma/migrations/cipherstash/contract.d.ts @@ -5,6 +5,6 @@ * alongside `contract.json` and `refs/head.json`. A typed `.d.ts` * rendering pass for extension contracts is tracked separately; * until that ships, consumers should import `contract.json` - * directly with `validateContract<…>(…)`. + * and pass it through the target descriptor’s `contractSerializer`. */ -export {} +export {}; diff --git a/examples/prisma/migrations/cipherstash/contract.json b/examples/prisma/migrations/cipherstash/contract.json index 0eed3abc3..9bab116fb 100644 --- a/examples/prisma/migrations/cipherstash/contract.json +++ b/examples/prisma/migrations/cipherstash/contract.json @@ -1,79 +1 @@ -{ - "_generated": { - "message": "This file is automatically generated by \"prisma-next contract emit\".", - "regenerate": "To regenerate, run: prisma-next contract emit", - "warning": "⚠️ GENERATED FILE - DO NOT EDIT" - }, - "capabilities": { - "postgres": { - "jsonAgg": true, - "lateral": true, - "limit": true, - "orderBy": true, - "returning": true - }, - "sql": { "defaultInInsert": true, "enums": true, "returning": true } - }, - "extensionPacks": {}, - "meta": {}, - "models": { - "EqlV2Configuration": { - "fields": { - "data": { - "nullable": false, - "type": { "codecId": "pg/jsonb@1", "kind": "scalar" } - }, - "id": { - "nullable": false, - "type": { "codecId": "pg/text@1", "kind": "scalar" } - }, - "state": { - "nullable": false, - "type": { "codecId": "pg/text@1", "kind": "scalar" } - } - }, - "relations": {}, - "storage": { - "fields": { - "data": { "column": "data" }, - "id": { "column": "id" }, - "state": { "column": "state" } - }, - "table": "eql_v2_configuration" - } - } - }, - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", - "roots": { "eql_v2_configuration": "EqlV2Configuration" }, - "schemaVersion": "1", - "storage": { - "storageHash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "tables": { - "eql_v2_configuration": { - "columns": { - "data": { - "codecId": "pg/jsonb@1", - "nativeType": "jsonb", - "nullable": false - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "state": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - } - }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { "columns": ["id"] }, - "uniques": [] - } - } - }, - "target": "postgres", - "targetFamily": "sql" -} +{"_generated":{"message":"This file is automatically generated by \"prisma-next contract emit\".","regenerate":"To regenerate, run: prisma-next contract emit","warning":"⚠️ GENERATED FILE - DO NOT EDIT"},"capabilities":{"postgres":{"distinctOn":true,"jsonAgg":true,"lateral":true,"limit":true,"orderBy":true,"returning":true},"sql":{"defaultInInsert":true,"enums":true,"lateral":true,"returning":true}},"domain":{"namespaces":{"public":{"models":{"EqlV2Configuration":{"fields":{"data":{"nullable":false,"type":{"codecId":"pg/jsonb@1","kind":"scalar"}},"id":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}},"state":{"nullable":false,"type":{"codecId":"pg/text@1","kind":"scalar"}}},"relations":{},"storage":{"fields":{"data":{"column":"data"},"id":{"column":"id"},"state":{"column":"state"}},"namespaceId":"public","table":"eql_v2_configuration"}}}}}},"extensionPacks":{},"meta":{},"profileHash":"sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb","roots":{"eql_v2_configuration":{"model":"EqlV2Configuration","namespace":"public"}},"schemaVersion":"1","storage":{"namespaces":{"public":{"entries":{"table":{"eql_v2_configuration":{"columns":{"data":{"codecId":"pg/jsonb@1","nativeType":"jsonb","nullable":false},"id":{"codecId":"pg/text@1","nativeType":"text","nullable":false},"state":{"codecId":"pg/text@1","nativeType":"text","nullable":false}},"foreignKeys":[],"indexes":[],"primaryKey":{"columns":["id"]},"uniques":[]}}},"id":"public","kind":"postgres-schema"}},"storageHash":"sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7"},"target":"postgres","targetFamily":"sql"} diff --git a/examples/prisma/migrations/cipherstash/refs/head.json b/examples/prisma/migrations/cipherstash/refs/head.json index 78f580898..d3a5021e4 100644 --- a/examples/prisma/migrations/cipherstash/refs/head.json +++ b/examples/prisma/migrations/cipherstash/refs/head.json @@ -1,4 +1 @@ -{ - "hash": "sha256:efa685171bebbb8f078f08d12be3578bb5d96b71669dccc6cc9e4be96af8cdb4", - "invariants": ["cipherstash:install-eql-bundle-v1"] -} +{"hash":"sha256:1e86a0160ba305fa74516b6d9449218308b258a51a913c1fc907e629f44568a7","invariants":["cipherstash:install-eql-bundle-v1"]} diff --git a/examples/prisma/package.json b/examples/prisma/package.json index 9560b85b7..7ad4aff1b 100644 --- a/examples/prisma/package.json +++ b/examples/prisma/package.json @@ -7,7 +7,7 @@ "scripts": { "emit": "prisma-next contract emit", "migration:plan": "prisma-next migration plan", - "migration:apply": "prisma-next migration apply", + "migration:apply": "prisma-next migrate --yes", "start": "tsx src/index.ts", "test:e2e": "vitest run --config test/e2e/vitest.config.ts", "typecheck": "tsc --project tsconfig.json --noEmit" @@ -15,21 +15,21 @@ "dependencies": { "@cipherstash/prisma-next": "workspace:*", "@cipherstash/stack": "workspace:*", - "@prisma-next/adapter-postgres": "0.8.0", - "@prisma-next/contract": "0.8.0", - "@prisma-next/driver-postgres": "0.8.0", - "@prisma-next/family-sql": "0.8.0", - "@prisma-next/framework-components": "0.8.0", - "@prisma-next/postgres": "0.8.0", - "@prisma-next/sql-contract": "0.8.0", - "@prisma-next/sql-contract-psl": "0.8.0", - "@prisma-next/sql-orm-client": "0.8.0", - "@prisma-next/sql-runtime": "0.8.0", - "@prisma-next/target-postgres": "0.8.0", + "@prisma-next/adapter-postgres": "0.14.0", + "@prisma-next/contract": "0.14.0", + "@prisma-next/driver-postgres": "0.14.0", + "@prisma-next/family-sql": "0.14.0", + "@prisma-next/framework-components": "0.14.0", + "@prisma-next/postgres": "0.14.0", + "@prisma-next/sql-contract": "0.14.0", + "@prisma-next/sql-contract-psl": "0.14.0", + "@prisma-next/sql-orm-client": "0.14.0", + "@prisma-next/sql-runtime": "0.14.0", + "@prisma-next/target-postgres": "0.14.0", "dotenv": "^17.4.2" }, "devDependencies": { - "@prisma-next/cli": "0.8.0", + "@prisma-next/cli": "0.14.0", "@types/node": "^22.19.19", "pathe": "^2.0.3", "tsx": "catalog:repo", diff --git a/examples/prisma/prisma-next.config.ts b/examples/prisma/prisma-next.config.ts index c08559a80..9cf0597bb 100644 --- a/examples/prisma/prisma-next.config.ts +++ b/examples/prisma/prisma-next.config.ts @@ -6,6 +6,7 @@ import postgresDriver from '@prisma-next/driver-postgres/control' import sql from '@prisma-next/family-sql/control' import { prismaContract } from '@prisma-next/sql-contract-psl/provider' import postgres from '@prisma-next/target-postgres/control' +import postgresPack from '@prisma-next/target-postgres/pack' const databaseUrl = process.env['DATABASE_URL'] @@ -17,7 +18,9 @@ export default defineConfig({ extensionPacks: [cipherstash], contract: prismaContract('./prisma/schema.prisma', { output: 'src/prisma/contract.json', - target: postgres, + // Since 0.14 `prismaContract` takes the target PACK ref (carrying + // `defaultNamespaceId`), not the control descriptor. + target: postgresPack, }), migrations: { dir: 'migrations', diff --git a/examples/prisma/src/index.ts b/examples/prisma/src/index.ts index 64242ec0d..885d3cddf 100644 --- a/examples/prisma/src/index.ts +++ b/examples/prisma/src/index.ts @@ -118,7 +118,7 @@ async function main() { } async function clearUsers(): Promise { - const removed = await db.orm.User.where((u) => u.id.isNotNull()).deleteCount() + const removed = await db.orm.public.User.where((u) => u.id.isNotNull()).deleteCount() if (removed > 0) { console.log(`--- Cleanup ---\nRemoved ${removed} existing user row(s).\n`) } @@ -128,7 +128,7 @@ async function insertUsers(): Promise { console.log('--- Insert (mixed-codec round-trip) ---') await Promise.all( SEED_USERS.map((seed) => - db.orm.User.create({ + db.orm.public.User.create({ id: seed.id, email: EncryptedString.from(seed.email), salary: EncryptedDouble.from(seed.salary), @@ -146,7 +146,7 @@ async function insertUsers(): Promise { async function searchByEq(): Promise { console.log('\n--- cipherstashEq (string equality) ---') - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.email.cipherstashEq('alice@example.com'), ).all() console.log(`Found ${rows.length} row(s) for alice@example.com.`) @@ -158,7 +158,7 @@ async function searchByEq(): Promise { async function searchByIlikeAndDecrypt(): Promise { console.log('\n--- cipherstashIlike (string free-text-search) ---') - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.email.cipherstashIlike('%@example.com'), ).all() console.log(`Found ${rows.length} row(s) matching %@example.com.`) @@ -170,7 +170,7 @@ async function searchByIlikeAndDecrypt(): Promise { async function rangeQueryOnSalary(): Promise { console.log('\n--- cipherstashGt (double order-and-range) ---') - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.salary.cipherstashGt(100_000), ).all() console.log(`Found ${rows.length} user(s) with salary > 100,000.`) @@ -184,7 +184,7 @@ async function betweenQueryOnBirthday(): Promise { console.log('\n--- cipherstashBetween (date order-and-range) ---') const lower = new Date('1985-01-01') const upper = new Date('1995-12-31') - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.birthday.cipherstashBetween(lower, upper), ).all() console.log(`Found ${rows.length} user(s) born between 1985 and 1995.`) @@ -192,7 +192,7 @@ async function betweenQueryOnBirthday(): Promise { async function inArrayQueryOnAccountId(): Promise { console.log('\n--- cipherstashInArray (bigint equality) ---') - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.accountId.cipherstashInArray([100_000_000_001n, 100_000_000_004n]), ).all() console.log( @@ -204,7 +204,7 @@ async function equalityQueryOnEmailVerified(): Promise { console.log('\n--- cipherstashInArray (boolean equality-only) ---') // Booleans surface only the equality-trait operators; a single-element // array is the canonical equality form on non-string codecs. - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.emailVerified.cipherstashInArray([true]), ).all() console.log(`Found ${rows.length} user(s) with emailVerified = true.`) @@ -212,7 +212,7 @@ async function equalityQueryOnEmailVerified(): Promise { async function sortByEmailAsc(): Promise { console.log('\n--- cipherstashAsc (bare-column ORDER BY) ---') - const rows = await db.orm.User.orderBy((u) => cipherstashAsc(u.email)).all() + const rows = await db.orm.public.User.orderBy((u) => cipherstashAsc(u.email)).all() await decryptAll(rows) for (const row of rows) { console.log(` ${row.id}: email=${await row.email.decrypt()}`) diff --git a/examples/prisma/src/prisma/contract.d.ts b/examples/prisma/src/prisma/contract.d.ts index 9bd67b083..d6c3d2b94 100644 --- a/examples/prisma/src/prisma/contract.d.ts +++ b/examples/prisma/src/prisma/contract.d.ts @@ -1,16 +1,9 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma-next contract emit'. // To regenerate, run: prisma-next contract emit - -import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types' -import type { - Contract as ContractType, - ExecutionHashBase, - ProfileHashBase, - StorageHashBase, -} from '@prisma-next/contract/types' -import type { CodecTypes as CipherstashTypes } from '@prisma-next/extension-cipherstash/codec-types' -import type { QueryOperationTypes as CipherstashQueryOperationTypes } from '@prisma-next/extension-cipherstash/operation-types' +import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma-next/adapter-postgres/operation-types'; +import type { CodecTypes as CipherstashTypes } from '@prisma-next/extension-cipherstash/codec-types'; +import type { QueryOperationTypes as CipherstashQueryOperationTypes } from '@prisma-next/extension-cipherstash/operation-types'; import type { EncryptedBigInt, EncryptedBoolean, @@ -18,516 +11,503 @@ import type { EncryptedDouble, EncryptedJson, EncryptedString, -} from '@prisma-next/extension-cipherstash/runtime' -import type { - ContractWithTypeMaps, - TypeMaps as TypeMapsType, -} from '@prisma-next/sql-contract/types' +} from '@prisma-next/extension-cipherstash/runtime'; import type { Bit, Char, + CodecTypes as PgTypes, Interval, JsonValue, Numeric, - CodecTypes as PgTypes, Time, Timestamp, Timestamptz, Timetz, VarBit, Varchar, -} from '@prisma-next/target-postgres/codec-types' +} from '@prisma-next/target-postgres/codec-types'; + +import type { + ContractWithTypeMaps, + TypeMaps as TypeMapsType, +} from '@prisma-next/sql-contract/types'; +import type { + Contract as ContractType, + ExecutionHashBase, + NamespaceId, + ProfileHashBase, + StorageHashBase, +} from '@prisma-next/contract/types'; export type StorageHash = - StorageHashBase<'sha256:7475191ce0d78258ce5586265bcdfd12202f5daf90690b902890e58eb7508373'> -export type ExecutionHash = ExecutionHashBase + StorageHashBase<'sha256:b8c4febc9397cf1b68293cdb3b2afe9f568db6967f428e3f207e32575a2bc2fa'>; +export type ExecutionHash = ExecutionHashBase; export type ProfileHash = - ProfileHashBase<'sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e'> + ProfileHashBase<'sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb'>; -export type CodecTypes = PgTypes & CipherstashTypes -export type LaneCodecTypes = CodecTypes +export type CodecTypes = PgTypes & CipherstashTypes; +export type LaneCodecTypes = CodecTypes; export type QueryOperationTypes = PgAdapterQueryOps & - CipherstashQueryOperationTypes -type DefaultLiteralValue< - CodecId extends string, - _Encoded, -> = CodecId extends keyof CodecTypes ? CodecTypes[CodecId]['output'] : _Encoded + CipherstashQueryOperationTypes; +type DefaultLiteralValue = CodecId extends keyof CodecTypes + ? CodecTypes[CodecId]['output'] + : _Encoded; export type FieldOutputTypes = { - readonly User: { - readonly id: CodecTypes['pg/text@1']['output'] - readonly email: CodecTypes['cipherstash/string@1']['output'] - readonly salary: CodecTypes['cipherstash/double@1']['output'] - readonly accountId: CodecTypes['cipherstash/bigint@1']['output'] - readonly birthday: CodecTypes['cipherstash/date@1']['output'] - readonly emailVerified: CodecTypes['cipherstash/boolean@1']['output'] - readonly preferences: CodecTypes['cipherstash/json@1']['output'] - } -} + readonly public: { + readonly User: { + readonly id: CodecTypes['pg/text@1']['output']; + readonly email: CodecTypes['cipherstash/string@1']['output']; + readonly salary: CodecTypes['cipherstash/double@1']['output']; + readonly accountId: CodecTypes['cipherstash/bigint@1']['output']; + readonly birthday: CodecTypes['cipherstash/date@1']['output']; + readonly emailVerified: CodecTypes['cipherstash/boolean@1']['output']; + readonly preferences: CodecTypes['cipherstash/json@1']['output']; + }; + }; +}; export type FieldInputTypes = { - readonly User: { - readonly id: CodecTypes['pg/text@1']['input'] - readonly email: CodecTypes['cipherstash/string@1']['input'] - readonly salary: CodecTypes['cipherstash/double@1']['input'] - readonly accountId: CodecTypes['cipherstash/bigint@1']['input'] - readonly birthday: CodecTypes['cipherstash/date@1']['input'] - readonly emailVerified: CodecTypes['cipherstash/boolean@1']['input'] - readonly preferences: CodecTypes['cipherstash/json@1']['input'] - } -} + readonly public: { + readonly User: { + readonly id: CodecTypes['pg/text@1']['input']; + readonly email: CodecTypes['cipherstash/string@1']['input']; + readonly salary: CodecTypes['cipherstash/double@1']['input']; + readonly accountId: CodecTypes['cipherstash/bigint@1']['input']; + readonly birthday: CodecTypes['cipherstash/date@1']['input']; + readonly emailVerified: CodecTypes['cipherstash/boolean@1']['input']; + readonly preferences: CodecTypes['cipherstash/json@1']['input']; + }; + }; +}; export type TypeMaps = TypeMapsType< CodecTypes, QueryOperationTypes, FieldOutputTypes, FieldInputTypes -> +>; -type ContractBase = ContractType< - { - readonly tables: { - readonly users: { - columns: { - readonly id: { - readonly nativeType: 'text' - readonly codecId: 'pg/text@1' - readonly nullable: false - } - readonly email: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/string@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly freeTextSearch: true - readonly orderAndRange: true - } - } - readonly salary: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/double@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - readonly accountid: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/bigint@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - readonly birthday: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/date@1' - readonly nullable: false - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - readonly emailverified: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/boolean@1' - readonly nullable: false - readonly typeParams: { readonly equality: true } - } - readonly preferences: { - readonly nativeType: 'eql_v2_encrypted' - readonly codecId: 'cipherstash/json@1' - readonly nullable: false - readonly typeParams: { readonly searchableJson: true } - } - } - primaryKey: { readonly columns: readonly ['id'] } - uniques: readonly [] - indexes: readonly [] - foreignKeys: readonly [] - } - } - readonly types: Record - readonly storageHash: StorageHash - }, - { - readonly User: { - readonly fields: { - readonly id: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'pg/text@1' - } - } - readonly email: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/string@1' - readonly typeParams: { - readonly equality: true - readonly freeTextSearch: true - readonly orderAndRange: true - } - } - } - readonly salary: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/double@1' - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - } - readonly accountId: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/bigint@1' - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - } - readonly birthday: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/date@1' - readonly typeParams: { - readonly equality: true - readonly orderAndRange: true - } - } - } - readonly emailVerified: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/boolean@1' - readonly typeParams: { readonly equality: true } - } - } - readonly preferences: { - readonly nullable: false - readonly type: { - readonly kind: 'scalar' - readonly codecId: 'cipherstash/json@1' - readonly typeParams: { readonly searchableJson: true } - } - } - } - readonly relations: Record - readonly storage: { - readonly table: 'users' - readonly fields: { - readonly id: { readonly column: 'id' } - readonly email: { readonly column: 'email' } - readonly salary: { readonly column: 'salary' } - readonly accountId: { readonly column: 'accountid' } - readonly birthday: { readonly column: 'birthday' } - readonly emailVerified: { readonly column: 'emailverified' } - readonly preferences: { readonly column: 'preferences' } - } - } - } - } +type ContractBase = Omit< + ContractType<{ + readonly namespaces: { + readonly public: { + readonly id: 'public'; + readonly kind: 'sql-namespace'; + readonly entries: { + readonly table: { + readonly users: { + columns: { + readonly id: { + readonly nativeType: 'text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + }; + readonly email: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/string@1'; + readonly nullable: false; + readonly typeParams: { + readonly equality: true; + readonly freeTextSearch: true; + readonly orderAndRange: true; + }; + }; + readonly salary: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/double@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + readonly accountid: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/bigint@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + readonly birthday: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/date@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + readonly emailverified: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/boolean@1'; + readonly nullable: false; + readonly typeParams: { readonly equality: true }; + }; + readonly preferences: { + readonly nativeType: 'eql_v2_encrypted'; + readonly codecId: 'cipherstash/json@1'; + readonly nullable: false; + readonly typeParams: { readonly searchableJson: true }; + }; + }; + primaryKey: { readonly columns: readonly ['id'] }; + uniques: readonly []; + indexes: readonly []; + foreignKeys: readonly []; + }; + }; + }; + }; + }; + readonly storageHash: StorageHash; + }>, + 'roots' | 'domain' > & { - readonly target: 'postgres' - readonly targetFamily: 'sql' - readonly roots: { readonly users: 'User' } + readonly target: 'postgres'; + readonly targetFamily: 'sql'; + readonly roots: { + readonly users: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + }; + readonly domain: { + readonly namespaces: { + readonly public: { + readonly models: { + readonly User: { + readonly fields: { + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; + }; + readonly email: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/string@1'; + readonly typeParams: { + readonly equality: true; + readonly freeTextSearch: true; + readonly orderAndRange: true; + }; + }; + }; + readonly salary: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/double@1'; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + }; + readonly accountId: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/bigint@1'; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + }; + readonly birthday: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/date@1'; + readonly typeParams: { readonly equality: true; readonly orderAndRange: true }; + }; + }; + readonly emailVerified: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/boolean@1'; + readonly typeParams: { readonly equality: true }; + }; + }; + readonly preferences: { + readonly nullable: false; + readonly type: { + readonly kind: 'scalar'; + readonly codecId: 'cipherstash/json@1'; + readonly typeParams: { readonly searchableJson: true }; + }; + }; + }; + readonly relations: Record; + readonly storage: { + readonly table: 'users'; + readonly namespaceId: 'public'; + readonly fields: { + readonly id: { readonly column: 'id' }; + readonly email: { readonly column: 'email' }; + readonly salary: { readonly column: 'salary' }; + readonly accountId: { readonly column: 'accountid' }; + readonly birthday: { readonly column: 'birthday' }; + readonly emailVerified: { readonly column: 'emailverified' }; + readonly preferences: { readonly column: 'preferences' }; + }; + }; + }; + }; + }; + }; + }; readonly capabilities: { readonly postgres: { - readonly jsonAgg: true - readonly lateral: true - readonly limit: true - readonly orderBy: true - readonly returning: true - } + readonly distinctOn: true; + readonly jsonAgg: true; + readonly lateral: true; + readonly limit: true; + readonly orderBy: true; + readonly returning: true; + }; readonly sql: { - readonly defaultInInsert: true - readonly enums: true - readonly returning: true - } - } + readonly defaultInInsert: true; + readonly enums: true; + readonly lateral: true; + readonly returning: true; + }; + }; readonly extensionPacks: { readonly cipherstash: { - readonly familyId: 'sql' - readonly id: 'cipherstash' - readonly kind: 'extension' - readonly targetId: 'postgres' + readonly familyId: 'sql'; + readonly id: 'cipherstash'; + readonly kind: 'extension'; + readonly targetId: 'postgres'; readonly types: { readonly codecTypes: { readonly codecInstances: readonly [ { readonly descriptor: { - readonly codecId: 'cipherstash/string@1' - readonly factory: unknown - readonly isParameterized: false + readonly codecId: 'cipherstash/string@1'; + readonly factory: unknown; + readonly isParameterized: false; readonly meta: { readonly db: { readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; readonly paramsSchema: { readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; readonly traits: readonly [ 'cipherstash:equality', 'cipherstash:free-text-search', 'cipherstash:order-and-range', - ] - } + ]; + }; }, { readonly descriptor: { - readonly codecId: 'cipherstash/double@1' - readonly factory: unknown - readonly isParameterized: false + readonly codecId: 'cipherstash/double@1'; + readonly factory: unknown; + readonly isParameterized: false; readonly meta: { readonly db: { readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; readonly paramsSchema: { readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly [ - 'cipherstash:equality', - 'cipherstash:order-and-range', - ] - } + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality', 'cipherstash:order-and-range']; + }; }, { readonly descriptor: { - readonly codecId: 'cipherstash/bigint@1' - readonly factory: unknown - readonly isParameterized: false + readonly codecId: 'cipherstash/bigint@1'; + readonly factory: unknown; + readonly isParameterized: false; readonly meta: { readonly db: { readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; readonly paramsSchema: { readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly [ - 'cipherstash:equality', - 'cipherstash:order-and-range', - ] - } + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality', 'cipherstash:order-and-range']; + }; }, { readonly descriptor: { - readonly codecId: 'cipherstash/date@1' - readonly factory: unknown - readonly isParameterized: false + readonly codecId: 'cipherstash/date@1'; + readonly factory: unknown; + readonly isParameterized: false; readonly meta: { readonly db: { readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; readonly paramsSchema: { readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly [ - 'cipherstash:equality', - 'cipherstash:order-and-range', - ] - } + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality', 'cipherstash:order-and-range']; + }; }, { readonly descriptor: { - readonly codecId: 'cipherstash/boolean@1' - readonly factory: unknown - readonly isParameterized: false + readonly codecId: 'cipherstash/boolean@1'; + readonly factory: unknown; + readonly isParameterized: false; readonly meta: { readonly db: { readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; readonly paramsSchema: { readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly ['cipherstash:equality'] - } + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:equality']; + }; }, { readonly descriptor: { - readonly codecId: 'cipherstash/json@1' - readonly factory: unknown - readonly isParameterized: false + readonly codecId: 'cipherstash/json@1'; + readonly factory: unknown; + readonly isParameterized: false; readonly meta: { readonly db: { readonly sql: { - readonly postgres: { - readonly nativeType: 'eql_v2_encrypted' - } - } - } - } + readonly postgres: { readonly nativeType: 'eql_v2_encrypted' }; + }; + }; + }; readonly paramsSchema: { readonly '~standard': { - readonly validate: unknown - readonly vendor: 'cipherstash' - readonly version: 1 - } - } - readonly renderOutputType: unknown - readonly targetTypes: readonly ['eql_v2_encrypted'] - readonly traits: readonly ['cipherstash:searchable-json'] - } + readonly validate: unknown; + readonly vendor: 'cipherstash'; + readonly version: 1; + }; + }; + readonly renderOutputType: unknown; + readonly targetTypes: readonly ['eql_v2_encrypted']; + readonly traits: readonly ['cipherstash:searchable-json']; + }; }, - ] + ]; readonly import: { - readonly alias: 'CipherstashTypes' - readonly named: 'CodecTypes' - readonly package: '@prisma-next/extension-cipherstash/codec-types' - } + readonly alias: 'CipherstashTypes'; + readonly named: 'CodecTypes'; + readonly package: '@prisma-next/extension-cipherstash/codec-types'; + }; readonly typeImports: readonly [ { - readonly alias: 'EncryptedString' - readonly named: 'EncryptedString' - readonly package: '@prisma-next/extension-cipherstash/runtime' + readonly alias: 'EncryptedString'; + readonly named: 'EncryptedString'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; }, { - readonly alias: 'EncryptedDouble' - readonly named: 'EncryptedDouble' - readonly package: '@prisma-next/extension-cipherstash/runtime' + readonly alias: 'EncryptedDouble'; + readonly named: 'EncryptedDouble'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; }, { - readonly alias: 'EncryptedBigInt' - readonly named: 'EncryptedBigInt' - readonly package: '@prisma-next/extension-cipherstash/runtime' + readonly alias: 'EncryptedBigInt'; + readonly named: 'EncryptedBigInt'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; }, { - readonly alias: 'EncryptedDate' - readonly named: 'EncryptedDate' - readonly package: '@prisma-next/extension-cipherstash/runtime' + readonly alias: 'EncryptedDate'; + readonly named: 'EncryptedDate'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; }, { - readonly alias: 'EncryptedBoolean' - readonly named: 'EncryptedBoolean' - readonly package: '@prisma-next/extension-cipherstash/runtime' + readonly alias: 'EncryptedBoolean'; + readonly named: 'EncryptedBoolean'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; }, { - readonly alias: 'EncryptedJson' - readonly named: 'EncryptedJson' - readonly package: '@prisma-next/extension-cipherstash/runtime' + readonly alias: 'EncryptedJson'; + readonly named: 'EncryptedJson'; + readonly package: '@prisma-next/extension-cipherstash/runtime'; }, - ] - } + ]; + }; readonly queryOperationTypes: { readonly import: { - readonly alias: 'CipherstashQueryOperationTypes' - readonly named: 'QueryOperationTypes' - readonly package: '@prisma-next/extension-cipherstash/operation-types' - } - } + readonly alias: 'CipherstashQueryOperationTypes'; + readonly named: 'QueryOperationTypes'; + readonly package: '@prisma-next/extension-cipherstash/operation-types'; + }; + }; readonly storage: readonly [ { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/string@1' + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/string@1'; }, { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/double@1' + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/double@1'; }, { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/bigint@1' + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/bigint@1'; }, { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/date@1' + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/date@1'; }, { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/boolean@1' + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/boolean@1'; }, { - readonly familyId: 'sql' - readonly nativeType: 'eql_v2_encrypted' - readonly targetId: 'postgres' - readonly typeId: 'cipherstash/json@1' + readonly familyId: 'sql'; + readonly nativeType: 'eql_v2_encrypted'; + readonly targetId: 'postgres'; + readonly typeId: 'cipherstash/json@1'; }, - ] - } - readonly version: '0.0.1' - } - } - readonly meta: {} + ]; + }; + readonly version: '0.0.1'; + }; + }; + readonly meta: {}; - readonly profileHash: ProfileHash -} + readonly profileHash: ProfileHash; +}; -export type Contract = ContractWithTypeMaps +export type Contract = ContractWithTypeMaps; -export type Tables = Contract['storage']['tables'] -export type Models = Contract['models'] +export type Namespaces = Contract['storage']['namespaces']; diff --git a/examples/prisma/src/prisma/contract.json b/examples/prisma/src/prisma/contract.json index 98c97b1fa..9a06c3eba 100644 --- a/examples/prisma/src/prisma/contract.json +++ b/examples/prisma/src/prisma/contract.json @@ -2,190 +2,211 @@ "schemaVersion": "1", "targetFamily": "sql", "target": "postgres", - "profileHash": "sha256:1a8dbe044289f30a1de958fe800cc5a8378b285d2e126a8c44b58864bac2c18e", + "profileHash": "sha256:9c8aa3114e84ed3b7ea2bd57526d9c2e1bf7c5292be694e9d3801f566fda7ccb", "roots": { - "users": "User" + "users": { + "model": "User", + "namespace": "public" + } }, - "models": { - "User": { - "fields": { - "accountId": { - "nullable": false, - "type": { - "codecId": "cipherstash/bigint@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - }, - "birthday": { - "nullable": false, - "type": { - "codecId": "cipherstash/date@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true - } - } - }, - "email": { - "nullable": false, - "type": { - "codecId": "cipherstash/string@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "freeTextSearch": true, - "orderAndRange": true - } - } - }, - "emailVerified": { - "nullable": false, - "type": { - "codecId": "cipherstash/boolean@1", - "kind": "scalar", - "typeParams": { - "equality": true - } - } - }, - "id": { - "nullable": false, - "type": { - "codecId": "pg/text@1", - "kind": "scalar" - } - }, - "preferences": { - "nullable": false, - "type": { - "codecId": "cipherstash/json@1", - "kind": "scalar", - "typeParams": { - "searchableJson": true - } - } - }, - "salary": { - "nullable": false, - "type": { - "codecId": "cipherstash/double@1", - "kind": "scalar", - "typeParams": { - "equality": true, - "orderAndRange": true + "domain": { + "namespaces": { + "public": { + "models": { + "User": { + "fields": { + "accountId": { + "nullable": false, + "type": { + "codecId": "cipherstash/bigint@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + }, + "birthday": { + "nullable": false, + "type": { + "codecId": "cipherstash/date@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + }, + "email": { + "nullable": false, + "type": { + "codecId": "cipherstash/string@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "freeTextSearch": true, + "orderAndRange": true + } + } + }, + "emailVerified": { + "nullable": false, + "type": { + "codecId": "cipherstash/boolean@1", + "kind": "scalar", + "typeParams": { + "equality": true + } + } + }, + "id": { + "nullable": false, + "type": { + "codecId": "pg/text@1", + "kind": "scalar" + } + }, + "preferences": { + "nullable": false, + "type": { + "codecId": "cipherstash/json@1", + "kind": "scalar", + "typeParams": { + "searchableJson": true + } + } + }, + "salary": { + "nullable": false, + "type": { + "codecId": "cipherstash/double@1", + "kind": "scalar", + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + } + }, + "relations": {}, + "storage": { + "fields": { + "accountId": { + "column": "accountid" + }, + "birthday": { + "column": "birthday" + }, + "email": { + "column": "email" + }, + "emailVerified": { + "column": "emailverified" + }, + "id": { + "column": "id" + }, + "preferences": { + "column": "preferences" + }, + "salary": { + "column": "salary" + } + }, + "namespaceId": "public", + "table": "users" } } } - }, - "relations": {}, - "storage": { - "fields": { - "accountId": { - "column": "accountid" - }, - "birthday": { - "column": "birthday" - }, - "email": { - "column": "email" - }, - "emailVerified": { - "column": "emailverified" - }, - "id": { - "column": "id" - }, - "preferences": { - "column": "preferences" - }, - "salary": { - "column": "salary" - } - }, - "table": "users" } } }, "storage": { - "storageHash": "sha256:7475191ce0d78258ce5586265bcdfd12202f5daf90690b902890e58eb7508373", - "tables": { - "users": { - "columns": { - "accountid": { - "codecId": "cipherstash/bigint@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true - } - }, - "birthday": { - "codecId": "cipherstash/date@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true - } - }, - "email": { - "codecId": "cipherstash/string@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "freeTextSearch": true, - "orderAndRange": true - } - }, - "emailverified": { - "codecId": "cipherstash/boolean@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true - } - }, - "id": { - "codecId": "pg/text@1", - "nativeType": "text", - "nullable": false - }, - "preferences": { - "codecId": "cipherstash/json@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "searchableJson": true - } - }, - "salary": { - "codecId": "cipherstash/double@1", - "nativeType": "eql_v2_encrypted", - "nullable": false, - "typeParams": { - "equality": true, - "orderAndRange": true + "namespaces": { + "public": { + "entries": { + "table": { + "users": { + "columns": { + "accountid": { + "codecId": "cipherstash/bigint@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "orderAndRange": true + } + }, + "birthday": { + "codecId": "cipherstash/date@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "orderAndRange": true + } + }, + "email": { + "codecId": "cipherstash/string@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "freeTextSearch": true, + "orderAndRange": true + } + }, + "emailverified": { + "codecId": "cipherstash/boolean@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true + } + }, + "id": { + "codecId": "pg/text@1", + "nativeType": "text", + "nullable": false + }, + "preferences": { + "codecId": "cipherstash/json@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "searchableJson": true + } + }, + "salary": { + "codecId": "cipherstash/double@1", + "nativeType": "eql_v2_encrypted", + "nullable": false, + "typeParams": { + "equality": true, + "orderAndRange": true + } + } + }, + "foreignKeys": [], + "indexes": [], + "primaryKey": { + "columns": [ + "id" + ] + }, + "uniques": [] } } }, - "foreignKeys": [], - "indexes": [], - "primaryKey": { - "columns": ["id"] - }, - "uniques": [] + "id": "public", + "kind": "postgres-schema" } - } + }, + "storageHash": "sha256:b8c4febc9397cf1b68293cdb3b2afe9f568db6967f428e3f207e32575a2bc2fa" }, "capabilities": { "postgres": { + "distinctOn": true, "jsonAgg": true, "lateral": true, "limit": true, @@ -195,6 +216,7 @@ "sql": { "defaultInInsert": true, "enums": true, + "lateral": true, "returning": true } }, @@ -225,7 +247,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:free-text-search", @@ -251,7 +275,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:order-and-range" @@ -276,7 +302,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:order-and-range" @@ -301,7 +329,9 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], + "targetTypes": [ + "eql_v2_encrypted" + ], "traits": [ "cipherstash:equality", "cipherstash:order-and-range" @@ -326,8 +356,12 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], - "traits": ["cipherstash:equality"] + "targetTypes": [ + "eql_v2_encrypted" + ], + "traits": [ + "cipherstash:equality" + ] } }, { @@ -348,8 +382,12 @@ "version": 1 } }, - "targetTypes": ["eql_v2_encrypted"], - "traits": ["cipherstash:searchable-json"] + "targetTypes": [ + "eql_v2_encrypted" + ], + "traits": [ + "cipherstash:searchable-json" + ] } } ], @@ -446,4 +484,4 @@ "message": "This file is automatically generated by \"prisma-next contract emit\".", "regenerate": "To regenerate, run: prisma-next contract emit" } -} +} \ No newline at end of file diff --git a/examples/prisma/test/e2e/README.md b/examples/prisma/test/e2e/README.md index cce2113bf..96f575a2a 100644 --- a/examples/prisma/test/e2e/README.md +++ b/examples/prisma/test/e2e/README.md @@ -20,7 +20,7 @@ The harness's Vitest global setup (`global-setup.ts`): 1. `docker compose up -d` and waits for `pg_isready`. 2. Sets `DATABASE_URL` to the harness's local Postgres URL. -3. Runs `prisma-next migration apply` against the example app (installs the cipherstash baseline migration + the `users` table). +3. Runs `prisma-next migrate` against the example app (installs the cipherstash baseline migration + the `users` table). 4. Skips cleanly (logging the missing env var) when `CS_WORKSPACE_CRN` / `CS_CLIENT_ID` / `CS_CLIENT_KEY` / `CS_DEFAULT_KEY_ID` are unset, so PRs without secrets configured don't fail the suite. `vitest.config.ts` wires the global setup, scopes the run to `*.e2e.test.ts`, and pins `pool: 'threads'` + `maxWorkers: 1` + `isolate: false` + `fileParallelism: false` so every test file shares one Postgres connection and one CipherStash SDK encryption client (and the SDK isn't asked to run encrypts across files concurrently). Each test file truncates `users` in its `beforeAll` for clean-slate isolation. diff --git a/examples/prisma/test/e2e/bigint.e2e.test.ts b/examples/prisma/test/e2e/bigint.e2e.test.ts index d2ea268ef..8d363c10c 100644 --- a/examples/prisma/test/e2e/bigint.e2e.test.ts +++ b/examples/prisma/test/e2e/bigint.e2e.test.ts @@ -57,11 +57,11 @@ describe('EncryptedBigInt e2e (live PG + EQL + ZeroKMS)', () => { beforeAll(async () => { await ensureConnected() await truncateUsers() - await Promise.all(SEED.map((s) => db.orm.User.create(seedRow(s)))) + await Promise.all(SEED.map((s) => db.orm.public.User.create(seedRow(s)))) }) it('round-trips an EncryptedBigInt through bulkEncrypt + bulkDecrypt', async () => { - const rows = await db.orm.User.all() + const rows = await db.orm.public.User.all() expect(rows).toHaveLength(SEED.length) await decryptAll(rows) const byId = new Map(rows.map((r) => [r.id, r] as const)) @@ -73,7 +73,7 @@ describe('EncryptedBigInt e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashGt filters by encrypted bigint numeric order', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.accountId.cipherstashGt(1_000_000_000_002n), ).all() expect(rows.map((r) => r.id).sort()).toEqual([ @@ -83,7 +83,7 @@ describe('EncryptedBigInt e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashLte includes the equality boundary', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.accountId.cipherstashLte(1_000_000_000_002n), ).all() expect(rows.map((r) => r.id).sort()).toEqual([ @@ -93,7 +93,7 @@ describe('EncryptedBigInt e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashBetween filters by encrypted bigint range', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.accountId.cipherstashBetween( 1_000_000_000_002n, 9_000_000_000_000_000n, @@ -106,7 +106,7 @@ describe('EncryptedBigInt e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashInArray returns rows whose value matches any of the supplied bigints', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.accountId.cipherstashInArray([ 1_000_000_000_001n, BigInt(Number.MAX_SAFE_INTEGER), @@ -119,7 +119,7 @@ describe('EncryptedBigInt e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashAsc orders by bigint value (bare-column ORDER BY)', async () => { - const rows = await db.orm.User.orderBy((u) => + const rows = await db.orm.public.User.orderBy((u) => cipherstashAsc(u.accountId), ).all() expect(rows.map((r) => r.id)).toEqual([ diff --git a/examples/prisma/test/e2e/bool.e2e.test.ts b/examples/prisma/test/e2e/bool.e2e.test.ts index 16602b313..9c7a77e11 100644 --- a/examples/prisma/test/e2e/bool.e2e.test.ts +++ b/examples/prisma/test/e2e/bool.e2e.test.ts @@ -49,11 +49,11 @@ describe('EncryptedBoolean e2e (live PG + EQL + ZeroKMS)', () => { beforeAll(async () => { await ensureConnected() await truncateUsers() - await Promise.all(SEED.map((s) => db.orm.User.create(seedRow(s)))) + await Promise.all(SEED.map((s) => db.orm.public.User.create(seedRow(s)))) }) it('round-trips an EncryptedBoolean through bulkEncrypt + bulkDecrypt', async () => { - const rows = await db.orm.User.all() + const rows = await db.orm.public.User.all() expect(rows).toHaveLength(SEED.length) await decryptAll(rows) const byId = new Map(rows.map((r) => [r.id, r] as const)) @@ -67,21 +67,21 @@ describe('EncryptedBoolean e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashInArray([true]) returns the verified subset', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.emailVerified.cipherstashInArray([true]), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-bool-0', 'e2e-bool-2']) }) it('cipherstashInArray([false]) returns the unverified subset', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.emailVerified.cipherstashInArray([false]), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-bool-1', 'e2e-bool-3']) }) it('cipherstashInArray([true, false]) returns the entire population', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.emailVerified.cipherstashInArray([true, false]), ).all() expect(rows.map((r) => r.id).sort()).toEqual([ @@ -93,7 +93,7 @@ describe('EncryptedBoolean e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashNe([true]) excludes the equality match', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.emailVerified.cipherstashNe(true), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-bool-1', 'e2e-bool-3']) diff --git a/examples/prisma/test/e2e/date.e2e.test.ts b/examples/prisma/test/e2e/date.e2e.test.ts index baee07635..b4aaf5b4e 100644 --- a/examples/prisma/test/e2e/date.e2e.test.ts +++ b/examples/prisma/test/e2e/date.e2e.test.ts @@ -49,11 +49,11 @@ describe('EncryptedDate e2e (live PG + EQL + ZeroKMS)', () => { beforeAll(async () => { await ensureConnected() await truncateUsers() - await Promise.all(SEED.map((s) => db.orm.User.create(seedRow(s)))) + await Promise.all(SEED.map((s) => db.orm.public.User.create(seedRow(s)))) }) it('round-trips an EncryptedDate through bulkEncrypt + bulkDecrypt', async () => { - const rows = await db.orm.User.all() + const rows = await db.orm.public.User.all() expect(rows).toHaveLength(SEED.length) await decryptAll(rows) const byId = new Map(rows.map((r) => [r.id, r] as const)) @@ -72,14 +72,14 @@ describe('EncryptedDate e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashGt filters dates after the cutoff', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.birthday.cipherstashGt(new Date('1995-01-01')), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-date-2', 'e2e-date-3']) }) it('cipherstashBetween filters a closed date interval', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.birthday.cipherstashBetween( new Date('1985-01-01'), new Date('2005-12-31'), @@ -89,7 +89,7 @@ describe('EncryptedDate e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashAsc orders by calendar date (bare-column ORDER BY)', async () => { - const rows = await db.orm.User.orderBy((u) => + const rows = await db.orm.public.User.orderBy((u) => cipherstashAsc(u.birthday), ).all() expect(rows.map((r) => r.id)).toEqual([ @@ -101,7 +101,7 @@ describe('EncryptedDate e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashDesc reverses the date order', async () => { - const rows = await db.orm.User.orderBy((u) => + const rows = await db.orm.public.User.orderBy((u) => cipherstashDesc(u.birthday), ).all() expect(rows.map((r) => r.id)).toEqual([ diff --git a/examples/prisma/test/e2e/global-setup.ts b/examples/prisma/test/e2e/global-setup.ts index 7d2d68bde..f6d0be8a4 100644 --- a/examples/prisma/test/e2e/global-setup.ts +++ b/examples/prisma/test/e2e/global-setup.ts @@ -113,7 +113,7 @@ export default async function setup(): Promise<() => Promise> { const apply = spawnSync( 'pnpm', - ['exec', 'prisma-next', 'migration', 'apply'], + ['exec', 'prisma-next', 'migrate', '--yes'], { cwd: exampleDir, stdio: 'pipe', @@ -123,7 +123,7 @@ export default async function setup(): Promise<() => Promise> { ) if (apply.error || apply.signal || apply.status !== 0) { throw new Error( - describeSpawnFailure('`prisma-next migration apply`', apply), + describeSpawnFailure('`prisma-next migrate`', apply), ) } diff --git a/examples/prisma/test/e2e/json.e2e.test.ts b/examples/prisma/test/e2e/json.e2e.test.ts index 2b23d6687..de62144dd 100644 --- a/examples/prisma/test/e2e/json.e2e.test.ts +++ b/examples/prisma/test/e2e/json.e2e.test.ts @@ -82,11 +82,11 @@ describe('EncryptedJson e2e (live PG + EQL + ZeroKMS)', () => { beforeAll(async () => { await ensureConnected() await truncateUsers() - await Promise.all(SEED.map((s) => db.orm.User.create(seedRow(s)))) + await Promise.all(SEED.map((s) => db.orm.public.User.create(seedRow(s)))) }) it('round-trips an EncryptedJson through bulkEncrypt + bulkDecrypt', async () => { - const rows = await db.orm.User.all() + const rows = await db.orm.public.User.all() expect(rows).toHaveLength(SEED.length) await decryptAll(rows) const byId = new Map(rows.map((r) => [r.id, r] as const)) @@ -98,7 +98,7 @@ describe('EncryptedJson e2e (live PG + EQL + ZeroKMS)', () => { }) it.skip('cipherstashJsonbPathExists filters by JSON path (KNOWN LIMITATION: needs client-side selector hashing)', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.preferences.cipherstashJsonbPathExists('$.locale'), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-json-0', 'e2e-json-1']) @@ -107,10 +107,10 @@ describe('EncryptedJson e2e (live PG + EQL + ZeroKMS)', () => { it('exposes cipherstashJsonbPathQueryFirst as a typed SELECT-expression helper', () => { // Type-level: the helper accepts an `Expression` and // returns an `Expression` typed as `cipherstash/json@1`. Wiring - // it into a `db.sql.users.select(...)` projection exercises the + // it into a `db.sql.public.users.select(...)` projection exercises the // typed surface; the live SQL execution is held back until the // STE-VEC selector hashing gap closes (see file docblock). - const projection = db.sql.users + const projection = db.sql.public.users .select((f) => ({ id: f.id, themeNode: cipherstashJsonbPathQueryFirst(f.preferences, '$.theme'), @@ -120,7 +120,7 @@ describe('EncryptedJson e2e (live PG + EQL + ZeroKMS)', () => { }) it('exposes cipherstashJsonbGet as a typed SELECT-expression helper', () => { - const projection = db.sql.users + const projection = db.sql.public.users .select((f) => ({ id: f.id, themeNode: cipherstashJsonbGet(f.preferences, 'theme'), diff --git a/examples/prisma/test/e2e/mixed.e2e.test.ts b/examples/prisma/test/e2e/mixed.e2e.test.ts index 05d711e73..aa386104b 100644 --- a/examples/prisma/test/e2e/mixed.e2e.test.ts +++ b/examples/prisma/test/e2e/mixed.e2e.test.ts @@ -155,7 +155,7 @@ describe('Mixed-codec e2e (live PG + EQL + ZeroKMS)', () => { }) runtime = (await db.connect({ url })) as { close(): Promise } await truncateUsers() - await Promise.all(SEED.map((s) => db.orm.User.create(seedRow(s)))) + await Promise.all(SEED.map((s) => db.orm.public.User.create(seedRow(s)))) counting.counts.reset() }) @@ -166,7 +166,7 @@ describe('Mixed-codec e2e (live PG + EQL + ZeroKMS)', () => { }) it('executes a four-column WHERE + ordered read end-to-end', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => and( u.email.cipherstashIlike('%@example.com'), u.salary.cipherstashGt(75_000), @@ -185,7 +185,7 @@ describe('Mixed-codec e2e (live PG + EQL + ZeroKMS)', () => { it('groups search-term encrypts: one bulkEncrypt per (table, column)', async () => { counting.counts.reset() - await db.orm.User.where((u) => + await db.orm.public.User.where((u) => and( u.email.cipherstashIlike('%@example.com'), u.salary.cipherstashGt(75_000), @@ -204,7 +204,7 @@ describe('Mixed-codec e2e (live PG + EQL + ZeroKMS)', () => { it('groups result decrypts: one bulkDecrypt per (table, column)', async () => { counting.counts.reset() - const rows = await db.orm.User.all() + const rows = await db.orm.public.User.all() await decryptAll(rows) // Six encrypted columns × N rows ⇒ exactly 6 `bulkDecrypt` calls // (one per `(users, )` group). diff --git a/examples/prisma/test/e2e/num.e2e.test.ts b/examples/prisma/test/e2e/num.e2e.test.ts index 1d989f833..6b22eb7b1 100644 --- a/examples/prisma/test/e2e/num.e2e.test.ts +++ b/examples/prisma/test/e2e/num.e2e.test.ts @@ -56,11 +56,11 @@ describe('EncryptedDouble e2e (live PG + EQL + ZeroKMS)', () => { beforeAll(async () => { await ensureConnected() await truncateUsers() - await Promise.all(SEED.map((s) => db.orm.User.create(seedRow(s)))) + await Promise.all(SEED.map((s) => db.orm.public.User.create(seedRow(s)))) }) it('round-trips an EncryptedDouble through bulkEncrypt + bulkDecrypt', async () => { - const rows = await db.orm.User.all() + const rows = await db.orm.public.User.all() expect(rows).toHaveLength(SEED.length) await decryptAll(rows) const byId = new Map(rows.map((r) => [r.id, r] as const)) @@ -72,14 +72,14 @@ describe('EncryptedDouble e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashGt filters by encrypted IEEE-754 numeric order', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.salary.cipherstashGt(95_000), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-num-2', 'e2e-num-3']) }) it('cipherstashGte includes the equality boundary', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.salary.cipherstashGte(95_000), ).all() expect(rows.map((r) => r.id).sort()).toEqual([ @@ -90,14 +90,14 @@ describe('EncryptedDouble e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashLt filters strict-less-than', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.salary.cipherstashLt(120_000), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-num-0', 'e2e-num-1']) }) it('cipherstashLte includes the equality boundary', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.salary.cipherstashLte(120_000), ).all() expect(rows.map((r) => r.id).sort()).toEqual([ @@ -108,14 +108,14 @@ describe('EncryptedDouble e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashBetween bounds inclusively on both sides', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.salary.cipherstashBetween(95_000, 120_000), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-num-1', 'e2e-num-2']) }) it('cipherstashAsc orders by numeric value via bare-column ORDER BY', async () => { - const rows = await db.orm.User.orderBy((u) => + const rows = await db.orm.public.User.orderBy((u) => cipherstashAsc(u.salary), ).all() expect(rows.map((r) => r.id)).toEqual([ @@ -127,7 +127,7 @@ describe('EncryptedDouble e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashDesc reverses the ascending order', async () => { - const rows = await db.orm.User.orderBy((u) => + const rows = await db.orm.public.User.orderBy((u) => cipherstashDesc(u.salary), ).all() expect(rows.map((r) => r.id)).toEqual([ diff --git a/examples/prisma/test/e2e/str-range.e2e.test.ts b/examples/prisma/test/e2e/str-range.e2e.test.ts index 33b64cdf4..c98bda626 100644 --- a/examples/prisma/test/e2e/str-range.e2e.test.ts +++ b/examples/prisma/test/e2e/str-range.e2e.test.ts @@ -51,11 +51,11 @@ describe('EncryptedString orderAndRange e2e (live PG + EQL + ZeroKMS)', () => { beforeAll(async () => { await ensureConnected() await truncateUsers() - await Promise.all(SEED.map((s) => db.orm.User.create(seedRow(s)))) + await Promise.all(SEED.map((s) => db.orm.public.User.create(seedRow(s)))) }) it('round-trips an EncryptedString through bulkEncrypt + bulkDecrypt', async () => { - const rows = await db.orm.User.all() + const rows = await db.orm.public.User.all() expect(rows).toHaveLength(SEED.length) await decryptAll(rows) const byId = new Map(rows.map((r) => [r.id, r] as const)) @@ -67,14 +67,14 @@ describe('EncryptedString orderAndRange e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashGt filters lexicographically', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.email.cipherstashGt('m'), ).all() expect(rows.map((r) => r.id).sort()).toEqual(['e2e-str-2', 'e2e-str-3']) }) it('cipherstashAsc orders alphabetically (bare-column ORDER BY on string)', async () => { - const rows = await db.orm.User.orderBy((u) => cipherstashAsc(u.email)).all() + const rows = await db.orm.public.User.orderBy((u) => cipherstashAsc(u.email)).all() expect(rows.map((r) => r.id)).toEqual([ 'e2e-str-0', 'e2e-str-1', @@ -84,7 +84,7 @@ describe('EncryptedString orderAndRange e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashDesc reverses the alphabetical order', async () => { - const rows = await db.orm.User.orderBy((u) => + const rows = await db.orm.public.User.orderBy((u) => cipherstashDesc(u.email), ).all() expect(rows.map((r) => r.id)).toEqual([ @@ -96,7 +96,7 @@ describe('EncryptedString orderAndRange e2e (live PG + EQL + ZeroKMS)', () => { }) it('cipherstashIlike coexists with order-and-range on the same column', async () => { - const rows = await db.orm.User.where((u) => + const rows = await db.orm.public.User.where((u) => u.email.cipherstashIlike('%@example.com'), ).all() expect(rows.map((r) => r.id).sort()).toEqual([ From 558fc7b30dc84584ad6891b0fafaad1776ad376b Mon Sep 17 00:00:00 2001 From: CJ Brewer Date: Thu, 9 Jul 2026 04:59:45 -0600 Subject: [PATCH 15/15] chore(prisma-example): device-flow auth support + collision-free Postgres port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - the e2e global-setup and the README-walkthrough test now accept the ~/.cipherstash profile written by 'stash auth login' (device-code flow stores device.json, not auth.json) — no .env with CS_* values is required for local runs; the stack client resolves the profile - move the bundled Postgres from port 5544 to 54338 (matching the 54329-style e2e harness port) — 5544 is a common enough choice to collide with other local containers, which cascaded into migrate/start failures in the walkthrough Both suites green on profile auth alone: example e2e 36 passed / 1 skipped; README walkthrough 7/7 steps exit 0 (including pnpm start). Co-Authored-By: Claude Fable 5 --- e2e/tests/prisma-example-readme.e2e.test.ts | 8 +++++++- examples/prisma/.env.example | 4 ++-- examples/prisma/README.md | 4 ++-- examples/prisma/docker-compose.yml | 6 +++--- examples/prisma/test/e2e/global-setup.ts | 18 +++++++++++++----- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/e2e/tests/prisma-example-readme.e2e.test.ts b/e2e/tests/prisma-example-readme.e2e.test.ts index 76ea909f6..119422bda 100644 --- a/e2e/tests/prisma-example-readme.e2e.test.ts +++ b/e2e/tests/prisma-example-readme.e2e.test.ts @@ -20,7 +20,13 @@ const authConfigured = (() => { if (process.env.CS_CLIENT_ID && process.env.CS_CLIENT_KEY) return true const home = process.env.HOME if (!home) return false - return existsSync(join(home, '.cipherstash', 'auth.json')) + // `stash auth login` stores either an `auth.json` (legacy PKCE flow) or a + // `device.json` (device-code flow) under the profile directory; both let + // the stack client authenticate without CS_* env vars. + return ( + existsSync(join(home, '.cipherstash', 'auth.json')) || + existsSync(join(home, '.cipherstash', 'device.json')) + ) })() interface StepResult { diff --git a/examples/prisma/.env.example b/examples/prisma/.env.example index 864423351..1180896ad 100644 --- a/examples/prisma/.env.example +++ b/examples/prisma/.env.example @@ -4,8 +4,8 @@ # # Defaults match the bundled `docker-compose.yml`. Run # `docker compose up -d` from this directory to start a Postgres on -# port 5544 with these credentials. -DATABASE_URL=postgres://postgres:postgres@localhost:5544/cipherstash_prisma_example +# port 54338 with these credentials. +DATABASE_URL=postgres://postgres:postgres@localhost:54338/cipherstash_prisma_example # CipherStash workspace credentials — **deployment only**. # diff --git a/examples/prisma/README.md b/examples/prisma/README.md index 67eafbfa1..6977e55d7 100644 --- a/examples/prisma/README.md +++ b/examples/prisma/README.md @@ -10,7 +10,7 @@ A single `User` model with one column per cipherstash codec (string, double, big | Path | Purpose | | -------------------------- | --------------------------------------------------------------------------------------------- | -| `docker-compose.yml` | Local Postgres 16 on port 5544. | +| `docker-compose.yml` | Local Postgres 16 on port 54338. | | `prisma/schema.prisma` | Application schema (one `User` model exercising all six cipherstash codecs). | | `prisma-next.config.ts` | Wires `cipherstash` into `extensionPacks`. | | `src/db.ts` | One-call setup via `cipherstashFromStack({ contractJson })`. | @@ -20,7 +20,7 @@ A single `User` model with one column per cipherstash codec (string, double, big ## Prerequisites -1. **Docker** for the bundled Postgres on port 5544 (or any Postgres 16+). +1. **Docker** for the bundled Postgres on port 54338 (or any Postgres 16+). 2. **A CipherStash workspace** — sign up at [cipherstash.com](https://cipherstash.com), then run `stash auth login` (PKCE; caches credentials in your OS keychain — no `CS_*` env vars needed in local dev). ## Run it diff --git a/examples/prisma/docker-compose.yml b/examples/prisma/docker-compose.yml index 49b0dd3b3..491c246b3 100644 --- a/examples/prisma/docker-compose.yml +++ b/examples/prisma/docker-compose.yml @@ -5,9 +5,9 @@ # docker compose down -v # stop + delete volume (fresh state) # # The DATABASE_URL in .env.example matches the values below: -# postgres://postgres:postgres@localhost:5544/cipherstash_prisma_example +# postgres://postgres:postgres@localhost:54338/cipherstash_prisma_example # -# Port 5544 (not 5432) is used to avoid colliding with any host-side +# Port 54338 (not 5432) is used to avoid colliding with any host-side # Postgres / other example containers. services: @@ -20,7 +20,7 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: cipherstash_prisma_example ports: - - "5544:5432" + - "54338:5432" volumes: - cipherstash-prisma-example-pg-data:/var/lib/postgresql/data healthcheck: diff --git a/examples/prisma/test/e2e/global-setup.ts b/examples/prisma/test/e2e/global-setup.ts index f6d0be8a4..3fb218342 100644 --- a/examples/prisma/test/e2e/global-setup.ts +++ b/examples/prisma/test/e2e/global-setup.ts @@ -27,9 +27,11 @@ */ import { type SpawnSyncReturns, spawnSync } from 'node:child_process' +import { existsSync } from 'node:fs' +import { homedir } from 'node:os' import { fileURLToPath } from 'node:url' import { config as loadDotenv } from 'dotenv' -import { dirname, resolve } from 'pathe' +import { dirname, join, resolve } from 'pathe' const HARNESS_DATABASE_URL = 'postgres://cipherstash:cipherstash@localhost:54329/cipherstash_e2e' @@ -73,11 +75,17 @@ export default async function setup(): Promise<() => Promise> { loadDotenv({ path: resolve(exampleDir, '.env') }) - if (!process.env['CS_WORKSPACE_CRN']) { + // Credentials come from either `.env` (`CS_*` variables) or the local + // CipherStash profile written by `stash auth login` (device-code flow; + // `~/.cipherstash`). The stack client resolves both — this gate only + // exists to fail fast with an actionable message when neither is present. + const hasProfile = existsSync(join(homedir(), '.cipherstash')) + if (!process.env['CS_WORKSPACE_CRN'] && !hasProfile) { throw new Error( - 'cipherstash e2e harness: `CS_WORKSPACE_CRN` is not set. Populate `.env` ' + - '(see `.env.example`) with a ZeroKMS workspace and the three companion ' + - 'credentials before running `pnpm test:e2e`.', + 'cipherstash e2e harness: no CipherStash credentials found. Either ' + + 'populate `.env` (see `.env.example`) with a ZeroKMS workspace and ' + + 'the three companion credentials, or log in once with ' + + '`stash auth login` (device-code flow) before running `pnpm test:e2e`.', ) }