@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,103 +1,40 @@
1
- import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
2
- import type { SqlOperationSignature } from '@prisma-next/sql-operations';
3
- import type { CodecRegistry, SelectAst } from '@prisma-next/sql-relational-core/ast';
1
+ import { type Contract, coreHash, executionHash, profileHash } from '@prisma-next/contract/types';
2
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
+ import type { SqlOperationDescriptor } from '@prisma-next/sql-operations';
4
4
  import { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
5
5
  import { describe, expect, it } from 'vitest';
6
6
  import {
7
- createRuntimeContext,
7
+ createExecutionContext,
8
+ type SqlExecutionStack,
8
9
  type SqlRuntimeExtensionDescriptor,
9
- type SqlRuntimeExtensionInstance,
10
+ type SqlRuntimeTargetDescriptor,
10
11
  } from '../src/sql-context';
12
+ import {
13
+ createStubAdapter,
14
+ createTestAdapterDescriptor,
15
+ createTestTargetDescriptor,
16
+ } from './utils';
11
17
 
12
- // Minimal test contract
13
- const testContract: SqlContract<SqlStorage> = {
14
- schemaVersion: '1',
18
+ const testContract: Contract<SqlStorage> = {
15
19
  targetFamily: 'sql',
16
20
  target: 'postgres',
17
- coreHash: 'sha256:test',
21
+ profileHash: profileHash('sha256:test'),
18
22
  models: {},
19
- relations: {},
20
- storage: { tables: {} },
23
+ roots: {},
24
+ storage: { storageHash: coreHash('sha256:test'), tables: {} },
21
25
  extensionPacks: {},
22
26
  capabilities: {},
23
27
  meta: {},
24
- sources: {},
25
- mappings: {
26
- codecTypes: {},
27
- operationTypes: {},
28
- },
29
28
  };
30
29
 
31
- // Stub adapter codecs
32
- function createStubCodecs(): CodecRegistry {
33
- const registry = createCodecRegistry();
34
- registry.register(
35
- codec({
36
- typeId: 'pg/int4@1',
37
- targetTypes: ['int4'],
38
- encode: (v: number) => v,
39
- decode: (w: number) => w,
40
- }),
41
- );
42
- return registry;
43
- }
44
-
45
- // Create a test adapter descriptor
46
- function createTestAdapterDescriptor() {
47
- const codecs = createStubCodecs();
48
- return {
49
- kind: 'adapter' as const,
50
- id: 'test-adapter',
51
- version: '0.0.1',
52
- familyId: 'sql' as const,
53
- targetId: 'postgres' as const,
54
- create() {
55
- return {
56
- familyId: 'sql' as const,
57
- targetId: 'postgres' as const,
58
- profile: {
59
- id: 'test-profile',
60
- target: 'postgres',
61
- capabilities: {},
62
- codecs() {
63
- return codecs;
64
- },
65
- },
66
- lower(ast: SelectAst) {
67
- return {
68
- profileId: 'test-profile',
69
- body: Object.freeze({ sql: JSON.stringify(ast), params: [] }),
70
- };
71
- },
72
- };
73
- },
74
- };
75
- }
76
-
77
- // Create a test target descriptor
78
- function createTestTargetDescriptor() {
79
- return {
80
- kind: 'target' as const,
81
- id: 'postgres',
82
- version: '0.0.1',
83
- familyId: 'sql' as const,
84
- targetId: 'postgres' as const,
85
- create() {
86
- return { familyId: 'sql' as const, targetId: 'postgres' as const };
87
- },
88
- };
89
- }
90
-
91
- // Create a test extension descriptor
92
30
  function createTestExtensionDescriptor(options?: {
93
31
  hasCodecs?: boolean;
94
32
  hasOperations?: boolean;
95
33
  }): SqlRuntimeExtensionDescriptor<'postgres'> {
96
34
  const { hasCodecs = false, hasOperations = false } = options ?? {};
97
35
 
98
- // Build the codecs function if needed
99
- const codecsFn = hasCodecs
100
- ? () => {
36
+ const codecRegistry = hasCodecs
37
+ ? (() => {
101
38
  const registry = createCodecRegistry();
102
39
  registry.register(
103
40
  codec({
@@ -108,21 +45,23 @@ function createTestExtensionDescriptor(options?: {
108
45
  }),
109
46
  );
110
47
  return registry;
111
- }
112
- : undefined;
48
+ })()
49
+ : createCodecRegistry();
113
50
 
114
- // Build the operations function if needed
115
- const operationsFn = hasOperations
116
- ? (): ReadonlyArray<SqlOperationSignature> => [
51
+ const operationsArray: ReadonlyArray<SqlOperationDescriptor> = hasOperations
52
+ ? [
117
53
  {
118
- forTypeId: 'test/ext@1',
119
54
  method: 'testOp',
120
- args: [],
121
- returns: { kind: 'builtin', type: 'number' },
122
- lowering: { targetFamily: 'sql', strategy: 'function', template: 'test()' },
55
+ args: [{ codecId: 'test/ext@1', nullable: false }],
56
+ returns: { codecId: 'test/ext@1', nullable: false },
57
+ lowering: {
58
+ targetFamily: 'sql' as const,
59
+ strategy: 'function' as const,
60
+ template: 'test()',
61
+ },
123
62
  },
124
63
  ]
125
- : undefined;
64
+ : [];
126
65
 
127
66
  return {
128
67
  kind: 'extension' as const,
@@ -130,88 +69,316 @@ function createTestExtensionDescriptor(options?: {
130
69
  version: '0.0.1',
131
70
  familyId: 'sql' as const,
132
71
  targetId: 'postgres' as const,
133
- create(): SqlRuntimeExtensionInstance<'postgres'> {
134
- // Return object with optional methods only if they exist
135
- const instance: SqlRuntimeExtensionInstance<'postgres'> = {
72
+ codecs: () => codecRegistry,
73
+ queryOperations: () => operationsArray,
74
+ parameterizedCodecs: () => [],
75
+ create() {
76
+ return {
136
77
  familyId: 'sql' as const,
137
78
  targetId: 'postgres' as const,
138
79
  };
139
- if (codecsFn) {
140
- (instance as { codecs?: () => CodecRegistry }).codecs = codecsFn;
141
- }
142
- if (operationsFn) {
143
- (instance as { operations?: () => ReadonlyArray<SqlOperationSignature> }).operations =
144
- operationsFn;
145
- }
146
- return instance;
147
80
  },
148
81
  };
149
82
  }
150
83
 
151
- describe('createRuntimeContext', () => {
152
- it('creates context with adapter codecs', () => {
153
- const context = createRuntimeContext({
84
+ function createStack(options?: {
85
+ extensionPacks?: ReadonlyArray<SqlRuntimeExtensionDescriptor<'postgres'>>;
86
+ }): SqlExecutionStack<'postgres'> {
87
+ return {
88
+ target: createTestTargetDescriptor(),
89
+ adapter: createTestAdapterDescriptor(createStubAdapter()),
90
+ extensionPacks: options?.extensionPacks ?? [],
91
+ };
92
+ }
93
+
94
+ describe('createExecutionContext', () => {
95
+ it('creates context with adapter codecs from descriptor', () => {
96
+ const context = createExecutionContext({
154
97
  contract: testContract,
155
- target: createTestTargetDescriptor(),
156
- adapter: createTestAdapterDescriptor(),
98
+ stack: createStack(),
157
99
  });
158
100
 
159
101
  expect(context.contract).toBe(testContract);
160
- expect(context.adapter).toBeDefined();
161
102
  expect(context.codecs.has('pg/int4@1')).toBe(true);
162
- expect(context.operations).toBeDefined();
103
+ expect(context.queryOperations).toBeDefined();
163
104
  });
164
105
 
165
106
  it('creates context with empty extension packs', () => {
166
- const context = createRuntimeContext({
107
+ const context = createExecutionContext({
167
108
  contract: testContract,
168
- target: createTestTargetDescriptor(),
169
- adapter: createTestAdapterDescriptor(),
170
- extensionPacks: [],
109
+ stack: createStack({ extensionPacks: [] }),
171
110
  });
172
111
 
173
112
  expect(context.codecs.has('pg/int4@1')).toBe(true);
174
- // No extension codecs registered
175
113
  expect(context.codecs.has('test/ext@1')).toBe(false);
176
114
  });
177
115
 
178
- it('registers extension codecs', () => {
179
- const context = createRuntimeContext({
116
+ it('registers extension codecs from descriptors', () => {
117
+ const context = createExecutionContext({
180
118
  contract: testContract,
181
- target: createTestTargetDescriptor(),
182
- adapter: createTestAdapterDescriptor(),
183
- extensionPacks: [createTestExtensionDescriptor({ hasCodecs: true })],
119
+ stack: createStack({
120
+ extensionPacks: [createTestExtensionDescriptor({ hasCodecs: true })],
121
+ }),
184
122
  });
185
123
 
186
- // Adapter codec
187
124
  expect(context.codecs.has('pg/int4@1')).toBe(true);
188
- // Extension codec
189
125
  expect(context.codecs.has('test/ext@1')).toBe(true);
190
126
  });
191
127
 
192
- it('registers extension operations', () => {
193
- const context = createRuntimeContext({
128
+ it('registers extension operations from descriptors', () => {
129
+ const context = createExecutionContext({
194
130
  contract: testContract,
195
- target: createTestTargetDescriptor(),
196
- adapter: createTestAdapterDescriptor(),
197
- extensionPacks: [createTestExtensionDescriptor({ hasOperations: true })],
131
+ stack: createStack({
132
+ extensionPacks: [createTestExtensionDescriptor({ hasOperations: true })],
133
+ }),
198
134
  });
199
135
 
200
- const ops = context.operations.byType('test/ext@1');
201
- expect(ops.length).toBe(1);
202
- expect(ops[0]?.method).toBe('testOp');
136
+ const entries = context.queryOperations.entries();
137
+ expect(entries['testOp']).toBeDefined();
203
138
  });
204
139
 
205
- it('handles extension without codecs or operations', () => {
206
- const context = createRuntimeContext({
140
+ it('handles extension with no contributions', () => {
141
+ const context = createExecutionContext({
207
142
  contract: testContract,
208
- target: createTestTargetDescriptor(),
209
- adapter: createTestAdapterDescriptor(),
210
- extensionPacks: [createTestExtensionDescriptor({ hasCodecs: false, hasOperations: false })],
143
+ stack: createStack({
144
+ extensionPacks: [createTestExtensionDescriptor({ hasCodecs: false, hasOperations: false })],
145
+ }),
211
146
  });
212
147
 
213
- // Only adapter codec
214
148
  expect(context.codecs.has('pg/int4@1')).toBe(true);
215
149
  expect(context.codecs.has('test/ext@1')).toBe(false);
216
150
  });
217
151
  });
152
+
153
+ describe('comprehensive descriptor-based derivation', () => {
154
+ it('includes all expected codec IDs and operations from target, adapter, and extensions', () => {
155
+ const targetCodecRegistry = createCodecRegistry();
156
+ targetCodecRegistry.register(
157
+ codec({
158
+ typeId: 'target/special@1',
159
+ targetTypes: ['special'],
160
+ encode: (v: string) => v,
161
+ decode: (w: string) => w,
162
+ }),
163
+ );
164
+
165
+ const targetOps: SqlOperationDescriptor[] = [
166
+ {
167
+ method: 'targetOp',
168
+ args: [{ codecId: 'target/special@1', nullable: false }],
169
+ returns: { codecId: 'target/special@1', nullable: false },
170
+ lowering: {
171
+ targetFamily: 'sql' as const,
172
+ strategy: 'function' as const,
173
+ template: 'target_fn()',
174
+ },
175
+ },
176
+ ];
177
+
178
+ const target: SqlRuntimeTargetDescriptor<'postgres'> = {
179
+ kind: 'target' as const,
180
+ id: 'postgres',
181
+ version: '0.0.1',
182
+ familyId: 'sql' as const,
183
+ targetId: 'postgres' as const,
184
+ codecs: () => targetCodecRegistry,
185
+ queryOperations: () => targetOps,
186
+ parameterizedCodecs: () => [],
187
+ create() {
188
+ return { familyId: 'sql' as const, targetId: 'postgres' as const };
189
+ },
190
+ };
191
+
192
+ const stack: SqlExecutionStack<'postgres'> = {
193
+ target,
194
+ adapter: createTestAdapterDescriptor(createStubAdapter()),
195
+ extensionPacks: [createTestExtensionDescriptor({ hasCodecs: true, hasOperations: true })],
196
+ };
197
+
198
+ const context = createExecutionContext({ contract: testContract, stack });
199
+
200
+ expect(context.codecs.has('target/special@1')).toBe(true);
201
+ expect(context.codecs.has('pg/int4@1')).toBe(true);
202
+ expect(context.codecs.has('test/ext@1')).toBe(true);
203
+
204
+ const entries = context.queryOperations.entries();
205
+ expect(entries['targetOp']).toBeDefined();
206
+ expect(entries['testOp']).toBeDefined();
207
+ });
208
+ });
209
+
210
+ describe('context.types presence', () => {
211
+ it('exists as empty object when no parameterized codecs are registered', () => {
212
+ const context = createExecutionContext({
213
+ contract: testContract,
214
+ stack: createStack(),
215
+ });
216
+
217
+ expect(context.types).toBeDefined();
218
+ expect(context.types).toEqual({});
219
+ });
220
+ });
221
+
222
+ describe('contract/stack validation errors', () => {
223
+ it('throws RUNTIME.CONTRACT_FAMILY_MISMATCH when contract targetFamily differs from stack', () => {
224
+ const mismatchedFamilyContract = {
225
+ ...testContract,
226
+ targetFamily: 'document',
227
+ } as unknown as Contract<SqlStorage>;
228
+
229
+ expect(() =>
230
+ createExecutionContext({ contract: mismatchedFamilyContract, stack: createStack() }),
231
+ ).toThrow(
232
+ expect.objectContaining({
233
+ code: 'RUNTIME.CONTRACT_FAMILY_MISMATCH',
234
+ category: 'RUNTIME',
235
+ severity: 'error',
236
+ details: {
237
+ actual: 'document',
238
+ expected: 'sql',
239
+ },
240
+ }),
241
+ );
242
+ });
243
+
244
+ it('throws RUNTIME.CONTRACT_TARGET_MISMATCH when contract target differs from stack', () => {
245
+ const mismatchedContract: Contract<SqlStorage> = {
246
+ ...testContract,
247
+ target: 'mysql',
248
+ };
249
+
250
+ expect(() =>
251
+ createExecutionContext({ contract: mismatchedContract, stack: createStack() }),
252
+ ).toThrow(
253
+ expect.objectContaining({
254
+ code: 'RUNTIME.CONTRACT_TARGET_MISMATCH',
255
+ category: 'RUNTIME',
256
+ severity: 'error',
257
+ details: {
258
+ actual: 'mysql',
259
+ expected: 'postgres',
260
+ },
261
+ }),
262
+ );
263
+ });
264
+
265
+ it('throws RUNTIME.MISSING_EXTENSION_PACK when contract requires extension not in stack', () => {
266
+ const contractWithExtension: Contract<SqlStorage> = {
267
+ ...testContract,
268
+ extensionPacks: {
269
+ 'required-extension': { id: 'required-extension', version: '1.0.0', capabilities: {} },
270
+ },
271
+ };
272
+
273
+ expect(() =>
274
+ createExecutionContext({ contract: contractWithExtension, stack: createStack() }),
275
+ ).toThrow(
276
+ expect.objectContaining({
277
+ code: 'RUNTIME.MISSING_EXTENSION_PACK',
278
+ category: 'RUNTIME',
279
+ severity: 'error',
280
+ details: {
281
+ packIds: ['required-extension'],
282
+ },
283
+ }),
284
+ );
285
+ });
286
+
287
+ it('lists all missing extension packs in a single error', () => {
288
+ const contractWithExtensions: Contract<SqlStorage> = {
289
+ ...testContract,
290
+ extensionPacks: {
291
+ 'ext-a': { id: 'ext-a', version: '1.0.0', capabilities: {} },
292
+ 'ext-b': { id: 'ext-b', version: '1.0.0', capabilities: {} },
293
+ },
294
+ };
295
+
296
+ expect(() =>
297
+ createExecutionContext({ contract: contractWithExtensions, stack: createStack() }),
298
+ ).toThrow(
299
+ expect.objectContaining({
300
+ code: 'RUNTIME.MISSING_EXTENSION_PACK',
301
+ details: {
302
+ packIds: expect.arrayContaining(['ext-a', 'ext-b']),
303
+ },
304
+ }),
305
+ );
306
+ });
307
+ });
308
+
309
+ describe('applyMutationDefaults', () => {
310
+ const contractWithDefaults: Contract<SqlStorage> = {
311
+ ...testContract,
312
+ storage: {
313
+ storageHash: coreHash('sha256:test'),
314
+ tables: {
315
+ user: {
316
+ columns: {
317
+ id: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
318
+ slug: { nativeType: 'text', codecId: 'pg/text@1', nullable: false },
319
+ },
320
+ uniques: [],
321
+ indexes: [],
322
+ foreignKeys: [],
323
+ },
324
+ },
325
+ },
326
+ execution: {
327
+ executionHash: executionHash('sha256:test'),
328
+ mutations: {
329
+ defaults: [
330
+ {
331
+ ref: { table: 'user', column: 'id' },
332
+ onCreate: { kind: 'generator', id: 'nanoid', params: { size: 8 } },
333
+ },
334
+ {
335
+ ref: { table: 'user', column: 'slug' },
336
+ onUpdate: { kind: 'generator', id: 'nanoid', params: { size: 6 } },
337
+ },
338
+ ],
339
+ },
340
+ },
341
+ };
342
+
343
+ it('applies create defaults with generator params', () => {
344
+ const context = createExecutionContext({
345
+ contract: contractWithDefaults,
346
+ stack: createStack(),
347
+ });
348
+
349
+ const applied = context.applyMutationDefaults({
350
+ op: 'create',
351
+ table: 'user',
352
+ values: {},
353
+ });
354
+
355
+ expect(applied).toEqual([
356
+ {
357
+ column: 'id',
358
+ value: expect.any(String),
359
+ },
360
+ ]);
361
+ expect((applied[0]?.value as string).length).toBe(8);
362
+ });
363
+
364
+ it('applies update defaults from onUpdate', () => {
365
+ const context = createExecutionContext({
366
+ contract: contractWithDefaults,
367
+ stack: createStack(),
368
+ });
369
+
370
+ const applied = context.applyMutationDefaults({
371
+ op: 'update',
372
+ table: 'user',
373
+ values: {},
374
+ });
375
+
376
+ expect(applied).toEqual([
377
+ {
378
+ column: 'slug',
379
+ value: expect.any(String),
380
+ },
381
+ ]);
382
+ expect((applied[0]?.value as string).length).toBe(6);
383
+ });
384
+ });
@@ -1,42 +1,59 @@
1
- import type { ExecutionPlan } from '@prisma-next/contract/types';
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 type { SqlStorage } from '@prisma-next/sql-contract/types';
4
+ import type { AdapterProfile } from '@prisma-next/sql-relational-core/ast';
3
5
  import { describe, expect, it } from 'vitest';
4
6
  import { SqlFamilyAdapter } from '../src/sql-family-adapter';
5
7
 
6
8
  // Minimal test contract
7
- const testContract: SqlContract<SqlStorage> = {
8
- schemaVersion: '1',
9
+ const testContract: Contract<SqlStorage> = {
9
10
  targetFamily: 'sql',
10
11
  target: 'postgres',
11
- coreHash: 'sha256:test-hash',
12
+ profileHash: profileHash('sha256:test-hash'),
12
13
  models: {},
13
- relations: {},
14
- storage: { tables: {} },
14
+ roots: {},
15
+ storage: { storageHash: coreHash('sha256:test-hash'), tables: {} },
15
16
  extensionPacks: {},
16
17
  capabilities: {},
17
18
  meta: {},
18
- sources: {},
19
- mappings: {
20
- codecTypes: {},
21
- operationTypes: {},
19
+ };
20
+
21
+ const testProfile: AdapterProfile = {
22
+ id: 'test/default@1',
23
+ target: 'postgres',
24
+ capabilities: {},
25
+ codecs: () => {
26
+ throw new Error('not needed in test');
22
27
  },
28
+ readMarkerStatement: () => ({
29
+ sql: 'SELECT core_hash, profile_hash FROM prisma_contract.marker WHERE id = $1',
30
+ params: [1],
31
+ }),
23
32
  };
24
33
 
25
34
  describe('SqlFamilyAdapter', () => {
26
35
  it('creates adapter with contract and marker reader', () => {
27
- const adapter = new SqlFamilyAdapter(testContract);
36
+ const adapter = new SqlFamilyAdapter(testContract, testProfile);
28
37
 
29
38
  expect(adapter.contract).toBe(testContract);
30
39
  expect(adapter.markerReader).toBeDefined();
31
40
  expect(adapter.markerReader.readMarkerStatement).toBeDefined();
32
41
  });
33
42
 
43
+ it('delegates readMarkerStatement to adapter profile', () => {
44
+ const adapter = new SqlFamilyAdapter(testContract, testProfile);
45
+ const stmt = adapter.markerReader.readMarkerStatement();
46
+
47
+ expect(stmt.sql).toContain('prisma_contract.marker');
48
+ expect(stmt.params).toEqual([1]);
49
+ });
50
+
34
51
  it('validates plan with matching target and hash', () => {
35
- const adapter = new SqlFamilyAdapter(testContract);
52
+ const adapter = new SqlFamilyAdapter(testContract, testProfile);
36
53
  const plan: ExecutionPlan = {
37
54
  meta: {
38
55
  target: 'postgres',
39
- coreHash: 'sha256:test-hash',
56
+ storageHash: 'sha256:test-hash',
40
57
  lane: 'sql',
41
58
  paramDescriptors: [],
42
59
  },
@@ -49,11 +66,11 @@ describe('SqlFamilyAdapter', () => {
49
66
  });
50
67
 
51
68
  it('throws on plan target mismatch', () => {
52
- const adapter = new SqlFamilyAdapter(testContract);
69
+ const adapter = new SqlFamilyAdapter(testContract, testProfile);
53
70
  const plan: ExecutionPlan = {
54
71
  meta: {
55
72
  target: 'mysql', // Wrong target
56
- coreHash: 'sha256:test-hash',
73
+ storageHash: 'sha256:test-hash',
57
74
  lane: 'sql',
58
75
  paramDescriptors: [],
59
76
  },
@@ -66,12 +83,12 @@ describe('SqlFamilyAdapter', () => {
66
83
  );
67
84
  });
68
85
 
69
- it('throws on plan coreHash mismatch', () => {
70
- const adapter = new SqlFamilyAdapter(testContract);
86
+ it('throws on plan storageHash mismatch', () => {
87
+ const adapter = new SqlFamilyAdapter(testContract, testProfile);
71
88
  const plan: ExecutionPlan = {
72
89
  meta: {
73
90
  target: 'postgres',
74
- coreHash: 'sha256:different-hash', // Wrong hash
91
+ storageHash: 'sha256:different-hash', // Wrong hash
75
92
  lane: 'sql',
76
93
  paramDescriptors: [],
77
94
  },
@@ -80,7 +97,7 @@ describe('SqlFamilyAdapter', () => {
80
97
  };
81
98
 
82
99
  expect(() => adapter.validatePlan(plan, testContract)).toThrow(
83
- 'Plan core hash does not match runtime contract',
100
+ 'Plan storage hash does not match runtime contract',
84
101
  );
85
102
  });
86
103
  });