@prisma-next/cli 0.3.0-dev.7 → 0.3.0-dev.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/{chunk-ZKYEJROM.js → chunk-CVNWLFXO.js} +6 -9
  2. package/dist/chunk-CVNWLFXO.js.map +1 -0
  3. package/dist/{chunk-464LNZCE.js → chunk-QUPBU4KV.js} +5 -8
  4. package/dist/chunk-QUPBU4KV.js.map +1 -0
  5. package/dist/cli.js +34 -60
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/contract-emit.d.ts.map +1 -1
  8. package/dist/commands/contract-emit.js +1 -1
  9. package/dist/commands/db-init.d.ts.map +1 -1
  10. package/dist/commands/db-init.js +6 -10
  11. package/dist/commands/db-init.js.map +1 -1
  12. package/dist/commands/db-introspect.d.ts.map +1 -1
  13. package/dist/commands/db-introspect.js +6 -10
  14. package/dist/commands/db-introspect.js.map +1 -1
  15. package/dist/commands/db-schema-verify.d.ts.map +1 -1
  16. package/dist/commands/db-schema-verify.js +6 -10
  17. package/dist/commands/db-schema-verify.js.map +1 -1
  18. package/dist/commands/db-sign.d.ts.map +1 -1
  19. package/dist/commands/db-sign.js +6 -10
  20. package/dist/commands/db-sign.js.map +1 -1
  21. package/dist/commands/db-verify.d.ts.map +1 -1
  22. package/dist/commands/db-verify.js +6 -10
  23. package/dist/commands/db-verify.js.map +1 -1
  24. package/dist/exports/index.js +1 -1
  25. package/dist/utils/framework-components.d.ts +7 -18
  26. package/dist/utils/framework-components.d.ts.map +1 -1
  27. package/package.json +10 -10
  28. package/src/commands/contract-emit.ts +4 -11
  29. package/src/commands/db-init.ts +5 -9
  30. package/src/commands/db-introspect.ts +5 -9
  31. package/src/commands/db-schema-verify.ts +5 -9
  32. package/src/commands/db-sign.ts +5 -9
  33. package/src/commands/db-verify.ts +5 -9
  34. package/src/utils/framework-components.ts +11 -30
  35. package/dist/chunk-464LNZCE.js.map +0 -1
  36. package/dist/chunk-ZKYEJROM.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/db-verify.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport type { ContractIR } from '@prisma-next/contract/ir';\nimport {\n errorDatabaseUrlRequired,\n errorDriverRequired,\n errorFileNotFound,\n errorHashMismatch,\n errorMarkerMissing,\n errorRuntime,\n errorTargetMismatch,\n errorUnexpected,\n} from '@prisma-next/core-control-plane/errors';\nimport type { VerifyDatabaseResult } from '@prisma-next/core-control-plane/types';\nimport { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { performAction } from '../utils/action';\nimport { setCommandDescriptions } from '../utils/command-helpers';\nimport { assertContractRequirementsSatisfied } from '../utils/framework-components';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport {\n formatCommandHelp,\n formatStyledHeader,\n formatVerifyJson,\n formatVerifyOutput,\n} from '../utils/output';\nimport { handleResult } from '../utils/result-handler';\nimport { withSpinner } from '../utils/spinner';\n\ninterface DbVerifyOptions {\n readonly db?: string;\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 createDbVerifyCommand(): Command {\n const command = new Command('verify');\n setCommandDescriptions(\n command,\n 'Check whether the database has been signed with your contract',\n 'Verifies that your database schema matches the emitted contract. Checks table structures,\\n' +\n 'column types, constraints, and codec coverage. Reports any mismatches or missing codecs.',\n );\n command\n .configureHelp({\n formatHelp: (cmd) => {\n const flags = parseGlobalFlags({});\n return formatCommandHelp({ command: cmd, flags });\n },\n })\n .option('--db <url>', 'Database connection string')\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: DbVerifyOptions) => {\n const flags = parseGlobalFlags(options);\n\n const result = await performAction(async () => {\n // Load config (file I/O)\n const config = await loadConfig(options.config);\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 const contractPathAbsolute = config.contract?.output\n ? resolve(config.contract.output)\n : resolve('src/prisma/contract.json');\n // Convert to relative path for display\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n // Output header (only for human-readable output)\n if (flags.json !== 'object' && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n ];\n if (options.db) {\n details.push({ label: 'database', value: options.db });\n }\n const header = formatStyledHeader({\n command: 'db verify',\n description: 'Check whether the database has been signed with your contract',\n url: 'https://pris.ly/db-verify',\n details,\n flags,\n });\n console.log(header);\n }\n\n // Load contract file (file I/O)\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n throw errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n });\n }\n throw errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n const contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;\n\n // Resolve database URL\n const dbUrl = options.db ?? config.db?.url;\n if (!dbUrl) {\n throw errorDatabaseUrlRequired();\n }\n\n // Check for driver\n if (!config.driver) {\n throw errorDriverRequired();\n }\n\n // Store driver descriptor after null check\n const driverDescriptor = config.driver;\n\n // Create driver\n const driver = await withSpinner(() => driverDescriptor.create(dbUrl), {\n message: 'Connecting to database...',\n flags,\n });\n\n try {\n // Create family instance\n const familyInstance = config.family.create({\n target: config.target,\n adapter: config.adapter,\n driver: driverDescriptor,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Validate contract using instance validator\n const contractIR = familyInstance.validateContractIR(contractJson) as ContractIR;\n assertContractRequirementsSatisfied({\n contract: contractIR,\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n extensionPacks: config.extensionPacks,\n });\n\n // Call family instance verify method\n let verifyResult: VerifyDatabaseResult;\n try {\n verifyResult = await withSpinner(\n () =>\n familyInstance.verify({\n driver,\n contractIR,\n expectedTargetId: config.target.targetId,\n contractPath: contractPathAbsolute,\n configPath,\n }),\n {\n message: 'Verifying database schema...',\n flags,\n },\n );\n } catch (error) {\n // Wrap errors from verify() in structured error\n throw errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to verify database: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n\n // If verification failed, throw structured error\n if (!verifyResult.ok && verifyResult.code) {\n if (verifyResult.code === 'PN-RTM-3001') {\n throw errorMarkerMissing();\n }\n if (verifyResult.code === 'PN-RTM-3002') {\n throw errorHashMismatch({\n expected: verifyResult.contract.coreHash,\n ...(verifyResult.marker?.coreHash ? { actual: verifyResult.marker.coreHash } : {}),\n });\n }\n if (verifyResult.code === 'PN-RTM-3003') {\n throw errorTargetMismatch(\n verifyResult.target.expected,\n verifyResult.target.actual ?? 'unknown',\n );\n }\n throw errorRuntime(verifyResult.summary);\n }\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 verifyResult;\n } finally {\n // Ensure driver connection is closed\n await driver.close();\n }\n });\n\n // Handle result - formats output and returns exit code\n const exitCode = handleResult(result, flags, (verifyResult) => {\n // Output based on flags\n if (flags.json === 'object') {\n // JSON output to stdout\n console.log(formatVerifyJson(verifyResult));\n } else {\n // Human-readable output to stdout\n const output = formatVerifyOutput(verifyResult, flags);\n if (output) {\n console.log(output);\n }\n }\n });\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,UAAU,eAAe;AAElC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,eAAe;AA8BjB,SAAS,wBAAiC;AAC/C,QAAM,UAAU,IAAI,QAAQ,QAAQ;AACpC;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EAEF;AACA,UACG,cAAc;AAAA,IACb,YAAY,CAAC,QAAQ;AACnB,YAAM,QAAQ,iBAAiB,CAAC,CAAC;AACjC,aAAO,kBAAkB,EAAE,SAAS,KAAK,MAAM,CAAC;AAAA,IAClD;AAAA,EACF,CAAC,EACA,OAAO,cAAc,4BAA4B,EACjD,OAAO,mBAAmB,+BAA+B,EACzD,OAAO,mBAAmB,qCAAqC,KAAK,EACpE,OAAO,eAAe,yBAAyB,EAC/C,OAAO,iBAAiB,qCAAqC,EAC7D,OAAO,gBAAgB,4CAA4C,EACnE,OAAO,gBAAgB,0BAA0B,EACjD,OAAO,WAAW,oBAAoB,EACtC,OAAO,cAAc,sBAAsB,EAC3C,OAAO,OAAO,YAA6B;AAC1C,UAAM,QAAQ,iBAAiB,OAAO;AAEtC,UAAM,SAAS,MAAM,cAAc,YAAY;AAE7C,YAAM,SAAS,MAAM,WAAW,QAAQ,MAAM;AAE9C,YAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,IAAI,GAAG,QAAQ,QAAQ,MAAM,CAAC,IAC/C;AACJ,YAAM,uBAAuB,OAAO,UAAU,SAC1C,QAAQ,OAAO,SAAS,MAAM,IAC9B,QAAQ,0BAA0B;AAEtC,YAAM,eAAe,SAAS,QAAQ,IAAI,GAAG,oBAAoB;AAGjE,UAAI,MAAM,SAAS,YAAY,CAAC,MAAM,OAAO;AAC3C,cAAM,UAAmD;AAAA,UACvD,EAAE,OAAO,UAAU,OAAO,WAAW;AAAA,UACrC,EAAE,OAAO,YAAY,OAAO,aAAa;AAAA,QAC3C;AACA,YAAI,QAAQ,IAAI;AACd,kBAAQ,KAAK,EAAE,OAAO,YAAY,OAAO,QAAQ,GAAG,CAAC;AAAA,QACvD;AACA,cAAM,SAAS,mBAAmB;AAAA,UAChC,SAAS;AAAA,UACT,aAAa;AAAA,UACb,KAAK;AAAA,UACL;AAAA,UACA;AAAA,QACF,CAAC;AACD,gBAAQ,IAAI,MAAM;AAAA,MACpB;AAGA,UAAI;AACJ,UAAI;AACF,8BAAsB,MAAM,SAAS,sBAAsB,OAAO;AAAA,MACpE,SAAS,OAAO;AACd,YAAI,iBAAiB,SAAU,MAA4B,SAAS,UAAU;AAC5E,gBAAM,kBAAkB,sBAAsB;AAAA,YAC5C,KAAK,8BAA8B,oBAAoB;AAAA,UACzD,CAAC;AAAA,QACH;AACA,cAAM,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;AAAA,UAC5E,KAAK,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QAC9F,CAAC;AAAA,MACH;AACA,YAAM,eAAe,KAAK,MAAM,mBAAmB;AAGnD,YAAM,QAAQ,QAAQ,MAAM,OAAO,IAAI;AACvC,UAAI,CAAC,OAAO;AACV,cAAM,yBAAyB;AAAA,MACjC;AAGA,UAAI,CAAC,OAAO,QAAQ;AAClB,cAAM,oBAAoB;AAAA,MAC5B;AAGA,YAAM,mBAAmB,OAAO;AAGhC,YAAM,SAAS,MAAM,YAAY,MAAM,iBAAiB,OAAO,KAAK,GAAG;AAAA,QACrE,SAAS;AAAA,QACT;AAAA,MACF,CAAC;AAED,UAAI;AAEF,cAAM,iBAAiB,OAAO,OAAO,OAAO;AAAA,UAC1C,QAAQ,OAAO;AAAA,UACf,SAAS,OAAO;AAAA,UAChB,QAAQ;AAAA,UACR,gBAAgB,OAAO,kBAAkB,CAAC;AAAA,QAC5C,CAAC;AAGD,cAAM,aAAa,eAAe,mBAAmB,YAAY;AACjE,4CAAoC;AAAA,UAClC,UAAU;AAAA,UACV,QAAQ,OAAO;AAAA,UACf,QAAQ,OAAO;AAAA,UACf,SAAS,OAAO;AAAA,UAChB,gBAAgB,OAAO;AAAA,QACzB,CAAC;AAGD,YAAI;AACJ,YAAI;AACF,yBAAe,MAAM;AAAA,YACnB,MACE,eAAe,OAAO;AAAA,cACpB;AAAA,cACA;AAAA,cACA,kBAAkB,OAAO,OAAO;AAAA,cAChC,cAAc;AAAA,cACd;AAAA,YACF,CAAC;AAAA,YACH;AAAA,cACE,SAAS;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AAEd,gBAAM,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;AAAA,YAC5E,KAAK,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAC3F,CAAC;AAAA,QACH;AAGA,YAAI,CAAC,aAAa,MAAM,aAAa,MAAM;AACzC,cAAI,aAAa,SAAS,eAAe;AACvC,kBAAM,mBAAmB;AAAA,UAC3B;AACA,cAAI,aAAa,SAAS,eAAe;AACvC,kBAAM,kBAAkB;AAAA,cACtB,UAAU,aAAa,SAAS;AAAA,cAChC,GAAI,aAAa,QAAQ,WAAW,EAAE,QAAQ,aAAa,OAAO,SAAS,IAAI,CAAC;AAAA,YAClF,CAAC;AAAA,UACH;AACA,cAAI,aAAa,SAAS,eAAe;AACvC,kBAAM;AAAA,cACJ,aAAa,OAAO;AAAA,cACpB,aAAa,OAAO,UAAU;AAAA,YAChC;AAAA,UACF;AACA,gBAAM,aAAa,aAAa,OAAO;AAAA,QACzC;AAGA,YAAI,CAAC,MAAM,SAAS,MAAM,SAAS,YAAY,QAAQ,OAAO,OAAO;AACnE,kBAAQ,IAAI,EAAE;AAAA,QAChB;AAEA,eAAO;AAAA,MACT,UAAE;AAEA,cAAM,OAAO,MAAM;AAAA,MACrB;AAAA,IACF,CAAC;AAGD,UAAM,WAAW,aAAa,QAAQ,OAAO,CAAC,iBAAiB;AAE7D,UAAI,MAAM,SAAS,UAAU;AAE3B,gBAAQ,IAAI,iBAAiB,YAAY,CAAC;AAAA,MAC5C,OAAO;AAEL,cAAM,SAAS,mBAAmB,cAAc,KAAK;AACrD,YAAI,QAAQ;AACV,kBAAQ,IAAI,MAAM;AAAA,QACpB;AAAA,MACF;AAAA,IACF,CAAC;AACD,YAAQ,KAAK,QAAQ;AAAA,EACvB,CAAC;AAEH,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/commands/db-verify.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport type { ContractIR } from '@prisma-next/contract/ir';\nimport {\n errorDatabaseUrlRequired,\n errorDriverRequired,\n errorFileNotFound,\n errorHashMismatch,\n errorMarkerMissing,\n errorRuntime,\n errorTargetMismatch,\n errorUnexpected,\n} from '@prisma-next/core-control-plane/errors';\nimport type { VerifyDatabaseResult } from '@prisma-next/core-control-plane/types';\nimport { createControlPlaneStack } from '@prisma-next/core-control-plane/types';\nimport { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { performAction } from '../utils/action';\nimport { setCommandDescriptions } from '../utils/command-helpers';\nimport { assertContractRequirementsSatisfied } from '../utils/framework-components';\nimport { parseGlobalFlags } from '../utils/global-flags';\nimport {\n formatCommandHelp,\n formatStyledHeader,\n formatVerifyJson,\n formatVerifyOutput,\n} from '../utils/output';\nimport { handleResult } from '../utils/result-handler';\nimport { withSpinner } from '../utils/spinner';\n\ninterface DbVerifyOptions {\n readonly db?: string;\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 createDbVerifyCommand(): Command {\n const command = new Command('verify');\n setCommandDescriptions(\n command,\n 'Check whether the database has been signed with your contract',\n 'Verifies that your database schema matches the emitted contract. Checks table structures,\\n' +\n 'column types, constraints, and codec coverage. Reports any mismatches or missing codecs.',\n );\n command\n .configureHelp({\n formatHelp: (cmd) => {\n const flags = parseGlobalFlags({});\n return formatCommandHelp({ command: cmd, flags });\n },\n })\n .option('--db <url>', 'Database connection string')\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: DbVerifyOptions) => {\n const flags = parseGlobalFlags(options);\n\n const result = await performAction(async () => {\n // Load config (file I/O)\n const config = await loadConfig(options.config);\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 const contractPathAbsolute = config.contract?.output\n ? resolve(config.contract.output)\n : resolve('src/prisma/contract.json');\n // Convert to relative path for display\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n // Output header (only for human-readable output)\n if (flags.json !== 'object' && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n ];\n if (options.db) {\n details.push({ label: 'database', value: options.db });\n }\n const header = formatStyledHeader({\n command: 'db verify',\n description: 'Check whether the database has been signed with your contract',\n url: 'https://pris.ly/db-verify',\n details,\n flags,\n });\n console.log(header);\n }\n\n // Load contract file (file I/O)\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n throw errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n });\n }\n throw errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n const contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;\n\n // Resolve database URL\n const dbUrl = options.db ?? config.db?.url;\n if (!dbUrl) {\n throw errorDatabaseUrlRequired();\n }\n\n // Check for driver\n if (!config.driver) {\n throw errorDriverRequired();\n }\n\n // Store driver descriptor after null check\n const driverDescriptor = config.driver;\n\n // Create driver\n const driver = await withSpinner(() => driverDescriptor.create(dbUrl), {\n message: 'Connecting to database...',\n flags,\n });\n\n try {\n // Create family instance\n const stack = createControlPlaneStack({\n target: config.target,\n adapter: config.adapter,\n driver: driverDescriptor,\n extensionPacks: config.extensionPacks,\n });\n const familyInstance = config.family.create(stack);\n\n // Validate contract using instance validator\n const contractIR = familyInstance.validateContractIR(contractJson) as ContractIR;\n assertContractRequirementsSatisfied({ contract: contractIR, stack });\n\n // Call family instance verify method\n let verifyResult: VerifyDatabaseResult;\n try {\n verifyResult = await withSpinner(\n () =>\n familyInstance.verify({\n driver,\n contractIR,\n expectedTargetId: config.target.targetId,\n contractPath: contractPathAbsolute,\n configPath,\n }),\n {\n message: 'Verifying database schema...',\n flags,\n },\n );\n } catch (error) {\n // Wrap errors from verify() in structured error\n throw errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to verify database: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n\n // If verification failed, throw structured error\n if (!verifyResult.ok && verifyResult.code) {\n if (verifyResult.code === 'PN-RTM-3001') {\n throw errorMarkerMissing();\n }\n if (verifyResult.code === 'PN-RTM-3002') {\n throw errorHashMismatch({\n expected: verifyResult.contract.coreHash,\n ...(verifyResult.marker?.coreHash ? { actual: verifyResult.marker.coreHash } : {}),\n });\n }\n if (verifyResult.code === 'PN-RTM-3003') {\n throw errorTargetMismatch(\n verifyResult.target.expected,\n verifyResult.target.actual ?? 'unknown',\n );\n }\n throw errorRuntime(verifyResult.summary);\n }\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 verifyResult;\n } finally {\n // Ensure driver connection is closed\n await driver.close();\n }\n });\n\n // Handle result - formats output and returns exit code\n const exitCode = handleResult(result, flags, (verifyResult) => {\n // Output based on flags\n if (flags.json === 'object') {\n // JSON output to stdout\n console.log(formatVerifyJson(verifyResult));\n } else {\n // Human-readable output to stdout\n const output = formatVerifyOutput(verifyResult, flags);\n if (output) {\n console.log(output);\n }\n }\n });\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createContractEmitCommand
3
- } from "../chunk-464LNZCE.js";
3
+ } from "../chunk-QUPBU4KV.js";
4
4
  import "../chunk-BZMBKEEQ.js";
5
5
  import "../chunk-HWYQOCAJ.js";
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { type TargetBoundComponentDescriptor } from '@prisma-next/contract/framework-components';
2
2
  import type { ContractIR } from '@prisma-next/contract/ir';
3
- import type { ControlAdapterDescriptor, ControlExtensionDescriptor, ControlFamilyDescriptor, ControlTargetDescriptor } from '@prisma-next/core-control-plane/types';
3
+ import type { ControlPlaneStack } from '@prisma-next/core-control-plane/types';
4
4
  /**
5
5
  * Asserts that all framework components are compatible with the expected family and target.
6
6
  *
@@ -37,17 +37,14 @@ import type { ControlAdapterDescriptor, ControlExtensionDescriptor, ControlFamil
37
37
  */
38
38
  export declare function assertFrameworkComponentsCompatible<TFamilyId extends string, TTargetId extends string>(expectedFamilyId: TFamilyId, expectedTargetId: TTargetId, frameworkComponents: ReadonlyArray<unknown>): ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;
39
39
  /**
40
- * Validates that a contract is compatible with the configured family, target, adapter,
40
+ * Validates that a contract is compatible with the configured target, adapter,
41
41
  * and extension packs. Throws on family/target mismatches or missing extension packs.
42
42
  *
43
43
  * This check ensures the emitted contract matches the CLI config before running
44
44
  * commands that depend on the contract (e.g., db verify, db sign).
45
45
  *
46
46
  * @param contract - The contract IR to validate (must include targetFamily, target, extensionPacks).
47
- * @param family - The configured family descriptor.
48
- * @param target - The configured target descriptor.
49
- * @param adapter - The configured adapter descriptor.
50
- * @param extensionPacks - Optional array of extension descriptors provided by the config.
47
+ * @param stack - The control plane stack (target, adapter, driver, extensionPacks).
51
48
  *
52
49
  * @throws {CliStructuredError} errorConfigValidation when contract.targetFamily or contract.target
53
50
  * doesn't match the configured family/target.
@@ -60,22 +57,14 @@ export declare function assertFrameworkComponentsCompatible<TFamilyId extends st
60
57
  *
61
58
  * const config = await loadConfig();
62
59
  * const contractIR = await loadContractJson(config.contract.output);
60
+ * const stack = createControlPlaneStack({ target: config.target, adapter: config.adapter, ... });
63
61
  *
64
62
  * // Throws if contract is incompatible with config
65
- * assertContractRequirementsSatisfied({
66
- * contract: contractIR,
67
- * family: config.family,
68
- * target: config.target,
69
- * adapter: config.adapter,
70
- * extensionPacks: config.extensionPacks,
71
- * });
63
+ * assertContractRequirementsSatisfied({ contract: contractIR, stack });
72
64
  * ```
73
65
  */
74
- export declare function assertContractRequirementsSatisfied<TFamilyId extends string, TTargetId extends string>({ contract, family, target, adapter, extensionPacks, }: {
66
+ export declare function assertContractRequirementsSatisfied<TFamilyId extends string, TTargetId extends string>({ contract, stack, }: {
75
67
  readonly contract: Pick<ContractIR, 'targetFamily' | 'target' | 'extensionPacks'>;
76
- readonly family: ControlFamilyDescriptor<TFamilyId>;
77
- readonly target: ControlTargetDescriptor<TFamilyId, TTargetId>;
78
- readonly adapter: ControlAdapterDescriptor<TFamilyId, TTargetId>;
79
- readonly extensionPacks?: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[] | undefined;
68
+ readonly stack: ControlPlaneStack<TFamilyId, TTargetId>;
80
69
  }): void;
81
70
  //# sourceMappingURL=framework-components.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"framework-components.d.ts","sourceRoot":"","sources":["../../src/utils/framework-components.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,uCAAuC,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mCAAmC,CACjD,SAAS,SAAS,MAAM,EACxB,SAAS,SAAS,MAAM,EAExB,gBAAgB,EAAE,SAAS,EAC3B,gBAAgB,EAAE,SAAS,EAC3B,mBAAmB,EAAE,aAAa,CAAC,OAAO,CAAC,GAC1C,aAAa,CAAC,8BAA8B,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CA0DrE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,mCAAmC,CACjD,SAAS,SAAS,MAAM,EACxB,SAAS,SAAS,MAAM,EACxB,EACA,QAAQ,EACR,MAAM,EACN,MAAM,EACN,OAAO,EACP,cAAc,GACf,EAAE;IACD,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,QAAQ,GAAG,gBAAgB,CAAC,CAAC;IAClF,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/D,QAAQ,CAAC,OAAO,EAAE,wBAAwB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,0BAA0B,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC;CACnG,GAAG,IAAI,CA+BP"}
1
+ {"version":3,"file":"framework-components.d.ts","sourceRoot":"","sources":["../../src/utils/framework-components.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mCAAmC,CACjD,SAAS,SAAS,MAAM,EACxB,SAAS,SAAS,MAAM,EAExB,gBAAgB,EAAE,SAAS,EAC3B,gBAAgB,EAAE,SAAS,EAC3B,mBAAmB,EAAE,aAAa,CAAC,OAAO,CAAC,GAC1C,aAAa,CAAC,8BAA8B,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CA0DrE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mCAAmC,CACjD,SAAS,SAAS,MAAM,EACxB,SAAS,SAAS,MAAM,EACxB,EACA,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,QAAQ,GAAG,gBAAgB,CAAC,CAAC;IAClF,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;CACzD,GAAG,IAAI,CA+BP"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma-next/cli",
3
- "version": "0.3.0-dev.7",
3
+ "version": "0.3.0-dev.8",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "files": [
@@ -20,10 +20,10 @@
20
20
  "string-width": "^7.2.0",
21
21
  "strip-ansi": "^7.1.2",
22
22
  "wrap-ansi": "^9.0.2",
23
- "@prisma-next/contract": "0.3.0-dev.7",
24
- "@prisma-next/emitter": "0.3.0-dev.7",
25
- "@prisma-next/utils": "0.3.0-dev.7",
26
- "@prisma-next/core-control-plane": "0.3.0-dev.7"
23
+ "@prisma-next/contract": "0.3.0-dev.8",
24
+ "@prisma-next/core-control-plane": "0.3.0-dev.8",
25
+ "@prisma-next/emitter": "0.3.0-dev.8",
26
+ "@prisma-next/utils": "0.3.0-dev.8"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "24.10.4",
@@ -31,11 +31,11 @@
31
31
  "tsup": "8.5.1",
32
32
  "typescript": "5.9.3",
33
33
  "vitest": "4.0.16",
34
- "@prisma-next/sql-contract": "0.3.0-dev.7",
35
- "@prisma-next/sql-contract-emitter": "0.3.0-dev.7",
36
- "@prisma-next/sql-contract-ts": "0.3.0-dev.7",
37
- "@prisma-next/sql-operations": "0.3.0-dev.7",
38
- "@prisma-next/sql-runtime": "0.3.0-dev.7",
34
+ "@prisma-next/sql-contract": "0.3.0-dev.8",
35
+ "@prisma-next/sql-contract-emitter": "0.3.0-dev.8",
36
+ "@prisma-next/sql-contract-ts": "0.3.0-dev.8",
37
+ "@prisma-next/sql-operations": "0.3.0-dev.8",
38
+ "@prisma-next/sql-runtime": "0.3.0-dev.8",
39
39
  "@prisma-next/test-utils": "0.0.1"
40
40
  },
41
41
  "exports": {
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, relative, resolve } from 'node:path';
3
3
  import { errorContractConfigMissing } from '@prisma-next/core-control-plane/errors';
4
+ import { createControlPlaneStack } from '@prisma-next/core-control-plane/types';
4
5
  import { Command } from 'commander';
5
6
  import { loadConfig } from '../config-loader';
6
7
  import { performAction } from '../utils/action';
@@ -103,21 +104,13 @@ export function createContractEmitCommand(): Command {
103
104
  console.log(header);
104
105
  }
105
106
 
106
- // Create family instance (assembles operation registry, type imports, extension IDs)
107
- // Note: emit command doesn't need driver, but ControlFamilyDescriptor.create() requires it
108
- // We'll need to provide a minimal driver descriptor or make driver optional for emit
109
- // For now, we'll require driver to be present in config even for emit
110
- if (!config.driver) {
111
- throw errorContractConfigMissing({
112
- why: 'Config.driver is required. Even though emit does not use the driver, it is required by ControlFamilyDescriptor.create()',
113
- });
114
- }
115
- const familyInstance = config.family.create({
107
+ const stack = createControlPlaneStack({
116
108
  target: config.target,
117
109
  adapter: config.adapter,
118
110
  driver: config.driver,
119
- extensionPacks: config.extensionPacks ?? [],
111
+ extensionPacks: config.extensionPacks,
120
112
  });
113
+ const familyInstance = config.family.create(stack);
121
114
 
122
115
  // Resolve contract source from config (user's config handles loading)
123
116
  let contractRaw: unknown;
@@ -6,6 +6,7 @@ import type {
6
6
  MigrationPlanOperation,
7
7
  MigrationRunnerResult,
8
8
  } from '@prisma-next/core-control-plane/types';
9
+ import { createControlPlaneStack } from '@prisma-next/core-control-plane/types';
9
10
  import { redactDatabaseUrl } from '@prisma-next/utils/redact-db-url';
10
11
  import { Command } from 'commander';
11
12
  import { loadConfig } from '../config-loader';
@@ -198,12 +199,13 @@ export function createDbInitCommand(): Command {
198
199
 
199
200
  try {
200
201
  // Create family instance
201
- const familyInstance = config.family.create({
202
+ const stack = createControlPlaneStack({
202
203
  target: config.target,
203
204
  adapter: config.adapter,
204
205
  driver: driverDescriptor,
205
- extensionPacks: config.extensionPacks ?? [],
206
+ extensionPacks: config.extensionPacks,
206
207
  });
208
+ const familyInstance = config.family.create(stack);
207
209
  const rawComponents = [config.target, config.adapter, ...(config.extensionPacks ?? [])];
208
210
  const frameworkComponents = assertFrameworkComponentsCompatible(
209
211
  config.family.familyId,
@@ -213,13 +215,7 @@ export function createDbInitCommand(): Command {
213
215
 
214
216
  // Validate contract
215
217
  const contractIR = familyInstance.validateContractIR(contractJson);
216
- assertContractRequirementsSatisfied({
217
- contract: contractIR,
218
- family: config.family,
219
- target: config.target,
220
- adapter: config.adapter,
221
- extensionPacks: config.extensionPacks,
222
- });
218
+ assertContractRequirementsSatisfied({ contract: contractIR, stack });
223
219
 
224
220
  // Create planner and runner from target migrations capability
225
221
  const planner = migrations.createPlanner(familyInstance);
@@ -8,6 +8,7 @@ import {
8
8
  } from '@prisma-next/core-control-plane/errors';
9
9
  import type { CoreSchemaView } from '@prisma-next/core-control-plane/schema-view';
10
10
  import type { IntrospectSchemaResult } from '@prisma-next/core-control-plane/types';
11
+ import { createControlPlaneStack } from '@prisma-next/core-control-plane/types';
11
12
  import { Command } from 'commander';
12
13
  import { loadConfig } from '../config-loader';
13
14
  import { performAction } from '../utils/action';
@@ -139,23 +140,18 @@ export function createDbIntrospectCommand(): Command {
139
140
 
140
141
  try {
141
142
  // Create family instance
142
- const familyInstance = config.family.create({
143
+ const stack = createControlPlaneStack({
143
144
  target: config.target,
144
145
  adapter: config.adapter,
145
146
  driver: driverDescriptor,
146
- extensionPacks: config.extensionPacks ?? [],
147
+ extensionPacks: config.extensionPacks,
147
148
  });
149
+ const familyInstance = config.family.create(stack);
148
150
 
149
151
  // Validate contract IR if we loaded it
150
152
  if (contractIR) {
151
153
  const validatedContract = familyInstance.validateContractIR(contractIR);
152
- assertContractRequirementsSatisfied({
153
- contract: validatedContract,
154
- family: config.family,
155
- target: config.target,
156
- adapter: config.adapter,
157
- extensionPacks: config.extensionPacks,
158
- });
154
+ assertContractRequirementsSatisfied({ contract: validatedContract, stack });
159
155
  contractIR = validatedContract;
160
156
  }
161
157
 
@@ -9,6 +9,7 @@ import {
9
9
  errorUnexpected,
10
10
  } from '@prisma-next/core-control-plane/errors';
11
11
  import type { VerifyDatabaseSchemaResult } from '@prisma-next/core-control-plane/types';
12
+ import { createControlPlaneStack } from '@prisma-next/core-control-plane/types';
12
13
  import { Command } from 'commander';
13
14
  import { loadConfig } from '../config-loader';
14
15
  import { performAction } from '../utils/action';
@@ -129,24 +130,19 @@ export function createDbSchemaVerifyCommand(): Command {
129
130
  const driverDescriptor = config.driver;
130
131
 
131
132
  // Create family instance (needed for contract validation)
132
- const familyInstance = config.family.create({
133
+ const stack = createControlPlaneStack({
133
134
  target: config.target,
134
135
  adapter: config.adapter,
135
136
  driver: driverDescriptor,
136
- extensionPacks: config.extensionPacks ?? [],
137
+ extensionPacks: config.extensionPacks,
137
138
  });
139
+ const familyInstance = config.family.create(stack);
138
140
 
139
141
  // Validate contract using instance validator
140
142
  const contractIR = familyInstance.validateContractIR(contractJson) as ContractIR;
141
143
 
142
144
  // Validate contract requirements fail-fast before connecting to database
143
- assertContractRequirementsSatisfied({
144
- contract: contractIR,
145
- family: config.family,
146
- target: config.target,
147
- adapter: config.adapter,
148
- extensionPacks: config.extensionPacks,
149
- });
145
+ assertContractRequirementsSatisfied({ contract: contractIR, stack });
150
146
 
151
147
  // Resolve database URL
152
148
  const dbUrl = options.db ?? config.db?.url;
@@ -11,6 +11,7 @@ import type {
11
11
  SignDatabaseResult,
12
12
  VerifyDatabaseSchemaResult,
13
13
  } from '@prisma-next/core-control-plane/types';
14
+ import { createControlPlaneStack } from '@prisma-next/core-control-plane/types';
14
15
  import { Command } from 'commander';
15
16
  import { loadConfig } from '../config-loader';
16
17
  import { performAction } from '../utils/action';
@@ -138,22 +139,17 @@ export function createDbSignCommand(): Command {
138
139
  const driverDescriptor = config.driver;
139
140
 
140
141
  // Create family instance (needed for contract validation - no DB connection required)
141
- const familyInstance = config.family.create({
142
+ const stack = createControlPlaneStack({
142
143
  target: config.target,
143
144
  adapter: config.adapter,
144
145
  driver: driverDescriptor,
145
- extensionPacks: config.extensionPacks ?? [],
146
+ extensionPacks: config.extensionPacks,
146
147
  });
148
+ const familyInstance = config.family.create(stack);
147
149
 
148
150
  // Validate contract using instance validator (fail-fast before DB connection)
149
151
  const contractIR = familyInstance.validateContractIR(contractJson);
150
- assertContractRequirementsSatisfied({
151
- contract: contractIR,
152
- family: config.family,
153
- target: config.target,
154
- adapter: config.adapter,
155
- extensionPacks: config.extensionPacks,
156
- });
152
+ assertContractRequirementsSatisfied({ contract: contractIR, stack });
157
153
 
158
154
  const rawComponents = [config.target, config.adapter, ...(config.extensionPacks ?? [])];
159
155
  const frameworkComponents = assertFrameworkComponentsCompatible(
@@ -12,6 +12,7 @@ import {
12
12
  errorUnexpected,
13
13
  } from '@prisma-next/core-control-plane/errors';
14
14
  import type { VerifyDatabaseResult } from '@prisma-next/core-control-plane/types';
15
+ import { createControlPlaneStack } from '@prisma-next/core-control-plane/types';
15
16
  import { Command } from 'commander';
16
17
  import { loadConfig } from '../config-loader';
17
18
  import { performAction } from '../utils/action';
@@ -139,22 +140,17 @@ export function createDbVerifyCommand(): Command {
139
140
 
140
141
  try {
141
142
  // Create family instance
142
- const familyInstance = config.family.create({
143
+ const stack = createControlPlaneStack({
143
144
  target: config.target,
144
145
  adapter: config.adapter,
145
146
  driver: driverDescriptor,
146
- extensionPacks: config.extensionPacks ?? [],
147
+ extensionPacks: config.extensionPacks,
147
148
  });
149
+ const familyInstance = config.family.create(stack);
148
150
 
149
151
  // Validate contract using instance validator
150
152
  const contractIR = familyInstance.validateContractIR(contractJson) as ContractIR;
151
- assertContractRequirementsSatisfied({
152
- contract: contractIR,
153
- family: config.family,
154
- target: config.target,
155
- adapter: config.adapter,
156
- extensionPacks: config.extensionPacks,
157
- });
153
+ assertContractRequirementsSatisfied({ contract: contractIR, stack });
158
154
 
159
155
  // Call family instance verify method
160
156
  let verifyResult: VerifyDatabaseResult;
@@ -3,12 +3,7 @@ import {
3
3
  type TargetBoundComponentDescriptor,
4
4
  } from '@prisma-next/contract/framework-components';
5
5
  import type { ContractIR } from '@prisma-next/contract/ir';
6
- import type {
7
- ControlAdapterDescriptor,
8
- ControlExtensionDescriptor,
9
- ControlFamilyDescriptor,
10
- ControlTargetDescriptor,
11
- } from '@prisma-next/core-control-plane/types';
6
+ import type { ControlPlaneStack } from '@prisma-next/core-control-plane/types';
12
7
  import { errorConfigValidation, errorContractMissingExtensionPacks } from './cli-errors';
13
8
 
14
9
  /**
@@ -113,17 +108,14 @@ export function assertFrameworkComponentsCompatible<
113
108
  }
114
109
 
115
110
  /**
116
- * Validates that a contract is compatible with the configured family, target, adapter,
111
+ * Validates that a contract is compatible with the configured target, adapter,
117
112
  * and extension packs. Throws on family/target mismatches or missing extension packs.
118
113
  *
119
114
  * This check ensures the emitted contract matches the CLI config before running
120
115
  * commands that depend on the contract (e.g., db verify, db sign).
121
116
  *
122
117
  * @param contract - The contract IR to validate (must include targetFamily, target, extensionPacks).
123
- * @param family - The configured family descriptor.
124
- * @param target - The configured target descriptor.
125
- * @param adapter - The configured adapter descriptor.
126
- * @param extensionPacks - Optional array of extension descriptors provided by the config.
118
+ * @param stack - The control plane stack (target, adapter, driver, extensionPacks).
127
119
  *
128
120
  * @throws {CliStructuredError} errorConfigValidation when contract.targetFamily or contract.target
129
121
  * doesn't match the configured family/target.
@@ -136,15 +128,10 @@ export function assertFrameworkComponentsCompatible<
136
128
  *
137
129
  * const config = await loadConfig();
138
130
  * const contractIR = await loadContractJson(config.contract.output);
131
+ * const stack = createControlPlaneStack({ target: config.target, adapter: config.adapter, ... });
139
132
  *
140
133
  * // Throws if contract is incompatible with config
141
- * assertContractRequirementsSatisfied({
142
- * contract: contractIR,
143
- * family: config.family,
144
- * target: config.target,
145
- * adapter: config.adapter,
146
- * extensionPacks: config.extensionPacks,
147
- * });
134
+ * assertContractRequirementsSatisfied({ contract: contractIR, stack });
148
135
  * ```
149
136
  */
150
137
  export function assertContractRequirementsSatisfied<
@@ -152,26 +139,20 @@ export function assertContractRequirementsSatisfied<
152
139
  TTargetId extends string,
153
140
  >({
154
141
  contract,
155
- family,
156
- target,
157
- adapter,
158
- extensionPacks,
142
+ stack,
159
143
  }: {
160
144
  readonly contract: Pick<ContractIR, 'targetFamily' | 'target' | 'extensionPacks'>;
161
- readonly family: ControlFamilyDescriptor<TFamilyId>;
162
- readonly target: ControlTargetDescriptor<TFamilyId, TTargetId>;
163
- readonly adapter: ControlAdapterDescriptor<TFamilyId, TTargetId>;
164
- readonly extensionPacks?: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[] | undefined;
145
+ readonly stack: ControlPlaneStack<TFamilyId, TTargetId>;
165
146
  }): void {
166
- const providedComponentIds = new Set<string>([target.id, adapter.id]);
167
- for (const extension of extensionPacks ?? []) {
147
+ const providedComponentIds = new Set<string>([stack.target.id, stack.adapter.id]);
148
+ for (const extension of stack.extensionPacks) {
168
149
  providedComponentIds.add(extension.id);
169
150
  }
170
151
 
171
152
  const result = checkContractComponentRequirements({
172
153
  contract,
173
- expectedTargetFamily: family.familyId,
174
- expectedTargetId: target.targetId,
154
+ expectedTargetFamily: stack.target.familyId,
155
+ expectedTargetId: stack.target.targetId,
175
156
  providedComponentIds,
176
157
  });
177
158
 
@@ -1 +0,0 @@
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":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/framework-components.ts"],"sourcesContent":["import {\n checkContractComponentRequirements,\n type TargetBoundComponentDescriptor,\n} from '@prisma-next/contract/framework-components';\nimport type { ContractIR } from '@prisma-next/contract/ir';\nimport type {\n ControlAdapterDescriptor,\n ControlExtensionDescriptor,\n ControlFamilyDescriptor,\n ControlTargetDescriptor,\n} from '@prisma-next/core-control-plane/types';\nimport { errorConfigValidation, errorContractMissingExtensionPacks } from './cli-errors';\n\n/**\n * Asserts that all framework components are compatible with the expected family and target.\n *\n * This function validates that each component in the framework components array:\n * - Has kind 'target', 'adapter', 'extension', or 'driver'\n * - Has familyId matching expectedFamilyId\n * - Has targetId matching expectedTargetId\n *\n * This validation happens at the CLI composition boundary, before passing components\n * to typed planner/runner instances. It fills the gap between runtime validation\n * (via `validateConfig()`) and compile-time type enforcement.\n *\n * @param expectedFamilyId - The expected family ID (e.g., 'sql')\n * @param expectedTargetId - The expected target ID (e.g., 'postgres')\n * @param frameworkComponents - Array of framework components to validate\n * @returns The same array typed as TargetBoundComponentDescriptor\n * @throws CliStructuredError if any component is incompatible\n *\n * @example\n * ```ts\n * const config = await loadConfig();\n * const frameworkComponents = [config.target, config.adapter, ...(config.extensionPacks ?? [])];\n *\n * // Validate and type-narrow components before passing to planner\n * const typedComponents = assertFrameworkComponentsCompatible(\n * config.family.familyId,\n * config.target.targetId,\n * frameworkComponents\n * );\n *\n * const planner = target.migrations.createPlanner(familyInstance);\n * planner.plan({ contract, schema, policy, frameworkComponents: typedComponents });\n * ```\n */\nexport function assertFrameworkComponentsCompatible<\n TFamilyId extends string,\n TTargetId extends string,\n>(\n expectedFamilyId: TFamilyId,\n expectedTargetId: TTargetId,\n frameworkComponents: ReadonlyArray<unknown>,\n): ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>> {\n for (let i = 0; i < frameworkComponents.length; i++) {\n const component = frameworkComponents[i];\n\n // Check that component is an object\n if (typeof component !== 'object' || component === null) {\n throw errorConfigValidation('frameworkComponents[]', {\n why: `Framework component at index ${i} must be an object`,\n });\n }\n\n const record = component as Record<string, unknown>;\n\n // Check kind\n if (!Object.hasOwn(record, 'kind')) {\n throw errorConfigValidation('frameworkComponents[].kind', {\n why: `Framework component at index ${i} must have 'kind' property`,\n });\n }\n\n const kind = record['kind'];\n if (kind !== 'target' && kind !== 'adapter' && kind !== 'extension' && kind !== 'driver') {\n throw errorConfigValidation('frameworkComponents[].kind', {\n why: `Framework component at index ${i} has invalid kind '${String(kind)}' (must be 'target', 'adapter', 'extension', or 'driver')`,\n });\n }\n\n // Check familyId\n if (!Object.hasOwn(record, 'familyId')) {\n throw errorConfigValidation('frameworkComponents[].familyId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'familyId' property`,\n });\n }\n\n const familyId = record['familyId'];\n if (familyId !== expectedFamilyId) {\n throw errorConfigValidation('frameworkComponents[].familyId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) has familyId '${String(familyId)}' but expected '${expectedFamilyId}'`,\n });\n }\n\n // Check targetId\n if (!Object.hasOwn(record, 'targetId')) {\n throw errorConfigValidation('frameworkComponents[].targetId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) must have 'targetId' property`,\n });\n }\n\n const targetId = record['targetId'];\n if (targetId !== expectedTargetId) {\n throw errorConfigValidation('frameworkComponents[].targetId', {\n why: `Framework component at index ${i} (kind: ${String(kind)}) has targetId '${String(targetId)}' but expected '${expectedTargetId}'`,\n });\n }\n }\n\n // Type assertion is safe because we've validated all components above\n return frameworkComponents as ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;\n}\n\n/**\n * Validates that a contract is compatible with the configured family, target, adapter,\n * and extension packs. Throws on family/target mismatches or missing extension packs.\n *\n * This check ensures the emitted contract matches the CLI config before running\n * commands that depend on the contract (e.g., db verify, db sign).\n *\n * @param contract - The contract IR to validate (must include targetFamily, target, extensionPacks).\n * @param family - The configured family descriptor.\n * @param target - The configured target descriptor.\n * @param adapter - The configured adapter descriptor.\n * @param extensionPacks - Optional array of extension descriptors provided by the config.\n *\n * @throws {CliStructuredError} errorConfigValidation when contract.targetFamily or contract.target\n * doesn't match the configured family/target.\n * @throws {CliStructuredError} errorContractMissingExtensionPacks when the contract requires\n * extension packs that are not provided in the config (includes all missing packs in error.meta).\n *\n * @example\n * ```ts\n * import { assertContractRequirementsSatisfied } from './framework-components';\n *\n * const config = await loadConfig();\n * const contractIR = await loadContractJson(config.contract.output);\n *\n * // Throws if contract is incompatible with config\n * assertContractRequirementsSatisfied({\n * contract: contractIR,\n * family: config.family,\n * target: config.target,\n * adapter: config.adapter,\n * extensionPacks: config.extensionPacks,\n * });\n * ```\n */\nexport function assertContractRequirementsSatisfied<\n TFamilyId extends string,\n TTargetId extends string,\n>({\n contract,\n family,\n target,\n adapter,\n extensionPacks,\n}: {\n readonly contract: Pick<ContractIR, 'targetFamily' | 'target' | 'extensionPacks'>;\n readonly family: ControlFamilyDescriptor<TFamilyId>;\n readonly target: ControlTargetDescriptor<TFamilyId, TTargetId>;\n readonly adapter: ControlAdapterDescriptor<TFamilyId, TTargetId>;\n readonly extensionPacks?: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[] | undefined;\n}): void {\n const providedComponentIds = new Set<string>([target.id, adapter.id]);\n for (const extension of extensionPacks ?? []) {\n providedComponentIds.add(extension.id);\n }\n\n const result = checkContractComponentRequirements({\n contract,\n expectedTargetFamily: family.familyId,\n expectedTargetId: target.targetId,\n providedComponentIds,\n });\n\n if (result.familyMismatch) {\n throw errorConfigValidation('contract.targetFamily', {\n why: `Contract was emitted for family '${result.familyMismatch.actual}' but CLI config is wired to '${result.familyMismatch.expected}'.`,\n });\n }\n\n if (result.targetMismatch) {\n throw errorConfigValidation('contract.target', {\n why: `Contract target '${result.targetMismatch.actual}' does not match CLI target '${result.targetMismatch.expected}'.`,\n });\n }\n\n if (result.missingExtensionPackIds.length > 0) {\n throw errorContractMissingExtensionPacks({\n missingExtensionPacks: result.missingExtensionPackIds,\n providedComponentIds: [...providedComponentIds],\n });\n }\n}\n"],"mappings":";;;;;;AAAA;AAAA,EACE;AAAA,OAEK;AA4CA,SAAS,oCAId,kBACA,kBACA,qBACqE;AACrE,WAAS,IAAI,GAAG,IAAI,oBAAoB,QAAQ,KAAK;AACnD,UAAM,YAAY,oBAAoB,CAAC;AAGvC,QAAI,OAAO,cAAc,YAAY,cAAc,MAAM;AACvD,YAAM,sBAAsB,yBAAyB;AAAA,QACnD,KAAK,gCAAgC,CAAC;AAAA,MACxC,CAAC;AAAA,IACH;AAEA,UAAM,SAAS;AAGf,QAAI,CAAC,OAAO,OAAO,QAAQ,MAAM,GAAG;AAClC,YAAM,sBAAsB,8BAA8B;AAAA,QACxD,KAAK,gCAAgC,CAAC;AAAA,MACxC,CAAC;AAAA,IACH;AAEA,UAAM,OAAO,OAAO,MAAM;AAC1B,QAAI,SAAS,YAAY,SAAS,aAAa,SAAS,eAAe,SAAS,UAAU;AACxF,YAAM,sBAAsB,8BAA8B;AAAA,QACxD,KAAK,gCAAgC,CAAC,sBAAsB,OAAO,IAAI,CAAC;AAAA,MAC1E,CAAC;AAAA,IACH;AAGA,QAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,GAAG;AACtC,YAAM,sBAAsB,kCAAkC;AAAA,QAC5D,KAAK,gCAAgC,CAAC,WAAW,OAAO,IAAI,CAAC;AAAA,MAC/D,CAAC;AAAA,IACH;AAEA,UAAM,WAAW,OAAO,UAAU;AAClC,QAAI,aAAa,kBAAkB;AACjC,YAAM,sBAAsB,kCAAkC;AAAA,QAC5D,KAAK,gCAAgC,CAAC,WAAW,OAAO,IAAI,CAAC,mBAAmB,OAAO,QAAQ,CAAC,mBAAmB,gBAAgB;AAAA,MACrI,CAAC;AAAA,IACH;AAGA,QAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,GAAG;AACtC,YAAM,sBAAsB,kCAAkC;AAAA,QAC5D,KAAK,gCAAgC,CAAC,WAAW,OAAO,IAAI,CAAC;AAAA,MAC/D,CAAC;AAAA,IACH;AAEA,UAAM,WAAW,OAAO,UAAU;AAClC,QAAI,aAAa,kBAAkB;AACjC,YAAM,sBAAsB,kCAAkC;AAAA,QAC5D,KAAK,gCAAgC,CAAC,WAAW,OAAO,IAAI,CAAC,mBAAmB,OAAO,QAAQ,CAAC,mBAAmB,gBAAgB;AAAA,MACrI,CAAC;AAAA,IACH;AAAA,EACF;AAGA,SAAO;AACT;AAqCO,SAAS,oCAGd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMS;AACP,QAAM,uBAAuB,oBAAI,IAAY,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC;AACpE,aAAW,aAAa,kBAAkB,CAAC,GAAG;AAC5C,yBAAqB,IAAI,UAAU,EAAE;AAAA,EACvC;AAEA,QAAM,SAAS,mCAAmC;AAAA,IAChD;AAAA,IACA,sBAAsB,OAAO;AAAA,IAC7B,kBAAkB,OAAO;AAAA,IACzB;AAAA,EACF,CAAC;AAED,MAAI,OAAO,gBAAgB;AACzB,UAAM,sBAAsB,yBAAyB;AAAA,MACnD,KAAK,oCAAoC,OAAO,eAAe,MAAM,iCAAiC,OAAO,eAAe,QAAQ;AAAA,IACtI,CAAC;AAAA,EACH;AAEA,MAAI,OAAO,gBAAgB;AACzB,UAAM,sBAAsB,mBAAmB;AAAA,MAC7C,KAAK,oBAAoB,OAAO,eAAe,MAAM,gCAAgC,OAAO,eAAe,QAAQ;AAAA,IACrH,CAAC;AAAA,EACH;AAEA,MAAI,OAAO,wBAAwB,SAAS,GAAG;AAC7C,UAAM,mCAAmC;AAAA,MACvC,uBAAuB,OAAO;AAAA,MAC9B,sBAAsB,CAAC,GAAG,oBAAoB;AAAA,IAChD,CAAC;AAAA,EACH;AACF;","names":[]}