@prisma-next/emitter 0.3.0-dev.135 → 0.3.0-dev.146
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 +33 -30
- package/dist/domain-type-generation.d.mts +14 -2
- package/dist/domain-type-generation.d.mts.map +1 -1
- package/dist/domain-type-generation.mjs +139 -1
- package/dist/domain-type-generation.mjs.map +1 -1
- package/dist/exports/index.d.mts +39 -4
- package/dist/exports/index.d.mts.map +1 -0
- package/dist/exports/index.mjs +104 -3
- package/dist/exports/index.mjs.map +1 -0
- package/dist/test/utils.d.mts +16 -14
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +12 -51
- package/dist/test/utils.mjs.map +1 -1
- package/dist/type-expression-safety-7_1tfJXA.mjs +8 -0
- package/dist/type-expression-safety-7_1tfJXA.mjs.map +1 -0
- package/dist/type-expression-safety.d.mts +5 -0
- package/dist/type-expression-safety.d.mts.map +1 -0
- package/dist/type-expression-safety.mjs +3 -0
- package/package.json +12 -6
- package/src/domain-type-generation.ts +227 -1
- package/src/emit-types.ts +23 -0
- package/src/emit.ts +68 -0
- package/src/exports/index.ts +4 -9
- package/src/generate-contract-dts.ts +116 -0
- package/src/type-expression-safety.ts +3 -0
- package/test/canonicalization.test.ts +25 -28
- package/test/domain-type-generation.test.ts +509 -2
- package/test/emitter.integration.test.ts +81 -139
- package/test/emitter.roundtrip.test.ts +114 -184
- package/test/emitter.test.ts +82 -467
- package/test/hashing.test.ts +8 -30
- package/test/mock-spi.ts +18 -0
- package/test/type-expression-safety.test.ts +34 -0
- package/test/utils.ts +30 -156
- package/src/target-family.ts +0 -7
- package/test/factories.test.ts +0 -274
package/test/hashing.test.ts
CHANGED
|
@@ -1,56 +1,34 @@
|
|
|
1
|
-
import { computeProfileHash, computeStorageHash } from '@prisma-next/
|
|
1
|
+
import { computeProfileHash, computeStorageHash } from '@prisma-next/contract/hashing';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
|
|
4
4
|
describe('hashing', () => {
|
|
5
5
|
it('computes storage hash', () => {
|
|
6
|
-
const
|
|
7
|
-
schemaVersion: '1',
|
|
6
|
+
const hash = computeStorageHash({
|
|
8
7
|
targetFamily: 'sql',
|
|
9
8
|
target: 'postgres',
|
|
10
|
-
models: {},
|
|
11
9
|
storage: { tables: {} },
|
|
12
|
-
|
|
13
|
-
capabilities: {},
|
|
14
|
-
meta: {},
|
|
15
|
-
sources: {},
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const hash = computeStorageHash(contract);
|
|
10
|
+
});
|
|
19
11
|
expect(hash).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
20
12
|
});
|
|
21
13
|
|
|
22
14
|
it('computes profile hash', () => {
|
|
23
|
-
const
|
|
24
|
-
schemaVersion: '1',
|
|
15
|
+
const hash = computeProfileHash({
|
|
25
16
|
targetFamily: 'sql',
|
|
26
17
|
target: 'postgres',
|
|
27
|
-
models: {},
|
|
28
|
-
storage: { tables: {} },
|
|
29
|
-
extensionPacks: {},
|
|
30
18
|
capabilities: { postgres: { jsonAgg: true } },
|
|
31
|
-
|
|
32
|
-
sources: {},
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const hash = computeProfileHash(contract);
|
|
19
|
+
});
|
|
36
20
|
expect(hash).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
37
21
|
});
|
|
38
22
|
|
|
39
23
|
it('produces stable hashes for identical input', () => {
|
|
40
|
-
const
|
|
41
|
-
schemaVersion: '1',
|
|
24
|
+
const args = {
|
|
42
25
|
targetFamily: 'sql',
|
|
43
26
|
target: 'postgres',
|
|
44
|
-
models: {},
|
|
45
27
|
storage: { tables: {} },
|
|
46
|
-
extensionPacks: {},
|
|
47
|
-
capabilities: {},
|
|
48
|
-
meta: {},
|
|
49
|
-
sources: {},
|
|
50
28
|
};
|
|
51
29
|
|
|
52
|
-
const hash1 = computeStorageHash(
|
|
53
|
-
const hash2 = computeStorageHash(
|
|
30
|
+
const hash1 = computeStorageHash(args);
|
|
31
|
+
const hash2 = computeStorageHash(args);
|
|
54
32
|
expect(hash1).toBe(hash2);
|
|
55
33
|
});
|
|
56
34
|
});
|
package/test/mock-spi.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EmissionSpi } from '@prisma-next/framework-components/emission';
|
|
2
|
+
|
|
3
|
+
export function createMockSpi(overrides: Partial<EmissionSpi> = {}): EmissionSpi {
|
|
4
|
+
return {
|
|
5
|
+
id: 'sql',
|
|
6
|
+
generateStorageType: () =>
|
|
7
|
+
'{ readonly tables: Record<string, never>; readonly types: Record<string, never>; readonly storageHash: StorageHash }',
|
|
8
|
+
generateModelStorageType: () => 'Record<string, never>',
|
|
9
|
+
getFamilyImports: () => [
|
|
10
|
+
"import type { ContractWithTypeMaps, TypeMaps as TypeMapsType } from '@prisma-next/sql-contract/types';",
|
|
11
|
+
],
|
|
12
|
+
getFamilyTypeAliases: () => 'export type LaneCodecTypes = CodecTypes;',
|
|
13
|
+
getTypeMapsExpression: () => 'TypeMapsType<CodecTypes, OperationTypes>',
|
|
14
|
+
getContractWrapper: (base, tm) =>
|
|
15
|
+
`export type Contract = ContractWithTypeMaps<${base}, ${tm}>;`,
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { isSafeTypeExpression } from '../src/type-expression-safety';
|
|
3
|
+
|
|
4
|
+
describe('isSafeTypeExpression', () => {
|
|
5
|
+
it('accepts normal type expressions', () => {
|
|
6
|
+
expect(isSafeTypeExpression('Char<36>')).toBe(true);
|
|
7
|
+
expect(isSafeTypeExpression('Vector<1536>')).toBe(true);
|
|
8
|
+
expect(isSafeTypeExpression("'USER' | 'ADMIN'")).toBe(true);
|
|
9
|
+
expect(isSafeTypeExpression('{ name: string; age: number }')).toBe(true);
|
|
10
|
+
expect(isSafeTypeExpression('Numeric<10, 2>')).toBe(true);
|
|
11
|
+
expect(isSafeTypeExpression('Timestamp<3>')).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('rejects import expressions', () => {
|
|
15
|
+
expect(isSafeTypeExpression('import("fs")')).toBe(false);
|
|
16
|
+
expect(isSafeTypeExpression('import ("fs")')).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('rejects require expressions', () => {
|
|
20
|
+
expect(isSafeTypeExpression('require("fs")')).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('rejects declare statements', () => {
|
|
24
|
+
expect(isSafeTypeExpression('declare module "foo"')).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('rejects export statements', () => {
|
|
28
|
+
expect(isSafeTypeExpression('export default 42')).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('rejects eval expressions', () => {
|
|
32
|
+
expect(isSafeTypeExpression('eval("code")')).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
package/test/utils.ts
CHANGED
|
@@ -1,157 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
profileHash?: string;
|
|
32
|
-
} = {};
|
|
33
|
-
|
|
34
|
-
if (hasTarget && overrides.target !== undefined) {
|
|
35
|
-
headerOpts.target = overrides.target;
|
|
36
|
-
} else if (!hasTarget) {
|
|
37
|
-
headerOpts.target = 'postgres';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (hasTargetFamily && overrides.targetFamily !== undefined) {
|
|
41
|
-
headerOpts.targetFamily = overrides.targetFamily;
|
|
42
|
-
} else if (!hasTargetFamily) {
|
|
43
|
-
headerOpts.targetFamily = 'sql';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (hasStorageHash && overrides.storageHash !== undefined) {
|
|
47
|
-
headerOpts.storageHash = overrides.storageHash;
|
|
48
|
-
} else if (!hasStorageHash) {
|
|
49
|
-
headerOpts.storageHash = 'sha256:test';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// profileHash is not part of ContractIR, but we can accept it for header creation
|
|
53
|
-
if (overrides.profileHash !== undefined) {
|
|
54
|
-
headerOpts.profileHash = overrides.profileHash;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const header = irHeader(
|
|
58
|
-
headerOpts as {
|
|
59
|
-
target: string;
|
|
60
|
-
targetFamily: string;
|
|
61
|
-
storageHash: string;
|
|
62
|
-
profileHash?: string;
|
|
63
|
-
},
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
// Build meta, handling explicitly undefined fields
|
|
67
|
-
// If a field is explicitly undefined, we'll omit it from the result later
|
|
68
|
-
const metaOpts: {
|
|
69
|
-
capabilities?: Record<string, Record<string, boolean>>;
|
|
70
|
-
extensionPacks?: Record<string, unknown>;
|
|
71
|
-
meta?: Record<string, unknown>;
|
|
72
|
-
sources?: Record<string, unknown>;
|
|
73
|
-
} = {};
|
|
74
|
-
|
|
75
|
-
if (hasCapabilities && overrides.capabilities !== undefined) {
|
|
76
|
-
metaOpts.capabilities = overrides.capabilities;
|
|
77
|
-
} else if (!hasCapabilities) {
|
|
78
|
-
metaOpts.capabilities = {};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (hasExtensionPacks && overrides.extensionPacks !== undefined) {
|
|
82
|
-
metaOpts.extensionPacks = overrides.extensionPacks;
|
|
83
|
-
} else if (!hasExtensionPacks) {
|
|
84
|
-
metaOpts.extensionPacks = {};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (hasMeta && overrides.meta !== undefined) {
|
|
88
|
-
metaOpts.meta = overrides.meta;
|
|
89
|
-
} else if (!hasMeta) {
|
|
90
|
-
metaOpts.meta = {};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (hasSources && overrides.sources !== undefined) {
|
|
94
|
-
metaOpts.sources = overrides.sources;
|
|
95
|
-
} else if (!hasSources) {
|
|
96
|
-
metaOpts.sources = {};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const meta = irMeta(Object.keys(metaOpts).length > 0 ? metaOpts : undefined);
|
|
100
|
-
|
|
101
|
-
// Build result by constructing the object directly (ContractIR doesn't include storageHash/profileHash)
|
|
102
|
-
// When fields are explicitly undefined, include them as undefined (tests use type assertions to bypass TS)
|
|
103
|
-
const result = {
|
|
104
|
-
schemaVersion:
|
|
105
|
-
hasSchemaVersion && overrides.schemaVersion !== undefined
|
|
106
|
-
? overrides.schemaVersion
|
|
107
|
-
: hasSchemaVersion && overrides.schemaVersion === undefined
|
|
108
|
-
? (undefined as unknown as string)
|
|
109
|
-
: header.schemaVersion,
|
|
110
|
-
target: header.target,
|
|
111
|
-
targetFamily: header.targetFamily,
|
|
112
|
-
// Only include meta fields if they're not explicitly undefined
|
|
113
|
-
capabilities:
|
|
114
|
-
hasCapabilities && overrides.capabilities === undefined
|
|
115
|
-
? (undefined as unknown as Record<string, Record<string, boolean>>)
|
|
116
|
-
: !hasCapabilities || overrides.capabilities !== undefined
|
|
117
|
-
? meta.capabilities
|
|
118
|
-
: ({} as Record<string, Record<string, boolean>>),
|
|
119
|
-
extensionPacks:
|
|
120
|
-
hasExtensionPacks && overrides.extensionPacks === undefined
|
|
121
|
-
? (undefined as unknown as Record<string, unknown>)
|
|
122
|
-
: !hasExtensionPacks || overrides.extensionPacks !== undefined
|
|
123
|
-
? meta.extensionPacks
|
|
124
|
-
: ({} as Record<string, unknown>),
|
|
125
|
-
meta:
|
|
126
|
-
hasMeta && overrides.meta === undefined
|
|
127
|
-
? (undefined as unknown as Record<string, unknown>)
|
|
128
|
-
: !hasMeta || overrides.meta !== undefined
|
|
129
|
-
? meta.meta
|
|
130
|
-
: ({} as Record<string, unknown>),
|
|
131
|
-
sources:
|
|
132
|
-
hasSources && overrides.sources === undefined
|
|
133
|
-
? (undefined as unknown as Record<string, unknown>)
|
|
134
|
-
: !hasSources || overrides.sources !== undefined
|
|
135
|
-
? meta.sources
|
|
136
|
-
: ({} as Record<string, unknown>),
|
|
137
|
-
// Only include family sections if they're not explicitly undefined
|
|
138
|
-
storage:
|
|
139
|
-
hasStorage && overrides.storage === undefined
|
|
140
|
-
? (undefined as unknown as Record<string, unknown>)
|
|
141
|
-
: hasStorage && overrides.storage !== undefined
|
|
142
|
-
? (overrides.storage as Record<string, unknown>)
|
|
143
|
-
: !hasStorage
|
|
144
|
-
? ({ tables: {} } as Record<string, unknown>)
|
|
145
|
-
: ({} as Record<string, unknown>),
|
|
146
|
-
models:
|
|
147
|
-
hasModels && overrides.models === undefined
|
|
148
|
-
? (undefined as unknown as Record<string, unknown>)
|
|
149
|
-
: hasModels && overrides.models !== undefined
|
|
150
|
-
? (overrides.models as Record<string, unknown>)
|
|
151
|
-
: !hasModels
|
|
152
|
-
? {}
|
|
153
|
-
: ({} as Record<string, unknown>),
|
|
154
|
-
} as ContractIR;
|
|
155
|
-
|
|
156
|
-
return result;
|
|
1
|
+
import { createContract } from '@prisma-next/contract/testing';
|
|
2
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
3
|
+
|
|
4
|
+
type TestContractOverrides = {
|
|
5
|
+
target?: string;
|
|
6
|
+
targetFamily?: string;
|
|
7
|
+
roots?: Record<string, string>;
|
|
8
|
+
models?: Record<string, unknown>;
|
|
9
|
+
storage?: Record<string, unknown>;
|
|
10
|
+
capabilities?: Record<string, Record<string, boolean>>;
|
|
11
|
+
extensionPacks?: Record<string, unknown>;
|
|
12
|
+
execution?: Record<string, unknown>;
|
|
13
|
+
meta?: Record<string, unknown>;
|
|
14
|
+
storageHash?: string;
|
|
15
|
+
schemaVersion?: string;
|
|
16
|
+
sources?: Record<string, unknown>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function createTestContract(overrides: TestContractOverrides = {}): Contract {
|
|
20
|
+
const { storageHash: _sh, schemaVersion: _sv, sources: _src, storage, ...rest } = overrides;
|
|
21
|
+
const cleanStorage = storage
|
|
22
|
+
? (() => {
|
|
23
|
+
const { storageHash: _innerSh, ...storageRest } = storage as Record<string, unknown>;
|
|
24
|
+
return storageRest;
|
|
25
|
+
})()
|
|
26
|
+
: undefined;
|
|
27
|
+
return createContract({
|
|
28
|
+
...rest,
|
|
29
|
+
...(cleanStorage ? { storage: cleanStorage } : {}),
|
|
30
|
+
} as Parameters<typeof createContract>[0]);
|
|
157
31
|
}
|
package/src/target-family.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// Re-export types from @prisma-next/contract for backward compatibility
|
|
2
|
-
// These types were moved to @prisma-next/contract to resolve dependency violations
|
|
3
|
-
export type {
|
|
4
|
-
TargetFamilyHook,
|
|
5
|
-
TypesImportSpec,
|
|
6
|
-
ValidationContext,
|
|
7
|
-
} from '@prisma-next/contract/types';
|
package/test/factories.test.ts
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { contractIR, irHeader, irMeta } from '@prisma-next/contract/ir';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
|
|
4
|
-
describe('emitter factories', () => {
|
|
5
|
-
describe('irHeader', () => {
|
|
6
|
-
it('creates header with required fields', () => {
|
|
7
|
-
const header = irHeader({
|
|
8
|
-
target: 'postgres',
|
|
9
|
-
targetFamily: 'sql',
|
|
10
|
-
storageHash: 'sha256:abc123',
|
|
11
|
-
});
|
|
12
|
-
expect(header).toEqual({
|
|
13
|
-
schemaVersion: '1',
|
|
14
|
-
target: 'postgres',
|
|
15
|
-
targetFamily: 'sql',
|
|
16
|
-
storageHash: 'sha256:abc123',
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('creates header with profileHash', () => {
|
|
21
|
-
const header = irHeader({
|
|
22
|
-
target: 'postgres',
|
|
23
|
-
targetFamily: 'sql',
|
|
24
|
-
storageHash: 'sha256:abc123',
|
|
25
|
-
profileHash: 'sha256:def456',
|
|
26
|
-
});
|
|
27
|
-
expect(header).toEqual({
|
|
28
|
-
schemaVersion: '1',
|
|
29
|
-
target: 'postgres',
|
|
30
|
-
targetFamily: 'sql',
|
|
31
|
-
storageHash: 'sha256:abc123',
|
|
32
|
-
profileHash: 'sha256:def456',
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('creates header for different target families', () => {
|
|
37
|
-
const header = irHeader({
|
|
38
|
-
target: 'mongodb',
|
|
39
|
-
targetFamily: 'document',
|
|
40
|
-
storageHash: 'sha256:xyz789',
|
|
41
|
-
});
|
|
42
|
-
expect(header.targetFamily).toBe('document');
|
|
43
|
-
expect(header.target).toBe('mongodb');
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('irMeta', () => {
|
|
48
|
-
it('creates empty meta when no options provided', () => {
|
|
49
|
-
const meta = irMeta({});
|
|
50
|
-
expect(meta).toEqual({
|
|
51
|
-
capabilities: {},
|
|
52
|
-
extensionPacks: {},
|
|
53
|
-
meta: {},
|
|
54
|
-
sources: {},
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('creates meta with capabilities', () => {
|
|
59
|
-
const meta = irMeta({
|
|
60
|
-
capabilities: {
|
|
61
|
-
postgres: {
|
|
62
|
-
returning: true,
|
|
63
|
-
lateral: true,
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
expect(meta.capabilities).toEqual({
|
|
68
|
-
postgres: {
|
|
69
|
-
returning: true,
|
|
70
|
-
lateral: true,
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
expect(meta.extensionPacks).toEqual({});
|
|
74
|
-
expect(meta.meta).toEqual({});
|
|
75
|
-
expect(meta.sources).toEqual({});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('creates meta with extension packs', () => {
|
|
79
|
-
const meta = irMeta({
|
|
80
|
-
extensionPacks: {
|
|
81
|
-
postgres: {
|
|
82
|
-
id: 'postgres',
|
|
83
|
-
version: '0.0.1',
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
expect(meta.extensionPacks).toEqual({
|
|
88
|
-
postgres: {
|
|
89
|
-
id: 'postgres',
|
|
90
|
-
version: '0.0.1',
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('creates meta with custom meta', () => {
|
|
96
|
-
const meta = irMeta({
|
|
97
|
-
meta: {
|
|
98
|
-
generated: true,
|
|
99
|
-
timestamp: '2024-01-01T00:00:00Z',
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
expect(meta.meta).toEqual({
|
|
103
|
-
generated: true,
|
|
104
|
-
timestamp: '2024-01-01T00:00:00Z',
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('creates meta with sources', () => {
|
|
109
|
-
const meta = irMeta({
|
|
110
|
-
sources: {
|
|
111
|
-
userView: {
|
|
112
|
-
kind: 'view',
|
|
113
|
-
sql: 'SELECT * FROM "user"',
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
expect(meta.sources).toEqual({
|
|
118
|
-
userView: {
|
|
119
|
-
kind: 'view',
|
|
120
|
-
sql: 'SELECT * FROM "user"',
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('creates meta with all fields', () => {
|
|
126
|
-
const meta = irMeta({
|
|
127
|
-
capabilities: {
|
|
128
|
-
postgres: { returning: true },
|
|
129
|
-
},
|
|
130
|
-
extensionPacks: {
|
|
131
|
-
postgres: { id: 'postgres', version: '0.0.1' },
|
|
132
|
-
},
|
|
133
|
-
meta: { generated: true },
|
|
134
|
-
sources: { userView: { kind: 'view' } },
|
|
135
|
-
});
|
|
136
|
-
expect(meta.capabilities).toEqual({
|
|
137
|
-
postgres: { returning: true },
|
|
138
|
-
});
|
|
139
|
-
expect(meta.extensionPacks).toEqual({
|
|
140
|
-
postgres: { id: 'postgres', version: '0.0.1' },
|
|
141
|
-
});
|
|
142
|
-
expect(meta.meta).toEqual({ generated: true });
|
|
143
|
-
expect(meta.sources).toEqual({ userView: { kind: 'view' } });
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe('contractIR', () => {
|
|
148
|
-
it('creates complete ContractIR from header, meta, and family sections', () => {
|
|
149
|
-
const header = irHeader({
|
|
150
|
-
target: 'postgres',
|
|
151
|
-
targetFamily: 'sql',
|
|
152
|
-
storageHash: 'sha256:abc123',
|
|
153
|
-
});
|
|
154
|
-
const meta = irMeta({
|
|
155
|
-
capabilities: {
|
|
156
|
-
postgres: { returning: true },
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
const storage = { tables: { user: { columns: {} } } };
|
|
160
|
-
const models = { User: { storage: { table: 'user' }, fields: {} } };
|
|
161
|
-
const relations = {};
|
|
162
|
-
|
|
163
|
-
const ir = contractIR({
|
|
164
|
-
header,
|
|
165
|
-
meta,
|
|
166
|
-
storage,
|
|
167
|
-
models,
|
|
168
|
-
relations,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
expect(ir.schemaVersion).toBe('1');
|
|
172
|
-
expect(ir.target).toBe('postgres');
|
|
173
|
-
expect(ir.targetFamily).toBe('sql');
|
|
174
|
-
// Note: storageHash is not part of ContractIR (it's computed by emitter)
|
|
175
|
-
expect(ir.storage).toEqual(storage);
|
|
176
|
-
expect(ir.models).toEqual(models);
|
|
177
|
-
expect(ir.relations).toEqual(relations);
|
|
178
|
-
expect(ir.capabilities).toEqual({
|
|
179
|
-
postgres: { returning: true },
|
|
180
|
-
});
|
|
181
|
-
expect(ir.extensionPacks).toEqual({});
|
|
182
|
-
expect(ir.meta).toEqual({});
|
|
183
|
-
expect(ir.sources).toEqual({});
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('creates ContractIR with profileHash in header', () => {
|
|
187
|
-
const header = irHeader({
|
|
188
|
-
target: 'postgres',
|
|
189
|
-
targetFamily: 'sql',
|
|
190
|
-
storageHash: 'sha256:abc123',
|
|
191
|
-
profileHash: 'sha256:def456',
|
|
192
|
-
});
|
|
193
|
-
const meta = irMeta({});
|
|
194
|
-
const storage = { tables: {} };
|
|
195
|
-
const models = {};
|
|
196
|
-
const relations = {};
|
|
197
|
-
|
|
198
|
-
const ir = contractIR({
|
|
199
|
-
header,
|
|
200
|
-
meta,
|
|
201
|
-
storage,
|
|
202
|
-
models,
|
|
203
|
-
relations,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Note: profileHash is not part of ContractIR (it's computed by emitter)
|
|
207
|
-
// The header contains it, but it's not included in the ContractIR
|
|
208
|
-
expect(header.profileHash).toBe('sha256:def456');
|
|
209
|
-
expect(ir.target).toBe('postgres');
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('creates ContractIR with all meta fields', () => {
|
|
213
|
-
const header = irHeader({
|
|
214
|
-
target: 'postgres',
|
|
215
|
-
targetFamily: 'sql',
|
|
216
|
-
storageHash: 'sha256:abc123',
|
|
217
|
-
});
|
|
218
|
-
const meta = irMeta({
|
|
219
|
-
capabilities: {
|
|
220
|
-
postgres: { returning: true },
|
|
221
|
-
},
|
|
222
|
-
extensionPacks: {
|
|
223
|
-
postgres: { id: 'postgres', version: '0.0.1' },
|
|
224
|
-
},
|
|
225
|
-
meta: { generated: true },
|
|
226
|
-
sources: { userView: { kind: 'view' } },
|
|
227
|
-
});
|
|
228
|
-
const storage = { tables: {} };
|
|
229
|
-
const models = {};
|
|
230
|
-
const relations = {};
|
|
231
|
-
|
|
232
|
-
const ir = contractIR({
|
|
233
|
-
header,
|
|
234
|
-
meta,
|
|
235
|
-
storage,
|
|
236
|
-
models,
|
|
237
|
-
relations,
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
expect(ir.capabilities).toEqual({
|
|
241
|
-
postgres: { returning: true },
|
|
242
|
-
});
|
|
243
|
-
expect(ir.extensionPacks).toEqual({
|
|
244
|
-
postgres: { id: 'postgres', version: '0.0.1' },
|
|
245
|
-
});
|
|
246
|
-
expect(ir.meta).toEqual({ generated: true });
|
|
247
|
-
expect(ir.sources).toEqual({ userView: { kind: 'view' } });
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('creates ContractIR for document family', () => {
|
|
251
|
-
const header = irHeader({
|
|
252
|
-
target: 'mongodb',
|
|
253
|
-
targetFamily: 'document',
|
|
254
|
-
storageHash: 'sha256:xyz789',
|
|
255
|
-
});
|
|
256
|
-
const meta = irMeta({});
|
|
257
|
-
const storage = { document: { collections: {} } };
|
|
258
|
-
const models = {};
|
|
259
|
-
const relations = {};
|
|
260
|
-
|
|
261
|
-
const ir = contractIR({
|
|
262
|
-
header,
|
|
263
|
-
meta,
|
|
264
|
-
storage,
|
|
265
|
-
models,
|
|
266
|
-
relations,
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
expect(ir.targetFamily).toBe('document');
|
|
270
|
-
expect(ir.target).toBe('mongodb');
|
|
271
|
-
expect(ir.storage).toEqual(storage);
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
});
|