@prisma-next/cli 0.1.0-pr.65.1 → 0.1.0-pr.65.2

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
 
@@ -909,8 +909,8 @@ See `.cursor/rules/config-validation-and-normalization.mdc` for detailed pattern
909
909
  - `schemaVerify(options)` - Verifies database schema against contract
910
910
  - `introspect(options)` - Introspects database schema
911
911
 
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.
912
+ ### Descriptor Declarative Fields
913
+ - Families expose component descriptors (target, adapter, driver, extensions) with declarative metadata (`version`, `capabilities`, `types`, `operations`). CLI consumers import descriptors directly—there is no JSON manifest parsing step.
914
914
 
915
915
  ## Dependencies
916
916
 
@@ -982,7 +982,6 @@ The CLI package exports several subpaths for different use cases:
982
982
 
983
983
  - **`@prisma-next/cli`** (main export): Exports `loadContractFromTs` and `createContractEmitCommand`
984
984
  - **`@prisma-next/cli/config-types`**: Exports `defineConfig` and config types
985
- - **`@prisma-next/cli/pack-loading`**: Exports `loadExtensionPacks` and `loadExtensionPackManifest`
986
985
  - **`@prisma-next/cli/commands/db-init`**: Exports `createDbInitCommand`
987
986
  - **`@prisma-next/cli/commands/db-introspect`**: Exports `createDbIntrospectCommand`
988
987
  - **`@prisma-next/cli/commands/db-schema-verify`**: Exports `createDbSchemaVerifyCommand`
@@ -991,7 +990,7 @@ The CLI package exports several subpaths for different use cases:
991
990
  - **`@prisma-next/cli/commands/contract-emit`**: Exports `createContractEmitCommand`
992
991
  - **`@prisma-next/cli/config-loader`**: Exports `loadConfig` function
993
992
 
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.
993
+ **Important**: `loadContractFromTs` is exported from the main package (`@prisma-next/cli`). See `.cursor/rules/cli-package-exports.mdc` for import patterns.
995
994
 
996
995
  ## Package Location
997
996
 
@@ -48,8 +48,39 @@ function assertFrameworkComponentsCompatible(expectedFamilyId, expectedTargetId,
48
48
  }
49
49
  return frameworkComponents;
50
50
  }
51
+ function assertContractRequirementsSatisfied({
52
+ contract,
53
+ family,
54
+ target,
55
+ adapter,
56
+ extensions
57
+ }) {
58
+ if (contract.targetFamily !== family.familyId) {
59
+ throw errorConfigValidation("contract.targetFamily", {
60
+ why: `Contract was emitted for family '${contract.targetFamily}' but CLI config is wired to '${family.familyId}'.`
61
+ });
62
+ }
63
+ if (contract.target !== target.targetId) {
64
+ throw errorConfigValidation("contract.target", {
65
+ why: `Contract target '${contract.target}' does not match CLI target '${target.targetId}'.`
66
+ });
67
+ }
68
+ const providedComponentIds = /* @__PURE__ */ new Set([target.id, adapter.id]);
69
+ for (const extension of extensions ?? []) {
70
+ providedComponentIds.add(extension.id);
71
+ }
72
+ const requiredPacks = contract.extensionPacks ? Object.keys(contract.extensionPacks) : [];
73
+ for (const packId of requiredPacks) {
74
+ if (!providedComponentIds.has(packId)) {
75
+ throw errorConfigValidation("contract.extensionPacks", {
76
+ why: `Contract requires extension pack '${packId}', but CLI config does not provide a matching descriptor.`
77
+ });
78
+ }
79
+ }
80
+ }
51
81
 
52
82
  export {
53
- assertFrameworkComponentsCompatible
83
+ assertFrameworkComponentsCompatible,
84
+ assertContractRequirementsSatisfied
54
85
  };
