@prisma-next/contract 0.3.0-dev.9 → 0.3.0-dev.90

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 (46) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +42 -6
  3. package/dist/framework-components.d.mts +529 -0
  4. package/dist/framework-components.d.mts.map +1 -0
  5. package/dist/framework-components.mjs +70 -0
  6. package/dist/framework-components.mjs.map +1 -0
  7. package/dist/ir-C9rRU5WS.d.mts +84 -0
  8. package/dist/ir-C9rRU5WS.d.mts.map +1 -0
  9. package/dist/ir.d.mts +2 -0
  10. package/dist/ir.mjs +51 -0
  11. package/dist/ir.mjs.map +1 -0
  12. package/dist/types-54JRJq9p.d.mts +395 -0
  13. package/dist/types-54JRJq9p.d.mts.map +1 -0
  14. package/dist/types.d.mts +2 -0
  15. package/dist/types.mjs +30 -0
  16. package/dist/types.mjs.map +1 -0
  17. package/package.json +24 -28
  18. package/schemas/data-contract-document-v1.json +9 -4
  19. package/src/exports/framework-components.ts +10 -1
  20. package/src/exports/types.ts +31 -7
  21. package/src/framework-components.ts +179 -46
  22. package/src/ir.ts +28 -12
  23. package/src/types.ts +274 -37
  24. package/dist/exports/framework-components.d.ts +0 -3
  25. package/dist/exports/framework-components.d.ts.map +0 -1
  26. package/dist/exports/framework-components.js +0 -24
  27. package/dist/exports/framework-components.js.map +0 -1
  28. package/dist/exports/ir.d.ts +0 -2
  29. package/dist/exports/ir.d.ts.map +0 -1
  30. package/dist/exports/ir.js +0 -35
  31. package/dist/exports/ir.js.map +0 -1
  32. package/dist/exports/pack-manifest-types.d.ts +0 -2
  33. package/dist/exports/pack-manifest-types.d.ts.map +0 -1
  34. package/dist/exports/pack-manifest-types.js +0 -1
  35. package/dist/exports/pack-manifest-types.js.map +0 -1
  36. package/dist/exports/types.d.ts +0 -3
  37. package/dist/exports/types.d.ts.map +0 -1
  38. package/dist/exports/types.js +0 -8
  39. package/dist/exports/types.js.map +0 -1
  40. package/dist/framework-components.d.ts +0 -408
  41. package/dist/framework-components.d.ts.map +0 -1
  42. package/dist/ir.d.ts +0 -76
  43. package/dist/ir.d.ts.map +0 -1
  44. package/dist/types.d.ts +0 -222
  45. package/dist/types.d.ts.map +0 -1
  46. package/src/exports/pack-manifest-types.ts +0 -6
