@prisma-next/cli 0.1.0-pr.71.1 → 0.1.0-pr.72.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 CHANGED
@@ -19,7 +19,7 @@ Provide a command-line interface that:
19
19
  - **TS Contract Loading**: Bundle and load TypeScript contract files with import allowlist enforcement
20
20
  - **CLI Command Interface**: Parse arguments and route to command handlers using commander
21
21
  - **File I/O**: Read TS contracts, write emitted artifacts (`contract.json`, `contract.d.ts`)
22
- - **Extension Pack Loading**: Load adapter and extension pack manifests for emission
22
+ - **Extension Pack Descriptor Assembly**: Collect adapter and extension descriptors for emission
23
23
  - **Help Output Formatting**: Custom styled help output with command trees and formatted descriptions
24
24
  - **Config Management**: Load and validate `prisma-next.config.ts` files using Arktype validation
25
25
 
@@ -62,7 +62,7 @@ export default defineConfig({
62
62
  target: postgres,
63
63
  adapter: postgresAdapter,
64
64
  driver: postgresDriver, // Required even though emit doesn't use it
65
- extensions: [],
65
+ extensionPacks: [],
66
66
  contract: {
67
67
  source: contract,
68
68
  output: 'src/prisma/contract.json',
@@ -102,7 +102,7 @@ prisma-next db verify [--db <url>] [--config <path>] [--json] [-v] [-q] [--times
102
102
  ```
103
103
 
104
104
  Options:
105
- - `--db <url>`: Database connection string (optional, falls back to `config.db.url` or `DATABASE_URL` environment variable)
105
+ - `--db <url>`: Database connection string (optional; defaults to `config.db.url` if set)
106
106
  - `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
107
107
  - `--json`: Output as JSON object
108
108
  - `-q, --quiet`: Quiet mode (errors only)
@@ -130,6 +130,8 @@ prisma-next db verify -v --timestamps
130
130
 
131
131
  The `db verify` command requires a `driver` in the config to connect to the database:
132
132
 
133
+ If your emitted `contract.json` declares `extensionPacks`, your config must include matching descriptors in `extensions` (by `id`). The CLI validates this wiring before running the command.
134
+
133
135
  ```typescript
134
136
  import { defineConfig } from '@prisma-next/cli/config-types';
135
137
  import postgresAdapter from '@prisma-next/adapter-postgres/control';
@@ -143,7 +145,7 @@ export default defineConfig({
143
145
  target: postgres,
144
146
  adapter: postgresAdapter,
145
147
  driver: postgresDriver,
146
- extensions: [],
148
+ extensionPacks: [],
147
149
  contract: {
148
150
  source: contract,
149
151
  output: 'src/prisma/contract.json',
@@ -256,7 +258,7 @@ prisma-next db introspect [--db <url>] [--config <path>] [--json] [-v] [-q] [--t
256
258
  ```
257
259
 
258
260
  Options:
259
- - `--db <url>`: Database connection string (optional, falls back to `config.db.url` or `DATABASE_URL` environment variable)
261
+ - `--db <url>`: Database connection string (optional; defaults to `config.db.url` if set)
260
262
  - `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
261
263
  - `--json`: Output as JSON object
262
264
  - `-q, --quiet`: Quiet mode (errors only)
@@ -296,7 +298,7 @@ export default defineConfig({
296
298
  target: postgres,
297
299
  adapter: postgresAdapter,
298
300
  driver: postgresDriver,
299
- extensions: [],
301
+ extensionPacks: [],
300
302
  db: {
301
303
  url: process.env.DATABASE_URL, // Optional: can also use --db flag
302
304
  },
@@ -373,7 +375,7 @@ sql schema (tables: 2)
373
375
 
374
376
  **Error Codes:**
375
377
  - `PN-CLI-4010`: Missing driver in config — provide a driver descriptor
376
- - `PN-CLI-4011`: Missing database URL — provide `--db` flag or `config.db.url` or `DATABASE_URL` environment variable
378
+ - `PN-CLI-4005`: Missing database URL — provide `--db <url>` or set `db.url` in config
377
379
 
378
380
  **Family Requirements:**
379
381
 
@@ -407,7 +409,7 @@ prisma-next db sign [--db <url>] [--config <path>] [--json] [-v] [-q] [--timesta
407
409
  ```
408
410
 
409
411
  Options:
410
- - `--db <url>`: Database connection string (optional, falls back to `config.db.url` or `DATABASE_URL` environment variable)
412
+ - `--db <url>`: Database connection string (optional; defaults to `config.db.url` if set)
411
413
  - `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
412
414
  - `--json`: Output as JSON object
413
415
  - `-q, --quiet`: Quiet mode (errors only)
@@ -448,7 +450,7 @@ export default defineConfig({
448
450
  target: postgres,
449
451
  adapter: postgresAdapter,
450
452
  driver: postgresDriver,
451
- extensions: [],
453
+ extensionPacks: [],
452
454
  contract: {
453
455
  source: contract,
454
456
  output: 'src/prisma/contract.json',
@@ -570,7 +572,7 @@ For updated markers:
570
572
 
571
573
  **Error Codes:**
572
574
  - `PN-CLI-4010`: Missing driver in config — provide a driver descriptor
573
- - `PN-CLI-4011`: Missing database URL — provide `--db` flag or `config.db.url` or `DATABASE_URL` environment variable
575
+ - `PN-CLI-4005`: Missing database URL — provide `--db <url>` or set `db.url` in config
574
576
  - Exit code 1: Schema verification failed — database schema does not match contract (marker is not written)
575
577
 
576
578
  **Relationship to Other Commands:**
@@ -609,7 +611,7 @@ The SQL family provides this via `@prisma-next/family-sql/control`. The `sign()`
609
611
 
610
612
  ### `prisma-next db init`
611
613
 
612
- Initialize a database schema from the contract. This command generates a migration plan to bring an empty database in sync with the contract and executes it.
614
+ Initialize a database schema from the contract. This command plans and applies **additive-only** operations (create missing tables/columns/constraints/indexes) until the database satisfies the contract, then writes the contract marker.
613
615
 
614
616
  **Command:**
615
617
  ```bash
@@ -617,10 +619,10 @@ prisma-next db init [--db <url>] [--config <path>] [--plan] [--json] [-v] [-q] [
617
619
  ```
618
620
 
619
621
  Options:
620
- - `--db <url>`: Database connection string (optional, falls back to `config.db.url` or `DATABASE_URL` environment variable)
622
+ - `--db <url>`: Database connection string (optional; defaults to `config.db.url` if set)
621
623
  - `--config <path>`: Optional. Path to `prisma-next.config.ts` (defaults to `./prisma-next.config.ts` if present)
622
624
  - `--plan`: Only show the migration plan, do not apply it
623
- - `--json`: Output as JSON object
625
+ - `--json [format]`: Output as JSON (`object` only; `ndjson` is not supported for this command)
624
626
  - `-q, --quiet`: Quiet mode (errors only)
625
627
  - `-v, --verbose`: Verbose output (debug info, timings)
626
628
  - `-vv, --trace`: Trace output (deep internals, stack traces)
@@ -646,6 +648,8 @@ prisma-next db init --json
646
648
 
647
649
  The `db init` command requires a `driver` in the config to connect to the database:
648
650
 
651
+ If your emitted `contract.json` declares `extensionPacks`, your config must include matching descriptors in `extensions` (by `id`). The CLI validates this wiring before running the command.
652
+
649
653
  ```typescript
650
654
  import { defineConfig } from '@prisma-next/cli/config-types';
651
655
  import postgresAdapter from '@prisma-next/adapter-postgres/control';
@@ -659,7 +663,7 @@ export default defineConfig({
659
663
  target: postgres,
660
664
  adapter: postgresAdapter,
661
665
  driver: postgresDriver,
662
- extensions: [],
666
+ extensionPacks: [],
663
667
  contract: {
664
668
  source: contract,
665
669
  output: 'src/prisma/contract.json',
@@ -677,49 +681,54 @@ export default defineConfig({
677
681
  2. **Connect to Database**: Uses `config.driver.create(url)` to create a driver
678
682
  3. **Create Family Instance**: Calls `config.family.create()` with target, adapter, driver, and extensions
679
683
  4. **Introspect Schema**: Calls `familyInstance.introspect()` to get the current database schema IR
680
- 5. **Create Planner/Runner**: Asks the target to construct a `MigrationPlanner` and `MigrationRunner`
681
- 6. **Plan Migration**: Calls `planner.plan()` with the contract IR, schema IR, and additive-only policy
684
+ 5. **Validate wiring**: Ensures the contract is compatible with the CLI config:
685
+ - `contract.targetFamily` matches `config.family.familyId`
686
+ - `contract.target` matches `config.target.targetId`
687
+ - `contract.extensionPacks` (if present) are provided by `config.extensions` (matched by descriptor `id`)
688
+ 6. **Create Planner/Runner**: Uses `config.target.migrations.createPlanner()` and `config.target.migrations.createRunner()`
689
+ 7. **Plan Migration**: Calls `planner.plan()` with the contract IR, schema IR, additive-only policy, and `frameworkComponents` (the active target/adapter/extension descriptors)
682
690
  - On conflict: Returns a structured failure with conflict list
683
691
  - On success: Returns a migration plan with operations
684
- 7. **Apply Migration** (if not `--plan`):
692
+ 8. **Apply Migration** (if not `--plan`):
685
693
  - Calls `runner.execute()` to apply the plan
686
- - Runner executes pre/post checks for each operation
687
694
  - After execution, verifies schema matches contract
688
- - Writes contract marker and ledger entry
695
+ - Writes contract marker (and records a ledger entry via the target runner)
689
696
 
690
697
  **Output Format (TTY - Plan Mode):**
691
698
 
692
699
  ```
693
- prisma-next db init ➜ Initialize database schema from contract
700
+ prisma-next db init ➜ Bootstrap a database to match the current contract
694
701
  config: prisma-next.config.ts
695
702
  contract: src/prisma/contract.json
696
- database: localhost:5432/mydb
703
+ mode: plan (dry run)
704
+
705
+ ✔ Planned 4 operation(s)
706
+
707
+ ├─ Create table user [additive]
708
+ ├─ Add unique constraint user_email_key on user [additive]
709
+ ├─ Create index user_email_idx on user [additive]
710
+ └─ Add foreign key post_userId_fkey on post [additive]
697
711
 
698
- Migration Plan (4 operations)
699
- ├─ Create extension "plpgsql"
700
- ├─ Create table "user"
701
- │ ├─ id: int4 (not null, primary key)
702
- │ └─ email: text (not null)
703
- └─ Create unique index "user_email_key"
712
+ Destination hash: sha256:abc123...
704
713
 
705
- Run without --plan to apply this migration.
714
+ This is a dry run. No changes were applied.
715
+ Run without --plan to apply changes.
706
716
  ```
707
717
 
708
718
  **Output Format (TTY - Apply Mode):**
709
719
 
710
720
  ```
711
- prisma-next db init ➜ Initialize database schema from contract
721
+ prisma-next db init ➜ Bootstrap a database to match the current contract
712
722
  config: prisma-next.config.ts
713
723
  contract: src/prisma/contract.json
714
- database: localhost:5432/mydb
715
724
 
716
- Applied 4 migration operations
717
- ├─ Created extension "plpgsql"
718
- ├─ Created table "user"
719
- └─ Created unique index "user_email_key"
720
- Contract marker written
721
- coreHash: sha256:abc123...
722
- Ledger entry recorded
725
+ Applying migration plan and verifying schema...
726
+ Create table user...
727
+ Add unique constraint user_email_key on user...
728
+ Create index user_email_idx on user...
729
+ Add foreign key post_userId_fkey on post...
730
+ Applied 4 operation(s)
731
+ Marker written: sha256:abc123...
723
732
  ```
724
733
 
725
734
  **Output Format (JSON):**
@@ -729,37 +738,45 @@ prisma-next db init ➜ Initialize database schema from contract
729
738
  "ok": true,
730
739
  "mode": "apply",
731
740
  "plan": {
732
- "operations": [...],
733
- "conflicts": []
741
+ "targetId": "postgres",
742
+ "destination": {
743
+ "coreHash": "sha256:abc123..."
744
+ },
745
+ "operations": [
746
+ {
747
+ "id": "table.user",
748
+ "label": "Create table user",
749
+ "operationClass": "additive"
750
+ }
751
+ ]
734
752
  },
735
753
  "execution": {
736
- "operations": [...],
737
- "marker": {
738
- "coreHash": "sha256:abc123...",
739
- "profileHash": "sha256:def456..."
740
- },
741
- "ledger": {
742
- "id": "ledger-entry-uuid",
743
- "createdAt": "2025-01-01T00:00:00Z"
744
- }
754
+ "operationsPlanned": 4,
755
+ "operationsExecuted": 4
756
+ },
757
+ "marker": {
758
+ "coreHash": "sha256:abc123..."
745
759
  }
746
760
  }
747
761
  ```
748
762
 
749
763
  **Error Codes:**
750
- - `PN-CLI-4010`: Missing driver in config — provide a driver descriptor
751
- - `PN-CLI-4011`: Missing database URL — provide `--db` flag or `config.db.url` or `DATABASE_URL` environment variable
752
- - `PN-CLI-4011`: Migration planning failed due to conflicts (exit code 1)
753
- - `PN-CLI-4012`: Target does not support migrations
764
+ - `PN-CLI-4004`: Contract file not found
765
+ - `PN-CLI-4005`: Missing database URL
766
+ - `PN-CLI-4008`: Unsupported JSON format (`--json ndjson` is rejected for `db init`)
767
+ - `PN-CLI-4010`: Missing driver in config
768
+ - `PN-CLI-4020`: Migration planning failed (conflicts)
769
+ - `PN-CLI-4021`: Target does not support migrations
770
+ - `PN-RTM-3000`: Runtime error (includes marker mismatch failures)
771
+
772
+ **Behavior Notes:**
754
773
 
755
- **Current Limitations (v1):**
756
- - Only supports empty databases (no existing tables)
757
- - Non-empty databases result in a planning failure with conflict details
758
- - Future `db update` command will support additive changes to existing schemas
774
+ - If the database already has a marker that matches the destination contract, `db init` succeeds as a noop (0 operations planned/executed).
775
+ - If the database has a marker that does **not** match the destination contract, `db init` fails (including in `--plan` mode). Use `db init` for bootstrapping; use your migration workflow to reconcile existing databases.
759
776
 
760
777
  **Config File (`prisma-next.config.ts`):**
761
778
 
762
- The CLI uses a config file to specify the target family, target, adapter, extensions, and contract.
779
+ The CLI uses a config file to specify the target family, target, adapter, extensionPacks, and contract.
763
780
 
764
781
  **Config Discovery:**
765
782
  - `--config <path>`: Explicit path (relative or absolute)
@@ -779,7 +796,7 @@ export default defineConfig({
779
796
  family: sql,
780
797
  target: postgres,
781
798
  adapter: postgresAdapter,
782
- extensions: [],
799
+ extensionPacks: [],
783
800
  contract: {
784
801
  source: contract, // Can be a value or a function: () => import('./contract').then(m => m.contract)
785
802
  output: 'src/prisma/contract.json', // Optional: defaults to 'src/prisma/contract.json'
@@ -909,8 +926,63 @@ See `.cursor/rules/config-validation-and-normalization.mdc` for detailed pattern
909
926
  - `schemaVerify(options)` - Verifies database schema against contract
910
927
  - `introspect(options)` - Introspects database schema
911
928
 
912
- ### Pack Manifest Types (IR)
913
- - Families define their manifest IR and related types under their own tooling packages. CLI treats manifests as opaque data.
929
+ ### Descriptor Declarative Fields
930
+ - Families expose component descriptors (target, adapter, driver, extensions) as plain TypeScript objects. Each descriptor includes **declarative fields**: metadata that describes what the component *provides* (independent of its runtime implementation), and that the CLI can safely copy into emitted artifacts.
931
+ - Common declarative keys:
932
+ - **`version`**: Component version included in emitted metadata (useful for debugging and reproducibility).
933
+ - **`capabilities`**: Feature flags the component contributes (e.g., adapter/runtime lowering requirements). Typically namespaced by target (e.g., `{ postgres: { returning: true } }`) so contracts can be validated against the active target.
934
+ - **`types`**: Type import specs and type IDs contributed by the component. Common examples:
935
+ - `types.codecTypes.import`: Where to import codec type mappings for `contract.d.ts`.
936
+ - `types.operationTypes.import`: Where to import operation type mappings for `contract.d.ts` (extensions).
937
+ - `types.storage`: Storage type bindings (`typeId`, `nativeType`, etc.) used in authoring/emission.
938
+ - **`operations`**: Operation signatures the component contributes (extensions), used for type generation and (optionally) validation/lowering.
939
+ - **Component-specific metadata**:
940
+ - Extensions may also include control-plane-only metadata like `databaseDependencies` (used by verify/schema-verify and not required at runtime).
941
+
942
+ Unlike the older **manifest-based IR** approach (separate JSON manifests + a parsing/validation step to build an IR), descriptors are imported directly from packages (e.g., `@prisma-next/*/control`). This removes a file-format boundary and keeps the data and its types co-located.
943
+ - Benefits: fewer moving parts (no JSON parsing), easier refactors (TypeScript catches drift), and clearer ownership (the package exports the canonical descriptor object).
944
+ - Trade-offs: descriptors must be available as build-time imports (less dynamic discovery vs scanning arbitrary manifest files).
945
+
946
+ **Illustrative example (descriptor object):**
947
+
948
+ ```typescript
949
+ import type { SqlControlExtensionDescriptor } from '@prisma-next/family-sql/control';
950
+
951
+ const exampleExtension: SqlControlExtensionDescriptor<'postgres'> = {
952
+ kind: 'extension',
953
+ id: 'example',
954
+ version: '1.0.0',
955
+ familyId: 'sql',
956
+ targetId: 'postgres',
957
+ capabilities: { postgres: { 'example/feature': true } },
958
+ types: {
959
+ operationTypes: {
960
+ import: {
961
+ package: '@prisma-next/extension-example/operation-types',
962
+ named: 'OperationTypes',
963
+ alias: 'ExampleOperationTypes',
964
+ },
965
+ },
966
+ },
967
+ operations: [],
968
+ create: () => ({ familyId: 'sql', targetId: 'postgres' }),
969
+ };
970
+
971
+ export default exampleExtension;
972
+ ```
973
+
974
+ **How CLI consumers import/use it:**
975
+ - Config imports descriptors directly and passes them to `defineConfig()` (see “Config File Requirements” under `prisma-next contract emit` above; also see “Entrypoints” below for the `@prisma-next/*/control` subpaths):
976
+
977
+ ```typescript
978
+ import { defineConfig } from '@prisma-next/cli/config-types';
979
+ import exampleExtension from '@prisma-next/extension-example/control';
980
+
981
+ export default defineConfig({
982
+ // family/target/adapter/driver omitted for brevity
983
+ extensionPacks: [exampleExtension],
984
+ });
985
+ ```
914
986
 
915
987
  ## Dependencies
916
988
 
@@ -982,7 +1054,6 @@ The CLI package exports several subpaths for different use cases:
982
1054
 
983
1055
  - **`@prisma-next/cli`** (main export): Exports `loadContractFromTs` and `createContractEmitCommand`
984
1056
  - **`@prisma-next/cli/config-types`**: Exports `defineConfig` and config types
985
- - **`@prisma-next/cli/pack-loading`**: Exports `loadExtensionPacks` and `loadExtensionPackManifest`
986
1057
  - **`@prisma-next/cli/commands/db-init`**: Exports `createDbInitCommand`
987
1058
  - **`@prisma-next/cli/commands/db-introspect`**: Exports `createDbIntrospectCommand`
988
1059
  - **`@prisma-next/cli/commands/db-schema-verify`**: Exports `createDbSchemaVerifyCommand`
@@ -991,7 +1062,7 @@ The CLI package exports several subpaths for different use cases:
991
1062
  - **`@prisma-next/cli/commands/contract-emit`**: Exports `createContractEmitCommand`
992
1063
  - **`@prisma-next/cli/config-loader`**: Exports `loadConfig` function
993
1064
 
994
- **Important**: `loadContractFromTs` is exported from the main package (`@prisma-next/cli`), not from `@prisma-next/cli/pack-loading`. See `.cursor/rules/cli-package-exports.mdc` for import patterns.
1065
+ **Important**: `loadContractFromTs` is exported from the main package (`@prisma-next/cli`). See `.cursor/rules/cli-package-exports.mdc` for import patterns.
995
1066
 
996
1067
  ## Package Location
997
1068
 
@@ -9,7 +9,7 @@ import {
9
9
  performAction,
10
10
  setCommandDescriptions,
11
11
  withSpinner
12
- } from "./chunk-I3G6AX7S.js";
12
+ } from "./chunk-BZMBKEEQ.js";
13
13
  import {
14
14
  loadConfig
15
15
  } from "./chunk-HWYQOCAJ.js";
@@ -74,7 +74,7 @@ function createContractEmitCommand() {
74
74
  target: config.target,
75
75
  adapter: config.adapter,
76
76
  driver: config.driver,
77
- extensions: config.extensions ?? []
77
+ extensionPacks: config.extensionPacks ?? []
78
78
  });
79
79
  let contractRaw;
80
80
  if (typeof contractConfig.source === "function") {
@@ -131,4 +131,4 @@ function createContractEmitCommand() {
131
131
  export {
132
132
  createContractEmitCommand
133
133
  };
134
- //# sourceMappingURL=chunk-3XVN5I7J.js.map
134
+ //# sourceMappingURL=chunk-464LNZCE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/contract-emit.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, relative, resolve } from 'node:path';\nimport { errorContractConfigMissing } from '@prisma-next/core-control-plane/errors';\nimport { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { performAction } from '../utils/action';\nimport { setCommandDescriptions } from '../utils/command-helpers';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport {\n formatCommandHelp,\n formatEmitJson,\n formatEmitOutput,\n formatStyledHeader,\n formatSuccessMessage,\n} from '../utils/output';\nimport { handleResult } from '../utils/result-handler';\nimport { withSpinner } from '../utils/spinner';\n\ninterface ContractEmitOptions {\n readonly config?: string;\n readonly json?: string | boolean;\n readonly quiet?: boolean;\n readonly q?: boolean;\n readonly verbose?: boolean;\n readonly v?: boolean;\n readonly vv?: boolean;\n readonly trace?: boolean;\n readonly timestamps?: boolean;\n readonly color?: boolean;\n readonly 'no-color'?: boolean;\n}\n\nexport function createContractEmitCommand(): Command {\n const command = new Command('emit');\n setCommandDescriptions(\n command,\n 'Write your contract to JSON and sign it',\n 'Reads your contract source (TypeScript or Prisma schema) and emits contract.json and\\n' +\n 'contract.d.ts. The contract.json contains the canonical contract structure, and\\n' +\n 'contract.d.ts provides TypeScript types for type-safe query building.',\n );\n command\n .configureHelp({\n formatHelp: (cmd) => {\n const flags = parseGlobalFlags({});\n return formatCommandHelp({ command: cmd, flags });\n },\n })\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--json [format]', 'Output as JSON (object or ndjson)', false)\n .option('-q, --quiet', 'Quiet mode: errors only')\n .option('-v, --verbose', 'Verbose output: debug info, timings')\n .option('-vv, --trace', 'Trace output: deep internals, stack traces')\n .option('--timestamps', 'Add timestamps to output')\n .option('--color', 'Force color output')\n .option('--no-color', 'Disable color output')\n .action(async (options: ContractEmitOptions) => {\n const flags = parseGlobalFlags(options);\n\n const result = await performAction(async () => {\n // Load config\n const config = await loadConfig(options.config);\n\n // Resolve contract from config\n if (!config.contract) {\n throw errorContractConfigMissing({\n why: 'Config.contract is required for emit. Define it in your config: contract: { source: ..., output: ..., types: ... }',\n });\n }\n\n // Contract config is already normalized by defineConfig() with defaults applied\n const contractConfig = config.contract;\n\n // Resolve artifact paths from config (already normalized by defineConfig() with defaults)\n if (!contractConfig.output || !contractConfig.types) {\n throw errorContractConfigMissing({\n why: 'Contract config must have output and types paths. This should not happen if defineConfig() was used.',\n });\n }\n const outputJsonPath = resolve(contractConfig.output);\n const outputDtsPath = resolve(contractConfig.types);\n\n // Output header (only for human-readable output)\n if (flags.json !== 'object' && !flags.quiet) {\n // Normalize config path for display (match contract path format - no ./ prefix)\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n // Convert absolute paths to relative paths for display\n const contractPath = relative(process.cwd(), outputJsonPath);\n const typesPath = relative(process.cwd(), outputDtsPath);\n const header = formatStyledHeader({\n command: 'contract emit',\n description: 'Write your contract to JSON and sign it',\n url: 'https://pris.ly/contract-emit',\n details: [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n { label: 'types', value: typesPath },\n ],\n flags,\n });\n console.log(header);\n }\n\n // Create family instance (assembles operation registry, type imports, extension IDs)\n // Note: emit command doesn't need driver, but ControlFamilyDescriptor.create() requires it\n // We'll need to provide a minimal driver descriptor or make driver optional for emit\n // For now, we'll require driver to be present in config even for emit\n if (!config.driver) {\n throw errorContractConfigMissing({\n why: 'Config.driver is required. Even though emit does not use the driver, it is required by ControlFamilyDescriptor.create()',\n });\n }\n const familyInstance = config.family.create({\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensions: config.extensions ?? [],\n });\n\n // Resolve contract source from config (user's config handles loading)\n let contractRaw: unknown;\n if (typeof contractConfig.source === 'function') {\n contractRaw = await contractConfig.source();\n } else {\n contractRaw = contractConfig.source;\n }\n\n // Call emitContract on family instance (handles stripping mappings and validation internally)\n const emitResult = await withSpinner(\n () => familyInstance.emitContract({ contractIR: contractRaw }),\n {\n message: 'Emitting contract...',\n flags,\n },\n );\n\n // Create directories if needed\n mkdirSync(dirname(outputJsonPath), { recursive: true });\n mkdirSync(dirname(outputDtsPath), { recursive: true });\n\n // Write the results to files\n writeFileSync(outputJsonPath, emitResult.contractJson, 'utf-8');\n writeFileSync(outputDtsPath, emitResult.contractDts, 'utf-8');\n\n // Add blank line after all async operations if spinners were shown\n if (!flags.quiet && flags.json !== 'object' && process.stdout.isTTY) {\n console.log('');\n }\n\n // Return result with file paths for output formatting\n return {\n coreHash: emitResult.coreHash,\n profileHash: emitResult.profileHash,\n outDir: dirname(outputJsonPath),\n files: {\n json: outputJsonPath,\n dts: outputDtsPath,\n },\n timings: {\n total: 0, // Timing is handled by emitContract internally if needed\n },\n };\n });\n\n // Handle result - formats output and returns exit code\n const exitCode = handleResult(result, flags, (emitResult) => {\n // Output based on flags\n if (flags.json === 'object') {\n // JSON output to stdout\n console.log(formatEmitJson(emitResult));\n } else {\n // Human-readable output to stdout\n const output = formatEmitOutput(emitResult, flags);\n if (output) {\n console.log(output);\n }\n // Output success message\n if (!flags.quiet) {\n console.log(formatSuccessMessage(flags));\n }\n }\n });\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/commands/contract-emit.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, relative, resolve } from 'node:path';\nimport { errorContractConfigMissing } from '@prisma-next/core-control-plane/errors';\nimport { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { performAction } from '../utils/action';\nimport { setCommandDescriptions } from '../utils/command-helpers';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport {\n formatCommandHelp,\n formatEmitJson,\n formatEmitOutput,\n formatStyledHeader,\n formatSuccessMessage,\n} from '../utils/output';\nimport { handleResult } from '../utils/result-handler';\nimport { withSpinner } from '../utils/spinner';\n\ninterface ContractEmitOptions {\n readonly config?: string;\n readonly json?: string | boolean;\n readonly quiet?: boolean;\n readonly q?: boolean;\n readonly verbose?: boolean;\n readonly v?: boolean;\n readonly vv?: boolean;\n readonly trace?: boolean;\n readonly timestamps?: boolean;\n readonly color?: boolean;\n readonly 'no-color'?: boolean;\n}\n\nexport function createContractEmitCommand(): Command {\n const command = new Command('emit');\n setCommandDescriptions(\n command,\n 'Write your contract to JSON and sign it',\n 'Reads your contract source (TypeScript or Prisma schema) and emits contract.json and\\n' +\n 'contract.d.ts. The contract.json contains the canonical contract structure, and\\n' +\n 'contract.d.ts provides TypeScript types for type-safe query building.',\n );\n command\n .configureHelp({\n formatHelp: (cmd) => {\n const flags = parseGlobalFlags({});\n return formatCommandHelp({ command: cmd, flags });\n },\n })\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--json [format]', 'Output as JSON (object or ndjson)', false)\n .option('-q, --quiet', 'Quiet mode: errors only')\n .option('-v, --verbose', 'Verbose output: debug info, timings')\n .option('-vv, --trace', 'Trace output: deep internals, stack traces')\n .option('--timestamps', 'Add timestamps to output')\n .option('--color', 'Force color output')\n .option('--no-color', 'Disable color output')\n .action(async (options: ContractEmitOptions) => {\n const flags = parseGlobalFlags(options);\n\n const result = await performAction(async () => {\n // Load config\n const config = await loadConfig(options.config);\n\n // Resolve contract from config\n if (!config.contract) {\n throw errorContractConfigMissing({\n why: 'Config.contract is required for emit. Define it in your config: contract: { source: ..., output: ..., types: ... }',\n });\n }\n\n // Contract config is already normalized by defineConfig() with defaults applied\n const contractConfig = config.contract;\n\n // Resolve artifact paths from config (already normalized by defineConfig() with defaults)\n if (!contractConfig.output || !contractConfig.types) {\n throw errorContractConfigMissing({\n why: 'Contract config must have output and types paths. This should not happen if defineConfig() was used.',\n });\n }\n const outputJsonPath = resolve(contractConfig.output);\n const outputDtsPath = resolve(contractConfig.types);\n\n // Output header (only for human-readable output)\n if (flags.json !== 'object' && !flags.quiet) {\n // Normalize config path for display (match contract path format - no ./ prefix)\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n // Convert absolute paths to relative paths for display\n const contractPath = relative(process.cwd(), outputJsonPath);\n const typesPath = relative(process.cwd(), outputDtsPath);\n const header = formatStyledHeader({\n command: 'contract emit',\n description: 'Write your contract to JSON and sign it',\n url: 'https://pris.ly/contract-emit',\n details: [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n { label: 'types', value: typesPath },\n ],\n flags,\n });\n console.log(header);\n }\n\n // Create family instance (assembles operation registry, type imports, extension IDs)\n // Note: emit command doesn't need driver, but ControlFamilyDescriptor.create() requires it\n // We'll need to provide a minimal driver descriptor or make driver optional for emit\n // For now, we'll require driver to be present in config even for emit\n if (!config.driver) {\n throw errorContractConfigMissing({\n why: 'Config.driver is required. Even though emit does not use the driver, it is required by ControlFamilyDescriptor.create()',\n });\n }\n const familyInstance = config.family.create({\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Resolve contract source from config (user's config handles loading)\n let contractRaw: unknown;\n if (typeof contractConfig.source === 'function') {\n contractRaw = await contractConfig.source();\n } else {\n contractRaw = contractConfig.source;\n }\n\n // Call emitContract on family instance (handles stripping mappings and validation internally)\n const emitResult = await withSpinner(\n () => familyInstance.emitContract({ contractIR: contractRaw }),\n {\n message: 'Emitting contract...',\n flags,\n },\n );\n\n // Create directories if needed\n mkdirSync(dirname(outputJsonPath), { recursive: true });\n mkdirSync(dirname(outputDtsPath), { recursive: true });\n\n // Write the results to files\n writeFileSync(outputJsonPath, emitResult.contractJson, 'utf-8');\n writeFileSync(outputDtsPath, emitResult.contractDts, 'utf-8');\n\n // Add blank line after all async operations if spinners were shown\n if (!flags.quiet && flags.json !== 'object' && process.stdout.isTTY) {\n console.log('');\n }\n\n // Return result with file paths for output formatting\n return {\n coreHash: emitResult.coreHash,\n profileHash: emitResult.profileHash,\n outDir: dirname(outputJsonPath),\n files: {\n json: outputJsonPath,\n dts: outputDtsPath,\n },\n timings: {\n total: 0, // Timing is handled by emitContract internally if needed\n },\n };\n });\n\n // Handle result - formats output and returns exit code\n const exitCode = handleResult(result, flags, (emitResult) => {\n // Output based on flags\n if (flags.json === 'object') {\n // JSON output to stdout\n console.log(formatEmitJson(emitResult));\n } else {\n // Human-readable output to stdout\n const output = formatEmitOutput(emitResult, flags);\n if (output) {\n console.log(output);\n }\n // Output success message\n if (!flags.quiet) {\n console.log(formatSuccessMessage(flags));\n }\n }\n });\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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","names":[]}
@@ -4,6 +4,7 @@ import {
4
4
  errorConfigFileNotFound,
5
5
  errorConfigValidation,
6
6
  errorContractConfigMissing,
7
+ errorContractMissingExtensionPacks,
7
8
  errorContractValidationFailed,
8
9
  errorDatabaseUrlRequired,
9
10
  errorDriverRequired,
@@ -961,6 +962,7 @@ async function withSpinner(operation, options) {
961
962
 
962
963
  export {
963
964
  errorConfigValidation,
965
+ errorContractMissingExtensionPacks,
964
966
  errorContractValidationFailed,
965
967
  errorDatabaseUrlRequired,
966
968
  errorDriverRequired,
@@ -992,4 +994,4 @@ export {
992
994
  handleResult,
993
995
  withSpinner
994
996
  };
995
- //# sourceMappingURL=chunk-I3G6AX7S.js.map
997
+ //# sourceMappingURL=chunk-BZMBKEEQ.js.map