Skip to content

Commit c37feca

Browse files
authored
TML-2727: migrate migration aggregate to elementCoordinates (#629)
1 parent 0fcead6 commit c37feca

26 files changed

Lines changed: 168 additions & 171 deletions

packages/1-framework/0-foundation/contract/src/exports/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ export type {
4545
ProfileHashBase,
4646
Source,
4747
StorageBase,
48+
StorageEntitySlot,
4849
StorageHashBase,
50+
StorageNamespace,
4951
} from '../types';
5052
export {
5153
coreHash,

packages/1-framework/0-foundation/contract/src/types.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,34 @@ export function profileHash<const T extends string>(value: T): ProfileHashBase<T
4848
return value as ProfileHashBase<T>;
4949
}
5050

51+
/**
52+
* One entity-kind slot in a namespace — a map of entity name to entry.
53+
* Values are opaque at the foundation layer; family and target concretions
54+
* refine them to typed IR classes.
55+
*/
56+
export type StorageEntitySlot = Readonly<Record<string, unknown>>;
57+
58+
/**
59+
* Plain-data namespace entry in a storage block. Every hydrated contract
60+
* carries at least `id` plus zero or more entity-kind slot maps (`tables`,
61+
* `collections`, …). Foundation declares only this shape — no IR machinery.
62+
*/
63+
export interface StorageNamespace {
64+
readonly id: string;
65+
}
66+
5167
/**
5268
* Base type for family-specific storage blocks.
5369
* Family storage types (SqlStorage, MongoStorage, etc.) extend this to carry the
5470
* storage hash alongside family-specific data (tables, collections, etc.).
71+
*
72+
* The `namespaces` map is carried by every hydrated storage block. Serialized
73+
* envelope shape is target-owned; this types the in-memory contract after
74+
* `deserializeContract`.
5575
*/
5676
export interface StorageBase<THash extends string = string> {
5777
readonly storageHash: StorageHashBase<THash>;
78+
readonly namespaces: Readonly<Record<string, StorageNamespace>>;
5879
}
5980

6081
export interface FieldType {

packages/1-framework/0-foundation/contract/test/canonicalization.test.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function minimal(overrides?: Record<string, unknown>): Contract {
5858
target: 'postgres',
5959
roots: {},
6060
models: {},
61-
storage: { storageHash: coreHash('sha256:stub') },
61+
storage: { storageHash: coreHash('sha256:stub'), namespaces: {} },
6262
extensionPacks: {},
6363
capabilities: {},
6464
meta: {},
@@ -131,7 +131,7 @@ describe('canonicalizeContractToObject', () => {
131131

132132
it('includes storageHash when provided inside storage', () => {
133133
const result = canonicalizeContractToObject(
134-
minimal({ storage: { storageHash: 'sha256:abc' } }),
134+
minimal({ storage: { storageHash: 'sha256:abc', namespaces: {} } }),
135135
);
136136
expect(drill(result, 'storage')['storageHash']).toBe('sha256:abc');
137137
});
@@ -142,7 +142,9 @@ describe('canonicalizeContractToObject', () => {
142142
});
143143

144144
it('keeps storageHash inside storage', () => {
145-
const result = canonicalizeContractToObject(minimal({ storage: { storageHash: 'sha256:s' } }));
145+
const result = canonicalizeContractToObject(
146+
minimal({ storage: { storageHash: 'sha256:s', namespaces: {} } }),
147+
);
146148
expect(result).not.toHaveProperty('storageHash');
147149
expect(drill(result, 'storage')['storageHash']).toBe('sha256:s');
148150
});
@@ -471,7 +473,7 @@ describe('index and unique sorting', () => {
471473

472474
it('handles storage without namespaces (no-op)', () => {
473475
const result = canonicalizeContractToObject(
474-
minimal({ storage: { storageHash: 'sha256:stub' } }),
476+
minimal({ storage: { storageHash: 'sha256:stub', namespaces: {} } }),
475477
);
476478
expect(result['storage']).toBeDefined();
477479
});
@@ -597,7 +599,7 @@ describe('canonicalizeContract', () => {
597599

598600
it('produces identical output as JSON.stringify of canonicalizeContractToObject', () => {
599601
const input = minimal({
600-
storage: { storageHash: 'sha256:test' },
602+
storage: { storageHash: 'sha256:test', namespaces: {} },
601603
profileHash: 'sha256:profile',
602604
});
603605
const objResult = canonicalizeContractToObject(input);
@@ -684,7 +686,11 @@ describe('typeParams preservation', () => {
684686
it('strips empty storage.types[].typeParams without shouldPreserveEmpty hook', () => {
685687
const result = canonicalizeContractToObject(
686688
minimal({
687-
storage: { storageHash: 'sha256:stub', types: { MyType: { typeParams: {} } } },
689+
storage: {
690+
storageHash: 'sha256:stub',
691+
namespaces: {},
692+
types: { MyType: { typeParams: {} } },
693+
},
688694
}),
689695
);
690696
const myType = drill(result, 'storage', 'types', 'MyType');

packages/1-framework/0-foundation/contract/test/contract-factories.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('createContract', () => {
4949

5050
it('computes different storageHash for different storage', () => {
5151
const c1 = createContract({ storage: { namespaces: {} } });
52-
const c2 = createContract({
52+
const c2 = createSqlContract({
5353
storage: {
5454
namespaces: {
5555
public: { id: 'public', tables: { user: { columns: {} } } },

packages/1-framework/0-foundation/contract/test/contract-types.test-d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type ExampleModels = {
4747
};
4848

4949
type ExampleStorage = StorageBase<'sha256:abc123'> & {
50+
readonly namespaces: Record<string, never>;
5051
readonly tables: {
5152
readonly user: {
5253
readonly columns: {

packages/1-framework/0-foundation/contract/test/contract-types.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('unified contract types', () => {
2828
describe('StorageBase', () => {
2929
it('carries branded storageHash', () => {
3030
const hash = 'sha256:abc123' as StorageHashBase<'sha256:abc123'>;
31-
const storage = { storageHash: hash };
31+
const storage = { storageHash: hash, namespaces: {} };
3232
expect(storage.storageHash).toBe('sha256:abc123');
3333
});
3434
});
@@ -48,7 +48,7 @@ describe('unified contract types', () => {
4848
storage: {},
4949
},
5050
},
51-
storage: { storageHash: hash },
51+
storage: { storageHash: hash, namespaces: {} },
5252
capabilities: {},
5353
extensionPacks: {},
5454
meta: {},
@@ -67,7 +67,7 @@ describe('unified contract types', () => {
6767
targetFamily: 'sql',
6868
roots: {},
6969
models: {},
70-
storage: { storageHash: hash },
70+
storage: { storageHash: hash, namespaces: {} },
7171
capabilities: {},
7272
extensionPacks: {},
7373
meta: {},
@@ -107,7 +107,7 @@ describe('unified contract types', () => {
107107
storage: {},
108108
},
109109
},
110-
storage: { storageHash: hash },
110+
storage: { storageHash: hash, namespaces: {} },
111111
capabilities: {},
112112
extensionPacks: {},
113113
meta: {},

packages/1-framework/1-core/framework-components/src/ir/namespace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { StorageNamespace } from '@prisma-next/contract/types';
12
import { type IRNode, IRNodeBase } from './ir-node';
23

34
/**
@@ -57,8 +58,7 @@ export const UNBOUND_NAMESPACE_ID = '__unbound__' as const;
5758
* on this invariant to enumerate entities structurally without
5859
* family-specific knowledge.
5960
*/
60-
export interface Namespace extends IRNode {
61-
readonly id: string;
61+
export interface Namespace extends IRNode, StorageNamespace {
6262
readonly kind: string;
6363
}
6464

packages/1-framework/1-core/framework-components/src/ir/storage.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { StorageBase } from '@prisma-next/contract/types';
12
import type { IRNode } from './ir-node';
23
import type { Namespace } from './namespace';
34

@@ -36,7 +37,9 @@ export interface EntityCoordinate {
3637
* property whose value is a non-null object, yields one coordinate per
3738
* entry key in that map. No family-specific slot vocabulary is required.
3839
*/
39-
export function* elementCoordinates(storage: Storage): Generator<EntityCoordinate> {
40+
export function* elementCoordinates(
41+
storage: Pick<StorageBase, 'namespaces'>,
42+
): Generator<EntityCoordinate> {
4043
for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
4144
for (const [entityKind, slot] of Object.entries(ns)) {
4245
if (entityKind === 'id') continue;

packages/1-framework/1-core/framework-components/test/element-coordinates.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'vitest';
2-
import { type EntityCoordinate, elementCoordinates, type Storage } from '../src/ir/storage';
2+
import { type EntityCoordinate, elementCoordinates } from '../src/ir/storage';
33

44
function assertStoragePlaneCoordinates(coordinates: EntityCoordinate[]): void {
55
expect(coordinates.length).toBeGreaterThan(0);
@@ -32,7 +32,7 @@ describe('elementCoordinates', () => {
3232
tables: { users: {}, posts: {}, comments: {} },
3333
},
3434
},
35-
} as Storage;
35+
};
3636

3737
const coordinates = [...elementCoordinates(storage)];
3838
assertStoragePlaneCoordinates(coordinates);

packages/1-framework/3-tooling/cli/test/commands/cross-consumer-integrity.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ async function writeContract(cwd: string, storageHash: string): Promise<void> {
159159
await writeFile(
160160
join(contractDir, 'contract.json'),
161161
JSON.stringify({
162-
storage: { storageHash },
162+
storage: { storageHash, namespaces: {} },
163163
schemaVersion: SCHEMA_VERSION,
164164
target: TARGET,
165165
targetFamily: TARGET_FAMILY,
@@ -190,7 +190,7 @@ async function writeCleanOrphanSpace(cwd: string, spaceId: string, hash: string)
190190
await writeFile(
191191
join(spaceDir, 'contract.json'),
192192
JSON.stringify({
193-
storage: { storageHash: hash },
193+
storage: { storageHash: hash, namespaces: {} },
194194
schemaVersion: SCHEMA_VERSION,
195195
target: TARGET,
196196
targetFamily: TARGET_FAMILY,
@@ -307,7 +307,7 @@ async function setupExtSpaceCorruptionFixture(): Promise<{ cwd: string; extId: s
307307
await writeFile(
308308
join(extDir, 'contract.json'),
309309
JSON.stringify({
310-
storage: { storageHash: HASH_C },
310+
storage: { storageHash: HASH_C, namespaces: {} },
311311
schemaVersion: SCHEMA_VERSION,
312312
target: TARGET,
313313
targetFamily: TARGET_FAMILY,

0 commit comments

Comments
 (0)