package/package.json CHANGED
@@ -1,51 +1,47 @@
1
1
  {
2
2
  "name": "@prisma-next/contract",
3
- "version": "0.3.0-dev.9",
3
+ "version": "0.3.0-dev.90",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Data contract type definitions and JSON schema for Prisma Next",
7
7
  "dependencies": {
8
- "@prisma-next/operations": "0.3.0-dev.9"
8
+ "@prisma-next/operations": "0.3.0-dev.90"
9
9
  },
10
10
  "devDependencies": {
11
- "@vitest/coverage-v8": "4.0.16",
12
- "tsup": "8.5.1",
11
+ "tsdown": "0.18.4",
13
12
  "typescript": "5.9.3",
14
- "vitest": "4.0.16",
15
- "@prisma-next/test-utils": "0.0.1"
13
+ "vitest": "4.0.17",
14
+ "@prisma-next/tsconfig": "0.0.0",
15
+ "@prisma-next/test-utils": "0.0.1",
16
+ "@prisma-next/tsdown": "0.0.0"
16
17
  },
17
18
  "files": [
18
19
  "dist",
19
20
  "src",
20
21
  "schemas"
21
22
  ],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
22
26
  "exports": {
23
- "./types": {
24
- "types": "./dist/exports/types.d.ts",
25
- "import": "./dist/exports/types.js"
26
- },
27
- "./pack-manifest-types": {
28
- "types": "./dist/exports/pack-manifest-types.d.ts",
29
- "import": "./dist/exports/pack-manifest-types.js"
30
- },
31
- "./ir": {
32
- "types": "./dist/exports/ir.d.ts",
33
- "import": "./dist/exports/ir.js"
34
- },
35
- "./framework-components": {
36
- "types": "./dist/exports/framework-components.d.ts",
37
- "import": "./dist/exports/framework-components.js"
38
- },
39
- "./schema-document": "./schemas/data-contract-document-v1.json"
27
+ "./framework-components": "./dist/framework-components.mjs",
28
+ "./ir": "./dist/ir.mjs",
29
+ "./types": "./dist/types.mjs",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/prisma/prisma-next.git",
35
+ "directory": "packages/1-framework/1-core/shared/contract"
40
36
  },
41
37
  "scripts": {
42
- "build": "tsup --config tsup.config.ts && tsc --project tsconfig.build.json",
38
+ "build": "tsdown",
43
39
  "test": "vitest run --passWithNoTests",
44
40
  "test:coverage": "vitest run --coverage --passWithNoTests",
45
41
  "typecheck": "tsc --project tsconfig.json --noEmit",
46
- "lint": "biome check . --config-path ../../../../../biome.json --error-on-warnings",
47
- "lint:fix": "biome check --write . --config-path ../../../biome.json",
48
- "lint:fix:unsafe": "biome check --write --unsafe . --config-path ../../../biome.json",
49
- "clean": "node ../../../../../scripts/clean.mjs"
42
+ "lint": "biome check . --error-on-warnings",
43
+ "lint:fix": "biome check --write .",
44
+ "lint:fix:unsafe": "biome check --write --unsafe .",
45
+ "clean": "rm -rf dist dist-tsc dist-tsc-prod coverage .tmp-output"
50
46
  }
51
47
  }
@@ -24,10 +24,15 @@
24
24
  "enum": ["document"],
25
25
  "description": "Target family classification"
26
26
  },
27
- "coreHash": {
27
+ "storageHash": {
28
28
  "type": "string",
29
29
  "pattern": "^sha256:[a-f0-9]{64}$",
30
- "description": "SHA-256 hash of the core schema structure (models, fields, relations, storage layout)"
30
+ "description": "SHA-256 hash of the storage section (DB-satisfied expectations)"
31
+ },
32
+ "executionHash": {
33
+ "type": "string",
34
+ "pattern": "^sha256:[a-f0-9]{64}$",
35
+ "description": "SHA-256 hash of the execution section (client-side behavior)"
31
36
  },
32
37
  "profileHash": {
33
38
  "type": "string",
@@ -85,7 +90,7 @@
85
90
  "required": ["document"]
86
91
  }
87
92
  },