55
- //# sourceMappingURL=chunk-YDE4ILKH.js.map
86
+ //# sourceMappingURL=chunk-TPVEV7KB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/framework-components.ts"],"sourcesContent":["import type { TargetBoundComponentDescriptor } 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 } 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.extensions ?? [])];\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\nexport function assertContractRequirementsSatisfied<\n TFamilyId extends string,\n TTargetId extends string,\n>({\n contract,\n family,\n target,\n adapter,\n extensions,\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 extensions?: readonly ControlExtensionDescriptor<TFamilyId, TTargetId>[];\n}): void {\n if (contract.targetFamily !== family.familyId) {\n throw errorConfigValidation('contract.targetFamily', {\n why: `Contract was emitted for family '${contract.targetFamily}' but CLI config is wired to '${family.familyId}'.`,\n });\n }\n\n if (contract.target !== target.targetId) {\n throw errorConfigValidation('contract.target', {\n why: `Contract target '${contract.target}' does not match CLI target '${target.targetId}'.`,\n });\n }\n\n const providedComponentIds = new Set<string>([target.id, adapter.id]);\n for (const extension of extensions ?? []) {\n providedComponentIds.add(extension.id);\n }\n\n const requiredPacks = contract.extensionPacks ? Object.keys(contract.extensionPacks) : [];\n for (const packId of requiredPacks) {\n if (!providedComponentIds.has(packId)) {\n throw errorConfigValidation('contract.extensionPacks', {\n why: `Contract requires extension pack '${packId}', but CLI config does not provide a matching descriptor.`,\n });\n }\n }\n}\n"],"mappings":";;;;;AA4CO,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;AAEO,SAAS,oCAGd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMS;AACP,MAAI,SAAS,iBAAiB,OAAO,UAAU;AAC7C,UAAM,sBAAsB,yBAAyB;AAAA,MACnD,KAAK,oCAAoC,SAAS,YAAY,iCAAiC,OAAO,QAAQ;AAAA,IAChH,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW,OAAO,UAAU;AACvC,UAAM,sBAAsB,mBAAmB;AAAA,MAC7C,KAAK,oBAAoB,SAAS,MAAM,gCAAgC,OAAO,QAAQ;AAAA,IACzF,CAAC;AAAA,EACH;AAEA,QAAM,uBAAuB,oBAAI,IAAY,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC;AACpE,aAAW,aAAa,cAAc,CAAC,GAAG;AACxC,yBAAqB,IAAI,UAAU,EAAE;AAAA,EACvC;AAEA,QAAM,gBAAgB,SAAS,iBAAiB,OAAO,KAAK,SAAS,cAAc,IAAI,CAAC;AACxF,aAAW,UAAU,eAAe;AAClC,QAAI,CAAC,qBAAqB,IAAI,MAAM,GAAG;AACrC,YAAM,sBAAsB,2BAA2B;AAAA,QACrD,KAAK,qCAAqC,MAAM;AAAA,MAClD,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
package/dist/cli.js CHANGED
@@ -1223,6 +1223,36 @@ function assertFrameworkComponentsCompatible(expectedFamilyId, expectedTargetId,
1223
1223
  }
1224
1224
  return frameworkComponents;
1225
1225
  }
1226
+ function assertContractRequirementsSatisfied({
1227
+ contract,
1228
+ family,
1229
+ target,
1230
+ adapter,
1231
+ extensions
1232
+ }) {
1233
+ if (contract.targetFamily !== family.familyId) {
1234
+ throw errorConfigValidation("contract.targetFamily", {
1235
+ why: `Contract was emitted for family '${contract.targetFamily}' but CLI config is wired to '${family.familyId}'.`
1236
+ });
1237
+ }
1238
+ if (contract.target !== target.targetId) {
1239
+ throw errorConfigValidation("contract.target", {
1240
+ why: `Contract target '${contract.target}' does not match CLI target '${target.targetId}'.`
1241
+ });
1242
+ }
1243
+ const providedComponentIds = /* @__PURE__ */ new Set([target.id, adapter.id]);
1244
+ for (const extension of extensions ?? []) {
1245
+ providedComponentIds.add(extension.id);
1246
+ }
1247
+ const requiredPacks = contract.extensionPacks ? Object.keys(contract.extensionPacks) : [];
1248
+ for (const packId of requiredPacks) {
1249
+ if (!providedComponentIds.has(packId)) {
1250
+ throw errorConfigValidation("contract.extensionPacks", {
1251
+ why: `Contract requires extension pack '${packId}', but CLI config does not provide a matching descriptor.`
1252
+ });
1253
+ }
1254
+ }
1255
+ }
1226
1256
 
