@prisma-next/sql-runtime 0.3.0-dev.4 → 0.3.0-dev.40

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 (144) hide show
  1. package/README.md +116 -26
  2. package/dist/exports-C8hi0N-a.mjs +622 -0
  3. package/dist/exports-C8hi0N-a.mjs.map +1 -0
  4. package/dist/index-SlQIrV_t.d.mts +131 -0
  5. package/dist/index-SlQIrV_t.d.mts.map +1 -0
  6. package/dist/index.d.mts +2 -0
  7. package/dist/index.mjs +3 -0
  8. package/dist/test/utils.d.mts +82 -0
  9. package/dist/test/utils.d.mts.map +1 -0
  10. package/dist/test/utils.mjs +212 -0
  11. package/dist/test/utils.mjs.map +1 -0
  12. package/package.json +35 -26
  13. package/src/codecs/decoding.ts +221 -0
  14. package/src/codecs/encoding.ts +89 -0
  15. package/src/codecs/json-schema-validation.ts +61 -0
  16. package/src/codecs/validation.ts +67 -0
  17. package/src/exports/index.ts +45 -0
  18. package/src/lower-sql-plan.ts +32 -0
  19. package/src/sql-context.ts +443 -0
  20. package/src/sql-family-adapter.ts +47 -0
  21. package/src/sql-marker.ts +105 -0
  22. package/src/sql-runtime.ts +232 -0
  23. package/test/async-iterable-result.test.ts +144 -0
  24. package/test/context.types.test-d.ts +68 -0
  25. package/test/execution-stack.test.ts +166 -0
  26. package/test/json-schema-validation.test.ts +653 -0
  27. package/test/parameterized-types.test.ts +539 -0
  28. package/test/sql-context.test.ts +392 -0
  29. package/test/sql-family-adapter.test.ts +87 -0
  30. package/test/sql-runtime.test.ts +241 -0
  31. package/test/utils.ts +292 -0
  32. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js +0 -137863
  33. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js.map +0 -1
  34. package/dist/amcheck-24VY6X5V.js +0 -13
  35. package/dist/amcheck-24VY6X5V.js.map +0 -1
  36. package/dist/bloom-VS74NLHT.js +0 -13
  37. package/dist/bloom-VS74NLHT.js.map +0 -1
  38. package/dist/btree_gin-WBC4EAAI.js +0 -13
  39. package/dist/btree_gin-WBC4EAAI.js.map +0 -1
  40. package/dist/btree_gist-UNC6QD3M.js +0 -13
  41. package/dist/btree_gist-UNC6QD3M.js.map +0 -1
  42. package/dist/chunk-3KTOEDFX.js +0 -49
  43. package/dist/chunk-3KTOEDFX.js.map +0 -1
  44. package/dist/chunk-47DZBRQC.js +0 -1280
  45. package/dist/chunk-47DZBRQC.js.map +0 -1
  46. package/dist/chunk-52N6AFZM.js +0 -133
  47. package/dist/chunk-52N6AFZM.js.map +0 -1
  48. package/dist/chunk-7D4SUZUM.js +0 -38
  49. package/dist/chunk-7D4SUZUM.js.map +0 -1
  50. package/dist/chunk-C6I3V3DM.js +0 -455
  51. package/dist/chunk-C6I3V3DM.js.map +0 -1
  52. package/dist/chunk-ECWIHLAT.js +0 -37
  53. package/dist/chunk-ECWIHLAT.js.map +0 -1
  54. package/dist/chunk-EI626SDC.js +0 -105
  55. package/dist/chunk-EI626SDC.js.map +0 -1
  56. package/dist/chunk-UKKOYUGL.js +0 -578
  57. package/dist/chunk-UKKOYUGL.js.map +0 -1
  58. package/dist/chunk-XPLNMXQV.js +0 -1537
  59. package/dist/chunk-XPLNMXQV.js.map +0 -1
  60. package/dist/citext-T7MXGUY7.js +0 -13
  61. package/dist/citext-T7MXGUY7.js.map +0 -1
  62. package/dist/client-5FENX6AW.js +0 -299
  63. package/dist/client-5FENX6AW.js.map +0 -1
  64. package/dist/cube-TFDQBZCI.js +0 -13
  65. package/dist/cube-TFDQBZCI.js.map +0 -1
  66. package/dist/dict_int-AEUOPGWP.js +0 -13
  67. package/dist/dict_int-AEUOPGWP.js.map +0 -1
  68. package/dist/dict_xsyn-DAAYX3FL.js +0 -13
  69. package/dist/dict_xsyn-DAAYX3FL.js.map +0 -1
  70. package/dist/dist-AQ3LWXOX.js +0 -570
  71. package/dist/dist-AQ3LWXOX.js.map +0 -1
  72. package/dist/dist-LBVX6BJW.js +0 -189
  73. package/dist/dist-LBVX6BJW.js.map +0 -1
  74. package/dist/dist-WLKUVDN2.js +0 -5127
  75. package/dist/dist-WLKUVDN2.js.map +0 -1
  76. package/dist/earthdistance-KIGTF4LE.js +0 -13
  77. package/dist/earthdistance-KIGTF4LE.js.map +0 -1
  78. package/dist/file_fdw-5N55UP6I.js +0 -13
  79. package/dist/file_fdw-5N55UP6I.js.map +0 -1
  80. package/dist/fuzzystrmatch-KN3YWBFP.js +0 -13
  81. package/dist/fuzzystrmatch-KN3YWBFP.js.map +0 -1
  82. package/dist/hstore-YX726NKN.js +0 -13
  83. package/dist/hstore-YX726NKN.js.map +0 -1
  84. package/dist/http-exception-FZY2H4OF.js +0 -8
  85. package/dist/http-exception-FZY2H4OF.js.map +0 -1
  86. package/dist/index.d.ts +0 -29
  87. package/dist/index.js +0 -30
  88. package/dist/index.js.map +0 -1
  89. package/dist/intarray-NKVXNO2D.js +0 -13
  90. package/dist/intarray-NKVXNO2D.js.map +0 -1
  91. package/dist/isn-FTEMJGEV.js +0 -13
  92. package/dist/isn-FTEMJGEV.js.map +0 -1
  93. package/dist/lo-DB7L4NGI.js +0 -13
  94. package/dist/lo-DB7L4NGI.js.map +0 -1
  95. package/dist/logger-WQ7SHNDD.js +0 -68
  96. package/dist/logger-WQ7SHNDD.js.map +0 -1
  97. package/dist/ltree-Z32TZT6W.js +0 -13
  98. package/dist/ltree-Z32TZT6W.js.map +0 -1
  99. package/dist/nodefs-NM46ACH7.js +0 -31
  100. package/dist/nodefs-NM46ACH7.js.map +0 -1
  101. package/dist/opfs-ahp-NJO33LVZ.js +0 -332
  102. package/dist/opfs-ahp-NJO33LVZ.js.map +0 -1
  103. package/dist/pageinspect-YP3IZR4X.js +0 -13
  104. package/dist/pageinspect-YP3IZR4X.js.map +0 -1
  105. package/dist/pg_buffercache-7TD5J2FB.js +0 -13
  106. package/dist/pg_buffercache-7TD5J2FB.js.map +0 -1
  107. package/dist/pg_dump-SG4KYBUB.js +0 -2492
  108. package/dist/pg_dump-SG4KYBUB.js.map +0 -1
  109. package/dist/pg_freespacemap-DZDNCPZK.js +0 -13
  110. package/dist/pg_freespacemap-DZDNCPZK.js.map +0 -1
  111. package/dist/pg_surgery-J2MUEWEP.js +0 -13
  112. package/dist/pg_surgery-J2MUEWEP.js.map +0 -1
  113. package/dist/pg_trgm-7VNQOYS6.js +0 -13
  114. package/dist/pg_trgm-7VNQOYS6.js.map +0 -1
  115. package/dist/pg_visibility-TTSIPHFL.js +0 -13
  116. package/dist/pg_visibility-TTSIPHFL.js.map +0 -1
  117. package/dist/pg_walinspect-KPFHSHRJ.js +0 -13
  118. package/dist/pg_walinspect-KPFHSHRJ.js.map +0 -1
  119. package/dist/proxy-signals-GUDAMDHV.js +0 -39
  120. package/dist/proxy-signals-GUDAMDHV.js.map +0 -1
  121. package/dist/seg-IYVDLE4O.js +0 -13
  122. package/dist/seg-IYVDLE4O.js.map +0 -1
  123. package/dist/sql-runtime-DgEbg2OP.d.ts +0 -109
  124. package/dist/tablefunc-EF4RCS7S.js +0 -13
  125. package/dist/tablefunc-EF4RCS7S.js.map +0 -1
  126. package/dist/tcn-3VT5BQYW.js +0 -13
  127. package/dist/tcn-3VT5BQYW.js.map +0 -1
  128. package/dist/test/utils.d.ts +0 -64
  129. package/dist/test/utils.js +0 -24634
  130. package/dist/test/utils.js.map +0 -1
  131. package/dist/tiny-CW6F4GX6.js +0 -10
  132. package/dist/tiny-CW6F4GX6.js.map +0 -1
  133. package/dist/tsm_system_rows-ES7KNUQH.js +0 -13
  134. package/dist/tsm_system_rows-ES7KNUQH.js.map +0 -1
  135. package/dist/tsm_system_time-76WEIMBG.js +0 -13
  136. package/dist/tsm_system_time-76WEIMBG.js.map +0 -1
  137. package/dist/unaccent-7RYF3R64.js +0 -13
  138. package/dist/unaccent-7RYF3R64.js.map +0 -1
  139. package/dist/utility-Q5A254LJ-J4HTKZPT.js +0 -347
  140. package/dist/utility-Q5A254LJ-J4HTKZPT.js.map +0 -1
  141. package/dist/uuid_ossp-4ETE4FPE.js +0 -13
  142. package/dist/uuid_ossp-4ETE4FPE.js.map +0 -1
  143. package/dist/vector-74GPNV7V.js +0 -13
  144. package/dist/vector-74GPNV7V.js.map +0 -1