88
- "required": ["schemaVersion", "target", "targetFamily", "coreHash", "storage"],
93
+ "required": ["schemaVersion", "target", "targetFamily", "storageHash", "storage"],
89
94
  "$defs": {
90
95
  "DocCollection": {
91
96
  "type": "object",
@@ -103,7 +108,7 @@
103
108
  "properties": {
104
109
  "strategy": {
105
110
  "type": "string",
106
- "enum": ["auto", "client", "uuid", "cuid", "objectId"],
111
+ "enum": ["auto", "client", "uuid", "objectId"],
107
112
  "description": "ID generation strategy"
108
113
  }
109
114
  }
@@ -16,11 +16,20 @@ export type {
16
16
  ExtensionPackRef,
17
17
  FamilyDescriptor,
18
18
  FamilyInstance,
19
+ // Type renderers for parameterized codec emission
20
+ NormalizedTypeRenderer,
19
21
  PackRefBase,
20
22
  TargetBoundComponentDescriptor,
21
23
  TargetDescriptor,
22
24
  TargetInstance,
23
25
  TargetPackRef,
26
+ TypeRenderer,
27
+ TypeRendererFunction,
28
+ TypeRendererTemplate,
24
29
  } from '../framework-components';
25
30
 
26
- export { checkContractComponentRequirements } from '../framework-components';
31
+ export {
32
+ checkContractComponentRequirements,
33
+ interpolateTypeTemplate,
34
+ normalizeRenderer,
35
+ } from '../framework-components';
@@ -1,26 +1,50 @@
1
- // Shared types
2
- // Document family types
3
- // Plan types - target-family agnostic execution types
4
- // Emitter types (moved from @prisma-next/emitter)
5
1
  export type {
2
+ $,
3
+ Brand,
4
+ ColumnDefault,
5
+ ColumnDefaultLiteralInputValue,
6
+ ColumnDefaultLiteralValue,
6
7
  ContractBase,
7
8
  ContractMarkerRecord,
8
9
  DocCollection,
9
10
  DocIndex,
10
11
  DocumentContract,
11
12
  DocumentStorage,
13
+ ExecutionHashBase,
14
+ ExecutionMutationDefault,
15
+ ExecutionMutationDefaultValue,
12
16
  ExecutionPlan,
17
+ ExecutionSection,
13
18
  Expr,
14
19
  FieldType,
15
- OperationManifest,
20
+ GenerateContractTypesOptions,
21
+ GeneratedValueSpec,
22
+ JsonPrimitive,
23
+ JsonValue,
16
24
  ParamDescriptor,
25
+ ParameterizedCodecDescriptor,
17
26
  PlanMeta,
18
27
  PlanRefs,
28
+ ProfileHashBase,
29
+ RenderTypeContext,
19
30
  ResultType,
20
31
  Source,
32
+ StorageHashBase,
33
+ TaggedBigInt,
34
+ TaggedLiteralValue,
35
+ TaggedRaw,
21
36
  TargetFamilyHook,
37
+ TypeRenderContext,
38
+ TypeRenderEntry,
39
+ TypeRenderer,
22
40
  TypesImportSpec,
23
41
  ValidationContext,
24
42
  } from '../types';
25
- // Type guards
26
- export { isDocumentContract } from '../types';
43
+ export {
44
+ bigintJsonReplacer,
45
+ coreHash,
46
+ isDocumentContract,
47
+ isTaggedBigInt,
48
+ isTaggedRaw,
49
+ profileHash,
50
+ } from '../types';
@@ -1,37 +1,156 @@
1
- import type { OperationManifest, TypesImportSpec } from './types';
2
-
3
- // ============================================================================
4
- // Framework Component Descriptor Base Types
5
- // ============================================================================
6
- //
7
- // Prisma Next uses a modular architecture where functionality is composed from
8
- // discrete "components". Each component has a descriptor that identifies it and
9
- // provides metadata. These base types define the shared structure for all
10
- // component descriptors across both control-plane (CLI/tooling) and runtime-plane.
11
- //
12
- // Component Hierarchy:
13
- //
14
- // Family (e.g., 'sql', 'document')
15
- // └── Target (e.g., 'postgres', 'mysql', 'mongodb')
16
- // ├── Adapter (protocol/dialect implementation)
17
- // ├── Driver (connection/execution layer)
18
- // └── Extension (optional capabilities like pgvector)
19
- //
20
- // Key design decisions:
21
- // - "Component" terminology separates framework building blocks from delivery
22
- // mechanism ("pack" refers to how components are packaged/distributed)
23
- // - `kind` is extensible (Kind extends string) - no closed union, allowing
24
- // ecosystem authors to define new component kinds
25
- // - Target-bound descriptors are generic in TFamilyId and TTargetId for type-safe
26
- // composition (e.g., TypeScript rejects Postgres adapter with MySQL target)
27
- // - Descriptors own declarative fields directly (version, types, operations, etc.)
28
- // rather than nesting them under a `manifest` property
29
- //
30
- // ============================================================================
1
+ import type { RenderTypeContext, TypesImportSpec } from './types';
2
+
3
+ /**
4
+ * A template-based type renderer (structured form).
5
+ * Uses mustache-style placeholders (e.g., `Vector<{{length}}>`) that are
6
+ * replaced with typeParams values during rendering.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * { kind: 'template', template: 'Vector<{{length}}>' }
11
+ * // With typeParams { length: 1536 }, renders: 'Vector<1536>'
12
+ * ```
13
+ */
14
+ export interface TypeRendererTemplate {
15
+ readonly kind: 'template';
16
+ /** Template string with `{{key}}` placeholders for typeParams values */
17
+ readonly template: string;
18
+ }
19
+
20
+ /**
21
+ * A function-based type renderer for full control over type expression generation.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * {
26
+ * kind: 'function',
27
+ * render: (params, ctx) => `Vector<${params.length}>`
28
+ * }
29
+ * ```
30
+ */
31
+ export interface TypeRendererFunction {
32
+ readonly kind: 'function';
33
+ /** Render function that produces a TypeScript type expression */
34
+ readonly render: (params: Record<string, unknown>, ctx: RenderTypeContext) => string;
35
+ }
36
+
37
+ /**
38
+ * A raw template string type renderer (convenience form).
39
+ * Shorthand for TypeRendererTemplate - just the template string without wrapper.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * 'Vector<{{length}}>'
44
+ * // Equivalent to: { kind: 'template', template: 'Vector<{{length}}>' }
45
+ * ```
46
+ */
47
+ export type TypeRendererString = string;
48
+
49
+ /**
50
+ * A raw function type renderer (convenience form).
51
+ * Shorthand for TypeRendererFunction - just the function without wrapper.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * (params, ctx) => `Vector<${params.length}>`
56
+ * // Equivalent to: { kind: 'function', render: ... }
57
+ * ```
58
+ */
59
+ export type TypeRendererRawFunction = (
60
+ params: Record<string, unknown>,
61
+ ctx: RenderTypeContext,
62
+ ) => string;
63
+
64
+ /**
65
+ * Union of type renderer formats.
66
+ *
67
+ * Supports both structured forms (with `kind` discriminator) and convenience forms:
68
+ * - `string` - Template string with `{{key}}` placeholders (manifest-safe, JSON-serializable)
69
+ * - `function` - Render function for full control (requires runtime execution)
70
+ * - `{ kind: 'template', template: string }` - Structured template form
71
+ * - `{ kind: 'function', render: fn }` - Structured function form
72
+ *
73
+ * Templates are normalized to functions during pack assembly.
74
+ * **Prefer template strings** for most cases - they are JSON-serializable.
75
+ */
76
+ export type TypeRenderer =
77
+ | TypeRendererString
78
+ | TypeRendererRawFunction
79
+ | TypeRendererTemplate
80
+ | TypeRendererFunction;
81
+
82
+ /**
83
+ * Normalized type renderer - always a function after assembly.
84
+ * This is the form received by the emitter.
85
+ */
86
+ export interface NormalizedTypeRenderer {
87
+ readonly codecId: string;
88
+ readonly render: (params: Record<string, unknown>, ctx: RenderTypeContext) => string;
89
+ }
90
+
91
+ /**
92
+ * Interpolates a template string with params values.
93
+ * Used internally by normalizeRenderer to compile templates to functions.
94
+ *
95
+ * @throws Error if a placeholder key is not found in params (except 'CodecTypes')
96
+ */
97
+ export function interpolateTypeTemplate(
98
+ template: string,
99
+ params: Record<string, unknown>,
100
+ ctx: RenderTypeContext,
101
+ ): string {
102
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => {
103
+ if (key === 'CodecTypes') return ctx.codecTypesName;
104
+ const value = params[key];
105
+ if (value === undefined) {
106
+ throw new Error(
107
+ `Missing template parameter "${key}" in template "${template}". ` +
108
+ `Available params: ${Object.keys(params).join(', ') || '(none)'}`,
109
+ );
110
+ }
111
+ return String(value);
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Normalizes a TypeRenderer to function form.
117
+ * Called during pack assembly, not at emission time.
118
+ *
119
+ * Handles all TypeRenderer forms:
120
+ * - Raw string template: `'Vector<{{length}}>'`
121
+ * - Raw function: `(params, ctx) => ...`
122
+ * - Structured template: `{ kind: 'template', template: '...' }`
123
+ * - Structured function: `{ kind: 'function', render: fn }`
124
+ */
125
+ export function normalizeRenderer(codecId: string, renderer: TypeRenderer): NormalizedTypeRenderer {
126
+ // Handle raw string (template shorthand)
127
+ if (typeof renderer === 'string') {
128
+ return {
129
+ codecId,
130
+ render: (params, ctx) => interpolateTypeTemplate(renderer, params, ctx),
131
+ };
132
+ }
133
+
134
+ // Handle raw function (function shorthand)
135
+ if (typeof renderer === 'function') {
136
+ return { codecId, render: renderer };
137
+ }
138
+
139
+ // Handle structured function form
140
+ if (renderer.kind === 'function') {
141
+ return { codecId, render: renderer.render };
142
+ }
143
+
144
+ // Handle structured template form
145
+ const { template } = renderer;
146
+ return {
147
+ codecId,
148
+ render: (params, ctx) => interpolateTypeTemplate(template, params, ctx),
149
+ };
150
+ }
31
151
 
32
152
  /**
33
153
  * Declarative fields that describe component metadata.
34
- * These fields are owned directly by descriptors (not nested under a manifest).
35
154
  */
36
155
  export interface ComponentMetadata {
37
156
  /** Component version (semver) */
@@ -49,7 +168,35 @@ export interface ComponentMetadata {
49
168
 
50
169
  /** Type imports for contract.d.ts generation */
51
170
  readonly types?: {
52
- readonly codecTypes?: { readonly import: TypesImportSpec };
171
+ readonly codecTypes?: {
172
+ /**
173
+ * Base codec types import spec.
174
+ * Optional: adapters typically provide this, extensions usually don't.
175
+ */
176
+ readonly import?: TypesImportSpec;
177
+ /**
178
+ * Optional renderers for parameterized codecs owned by this component.
179
+ * Key is codecId (e.g., 'pg/vector@1'), value is the type renderer.
180
+ *
181
+ * Templates are normalized to functions during pack assembly.
182
+ * Duplicate codecId across descriptors is a hard error.
183
+ */
184
+ readonly parameterized?: Record<string, TypeRenderer>;
185
+ /**
186
+ * Optional additional type-only imports required by parameterized renderers.
187
+ *
188
+ * These imports are included in generated `contract.d.ts` but are NOT treated as
189
+ * codec type maps (i.e., they should not be intersected into `export type CodecTypes = ...`).
190
+ *
191
+ * Example: `Vector<N>` for pgvector renderers that emit `Vector<{{length}}>`
192
+ */
193
+ readonly typeImports?: ReadonlyArray<TypesImportSpec>;
194
+ /**
195
+ * Optional control-plane hooks keyed by codecId.
196
+ * Used by family-specific planners/verifiers to handle storage types.
197
+ */
198
+ readonly controlPlaneHooks?: Record<string, unknown>;
199
+ };
53
200
  readonly operationTypes?: { readonly import: TypesImportSpec };
54
201
  readonly storage?: ReadonlyArray<{
55
202
  readonly typeId: string;
@@ -58,9 +205,6 @@ export interface ComponentMetadata {
58
205
  readonly nativeType?: string;
59
206
  }>;
60
207
  };
61
-
62
- /** Operation manifests for building operation registries */
63
- readonly operations?: ReadonlyArray<OperationManifest>;
64
208
  }
65
209
 
66
210
  /**
@@ -393,17 +537,6 @@ export type TargetBoundComponentDescriptor<TFamilyId extends string, TTargetId e
393
537
  | DriverDescriptor<TFamilyId, TTargetId>
394
538
  | ExtensionDescriptor<TFamilyId, TTargetId>;
395
539
 
396
- // ============================================================================
397
- // Framework Component Instance Base Types
398
- // ============================================================================
399
- //
400
- // These are minimal, identity-only interfaces for component instances.
401
- // They carry the component's identity (familyId, targetId) without any
402
- // behavior methods. Plane-specific interfaces (ControlFamilyInstance,
403
- // RuntimeFamilyInstance, etc.) extend these bases and add domain actions.
404
- //
405
- // ============================================================================
406
-
407
540
  /**
408
541
  * Base interface for family instances.
409
542
  *
package/src/ir.ts CHANGED
@@ -1,3 +1,10 @@
1
+ function ifDefined<K extends string, V>(
2
+ key: K,
3
+ value: V | undefined,
4
+ ): Record<never, never> | { [P in K]: V } {
5
+ return value !== undefined ? ({ [key]: value } as { [P in K]: V }) : {};
6
+ }
7
+
1
8
  /**
2
9
  * ContractIR types and factories for building contract intermediate representation.
3
10
  * ContractIR is family-agnostic and used by authoring, emitter, and no-emit runtime.
@@ -6,12 +13,13 @@
6
13
  /**
7
14
  * ContractIR represents the intermediate representation of a contract.
8
15
  * It is family-agnostic and contains generic storage, models, and relations.
9
- * Note: coreHash and profileHash are computed by the emitter, not part of the IR.
16
+ * Note: storageHash/executionHash and profileHash are computed by the emitter, not part of the IR.
10
17
  */
11
18
  export interface ContractIR<
12
19
  TStorage extends Record<string, unknown> = Record<string, unknown>,
13
20
  TModels extends Record<string, unknown> = Record<string, unknown>,
14
21
  TRelations extends Record<string, unknown> = Record<string, unknown>,
22
+ TExecution extends Record<string, unknown> = Record<string, unknown>,
15
23
  > {
16
24
  readonly schemaVersion: string;
17
25
  readonly targetFamily: string;
@@ -19,6 +27,7 @@ export interface ContractIR<
19
27
  readonly models: TModels;
20
28
  readonly relations: TRelations;
21
29
  readonly storage: TStorage;
30
+ readonly execution?: TExecution;
22
31
  readonly extensionPacks: Record<string, unknown>;
23
32
  readonly capabilities: Record<string, Record<string, boolean>>;
24
33
  readonly meta: Record<string, unknown>;
@@ -27,26 +36,29 @@ export interface ContractIR<
27
36
 
28
37
  /**
29
38
  * Creates the header portion of a ContractIR.
30
- * Contains schema version, target, target family, core hash, and optional profile hash.
39
+ * Contains schema version, target, target family, storage hash, and optional profile hash.
31
40
  */
32
41
  export function irHeader(opts: {
33
42
  target: string;
34
43
  targetFamily: string;
35
- coreHash: string;
36
- profileHash?: string;
44
+ storageHash: string;
45
+ executionHash?: string | undefined;
46
+ profileHash?: string | undefined;
37
47
  }): {
38
48
  readonly schemaVersion: string;
39
49
  readonly target: string;
40
50
  readonly targetFamily: string;
41
- readonly coreHash: string;
42
- readonly profileHash?: string;
51
+ readonly storageHash: string;
52
+ readonly executionHash?: string | undefined;
53
+ readonly profileHash?: string | undefined;
43
54
  } {
44
55
  return {
45
56
  schemaVersion: '1',
46
57
  target: opts.target,
47
58
  targetFamily: opts.targetFamily,
48
- coreHash: opts.coreHash,
49
- ...(opts.profileHash !== undefined && { profileHash: opts.profileHash }),
59
+ storageHash: opts.storageHash,
60
+ ...ifDefined('executionHash', opts.executionHash),
61
+ ...ifDefined('profileHash', opts.profileHash),
50
62
  };
51
63
  }
52
64
 
@@ -82,13 +94,15 @@ export function contractIR<
82
94
  TStorage extends Record<string, unknown>,
83
95
  TModels extends Record<string, unknown>,
84
96
  TRelations extends Record<string, unknown>,
97
+ TExecution extends Record<string, unknown>,
85
98
  >(opts: {
86
99
  header: {
87
100
  readonly schemaVersion: string;
88
101
  readonly target: string;
89
102
  readonly targetFamily: string;
90
- readonly coreHash: string;
91
- readonly profileHash?: string;
103
+ readonly storageHash: string;
104
+ readonly executionHash?: string | undefined;
105
+ readonly profileHash?: string | undefined;
92
106
  };
93
107
  meta: {
94
108
  readonly capabilities: Record<string, Record<string, boolean>>;
@@ -99,8 +113,9 @@ export function contractIR<
99
113
  storage: TStorage;
100
114
  models: TModels;
101
115
  relations: TRelations;
102
- }): ContractIR<TStorage, TModels, TRelations> {
103
- // ContractIR doesn't include coreHash or profileHash (those are computed by emitter)
116
+ execution?: TExecution;
117
+ }): ContractIR<TStorage, TModels, TRelations, TExecution> {
118
+ // ContractIR doesn't include storageHash/executionHash or profileHash (those are computed by emitter)
104
119
  return {
105
120
  schemaVersion: opts.header.schemaVersion,
106
121
  target: opts.header.target,
@@ -109,5 +124,6 @@ export function contractIR<
109
124
  storage: opts.storage,
110
125
  models: opts.models,
111
126
  relations: opts.relations,
127
+ ...ifDefined('execution', opts.execution),
112
128
  };
113
129
  }