@prisma-next/sql-runtime 0.3.0-pr.99.5 → 0.3.0

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 (171) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +141 -24
  3. package/dist/exports-BO6Fl7yn.mjs +889 -0
  4. package/dist/exports-BO6Fl7yn.mjs.map +1 -0
  5. package/dist/index-n6z6trta.d.mts +186 -0
  6. package/dist/index-n6z6trta.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 +77 -0
  10. package/dist/test/utils.d.mts.map +1 -0
  11. package/dist/test/utils.mjs +221 -0
  12. package/dist/test/utils.mjs.map +1 -0
  13. package/package.json +26 -20
  14. package/src/codecs/decoding.ts +84 -3
  15. package/src/codecs/encoding.ts +5 -15
  16. package/src/codecs/json-schema-validation.ts +61 -0
  17. package/src/codecs/validation.ts +7 -6
  18. package/src/exports/index.ts +20 -9
  19. package/src/lower-sql-plan.ts +9 -9
  20. package/src/middleware/budgets.ts +256 -0
  21. package/src/middleware/lints.ts +192 -0
  22. package/src/middleware/sql-middleware.ts +26 -0
  23. package/src/sql-context.ts +357 -257
  24. package/src/sql-family-adapter.ts +17 -23
  25. package/src/sql-marker.ts +2 -2
  26. package/src/sql-runtime.ts +136 -61
  27. package/test/async-iterable-result.test.ts +42 -37
  28. package/test/budgets.test.ts +431 -0
  29. package/test/context.types.test-d.ts +18 -20
  30. package/test/execution-stack.test.ts +164 -0
  31. package/test/json-schema-validation.test.ts +571 -0
  32. package/test/lints.test.ts +159 -0
  33. package/test/mutation-default-generators.test.ts +254 -0
  34. package/test/parameterized-types.test.ts +181 -205
  35. package/test/sql-context.test.ts +301 -134
  36. package/test/sql-family-adapter.test.ts +37 -20
  37. package/test/sql-runtime.test.ts +261 -49
  38. package/test/utils.ts +101 -67
  39. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js +0 -137863
  40. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js.map +0 -1
  41. package/dist/amcheck-24VY6X5V.js +0 -13
  42. package/dist/amcheck-24VY6X5V.js.map +0 -1
  43. package/dist/bloom-VS74NLHT.js +0 -13
  44. package/dist/bloom-VS74NLHT.js.map +0 -1
  45. package/dist/btree_gin-WBC4EAAI.js +0 -13
  46. package/dist/btree_gin-WBC4EAAI.js.map +0 -1
  47. package/dist/btree_gist-UNC6QD3M.js +0 -13
  48. package/dist/btree_gist-UNC6QD3M.js.map +0 -1
  49. package/dist/chunk-3KTOEDFX.js +0 -49
  50. package/dist/chunk-3KTOEDFX.js.map +0 -1
  51. package/dist/chunk-47DZBRQC.js +0 -1280
  52. package/dist/chunk-47DZBRQC.js.map +0 -1
  53. package/dist/chunk-52N6AFZM.js +0 -133
  54. package/dist/chunk-52N6AFZM.js.map +0 -1
  55. package/dist/chunk-7D4SUZUM.js +0 -38
  56. package/dist/chunk-7D4SUZUM.js.map +0 -1
  57. package/dist/chunk-APA6GHYY.js +0 -537
  58. package/dist/chunk-APA6GHYY.js.map +0 -1
  59. package/dist/chunk-ECWIHLAT.js +0 -37
  60. package/dist/chunk-ECWIHLAT.js.map +0 -1
  61. package/dist/chunk-EI626SDC.js +0 -105
  62. package/dist/chunk-EI626SDC.js.map +0 -1
  63. package/dist/chunk-UKKOYUGL.js +0 -578
  64. package/dist/chunk-UKKOYUGL.js.map +0 -1
  65. package/dist/chunk-XPLNMXQV.js +0 -1537
  66. package/dist/chunk-XPLNMXQV.js.map +0 -1
  67. package/dist/citext-T7MXGUY7.js +0 -13
  68. package/dist/citext-T7MXGUY7.js.map +0 -1
  69. package/dist/client-5FENX6AW.js +0 -299
  70. package/dist/client-5FENX6AW.js.map +0 -1
  71. package/dist/cube-TFDQBZCI.js +0 -13
  72. package/dist/cube-TFDQBZCI.js.map +0 -1
  73. package/dist/dict_int-AEUOPGWP.js +0 -13
  74. package/dist/dict_int-AEUOPGWP.js.map +0 -1
  75. package/dist/dict_xsyn-DAAYX3FL.js +0 -13
  76. package/dist/dict_xsyn-DAAYX3FL.js.map +0 -1
  77. package/dist/dist-AQ3LWXOX.js +0 -570
  78. package/dist/dist-AQ3LWXOX.js.map +0 -1
  79. package/dist/dist-LBVX6BJW.js +0 -189
  80. package/dist/dist-LBVX6BJW.js.map +0 -1
  81. package/dist/dist-WLKUVDN2.js +0 -5127
  82. package/dist/dist-WLKUVDN2.js.map +0 -1
  83. package/dist/earthdistance-KIGTF4LE.js +0 -13
  84. package/dist/earthdistance-KIGTF4LE.js.map +0 -1
  85. package/dist/file_fdw-5N55UP6I.js +0 -13
  86. package/dist/file_fdw-5N55UP6I.js.map +0 -1
  87. package/dist/fuzzystrmatch-KN3YWBFP.js +0 -13
  88. package/dist/fuzzystrmatch-KN3YWBFP.js.map +0 -1
  89. package/dist/hstore-YX726NKN.js +0 -13
  90. package/dist/hstore-YX726NKN.js.map +0 -1
  91. package/dist/http-exception-FZY2H4OF.js +0 -8
  92. package/dist/http-exception-FZY2H4OF.js.map +0 -1
  93. package/dist/index.js +0 -30
  94. package/dist/index.js.map +0 -1
  95. package/dist/intarray-NKVXNO2D.js +0 -13
  96. package/dist/intarray-NKVXNO2D.js.map +0 -1
  97. package/dist/isn-FTEMJGEV.js +0 -13
  98. package/dist/isn-FTEMJGEV.js.map +0 -1
  99. package/dist/lo-DB7L4NGI.js +0 -13
  100. package/dist/lo-DB7L4NGI.js.map +0 -1
  101. package/dist/logger-WQ7SHNDD.js +0 -68
  102. package/dist/logger-WQ7SHNDD.js.map +0 -1
  103. package/dist/ltree-Z32TZT6W.js +0 -13
  104. package/dist/ltree-Z32TZT6W.js.map +0 -1
  105. package/dist/nodefs-NM46ACH7.js +0 -31
  106. package/dist/nodefs-NM46ACH7.js.map +0 -1
  107. package/dist/opfs-ahp-NJO33LVZ.js +0 -332
  108. package/dist/opfs-ahp-NJO33LVZ.js.map +0 -1
  109. package/dist/pageinspect-YP3IZR4X.js +0 -13
  110. package/dist/pageinspect-YP3IZR4X.js.map +0 -1
  111. package/dist/pg_buffercache-7TD5J2FB.js +0 -13
  112. package/dist/pg_buffercache-7TD5J2FB.js.map +0 -1
  113. package/dist/pg_dump-SG4KYBUB.js +0 -2492
  114. package/dist/pg_dump-SG4KYBUB.js.map +0 -1
  115. package/dist/pg_freespacemap-DZDNCPZK.js +0 -13
  116. package/dist/pg_freespacemap-DZDNCPZK.js.map +0 -1
  117. package/dist/pg_surgery-J2MUEWEP.js +0 -13
  118. package/dist/pg_surgery-J2MUEWEP.js.map +0 -1
  119. package/dist/pg_trgm-7VNQOYS6.js +0 -13
  120. package/dist/pg_trgm-7VNQOYS6.js.map +0 -1
  121. package/dist/pg_visibility-TTSIPHFL.js +0 -13
  122. package/dist/pg_visibility-TTSIPHFL.js.map +0 -1
  123. package/dist/pg_walinspect-KPFHSHRJ.js +0 -13
  124. package/dist/pg_walinspect-KPFHSHRJ.js.map +0 -1
  125. package/dist/proxy-signals-GUDAMDHV.js +0 -39
  126. package/dist/proxy-signals-GUDAMDHV.js.map +0 -1
  127. package/dist/seg-IYVDLE4O.js +0 -13
  128. package/dist/seg-IYVDLE4O.js.map +0 -1
  129. package/dist/src/codecs/decoding.d.ts +0 -4
  130. package/dist/src/codecs/decoding.d.ts.map +0 -1
  131. package/dist/src/codecs/encoding.d.ts +0 -5
  132. package/dist/src/codecs/encoding.d.ts.map +0 -1
  133. package/dist/src/codecs/validation.d.ts +0 -6
  134. package/dist/src/codecs/validation.d.ts.map +0 -1
  135. package/dist/src/exports/index.d.ts +0 -11
  136. package/dist/src/exports/index.d.ts.map +0 -1
  137. package/dist/src/index.d.ts +0 -2
  138. package/dist/src/index.d.ts.map +0 -1
  139. package/dist/src/lower-sql-plan.d.ts +0 -15
  140. package/dist/src/lower-sql-plan.d.ts.map +0 -1
  141. package/dist/src/sql-context.d.ts +0 -130
  142. package/dist/src/sql-context.d.ts.map +0 -1
  143. package/dist/src/sql-family-adapter.d.ts +0 -10
  144. package/dist/src/sql-family-adapter.d.ts.map +0 -1
  145. package/dist/src/sql-marker.d.ts +0 -22
  146. package/dist/src/sql-marker.d.ts.map +0 -1
  147. package/dist/src/sql-runtime.d.ts +0 -25
  148. package/dist/src/sql-runtime.d.ts.map +0 -1
  149. package/dist/tablefunc-EF4RCS7S.js +0 -13
  150. package/dist/tablefunc-EF4RCS7S.js.map +0 -1
  151. package/dist/tcn-3VT5BQYW.js +0 -13
  152. package/dist/tcn-3VT5BQYW.js.map +0 -1
  153. package/dist/test/utils.d.ts +0 -60
  154. package/dist/test/utils.d.ts.map +0 -1
  155. package/dist/test/utils.js +0 -24635
  156. package/dist/test/utils.js.map +0 -1
  157. package/dist/tiny-CW6F4GX6.js +0 -10
  158. package/dist/tiny-CW6F4GX6.js.map +0 -1
  159. package/dist/tsm_system_rows-ES7KNUQH.js +0 -13
  160. package/dist/tsm_system_rows-ES7KNUQH.js.map +0 -1
  161. package/dist/tsm_system_time-76WEIMBG.js +0 -13
  162. package/dist/tsm_system_time-76WEIMBG.js.map +0 -1
  163. package/dist/unaccent-7RYF3R64.js +0 -13
  164. package/dist/unaccent-7RYF3R64.js.map +0 -1
  165. package/dist/utility-Q5A254LJ-J4HTKZPT.js +0 -347
  166. package/dist/utility-Q5A254LJ-J4HTKZPT.js.map +0 -1
  167. package/dist/uuid_ossp-4ETE4FPE.js +0 -13
  168. package/dist/uuid_ossp-4ETE4FPE.js.map +0 -1
  169. package/dist/vector-74GPNV7V.js +0 -13
  170. package/dist/vector-74GPNV7V.js.map +0 -1
  171. package/src/index.ts +0 -1
