@prisma-next/emitter 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,191 @@
1
+ # @prisma-next/emitter
2
+
3
+ Contract emission engine that transforms authored data models into canonical JSON contracts and TypeScript type definitions.
4
+
5
+ ## Overview
6
+
7
+ The emitter is the core of Prisma Next's contract-first architecture. It takes authored data models (from PSL or TypeScript builders) and produces two deterministic artifacts:
8
+
9
+ 1. **`contract.json`** — Canonical JSON representation of the data contract with embedded `coreHash` and `profileHash`. Callers may add `_generated` metadata field to indicate it's a generated artifact (excluded from canonicalization/hashing).
10
+ 2. **`contract.d.ts`** — TypeScript type definitions used by query builders and tooling (types-only, no runtime code). Includes warning header comments generated by target family hooks to indicate it's a generated file.
11
+
12
+ The emitter is target-family-agnostic and uses a pluggable hook system (`TargetFamilyHook`) to handle family-specific validation and type generation. This keeps the core thin while allowing SQL, Document, and other target families to extend emission behavior.
13
+
14
+ ## Purpose
15
+
16
+ Provide a deterministic, verifiable representation of the application's data contract that downstream subsystems consume for planning, verification, and execution.
17
+
18
+ ## Responsibilities
19
+
20
+ - **Parse**: Accept contract IR (Intermediate Representation) from authoring surfaces
21
+ - **Validate**: Core structure validation plus family-specific type and structure validation via hooks
22
+ - **Canonicalize**: Compute `coreHash` (schema meaning) and `profileHash` (capabilities/pins) from canonical JSON
23
+ - **Emit**: Generate `contract.json` and `contract.d.ts` with family-specific type generation
24
+ - **Manifest-Agnostic**: The emitter is completely manifest-agnostic. It receives pre-assembled `OperationRegistry`, `codecTypeImports`, `operationTypeImports`, and `extensionIds` from the CLI, not extension packs. Manifest parsing and assembly happens in family instances (e.g., `createSqlFamilyInstance` in `@prisma-next/family-sql`).
25
+
26
+ **Note**: The emitter does NOT normalize contracts. Normalization must happen in the contract builder when the contract is created. The emitter assumes contracts are already normalized (all required fields present, including `schemaVersion`, `models`, `relations`, `storage`, `extensions`, `capabilities`, `meta`, and `sources`). All fields can be empty objects/arrays, but they must be present.
27
+
28
+ **Non-goals:**
29
+ - Migration planning or execution
30
+ - Query compilation or execution
31
+ - Runtime capability discovery
32
+ - Policy enforcement
33
+
34
+ ## Architecture
35
+
36
+ ```mermaid
37
+ flowchart TD
38
+ subgraph "Authoring Surfaces"
39
+ PSL[PSL Parser]
40
+ TS[TS Builder]
41
+ end
42
+
43
+ subgraph "Emitter Core"
44
+ IR[Contract IR]
45
+ VAL[Validate Core Structure]
46
+ HASH[Compute Hashes]
47
+ EMIT[Emit JSON + DTS]
48
+ end
49
+
50
+ subgraph "Target Family Hooks"
51
+ SQL[SQL Hook]
52
+ DOC[Document Hook]
53
+ end
54
+
55
+ subgraph "Extension Packs"
56
+ PACK1[Adapter Pack]
57
+ PACK2[Extension Pack]
58
+ end
59
+
60
+ PSL --> IR
61
+ TS --> IR
62
+ IR --> VAL
63
+ VAL --> SQL
64
+ VAL --> DOC
65
+ SQL --> HASH
66
+ DOC --> HASH
67
+ PACK1 --> SQL
68
+ PACK2 --> DOC
69
+ HASH --> EMIT
70
+ EMIT --> JSON[contract.json]
71
+ EMIT --> DTS[contract.d.ts]
72
+ ```
73
+
74
+ ## Components
75
+
76
+ ### Core Emitter (`emitter.ts`)
77
+ - Orchestrates validation, hashing, and type generation
78
+ - Returns contract JSON and TypeScript definitions as strings (no file I/O)
79
+ - Pure transformation function
80
+ - Accepts `targetFamily: TargetFamilyHook` as a required parameter (no global registry)
81
+
82
+ ### Target Family Hook (`target-family.ts`)
83
+ - SPI interface (`TargetFamilyHook`) for extending emission with family-specific logic:
84
+ - `validateTypes`: Validate type IDs against referenced extensions (receives `ValidationContext` with `operationRegistry` and `extensionIds`)
85
+ - `validateStructure`: Family-specific structural validation
86
+ - `generateContractTypes`: Generate `contract.d.ts` content (receives separate `codecTypeImports` and `operationTypeImports` arrays)
87
+ - Authoring surfaces determine which target family SPI to use based on the contract's `targetFamily` field and pass it directly to `emit()`
88
+ - No global registry or auto-registration - dependencies are explicit and passed directly
89
+ - **Manifest-Agnostic**: Hooks receive pre-assembled context (operation registry, type imports, extension IDs), not extension packs. Manifest parsing and assembly happens in the CLI layer.
90
+ - **Note**: `TargetFamilyHook`, `ValidationContext`, and `TypesImportSpec` types are defined in `@prisma-next/contract/types` (shared plane) and re-exported from this package for backward compatibility.
91
+
92
+ ### Hashing (`hashing.ts`)
93
+ - `computeCoreHash`: SHA-256 of schema structure (models, storage, relations)
94
+ - `computeProfileHash`: SHA-256 of capabilities and adapter pins
95
+
96
+ ### Canonicalization (`canonicalization.ts`)
97
+ - `canonicalizeContract`: Normalizes contract IR into stable JSON string for hashing
98
+ - Excludes `_generated` metadata field from canonicalization to ensure determinism
99
+ - Sorts object keys, omits default values, and orders top-level fields consistently
100
+
101
+ **Note**: Extension pack loading and manifest parsing are CLI-only responsibilities. The emitter does not export pack loading functions. Import `loadExtensionPacks` from `@prisma-next/cli` or use the CLI's `pack-loading.ts` module directly. Manifest types (`ExtensionPack`, `ExtensionPackManifest`, `OperationManifest`) are defined in `@prisma-next/control-plane/pack-manifest-types`.
102
+
103
+ **Note**: `TargetFamilyHook`, `ValidationContext`, and `TypesImportSpec` types are defined in `@prisma-next/contract/types` (shared plane) to allow both migration-plane (emitter) and shared-plane (control-plane) packages to import them without violating dependency rules. These types are re-exported from this package for backward compatibility.
104
+
105
+ ## Dependencies
106
+
107
+ - **`arktype`**: Runtime type validation for manifests
108
+
109
+ ## Package Location
110
+
111
+ This package is part of the **framework domain**, **tooling layer**, **migration plane**:
112
+ - **Domain**: framework (target-agnostic)
113
+ - **Layer**: tooling
114
+ - **Plane**: migration
115
+ - **Path**: `packages/framework/tooling/emitter`
116
+
117
+ ## Related Subsystems
118
+
119
+ - **[Contract Emitter & Types](../../../../docs/architecture%20docs/subsystems/2.%20Contract%20Emitter%20&%20Types.md)**: Detailed subsystem specification
120
+ - **[Data Contract](../../../../docs/architecture%20docs/subsystems/1.%20Data%20Contract.md)**: Contract structure and hashing
121
+
122
+ ## Related ADRs
123
+
124
+ - [ADR 004 - Core Hash vs Profile Hash](../../../../docs/architecture%20docs/adrs/ADR%20004%20-%20Core%20Hash%20vs%20Profile%20Hash.md)
125
+ - [ADR 006 - Dual Authoring Modes](../../../../docs/architecture%20docs/adrs/ADR%20006%20-%20Dual%20Authoring%20Modes.md)
126
+ - [ADR 007 - Types Only Emission](../../../../docs/architecture%20docs/adrs/ADR%20007%20-%20Types%20Only%20Emission.md)
127
+ - [ADR 010 - Canonicalization Rules](../../../../docs/architecture%20docs/adrs/ADR%20010%20-%20Canonicalization%20Rules.md)
128
+ - [ADR 097 - Tooling runs on canonical JSON only](../../../../docs/architecture%20docs/adrs/ADR%20097%20-%20Tooling%20runs%20on%20canonical%20JSON%20only.md)
129
+
130
+ ## Usage
131
+
132
+ ```typescript
133
+ import { emit } from '@prisma-next/emitter';
134
+ import type { ContractIR, EmitOptions } from '@prisma-next/emitter';
135
+ import { createOperationRegistry } from '@prisma-next/operations';
136
+
137
+ // Determine target family SPI based on target family
138
+ import { sqlTargetFamilyHook } from '@prisma-next/sql-contract-emitter';
139
+
140
+ // Emit contract
141
+ const ir: ContractIR = {
142
+ schemaVersion: '1',
143
+ targetFamily: 'sql',
144
+ target: 'postgres',
145
+ // ... contract structure
146
+ };
147
+
148
+ // Pass pre-assembled context to emit() (pack loading happens in CLI layer)
149
+ const result = await emit(ir, {
150
+ outputDir: './dist',
151
+ operationRegistry: createOperationRegistry(), // Pre-assembled from packs
152
+ codecTypeImports: [], // Extracted from packs (codec types)
153
+ operationTypeImports: [], // Extracted from packs (operation types)
154
+ extensionIds: ['postgres', 'pg'], // Extracted from packs
155
+ }, sqlTargetFamilyHook);
156
+
157
+ // result.contractJson: string (JSON) - canonical JSON without _generated metadata
158
+ // result.contractDts: string (TypeScript definitions) - includes warning header
159
+ // result.coreHash: string
160
+ // result.profileHash?: string
161
+ ```
162
+
163
+ **Note**: The emitter returns canonical JSON without `_generated` metadata. Callers (e.g., CLI) may add `_generated` metadata to the JSON before writing to disk. The `_generated` field is excluded from canonicalization/hashing to ensure determinism.
164
+
165
+ ## Test Utilities
166
+
167
+ When writing tests that create `ContractIR` objects, use the factory function from test utilities:
168
+
169
+ ```typescript
170
+ import { createContractIR } from './test/utils';
171
+
172
+ const ir = createContractIR({
173
+ storage: {
174
+ tables: {
175
+ user: {
176
+ columns: {
177
+ id: { type: 'pg/int4@1', nullable: false },
178
+ },
179
+ },
180
+ },
181
+ },
182
+ });
183
+ ```
184
+
185
+ This ensures all required fields are present with sensible defaults. See `.cursor/rules/use-contract-ir-factories.mdc` for guidelines.
186
+
187
+ ## Exports
188
+
189
+ - `.`: Main emitter API (`emit`, types)
190
+ - **Note**: Pack loading functions (`loadExtensionPacks`, `loadExtensionPackManifest`) are CLI-only and not exported from the emitter. Import them from `@prisma-next/cli` or use the CLI's `pack-loading.ts` module directly.
191
+
@@ -0,0 +1,2 @@
1
+ export { TargetFamilyHook, TypesImportSpec, ValidationContext } from '@prisma-next/contract/types';
2
+ export { EmitOptions, EmitResult, emit } from '@prisma-next/core-control-plane/emission';
@@ -0,0 +1,6 @@
1
+ // src/exports/index.ts
2
+ import { emit } from "@prisma-next/core-control-plane/emission";
3
+ export {
4
+ emit
5
+ };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exports/index.ts"],"sourcesContent":["// Re-export types from @prisma-next/contract for backward compatibility\nexport type {\n TargetFamilyHook,\n TypesImportSpec,\n ValidationContext,\n} from '@prisma-next/contract/types';\nexport type { EmitOptions, EmitResult } from '@prisma-next/core-control-plane/emission';\n// Re-export emit function and types from core-control-plane\nexport { emit } from '@prisma-next/core-control-plane/emission';\n"],"mappings":";AAQA,SAAS,YAAY;","names":[]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@prisma-next/emitter",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "dependencies": {
10
+ "arktype": "^2.0.0",
11
+ "@prisma-next/contract": "0.0.1",
12
+ "@prisma-next/core-control-plane": "0.0.1"
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^20.0.0",
16
+ "tsup": "^8.3.0",
17
+ "typescript": "^5.9.3",
18
+ "vitest": "^2.1.1",
19
+ "@prisma-next/test-utils": "0.0.1",
20
+ "@prisma-next/operations": "0.0.1"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/exports/index.d.ts",
25
+ "import": "./dist/exports/index.js"
26
+ }
27
+ },
28
+ "scripts": {
29
+ "build": "tsup --config tsup.config.ts",
30
+ "test": "vitest run",
31
+ "test:coverage": "vitest run --coverage",
32
+ "typecheck": "tsc --project tsconfig.json --noEmit",
33
+ "lint": "biome check . --config-path ../../../../biome.json --error-on-warnings",
34
+ "clean": "node ../../../../scripts/clean.mjs"
35
+ }
36
+ }