@@ -0,0 +1,392 @@
1
+ import { coreHash } from '@prisma-next/contract/types';
2
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
3
+ import type { SqlOperationSignature } from '@prisma-next/sql-operations';
4
+ import { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
5
+ import { describe, expect, it } from 'vitest';
6
+ import {
7
+ createExecutionContext,
8
+ type SqlExecutionStack,
9
+ type SqlRuntimeExtensionDescriptor,
10
+ type SqlRuntimeTargetDescriptor,
11
+ } from '../src/sql-context';
12
+ import {
13
+ createStubAdapter,
14
+ createTestAdapterDescriptor,
15
+ createTestTargetDescriptor,
16
+ } from './utils';
17
+
18
+ const testContract: SqlContract<SqlStorage> = {
19
+ schemaVersion: '1',
20
+ targetFamily: 'sql',
21
+ target: 'postgres',
22
+ storageHash: coreHash('sha256:test'),
23
+ models: {},
24
+ relations: {},
25
+ storage: { tables: {} },
26
+ extensionPacks: {},
27
+ capabilities: {},
28
+ meta: {},
29
+ sources: {},
30
+ mappings: {
31
+ codecTypes: {},
32
+ operationTypes: {},
33
+ },
34
+ };
35
+
36
+ function createTestExtensionDescriptor(options?: {
37
+ hasCodecs?: boolean;
38
+ hasOperations?: boolean;
39
+ }): SqlRuntimeExtensionDescriptor<'postgres'> {
40
+ const { hasCodecs = false, hasOperations = false } = options ?? {};
41
+
42
+ const codecRegistry = hasCodecs
43
+ ? (() => {
44
+ const registry = createCodecRegistry();
45
+ registry.register(
46
+ codec({
47
+ typeId: 'test/ext@1',
48
+ targetTypes: ['ext'],
49
+ encode: (v: string) => v,
50
+ decode: (w: string) => w,
51
+ }),
52
+ );
53
+ return registry;
54
+ })()
55
+ : createCodecRegistry();
56
+
57
+ const operationsArray: ReadonlyArray<SqlOperationSignature> = hasOperations
58
+ ? [
59
+ {
60
+ forTypeId: 'test/ext@1',
61
+ method: 'testOp',
62
+ args: [],
63
+ returns: { kind: 'builtin' as const, type: 'number' as const },
64
+ lowering: {
65
+ targetFamily: 'sql' as const,
66
+ strategy: 'function' as const,
67
+ template: 'test()',
68
+ },
69
+ },
70
+ ]
71
+ : [];
72
+
73
+ return {
74
+ kind: 'extension' as const,
75
+ id: 'test-extension',
76
+ version: '0.0.1',
77
+ familyId: 'sql' as const,
78
+ targetId: 'postgres' as const,
79
+ codecs: () => codecRegistry,
80
+ operationSignatures: () => operationsArray,
81
+ parameterizedCodecs: () => [],
82
+ create() {
83
+ return {
84
+ familyId: 'sql' as const,
85
+ targetId: 'postgres' as const,
86
+ };
87
+ },
88
+ };
89
+ }
90
+
91
+ function createStack(options?: {
92
+ extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;
93
+ }): SqlExecutionStack<'postgres'> {
94
+ return {
95
+ target: createTestTargetDescriptor(),
96
+ adapter: createTestAdapterDescriptor(createStubAdapter()),
97
+ extensionPacks: options?.extensionPacks ?? [],
98
+ };
99
+ }
100
+
101
+ describe('createExecutionContext', () => {
102
+ it('creates context with adapter codecs from descriptor', () => {
103
+ const context = createExecutionContext({
104
+ contract: testContract,
105
+ stack: createStack(),
106
+ });
107
+
108
+ expect(context.contract).toBe(testContract);
109
+ expect(context.codecs.has('pg/int4@1')).toBe(true);
110
+ expect(context.operations).toBeDefined();
111
+ });
112
+
113
+ it('creates context with empty extension packs', () => {
114
+ const context = createExecutionContext({
115
+ contract: testContract,
116
+ stack: createStack({ extensionPacks: [] }),
117
+ });
118
+
119
+ expect(context.codecs.has('pg/int4@1')).toBe(true);
120
+ expect(context.codecs.has('test/ext@1')).toBe(false);
121
+ });
122
+
123
+ it('registers extension codecs from descriptors', () => {
124
+ const context = createExecutionContext({
125
+ contract: testContract,
126
+ stack: createStack({
127
+ extensionPacks: [createTestExtensionDescriptor({ hasCodecs: true })],
128
+ }),
129
+ });
130
+
131
+ expect(context.codecs.has('pg/int4@1')).toBe(true);
132
+ expect(context.codecs.has('test/ext@1')).toBe(true);
133
+ });
134
+
135
+ it('registers extension operations from descriptors', () => {
136
+ const context = createExecutionContext({
137
+ contract: testContract,
138
+ stack: createStack({
139
+ extensionPacks: [createTestExtensionDescriptor({ hasOperations: true })],
140
+ }),
141
+ });
142
+
143
+ const ops = context.operations.byType('test/ext@1');
144
+ expect(ops.length).toBe(1);
145
+ expect(ops[0]?.method).toBe('testOp');
146
+ });
147
+
148
+ it('handles extension with no contributions', () => {
149
+ const context = createExecutionContext({
150
+ contract: testContract,
151
+ stack: createStack({
152
+ extensionPacks: [createTestExtensionDescriptor({ hasCodecs: false, hasOperations: false })],
153
+ }),
154
+ });
155
+
156
+ expect(context.codecs.has('pg/int4@1')).toBe(true);
157
+ expect(context.codecs.has('test/ext@1')).toBe(false);
158
+ });
159
+ });
160
+
161
+ describe('comprehensive descriptor-based derivation', () => {
162
+ it('includes all expected codec IDs and operations from target, adapter, and extensions', () => {
163
+ const targetCodecRegistry = createCodecRegistry();
164
+ targetCodecRegistry.register(
165
+ codec({
166
+ typeId: 'target/special@1',
167
+ targetTypes: ['special'],
168
+ encode: (v: string) => v,
169
+ decode: (w: string) => w,
170
+ }),
171
+ );
172
+
173
+ const targetOps: SqlOperationSignature[] = [
174
+ {
175
+ forTypeId: 'target/special@1',
176
+ method: 'targetOp',
177
+ args: [],
178
+ returns: { kind: 'builtin' as const, type: 'string' as const },
179
+ lowering: {
180
+ targetFamily: 'sql' as const,
181
+ strategy: 'function' as const,
182
+ template: 'target_fn()',
183
+ },
184
+ },
185
+ ];
186
+
187
+ const target: SqlRuntimeTargetDescriptor<'postgres'> = {
188
+ kind: 'target' as const,
189
+ id: 'postgres',
190
+ version: '0.0.1',
191
+ familyId: 'sql' as const,
192
+ targetId: 'postgres' as const,
193
+ codecs: () => targetCodecRegistry,
194
+ operationSignatures: () => targetOps,
195
+ parameterizedCodecs: () => [],
196
+ create() {
197
+ return { familyId: 'sql' as const, targetId: 'postgres' as const };
198
+ },
199
+ };
200
+
201
+ const stack: SqlExecutionStack<'postgres'> = {
202
+ target,
203
+ adapter: createTestAdapterDescriptor(createStubAdapter()),
204
+ extensionPacks: [createTestExtensionDescriptor({ hasCodecs: true, hasOperations: true })],
205
+ };
206
+
207
+ const context = createExecutionContext({ contract: testContract, stack });
208
+
209
+ expect(context.codecs.has('target/special@1')).toBe(true);
210
+ expect(context.codecs.has('pg/int4@1')).toBe(true);
211
+ expect(context.codecs.has('test/ext@1')).toBe(true);
212
+
213
+ expect(context.operations.byType('target/special@1').length).toBe(1);
214
+ expect(context.operations.byType('target/special@1')[0]?.method).toBe('targetOp');
215
+ expect(context.operations.byType('test/ext@1').length).toBe(1);
216
+ expect(context.operations.byType('test/ext@1')[0]?.method).toBe('testOp');
217
+ });
218
+ });
219
+
220
+ describe('context.types presence', () => {
221
+ it('exists as empty object when no parameterized codecs are registered', () => {
222
+ const context = createExecutionContext({
223
+ contract: testContract,
224
+ stack: createStack(),
225
+ });
226
+
227
+ expect(context.types).toBeDefined();
228
+ expect(context.types).toEqual({});
229
+ });
230
+ });
231
+
232
+ describe('contract/stack validation errors', () => {
233
+ it('throws RUNTIME.CONTRACT_FAMILY_MISMATCH when contract targetFamily differs from stack', () => {
234
+ const mismatchedFamilyContract = {
235
+ ...testContract,
236
+ targetFamily: 'document',
237
+ } as unknown as SqlContract<SqlStorage>;
238
+
239
+ expect(() =>
240
+ createExecutionContext({ contract: mismatchedFamilyContract, stack: createStack() }),
241
+ ).toThrow(
242
+ expect.objectContaining({
243
+ code: 'RUNTIME.CONTRACT_FAMILY_MISMATCH',
244
+ category: 'RUNTIME',
245
+ severity: 'error',
246
+ details: {
247
+ actual: 'document',
248
+ expected: 'sql',
249
+ },
250
+ }),
251
+ );
252
+ });
253
+
254
+ it('throws RUNTIME.CONTRACT_TARGET_MISMATCH when contract target differs from stack', () => {
255
+ const mismatchedContract: SqlContract<SqlStorage> = {
256
+ ...testContract,
257
+ target: 'mysql',
258
+ };
259
+
260
+ expect(() =>
261
+ createExecutionContext({ contract: mismatchedContract, stack: createStack() }),
262
+ ).toThrow(
263
+ expect.objectContaining({
264
+ code: 'RUNTIME.CONTRACT_TARGET_MISMATCH',
265
+ category: 'RUNTIME',
266
+ severity: 'error',
267
+ details: {
268
+ actual: 'mysql',
269
+ expected: 'postgres',
270
+ },
271
+ }),
272
+ );
273
+ });
274
+
275
+ it('throws RUNTIME.MISSING_EXTENSION_PACK when contract requires extension not in stack', () => {
276
+ const contractWithExtension: SqlContract<SqlStorage> = {
277
+ ...testContract,
278
+ extensionPacks: {
279
+ 'required-extension': { id: 'required-extension', version: '1.0.0', capabilities: {} },
280
+ },
281
+ };
282
+
283
+ expect(() =>
284
+ createExecutionContext({ contract: contractWithExtension, stack: createStack() }),
285
+ ).toThrow(
286
+ expect.objectContaining({
287
+ code: 'RUNTIME.MISSING_EXTENSION_PACK',
288
+ category: 'RUNTIME',
289
+ severity: 'error',
290
+ details: {
291
+ packIds: ['required-extension'],
292
+ },
293
+ }),
294
+ );
295
+ });
296
+
297
+ it('lists all missing extension packs in a single error', () => {
298
+ const contractWithExtensions: SqlContract<SqlStorage> = {
299
+ ...testContract,
300
+ extensionPacks: {
301
+ 'ext-a': { id: 'ext-a', version: '1.0.0', capabilities: {} },
302
+ 'ext-b': { id: 'ext-b', version: '1.0.0', capabilities: {} },
303
+ },
304
+ };
305
+
306
+ expect(() =>
307
+ createExecutionContext({ contract: contractWithExtensions, stack: createStack() }),
308
+ ).toThrow(
309
+ expect.objectContaining({
310
+ code: 'RUNTIME.MISSING_EXTENSION_PACK',
311
+ details: {
312
+ packIds: expect.arrayContaining(['ext-a', 'ext-b']),
313
+ },
314
+ }),
315
+ );
316
+ });
317
+ });
318
+
319
+ describe('applyMutationDefaults', () => {
320
+ const contractWithDefaults: SqlContract<SqlStorage> = {
321
+ ...testContract,
322
+ storage: {
323
+ tables: {
324
+ user: {
325
+ columns: {
326
+ id: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
327
+ slug: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
328
+ },
329
+ uniques: [],
330
+ indexes: [],
331
+ foreignKeys: [],
332
+ },
333
+ },
334
+ },
335
+ execution: {
336
+ mutations: {
337
+ defaults: [
338
+ {
339
+ ref: { table: 'user', column: 'id' },
340
+ onCreate: { kind: 'generator', id: 'nanoid', params: { size: 8 } },
341
+ },
342
+ {
343
+ ref: { table: 'user', column: 'slug' },
344
+ onUpdate: { kind: 'generator', id: 'nanoid', params: { size: 6 } },
345
+ },
346
+ ],
347
+ },
348
+ },
349
+ };
350
+
351
+ it('applies create defaults with generator params', () => {
352
+ const context = createExecutionContext({
353
+ contract: contractWithDefaults,
354
+ stack: createStack(),
355
+ });
356
+
357
+ const applied = context.applyMutationDefaults({
358
+ op: 'create',
359
+ table: 'user',
360
+ values: {},
361
+ });
362
+
363
+ expect(applied).toEqual([
364
+ {
365
+ column: 'id',
366
+ value: expect.any(String),
367
+ },
368
+ ]);
369
+ expect((applied[0]?.value as string).length).toBe(8);
370
+ });
371
+
372
+ it('applies update defaults from onUpdate', () => {
373
+ const context = createExecutionContext({
374
+ contract: contractWithDefaults,
375
+ stack: createStack(),
376
+ });
377
+
378
+ const applied = context.applyMutationDefaults({
379
+ op: 'update',
380
+ table: 'user',
381
+ values: {},
382
+ });
383
+
384
+ expect(applied).toEqual([
385
+ {
386
+ column: 'slug',
387
+ value: expect.any(String),
388
+ },
389
+ ]);
390
+ expect((applied[0]?.value as string).length).toBe(6);
391
+ });
392
+ });
@@ -0,0 +1,87 @@
1
+ import type { ExecutionPlan } from '@prisma-next/contract/types';
2
+ import { coreHash } from '@prisma-next/contract/types';
3
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { SqlFamilyAdapter } from '../src/sql-family-adapter';
6
+
7
+ // Minimal test contract
8
+ const testContract: SqlContract<SqlStorage> = {
9
+ schemaVersion: '1',
10
+ targetFamily: 'sql',
11
+ target: 'postgres',
12
+ storageHash: coreHash('sha256:test-hash'),
13
+ models: {},
14
+ relations: {},
15
+ storage: { tables: {} },
16
+ extensionPacks: {},
17
+ capabilities: {},
18
+ meta: {},
19
+ sources: {},
20
+ mappings: {
21
+ codecTypes: {},
22
+ operationTypes: {},
23
+ },
24
+ };
25
+
26
+ describe('SqlFamilyAdapter', () => {
27
+ it('creates adapter with contract and marker reader', () => {
28
+ const adapter = new SqlFamilyAdapter(testContract);
29
+
30
+ expect(adapter.contract).toBe(testContract);
31
+ expect(adapter.markerReader).toBeDefined();
32
+ expect(adapter.markerReader.readMarkerStatement).toBeDefined();
33
+ });
34
+
35
+ it('validates plan with matching target and hash', () => {
36
+ const adapter = new SqlFamilyAdapter(testContract);
37
+ const plan: ExecutionPlan = {
38
+ meta: {
39
+ target: 'postgres',
40
+ storageHash: 'sha256:test-hash',
41
+ lane: 'sql',
42
+ paramDescriptors: [],
43
+ },
44
+ sql: 'SELECT 1',
45
+ params: [],
46
+ };
47
+
48
+ // Should not throw
49
+ expect(() => adapter.validatePlan(plan, testContract)).not.toThrow();
50
+ });
51
+
52
+ it('throws on plan target mismatch', () => {
53
+ const adapter = new SqlFamilyAdapter(testContract);
54
+ const plan: ExecutionPlan = {
55
+ meta: {
56
+ target: 'mysql', // Wrong target
57
+ storageHash: 'sha256:test-hash',
58
+ lane: 'sql',
59
+ paramDescriptors: [],
60
+ },
61
+ sql: 'SELECT 1',
62
+ params: [],
63
+ };
64
+
65
+ expect(() => adapter.validatePlan(plan, testContract)).toThrow(
66
+ 'Plan target does not match runtime target',
67
+ );
68
+ });
69
+
70
+ it('throws on plan storageHash mismatch', () => {
71
+ const adapter = new SqlFamilyAdapter(testContract);
72
+ const plan: ExecutionPlan = {
73
+ meta: {
74
+ target: 'postgres',
75
+ storageHash: 'sha256:different-hash', // Wrong hash
76
+ lane: 'sql',
77
+ paramDescriptors: [],
78
+ },
79
+ sql: 'SELECT 1',
80
+ params: [],
81
+ };
82
+
83
+ expect(() => adapter.validatePlan(plan, testContract)).toThrow(
84
+ 'Plan storage hash does not match runtime contract',
85
+ );
86
+ });
87
+ });