1227
1257
  // src/commands/db-init.ts
1228
1258
  function createDbInitCommand() {
@@ -1318,6 +1348,13 @@ function createDbInitCommand() {
1318
1348
  rawComponents
1319
1349
  );
1320
1350
  const contractIR = familyInstance.validateContractIR(contractJson);
1351
+ assertContractRequirementsSatisfied({
1352
+ contract: contractIR,
1353
+ family: config.family,
1354
+ target: config.target,
1355
+ adapter: config.adapter,
1356
+ extensions: config.extensions ?? []
1357
+ });
1321
1358
  const planner = migrations.createPlanner(familyInstance);
1322
1359
  const runner = migrations.createRunner(familyInstance);
1323
1360
  const schemaIR = await withSpinner(() => familyInstance.introspect({ driver }), {
@@ -1571,7 +1608,15 @@ function createDbIntrospectCommand() {
1571
1608
  extensions: config.extensions ?? []
1572
1609
  });
1573
1610
  if (contractIR) {
1574
- contractIR = familyInstance.validateContractIR(contractIR);
1611
+ const validatedContract = familyInstance.validateContractIR(contractIR);
1612
+ assertContractRequirementsSatisfied({
1613
+ contract: validatedContract,
1614
+ family: config.family,
1615
+ target: config.target,
1616
+ adapter: config.adapter,
1617
+ extensions: config.extensions ?? []
1618
+ });
1619
+ contractIR = validatedContract;
1575
1620
  }
1576
1621
  let schemaIR;
1577
1622
  try {
@@ -1737,6 +1782,13 @@ function createDbSchemaVerifyCommand() {
1737
1782
  rawComponents
1738
1783
  );
1739
1784
  const contractIR = familyInstance.validateContractIR(contractJson);
1785
+ assertContractRequirementsSatisfied({
1786
+ contract: contractIR,
1787
+ family: config.family,
1788
+ target: config.target,
1789
+ adapter: config.adapter,
1790
+ extensions: config.extensions ?? []
1791
+ });
1740
1792
  let schemaVerifyResult;
1741
1793
  try {
1742
1794
  schemaVerifyResult = await withSpinner(
@@ -1869,6 +1921,13 @@ function createDbSignCommand() {
1869
1921
  rawComponents
1870
1922
  );
1871
1923
  const contractIR = familyInstance.validateContractIR(contractJson);
1924
+ assertContractRequirementsSatisfied({
1925
+ contract: contractIR,
1926
+ family: config.family,
1927
+ target: config.target,
1928
+ adapter: config.adapter,
1929
+ extensions: config.extensions ?? []
1930
+ });
1872
1931
  let schemaVerifyResult;
1873
1932
  try {
1874
1933
  schemaVerifyResult = await withSpinner(
@@ -2037,6 +2096,13 @@ function createDbVerifyCommand() {
2037
2096
  extensions: config.extensions ?? []
2038
2097
  });
2039
2098
  const contractIR = familyInstance.validateContractIR(contractJson);
2099
+ assertContractRequirementsSatisfied({
2100
+ contract: contractIR,
2101
+ family: config.family,
2102
+ target: config.target,
2103
+ adapter: config.adapter,
2104
+ extensions: config.extensions ?? []
2105
+ });
2040
2106
  let verifyResult;
2041
2107
  try {
2042
2108
  verifyResult = await withSpinner(