@prisma-next/sql-runtime 0.3.0-dev.3 → 0.3.0-dev.30
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.
- package/README.md +1 -2
- package/dist/{accelerate-EEKAFGN3-SHR4XFVV.js → accelerate-EEKAFGN3-P6A6XJWJ.js} +28 -28
- package/dist/{accelerate-EEKAFGN3-SHR4XFVV.js.map → accelerate-EEKAFGN3-P6A6XJWJ.js.map} +1 -1
- package/dist/{chunk-C6I3V3DM.js → chunk-APA6GHYY.js} +84 -2
- package/dist/chunk-APA6GHYY.js.map +1 -0
- package/dist/{dist-LCVVJCGI.js → dist-AQ3LWXOX.js} +13 -13
- package/dist/{dist-LCVVJCGI.js.map → dist-AQ3LWXOX.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/src/codecs/decoding.d.ts +4 -0
- package/dist/src/codecs/decoding.d.ts.map +1 -0
- package/dist/src/codecs/encoding.d.ts +5 -0
- package/dist/src/codecs/encoding.d.ts.map +1 -0
- package/dist/src/codecs/validation.d.ts +6 -0
- package/dist/src/codecs/validation.d.ts.map +1 -0
- package/dist/src/exports/index.d.ts +11 -0
- package/dist/src/exports/index.d.ts.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/lower-sql-plan.d.ts +15 -0
- package/dist/src/lower-sql-plan.d.ts.map +1 -0
- package/dist/src/sql-context.d.ts +130 -0
- package/dist/src/sql-context.d.ts.map +1 -0
- package/dist/src/sql-family-adapter.d.ts +10 -0
- package/dist/src/sql-family-adapter.d.ts.map +1 -0
- package/dist/src/sql-marker.d.ts +22 -0
- package/dist/src/sql-marker.d.ts.map +1 -0
- package/dist/src/sql-runtime.d.ts +25 -0
- package/dist/src/sql-runtime.d.ts.map +1 -0
- package/dist/test/utils.d.ts +20 -24
- package/dist/test/utils.d.ts.map +1 -0
- package/dist/test/utils.js +26 -26
- package/dist/test/utils.js.map +1 -1
- package/package.json +25 -22
- package/src/codecs/decoding.ts +140 -0
- package/src/codecs/encoding.ts +76 -0
- package/src/codecs/validation.ts +67 -0
- package/src/exports/index.ts +40 -0
- package/src/index.ts +1 -0
- package/src/lower-sql-plan.ts +32 -0
- package/src/sql-context.ts +402 -0
- package/src/sql-family-adapter.ts +43 -0
- package/src/sql-marker.ts +105 -0
- package/src/sql-runtime.ts +166 -0
- package/test/async-iterable-result.test.ts +136 -0
- package/test/context.types.test-d.ts +70 -0
- package/test/parameterized-types.test.ts +553 -0
- package/test/sql-context.test.ts +217 -0
- package/test/sql-family-adapter.test.ts +86 -0
- package/test/sql-runtime.test.ts +155 -0
- package/test/utils.ts +266 -0
- package/dist/chunk-C6I3V3DM.js.map +0 -1
- package/dist/index.d.ts +0 -29
- package/dist/sql-runtime-DgEbg2OP.d.ts +0 -109
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
import type { SqlContract, SqlStorage, StorageTypeInstance } from '@prisma-next/sql-contract/types';
|
|
2
|
+
import type { SelectAst } from '@prisma-next/sql-relational-core/ast';
|
|
3
|
+
import { codec, createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
|
|
4
|
+
import type { Type } from 'arktype';
|
|
5
|
+
import { type as arktype } from 'arktype';
|
|
6
|
+
import { describe, expect, it } from 'vitest';
|
|
7
|
+
import {
|
|
8
|
+
createRuntimeContext,
|
|
9
|
+
type RuntimeParameterizedCodecDescriptor,
|
|
10
|
+
type SqlRuntimeExtensionDescriptor,
|
|
11
|
+
type SqlRuntimeExtensionInstance,
|
|
12
|
+
} from '../src/sql-context';
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Test helpers
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
function createTestContract(
|
|
19
|
+
options?: Partial<{
|
|
20
|
+
types: Record<string, StorageTypeInstance>;
|
|
21
|
+
tableColumns: Record<
|
|
22
|
+
string,
|
|
23
|
+
{
|
|
24
|
+
nativeType: string;
|
|
25
|
+
codecId: string;
|
|
26
|
+
nullable: boolean;
|
|
27
|
+
typeParams?: Record<string, unknown>;
|
|
28
|
+
typeRef?: string;
|
|
29
|
+
}
|
|
30
|
+
>;
|
|
31
|
+
}>,
|
|
32
|
+
): SqlContract<SqlStorage> {
|
|
33
|
+
return {
|
|
34
|
+
schemaVersion: '1',
|
|
35
|
+
targetFamily: 'sql',
|
|
36
|
+
target: 'postgres',
|
|
37
|
+
coreHash: 'sha256:test',
|
|
38
|
+
models: {},
|
|
39
|
+
relations: {},
|
|
40
|
+
storage: {
|
|
41
|
+
tables: {
|
|
42
|
+
test: {
|
|
43
|
+
columns: options?.tableColumns ?? {
|
|
44
|
+
id: { nativeType: 'int4', codecId: 'pg/int4@1', nullable: false },
|
|
45
|
+
},
|
|
46
|
+
primaryKey: { columns: ['id'] },
|
|
47
|
+
uniques: [],
|
|
48
|
+
indexes: [],
|
|
49
|
+
foreignKeys: [],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
...(options?.types ? { types: options.types } : {}),
|
|
53
|
+
},
|
|
54
|
+
extensionPacks: {},
|
|
55
|
+
capabilities: {},
|
|
56
|
+
meta: {},
|
|
57
|
+
sources: {},
|
|
58
|
+
mappings: {
|
|
59
|
+
codecTypes: {},
|
|
60
|
+
operationTypes: {},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createTestAdapterDescriptor() {
|
|
66
|
+
const codecs = createCodecRegistry();
|
|
67
|
+
codecs.register(
|
|
68
|
+
codec({
|
|
69
|
+
typeId: 'pg/int4@1',
|
|
70
|
+
targetTypes: ['int4'],
|
|
71
|
+
encode: (v: number) => v,
|
|
72
|
+
decode: (w: number) => w,
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
kind: 'adapter' as const,
|
|
78
|
+
id: 'test-adapter',
|
|
79
|
+
version: '0.0.1',
|
|
80
|
+
familyId: 'sql' as const,
|
|
81
|
+
targetId: 'postgres' as const,
|
|
82
|
+
create() {
|
|
83
|
+
return {
|
|
84
|
+
familyId: 'sql' as const,
|
|
85
|
+
targetId: 'postgres' as const,
|
|
86
|
+
profile: {
|
|
87
|
+
id: 'test-profile',
|
|
88
|
+
target: 'postgres',
|
|
89
|
+
capabilities: {},
|
|
90
|
+
codecs() {
|
|
91
|
+
return codecs;
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
lower(ast: SelectAst) {
|
|
95
|
+
return {
|
|
96
|
+
profileId: 'test-profile',
|
|
97
|
+
body: Object.freeze({ sql: JSON.stringify(ast), params: [] }),
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function createTestTargetDescriptor() {
|
|
106
|
+
return {
|
|
107
|
+
kind: 'target' as const,
|
|
108
|
+
id: 'postgres',
|
|
109
|
+
version: '0.0.1',
|
|
110
|
+
familyId: 'sql' as const,
|
|
111
|
+
targetId: 'postgres' as const,
|
|
112
|
+
create() {
|
|
113
|
+
return { familyId: 'sql' as const, targetId: 'postgres' as const };
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// =============================================================================
|
|
119
|
+
// Tests: Parameterized type validation
|
|
120
|
+
// =============================================================================
|
|
121
|
+
|
|
122
|
+
describe('parameterized types', () => {
|
|
123
|
+
describe('storage.types validation', () => {
|
|
124
|
+
it('creates context with empty storage.types', () => {
|
|
125
|
+
const contract = createTestContract({ types: {} });
|
|
126
|
+
const context = createRuntimeContext({
|
|
127
|
+
contract,
|
|
128
|
+
target: createTestTargetDescriptor(),
|
|
129
|
+
adapter: createTestAdapterDescriptor(),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(context.contract.storage.types).toEqual({});
|
|
133
|
+
expect(context.types).toEqual({});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('creates context with storage.types containing valid type instances', () => {
|
|
137
|
+
const contract = createTestContract({
|
|
138
|
+
types: {
|
|
139
|
+
Vector1536: {
|
|
140
|
+
codecId: 'pg/vector@1',
|
|
141
|
+
nativeType: 'vector',
|
|
142
|
+
typeParams: { length: 1536 },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
const context = createRuntimeContext({
|
|
147
|
+
contract,
|
|
148
|
+
target: createTestTargetDescriptor(),
|
|
149
|
+
adapter: createTestAdapterDescriptor(),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expect(context.contract.storage.types).toEqual({
|
|
153
|
+
Vector1536: {
|
|
154
|
+
codecId: 'pg/vector@1',
|
|
155
|
+
nativeType: 'vector',
|
|
156
|
+
typeParams: { length: 1536 },
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
// types registry should contain the raw type instance (no init hook provided)
|
|
160
|
+
expect(context.types?.['Vector1536']).toBeDefined();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('typeParams validation with paramsSchema', () => {
|
|
165
|
+
const vectorParamsSchema = arktype({
|
|
166
|
+
length: 'number',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
function createVectorExtensionDescriptor(options?: {
|
|
170
|
+
paramsSchema?: Type<{ length: number }>;
|
|
171
|
+
init?: (params: { length: number }) => { dimensions: number };
|
|
172
|
+
}): SqlRuntimeExtensionDescriptor<'postgres'> {
|
|
173
|
+
// biome-ignore lint/suspicious/noExplicitAny: test helper with flexible type params
|
|
174
|
+
const parameterizedCodecs: RuntimeParameterizedCodecDescriptor<any, any>[] = [
|
|
175
|
+
{
|
|
176
|
+
codecId: 'pg/vector@1',
|
|
177
|
+
paramsSchema: options?.paramsSchema ?? vectorParamsSchema,
|
|
178
|
+
...(options?.init ? { init: options.init } : {}),
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
kind: 'extension' as const,
|
|
184
|
+
id: 'pgvector',
|
|
185
|
+
version: '0.0.1',
|
|
186
|
+
familyId: 'sql' as const,
|
|
187
|
+
targetId: 'postgres' as const,
|
|
188
|
+
create(): SqlRuntimeExtensionInstance<'postgres'> {
|
|
189
|
+
const registry = createCodecRegistry();
|
|
190
|
+
registry.register(
|
|
191
|
+
codec({
|
|
192
|
+
typeId: 'pg/vector@1',
|
|
193
|
+
targetTypes: ['vector'],
|
|
194
|
+
encode: (v: number[]) => v,
|
|
195
|
+
decode: (w: number[]) => w,
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
return {
|
|
199
|
+
familyId: 'sql' as const,
|
|
200
|
+
targetId: 'postgres' as const,
|
|
201
|
+
codecs: () => registry,
|
|
202
|
+
parameterizedCodecs: () => parameterizedCodecs,
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
it('validates typeParams against codec paramsSchema', () => {
|
|
209
|
+
const contract = createTestContract({
|
|
210
|
+
types: {
|
|
211
|
+
Vector1536: {
|
|
212
|
+
codecId: 'pg/vector@1',
|
|
213
|
+
nativeType: 'vector',
|
|
214
|
+
typeParams: { length: 1536 },
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const context = createRuntimeContext({
|
|
220
|
+
contract,
|
|
221
|
+
target: createTestTargetDescriptor(),
|
|
222
|
+
adapter: createTestAdapterDescriptor(),
|
|
223
|
+
extensionPacks: [createVectorExtensionDescriptor()],
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(context.types?.['Vector1536']).toBeDefined();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('rejects invalid typeParams with stable error code', () => {
|
|
230
|
+
const contract = createTestContract({
|
|
231
|
+
types: {
|
|
232
|
+
InvalidVector: {
|
|
233
|
+
codecId: 'pg/vector@1',
|
|
234
|
+
nativeType: 'vector',
|
|
235
|
+
typeParams: { length: 'not-a-number' },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
let thrownError: unknown;
|
|
241
|
+
try {
|
|
242
|
+
createRuntimeContext({
|
|
243
|
+
contract,
|
|
244
|
+
target: createTestTargetDescriptor(),
|
|
245
|
+
adapter: createTestAdapterDescriptor(),
|
|
246
|
+
extensionPacks: [createVectorExtensionDescriptor()],
|
|
247
|
+
});
|
|
248
|
+
} catch (e) {
|
|
249
|
+
thrownError = e;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
expect(thrownError).toBeDefined();
|
|
253
|
+
expect(thrownError).toMatchObject({
|
|
254
|
+
code: 'RUNTIME.TYPE_PARAMS_INVALID',
|
|
255
|
+
category: 'RUNTIME',
|
|
256
|
+
severity: 'error',
|
|
257
|
+
details: {
|
|
258
|
+
codecId: 'pg/vector@1',
|
|
259
|
+
typeName: 'InvalidVector',
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('rejects missing required typeParams with stable error code', () => {
|
|
265
|
+
const contract = createTestContract({
|
|
266
|
+
types: {
|
|
267
|
+
InvalidVector: {
|
|
268
|
+
codecId: 'pg/vector@1',
|
|
269
|
+
nativeType: 'vector',
|
|
270
|
+
typeParams: {},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
let thrownError: unknown;
|
|
276
|
+
try {
|
|
277
|
+
createRuntimeContext({
|
|
278
|
+
contract,
|
|
279
|
+
target: createTestTargetDescriptor(),
|
|
280
|
+
adapter: createTestAdapterDescriptor(),
|
|
281
|
+
extensionPacks: [createVectorExtensionDescriptor()],
|
|
282
|
+
});
|
|
283
|
+
} catch (e) {
|
|
284
|
+
thrownError = e;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
expect(thrownError).toBeDefined();
|
|
288
|
+
expect(thrownError).toMatchObject({
|
|
289
|
+
code: 'RUNTIME.TYPE_PARAMS_INVALID',
|
|
290
|
+
category: 'RUNTIME',
|
|
291
|
+
severity: 'error',
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('init hook for type helpers', () => {
|
|
297
|
+
it('calls init hook and stores result in context.types', () => {
|
|
298
|
+
const vectorParamsSchema = arktype({
|
|
299
|
+
length: 'number',
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const initFn = (params: { length: number }) => ({
|
|
303
|
+
dimensions: params.length,
|
|
304
|
+
isVector: true,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const extensionDescriptor: SqlRuntimeExtensionDescriptor<'postgres'> = {
|
|
308
|
+
kind: 'extension' as const,
|
|
309
|
+
id: 'pgvector',
|
|
310
|
+
version: '0.0.1',
|
|
311
|
+
familyId: 'sql' as const,
|
|
312
|
+
targetId: 'postgres' as const,
|
|
313
|
+
create(): SqlRuntimeExtensionInstance<'postgres'> {
|
|
314
|
+
const registry = createCodecRegistry();
|
|
315
|
+
registry.register(
|
|
316
|
+
codec({
|
|
317
|
+
typeId: 'pg/vector@1',
|
|
318
|
+
targetTypes: ['vector'],
|
|
319
|
+
encode: (v: number[]) => v,
|
|
320
|
+
decode: (w: number[]) => w,
|
|
321
|
+
}),
|
|
322
|
+
);
|
|
323
|
+
return {
|
|
324
|
+
familyId: 'sql' as const,
|
|
325
|
+
targetId: 'postgres' as const,
|
|
326
|
+
codecs: () => registry,
|
|
327
|
+
parameterizedCodecs: () => [
|
|
328
|
+
{
|
|
329
|
+
codecId: 'pg/vector@1',
|
|
330
|
+
paramsSchema: vectorParamsSchema,
|
|
331
|
+
init: initFn,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
};
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const contract = createTestContract({
|
|
339
|
+
types: {
|
|
340
|
+
Vector1536: {
|
|
341
|
+
codecId: 'pg/vector@1',
|
|
342
|
+
nativeType: 'vector',
|
|
343
|
+
typeParams: { length: 1536 },
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const context = createRuntimeContext({
|
|
349
|
+
contract,
|
|
350
|
+
target: createTestTargetDescriptor(),
|
|
351
|
+
adapter: createTestAdapterDescriptor(),
|
|
352
|
+
extensionPacks: [extensionDescriptor],
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
expect(context.types?.['Vector1536']).toEqual({
|
|
356
|
+
dimensions: 1536,
|
|
357
|
+
isVector: true,
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('stores full type instance when no init hook is provided', () => {
|
|
362
|
+
const vectorParamsSchema = arktype({
|
|
363
|
+
length: 'number',
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const extensionDescriptor: SqlRuntimeExtensionDescriptor<'postgres'> = {
|
|
367
|
+
kind: 'extension' as const,
|
|
368
|
+
id: 'pgvector',
|
|
369
|
+
version: '0.0.1',
|
|
370
|
+
familyId: 'sql' as const,
|
|
371
|
+
targetId: 'postgres' as const,
|
|
372
|
+
create(): SqlRuntimeExtensionInstance<'postgres'> {
|
|
373
|
+
const registry = createCodecRegistry();
|
|
374
|
+
registry.register(
|
|
375
|
+
codec({
|
|
376
|
+
typeId: 'pg/vector@1',
|
|
377
|
+
targetTypes: ['vector'],
|
|
378
|
+
encode: (v: number[]) => v,
|
|
379
|
+
decode: (w: number[]) => w,
|
|
380
|
+
}),
|
|
381
|
+
);
|
|
382
|
+
return {
|
|
383
|
+
familyId: 'sql' as const,
|
|
384
|
+
targetId: 'postgres' as const,
|
|
385
|
+
codecs: () => registry,
|
|
386
|
+
parameterizedCodecs: () => [
|
|
387
|
+
{
|
|
388
|
+
codecId: 'pg/vector@1',
|
|
389
|
+
paramsSchema: vectorParamsSchema,
|
|
390
|
+
// No init hook
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const contract = createTestContract({
|
|
398
|
+
types: {
|
|
399
|
+
Vector1536: {
|
|
400
|
+
codecId: 'pg/vector@1',
|
|
401
|
+
nativeType: 'vector',
|
|
402
|
+
typeParams: { length: 1536 },
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const context = createRuntimeContext({
|
|
408
|
+
contract,
|
|
409
|
+
target: createTestTargetDescriptor(),
|
|
410
|
+
adapter: createTestAdapterDescriptor(),
|
|
411
|
+
extensionPacks: [extensionDescriptor],
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Without init hook, stores the full type instance (matches contract typing)
|
|
415
|
+
expect(context.types?.['Vector1536']).toEqual({
|
|
416
|
+
codecId: 'pg/vector@1',
|
|
417
|
+
nativeType: 'vector',
|
|
418
|
+
typeParams: { length: 1536 },
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
describe('column typeParams validation', () => {
|
|
424
|
+
it('validates inline column typeParams against codec paramsSchema', () => {
|
|
425
|
+
const vectorParamsSchema = arktype({
|
|
426
|
+
length: 'number',
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const extensionDescriptor: SqlRuntimeExtensionDescriptor<'postgres'> = {
|
|
430
|
+
kind: 'extension' as const,
|
|
431
|
+
id: 'pgvector',
|
|
432
|
+
version: '0.0.1',
|
|
433
|
+
familyId: 'sql' as const,
|
|
434
|
+
targetId: 'postgres' as const,
|
|
435
|
+
create(): SqlRuntimeExtensionInstance<'postgres'> {
|
|
436
|
+
const registry = createCodecRegistry();
|
|
437
|
+
registry.register(
|
|
438
|
+
codec({
|
|
439
|
+
typeId: 'pg/vector@1',
|
|
440
|
+
targetTypes: ['vector'],
|
|
441
|
+
encode: (v: number[]) => v,
|
|
442
|
+
decode: (w: number[]) => w,
|
|
443
|
+
}),
|
|
444
|
+
);
|
|
445
|
+
return {
|
|
446
|
+
familyId: 'sql' as const,
|
|
447
|
+
targetId: 'postgres' as const,
|
|
448
|
+
codecs: () => registry,
|
|
449
|
+
parameterizedCodecs: () => [
|
|
450
|
+
{
|
|
451
|
+
codecId: 'pg/vector@1',
|
|
452
|
+
paramsSchema: vectorParamsSchema,
|
|
453
|
+
},
|
|
454
|
+
],
|
|
455
|
+
};
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const contract = createTestContract({
|
|
460
|
+
tableColumns: {
|
|
461
|
+
id: { nativeType: 'int4', codecId: 'pg/int4@1', nullable: false },
|
|
462
|
+
embedding: {
|
|
463
|
+
nativeType: 'vector',
|
|
464
|
+
codecId: 'pg/vector@1',
|
|
465
|
+
nullable: false,
|
|
466
|
+
typeParams: { length: 1536 },
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Should not throw - valid typeParams
|
|
472
|
+
const context = createRuntimeContext({
|
|
473
|
+
contract,
|
|
474
|
+
target: createTestTargetDescriptor(),
|
|
475
|
+
adapter: createTestAdapterDescriptor(),
|
|
476
|
+
extensionPacks: [extensionDescriptor],
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
expect(context.contract).toBe(contract);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('rejects invalid inline column typeParams with stable error code', () => {
|
|
483
|
+
const vectorParamsSchema = arktype({
|
|
484
|
+
length: 'number',
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
const extensionDescriptor: SqlRuntimeExtensionDescriptor<'postgres'> = {
|
|
488
|
+
kind: 'extension' as const,
|
|
489
|
+
id: 'pgvector',
|
|
490
|
+
version: '0.0.1',
|
|
491
|
+
familyId: 'sql' as const,
|
|
492
|
+
targetId: 'postgres' as const,
|
|
493
|
+
create(): SqlRuntimeExtensionInstance<'postgres'> {
|
|
494
|
+
const registry = createCodecRegistry();
|
|
495
|
+
registry.register(
|
|
496
|
+
codec({
|
|
497
|
+
typeId: 'pg/vector@1',
|
|
498
|
+
targetTypes: ['vector'],
|
|
499
|
+
encode: (v: number[]) => v,
|
|
500
|
+
decode: (w: number[]) => w,
|
|
501
|
+
}),
|
|
502
|
+
);
|
|
503
|
+
return {
|
|
504
|
+
familyId: 'sql' as const,
|
|
505
|
+
targetId: 'postgres' as const,
|
|
506
|
+
codecs: () => registry,
|
|
507
|
+
parameterizedCodecs: () => [
|
|
508
|
+
{
|
|
509
|
+
codecId: 'pg/vector@1',
|
|
510
|
+
paramsSchema: vectorParamsSchema,
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
};
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const contract = createTestContract({
|
|
518
|
+
tableColumns: {
|
|
519
|
+
id: { nativeType: 'int4', codecId: 'pg/int4@1', nullable: false },
|
|
520
|
+
embedding: {
|
|
521
|
+
nativeType: 'vector',
|
|
522
|
+
codecId: 'pg/vector@1',
|
|
523
|
+
nullable: false,
|
|
524
|
+
typeParams: { length: 'invalid' },
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
let thrownError: unknown;
|
|
530
|
+
try {
|
|
531
|
+
createRuntimeContext({
|
|
532
|
+
contract,
|
|
533
|
+
target: createTestTargetDescriptor(),
|
|
534
|
+
adapter: createTestAdapterDescriptor(),
|
|
535
|
+
extensionPacks: [extensionDescriptor],
|
|
536
|
+
});
|
|
537
|
+
} catch (e) {
|
|
538
|
+
thrownError = e;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
expect(thrownError).toBeDefined();
|
|
542
|
+
expect(thrownError).toMatchObject({
|
|
543
|
+
code: 'RUNTIME.TYPE_PARAMS_INVALID',
|
|
544
|
+
category: 'RUNTIME',
|
|
545
|
+
severity: 'error',
|
|
546
|
+
details: {
|
|
547
|
+
tableName: 'test',
|
|
548
|
+
columnName: 'embedding',
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
});
|