@prisma-next/sql-runtime 0.3.0-dev.7 → 0.3.0-dev.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +137 -26
  3. package/dist/exports-BhZqJPVb.mjs +771 -0
  4. package/dist/exports-BhZqJPVb.mjs.map +1 -0
  5. package/dist/index-D59jqEKF.d.mts +159 -0
  6. package/dist/index-D59jqEKF.d.mts.map +1 -0
  7. package/dist/index.d.mts +2 -0
  8. package/dist/index.mjs +3 -0
  9. package/dist/test/utils.d.mts +82 -0
  10. package/dist/test/utils.d.mts.map +1 -0
  11. package/dist/test/utils.mjs +212 -0
  12. package/dist/test/utils.mjs.map +1 -0
  13. package/package.json +32 -25
  14. package/src/codecs/decoding.ts +84 -3
  15. package/src/codecs/encoding.ts +15 -2
  16. package/src/codecs/json-schema-validation.ts +61 -0
  17. package/src/exports/index.ts +14 -6
  18. package/src/lower-sql-plan.ts +8 -8
  19. package/src/plugins/lints.ts +204 -0
  20. package/src/sql-context.ts +385 -98
  21. package/src/sql-family-adapter.ts +9 -5
  22. package/src/sql-marker.ts +2 -2
  23. package/src/sql-runtime.ts +131 -31
  24. package/test/async-iterable-result.test.ts +42 -34
  25. package/test/context.types.test-d.ts +68 -0
  26. package/test/execution-stack.test.ts +166 -0
  27. package/test/json-schema-validation.test.ts +653 -0
  28. package/test/lints.test.ts +330 -0
  29. package/test/parameterized-types.test.ts +539 -0
  30. package/test/sql-context.test.ts +292 -117
  31. package/test/sql-family-adapter.test.ts +7 -6
  32. package/test/sql-runtime.test.ts +218 -30
  33. package/test/utils.ts +80 -51
  34. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js +0 -137863
  35. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js.map +0 -1
  36. package/dist/amcheck-24VY6X5V.js +0 -13
  37. package/dist/amcheck-24VY6X5V.js.map +0 -1
  38. package/dist/bloom-VS74NLHT.js +0 -13
  39. package/dist/bloom-VS74NLHT.js.map +0 -1
  40. package/dist/btree_gin-WBC4EAAI.js +0 -13
  41. package/dist/btree_gin-WBC4EAAI.js.map +0 -1
  42. package/dist/btree_gist-UNC6QD3M.js +0 -13
  43. package/dist/btree_gist-UNC6QD3M.js.map +0 -1
  44. package/dist/chunk-3KTOEDFX.js +0 -49
  45. package/dist/chunk-3KTOEDFX.js.map +0 -1
  46. package/dist/chunk-47DZBRQC.js +0 -1280
  47. package/dist/chunk-47DZBRQC.js.map +0 -1
  48. package/dist/chunk-52N6AFZM.js +0 -133
  49. package/dist/chunk-52N6AFZM.js.map +0 -1
  50. package/dist/chunk-7D4SUZUM.js +0 -38
  51. package/dist/chunk-7D4SUZUM.js.map +0 -1
  52. package/dist/chunk-C6I3V3DM.js +0 -455
  53. package/dist/chunk-C6I3V3DM.js.map +0 -1
  54. package/dist/chunk-ECWIHLAT.js +0 -37
  55. package/dist/chunk-ECWIHLAT.js.map +0 -1
  56. package/dist/chunk-EI626SDC.js +0 -105
  57. package/dist/chunk-EI626SDC.js.map +0 -1
  58. package/dist/chunk-UKKOYUGL.js +0 -578
  59. package/dist/chunk-UKKOYUGL.js.map +0 -1
  60. package/dist/chunk-XPLNMXQV.js +0 -1537
  61. package/dist/chunk-XPLNMXQV.js.map +0 -1
  62. package/dist/citext-T7MXGUY7.js +0 -13
  63. package/dist/citext-T7MXGUY7.js.map +0 -1
  64. package/dist/client-5FENX6AW.js +0 -299
  65. package/dist/client-5FENX6AW.js.map +0 -1
  66. package/dist/cube-TFDQBZCI.js +0 -13
  67. package/dist/cube-TFDQBZCI.js.map +0 -1
  68. package/dist/dict_int-AEUOPGWP.js +0 -13
  69. package/dist/dict_int-AEUOPGWP.js.map +0 -1
  70. package/dist/dict_xsyn-DAAYX3FL.js +0 -13
  71. package/dist/dict_xsyn-DAAYX3FL.js.map +0 -1
  72. package/dist/dist-AQ3LWXOX.js +0 -570
  73. package/dist/dist-AQ3LWXOX.js.map +0 -1
  74. package/dist/dist-LBVX6BJW.js +0 -189
  75. package/dist/dist-LBVX6BJW.js.map +0 -1
  76. package/dist/dist-WLKUVDN2.js +0 -5127
  77. package/dist/dist-WLKUVDN2.js.map +0 -1
  78. package/dist/earthdistance-KIGTF4LE.js +0 -13
  79. package/dist/earthdistance-KIGTF4LE.js.map +0 -1
  80. package/dist/file_fdw-5N55UP6I.js +0 -13
  81. package/dist/file_fdw-5N55UP6I.js.map +0 -1
  82. package/dist/fuzzystrmatch-KN3YWBFP.js +0 -13
  83. package/dist/fuzzystrmatch-KN3YWBFP.js.map +0 -1
  84. package/dist/hstore-YX726NKN.js +0 -13
  85. package/dist/hstore-YX726NKN.js.map +0 -1
  86. package/dist/http-exception-FZY2H4OF.js +0 -8
  87. package/dist/http-exception-FZY2H4OF.js.map +0 -1
  88. package/dist/index.js +0 -30
  89. package/dist/index.js.map +0 -1
  90. package/dist/intarray-NKVXNO2D.js +0 -13
  91. package/dist/intarray-NKVXNO2D.js.map +0 -1
  92. package/dist/isn-FTEMJGEV.js +0 -13
  93. package/dist/isn-FTEMJGEV.js.map +0 -1
  94. package/dist/lo-DB7L4NGI.js +0 -13
  95. package/dist/lo-DB7L4NGI.js.map +0 -1
  96. package/dist/logger-WQ7SHNDD.js +0 -68
  97. package/dist/logger-WQ7SHNDD.js.map +0 -1
  98. package/dist/ltree-Z32TZT6W.js +0 -13
  99. package/dist/ltree-Z32TZT6W.js.map +0 -1
  100. package/dist/nodefs-NM46ACH7.js +0 -31
  101. package/dist/nodefs-NM46ACH7.js.map +0 -1
  102. package/dist/opfs-ahp-NJO33LVZ.js +0 -332
  103. package/dist/opfs-ahp-NJO33LVZ.js.map +0 -1
  104. package/dist/pageinspect-YP3IZR4X.js +0 -13
  105. package/dist/pageinspect-YP3IZR4X.js.map +0 -1
  106. package/dist/pg_buffercache-7TD5J2FB.js +0 -13
  107. package/dist/pg_buffercache-7TD5J2FB.js.map +0 -1
  108. package/dist/pg_dump-SG4KYBUB.js +0 -2492
  109. package/dist/pg_dump-SG4KYBUB.js.map +0 -1
  110. package/dist/pg_freespacemap-DZDNCPZK.js +0 -13
  111. package/dist/pg_freespacemap-DZDNCPZK.js.map +0 -1
  112. package/dist/pg_surgery-J2MUEWEP.js +0 -13
  113. package/dist/pg_surgery-J2MUEWEP.js.map +0 -1
  114. package/dist/pg_trgm-7VNQOYS6.js +0 -13
  115. package/dist/pg_trgm-7VNQOYS6.js.map +0 -1
  116. package/dist/pg_visibility-TTSIPHFL.js +0 -13
  117. package/dist/pg_visibility-TTSIPHFL.js.map +0 -1
  118. package/dist/pg_walinspect-KPFHSHRJ.js +0 -13
  119. package/dist/pg_walinspect-KPFHSHRJ.js.map +0 -1
  120. package/dist/proxy-signals-GUDAMDHV.js +0 -39
  121. package/dist/proxy-signals-GUDAMDHV.js.map +0 -1
  122. package/dist/seg-IYVDLE4O.js +0 -13
  123. package/dist/seg-IYVDLE4O.js.map +0 -1
  124. package/dist/src/codecs/decoding.d.ts +0 -4
  125. package/dist/src/codecs/decoding.d.ts.map +0 -1
  126. package/dist/src/codecs/encoding.d.ts +0 -5
  127. package/dist/src/codecs/encoding.d.ts.map +0 -1
  128. package/dist/src/codecs/validation.d.ts +0 -6
  129. package/dist/src/codecs/validation.d.ts.map +0 -1
  130. package/dist/src/exports/index.d.ts +0 -11
  131. package/dist/src/exports/index.d.ts.map +0 -1
  132. package/dist/src/index.d.ts +0 -2
  133. package/dist/src/index.d.ts.map +0 -1
  134. package/dist/src/lower-sql-plan.d.ts +0 -15
  135. package/dist/src/lower-sql-plan.d.ts.map +0 -1
  136. package/dist/src/sql-context.d.ts +0 -65
  137. package/dist/src/sql-context.d.ts.map +0 -1
  138. package/dist/src/sql-family-adapter.d.ts +0 -10
  139. package/dist/src/sql-family-adapter.d.ts.map +0 -1
  140. package/dist/src/sql-marker.d.ts +0 -22
  141. package/dist/src/sql-marker.d.ts.map +0 -1
  142. package/dist/src/sql-runtime.d.ts +0 -25
  143. package/dist/src/sql-runtime.d.ts.map +0 -1
  144. package/dist/tablefunc-EF4RCS7S.js +0 -13
  145. package/dist/tablefunc-EF4RCS7S.js.map +0 -1
  146. package/dist/tcn-3VT5BQYW.js +0 -13
  147. package/dist/tcn-3VT5BQYW.js.map +0 -1
  148. package/dist/test/utils.d.ts +0 -59
  149. package/dist/test/utils.d.ts.map +0 -1
  150. package/dist/test/utils.js +0 -24634
  151. package/dist/test/utils.js.map +0 -1
  152. package/dist/tiny-CW6F4GX6.js +0 -10
  153. package/dist/tiny-CW6F4GX6.js.map +0 -1
  154. package/dist/tsm_system_rows-ES7KNUQH.js +0 -13
  155. package/dist/tsm_system_rows-ES7KNUQH.js.map +0 -1
  156. package/dist/tsm_system_time-76WEIMBG.js +0 -13
  157. package/dist/tsm_system_time-76WEIMBG.js.map +0 -1
  158. package/dist/unaccent-7RYF3R64.js +0 -13
  159. package/dist/unaccent-7RYF3R64.js.map +0 -1
  160. package/dist/utility-Q5A254LJ-J4HTKZPT.js +0 -347
  161. package/dist/utility-Q5A254LJ-J4HTKZPT.js.map +0 -1
  162. package/dist/uuid_ossp-4ETE4FPE.js +0 -13
  163. package/dist/uuid_ossp-4ETE4FPE.js.map +0 -1
  164. package/dist/vector-74GPNV7V.js +0 -13
  165. package/dist/vector-74GPNV7V.js.map +0 -1
  166. package/src/index.ts +0 -1
