@prisma-next/emitter 0.3.0-pr.93.4 → 0.3.0-pr.94.1

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.
@@ -1,274 +0,0 @@
1
- import type { ContractIR } from '@prisma-next/contract/ir';
2
- import type {
3
- TargetFamilyHook,
4
- TypesImportSpec,
5
- ValidationContext,
6
- } from '@prisma-next/contract/types';
7
- import type { EmitOptions } from '@prisma-next/core-control-plane/emission';
8
- import { emit } from '@prisma-next/core-control-plane/emission';
9
- import { createOperationRegistry } from '@prisma-next/operations';
10
- import { timeouts } from '@prisma-next/test-utils';
11
- import { describe, expect, it } from 'vitest';
12
- import { createContractIR } from './utils';
13
-
14
- const mockSqlHook: TargetFamilyHook = {
15
- id: 'sql',
16
- validateTypes: (ir: ContractIR, _ctx: ValidationContext) => {
17
- const storage = ir.storage as
18
- | { tables?: Record<string, { columns?: Record<string, { type?: string }> }> }
19
- | undefined;
20
- if (!storage?.tables) {
21
- return;
22
- }
23
-
24
- const referencedNamespaces = new Set<string>();
25
- const extensionPacks = ir.extensionPacks as Record<string, unknown> | undefined;
26
- if (extensionPacks) {
27
- for (const namespace of Object.keys(extensionPacks)) {
28
- referencedNamespaces.add(namespace);
29
- }
30
- }
31
-
32
- const typeIdRegex = /^([^/]+)\/([^@]+)@(\d+)$/;
33
-
34
- for (const [tableName, table] of Object.entries(storage.tables)) {
35
- if (!table.columns) continue;
36
- for (const [colName, col] of Object.entries(table.columns)) {
37
- const column = col as { codecId?: string };
38
- if (!column.codecId) {
39
- throw new Error(`Column "${colName}" in table "${tableName}" is missing codecId`);
40
- }
41
-
42
- if (!typeIdRegex.test(column.codecId)) {
43
- throw new Error(
44
- `Column "${colName}" in table "${tableName}" has invalid codecId format "${column.codecId}". Expected format: ns/name@version`,
45
- );
46
- }
47
-
48
- const match = column.codecId.match(typeIdRegex);
49
- if (match?.[1]) {
50
- const namespace = match[1];
51
- if (!referencedNamespaces.has(namespace)) {
52
- if (namespace === 'pg' && referencedNamespaces.has('postgres')) {
53
- continue;
54
- }
55
- throw new Error(
56
- `Column "${colName}" in table "${tableName}" uses codecId "${column.codecId}" from namespace "${namespace}" which is not referenced in contract.extensionPacks`,
57
- );
58
- }
59
- }
60
- }
61
- }
62
- },
63
- validateStructure: (ir: ContractIR) => {
64
- if (ir.targetFamily !== 'sql') {
65
- throw new Error(`Expected targetFamily "sql", got "${ir.targetFamily}"`);
66
- }
67
- },
68
- generateContractTypes: (_ir, _codecTypeImports, _operationTypeImports) => {
69
- void _codecTypeImports;
70
- void _operationTypeImports;
71
- return `// Generated contract types
72
- export type CodecTypes = Record<string, never>;
73
- export type LaneCodecTypes = CodecTypes;
74
- export type Contract = unknown;
75
- `;
76
- },
77
- };
78
-
79
- describe('emitter integration', () => {
80
- it(
81
- 'emits complete contract from IR to artifacts',
82
- async () => {
83
- const ir = createContractIR({
84
- models: {
85
- User: {
86
- storage: { table: 'user' },
87
- fields: {
88
- id: { column: 'id' },
89
- email: { column: 'email' },
90
- },
91
- relations: {},
92
- },
93
- },
94
- storage: {
95
- tables: {
96
- user: {
97
- columns: {
98
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
99
- email: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
100
- },
101
- primaryKey: { columns: ['id'] },
102
- uniques: [],
103
- indexes: [],
104
- foreignKeys: [],
105
- },
106
- },
107
- },
108
- extensionPacks: {
109
- postgres: {
110
- version: '0.0.1',
111
- },
112
- pg: {},
113
- },
114
- });
115
-
116
- // Create minimal test data (emitter tests don't load packs)
117
- const operationRegistry = createOperationRegistry();
118
- const codecTypeImports: TypesImportSpec[] = [];
119
- const operationTypeImports: TypesImportSpec[] = [];
120
- const extensionIds = ['postgres', 'pg'];
121
- const options: EmitOptions = {
122
- outputDir: '',
123
- operationRegistry,
124
- codecTypeImports,
125
- operationTypeImports,
126
- extensionIds,
127
- };
128
-
129
- const result = await emit(ir, options, mockSqlHook);
130
-
131
- expect(result.coreHash).toMatch(/^sha256:[a-f0-9]{64}$/);
132
- expect(result.contractDts).toContain('export type Contract');
133
- expect(result.contractDts).toContain('CodecTypes');
134
- expect(result.contractDts).toContain('LaneCodecTypes');
135
-
136
- const contractJson = JSON.parse(result.contractJson);
137
- expect(contractJson).toMatchObject({
138
- schemaVersion: '1',
139
- targetFamily: 'sql',
140
- target: 'postgres',
141
- coreHash: result.coreHash,
142
- storage: {
143
- tables: {
144
- user: expect.anything(),
145
- },
146
- },
147
- });
148
- },
149
- timeouts.typeScriptCompilation,
150
- );
151
-
152
- it('produces stable hashes for identical input', async () => {
153
- const ir = createContractIR({
154
- models: {
155
- User: {
156
- storage: { table: 'user' },
157
- fields: {
158
- id: { column: 'id' },
159
- },
160
- relations: {},
161
- },
162
- },
163
- storage: {
164
- tables: {
165
- user: {
166
- columns: {
167
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
168
- },
169
- primaryKey: { columns: ['id'] },
170
- uniques: [],
171
- indexes: [],
172
- foreignKeys: [],
173
- },
174
- },
175
- },
176
- extensionPacks: {
177
- postgres: {
178
- version: '0.0.1',
179
- },
180
- pg: {},
181
- },
182
- });
183
-
184
- // Create minimal test data (emitter tests don't load packs)
185
- const operationRegistry = createOperationRegistry();
186
- const codecTypeImports: TypesImportSpec[] = [];
187
- const operationTypeImports: TypesImportSpec[] = [];
188
- const extensionIds = ['postgres', 'pg'];
189
- const options: EmitOptions = {
190
- outputDir: '',
191
- operationRegistry,
192
- codecTypeImports,
193
- operationTypeImports,
194
- extensionIds,
195
- };
196
-
197
- const result1 = await emit(ir, options, mockSqlHook);
198
- const result2 = await emit(ir, options, mockSqlHook);
199
-
200
- expect(result1.coreHash).toBe(result2.coreHash);
201
- expect(result1.contractDts).toBe(result2.contractDts);
202
- expect(result1.contractJson).toBe(result2.contractJson);
203
- });
204
-
205
- it('round-trip: IR → JSON → parse JSON → compare', async () => {
206
- const ir = createContractIR({
207
- models: {
208
- User: {
209
- storage: { table: 'user' },
210
- fields: {
211
- id: { column: 'id' },
212
- email: { column: 'email' },
213
- },
214
- relations: {},
215
- },
216
- },
217
- storage: {
218
- tables: {
219
- user: {
220
- columns: {
221
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
222
- email: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
223
- },
224
- primaryKey: { columns: ['id'] },
225
- uniques: [],
226
- indexes: [],
227
- foreignKeys: [],
228
- },
229
- },
230
- },
231
- extensionPacks: {
232
- postgres: {
233
- version: '0.0.1',
234
- },
235
- pg: {},
236
- },
237
- });
238
-
239
- // Create minimal test data (emitter tests don't load packs)
240
- const operationRegistry = createOperationRegistry();
241
- const codecTypeImports: TypesImportSpec[] = [];
242
- const operationTypeImports: TypesImportSpec[] = [];
243
- const extensionIds = ['postgres', 'pg'];
244
- const options: EmitOptions = {
245
- outputDir: '',
246
- operationRegistry,
247
- codecTypeImports,
248
- operationTypeImports,
249
- extensionIds,
250
- };
251
-
252
- const result1 = await emit(ir, options, mockSqlHook);
253
- const contractJson1 = JSON.parse(result1.contractJson) as Record<string, unknown>;
254
-
255
- const ir2 = createContractIR({
256
- schemaVersion: contractJson1['schemaVersion'] as string,
257
- targetFamily: contractJson1['targetFamily'] as string,
258
- target: contractJson1['target'] as string,
259
- models: contractJson1['models'] as Record<string, unknown>,
260
- relations: (contractJson1['relations'] as Record<string, unknown>) || {},
261
- storage: contractJson1['storage'] as Record<string, unknown>,
262
- extensionPacks: contractJson1['extensionPacks'] as Record<string, unknown>,
263
- capabilities:
264
- (contractJson1['capabilities'] as Record<string, Record<string, boolean>>) || {},
265
- meta: (contractJson1['meta'] as Record<string, unknown>) || {},
266
- sources: (contractJson1['sources'] as Record<string, unknown>) || {},
267
- });
268
-
269
- const result2 = await emit(ir2, options, mockSqlHook);
270
-
271
- expect(result1.contractJson).toBe(result2.contractJson);
272
- expect(result1.coreHash).toBe(result2.coreHash);
273
- });
274
- });
@@ -1,370 +0,0 @@
1
- import type { ContractIR } from '@prisma-next/contract/ir';
2
- import type {
3
- TargetFamilyHook,
4
- TypesImportSpec,
5
- ValidationContext,
6
- } from '@prisma-next/contract/types';
7
- import type { EmitOptions } from '@prisma-next/core-control-plane/emission';
8
- import { emit } from '@prisma-next/core-control-plane/emission';
9
- import { createOperationRegistry } from '@prisma-next/operations';
10
- import { timeouts } from '@prisma-next/test-utils';
11
- import { describe, expect, it } from 'vitest';
12
- import { createContractIR } from './utils';
13
-
14
- const mockSqlHook: TargetFamilyHook = {
15
- id: 'sql',
16
- validateTypes: (ir: ContractIR, _ctx: ValidationContext) => {
17
- const storage = ir.storage as
18
- | { tables?: Record<string, { columns?: Record<string, { codecId?: string }> }> }
19
- | undefined;
20
- if (!storage?.tables) {
21
- return;
22
- }
23
-
24
- const referencedNamespaces = new Set<string>();
25
- const extensionPacks = ir.extensionPacks as Record<string, unknown> | undefined;
26
- if (extensionPacks) {
27
- for (const namespace of Object.keys(extensionPacks)) {
28
- referencedNamespaces.add(namespace);
29
- }
30
- }
31
-
32
- const typeIdRegex = /^([^/]+)\/([^@]+)@(\d+)$/;
33
-
34
- for (const [tableName, table] of Object.entries(storage.tables)) {
35
- if (!table.columns) continue;
36
- for (const [colName, col] of Object.entries(table.columns)) {
37
- const column = col as { codecId?: string };
38
- if (!column.codecId) {
39
- throw new Error(`Column "${colName}" in table "${tableName}" is missing codecId`);
40
- }
41
-
42
- if (!typeIdRegex.test(column.codecId)) {
43
- throw new Error(
44
- `Column "${colName}" in table "${tableName}" has invalid codecId format "${column.codecId}". Expected format: ns/name@version`,
45
- );
46
- }
47
-
48
- const match = column.codecId.match(typeIdRegex);
49
- if (match?.[1]) {
50
- const namespace = match[1];
51
- if (!referencedNamespaces.has(namespace)) {
52
- if (namespace === 'pg' && referencedNamespaces.has('postgres')) {
53
- continue;
54
- }
55
- throw new Error(
56
- `Column "${colName}" in table "${tableName}" uses codecId "${column.codecId}" from namespace "${namespace}" which is not referenced in contract.extensionPacks`,
57
- );
58
- }
59
- }
60
- }
61
- }
62
- },
63
- validateStructure: (ir: ContractIR) => {
64
- if (ir.targetFamily !== 'sql') {
65
- throw new Error(`Expected targetFamily "sql", got "${ir.targetFamily}"`);
66
- }
67
- },
68
- generateContractTypes: (_ir, _codecTypeImports, _operationTypeImports) => {
69
- void _codecTypeImports;
70
- void _operationTypeImports;
71
- return `// Generated contract types
72
- export type CodecTypes = Record<string, never>;
73
- export type LaneCodecTypes = CodecTypes;
74
- export type Contract = unknown;
75
- `;
76
- },
77
- };
78
-
79
- describe('emitter round-trip', () => {
80
- it(
81
- 'round-trip with minimal IR',
82
- async () => {
83
- const ir = createContractIR({
84
- storage: {
85
- tables: {
86
- user: {
87
- columns: {
88
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
89
- },
90
- primaryKey: { columns: ['id'] },
91
- uniques: [],
92
- indexes: [],
93
- foreignKeys: [],
94
- },
95
- },
96
- },
97
- extensionPacks: {
98
- postgres: { version: '0.0.1' },
99
- pg: {},
100
- },
101
- });
102
-
103
- // Create minimal test data (emitter tests don't load packs)
104
- const operationRegistry = createOperationRegistry();
105
- const codecTypeImports: TypesImportSpec[] = [];
106
- const operationTypeImports: TypesImportSpec[] = [];
107
- const extensionIds = ['postgres', 'pg'];
108
- const options: EmitOptions = {
109
- outputDir: '',
110
- operationRegistry,
111
- codecTypeImports,
112
- operationTypeImports,
113
- extensionIds,
114
- };
115
-
116
- const result1 = await emit(ir, options, mockSqlHook);
117
- const contractJson1 = JSON.parse(result1.contractJson) as Record<string, unknown>;
118
-
119
- const ir2 = createContractIR({
120
- schemaVersion: contractJson1['schemaVersion'] as string,
121
- targetFamily: contractJson1['targetFamily'] as string,
122
- target: contractJson1['target'] as string,
123
- models: contractJson1['models'] as Record<string, unknown>,
124
- relations: (contractJson1['relations'] as Record<string, unknown>) || {},
125
- storage: contractJson1['storage'] as Record<string, unknown>,
126
- extensionPacks: contractJson1['extensionPacks'] as Record<string, unknown>,
127
- capabilities:
128
- (contractJson1['capabilities'] as Record<string, Record<string, boolean>>) || {},
129
- meta: (contractJson1['meta'] as Record<string, unknown>) || {},
130
- sources: (contractJson1['sources'] as Record<string, unknown>) || {},
131
- });
132
-
133
- const result2 = await emit(ir2, options, mockSqlHook);
134
-
135
- expect(result1.contractJson).toBe(result2.contractJson);
136
- expect(result1.coreHash).toBe(result2.coreHash);
137
- },
138
- timeouts.typeScriptCompilation,
139
- );
140
-
141
- it('round-trip with complex IR', async () => {
142
- const ir = createContractIR({
143
- models: {
144
- User: {
145
- storage: { table: 'user' },
146
- fields: {
147
- id: { column: 'id' },
148
- email: { column: 'email' },
149
- name: { column: 'name' },
150
- },
151
- relations: {},
152
- },
153
- Post: {
154
- storage: { table: 'post' },
155
- fields: {
156
- id: { column: 'id' },
157
- title: { column: 'title' },
158
- userId: { column: 'user_id' },
159
- },
160
- relations: {},
161
- },
162
- },
163
- storage: {
164
- tables: {
165
- user: {
166
- columns: {
167
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
168
- email: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
169
- name: { codecId: 'pg/text@1', nativeType: 'text', nullable: true },
170
- },
171
- primaryKey: { columns: ['id'] },
172
- uniques: [{ columns: ['email'], name: 'user_email_key' }],
173
- indexes: [{ columns: ['name'], name: 'user_name_idx' }],
174
- foreignKeys: [],
175
- },
176
- post: {
177
- columns: {
178
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
179
- title: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
180
- user_id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
181
- },
182
- primaryKey: { columns: ['id'] },
183
- uniques: [],
184
- indexes: [],
185
- foreignKeys: [
186
- {
187
- columns: ['user_id'],
188
- references: { table: 'user', columns: ['id'] },
189
- name: 'post_user_id_fkey',
190
- },
191
- ],
192
- },
193
- },
194
- },
195
- extensionPacks: {
196
- postgres: { version: '0.0.1' },
197
- },
198
- });
199
-
200
- // Create minimal test data (emitter tests don't load packs)
201
- const operationRegistry = createOperationRegistry();
202
- const codecTypeImports: TypesImportSpec[] = [];
203
- const operationTypeImports: TypesImportSpec[] = [];
204
- const extensionIds = ['postgres'];
205
- const options: EmitOptions = {
206
- outputDir: '',
207
- operationRegistry,
208
- codecTypeImports,
209
- operationTypeImports,
210
- extensionIds,
211
- };
212
-
213
- const result1 = await emit(ir, options, mockSqlHook);
214
- const contractJson1 = JSON.parse(result1.contractJson) as Record<string, unknown>;
215
-
216
- const ir2 = createContractIR({
217
- schemaVersion: contractJson1['schemaVersion'] as string,
218
- targetFamily: contractJson1['targetFamily'] as string,
219
- target: contractJson1['target'] as string,
220
- models: contractJson1['models'] as Record<string, unknown>,
221
- relations: (contractJson1['relations'] as Record<string, unknown>) || {},
222
- storage: contractJson1['storage'] as Record<string, unknown>,
223
- extensionPacks: contractJson1['extensionPacks'] as Record<string, unknown>,
224
- capabilities:
225
- (contractJson1['capabilities'] as Record<string, Record<string, boolean>>) || {},
226
- meta: (contractJson1['meta'] as Record<string, unknown>) || {},
227
- sources: (contractJson1['sources'] as Record<string, unknown>) || {},
228
- });
229
-
230
- const result2 = await emit(ir2, options, mockSqlHook);
231
-
232
- expect(result1.contractJson).toBe(result2.contractJson);
233
- expect(result1.coreHash).toBe(result2.coreHash);
234
- });
235
-
236
- it('round-trip with nullable fields', async () => {
237
- const ir = createContractIR({
238
- storage: {
239
- tables: {
240
- user: {
241
- columns: {
242
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
243
- email: { codecId: 'pg/text@1', nativeType: 'text', nullable: true },
244
- name: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
245
- },
246
- primaryKey: { columns: ['id'] },
247
- uniques: [],
248
- indexes: [],
249
- foreignKeys: [],
250
- },
251
- },
252
- },
253
- extensionPacks: {
254
- postgres: { version: '0.0.1' },
255
- pg: {},
256
- },
257
- });
258
-
259
- // Create minimal test data (emitter tests don't load packs)
260
- const operationRegistry = createOperationRegistry();
261
- const codecTypeImports: TypesImportSpec[] = [];
262
- const operationTypeImports: TypesImportSpec[] = [];
263
- const extensionIds = ['postgres', 'pg'];
264
- const options: EmitOptions = {
265
- outputDir: '',
266
- operationRegistry,
267
- codecTypeImports,
268
- operationTypeImports,
269
- extensionIds,
270
- };
271
-
272
- const result1 = await emit(ir, options, mockSqlHook);
273
- const contractJson1 = JSON.parse(result1.contractJson) as Record<string, unknown>;
274
-
275
- const ir2 = createContractIR({
276
- schemaVersion: contractJson1['schemaVersion'] as string,
277
- targetFamily: contractJson1['targetFamily'] as string,
278
- target: contractJson1['target'] as string,
279
- models: contractJson1['models'] as Record<string, unknown>,
280
- relations: (contractJson1['relations'] as Record<string, unknown>) || {},
281
- storage: contractJson1['storage'] as Record<string, unknown>,
282
- extensionPacks: contractJson1['extensionPacks'] as Record<string, unknown>,
283
- capabilities:
284
- (contractJson1['capabilities'] as Record<string, Record<string, boolean>>) || {},
285
- meta: (contractJson1['meta'] as Record<string, unknown>) || {},
286
- sources: (contractJson1['sources'] as Record<string, unknown>) || {},
287
- });
288
-
289
- const result2 = await emit(ir2, options, mockSqlHook);
290
-
291
- expect(result1.contractJson).toBe(result2.contractJson);
292
- expect(result1.coreHash).toBe(result2.coreHash);
293
-
294
- const parsed2 = JSON.parse(result2.contractJson) as Record<string, unknown>;
295
- const storage = parsed2['storage'] as Record<string, unknown>;
296
- const tables = storage['tables'] as Record<string, unknown>;
297
- const user = tables['user'] as Record<string, unknown>;
298
- const columns = user['columns'] as Record<string, unknown>;
299
- const id = columns['id'] as Record<string, unknown>;
300
- const email = columns['email'] as Record<string, unknown>;
301
- const name = columns['name'] as Record<string, unknown>;
302
- expect(id['nullable']).toBeUndefined();
303
- expect(email['nullable']).toBe(true);
304
- expect(name['nullable']).toBeUndefined();
305
- });
306
-
307
- it('round-trip with capabilities', async () => {
308
- const ir = createContractIR({
309
- storage: {
310
- tables: {
311
- user: {
312
- columns: {
313
- id: { codecId: 'pg/int4@1', nativeType: 'int4', nullable: false },
314
- },
315
- primaryKey: { columns: ['id'] },
316
- uniques: [],
317
- indexes: [],
318
- foreignKeys: [],
319
- },
320
- },
321
- },
322
- extensionPacks: {
323
- postgres: { version: '0.0.1' },
324
- pg: {},
325
- },
326
- capabilities: {
327
- postgres: {
328
- jsonAgg: true,
329
- lateral: true,
330
- },
331
- },
332
- });
333
-
334
- // Create minimal test data (emitter tests don't load packs)
335
- const operationRegistry = createOperationRegistry();
336
- const codecTypeImports: TypesImportSpec[] = [];
337
- const operationTypeImports: TypesImportSpec[] = [];
338
- const extensionIds = ['postgres', 'pg'];
339
- const options: EmitOptions = {
340
- outputDir: '',
341
- operationRegistry,
342
- codecTypeImports,
343
- operationTypeImports,
344
- extensionIds,
345
- };
346
-
347
- const result1 = await emit(ir, options, mockSqlHook);
348
- const contractJson1 = JSON.parse(result1.contractJson) as Record<string, unknown>;
349
-
350
- const ir2 = createContractIR({
351
- schemaVersion: contractJson1['schemaVersion'] as string,
352
- targetFamily: contractJson1['targetFamily'] as string,
353
- target: contractJson1['target'] as string,
354
- models: contractJson1['models'] as Record<string, unknown>,
355
- relations: (contractJson1['relations'] as Record<string, unknown>) || {},
356
- storage: contractJson1['storage'] as Record<string, unknown>,
357
- extensionPacks: contractJson1['extensionPacks'] as Record<string, unknown>,
358
- capabilities:
359
- (contractJson1['capabilities'] as Record<string, Record<string, boolean>>) || {},
360
- meta: (contractJson1['meta'] as Record<string, unknown>) || {},
361
- sources: (contractJson1['sources'] as Record<string, unknown>) || {},
362
- });
363
-
364
- const result2 = await emit(ir2, options, mockSqlHook);
365
-
366
- expect(result1.contractJson).toBe(result2.contractJson);
367
- expect(result1.coreHash).toBe(result2.coreHash);
368
- expect(result1.profileHash).toBe(result2.profileHash);
369
- });
370
- });