@prisma-next/contract 0.0.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.
package/README.md ADDED
@@ -0,0 +1,233 @@
1
+ # @prisma-next/contract
2
+
3
+ Data contract type definitions and JSON schema for Prisma Next.
4
+
5
+ ## Overview
6
+
7
+ This package provides TypeScript type definitions and JSON Schemas for Prisma Next data contracts. The data contract is the canonical description of an application's data model and storage layout, independent of any specific query language or database target.
8
+
9
+ ## Responsibilities
10
+
11
+ - **Core Contract Types**: Defines framework-level contract types (`ContractBase`, `Source`) that are shared across all target families
12
+ - **Document Family Types**: Provides TypeScript types for document target family contracts (`DocumentContract`)
13
+ - **JSON Schema Validation**: Provides JSON Schemas for validating contract structure in IDEs and tooling
14
+ - **Type Guards**: Provides runtime type guards for narrowing contract types (`isDocumentContract`)
15
+ - **Emitter Types**: Defines emitter SPI types (`TargetFamilyHook`, `ValidationContext`, `TypesImportSpec`) that are shared between emitter and control plane
16
+
17
+ The contract supports document target families:
18
+ - **Document**: For document databases (MongoDB, Firestore, etc.)
19
+
20
+ ## Package Contents
21
+
22
+ - **TypeScript Types**: Type definitions for `DocumentContract`, `ContractMarkerRecord`, and related types
23
+ - **Emitter Types**: SPI types for emitter hooks (`TargetFamilyHook`, `ValidationContext`, `TypesImportSpec`)
24
+ - **JSON Schemas**: Schema definitions for validating `contract.json` files in IDEs and tooling
25
+ - `data-contract-document-v1.json` (Document family)
26
+
27
+ ## Usage
28
+
29
+ ### TypeScript Types
30
+
31
+ Import contract types in your TypeScript code:
32
+
33
+ ```typescript
34
+ import type {
35
+ ContractMarkerRecord,
36
+ DocumentContract,
37
+ TargetFamilyHook,
38
+ TypesImportSpec,
39
+ ValidationContext,
40
+ } from '@prisma-next/contract/types';
41
+ import { isDocumentContract } from '@prisma-next/contract/types';
42
+
43
+ // Use type guards to narrow the contract type
44
+ function processContract(contract: DocumentContract) {
45
+ if (isDocumentContract(contract)) {
46
+ // contract is DocumentContract
47
+ console.log(contract.storage.document.collections);
48
+ }
49
+ }
50
+
51
+ // Use ContractMarkerRecord for database marker operations
52
+ function processMarker(marker: ContractMarkerRecord) {
53
+ console.log(marker.coreHash, marker.profileHash);
54
+ }
55
+
56
+ // Use emitter types for implementing family hooks
57
+ const myFamilyHook: TargetFamilyHook = {
58
+ id: 'my-family',
59
+ validateTypes: (ir, ctx: ValidationContext) => {
60
+ // Validation logic
61
+ },
62
+ validateStructure: (ir) => {
63
+ // Structure validation
64
+ },
65
+ generateContractTypes: (ir, codecTypeImports: TypesImportSpec[], operationTypeImports: TypesImportSpec[]) => {
66
+ // Type generation
67
+ return '// Generated types...';
68
+ },
69
+ };
70
+ ```
71
+
72
+ ### JSON Schema Validation
73
+
74
+ Reference the appropriate JSON schema in your `contract.json` files to enable IDE validation and autocomplete.
75
+
76
+ #### Document Family
77
+
78
+ For document targets (MongoDB, Firestore, etc.):
79
+
80
+ ```json
81
+ {
82
+ "$schema": "node_modules/@prisma-next/contract/schemas/data-contract-document-v1.json",
83
+ "schemaVersion": "1",
84
+ "target": "mongodb",
85
+ "targetFamily": "document",
86
+ "coreHash": "sha256:...",
87
+ "storage": {
88
+ "document": {
89
+ "collections": {
90
+ "users": {
91
+ "name": "users",
92
+ "fields": {
93
+ "id": { "type": "objectId", "nullable": false },
94
+ "email": { "type": "string", "nullable": false }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ **Note:** For SQL contracts, use `@prisma-next/sql-query/schema-sql` instead:
104
+
105
+ ```json
106
+ {
107
+ "$schema": "node_modules/@prisma-next/sql-query/schemas/data-contract-sql-v1.json",
108
+ "schemaVersion": "1",
109
+ "target": "postgres",
110
+ "targetFamily": "sql",
111
+ "coreHash": "sha256:...",
112
+ "storage": {
113
+ "tables": {
114
+ "user": {
115
+ "columns": {
116
+ "id": { "type": "int4", "nullable": false },
117
+ "email": { "type": "text", "nullable": false }
118
+ },
119
+ "primaryKey": {
120
+ "columns": ["id"],
121
+ "name": "user_pkey"
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ After installing this package, IDEs like VS Code will automatically:
130
+ - Validate your contract structure
131
+ - Provide autocomplete for properties
132
+ - Show descriptions and constraints in tooltips
133
+ - Highlight errors for invalid configurations
134
+
135
+ ## Schema Reference
136
+
137
+ ### Common Header Fields
138
+
139
+ All contracts share these common fields:
140
+
141
+ - **`schemaVersion`** (required): Contract schema version (currently `"1"`)
142
+ - **`target`** (required): Database target identifier (e.g., `"postgres"`, `"mongo"`, `"firestore"`)
143
+ - **`targetFamily`** (required): Target family classification (`"document"` for document contracts)
144
+ - **`coreHash`** (required): SHA-256 hash of the core schema structure
145
+ - **`profileHash`** (optional): SHA-256 hash of the capability profile
146
+ - **`capabilities`** (optional): Capability flags declared by the contract
147
+ - **`extensions`** (optional): Extension packs and their configuration
148
+ - **`meta`** (optional): Non-semantic metadata (excluded from hashing)
149
+ - **`sources`** (optional): Read-only sources (views, etc.) available for querying
150
+
151
+ ### Document Family Structure
152
+
153
+ - **`storage.document.collections`**: Object mapping collection names to collection definitions
154
+ - Each collection includes:
155
+ - **`name`**: Logical collection name
156
+ - **`id`** (optional): ID generation strategy (`auto`, `client`, `uuid`, `cuid`, `objectId`)
157
+ - **`fields`**: Field definitions using `FieldType` (supports nested objects and arrays)
158
+ - **`indexes`** (optional): Array of index definitions with keys and optional predicates
159
+ - **`readOnly`** (optional): Whether mutations are disallowed
160
+
161
+ ## Type System
162
+
163
+ ### Type Guards
164
+
165
+ Use type guards to narrow the contract type:
166
+
167
+ ```typescript
168
+ import { isDocumentContract } from '@prisma-next/contract/types';
169
+
170
+ if (isDocumentContract(contract)) {
171
+ // TypeScript knows contract is DocumentContract
172
+ const collections = contract.storage.document.collections;
173
+ }
174
+ ```
175
+
176
+ ## Exports
177
+
178
+ - `./types`: TypeScript type definitions and type guards
179
+ - `./schema-document`: Document family JSON Schema (`schemas/data-contract-document-v1.json`)
180
+
181
+ ## Architecture
182
+
183
+ ```mermaid
184
+ flowchart TD
185
+ subgraph "Contract Package"
186
+ TYPES[Type Definitions]
187
+ SCHEMA[JSON Schemas]
188
+ GUARDS[Type Guards]
189
+ end
190
+
191
+ subgraph "Consumers"
192
+ EMITTER[Emitter]
193
+ RUNTIME[Runtime]
194
+ QUERY[Query Builder]
195
+ end
196
+
197
+ TYPES --> EMITTER
198
+ TYPES --> RUNTIME
199
+ TYPES --> QUERY
200
+ SCHEMA --> EMITTER
201
+ GUARDS --> RUNTIME
202
+ GUARDS --> QUERY
203
+ ```
204
+
205
+ ## Related Subsystems
206
+
207
+ - **[Data Contract](../../docs/architecture%20docs/subsystems/1.%20Data%20Contract.md)**: Detailed subsystem specification
208
+ - **[Contract Emitter & Types](../../docs/architecture%20docs/subsystems/2.%20Contract%20Emitter%20&%20Types.md)**: Contract emission
209
+
210
+ ## Related ADRs
211
+
212
+ - [ADR 001 - Migrations as Edges](../../docs/architecture%20docs/adrs/ADR%20001%20-%20Migrations%20as%20Edges.md)
213
+ - [ADR 004 - Core Hash vs Profile Hash](../../docs/architecture%20docs/adrs/ADR%20004%20-%20Core%20Hash%20vs%20Profile%20Hash.md)
214
+ - [ADR 006 - Dual Authoring Modes](../../docs/architecture%20docs/adrs/ADR%20006%20-%20Dual%20Authoring%20Modes.md)
215
+ - [ADR 010 - Canonicalization Rules](../../docs/architecture%20docs/adrs/ADR%20010%20-%20Canonicalization%20Rules.md)
216
+ - [ADR 021 - Contract Marker Storage](../../docs/architecture%20docs/adrs/ADR%20021%20-%20Contract%20Marker%20Storage.md)
217
+
218
+ ## Dependencies
219
+
220
+ - **`@prisma-next/operations`**: For `OperationRegistry` type used in `ValidationContext` and `TargetFamilyHook`
221
+ - **Note**: This package depends on `@prisma-next/operations`, but `@prisma-next/operations` does not depend on this package (no cycle). The `OperationRegistry` type is used in emitter SPI types that are shared between migration-plane and shared-plane packages.
222
+
223
+ **Dependents:**
224
+ - **`@prisma-next/contract-authoring`**: Uses core contract types for authoring
225
+ - **`@prisma-next/sql-contract`**: Extends core contract types for SQL family
226
+ - **`@prisma-next/emitter`**: Uses contract types for emission
227
+ - **`@prisma-next/runtime`**: Uses contract types for runtime execution
228
+ - **`@prisma-next/sql-query`**: Uses contract types for query building
229
+
230
+ ## Related Packages
231
+
232
+ - `@prisma-next/sql-query`: SQL query builder and plan types
233
+ - `@prisma-next/runtime`: Runtime execution engine that consumes contracts
@@ -0,0 +1,77 @@
1
+ /**
2
+ * ContractIR types and factories for building contract intermediate representation.
3
+ * ContractIR is family-agnostic and used by authoring, emitter, and no-emit runtime.
4
+ */
5
+ /**
6
+ * ContractIR represents the intermediate representation of a contract.
7
+ * It is family-agnostic and contains generic storage, models, and relations.
8
+ * Note: coreHash and profileHash are computed by the emitter, not part of the IR.
9
+ */
10
+ interface ContractIR<TStorage extends Record<string, unknown> = Record<string, unknown>, TModels extends Record<string, unknown> = Record<string, unknown>, TRelations extends Record<string, unknown> = Record<string, unknown>> {
11
+ readonly schemaVersion: string;
12
+ readonly targetFamily: string;
13
+ readonly target: string;
14
+ readonly models: TModels;
15
+ readonly relations: TRelations;
16
+ readonly storage: TStorage;
17
+ readonly extensions: Record<string, unknown>;
18
+ readonly capabilities: Record<string, Record<string, boolean>>;
19
+ readonly meta: Record<string, unknown>;
20
+ readonly sources: Record<string, unknown>;
21
+ }
22
+ /**
23
+ * Creates the header portion of a ContractIR.
24
+ * Contains schema version, target, target family, core hash, and optional profile hash.
25
+ */
26
+ declare function irHeader(opts: {
27
+ target: string;
28
+ targetFamily: string;
29
+ coreHash: string;
30
+ profileHash?: string;
31
+ }): {
32
+ readonly schemaVersion: string;
33
+ readonly target: string;
34
+ readonly targetFamily: string;
35
+ readonly coreHash: string;
36
+ readonly profileHash?: string;
37
+ };
38
+ /**
39
+ * Creates the meta portion of a ContractIR.
40
+ * Contains capabilities, extensions, meta, and sources with empty object defaults.
41
+ * If a field is explicitly `undefined`, it will be omitted (for testing validation).
42
+ */
43
+ declare function irMeta(opts?: {
44
+ capabilities?: Record<string, Record<string, boolean>> | undefined;
45
+ extensions?: Record<string, unknown> | undefined;
46
+ meta?: Record<string, unknown> | undefined;
47
+ sources?: Record<string, unknown> | undefined;
48
+ }): {
49
+ readonly capabilities: Record<string, Record<string, boolean>>;
50
+ readonly extensions: Record<string, unknown>;
51
+ readonly meta: Record<string, unknown>;
52
+ readonly sources: Record<string, unknown>;
53
+ };
54
+ /**
55
+ * Creates a complete ContractIR by combining header, meta, and family-specific sections.
56
+ * This is a family-agnostic factory that accepts generic storage, models, and relations.
57
+ */
58
+ declare function contractIR<TStorage extends Record<string, unknown>, TModels extends Record<string, unknown>, TRelations extends Record<string, unknown>>(opts: {
59
+ header: {
60
+ readonly schemaVersion: string;
61
+ readonly target: string;
62
+ readonly targetFamily: string;
63
+ readonly coreHash: string;
64
+ readonly profileHash?: string;
65
+ };
66
+ meta: {
67
+ readonly capabilities: Record<string, Record<string, boolean>>;
68
+ readonly extensions: Record<string, unknown>;
69
+ readonly meta: Record<string, unknown>;
70
+ readonly sources: Record<string, unknown>;
71
+ };
72
+ storage: TStorage;
73
+ models: TModels;
74
+ relations: TRelations;
75
+ }): ContractIR<TStorage, TModels, TRelations>;
76
+
77
+ export { type ContractIR, contractIR, irHeader, irMeta };
@@ -0,0 +1,35 @@
1
+ // src/ir.ts
2
+ function irHeader(opts) {
3
+ return {
4
+ schemaVersion: "1",
5
+ target: opts.target,
6
+ targetFamily: opts.targetFamily,
7
+ coreHash: opts.coreHash,
8
+ ...opts.profileHash !== void 0 && { profileHash: opts.profileHash }
9
+ };
10
+ }
11
+ function irMeta(opts) {
12
+ return {
13
+ capabilities: opts?.capabilities ?? {},
14
+ extensions: opts?.extensions ?? {},
15
+ meta: opts?.meta ?? {},
16
+ sources: opts?.sources ?? {}
17
+ };
18
+ }
19
+ function contractIR(opts) {
20
+ return {
21
+ schemaVersion: opts.header.schemaVersion,
22
+ target: opts.header.target,
23
+ targetFamily: opts.header.targetFamily,
24
+ ...opts.meta,
25
+ storage: opts.storage,
26
+ models: opts.models,
27
+ relations: opts.relations
28
+ };
29
+ }
30
+ export {
31
+ contractIR,
32
+ irHeader,
33
+ irMeta
34
+ };
35
+ //# sourceMappingURL=ir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ir.ts"],"sourcesContent":["/**\n * ContractIR types and factories for building contract intermediate representation.\n * ContractIR is family-agnostic and used by authoring, emitter, and no-emit runtime.\n */\n\n/**\n * ContractIR represents the intermediate representation of a contract.\n * It is family-agnostic and contains generic storage, models, and relations.\n * Note: coreHash and profileHash are computed by the emitter, not part of the IR.\n */\nexport interface ContractIR<\n TStorage extends Record<string, unknown> = Record<string, unknown>,\n TModels extends Record<string, unknown> = Record<string, unknown>,\n TRelations extends Record<string, unknown> = Record<string, unknown>,\n> {\n readonly schemaVersion: string;\n readonly targetFamily: string;\n readonly target: string;\n readonly models: TModels;\n readonly relations: TRelations;\n readonly storage: TStorage;\n readonly extensions: Record<string, unknown>;\n readonly capabilities: Record<string, Record<string, boolean>>;\n readonly meta: Record<string, unknown>;\n readonly sources: Record<string, unknown>;\n}\n\n/**\n * Creates the header portion of a ContractIR.\n * Contains schema version, target, target family, core hash, and optional profile hash.\n */\nexport function irHeader(opts: {\n target: string;\n targetFamily: string;\n coreHash: string;\n profileHash?: string;\n}): {\n readonly schemaVersion: string;\n readonly target: string;\n readonly targetFamily: string;\n readonly coreHash: string;\n readonly profileHash?: string;\n} {\n return {\n schemaVersion: '1',\n target: opts.target,\n targetFamily: opts.targetFamily,\n coreHash: opts.coreHash,\n ...(opts.profileHash !== undefined && { profileHash: opts.profileHash }),\n };\n}\n\n/**\n * Creates the meta portion of a ContractIR.\n * Contains capabilities, extensions, meta, and sources with empty object defaults.\n * If a field is explicitly `undefined`, it will be omitted (for testing validation).\n */\nexport function irMeta(opts?: {\n capabilities?: Record<string, Record<string, boolean>> | undefined;\n extensions?: Record<string, unknown> | undefined;\n meta?: Record<string, unknown> | undefined;\n sources?: Record<string, unknown> | undefined;\n}): {\n readonly capabilities: Record<string, Record<string, boolean>>;\n readonly extensions: Record<string, unknown>;\n readonly meta: Record<string, unknown>;\n readonly sources: Record<string, unknown>;\n} {\n return {\n capabilities: opts?.capabilities ?? {},\n extensions: opts?.extensions ?? {},\n meta: opts?.meta ?? {},\n sources: opts?.sources ?? {},\n };\n}\n\n/**\n * Creates a complete ContractIR by combining header, meta, and family-specific sections.\n * This is a family-agnostic factory that accepts generic storage, models, and relations.\n */\nexport function contractIR<\n TStorage extends Record<string, unknown>,\n TModels extends Record<string, unknown>,\n TRelations extends Record<string, unknown>,\n>(opts: {\n header: {\n readonly schemaVersion: string;\n readonly target: string;\n readonly targetFamily: string;\n readonly coreHash: string;\n readonly profileHash?: string;\n };\n meta: {\n readonly capabilities: Record<string, Record<string, boolean>>;\n readonly extensions: Record<string, unknown>;\n readonly meta: Record<string, unknown>;\n readonly sources: Record<string, unknown>;\n };\n storage: TStorage;\n models: TModels;\n relations: TRelations;\n}): ContractIR<TStorage, TModels, TRelations> {\n // ContractIR doesn't include coreHash or profileHash (those are computed by emitter)\n return {\n schemaVersion: opts.header.schemaVersion,\n target: opts.header.target,\n targetFamily: opts.header.targetFamily,\n ...opts.meta,\n storage: opts.storage,\n models: opts.models,\n relations: opts.relations,\n };\n}\n"],"mappings":";AA+BO,SAAS,SAAS,MAWvB;AACA,SAAO;AAAA,IACL,eAAe;AAAA,IACf,QAAQ,KAAK;AAAA,IACb,cAAc,KAAK;AAAA,IACnB,UAAU,KAAK;AAAA,IACf,GAAI,KAAK,gBAAgB,UAAa,EAAE,aAAa,KAAK,YAAY;AAAA,EACxE;AACF;AAOO,SAAS,OAAO,MAUrB;AACA,SAAO;AAAA,IACL,cAAc,MAAM,gBAAgB,CAAC;AAAA,IACrC,YAAY,MAAM,cAAc,CAAC;AAAA,IACjC,MAAM,MAAM,QAAQ,CAAC;AAAA,IACrB,SAAS,MAAM,WAAW,CAAC;AAAA,EAC7B;AACF;AAMO,SAAS,WAId,MAiB4C;AAE5C,SAAO;AAAA,IACL,eAAe,KAAK,OAAO;AAAA,IAC3B,QAAQ,KAAK,OAAO;AAAA,IACpB,cAAc,KAAK,OAAO;AAAA,IAC1B,GAAG,KAAK;AAAA,IACR,SAAS,KAAK;AAAA,IACd,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK;AAAA,EAClB;AACF;","names":[]}
@@ -0,0 +1,3 @@
1
+ export { A as ArgSpecManifest, E as ExtensionPack, a as ExtensionPackManifest, L as LoweringSpecManifest, O as OperationManifest, R as ReturnSpecManifest } from './types.js';
2
+ import '@prisma-next/operations';
3
+ import './ir.js';
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=pack-manifest-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,250 @@
1
+ import { OperationRegistry } from '@prisma-next/operations';
2
+ import { ContractIR } from './ir.js';
3
+
4
+ interface ContractBase {
5
+ readonly schemaVersion: string;
6
+ readonly target: string;
7
+ readonly targetFamily: string;
8
+ readonly coreHash: string;
9
+ readonly profileHash?: string;
10
+ readonly capabilities?: Record<string, Record<string, boolean>>;
11
+ readonly extensions?: Record<string, unknown>;
12
+ readonly meta?: Record<string, unknown>;
13
+ readonly sources?: Record<string, Source>;
14
+ }
15
+ interface FieldType {
16
+ readonly type: string;
17
+ readonly nullable: boolean;
18
+ readonly items?: FieldType;
19
+ readonly properties?: Record<string, FieldType>;
20
+ }
21
+ interface Source {
22
+ readonly readOnly: boolean;
23
+ readonly projection: Record<string, FieldType>;
24
+ readonly origin?: Record<string, unknown>;
25
+ readonly capabilities?: Record<string, boolean>;
26
+ }
27
+ interface DocIndex {
28
+ readonly name: string;
29
+ readonly keys: Record<string, 'asc' | 'desc'>;
30
+ readonly unique?: boolean;
31
+ readonly where?: Expr;
32
+ }
33
+ type Expr = {
34
+ readonly kind: 'eq';
35
+ readonly path: ReadonlyArray<string>;
36
+ readonly value: unknown;
37
+ } | {
38
+ readonly kind: 'exists';
39
+ readonly path: ReadonlyArray<string>;
40
+ };
41
+ interface DocCollection {
42
+ readonly name: string;
43
+ readonly id?: {
44
+ readonly strategy: 'auto' | 'client' | 'uuid' | 'cuid' | 'objectId';
45
+ };
46
+ readonly fields: Record<string, FieldType>;
47
+ readonly indexes?: ReadonlyArray<DocIndex>;
48
+ readonly readOnly?: boolean;
49
+ }
50
+ interface DocumentStorage {
51
+ readonly document: {
52
+ readonly collections: Record<string, DocCollection>;
53
+ };
54
+ }
55
+ interface DocumentContract extends ContractBase {
56
+ readonly targetFamily: string;
57
+ readonly storage: DocumentStorage;
58
+ }
59
+ interface ParamDescriptor {
60
+ readonly index?: number;
61
+ readonly name?: string;
62
+ readonly type?: string;
63
+ readonly nullable?: boolean;
64
+ readonly source: 'dsl' | 'raw';
65
+ readonly refs?: {
66
+ table: string;
67
+ column: string;
68
+ };
69
+ }
70
+ interface PlanRefs {
71
+ readonly tables?: readonly string[];
72
+ readonly columns?: ReadonlyArray<{
73
+ table: string;
74
+ column: string;
75
+ }>;
76
+ readonly indexes?: ReadonlyArray<{
77
+ readonly table: string;
78
+ readonly columns: ReadonlyArray<string>;
79
+ readonly name?: string;
80
+ }>;
81
+ }
82
+ interface PlanMeta {
83
+ readonly target: string;
84
+ readonly targetFamily?: string;
85
+ readonly coreHash: string;
86
+ readonly profileHash?: string;
87
+ readonly lane: string;
88
+ readonly annotations?: {
89
+ codecs?: Record<string, string>;
90
+ [key: string]: unknown;
91
+ };
92
+ readonly paramDescriptors: ReadonlyArray<ParamDescriptor>;
93
+ readonly refs?: PlanRefs;
94
+ readonly projection?: Record<string, string> | ReadonlyArray<string>;
95
+ /**
96
+ * Optional mapping of projection alias → column type ID (fully qualified ns/name@version).
97
+ * Used for codec resolution when AST+refs don't provide enough type info.
98
+ */
99
+ readonly projectionTypes?: Record<string, string>;
100
+ }
101
+ /**
102
+ * Canonical execution plan shape used by runtimes.
103
+ *
104
+ * - Row is the inferred result row type (TypeScript-only).
105
+ * - Ast is the optional, family-specific AST type (e.g. SQL QueryAst).
106
+ *
107
+ * The payload executed by the runtime is represented by the sql + params pair
108
+ * for now; future families can specialize this via Ast or additional metadata.
109
+ */
110
+ interface ExecutionPlan<Row = unknown, Ast = unknown> {
111
+ readonly sql: string;
112
+ readonly params: readonly unknown[];
113
+ readonly ast?: Ast;
114
+ readonly meta: PlanMeta;
115
+ /**
116
+ * Phantom property to carry the Row generic for type-level utilities.
117
+ * Not set at runtime; used only for ResultType extraction.
118
+ */
119
+ readonly _row?: Row;
120
+ }
121
+ /**
122
+ * Utility type to extract the Row type from an ExecutionPlan.
123
+ * Example: `type Row = ResultType<typeof plan>`
124
+ *
125
+ * Works with both ExecutionPlan and SqlQueryPlan (SQL query plans before lowering).
126
+ * SqlQueryPlan includes a phantom `_Row` property to preserve the generic parameter
127
+ * for type extraction.
128
+ */
129
+ type ResultType<P> = P extends ExecutionPlan<infer R, unknown> ? R : P extends {
130
+ readonly _Row?: infer R;
131
+ } ? R : never;
132
+ /**
133
+ * Type guard to check if a contract is a Document contract
134
+ */
135
+ declare function isDocumentContract(contract: unknown): contract is DocumentContract;
136
+ /**
137
+ * Contract marker record stored in the database.
138
+ * Represents the current contract identity for a database.
139
+ */
140
+ interface ContractMarkerRecord {
141
+ readonly coreHash: string;
142
+ readonly profileHash: string;
143
+ readonly contractJson: unknown | null;
144
+ readonly canonicalVersion: number | null;
145
+ readonly updatedAt: Date;
146
+ readonly appTag: string | null;
147
+ readonly meta: Record<string, unknown>;
148
+ }
149
+ /**
150
+ * Specifies how to import TypeScript types from a package.
151
+ * Used in extension pack manifests to declare codec and operation type imports.
152
+ */
153
+ interface TypesImportSpec {
154
+ readonly package: string;
155
+ readonly named: string;
156
+ readonly alias: string;
157
+ }
158
+ /**
159
+ * Validation context passed to TargetFamilyHook.validateTypes().
160
+ * Contains pre-assembled operation registry, type imports, and extension IDs.
161
+ */
162
+ interface ValidationContext {
163
+ readonly operationRegistry?: OperationRegistry;
164
+ readonly codecTypeImports?: ReadonlyArray<TypesImportSpec>;
165
+ readonly operationTypeImports?: ReadonlyArray<TypesImportSpec>;
166
+ readonly extensionIds?: ReadonlyArray<string>;
167
+ }
168
+ /**
169
+ * SPI interface for target family hooks that extend emission behavior.
170
+ * Implemented by family-specific emitter hooks (e.g., SQL family).
171
+ */
172
+ interface TargetFamilyHook {
173
+ readonly id: string;
174
+ /**
175
+ * Validates that all type IDs in the contract come from referenced extensions.
176
+ * @param ir - Contract IR to validate
177
+ * @param ctx - Validation context with operation registry and extension IDs
178
+ */
179
+ validateTypes(ir: ContractIR, ctx: ValidationContext): void;
180
+ /**
181
+ * Validates family-specific contract structure.
182
+ * @param ir - Contract IR to validate
183
+ */
184
+ validateStructure(ir: ContractIR): void;
185
+ /**
186
+ * Generates contract.d.ts file content.
187
+ * @param ir - Contract IR
188
+ * @param codecTypeImports - Array of codec type import specs
189
+ * @param operationTypeImports - Array of operation type import specs
190
+ * @returns Generated TypeScript type definitions as string
191
+ */
192
+ generateContractTypes(ir: ContractIR, codecTypeImports: ReadonlyArray<TypesImportSpec>, operationTypeImports: ReadonlyArray<TypesImportSpec>): string;
193
+ }
194
+ type ArgSpecManifest = {
195
+ readonly kind: 'typeId';
196
+ readonly type: string;
197
+ } | {
198
+ readonly kind: 'param';
199
+ } | {
200
+ readonly kind: 'literal';
201
+ };
202
+ type ReturnSpecManifest = {
203
+ readonly kind: 'typeId';
204
+ readonly type: string;
205
+ } | {
206
+ readonly kind: 'builtin';
207
+ readonly type: 'number' | 'boolean' | 'string';
208
+ };
209
+ interface LoweringSpecManifest {
210
+ readonly targetFamily: 'sql';
211
+ readonly strategy: 'infix' | 'function';
212
+ readonly template: string;
213
+ }
214
+ interface OperationManifest {
215
+ readonly for: string;
216
+ readonly method: string;
217
+ readonly args: ReadonlyArray<ArgSpecManifest>;
218
+ readonly returns: ReturnSpecManifest;
219
+ readonly lowering: LoweringSpecManifest;
220
+ readonly capabilities?: ReadonlyArray<string>;
221
+ }
222
+ interface ExtensionPackManifest {
223
+ readonly id: string;
224
+ readonly version: string;
225
+ readonly targets?: Record<string, {
226
+ readonly minVersion?: string;
227
+ }>;
228
+ readonly capabilities?: Record<string, unknown>;
229
+ readonly types?: {
230
+ readonly codecTypes?: {
231
+ readonly import: TypesImportSpec;
232
+ };
233
+ readonly operationTypes?: {
234
+ readonly import: TypesImportSpec;
235
+ };
236
+ readonly storage?: readonly {
237
+ readonly typeId: string;
238
+ readonly familyId: string;
239
+ readonly targetId: string;
240
+ readonly nativeType?: string;
241
+ }[];
242
+ };
243
+ readonly operations?: ReadonlyArray<OperationManifest>;
244
+ }
245
+ interface ExtensionPack {
246
+ readonly manifest: ExtensionPackManifest;
247
+ readonly path: string;
248
+ }
249
+
250
+ export { type ArgSpecManifest as A, type ContractBase, type ContractMarkerRecord, type DocCollection, type DocIndex, type DocumentContract, type DocumentStorage, type ExtensionPack as E, type ExecutionPlan, type Expr, type FieldType, type LoweringSpecManifest as L, type OperationManifest as O, type ParamDescriptor, type PlanMeta, type PlanRefs, type ReturnSpecManifest as R, type ResultType, type Source, type TargetFamilyHook, type TypesImportSpec, type ValidationContext, type ExtensionPackManifest as a, isDocumentContract };
@@ -0,0 +1,8 @@
1
+ // src/types.ts
2
+ function isDocumentContract(contract) {
3
+ return typeof contract === "object" && contract !== null && "targetFamily" in contract && contract.targetFamily === "document";
4
+ }
5
+ export {
6
+ isDocumentContract
7
+ };
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { OperationRegistry } from '@prisma-next/operations';\nimport type { ContractIR } from './ir';\n\n// Shared header and neutral types\n// Note: Fields like targetFamily accept string to work with JSON imports,\n// which don't preserve literal types. Runtime validation ensures correct values\nexport interface ContractBase {\n readonly schemaVersion: string;\n readonly target: string;\n readonly targetFamily: string;\n readonly coreHash: string;\n readonly profileHash?: string;\n readonly capabilities?: Record<string, Record<string, boolean>>;\n readonly extensions?: Record<string, unknown>;\n readonly meta?: Record<string, unknown>;\n readonly sources?: Record<string, Source>;\n}\n\nexport interface FieldType {\n readonly type: string;\n readonly nullable: boolean;\n readonly items?: FieldType;\n readonly properties?: Record<string, FieldType>;\n}\n\nexport interface Source {\n readonly readOnly: boolean;\n readonly projection: Record<string, FieldType>;\n readonly origin?: Record<string, unknown>;\n readonly capabilities?: Record<string, boolean>;\n}\n\n// Document family types\nexport interface DocIndex {\n readonly name: string;\n readonly keys: Record<string, 'asc' | 'desc'>;\n readonly unique?: boolean;\n readonly where?: Expr;\n}\n\nexport type Expr =\n | { readonly kind: 'eq'; readonly path: ReadonlyArray<string>; readonly value: unknown }\n | { readonly kind: 'exists'; readonly path: ReadonlyArray<string> };\n\nexport interface DocCollection {\n readonly name: string;\n readonly id?: {\n readonly strategy: 'auto' | 'client' | 'uuid' | 'cuid' | 'objectId';\n };\n readonly fields: Record<string, FieldType>;\n readonly indexes?: ReadonlyArray<DocIndex>;\n readonly readOnly?: boolean;\n}\n\nexport interface DocumentStorage {\n readonly document: {\n readonly collections: Record<string, DocCollection>;\n };\n}\n\nexport interface DocumentContract extends ContractBase {\n // Accept string to work with JSON imports; runtime validation ensures 'document'\n readonly targetFamily: string;\n readonly storage: DocumentStorage;\n}\n\n// Plan types - target-family agnostic execution types\nexport interface ParamDescriptor {\n readonly index?: number;\n readonly name?: string;\n readonly type?: string;\n readonly nullable?: boolean;\n readonly source: 'dsl' | 'raw';\n readonly refs?: { table: string; column: string };\n}\n\nexport interface PlanRefs {\n readonly tables?: readonly string[];\n readonly columns?: ReadonlyArray<{ table: string; column: string }>;\n readonly indexes?: ReadonlyArray<{\n readonly table: string;\n readonly columns: ReadonlyArray<string>;\n readonly name?: string;\n }>;\n}\n\nexport interface PlanMeta {\n readonly target: string;\n readonly targetFamily?: string;\n readonly coreHash: string;\n readonly profileHash?: string;\n readonly lane: string;\n readonly annotations?: {\n codecs?: Record<string, string>; // alias/param → codec id ('ns/name@v')\n [key: string]: unknown;\n };\n readonly paramDescriptors: ReadonlyArray<ParamDescriptor>;\n readonly refs?: PlanRefs;\n readonly projection?: Record<string, string> | ReadonlyArray<string>;\n /**\n * Optional mapping of projection alias → column type ID (fully qualified ns/name@version).\n * Used for codec resolution when AST+refs don't provide enough type info.\n */\n readonly projectionTypes?: Record<string, string>;\n}\n\n/**\n * Canonical execution plan shape used by runtimes.\n *\n * - Row is the inferred result row type (TypeScript-only).\n * - Ast is the optional, family-specific AST type (e.g. SQL QueryAst).\n *\n * The payload executed by the runtime is represented by the sql + params pair\n * for now; future families can specialize this via Ast or additional metadata.\n */\nexport interface ExecutionPlan<Row = unknown, Ast = unknown> {\n readonly sql: string;\n readonly params: readonly unknown[];\n readonly ast?: Ast;\n readonly meta: PlanMeta;\n /**\n * Phantom property to carry the Row generic for type-level utilities.\n * Not set at runtime; used only for ResultType extraction.\n */\n readonly _row?: Row;\n}\n\n/**\n * Utility type to extract the Row type from an ExecutionPlan.\n * Example: `type Row = ResultType<typeof plan>`\n *\n * Works with both ExecutionPlan and SqlQueryPlan (SQL query plans before lowering).\n * SqlQueryPlan includes a phantom `_Row` property to preserve the generic parameter\n * for type extraction.\n */\nexport type ResultType<P> = P extends ExecutionPlan<infer R, unknown>\n ? R\n : P extends { readonly _Row?: infer R }\n ? R\n : never;\n\n/**\n * Type guard to check if a contract is a Document contract\n */\nexport function isDocumentContract(contract: unknown): contract is DocumentContract {\n return (\n typeof contract === 'object' &&\n contract !== null &&\n 'targetFamily' in contract &&\n contract.targetFamily === 'document'\n );\n}\n\n/**\n * Contract marker record stored in the database.\n * Represents the current contract identity for a database.\n */\nexport interface ContractMarkerRecord {\n readonly coreHash: string;\n readonly profileHash: string;\n readonly contractJson: unknown | null;\n readonly canonicalVersion: number | null;\n readonly updatedAt: Date;\n readonly appTag: string | null;\n readonly meta: Record<string, unknown>;\n}\n\n// Emitter types - moved from @prisma-next/emitter to shared location\n/**\n * Specifies how to import TypeScript types from a package.\n * Used in extension pack manifests to declare codec and operation type imports.\n */\nexport interface TypesImportSpec {\n readonly package: string;\n readonly named: string;\n readonly alias: string;\n}\n\n/**\n * Validation context passed to TargetFamilyHook.validateTypes().\n * Contains pre-assembled operation registry, type imports, and extension IDs.\n */\nexport interface ValidationContext {\n readonly operationRegistry?: OperationRegistry;\n readonly codecTypeImports?: ReadonlyArray<TypesImportSpec>;\n readonly operationTypeImports?: ReadonlyArray<TypesImportSpec>;\n readonly extensionIds?: ReadonlyArray<string>;\n}\n\n/**\n * SPI interface for target family hooks that extend emission behavior.\n * Implemented by family-specific emitter hooks (e.g., SQL family).\n */\nexport interface TargetFamilyHook {\n readonly id: string;\n\n /**\n * Validates that all type IDs in the contract come from referenced extensions.\n * @param ir - Contract IR to validate\n * @param ctx - Validation context with operation registry and extension IDs\n */\n validateTypes(ir: ContractIR, ctx: ValidationContext): void;\n\n /**\n * Validates family-specific contract structure.\n * @param ir - Contract IR to validate\n */\n validateStructure(ir: ContractIR): void;\n\n /**\n * Generates contract.d.ts file content.\n * @param ir - Contract IR\n * @param codecTypeImports - Array of codec type import specs\n * @param operationTypeImports - Array of operation type import specs\n * @returns Generated TypeScript type definitions as string\n */\n generateContractTypes(\n ir: ContractIR,\n codecTypeImports: ReadonlyArray<TypesImportSpec>,\n operationTypeImports: ReadonlyArray<TypesImportSpec>,\n ): string;\n}\n\n// Extension pack manifest types - moved from @prisma-next/core-control-plane to shared location\nexport type ArgSpecManifest =\n | { readonly kind: 'typeId'; readonly type: string }\n | { readonly kind: 'param' }\n | { readonly kind: 'literal' };\n\nexport type ReturnSpecManifest =\n | { readonly kind: 'typeId'; readonly type: string }\n | { readonly kind: 'builtin'; readonly type: 'number' | 'boolean' | 'string' };\n\nexport interface LoweringSpecManifest {\n readonly targetFamily: 'sql';\n readonly strategy: 'infix' | 'function';\n readonly template: string;\n}\n\nexport interface OperationManifest {\n readonly for: string;\n readonly method: string;\n readonly args: ReadonlyArray<ArgSpecManifest>;\n readonly returns: ReturnSpecManifest;\n readonly lowering: LoweringSpecManifest;\n readonly capabilities?: ReadonlyArray<string>;\n}\n\nexport interface ExtensionPackManifest {\n readonly id: string;\n readonly version: string;\n readonly targets?: Record<string, { readonly minVersion?: string }>;\n readonly capabilities?: Record<string, unknown>;\n readonly types?: {\n readonly codecTypes?: {\n readonly import: TypesImportSpec;\n };\n readonly operationTypes?: {\n readonly import: TypesImportSpec;\n };\n readonly storage?: readonly {\n readonly typeId: string;\n readonly familyId: string;\n readonly targetId: string;\n readonly nativeType?: string;\n }[];\n };\n readonly operations?: ReadonlyArray<OperationManifest>;\n}\n\nexport interface ExtensionPack {\n readonly manifest: ExtensionPackManifest;\n readonly path: string;\n}\n"],"mappings":";AAgJO,SAAS,mBAAmB,UAAiD;AAClF,SACE,OAAO,aAAa,YACpB,aAAa,QACb,kBAAkB,YAClB,SAAS,iBAAiB;AAE9B;","names":[]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@prisma-next/contract",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "description": "Data contract type definitions and JSON schema for Prisma Next",
7
+ "dependencies": {
8
+ "@prisma-next/operations": "0.0.1"
9
+ },
10
+ "devDependencies": {
11
+ "@vitest/coverage-v8": "^2.1.1",
12
+ "tsup": "^8.3.0",
13
+ "typescript": "^5.9.3",
14
+ "vitest": "^2.1.1",
15
+ "@prisma-next/test-utils": "0.0.1"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "schemas"
20
+ ],
21
+ "exports": {
22
+ "./types": {
23
+ "types": "./dist/exports/types.d.ts",
24
+ "import": "./dist/exports/types.js"
25
+ },
26
+ "./pack-manifest-types": {
27
+ "types": "./dist/exports/pack-manifest-types.d.ts",
28
+ "import": "./dist/exports/pack-manifest-types.js"
29
+ },
30
+ "./ir": {
31
+ "types": "./dist/exports/ir.d.ts",
32
+ "import": "./dist/exports/ir.js"
33
+ },
34
+ "./schema-document": "./schemas/data-contract-document-v1.json"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup --config tsup.config.ts",
38
+ "test": "vitest run --passWithNoTests",
39
+ "test:coverage": "vitest run --coverage --passWithNoTests",
40
+ "typecheck": "tsc --project tsconfig.json --noEmit",
41
+ "lint": "biome check . --config-path ../../../biome.json --error-on-warnings",
42
+ "clean": "node ../../scripts/clean.mjs"
43
+ }
44
+ }
@@ -0,0 +1,262 @@
1
+ {
2
+ "$id": "https://prisma.dev/schemas/data-contract-document-v1.json",
3
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
4
+ "title": "Prisma Next Data Contract Document v1",
5
+ "description": "Schema for Prisma Next contract.json files for Document target family (mongo, firestore, etc.)",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": {
10
+ "type": "string",
11
+ "description": "Reference to this JSON schema for IDE validation"
12
+ },
13
+ "schemaVersion": {
14
+ "type": "string",
15
+ "enum": ["1"],
16
+ "description": "Contract schema version"
17
+ },
18
+ "target": {
19
+ "type": "string",
20
+ "description": "Database target identifier (e.g., 'mongo', 'firestore')"
21
+ },
22
+ "targetFamily": {
23
+ "type": "string",
24
+ "enum": ["document"],
25
+ "description": "Target family classification"
26
+ },
27
+ "coreHash": {
28
+ "type": "string",
29
+ "pattern": "^sha256:[a-f0-9]{64}$",
30
+ "description": "SHA-256 hash of the core schema structure (models, fields, relations, storage layout)"
31
+ },
32
+ "profileHash": {
33
+ "type": "string",
34
+ "pattern": "^sha256:[a-f0-9]{64}$",
35
+ "description": "SHA-256 hash of the pinned capability profile (capability keys/variants and adapter pins)"
36
+ },
37
+ "capabilities": {
38
+ "type": "object",
39
+ "description": "Capability flags declared by the contract",
40
+ "additionalProperties": {
41
+ "type": "object",
42
+ "additionalProperties": {
43
+ "type": "boolean"
44
+ }
45
+ }
46
+ },
47
+ "extensions": {
48
+ "type": "object",
49
+ "description": "Extension packs and their configuration",
50
+ "additionalProperties": true
51
+ },
52
+ "meta": {
53
+ "type": "object",
54
+ "description": "Non-semantic metadata (excluded from hashing)",
55
+ "additionalProperties": true
56
+ },
57
+ "sources": {
58
+ "type": "object",
59
+ "description": "Read-only sources (views, etc.) available for querying",
60
+ "additionalProperties": {
61
+ "$ref": "#/$defs/Source"
62
+ }
63
+ },
64
+ "storage": {
65
+ "type": "object",
66
+ "description": "Storage layout definition",
67
+ "additionalProperties": false,
68
+ "properties": {
69
+ "document": {
70
+ "type": "object",
71
+ "description": "Document storage definition",
72
+ "additionalProperties": false,
73
+ "properties": {
74
+ "collections": {
75
+ "type": "object",
76
+ "description": "Collection definitions keyed by collection name",
77
+ "additionalProperties": {
78
+ "$ref": "#/$defs/DocCollection"
79
+ }
80
+ }
81
+ },
82
+ "required": ["collections"]
83
+ }
84
+ },
85
+ "required": ["document"]
86
+ }
87
+ },
88
+ "required": ["schemaVersion", "target", "targetFamily", "coreHash", "storage"],
89
+ "$defs": {
90
+ "DocCollection": {
91
+ "type": "object",
92
+ "description": "Document collection definition with fields and indexes",
93
+ "additionalProperties": false,
94
+ "properties": {
95
+ "name": {
96
+ "type": "string",
97
+ "description": "Logical collection name"
98
+ },
99
+ "id": {
100
+ "type": "object",
101
+ "description": "ID generation strategy",
102
+ "additionalProperties": false,
103
+ "properties": {
104
+ "strategy": {
105
+ "type": "string",
106
+ "enum": ["auto", "client", "uuid", "cuid", "objectId"],
107
+ "description": "ID generation strategy"
108
+ }
109
+ }
110
+ },
111
+ "fields": {
112
+ "type": "object",
113
+ "description": "Field definitions keyed by field name",
114
+ "additionalProperties": {
115
+ "$ref": "#/$defs/FieldType"
116
+ }
117
+ },
118
+ "indexes": {
119
+ "type": "array",
120
+ "description": "Index definitions",
121
+ "items": {
122
+ "$ref": "#/$defs/DocIndex"
123
+ }
124
+ },
125
+ "readOnly": {
126
+ "type": "boolean",
127
+ "description": "Whether mutations are disallowed (mirrors views concept)"
128
+ }
129
+ },
130
+ "required": ["name", "fields"]
131
+ },
132
+ "DocIndex": {
133
+ "type": "object",
134
+ "description": "Document index definition with keys and optional predicate",
135
+ "additionalProperties": false,
136
+ "properties": {
137
+ "name": {
138
+ "type": "string",
139
+ "description": "Index name"
140
+ },
141
+ "keys": {
142
+ "type": "object",
143
+ "description": "Field keys and sort order",
144
+ "additionalProperties": {
145
+ "type": "string",
146
+ "enum": ["asc", "desc"]
147
+ }
148
+ },
149
+ "unique": {
150
+ "type": "boolean",
151
+ "description": "Whether the index enforces uniqueness"
152
+ },
153
+ "where": {
154
+ "$ref": "#/$defs/Expr",
155
+ "description": "Optional predicate expression for partial indexes"
156
+ }
157
+ },
158
+ "required": ["name", "keys"]
159
+ },
160
+ "Expr": {
161
+ "type": "object",
162
+ "description": "Portable predicate expression AST",
163
+ "additionalProperties": false,
164
+ "oneOf": [
165
+ {
166
+ "properties": {
167
+ "kind": {
168
+ "type": "string",
169
+ "enum": ["eq"]
170
+ },
171
+ "path": {
172
+ "type": "array",
173
+ "items": {
174
+ "type": "string"
175
+ },
176
+ "description": "Field path as array of strings"
177
+ },
178
+ "value": {
179
+ "description": "Comparison value"
180
+ }
181
+ },
182
+ "required": ["kind", "path", "value"]
183
+ },
184
+ {
185
+ "properties": {
186
+ "kind": {
187
+ "type": "string",
188
+ "enum": ["exists"]
189
+ },
190
+ "path": {
191
+ "type": "array",
192
+ "items": {
193
+ "type": "string"
194
+ },
195
+ "description": "Field path as array of strings"
196
+ }
197
+ },
198
+ "required": ["kind", "path"]
199
+ }
200
+ ]
201
+ },
202
+ "FieldType": {
203
+ "type": "object",
204
+ "description": "Neutral field type definition for sources and document fields",
205
+ "additionalProperties": false,
206
+ "properties": {
207
+ "type": {
208
+ "type": "string",
209
+ "description": "Field type identifier (e.g., 'int4', 'text', 'bool', 'object', 'array', 'date', 'bytes', 'id')"
210
+ },
211
+ "nullable": {
212
+ "type": "boolean",
213
+ "default": false,
214
+ "description": "Whether the field allows null values"
215
+ },
216
+ "items": {
217
+ "$ref": "#/$defs/FieldType",
218
+ "description": "Item type for array fields"
219
+ },
220
+ "properties": {
221
+ "type": "object",
222
+ "description": "Property definitions for object fields",
223
+ "additionalProperties": {
224
+ "$ref": "#/$defs/FieldType"
225
+ }
226
+ }
227
+ },
228
+ "required": ["type", "nullable"]
229
+ },
230
+ "Source": {
231
+ "type": "object",
232
+ "description": "Read-only source definition for views and other queryable sources",
233
+ "additionalProperties": false,
234
+ "properties": {
235
+ "readOnly": {
236
+ "type": "boolean",
237
+ "description": "Whether mutations (INSERT/UPDATE/DELETE) are disallowed"
238
+ },
239
+ "projection": {
240
+ "type": "object",
241
+ "description": "Field projection mapping column/field name to FieldType",
242
+ "additionalProperties": {
243
+ "$ref": "#/$defs/FieldType"
244
+ }
245
+ },
246
+ "origin": {
247
+ "type": "object",
248
+ "description": "Provenance link to pack-owned block (e.g., view, enum)",
249
+ "additionalProperties": true
250
+ },
251
+ "capabilities": {
252
+ "type": "object",
253
+ "description": "Feature flags relevant to this source",
254
+ "additionalProperties": {
255
+ "type": "boolean"
256
+ }
257
+ }
258
+ },
259
+ "required": ["readOnly", "projection"]
260
+ }
261
+ }
262
+ }