@@ -0,0 +1,212 @@
1
+ import { c as createSqlExecutionStack, i as ensureTableStatement, o as writeContractMarker, r as ensureSchemaStatement, s as createExecutionContext } from "../exports-BhZqJPVb.mjs";
2
+ import { instantiateExecutionStack } from "@prisma-next/core-execution-plane/stack";
3
+ import { codec, createCodecRegistry } from "@prisma-next/sql-relational-core/ast";
4
+ import { coreHash, profileHash } from "@prisma-next/contract/types";
5
+ import { collectAsync, collectAsync as collectAsync$1, createDevDatabase, drainAsyncIterable, teardownTestDatabase, withClient } from "@prisma-next/test-utils";
6
+
7
+ //#region test/utils.ts
8
+ /**
9
+ * Executes a plan and collects all results into an array.
10
+ * This helper DRYs up the common pattern of executing plans in tests.
11
+ * The return type is inferred from the plan's type parameter.
12
+ */
13
+ async function executePlanAndCollect(runtime, plan) {
14
+ return collectAsync$1(runtime.execute(plan));
15
+ }
16
+ /**
17
+ * Drains a plan execution, consuming all results without collecting them.
18
+ * Useful for testing side effects without memory overhead.
19
+ */
20
+ async function drainPlanExecution(runtime, plan) {
21
+ return drainAsyncIterable(runtime.execute(plan));
22
+ }
23
+ /**
24
+ * Executes a SQL statement on a database client.
25
+ */
26
+ async function executeStatement(client, statement) {
27
+ if (statement.params.length > 0) {
28
+ await client.query(statement.sql, [...statement.params]);
29
+ return;
30
+ }
31
+ await client.query(statement.sql);
32
+ }
33
+ /**
34
+ * Sets up database schema and data, then writes the contract marker.
35
+ * This helper DRYs up the common pattern of database setup in tests.
36
+ */
37
+ async function setupTestDatabase(client, contract, setupFn) {
38
+ await client.query("drop schema if exists prisma_contract cascade");
39
+ await client.query("create schema if not exists public");
40
+ await setupFn(client);
41
+ await executeStatement(client, ensureSchemaStatement);
42
+ await executeStatement(client, ensureTableStatement);
43
+ await executeStatement(client, writeContractMarker({
44
+ storageHash: contract.storageHash,
45
+ profileHash: contract.profileHash ?? contract.storageHash,
46
+ contractJson: contract,
47
+ canonicalVersion: 1
48
+ }).insert);
49
+ }
50
+ /**
51
+ * Writes a contract marker to the database.
52
+ * This helper DRYs up the common pattern of writing contract markers in tests.
53
+ */
54
+ async function writeTestContractMarker(client, contract) {
55
+ await executeStatement(client, writeContractMarker({
56
+ storageHash: contract.storageHash,
57
+ profileHash: contract.profileHash ?? contract.storageHash,
58
+ contractJson: contract,
59
+ canonicalVersion: 1
60
+ }).insert);
61
+ }
62
+ /**
63
+ * Creates a test adapter descriptor from a raw adapter.
64
+ * Wraps the adapter in an SqlRuntimeAdapterDescriptor with static contributions
65
+ * derived from the adapter's codec registry.
66
+ */
67
+ function createTestAdapterDescriptor(adapter) {
68
+ const codecRegistry = adapter.profile.codecs();
69
+ return {
70
+ kind: "adapter",
71
+ id: "test-adapter",
72
+ version: "0.0.1",
73
+ familyId: "sql",
74
+ targetId: "postgres",
75
+ codecs: () => codecRegistry,
76
+ operationSignatures: () => [],
77
+ parameterizedCodecs: () => [],
78
+ create() {
79
+ return Object.assign({
80
+ familyId: "sql",
81
+ targetId: "postgres"
82
+ }, adapter);
83
+ }
84
+ };
85
+ }
86
+ /**
87
+ * Creates a test target descriptor with empty static contributions.
88
+ */
89
+ function createTestTargetDescriptor() {
90
+ return {
91
+ kind: "target",
92
+ id: "postgres",
93
+ version: "0.0.1",
94
+ familyId: "sql",
95
+ targetId: "postgres",
96
+ codecs: () => createCodecRegistry(),
97
+ operationSignatures: () => [],
98
+ parameterizedCodecs: () => [],
99
+ create() {
100
+ return {
101
+ familyId: "sql",
102
+ targetId: "postgres"
103
+ };
104
+ }
105
+ };
106
+ }
107
+ /**
108
+ * Creates an ExecutionContext for testing.
109
+ * This helper DRYs up the common pattern of context creation in tests.
110
+ *
111
+ * Accepts a raw adapter and optional extension descriptors, wrapping the
112
+ * adapter in a descriptor internally for descriptor-first context creation.
113
+ */
114
+ function createTestContext(contract, adapter, options) {
115
+ return createExecutionContext({
116
+ contract,
117
+ stack: {
118
+ target: createTestTargetDescriptor(),
119
+ adapter: createTestAdapterDescriptor(adapter),
120
+ extensionPacks: options?.extensionPacks ?? []
121
+ }
122
+ });
123
+ }
124
+ function createTestStackInstance(options) {
125
+ return instantiateExecutionStack(createSqlExecutionStack({
126
+ target: createTestTargetDescriptor(),
127
+ adapter: createTestAdapterDescriptor(createStubAdapter()),
128
+ driver: options?.driver,
129
+ extensionPacks: options?.extensionPacks ?? []
130
+ }));
131
+ }
132
+ /**
133
+ * Creates a stub adapter for testing.
134
+ * This helper DRYs up the common pattern of adapter creation in tests.
135
+ *
136
+ * The stub adapter includes simple codecs for common test types (pg/int4@1, pg/text@1, pg/timestamptz@1)
137
+ * to enable type inference in tests without requiring the postgres adapter package.
138
+ */
139
+ function createStubAdapter() {
140
+ const codecRegistry = createCodecRegistry();
141
+ codecRegistry.register(codec({
142
+ typeId: "pg/int4@1",
143
+ targetTypes: ["int4"],
144
+ encode: (value) => value,
145
+ decode: (wire) => wire
146
+ }));
147
+ codecRegistry.register(codec({
148
+ typeId: "pg/text@1",
149
+ targetTypes: ["text"],
150
+ encode: (value) => value,
151
+ decode: (wire) => wire
152
+ }));
153
+ codecRegistry.register(codec({
154
+ typeId: "pg/timestamptz@1",
155
+ targetTypes: ["timestamptz"],
156
+ encode: (value) => value instanceof Date ? value.toISOString() : value,
157
+ decode: (wire) => wire instanceof Date ? wire : new Date(wire)
158
+ }));
159
+ return {
160
+ profile: {
161
+ id: "stub-profile",
162
+ target: "postgres",
163
+ capabilities: {},
164
+ codecs() {
165
+ return codecRegistry;
166
+ }
167
+ },
168
+ lower(ast, ctx) {
169
+ const sqlText = JSON.stringify(ast);
170
+ return {
171
+ profileId: this.profile.id,
172
+ body: Object.freeze({
173
+ sql: sqlText,
174
+ params: ctx.params ? [...ctx.params] : []
175
+ })
176
+ };
177
+ }
178
+ };
179
+ }
180
+ /**
181
+ * Creates a valid test contract without using validateContract.
182
+ * Ensures all required fields are present (mappings, capabilities, extensionPacks, meta, sources)
183
+ * and returns the contract with proper typing.
184
+ * This helper allows tests to create contracts without depending on sql-query.
185
+ */
186
+ function createTestContract(contract) {
187
+ const { execution, ...rest } = contract;
188
+ return {
189
+ ...rest,
190
+ schemaVersion: rest.schemaVersion ?? "1",
191
+ target: rest.target ?? "postgres",
192
+ targetFamily: rest.targetFamily ?? "sql",
193
+ storage: rest.storage ?? { tables: {} },
194
+ models: rest.models ?? {},
195
+ relations: rest.relations ?? {},
196
+ mappings: rest.mappings ?? {
197
+ codecTypes: {},
198
+ operationTypes: {}
199
+ },
200
+ capabilities: rest.capabilities ?? {},
201
+ extensionPacks: rest.extensionPacks ?? {},
202
+ meta: rest.meta ?? {},
203
+ sources: rest.sources ?? {},
204
+ ...execution ? { execution } : {},
205
+ storageHash: coreHash(rest.storageHash ?? "sha256:testcore"),
206
+ profileHash: profileHash(rest.profileHash ?? "sha256:testprofile")
207
+ };
208
+ }
209
+
210
+ //#endregion
211
+ export { collectAsync, createDevDatabase, createStubAdapter, createTestAdapterDescriptor, createTestContext, createTestContract, createTestStackInstance, createTestTargetDescriptor, drainPlanExecution, executePlanAndCollect, executeStatement, setupTestDatabase, teardownTestDatabase, withClient, writeTestContractMarker };
212
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.mjs","names":["collectAsync"],"sources":["../../test/utils.ts"],"sourcesContent":["import type { ExecutionPlan, ResultType } from '@prisma-next/contract/types';\nimport { coreHash, profileHash } from '@prisma-next/contract/types';\nimport { instantiateExecutionStack } from '@prisma-next/core-execution-plane/stack';\nimport type { RuntimeDriverDescriptor } from '@prisma-next/core-execution-plane/types';\nimport type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { Adapter, LoweredStatement, SelectAst } from '@prisma-next/sql-relational-core/ast';\nimport { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';\nimport type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';\nimport { collectAsync, drainAsyncIterable } from '@prisma-next/test-utils';\nimport type { Client } from 'pg';\nimport type { SqlStatement } from '../src/exports';\nimport {\n createExecutionContext,\n type createRuntime,\n createSqlExecutionStack,\n ensureSchemaStatement,\n ensureTableStatement,\n writeContractMarker,\n} from '../src/exports';\nimport type {\n ExecutionContext,\n SqlRuntimeAdapterDescriptor,\n SqlRuntimeAdapterInstance,\n SqlRuntimeDriverInstance,\n SqlRuntimeExtensionDescriptor,\n SqlRuntimeTargetDescriptor,\n} from '../src/sql-context';\n\n/**\n * Executes a plan and collects all results into an array.\n * This helper DRYs up the common pattern of executing plans in tests.\n * The return type is inferred from the plan's type parameter.\n */\nexport async function executePlanAndCollect<\n P extends ExecutionPlan<ResultType<P>> | SqlQueryPlan<ResultType<P>>,\n>(runtime: ReturnType<typeof createRuntime>, plan: P): Promise<ResultType<P>[]> {\n type Row = ResultType<P>;\n return collectAsync<Row>(runtime.execute<Row>(plan));\n}\n\n/**\n * Drains a plan execution, consuming all results without collecting them.\n * Useful for testing side effects without memory overhead.\n */\nexport async function drainPlanExecution(\n runtime: ReturnType<typeof createRuntime>,\n plan: ExecutionPlan | SqlQueryPlan<unknown>,\n): Promise<void> {\n return drainAsyncIterable(runtime.execute(plan));\n}\n\n/**\n * Executes a SQL statement on a database client.\n */\nexport async function executeStatement(client: Client, statement: SqlStatement): Promise<void> {\n if (statement.params.length > 0) {\n await client.query(statement.sql, [...statement.params]);\n return;\n }\n\n await client.query(statement.sql);\n}\n\n/**\n * Sets up database schema and data, then writes the contract marker.\n * This helper DRYs up the common pattern of database setup in tests.\n */\nexport async function setupTestDatabase(\n client: Client,\n contract: SqlContract<SqlStorage>,\n setupFn: (client: Client) => Promise<void>,\n): Promise<void> {\n await client.query('drop schema if exists prisma_contract cascade');\n await client.query('create schema if not exists public');\n\n await setupFn(client);\n\n await executeStatement(client, ensureSchemaStatement);\n await executeStatement(client, ensureTableStatement);\n const write = writeContractMarker({\n storageHash: contract.storageHash,\n profileHash: contract.profileHash ?? contract.storageHash,\n contractJson: contract,\n canonicalVersion: 1,\n });\n await executeStatement(client, write.insert);\n}\n\n/**\n * Writes a contract marker to the database.\n * This helper DRYs up the common pattern of writing contract markers in tests.\n */\nexport async function writeTestContractMarker(\n client: Client,\n contract: SqlContract<SqlStorage>,\n): Promise<void> {\n const write = writeContractMarker({\n storageHash: contract.storageHash,\n profileHash: contract.profileHash ?? contract.storageHash,\n contractJson: contract,\n canonicalVersion: 1,\n });\n await executeStatement(client, write.insert);\n}\n\n/**\n * Creates a test adapter descriptor from a raw adapter.\n * Wraps the adapter in an SqlRuntimeAdapterDescriptor with static contributions\n * derived from the adapter's codec registry.\n */\nexport function createTestAdapterDescriptor(\n adapter: Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,\n): SqlRuntimeAdapterDescriptor<'postgres'> {\n const codecRegistry = adapter.profile.codecs();\n return {\n kind: 'adapter' as const,\n id: 'test-adapter',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => codecRegistry,\n operationSignatures: () => [],\n parameterizedCodecs: () => [],\n create(): SqlRuntimeAdapterInstance<'postgres'> {\n return Object.assign({ familyId: 'sql' as const, targetId: 'postgres' as const }, adapter);\n },\n };\n}\n\n/**\n * Creates a test target descriptor with empty static contributions.\n */\nexport function createTestTargetDescriptor(): SqlRuntimeTargetDescriptor<'postgres'> {\n return {\n kind: 'target' as const,\n id: 'postgres',\n version: '0.0.1',\n familyId: 'sql' as const,\n targetId: 'postgres' as const,\n codecs: () => createCodecRegistry(),\n operationSignatures: () => [],\n parameterizedCodecs: () => [],\n create() {\n return { familyId: 'sql' as const, targetId: 'postgres' as const };\n },\n };\n}\n\n/**\n * Creates an ExecutionContext for testing.\n * This helper DRYs up the common pattern of context creation in tests.\n *\n * Accepts a raw adapter and optional extension descriptors, wrapping the\n * adapter in a descriptor internally for descriptor-first context creation.\n */\nexport function createTestContext<TContract extends SqlContract<SqlStorage>>(\n contract: TContract,\n adapter: Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,\n options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n },\n): ExecutionContext<TContract> {\n return createExecutionContext({\n contract,\n stack: {\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(adapter),\n extensionPacks: options?.extensionPacks ?? [],\n },\n });\n}\n\nexport function createTestStackInstance(options?: {\n extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;\n driver?: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n unknown,\n SqlRuntimeDriverInstance<'postgres'>\n >;\n}) {\n const stack = createSqlExecutionStack({\n target: createTestTargetDescriptor(),\n adapter: createTestAdapterDescriptor(createStubAdapter()),\n driver: options?.driver,\n extensionPacks: options?.extensionPacks ?? [],\n });\n\n return instantiateExecutionStack(stack);\n}\n\n/**\n * Creates a stub adapter for testing.\n * This helper DRYs up the common pattern of adapter creation in tests.\n *\n * The stub adapter includes simple codecs for common test types (pg/int4@1, pg/text@1, pg/timestamptz@1)\n * to enable type inference in tests without requiring the postgres adapter package.\n */\nexport function createStubAdapter(): Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement> {\n const codecRegistry = createCodecRegistry();\n\n // Register stub codecs for common test types\n // These match the codec IDs used in test contracts (pg/int4@1, pg/text@1, pg/timestamptz@1)\n // but don't require importing from the postgres adapter package\n codecRegistry.register(\n codec({\n typeId: 'pg/int4@1',\n targetTypes: ['int4'],\n encode: (value: number) => value,\n decode: (wire: number) => wire,\n }),\n );\n\n codecRegistry.register(\n codec({\n typeId: 'pg/text@1',\n targetTypes: ['text'],\n encode: (value: string) => value,\n decode: (wire: string) => wire,\n }),\n );\n\n codecRegistry.register(\n codec({\n typeId: 'pg/timestamptz@1',\n targetTypes: ['timestamptz'],\n encode: (value: string | Date) => (value instanceof Date ? value.toISOString() : value),\n decode: (wire: string | Date) => (wire instanceof Date ? wire : new Date(wire)),\n }),\n );\n\n return {\n profile: {\n id: 'stub-profile',\n target: 'postgres',\n capabilities: {},\n codecs() {\n return codecRegistry;\n },\n },\n lower(ast: SelectAst, ctx: { contract: SqlContract<SqlStorage>; params?: readonly unknown[] }) {\n const sqlText = JSON.stringify(ast);\n return {\n profileId: this.profile.id,\n body: Object.freeze({ sql: sqlText, params: ctx.params ? [...ctx.params] : [] }),\n };\n },\n };\n}\n\n/**\n * Creates a valid test contract without using validateContract.\n * Ensures all required fields are present (mappings, capabilities, extensionPacks, meta, sources)\n * and returns the contract with proper typing.\n * This helper allows tests to create contracts without depending on sql-query.\n */\nexport function createTestContract(\n contract: Partial<Omit<SqlContract<SqlStorage>, 'storageHash' | 'profileHash'>> & {\n storageHash?: string;\n profileHash?: string;\n },\n): SqlContract<SqlStorage> {\n const { execution, ...rest } = contract;\n\n return {\n ...rest,\n schemaVersion: rest.schemaVersion ?? '1',\n target: rest.target ?? 'postgres',\n targetFamily: rest.targetFamily ?? 'sql',\n storage: rest.storage ?? { tables: {} },\n models: rest.models ?? {},\n relations: rest.relations ?? {},\n mappings: rest.mappings ?? { codecTypes: {}, operationTypes: {} },\n capabilities: rest.capabilities ?? {},\n extensionPacks: rest.extensionPacks ?? {},\n meta: rest.meta ?? {},\n sources: rest.sources ?? {},\n ...(execution ? { execution } : {}),\n storageHash: coreHash(rest.storageHash ?? 'sha256:testcore'),\n profileHash: profileHash(rest.profileHash ?? 'sha256:testprofile'),\n } satisfies SqlContract<SqlStorage>;\n}\n\n// Re-export generic utilities from test-utils\nexport {\n collectAsync,\n createDevDatabase,\n type DevDatabase,\n teardownTestDatabase,\n withClient,\n} from '@prisma-next/test-utils';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,26 +1,30 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-runtime",
3
- "version": "0.3.0-dev.7",
3
+ "version": "0.3.0-dev.70",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "SQL runtime implementation for Prisma Next",
7
7
  "dependencies": {
8
- "@prisma-next/contract": "0.3.0-dev.7",
9
- "@prisma-next/core-execution-plane": "0.3.0-dev.7",
10
- "@prisma-next/operations": "0.3.0-dev.7",
11
- "@prisma-next/runtime-executor": "0.3.0-dev.7",
12
- "@prisma-next/sql-operations": "0.3.0-dev.7",
13
- "@prisma-next/sql-relational-core": "0.3.0-dev.7",
14
- "@prisma-next/sql-contract": "0.3.0-dev.7"
8
+ "arktype": "^2.1.26",
9
+ "@prisma-next/utils": "0.3.0-dev.70",
10
+ "@prisma-next/ids": "0.3.0-dev.70",
11
+ "@prisma-next/operations": "0.3.0-dev.70",
12
+ "@prisma-next/sql-contract": "0.3.0-dev.70",
13
+ "@prisma-next/core-execution-plane": "0.3.0-dev.70",
14
+ "@prisma-next/runtime-executor": "0.3.0-dev.70",
15
+ "@prisma-next/contract": "0.3.0-dev.70",
16
+ "@prisma-next/sql-relational-core": "0.3.0-dev.70",
17
+ "@prisma-next/sql-operations": "0.3.0-dev.70"
15
18
  },
16
19
  "devDependencies": {
17
20
  "@types/pg": "8.16.0",
18
- "@vitest/coverage-v8": "4.0.16",
19
21
  "pg": "8.16.3",
20
- "tsup": "8.5.1",
22
+ "tsdown": "0.18.4",
21
23
  "typescript": "5.9.3",
22
- "vitest": "4.0.16",
23
- "@prisma-next/test-utils": "0.0.1"
24
+ "vitest": "4.0.17",
25
+ "@prisma-next/test-utils": "0.0.1",
26
+ "@prisma-next/tsdown": "0.0.0",
27
+ "@prisma-next/tsconfig": "0.0.0"
24
28
  },
25
29
  "files": [
26
30
  "dist",
@@ -28,23 +32,26 @@
28
32
  "test"
29
33
  ],
30
34
  "exports": {
31
- ".": {
32
- "types": "./dist/src/index.d.ts",
33
- "import": "./dist/index.js"
34
- },
35
- "./test/utils": {
36
- "types": "./dist/test/utils.d.ts",
37
- "import": "./dist/test/utils.js"
38
- }
35
+ ".": "./dist/index.mjs",
36
+ "./test/utils": "./dist/test/utils.mjs",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "main": "./dist/index.mjs",
40
+ "module": "./dist/index.mjs",
41
+ "types": "./dist/index.d.mts",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/prisma/prisma-next.git",
45
+ "directory": "packages/2-sql/5-runtime"
39
46
  },
40
47
  "scripts": {
41
- "build": "tsup --config tsup.config.ts && tsc --project tsconfig.build.json",
48
+ "build": "tsdown",
42
49
  "test": "vitest run --passWithNoTests",
43
50
  "test:coverage": "vitest run --coverage --passWithNoTests",
44
51
  "typecheck": "tsc --project tsconfig.json --noEmit",
45
- "lint": "biome check . --config-path ../../../biome.json --error-on-warnings",
46
- "lint:fix": "biome check --write . --config-path ../../../biome.json",
47
- "lint:fix:unsafe": "biome check --write --unsafe . --config-path ../../../biome.json",
48
- "clean": "node ../../../scripts/clean.mjs"
52
+ "lint": "biome check . --error-on-warnings",
53
+ "lint:fix": "biome check --write .",
54
+ "lint:fix:unsafe": "biome check --write --unsafe .",
55
+ "clean": "rm -rf dist dist-tsc dist-tsc-prod coverage .tmp-output"
49
56
  }
50
57
  }