@@ -1,5 +1,12 @@
1
- import { createOperationRegistry } from '@prisma-next/operations';
2
- import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
1
+ import type { Contract, ExecutionPlan } from '@prisma-next/contract/types';
2
+ import { coreHash, profileHash } from '@prisma-next/contract/types';
3
+ import {
4
+ type ExecutionStackInstance,
5
+ instantiateExecutionStack,
6
+ type RuntimeDriverInstance,
7
+ type RuntimeExtensionInstance,
8
+ } from '@prisma-next/framework-components/execution';
9
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
10
  import type {
4
11
  CodecRegistry,
5
12
  SelectAst,
@@ -8,29 +15,34 @@ import type {
8
15
  } from '@prisma-next/sql-relational-core/ast';
9
16
  import { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
10
17
  import { describe, expect, it, vi } from 'vitest';
11
- import type { RuntimeContext } from '../src/sql-context';
18
+ import type {
19
+ SqlRuntimeAdapterDescriptor,
20
+ SqlRuntimeAdapterInstance,
21
+ SqlRuntimeTargetDescriptor,
22
+ } from '../src/sql-context';
23
+ import { createExecutionContext, createSqlExecutionStack } from '../src/sql-context';
12
24
  import { createRuntime } from '../src/sql-runtime';
13
25
 
14
- // Minimal test contract
15
- const testContract: SqlContract<SqlStorage> = {
16
- schemaVersion: '1',
26
+ const testContract: Contract<SqlStorage> = {
17
27
  targetFamily: 'sql',
18
28
  target: 'postgres',
19
- coreHash: 'sha256:test',
29
+ profileHash: profileHash('sha256:test'),
20
30
  models: {},
21
- relations: {},
22
- storage: { tables: {} },
31
+ roots: {},
32
+ storage: { storageHash: coreHash('sha256:test'), tables: {} },
23
33
  extensionPacks: {},
24
34
  capabilities: {},
25
35
  meta: {},
26
- sources: {},
27
- mappings: {
28
- codecTypes: {},
29
- operationTypes: {},
30
- },
31
36
  };
32
37
 
33
- // Create a stub codec registry
38
+ interface DriverExecuteSpies {
39
+ rootExecute: ReturnType<typeof vi.fn>;
40
+ connectionExecute: ReturnType<typeof vi.fn>;
41
+ transactionExecute: ReturnType<typeof vi.fn>;
42
+ }
43
+
44
+ type MockSqlDriver = SqlDriver & { __spies: DriverExecuteSpies };
45
+
34
46
  function createStubCodecs(): CodecRegistry {
35
47
  const registry = createCodecRegistry();
36
48
  registry.register(
@@ -44,7 +56,6 @@ function createStubCodecs(): CodecRegistry {
44
56
  return registry;
45
57
  }
46
58
 
47
- // Create a stub adapter
48
59
  function createStubAdapter() {
49
60
  const codecs = createStubCodecs();
50
61
  return {
@@ -57,6 +68,12 @@ function createStubAdapter() {
57
68
  codecs() {
58
69
  return codecs;
59
70
  },
71
+ readMarkerStatement() {
72
+ return {
73
+ sql: 'select core_hash, profile_hash, contract_json, canonical_version, updated_at, app_tag, meta from prisma_contract.marker where id = $1',
74
+ params: [1],
75
+ };
76
+ },
60
77
  },
61
78
  lower(ast: SelectAst) {
62
79
  return {
@@ -67,37 +84,139 @@ function createStubAdapter() {
67
84
  };
68
85
  }
69
86
 
70
- // Create a mock driver that implements SqlDriver interface
71
- function createMockDriver(): SqlDriver {
72
- const execute = vi.fn().mockImplementation(async function* (_request: SqlExecuteRequest) {
87
+ function createMockDriver(): MockSqlDriver {
88
+ const rootExecute = vi.fn().mockImplementation(async function* (_request: SqlExecuteRequest) {
73
89
  yield { id: 1 };
74
90
  });
91
+ const connectionExecute = vi.fn().mockImplementation(async function* (
92
+ _request: SqlExecuteRequest,
93
+ ) {
94
+ yield { id: 2 };
95
+ });
96
+ const transactionExecute = vi.fn().mockImplementation(async function* (
97
+ _request: SqlExecuteRequest,
98
+ ) {
99
+ yield { id: 3 };
100
+ });
75
101
 
76
- return {
77
- connect: vi.fn().mockResolvedValue(undefined),
78
- execute,
79
- query: vi.fn().mockResolvedValue({ rows: [], rowCount: 0 }),
102
+ const query = vi.fn().mockResolvedValue({ rows: [], rowCount: 0 });
103
+
104
+ const transaction = {
105
+ execute: transactionExecute,
106
+ query,
107
+ commit: vi.fn().mockResolvedValue(undefined),
108
+ rollback: vi.fn().mockResolvedValue(undefined),
109
+ };
110
+
111
+ const connection = {
112
+ execute: connectionExecute,
113
+ query,
114
+ release: vi.fn().mockResolvedValue(undefined),
115
+ beginTransaction: vi.fn().mockResolvedValue(transaction),
116
+ };
117
+
118
+ const driver: SqlDriver = {
119
+ execute: rootExecute,
120
+ query,
121
+ connect: vi.fn().mockImplementation(async (_binding?: undefined) => undefined),
122
+ acquireConnection: vi.fn().mockResolvedValue(connection),
80
123
  close: vi.fn().mockResolvedValue(undefined),
81
124
  };
125
+
126
+ return Object.assign(driver, {
127
+ __spies: {
128
+ rootExecute,
129
+ connectionExecute,
130
+ transactionExecute,
131
+ },
132
+ });
82
133
  }
83
134
 
84
- // Create a test runtime context
85
- function createTestContext(contract: SqlContract<SqlStorage>): RuntimeContext<typeof contract> {
135
+ function createTestTargetDescriptor(): SqlRuntimeTargetDescriptor<'postgres'> {
136
+ return {
137
+ kind: 'target',
138
+ id: 'postgres',
139
+ version: '0.0.1',
140
+ familyId: 'sql' as const,
141
+ targetId: 'postgres' as const,
142
+ codecs: () => createCodecRegistry(),
143
+ parameterizedCodecs: () => [],
144
+ create() {
145
+ return { familyId: 'sql' as const, targetId: 'postgres' as const };
146
+ },
147
+ };
148
+ }
149
+
150
+ function createTestAdapterDescriptor(
151
+ adapter: ReturnType<typeof createStubAdapter>,
152
+ ): SqlRuntimeAdapterDescriptor<'postgres'> {
153
+ const codecRegistry = adapter.profile.codecs();
154
+ return {
155
+ kind: 'adapter',
156
+ id: 'test-adapter',
157
+ version: '0.0.1',
158
+ familyId: 'sql' as const,
159
+ targetId: 'postgres' as const,
160
+ codecs: () => codecRegistry,
161
+ parameterizedCodecs: () => [],
162
+ create() {
163
+ return Object.assign(
164
+ { familyId: 'sql' as const, targetId: 'postgres' as const },
165
+ adapter,
166
+ ) as SqlRuntimeAdapterInstance<'postgres'>;
167
+ },
168
+ };
169
+ }
170
+
171
+ function createTestSetup() {
86
172
  const adapter = createStubAdapter();
173
+ const driver = createMockDriver();
174
+
175
+ const targetDescriptor = createTestTargetDescriptor();
176
+ const adapterDescriptor = createTestAdapterDescriptor(adapter);
177
+
178
+ const stack = createSqlExecutionStack({
179
+ target: targetDescriptor,
180
+ adapter: adapterDescriptor,
181
+ extensionPacks: [],
182
+ });
183
+ type SqlTestStackInstance = ExecutionStackInstance<
184
+ 'sql',
185
+ 'postgres',
186
+ SqlRuntimeAdapterInstance<'postgres'>,
187
+ RuntimeDriverInstance<'sql', 'postgres'>,
188
+ RuntimeExtensionInstance<'sql', 'postgres'>
189
+ >;
190
+ const stackInstance = instantiateExecutionStack(stack) as SqlTestStackInstance;
191
+
192
+ const context = createExecutionContext({
193
+ contract: testContract,
194
+ stack: { target: targetDescriptor, adapter: adapterDescriptor, extensionPacks: [] },
195
+ });
196
+
197
+ return { stackInstance, context, driver };
198
+ }
199
+
200
+ function createRawExecutionPlan<Row = Record<string, unknown>>(): ExecutionPlan<Row> {
87
201
  return {
88
- contract,
89
- adapter,
90
- codecs: adapter.profile.codecs(),
91
- operations: createOperationRegistry(),
202
+ sql: 'select 1',
203
+ params: [],
204
+ meta: {
205
+ target: testContract.target,
206
+ targetFamily: testContract.targetFamily,
207
+ storageHash: testContract.storage.storageHash,
208
+ lane: 'raw',
209
+ paramDescriptors: [],
210
+ },
92
211
  };
93
212
  }
94
213
 
95
214
  describe('createRuntime', () => {
96
- it('creates runtime with valid options', () => {
97
- const context = createTestContext(testContract);
98
- const driver = createMockDriver();
215
+ it('creates runtime with context and driver', () => {
216
+ const { stackInstance, context, driver } = createTestSetup();
99
217
 
100
218
  const runtime = createRuntime({
219
+ stackInstance,
101
220
  context,
102
221
  driver,
103
222
  verify: { mode: 'onFirstUse', requireMarker: false },
@@ -106,50 +225,143 @@ describe('createRuntime', () => {
106
225
  expect(runtime).toBeDefined();
107
226
  expect(runtime.execute).toBeDefined();
108
227
  expect(runtime.telemetry).toBeDefined();
109
- expect(runtime.operations).toBeDefined();
110
228
  expect(runtime.close).toBeDefined();
111
229
  });
112
230
 
113
- it('returns operations registry', () => {
114
- const context = createTestContext(testContract);
115
- const driver = createMockDriver();
231
+ it('returns null telemetry when no events', () => {
232
+ const { stackInstance, context, driver } = createTestSetup();
233
+
234
+ const runtime = createRuntime({
235
+ stackInstance,
236
+ context,
237
+ driver,
238
+ verify: { mode: 'onFirstUse', requireMarker: false },
239
+ });
240
+
241
+ expect(runtime.telemetry()).toBeNull();
242
+ });
243
+
244
+ it('closes runtime and driver', async () => {
245
+ const { stackInstance, context, driver } = createTestSetup();
116
246
 
117
247
  const runtime = createRuntime({
248
+ stackInstance,
118
249
  context,
119
250
  driver,
120
251
  verify: { mode: 'onFirstUse', requireMarker: false },
121
252
  });
122
253
 
123
- const ops = runtime.operations();
124
- expect(ops).toBeDefined();
125
- expect(ops.byType).toBeDefined();
254
+ await runtime.close();
255
+ expect(driver.close).toHaveBeenCalled();
126
256
  });
127
257
 
128
- it('returns null telemetry when no events', () => {
129
- const context = createTestContext(testContract);
130
- const driver = createMockDriver();
258
+ it('validates codec registry at startup when verify mode is startup', () => {
259
+ const { stackInstance, context, driver } = createTestSetup();
260
+
261
+ const runtime = createRuntime({
262
+ stackInstance,
263
+ context,
264
+ driver,
265
+ verify: { mode: 'startup', requireMarker: false },
266
+ });
131
267
 
268
+ expect(runtime).toBeDefined();
269
+ });
270
+
271
+ it('uses acquired connection queryable for connection.execute', async () => {
272
+ const { stackInstance, context, driver } = createTestSetup();
132
273
  const runtime = createRuntime({
274
+ stackInstance,
133
275
  context,
134
276
  driver,
135
277
  verify: { mode: 'onFirstUse', requireMarker: false },
136
278
  });
137
279
 
138
- // Before any execution, telemetry should be null
139
- expect(runtime.telemetry()).toBeNull();
280
+ const connection = await runtime.connection();
281
+ await connection.execute(createRawExecutionPlan()).toArray();
282
+
283
+ expect(driver.__spies.connectionExecute).toHaveBeenCalledTimes(1);
284
+ expect(driver.__spies.transactionExecute).not.toHaveBeenCalled();
285
+ expect(driver.__spies.rootExecute).not.toHaveBeenCalled();
286
+
287
+ await connection.release();
140
288
  });
141
289
 
142
- it('closes runtime', async () => {
143
- const context = createTestContext(testContract);
144
- const driver = createMockDriver();
290
+ it('uses transaction queryable for transaction.execute', async () => {
291
+ const { stackInstance, context, driver } = createTestSetup();
292
+ const runtime = createRuntime({
293
+ stackInstance,
294
+ context,
295
+ driver,
296
+ verify: { mode: 'onFirstUse', requireMarker: false },
297
+ });
298
+
299
+ const connection = await runtime.connection();
300
+ const transaction = await connection.transaction();
301
+ await transaction.execute(createRawExecutionPlan()).toArray();
302
+
303
+ expect(driver.__spies.transactionExecute).toHaveBeenCalledTimes(1);
304
+ expect(driver.__spies.connectionExecute).not.toHaveBeenCalled();
305
+ expect(driver.__spies.rootExecute).not.toHaveBeenCalled();
306
+
307
+ await transaction.rollback();
308
+ await connection.release();
309
+ });
145
310
 
311
+ it('keeps root execute on driver queryable for runtime.execute', async () => {
312
+ const { stackInstance, context, driver } = createTestSetup();
146
313
  const runtime = createRuntime({
314
+ stackInstance,
147
315
  context,
148
316
  driver,
149
317
  verify: { mode: 'onFirstUse', requireMarker: false },
150
318
  });
151
319
 
152
- await runtime.close();
153
- expect(driver.close).toHaveBeenCalled();
320
+ await runtime.execute(createRawExecutionPlan()).toArray();
321
+
322
+ expect(driver.__spies.rootExecute).toHaveBeenCalledTimes(1);
323
+ expect(driver.__spies.connectionExecute).not.toHaveBeenCalled();
324
+ expect(driver.__spies.transactionExecute).not.toHaveBeenCalled();
325
+ });
326
+
327
+ it('accepts a generic middleware (no familyId)', () => {
328
+ const { stackInstance, context, driver } = createTestSetup();
329
+ expect(() =>
330
+ createRuntime({
331
+ stackInstance,
332
+ context,
333
+ driver,
334
+ verify: { mode: 'onFirstUse', requireMarker: false },
335
+ middleware: [{ name: 'generic' }],
336
+ }),
337
+ ).not.toThrow();
338
+ });
339
+
340
+ it('accepts an SQL middleware', () => {
341
+ const { stackInstance, context, driver } = createTestSetup();
342
+ expect(() =>
343
+ createRuntime({
344
+ stackInstance,
345
+ context,
346
+ driver,
347
+ verify: { mode: 'onFirstUse', requireMarker: false },
348
+ middleware: [{ name: 'sql-lints', familyId: 'sql' }],
349
+ }),
350
+ ).not.toThrow();
351
+ });
352
+
353
+ it('rejects a Mongo middleware with a clear error', () => {
354
+ const { stackInstance, context, driver } = createTestSetup();
355
+ expect(() =>
356
+ createRuntime({
357
+ stackInstance,
358
+ context,
359
+ driver,
360
+ verify: { mode: 'onFirstUse', requireMarker: false },
361
+ middleware: [{ name: 'mongo-mw', familyId: 'mongo' }],
362
+ }),
363
+ ).toThrow(
364
+ "Middleware 'mongo-mw' requires family 'mongo' but the runtime is configured for family 'sql'",
365
+ );
154
366
  });
155
367
  });
package/test/utils.ts CHANGED
@@ -1,5 +1,12 @@
1
- import type { ExecutionPlan, ResultType } from '@prisma-next/contract/types';
2
- import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
1
+ import type { Contract, ExecutionPlan, ResultType } from '@prisma-next/contract/types';
2
+ import { coreHash, profileHash } from '@prisma-next/contract/types';
3
+ import {
4
+ instantiateExecutionStack,
5
+ type RuntimeDriverDescriptor,
6
+ } from '@prisma-next/framework-components/execution';
7
+ import { builtinGeneratorIds } from '@prisma-next/ids';
8
+ import { generateId } from '@prisma-next/ids/runtime';
9
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
10
  import type { Adapter, LoweredStatement, SelectAst } from '@prisma-next/sql-relational-core/ast';
4
11
  import { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
5
12
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
@@ -7,18 +14,29 @@ import { collectAsync, drainAsyncIterable } from '@prisma-next/test-utils';
7
14
  import type { Client } from 'pg';
8
15
  import type { SqlStatement } from '../src/exports';
9
16
  import {
17
+ createExecutionContext,
10
18
  type createRuntime,
11
- createRuntimeContext,
19
+ createSqlExecutionStack,
12
20
  ensureSchemaStatement,
13
21
  ensureTableStatement,
14
22
  writeContractMarker,
15
23
  } from '../src/exports';
16
24
  import type {
17
- RuntimeContext,
25
+ ExecutionContext,
26
+ SqlRuntimeAdapterDescriptor,
18
27
  SqlRuntimeAdapterInstance,
28
+ SqlRuntimeDriverInstance,
19
29
  SqlRuntimeExtensionDescriptor,
30
+ SqlRuntimeTargetDescriptor,
20
31
  } from '../src/sql-context';
21
32
 
33
+ function createTestMutationDefaultGenerators() {
34
+ return builtinGeneratorIds.map((id) => ({
35
+ id,
36
+ generate: (params?: Record<string, unknown>) => generateId(params ? { id, params } : { id }),
37
+ }));
38
+ }
39
+
22
40
  /**
23
41
  * Executes a plan and collects all results into an array.
24
42
  * This helper DRYs up the common pattern of executing plans in tests.
@@ -60,7 +78,7 @@ export async function executeStatement(client: Client, statement: SqlStatement):
60
78
  */
61
79
  export async function setupTestDatabase(
62
80
  client: Client,
63
- contract: SqlContract<SqlStorage>,
81
+ contract: Contract<SqlStorage>,
64
82
  setupFn: (client: Client) => Promise<void>,
65
83
  ): Promise<void> {
66
84
  await client.query('drop schema if exists prisma_contract cascade');
@@ -71,8 +89,8 @@ export async function setupTestDatabase(
71
89
  await executeStatement(client, ensureSchemaStatement);
72
90
  await executeStatement(client, ensureTableStatement);
73
91
  const write = writeContractMarker({
74
- coreHash: contract.coreHash,
75
- profileHash: contract.profileHash ?? contract.coreHash,
92
+ storageHash: contract.storage.storageHash,
93
+ profileHash: contract.profileHash,
76
94
  contractJson: contract,
77
95
  canonicalVersion: 1,
78
96
  });
@@ -85,11 +103,11 @@ export async function setupTestDatabase(
85
103
  */
86
104
  export async function writeTestContractMarker(
87
105
  client: Client,
88
- contract: SqlContract<SqlStorage>,
106
+ contract: Contract<SqlStorage>,
89
107
  ): Promise<void> {
90
108
  const write = writeContractMarker({
91
- coreHash: contract.coreHash,
92
- profileHash: contract.profileHash ?? contract.coreHash,
109
+ storageHash: contract.storage.storageHash,
110
+ profileHash: contract.profileHash,
93
111
  contractJson: contract,
94
112
  canonicalVersion: 1,
95
113
  });
@@ -98,56 +116,40 @@ export async function writeTestContractMarker(
98
116
 
99
117
  /**
100
118
  * Creates a test adapter descriptor from a raw adapter.
101
- * This wraps the adapter in a descriptor for descriptor-first context creation in tests.
102
- * The adapter instance IS an Adapter (via intersection), with identity properties added.
119
+ * Wraps the adapter in an SqlRuntimeAdapterDescriptor with static contributions
120
+ * derived from the adapter's codec registry.
103
121
  */
104
- function createTestAdapterDescriptor(
105
- adapter: Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,
106
- ): {
107
- readonly kind: 'adapter';
108
- readonly id: string;
109
- readonly version: string;
110
- readonly familyId: 'sql';
111
- readonly targetId: 'postgres';
112
- create(): SqlRuntimeAdapterInstance<'postgres'>;
113
- } {
122
+ export function createTestAdapterDescriptor(
123
+ adapter: Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement>,
124
+ ): SqlRuntimeAdapterDescriptor<'postgres'> {
125
+ const codecRegistry = adapter.profile.codecs();
114
126
  return {
115
127
  kind: 'adapter' as const,
116
128
  id: 'test-adapter',
117
129
  version: '0.0.1',
118
130
  familyId: 'sql' as const,
119
131
  targetId: 'postgres' as const,
132
+ codecs: () => codecRegistry,
133
+ parameterizedCodecs: () => [],
134
+ mutationDefaultGenerators: createTestMutationDefaultGenerators,
120
135
  create(): SqlRuntimeAdapterInstance<'postgres'> {
121
- // Return an object that combines identity properties with the adapter's methods
122
- return Object.assign(
123
- {
124
- familyId: 'sql' as const,
125
- targetId: 'postgres' as const,
126
- },
127
- adapter,
128
- );
136
+ return Object.assign({ familyId: 'sql' as const, targetId: 'postgres' as const }, adapter);
129
137
  },
130
138
  };
131
139
  }
132
140
 
133
141
  /**
134
- * Creates a test target descriptor.
135
- * This is a minimal descriptor for descriptor-first context creation in tests.
142
+ * Creates a test target descriptor with empty static contributions.
136
143
  */
137
- function createTestTargetDescriptor(): {
138
- readonly kind: 'target';
139
- readonly id: string;
140
- readonly version: string;
141
- readonly familyId: 'sql';
142
- readonly targetId: 'postgres';
143
- create(): { readonly familyId: 'sql'; readonly targetId: 'postgres' };
144
- } {
144
+ export function createTestTargetDescriptor(): SqlRuntimeTargetDescriptor<'postgres'> {
145
145
  return {
146
146
  kind: 'target' as const,
147
147
  id: 'postgres',
148
148
  version: '0.0.1',
149
149
  familyId: 'sql' as const,
150
150
  targetId: 'postgres' as const,
151
+ codecs: () => createCodecRegistry(),
152
+ parameterizedCodecs: () => [],
151
153
  create() {
152
154
  return { familyId: 'sql' as const, targetId: 'postgres' as const };
153
155
  },
@@ -155,25 +157,46 @@ function createTestTargetDescriptor(): {
155
157
  }
156
158
 
157
159
  /**
158
- * Creates a runtime context with standard test configuration.
160
+ * Creates an ExecutionContext for testing.
159
161
  * This helper DRYs up the common pattern of context creation in tests.
160
162
  *
161
163
  * Accepts a raw adapter and optional extension descriptors, wrapping the
162
164
  * adapter in a descriptor internally for descriptor-first context creation.
163
165
  */
164
- export function createTestContext<TContract extends SqlContract<SqlStorage>>(
166
+ export function createTestContext<TContract extends Contract<SqlStorage>>(
165
167
  contract: TContract,
166
- adapter: Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement>,
168
+ adapter: Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement>,
167
169
  options?: {
168
170
  extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;
169
171
  },
170
- ): RuntimeContext<TContract> {
171
- return createRuntimeContext<TContract, 'postgres'>({
172
+ ): ExecutionContext<TContract> {
173
+ return createExecutionContext({
172
174
  contract,
175
+ stack: {
176
+ target: createTestTargetDescriptor(),
177
+ adapter: createTestAdapterDescriptor(adapter),
178
+ extensionPacks: options?.extensionPacks ?? [],
179
+ },
180
+ });
181
+ }
182
+
183
+ export function createTestStackInstance(options?: {
184
+ extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;
185
+ driver?: RuntimeDriverDescriptor<
186
+ 'sql',
187
+ 'postgres',
188
+ unknown,
189
+ SqlRuntimeDriverInstance<'postgres'>
190
+ >;
191
+ }) {
192
+ const stack = createSqlExecutionStack({
173
193
  target: createTestTargetDescriptor(),
174
- adapter: createTestAdapterDescriptor(adapter),
194
+ adapter: createTestAdapterDescriptor(createStubAdapter()),
195
+ driver: options?.driver,
175
196
  extensionPacks: options?.extensionPacks ?? [],
176
197
  });
198
+
199
+ return instantiateExecutionStack(stack);
177
200
  }
178
201
 
179
202
  /**
@@ -183,7 +206,7 @@ export function createTestContext<TContract extends SqlContract<SqlStorage>>(
183
206
  * The stub adapter includes simple codecs for common test types (pg/int4@1, pg/text@1, pg/timestamptz@1)
184
207
  * to enable type inference in tests without requiring the postgres adapter package.
185
208
  */
186
- export function createStubAdapter(): Adapter<SelectAst, SqlContract<SqlStorage>, LoweredStatement> {
209
+ export function createStubAdapter(): Adapter<SelectAst, Contract<SqlStorage>, LoweredStatement> {
187
210
  const codecRegistry = createCodecRegistry();
188
211
 
189
212
  // Register stub codecs for common test types
@@ -212,8 +235,7 @@ export function createStubAdapter(): Adapter<SelectAst, SqlContract<SqlStorage>,
212
235
  typeId: 'pg/timestamptz@1',
213
236
  targetTypes: ['timestamptz'],
214
237
  encode: (value: string | Date) => (value instanceof Date ? value.toISOString() : value),
215
- decode: (wire: string | Date) =>
216
- typeof wire === 'string' ? wire : wire instanceof Date ? wire.toISOString() : String(wire),
238
+ decode: (wire: string | Date) => (wire instanceof Date ? wire : new Date(wire)),
217
239
  }),
218
240
  );
219
241
 
@@ -225,8 +247,14 @@ export function createStubAdapter(): Adapter<SelectAst, SqlContract<SqlStorage>,
225
247
  codecs() {
226
248
  return codecRegistry;
227
249
  },
250
+ readMarkerStatement() {
251
+ return {
252
+ sql: 'select core_hash, profile_hash, contract_json, canonical_version, updated_at, app_tag, meta from prisma_contract.marker where id = $1',
253
+ params: [1],
254
+ };
255
+ },
228
256
  },
229
- lower(ast: SelectAst, ctx: { contract: SqlContract<SqlStorage>; params?: readonly unknown[] }) {
257
+ lower(ast: SelectAst, ctx: { contract: Contract<SqlStorage>; params?: readonly unknown[] }) {
230
258
  const sqlText = JSON.stringify(ast);
231
259
  return {
232
260
  profileId: this.profile.id,
@@ -236,24 +264,30 @@ export function createStubAdapter(): Adapter<SelectAst, SqlContract<SqlStorage>,
236
264
  };
237
265
  }
238
266
 
239
- /**
240
- * Creates a valid test contract without using validateContract.
241
- * Ensures all required fields are present (mappings, capabilities, extensionPacks, meta, sources)
242
- * and returns the contract with proper typing.
243
- * This helper allows tests to create contracts without depending on sql-query.
244
- */
245
- export function createTestContract<T extends SqlContract<SqlStorage>>(
246
- contract: Partial<T> &
247
- Omit<T, 'mappings' | 'capabilities' | 'extensionPacks' | 'meta' | 'sources'>,
248
- ): T {
267
+ export function createTestContract(
268
+ contract: Partial<Omit<Contract<SqlStorage>, 'profileHash' | 'storage'>> & {
269
+ storageHash?: string;
270
+ profileHash?: string;
271
+ storage?: Omit<SqlStorage, 'storageHash'>;
272
+ },
273
+ ): Contract<SqlStorage> {
274
+ const { execution, ...rest } = contract;
275
+ const storageHashValue = coreHash(rest['storageHash'] ?? 'sha256:testcore');
276
+
249
277
  return {
250
- ...contract,
251
- mappings: contract.mappings ?? { codecTypes: {}, operationTypes: {} },
252
- capabilities: contract.capabilities ?? {},
253
- extensionPacks: contract.extensionPacks ?? {},
254
- meta: contract.meta ?? {},
255
- sources: contract.sources ?? {},
256
- } as T;
278
+ target: rest['target'] ?? 'postgres',
279
+ targetFamily: rest['targetFamily'] ?? 'sql',
280
+ storage: rest['storage']
281
+ ? { ...rest['storage'], storageHash: storageHashValue }
282
+ : { storageHash: storageHashValue, tables: {} },
283
+ models: rest['models'] ?? {},
284
+ roots: rest['roots'] ?? {},
285
+ capabilities: rest['capabilities'] ?? {},
286
+ extensionPacks: rest['extensionPacks'] ?? {},
287
+ meta: rest['meta'] ?? {},
288
+ ...(execution ? { execution } : {}),
289
+ profileHash: profileHash(rest['profileHash'] ?? 'sha256:testprofile'),
290
+ };
257
291
  }
258
292
 
259
293
  // Re-export generic utilities from test-utils