@@ -1,5 +1,7 @@
1
1
  import type { ExecutionPlan } from '@prisma-next/contract/types';
2
2
  import type { Codec, CodecRegistry } from '@prisma-next/sql-relational-core/ast';
3
+ import type { JsonSchemaValidatorRegistry } from '@prisma-next/sql-relational-core/query-lane-context';
4
+ import { validateJsonValue } from './json-schema-validation';
3
5
 
4
6
  function resolveRowCodec(
5
7
  alias: string,
@@ -27,15 +29,69 @@ function resolveRowCodec(
27
29
  return null;
28
30
  }
29
31
 
32
+ type ColumnRefIndex = Map<string, { table: string; column: string }>;
33
+
34
+ /**
35
+ * Builds a lookup index from column name → { table, column } ref.
36
+ * Called once per decodeRow invocation to avoid O(aliases × refs) linear scans.
37
+ */
38
+ function buildColumnRefIndex(plan: ExecutionPlan): ColumnRefIndex | null {
39
+ const columns = plan.meta.refs?.columns;
40
+ if (!columns) return null;
41
+
42
+ const index: ColumnRefIndex = new Map();
43
+ for (const ref of columns) {
44
+ index.set(ref.column, ref);
45
+ }
46
+ return index;
47
+ }
48
+
49
+ function parseProjectionRef(value: string): { table: string; column: string } | null {
50
+ if (value.startsWith('include:') || value.startsWith('operation:')) {
51
+ return null;
52
+ }
53
+
54
+ const separatorIndex = value.indexOf('.');
55
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
56
+ return null;
57
+ }
58
+
59
+ return {
60
+ table: value.slice(0, separatorIndex),
61
+ column: value.slice(separatorIndex + 1),
62
+ };
63
+ }
64
+
65
+ function resolveColumnRefForAlias(
66
+ alias: string,
67
+ projection: ExecutionPlan['meta']['projection'],
68
+ fallbackColumnRefIndex: ColumnRefIndex | null,
69
+ ): { table: string; column: string } | undefined {
70
+ if (projection && !Array.isArray(projection)) {
71
+ const mappedRef = (projection as Record<string, string>)[alias];
72
+ if (typeof mappedRef !== 'string') {
73
+ return undefined;
74
+ }
75
+ return parseProjectionRef(mappedRef) ?? undefined;
76
+ }
77
+
78
+ return fallbackColumnRefIndex?.get(alias);
79
+ }
80
+
30
81
  export function decodeRow(
31
82
  row: Record<string, unknown>,
32
83
  plan: ExecutionPlan,
33
84
  registry: CodecRegistry,
85
+ jsonValidators?: JsonSchemaValidatorRegistry,
34
86
  ): Record<string, unknown> {
35
87
  const decoded: Record<string, unknown> = {};
88
+ const projection = plan.meta.projection;
89
+
90
+ // Fallback for plans that do not provide projection alias -> table.column mapping.
91
+ const fallbackColumnRefIndex =
92
+ jsonValidators && (!projection || Array.isArray(projection)) ? buildColumnRefIndex(plan) : null;
36
93
 
37
94
  let aliases: readonly string[];
38
- const projection = plan.meta.projection;
39
95
  if (projection && !Array.isArray(projection)) {
40
96
  aliases = Object.keys(projection);
41
97
  } else if (projection && Array.isArray(projection)) {
@@ -47,7 +103,6 @@ export function decodeRow(
47
103
  for (const alias of aliases) {
48
104
  const wireValue = row[alias];
49
105
 
50
- const projection = plan.meta.projection;
51
106
  const projectionValue =
52
107
  projection && typeof projection === 'object' && !Array.isArray(projection)
53
108
  ? (projection as Record<string, string>)[alias]
@@ -111,8 +166,34 @@ export function decodeRow(
111
166
  }
112
167
 
113
168
  try {
114
- decoded[alias] = codec.decode(wireValue);
169
+ const decodedValue = codec.decode(wireValue);
170
+
171
+ // Validate decoded JSON value against schema
172
+ if (jsonValidators) {
173
+ const ref = resolveColumnRefForAlias(alias, projection, fallbackColumnRefIndex);
174
+ if (ref) {
175
+ validateJsonValue(
176
+ jsonValidators,
177
+ ref.table,
178
+ ref.column,
179
+ decodedValue,
180
+ 'decode',
181
+ codec.id,
182
+ );
183
+ }
184
+ }
185
+
186
+ decoded[alias] = decodedValue;
115
187
  } catch (error) {
188
+ // Re-throw JSON schema validation errors as-is
189
+ if (
190
+ error instanceof Error &&
191
+ 'code' in error &&
192
+ (error as Error & { code: string }).code === 'RUNTIME.JSON_SCHEMA_VALIDATION_FAILED'
193
+ ) {
194
+ throw error;
195
+ }
196
+
116
197
  const decodeError = new Error(
117
198
  `Failed to decode row alias '${alias}' with codec '${codec.id}': ${error instanceof Error ? error.message : String(error)}`,
118
199
  ) as Error & {
@@ -1,5 +1,7 @@
1
1
  import type { ExecutionPlan, ParamDescriptor } from '@prisma-next/contract/types';
2
2
  import type { Codec, CodecRegistry } from '@prisma-next/sql-relational-core/ast';
3
+ import type { JsonSchemaValidatorRegistry } from '@prisma-next/sql-relational-core/query-lane-context';
4
+ import { validateJsonValue } from './json-schema-validation';
3
5
 
4
6
  function resolveParamCodec(
5
7
  paramDescriptor: ParamDescriptor,
@@ -31,11 +33,18 @@ export function encodeParam(
31
33
  paramDescriptor: ParamDescriptor,
32
34
  plan: ExecutionPlan,
33
35
  registry: CodecRegistry,
36
+ jsonValidators?: JsonSchemaValidatorRegistry,
34
37
  ): unknown {
35
38
  if (value === null || value === undefined) {
36
39
  return null;
37
40
  }
38
41
 
42
+ // Validate JSON value against schema before encoding
43
+ if (jsonValidators && paramDescriptor.refs) {
44
+ const { table, column } = paramDescriptor.refs;
45
+ validateJsonValue(jsonValidators, table, column, value, 'encode', paramDescriptor.codecId);
46
+ }
47
+
39
48
  const codec = resolveParamCodec(paramDescriptor, plan, registry);
40
49
  if (!codec) {
41
50
  return value;
@@ -54,7 +63,11 @@ export function encodeParam(
54
63
  return value;
55
64
  }
56
65
 
57
- export function encodeParams(plan: ExecutionPlan, registry: CodecRegistry): readonly unknown[] {
66
+ export function encodeParams(
67
+ plan: ExecutionPlan,
68
+ registry: CodecRegistry,
69
+ jsonValidators?: JsonSchemaValidatorRegistry,
70
+ ): readonly unknown[] {
58
71
  if (plan.params.length === 0) {
59
72
  return plan.params;
60
73
  }
@@ -66,7 +79,7 @@ export function encodeParams(plan: ExecutionPlan, registry: CodecRegistry): read
66
79
  const paramDescriptor = plan.meta.paramDescriptors[i];
67
80
 
68
81
  if (paramDescriptor) {
69
- encoded.push(encodeParam(paramValue, paramDescriptor, plan, registry));
82
+ encoded.push(encodeParam(paramValue, paramDescriptor, plan, registry, jsonValidators));
70
83
  } else {
71
84
  encoded.push(paramValue);
72
85
  }
@@ -0,0 +1,61 @@
1
+ import { runtimeError } from '@prisma-next/runtime-executor';
2
+ import type {
3
+ JsonSchemaValidationError,
4
+ JsonSchemaValidatorRegistry,
5
+ } from '@prisma-next/sql-relational-core/query-lane-context';
6
+
7
+ /**
8
+ * Validates a JSON value against its column's JSON Schema, if a validator exists.
9
+ *
10
+ * Throws `RUNTIME.JSON_SCHEMA_VALIDATION_FAILED` on validation failure.
11
+ * No-ops if no validator is registered for the column.
12
+ */
13
+ export function validateJsonValue(
14
+ registry: JsonSchemaValidatorRegistry,
15
+ table: string,
16
+ column: string,
17
+ value: unknown,
18
+ direction: 'encode' | 'decode',
19
+ codecId?: string,
20
+ ): void {
21
+ const key = `${table}.${column}`;
22
+ const validate = registry.get(key);
23
+ if (!validate) return;
24
+
25
+ const result = validate(value);
26
+ if (result.valid) return;
27
+
28
+ throw createJsonSchemaValidationError(table, column, direction, result.errors, codecId);
29
+ }
30
+
31
+ function createJsonSchemaValidationError(
32
+ table: string,
33
+ column: string,
34
+ direction: 'encode' | 'decode',
35
+ errors: ReadonlyArray<JsonSchemaValidationError>,
36
+ codecId?: string,
37
+ ): Error {
38
+ const summary = formatErrorSummary(errors);
39
+ return runtimeError(
40
+ 'RUNTIME.JSON_SCHEMA_VALIDATION_FAILED',
41
+ `JSON schema validation failed for column '${table}.${column}' (${direction}): ${summary}`,
42
+ {
43
+ table,
44
+ column,
45
+ codecId,
46
+ direction,
47
+ errors: [...errors],
48
+ },
49
+ );
50
+ }
51
+
52
+ function formatErrorSummary(errors: ReadonlyArray<JsonSchemaValidationError>): string {
53
+ if (errors.length === 0) return 'unknown validation error';
54
+ if (errors.length === 1) {
55
+ const err = errors[0] as JsonSchemaValidationError;
56
+ return err.path === '/' ? err.message : `${err.path}: ${err.message}`;
57
+ }
58
+ return errors
59
+ .map((err) => (err.path === '/' ? err.message : `${err.path}: ${err.message}`))
60
+ .join('; ');
61
+ }
@@ -1,26 +1,34 @@
1
1
  export type {
2
2
  AfterExecuteResult,
3
3
  BudgetsOptions,
4
- LintsOptions,
5
4
  Log,
6
5
  Plugin,
7
6
  PluginContext,
8
7
  } from '@prisma-next/runtime-executor';
9
- export { budgets, lints } from '@prisma-next/runtime-executor';
8
+ export { budgets } from '@prisma-next/runtime-executor';
10
9
  export {
11
10
  extractCodecIds,
12
11
  validateCodecRegistryCompleteness,
13
12
  validateContractCodecMappings,
14
13
  } from '../codecs/validation';
15
14
  export { lowerSqlPlan } from '../lower-sql-plan';
15
+ export type { LintsOptions } from '../plugins/lints';
16
+ export { lints } from '../plugins/lints';
16
17
  export type {
17
- CreateRuntimeContextOptions,
18
- RuntimeContext,
18
+ ExecutionContext,
19
+ RuntimeParameterizedCodecDescriptor,
20
+ SqlExecutionStack,
21
+ SqlExecutionStackWithDriver,
22
+ SqlRuntimeAdapterDescriptor,
19
23
  SqlRuntimeAdapterInstance,
24
+ SqlRuntimeDriverInstance,
20
25
  SqlRuntimeExtensionDescriptor,
21
26
  SqlRuntimeExtensionInstance,
27
+ SqlRuntimeTargetDescriptor,
28
+ SqlStaticContributions,
29
+ TypeHelperRegistry,
22
30
  } from '../sql-context';
23
- export { createRuntimeContext } from '../sql-context';
31
+ export { createExecutionContext, createSqlExecutionStack } from '../sql-context';
24
32
  export type { SqlStatement } from '../sql-marker';
25
33
  export {
26
34
  ensureSchemaStatement,
@@ -29,8 +37,8 @@ export {
29
37
  writeContractMarker,
30
38
  } from '../sql-marker';
31
39
  export type {
40
+ CreateRuntimeOptions,
32
41
  Runtime,
33
- RuntimeOptions,
34
42
  RuntimeTelemetryEvent,
35
43
  RuntimeVerifyOptions,
36
44
  TelemetryOutcome,
@@ -1,23 +1,23 @@
1
1
  import type { ExecutionPlan } from '@prisma-next/contract/types';
2
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
3
+ import type { Adapter, LoweredStatement, QueryAst } from '@prisma-next/sql-relational-core/ast';
2
4
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
3
- import type { RuntimeContext } from './sql-context';
4
5
 
5
6
  /**
6
7
  * Lowers a SQL query plan to an executable Plan by calling the adapter's lower method.
7
8
  *
8
- * This function is responsible for converting a lane-produced SqlQueryPlan (which contains
9
- * AST and params but no SQL) into a fully executable Plan (which includes SQL string).
10
- *
11
- * @param context - Runtime context containing the adapter
9
+ * @param adapter - Adapter to lower AST to SQL
10
+ * @param contract - Contract for lowering context
12
11
  * @param queryPlan - SQL query plan from a lane (contains AST, params, meta, but no SQL)
13
12
  * @returns Fully executable Plan with SQL string
14
13
  */
15
14
  export function lowerSqlPlan<Row>(
16
- context: RuntimeContext,
15
+ adapter: Adapter<QueryAst, SqlContract<SqlStorage>, LoweredStatement>,
16
+ contract: SqlContract<SqlStorage>,
17
17
  queryPlan: SqlQueryPlan<Row>,
18
18
  ): ExecutionPlan<Row> {
19
- const lowered = context.adapter.lower(queryPlan.ast, {
20
- contract: context.contract,
19
+ const lowered = adapter.lower(queryPlan.ast, {
20
+ contract,
21
21
  params: queryPlan.params,
22
22
  });
23
23