@prisma-next/family-mongo 0.11.0-dev.49 → 0.11.0-dev.50

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.
@@ -1 +1 @@
1
- {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/control-target-descriptor.ts","../src/core/control-types.ts","../src/core/operation-preview.ts","../src/core/schema-diff.ts","../src/core/schema-verify/canonicalize-introspection.ts"],"mappings":";;;;;;iBAyEgB,uBAAA,CAAwB,QAAA,EAAU,aAAA,UAAuB,aAAa;;;UCzCrE,0BAAA,SACP,qBAAA,UAA+B,aAAA,GACrC,iBAAA,CAAkB,aAAA,GAClB,uBAAA;;;ADsCJ;;;;;;;;EC3BE,mBAAA,CAAoB,YAAA,YAAwB,QAAA;AAAA;AAAA,iBAmF9B,yBAAA,CAA0B,YAAA,EAAc,YAAA,GAAe,0BAA0B;;;cC1GpF,qBAAA,EAAuB,uBAAuB,UAAU,0BAAA;;;;;AFkDrE;;;;;;;;AAAsF;;UGpDrE,4BAAA,mBAA+C,aAAA,GAAgB,aAAA,UACtE,0BAAA,mBAA6C,0BAAA;EAAA,SAC5C,kBAAA,EAAoB,kBAAA,CAAmB,SAAA;EAAA,SACvC,cAAA,EAAgB,cAAA,CAAe,SAAA,EAAW,aAAA;AAAA;;;;;;;AHiDrD;;;;;;;;AAAsF;;;UIpDrE,+BAAA,SACP,0BAAA;EAAA,SACC,aAAA,GAAgB,aAAA,CAAc,aAAA,CAAc,iBAAA;AAAA;;;iBC6EvC,qBAAA,CAAsB,UAA6C,WAAxB,sBAAsB;;;;;AL3BjF;iBKgDgB,wBAAA,CACd,UAAA,WAAqB,sBAAA,KACpB,gBAAgB;;;iBC/GH,gBAAA,CACd,IAAA,EAAM,aAAA,EACN,QAAA,EAAU,aAAA,EACV,MAAA;EAEA,IAAA,EAAM,sBAAA;EACN,MAAA,EAAQ,WAAA;EACR,MAAA;IAAU,IAAA;IAAc,IAAA;IAAc,IAAA;IAAc,UAAA;EAAA;AAAA;;;UCWrC,oBAAA;EAAA,SACN,IAAA,EAAM,aAAA;EAAA,SACN,QAAA,EAAU,aAAa;AAAA;AAAA,iBAGlB,kCAAA,CACd,IAAA,EAAM,aAAA,EACN,QAAA,EAAU,aAAA,GACT,oBAAA"}
1
+ {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/control-target-descriptor.ts","../src/core/control-types.ts","../src/core/operation-preview.ts","../src/core/schema-diff.ts","../src/core/schema-verify/canonicalize-introspection.ts"],"mappings":";;;;;;iBAyEgB,uBAAA,CAAwB,QAAA,EAAU,aAAA,UAAuB,aAAa;;;UCxCrE,0BAAA,SACP,qBAAA,UAA+B,aAAA,GACrC,iBAAA,CAAkB,aAAA,GAClB,uBAAA;;;ADqCJ;;;;;;;;EC1BE,mBAAA,CAAoB,YAAA,YAAwB,QAAA;AAAA;AAAA,iBAmF9B,yBAAA,CAA0B,YAAA,EAAc,YAAA,GAAe,0BAA0B;;;cC3GpF,qBAAA,EAAuB,uBAAuB,UAAU,0BAAA;;;;;AFkDrE;;;;;;;;AAAsF;;UGpDrE,4BAAA,mBAA+C,aAAA,GAAgB,aAAA,UACtE,0BAAA,mBAA6C,0BAAA;EAAA,SAC5C,kBAAA,EAAoB,kBAAA,CAAmB,SAAA;EAAA,SACvC,cAAA,EAAgB,cAAA,CAAe,SAAA,EAAW,aAAA;AAAA;;;;;;;AHiDrD;;;;;;;;AAAsF;;;UIpDrE,+BAAA,SACP,0BAAA;EAAA,SACC,aAAA,GAAgB,aAAA,CAAc,aAAA,CAAc,iBAAA;AAAA;;;iBC6EvC,qBAAA,CAAsB,UAA6C,WAAxB,sBAAsB;;;;;AL3BjF;iBKgDgB,wBAAA,CACd,UAAA,WAAqB,sBAAA,KACpB,gBAAgB;;;iBC/GH,gBAAA,CACd,IAAA,EAAM,aAAA,EACN,QAAA,EAAU,aAAA,EACV,MAAA;EAEA,IAAA,EAAM,sBAAA;EACN,MAAA,EAAQ,WAAA;EACR,MAAA;IAAU,IAAA;IAAc,IAAA;IAAc,IAAA;IAAc,UAAA;EAAA;AAAA;;;UCWrC,oBAAA;EAAA,SACN,IAAA,EAAM,aAAA;EAAA,SACN,QAAA,EAAU,aAAa;AAAA;AAAA,iBAGlB,kCAAA,CACd,IAAA,EAAM,aAAA,EACN,QAAA,EAAU,aAAA,GACT,oBAAA"}
package/dist/control.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  import { i as contractToMongoSchemaIR, n as canonicalizeSchemasForVerification, r as diffMongoSchemas, t as verifyMongoSchema } from "./verify-mongo-schema-Bhxvdah3.mjs";
2
- import { t as MongoContractSerializer } from "./mongo-contract-serializer-qnignx7Y.mjs";
2
+ import { t as MongoContractSerializer } from "./mongo-contract-serializer-DAoKJxiP.mjs";
3
3
  import { mongoEmission } from "@prisma-next/mongo-emitter";
4
4
  import { APP_SPACE_ID, SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH } from "@prisma-next/framework-components/control";
5
5
  import { assertDescriptorSelfConsistency } from "@prisma-next/migration-tools/spaces";
6
+ import { mongoContractCanonicalizationHooks } from "@prisma-next/mongo-contract/canonicalization-hooks";
6
7
  import { ifDefined } from "@prisma-next/utils/defined";
7
8
  //#region src/core/operation-preview.ts
8
9
  function formatKeySpec(keys) {
@@ -234,7 +235,8 @@ function createMongoFamilyInstance(controlStack) {
234
235
  target: contractJson.target,
235
236
  targetFamily: contractJson.targetFamily,
236
237
  storage: contractJson.storage,
237
- headRefHash: headRef.hash
238
+ headRefHash: headRef.hash,
239
+ ...mongoContractCanonicalizationHooks
238
240
  });
239
241
  }
240
242
  const adapter = controlStack.adapter;
@@ -1 +1 @@
1
- {"version":3,"file":"control.mjs","names":[],"sources":["../src/core/operation-preview.ts","../src/core/schema-to-view.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts"],"sourcesContent":["import type {\n MigrationPlanOperation,\n OperationPreview,\n} from '@prisma-next/framework-components/control';\nimport type {\n CollModCommand,\n CreateCollectionCommand,\n CreateIndexCommand,\n DropCollectionCommand,\n DropIndexCommand,\n MongoDdlCommandVisitor,\n MongoIndexKey,\n} from '@prisma-next/mongo-query-ast/control';\n\nfunction formatKeySpec(keys: ReadonlyArray<MongoIndexKey>): string {\n const entries = keys.map((k) => `${JSON.stringify(k.field)}: ${JSON.stringify(k.direction)}`);\n return `{ ${entries.join(', ')} }`;\n}\n\nfunction formatOptions(cmd: CreateIndexCommand): string | undefined {\n const parts: string[] = [];\n if (cmd.unique) parts.push('unique: true');\n if (cmd.sparse) parts.push('sparse: true');\n if (cmd.expireAfterSeconds !== undefined)\n parts.push(`expireAfterSeconds: ${cmd.expireAfterSeconds}`);\n if (cmd.name) parts.push(`name: ${JSON.stringify(cmd.name)}`);\n if (cmd.collation) parts.push(`collation: ${JSON.stringify(cmd.collation)}`);\n if (cmd.weights) parts.push(`weights: ${JSON.stringify(cmd.weights)}`);\n if (cmd.default_language) parts.push(`default_language: ${JSON.stringify(cmd.default_language)}`);\n if (cmd.language_override)\n parts.push(`language_override: ${JSON.stringify(cmd.language_override)}`);\n if (cmd.wildcardProjection)\n parts.push(`wildcardProjection: ${JSON.stringify(cmd.wildcardProjection)}`);\n if (cmd.partialFilterExpression)\n parts.push(`partialFilterExpression: ${JSON.stringify(cmd.partialFilterExpression)}`);\n if (parts.length === 0) return undefined;\n return `{ ${parts.join(', ')} }`;\n}\n\nfunction formatCreateCollectionOptions(cmd: CreateCollectionCommand): string | undefined {\n const parts: string[] = [];\n if (cmd.capped) parts.push('capped: true');\n if (cmd.size !== undefined) parts.push(`size: ${cmd.size}`);\n if (cmd.max !== undefined) parts.push(`max: ${cmd.max}`);\n if (cmd.timeseries) parts.push(`timeseries: ${JSON.stringify(cmd.timeseries)}`);\n if (cmd.collation) parts.push(`collation: ${JSON.stringify(cmd.collation)}`);\n if (cmd.clusteredIndex) parts.push(`clusteredIndex: ${JSON.stringify(cmd.clusteredIndex)}`);\n if (cmd.validator) parts.push(`validator: ${JSON.stringify(cmd.validator)}`);\n if (cmd.validationLevel) parts.push(`validationLevel: ${JSON.stringify(cmd.validationLevel)}`);\n if (cmd.validationAction) parts.push(`validationAction: ${JSON.stringify(cmd.validationAction)}`);\n if (cmd.changeStreamPreAndPostImages)\n parts.push(`changeStreamPreAndPostImages: ${JSON.stringify(cmd.changeStreamPreAndPostImages)}`);\n if (parts.length === 0) return undefined;\n return `{ ${parts.join(', ')} }`;\n}\n\nclass MongoDdlCommandFormatter implements MongoDdlCommandVisitor<string> {\n createIndex(cmd: CreateIndexCommand): string {\n const keySpec = formatKeySpec(cmd.keys);\n const opts = formatOptions(cmd);\n return opts\n ? `db.${cmd.collection}.createIndex(${keySpec}, ${opts})`\n : `db.${cmd.collection}.createIndex(${keySpec})`;\n }\n\n dropIndex(cmd: DropIndexCommand): string {\n return `db.${cmd.collection}.dropIndex(${JSON.stringify(cmd.name)})`;\n }\n\n createCollection(cmd: CreateCollectionCommand): string {\n const opts = formatCreateCollectionOptions(cmd);\n return opts\n ? `db.createCollection(${JSON.stringify(cmd.collection)}, ${opts})`\n : `db.createCollection(${JSON.stringify(cmd.collection)})`;\n }\n\n dropCollection(cmd: DropCollectionCommand): string {\n return `db.${cmd.collection}.drop()`;\n }\n\n collMod(cmd: CollModCommand): string {\n const parts: string[] = [`collMod: ${JSON.stringify(cmd.collection)}`];\n if (cmd.validator) parts.push(`validator: ${JSON.stringify(cmd.validator)}`);\n if (cmd.validationLevel) parts.push(`validationLevel: ${JSON.stringify(cmd.validationLevel)}`);\n if (cmd.validationAction)\n parts.push(`validationAction: ${JSON.stringify(cmd.validationAction)}`);\n if (cmd.changeStreamPreAndPostImages)\n parts.push(\n `changeStreamPreAndPostImages: ${JSON.stringify(cmd.changeStreamPreAndPostImages)}`,\n );\n return `db.runCommand({ ${parts.join(', ')} })`;\n }\n}\n\nconst formatter = new MongoDdlCommandFormatter();\n\ninterface MongoExecuteStep {\n readonly command: { readonly accept: <R>(visitor: MongoDdlCommandVisitor<R>) => R };\n}\n\nexport function formatMongoOperations(operations: readonly MigrationPlanOperation[]): string[] {\n const statements: string[] = [];\n for (const operation of operations) {\n const candidate = operation as unknown as Record<string, unknown>;\n if (!('execute' in candidate) || !Array.isArray(candidate['execute'])) {\n continue;\n }\n for (const step of candidate['execute'] as MongoExecuteStep[]) {\n if (step.command && typeof step.command.accept === 'function') {\n statements.push(step.command.accept(formatter));\n }\n }\n }\n return statements;\n}\n\n/**\n * Wraps `formatMongoOperations` into the family-agnostic\n * `OperationPreview` shape. Each statement carries\n * `language: 'mongodb-shell'`. Mirrors `sqlOperationsToPreview`.\n */\nexport function mongoOperationsToPreview(\n operations: readonly MigrationPlanOperation[],\n): OperationPreview {\n return {\n statements: formatMongoOperations(operations).map((text) => ({\n text,\n language: 'mongodb-shell',\n })),\n };\n}\n","import type { CoreSchemaView } from '@prisma-next/framework-components/control';\nimport { SchemaTreeNode } from '@prisma-next/framework-components/control';\nimport type { MongoSchemaCollection, MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport function mongoSchemaToView(schema: MongoSchemaIR): CoreSchemaView {\n const collectionNodes = schema.collections.map((collection) =>\n collectionToSchemaNode(collection.name, collection),\n );\n\n return {\n root: new SchemaTreeNode({\n kind: 'root',\n id: 'mongo-schema',\n label: 'database',\n ...ifDefined('children', collectionNodes.length > 0 ? collectionNodes : undefined),\n }),\n };\n}\n\nfunction collectionToSchemaNode(name: string, collection: MongoSchemaCollection): SchemaTreeNode {\n const children: SchemaTreeNode[] = [];\n\n for (const index of collection.indexes) {\n const keysSummary = index.keys\n .map((k) => {\n if (k.direction === 1) return k.field;\n if (k.direction === -1) return `${k.field} desc`;\n return `${k.field} ${k.direction}`;\n })\n .join(', ');\n const prefix = index.unique ? 'unique index' : 'index';\n const options: string[] = [];\n if (index.sparse) options.push('sparse');\n if (index.expireAfterSeconds != null) options.push(`ttl: ${index.expireAfterSeconds}s`);\n if (index.partialFilterExpression) options.push('partial');\n const optsSuffix = options.length > 0 ? ` (${options.join(', ')})` : '';\n\n children.push(\n new SchemaTreeNode({\n kind: 'index',\n id: `index-${name}-${index.keys.map((k) => `${k.field}_${k.direction}`).join('_')}`,\n label: `${prefix} (${keysSummary})${optsSuffix}`,\n meta: {\n keys: index.keys,\n unique: index.unique,\n ...ifDefined('sparse', index.sparse || undefined),\n ...ifDefined('expireAfterSeconds', index.expireAfterSeconds ?? undefined),\n ...ifDefined('partialFilterExpression', index.partialFilterExpression ?? undefined),\n },\n }),\n );\n }\n\n if (collection.validator) {\n const validatorChildren: SchemaTreeNode[] = [];\n const jsonSchema = collection.validator.jsonSchema as Record<string, unknown>;\n const properties = jsonSchema['properties'] as\n | Record<string, Record<string, unknown>>\n | undefined;\n const required = new Set((jsonSchema['required'] as string[] | undefined) ?? []);\n\n if (properties) {\n for (const [propName, propDef] of Object.entries(properties)) {\n const bsonType = (propDef['bsonType'] as string) ?? 'unknown';\n const suffix = required.has(propName) ? ' (required)' : '';\n validatorChildren.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `field-${name}-${propName}`,\n label: `${propName}: ${bsonType}${suffix}`,\n }),\n );\n }\n }\n\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `validator-${name}`,\n label: `validator (level: ${collection.validator.validationLevel}, action: ${collection.validator.validationAction})`,\n meta: {\n validationLevel: collection.validator.validationLevel,\n validationAction: collection.validator.validationAction,\n jsonSchema: collection.validator.jsonSchema,\n },\n ...ifDefined('children', validatorChildren.length > 0 ? validatorChildren : undefined),\n }),\n );\n }\n\n if (collection.options) {\n const opts = collection.options;\n const optLabels: string[] = [];\n if (opts.capped) optLabels.push('capped');\n if (opts.timeseries) optLabels.push('timeseries');\n if (opts.collation) optLabels.push('collation');\n if (opts.changeStreamPreAndPostImages) optLabels.push('changeStreamPreAndPostImages');\n if (opts.clusteredIndex) optLabels.push('clusteredIndex');\n\n if (optLabels.length > 0) {\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `options-${name}`,\n label: `options (${optLabels.join(', ')})`,\n meta: {\n ...ifDefined('capped', opts.capped ?? undefined),\n ...ifDefined('timeseries', opts.timeseries ?? undefined),\n ...ifDefined('collation', opts.collation ?? undefined),\n ...ifDefined(\n 'changeStreamPreAndPostImages',\n opts.changeStreamPreAndPostImages ?? undefined,\n ),\n ...ifDefined('clusteredIndex', opts.clusteredIndex ?? undefined),\n },\n }),\n );\n }\n }\n\n return new SchemaTreeNode({\n kind: 'collection',\n id: `collection-${name}`,\n label: `collection ${name}`,\n ...ifDefined('children', children.length > 0 ? children : undefined),\n });\n}\n","import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n ControlStack,\n CoreSchemaView,\n MigrationPlanOperation,\n OperationPreview,\n OperationPreviewCapable,\n SchemaViewCapable,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport {\n APP_SPACE_ID,\n VERIFY_CODE_HASH_MISMATCH,\n VERIFY_CODE_MARKER_MISSING,\n VERIFY_CODE_TARGET_MISMATCH,\n} from '@prisma-next/framework-components/control';\nimport { assertDescriptorSelfConsistency } from '@prisma-next/migration-tools/spaces';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { MongoControlAdapter, MongoControlAdapterDescriptor } from './control-adapter';\nimport type { MongoControlExtensionDescriptor } from './control-types';\nimport { MongoContractSerializer } from './ir/mongo-contract-serializer';\nimport { mongoOperationsToPreview } from './operation-preview';\nimport { mongoSchemaToView } from './schema-to-view';\nimport { verifyMongoSchema } from './schema-verify/verify-mongo-schema';\n\nexport interface MongoControlFamilyInstance\n extends ControlFamilyInstance<'mongo', MongoSchemaIR>,\n SchemaViewCapable<MongoSchemaIR>,\n OperationPreviewCapable {\n /**\n * The family seam-of-record for on-disk contract reads. Structurally\n * validates the JSON envelope, then casts to the framework `Contract`\n * shape; the per-target serializer (held on the Mongo target\n * descriptor) does the class-form wrap for downstream consumers, so\n * the family only needs the validated data. The single named entry\n * point every CLI on-disk read crosses (TML-2536) — `as Contract`\n * casts in production package sources are a serializer-bypass smell\n * guarded by `pnpm lint:no-contract-cast`.\n */\n deserializeContract(contractJson: unknown): Contract;\n}\n\nfunction deserializeMongoContract(contractJson: unknown): MongoContract {\n // Structural validation only — the per-target serializer wraps the\n // result in a class-form `MongoTargetContract` for downstream\n // consumers (CLI, runner). The family-instance methods only read\n // hash/target fields off the validated shape, so the unwrapped\n // `MongoContract` is sufficient here and avoids a family→target\n // runtime dep.\n return new MongoContractSerializer().deserializeContract(contractJson);\n}\n\n/**\n * Family-method contract input. By the time control-plane methods\n * (`verify`, `verifySchema`, `sign`, …) are invoked through the CLI\n * control client (`client.ts`), the input has already been threaded\n * through `familyInstance.deserializeContract`. The value is therefore a\n * class-form `MongoTargetContract` (or a structurally-equivalent\n * envelope post-deserialization) and must NOT be re-fed through\n * structural validation (arktype rejects extra keys like `namespaces`).\n *\n * The parameter type on the framework SPI is `unknown` for variance\n * reasons (so the family can express its own contract type without\n * leaking it to the framework). This helper recovers the validated\n * shape with a single narrow cast.\n */\nfunction asValidatedMongoContract(contract: unknown): MongoContract {\n return contract as MongoContract;\n}\n\nfunction isMongoControlAdapter(value: unknown): value is MongoControlAdapter<'mongo'> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'readMarker' in value &&\n typeof (value as { readMarker: unknown }).readMarker === 'function' &&\n 'readAllMarkers' in value &&\n typeof (value as { readAllMarkers: unknown }).readAllMarkers === 'function' &&\n 'introspectSchema' in value &&\n typeof (value as { introspectSchema: unknown }).introspectSchema === 'function'\n );\n}\n\nfunction buildVerifyResult(opts: {\n ok: boolean;\n code?: string;\n summary: string;\n contractStorageHash: string;\n contractProfileHash?: string;\n marker?: ContractMarkerRecord;\n expectedTargetId: string;\n actualTargetId?: string;\n contractPath: string;\n configPath?: string;\n totalTime: number;\n}): VerifyDatabaseResult {\n return {\n ok: opts.ok,\n ...ifDefined('code', opts.code),\n summary: opts.summary,\n contract: {\n storageHash: opts.contractStorageHash,\n ...ifDefined('profileHash', opts.contractProfileHash),\n },\n ...ifDefined(\n 'marker',\n opts.marker\n ? { storageHash: opts.marker.storageHash, profileHash: opts.marker.profileHash }\n : undefined,\n ),\n target: {\n expected: opts.expectedTargetId,\n ...ifDefined('actual', opts.actualTargetId),\n },\n meta: {\n contractPath: opts.contractPath,\n ...ifDefined('configPath', opts.configPath),\n },\n timings: { total: opts.totalTime },\n };\n}\n\nexport function createMongoFamilyInstance(controlStack: ControlStack): MongoControlFamilyInstance {\n // Descriptor self-consistency check.\n // Each extension that exposes a `contractSpace` must publish a\n // `headRef.hash` that matches the canonical hash recomputed from its\n // `contractJson`. A stale value would silently corrupt every downstream\n // boundary that trusts `headRef.hash` as the canonical identity (drift\n // detection, on-disk artefact emission, runner marker writes). Failing\n // fast at descriptor-load time turns \"extension author shipped an\n // inconsistent descriptor\" into an explicit, actionable error\n // (`MIGRATION.DESCRIPTOR_HEAD_HASH_MISMATCH`) rather than a confusing\n // mismatch surfacing several layers downstream. Mirrors the SQL family.\n const extensions = (controlStack.extensionPacks ??\n []) as readonly MongoControlExtensionDescriptor[];\n for (const extension of extensions) {\n if (extension.contractSpace) {\n const { contractJson, headRef } = extension.contractSpace;\n assertDescriptorSelfConsistency({\n extensionId: extension.id,\n target: contractJson.target,\n targetFamily: contractJson.targetFamily,\n storage: contractJson.storage,\n headRefHash: headRef.hash,\n });\n }\n }\n\n // Mongo dispatch surface. Every wire-level operation routes through\n // the adapter resolved from the control stack; the family carries no\n // direct imports of target/adapter/driver internals. Mirrors the SQL\n // family's `getControlAdapter()` helper.\n const adapter = controlStack.adapter as MongoControlAdapterDescriptor<'mongo'> | undefined;\n const getControlAdapter = (): MongoControlAdapter<'mongo'> => {\n if (!adapter) {\n throw new Error('Mongo family requires an adapter descriptor in ControlStack');\n }\n const controlAdapter = adapter.create(controlStack as ControlStack<'mongo', 'mongo'>);\n if (!isMongoControlAdapter(controlAdapter)) {\n throw new Error(\n 'Adapter does not implement MongoControlAdapter (missing readMarker, readAllMarkers, or introspectSchema)',\n );\n }\n return controlAdapter;\n };\n\n // The family-level driver type is `ControlDriverInstance<'mongo', string>`,\n // but the SPI methods are typed against `<'mongo', 'mongo'>`. Today's only\n // Mongo target is `'mongo'`, so the runtime values are identical; the cast\n // satisfies the structural type-system mismatch on `targetId`.\n const asMongoDriver = (\n driver: ControlDriverInstance<'mongo', string>,\n ): ControlDriverInstance<'mongo', 'mongo'> => driver as ControlDriverInstance<'mongo', 'mongo'>;\n\n return {\n familyId: 'mongo' as const,\n\n deserializeContract(contractJson: unknown): Contract {\n // The deserialized class form (MongoTargetContract, owned by\n // target-mongo) and the framework Contract are structurally\n // compatible — same fields, just a class instance on the storage\n // envelope. The cast preserves the framework signature.\n return deserializeMongoContract(contractJson) as unknown as Contract;\n },\n\n async verify(options): Promise<VerifyDatabaseResult> {\n const { driver, contract: rawContract, expectedTargetId, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const contract = asValidatedMongoContract(rawContract);\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n const contractTarget = contract.target;\n\n const baseOpts = {\n contractStorageHash,\n contractProfileHash,\n expectedTargetId,\n contractPath,\n ...ifDefined('configPath', configPath),\n };\n\n if (contractTarget !== expectedTargetId) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_TARGET_MISMATCH,\n summary: 'Target mismatch',\n actualTargetId: contractTarget,\n totalTime: Date.now() - startTime,\n });\n }\n\n const marker = await getControlAdapter().readMarker(asMongoDriver(driver), APP_SPACE_ID);\n\n if (!marker) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_MARKER_MISSING,\n summary: 'Marker missing',\n totalTime: Date.now() - startTime,\n });\n }\n\n if (marker.storageHash !== contractStorageHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n if (contractProfileHash && marker.profileHash !== contractProfileHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n return buildVerifyResult({\n ...baseOpts,\n ok: true,\n summary: 'Database matches contract',\n marker,\n totalTime: Date.now() - startTime,\n });\n },\n\n verifySchema(options: {\n readonly contract: unknown;\n readonly schema: MongoSchemaIR;\n readonly strict: boolean;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', string>>;\n }): VerifyDatabaseSchemaResult {\n const contract = asValidatedMongoContract(options.contract);\n return verifyMongoSchema({\n contract,\n schema: options.schema,\n strict: options.strict,\n frameworkComponents: options.frameworkComponents,\n });\n },\n\n async sign(options): Promise<SignDatabaseResult> {\n const { driver, contract: rawContract, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const contract = asValidatedMongoContract(rawContract);\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n\n const controlAdapter = getControlAdapter();\n const mongoDriver = asMongoDriver(driver);\n\n const existingMarker = await controlAdapter.readMarker(mongoDriver, APP_SPACE_ID);\n\n let markerCreated = false;\n let markerUpdated = false;\n let previousHashes: { storageHash?: string; profileHash?: string } | undefined;\n\n if (!existingMarker) {\n await controlAdapter.initMarker(mongoDriver, APP_SPACE_ID, {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n });\n markerCreated = true;\n } else {\n const storageHashMatches = existingMarker.storageHash === contractStorageHash;\n const profileHashMatches = existingMarker.profileHash === contractProfileHash;\n\n if (!storageHashMatches || !profileHashMatches) {\n previousHashes = {\n storageHash: existingMarker.storageHash,\n profileHash: existingMarker.profileHash,\n };\n const updated = await controlAdapter.updateMarker(\n mongoDriver,\n APP_SPACE_ID,\n existingMarker.storageHash,\n {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n },\n );\n if (!updated) {\n throw new Error('CAS conflict: marker was modified by another process during sign');\n }\n markerUpdated = true;\n }\n }\n\n let summary: string;\n if (markerCreated) {\n summary = 'Database signed (marker created)';\n } else if (markerUpdated) {\n summary = `Database signed (marker updated from ${previousHashes?.storageHash ?? 'unknown'})`;\n } else {\n summary = 'Database already signed with this contract';\n }\n\n return {\n ok: true,\n summary,\n contract: {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n },\n target: {\n expected: contract.target,\n actual: contract.target,\n },\n marker: {\n created: markerCreated,\n updated: markerUpdated,\n ...ifDefined('previous', previousHashes),\n },\n meta: {\n contractPath,\n ...ifDefined('configPath', configPath),\n },\n timings: {\n total: Date.now() - startTime,\n },\n };\n },\n\n async readMarker(options): Promise<ContractMarkerRecord | null> {\n return getControlAdapter().readMarker(asMongoDriver(options.driver), options.space);\n },\n\n async readAllMarkers(options): Promise<ReadonlyMap<string, ContractMarkerRecord>> {\n return getControlAdapter().readAllMarkers(asMongoDriver(options.driver));\n },\n\n async introspect(options): Promise<MongoSchemaIR> {\n return getControlAdapter().introspectSchema(asMongoDriver(options.driver));\n },\n\n toSchemaView(schema: MongoSchemaIR): CoreSchemaView {\n return mongoSchemaToView(schema);\n },\n\n toOperationPreview(operations: readonly MigrationPlanOperation[]): OperationPreview {\n return mongoOperationsToPreview(operations);\n },\n };\n}\n","import type {\n ControlFamilyDescriptor,\n ControlStack,\n} from '@prisma-next/framework-components/control';\nimport { mongoEmission } from '@prisma-next/mongo-emitter';\nimport { createMongoFamilyInstance, type MongoControlFamilyInstance } from './control-instance';\n\nclass MongoFamilyDescriptor\n implements ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance>\n{\n readonly kind = 'family' as const;\n readonly id = 'mongo';\n readonly familyId = 'mongo' as const;\n readonly version = '0.0.1';\n readonly emission = mongoEmission;\n\n create<TTargetId extends string>(\n stack: ControlStack<'mongo', TTargetId>,\n ): MongoControlFamilyInstance {\n return createMongoFamilyInstance(stack);\n }\n}\n\nexport const mongoFamilyDescriptor: ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance> =\n new MongoFamilyDescriptor();\n"],"mappings":"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"}
1
+ {"version":3,"file":"control.mjs","names":[],"sources":["../src/core/operation-preview.ts","../src/core/schema-to-view.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts"],"sourcesContent":["import type {\n MigrationPlanOperation,\n OperationPreview,\n} from '@prisma-next/framework-components/control';\nimport type {\n CollModCommand,\n CreateCollectionCommand,\n CreateIndexCommand,\n DropCollectionCommand,\n DropIndexCommand,\n MongoDdlCommandVisitor,\n MongoIndexKey,\n} from '@prisma-next/mongo-query-ast/control';\n\nfunction formatKeySpec(keys: ReadonlyArray<MongoIndexKey>): string {\n const entries = keys.map((k) => `${JSON.stringify(k.field)}: ${JSON.stringify(k.direction)}`);\n return `{ ${entries.join(', ')} }`;\n}\n\nfunction formatOptions(cmd: CreateIndexCommand): string | undefined {\n const parts: string[] = [];\n if (cmd.unique) parts.push('unique: true');\n if (cmd.sparse) parts.push('sparse: true');\n if (cmd.expireAfterSeconds !== undefined)\n parts.push(`expireAfterSeconds: ${cmd.expireAfterSeconds}`);\n if (cmd.name) parts.push(`name: ${JSON.stringify(cmd.name)}`);\n if (cmd.collation) parts.push(`collation: ${JSON.stringify(cmd.collation)}`);\n if (cmd.weights) parts.push(`weights: ${JSON.stringify(cmd.weights)}`);\n if (cmd.default_language) parts.push(`default_language: ${JSON.stringify(cmd.default_language)}`);\n if (cmd.language_override)\n parts.push(`language_override: ${JSON.stringify(cmd.language_override)}`);\n if (cmd.wildcardProjection)\n parts.push(`wildcardProjection: ${JSON.stringify(cmd.wildcardProjection)}`);\n if (cmd.partialFilterExpression)\n parts.push(`partialFilterExpression: ${JSON.stringify(cmd.partialFilterExpression)}`);\n if (parts.length === 0) return undefined;\n return `{ ${parts.join(', ')} }`;\n}\n\nfunction formatCreateCollectionOptions(cmd: CreateCollectionCommand): string | undefined {\n const parts: string[] = [];\n if (cmd.capped) parts.push('capped: true');\n if (cmd.size !== undefined) parts.push(`size: ${cmd.size}`);\n if (cmd.max !== undefined) parts.push(`max: ${cmd.max}`);\n if (cmd.timeseries) parts.push(`timeseries: ${JSON.stringify(cmd.timeseries)}`);\n if (cmd.collation) parts.push(`collation: ${JSON.stringify(cmd.collation)}`);\n if (cmd.clusteredIndex) parts.push(`clusteredIndex: ${JSON.stringify(cmd.clusteredIndex)}`);\n if (cmd.validator) parts.push(`validator: ${JSON.stringify(cmd.validator)}`);\n if (cmd.validationLevel) parts.push(`validationLevel: ${JSON.stringify(cmd.validationLevel)}`);\n if (cmd.validationAction) parts.push(`validationAction: ${JSON.stringify(cmd.validationAction)}`);\n if (cmd.changeStreamPreAndPostImages)\n parts.push(`changeStreamPreAndPostImages: ${JSON.stringify(cmd.changeStreamPreAndPostImages)}`);\n if (parts.length === 0) return undefined;\n return `{ ${parts.join(', ')} }`;\n}\n\nclass MongoDdlCommandFormatter implements MongoDdlCommandVisitor<string> {\n createIndex(cmd: CreateIndexCommand): string {\n const keySpec = formatKeySpec(cmd.keys);\n const opts = formatOptions(cmd);\n return opts\n ? `db.${cmd.collection}.createIndex(${keySpec}, ${opts})`\n : `db.${cmd.collection}.createIndex(${keySpec})`;\n }\n\n dropIndex(cmd: DropIndexCommand): string {\n return `db.${cmd.collection}.dropIndex(${JSON.stringify(cmd.name)})`;\n }\n\n createCollection(cmd: CreateCollectionCommand): string {\n const opts = formatCreateCollectionOptions(cmd);\n return opts\n ? `db.createCollection(${JSON.stringify(cmd.collection)}, ${opts})`\n : `db.createCollection(${JSON.stringify(cmd.collection)})`;\n }\n\n dropCollection(cmd: DropCollectionCommand): string {\n return `db.${cmd.collection}.drop()`;\n }\n\n collMod(cmd: CollModCommand): string {\n const parts: string[] = [`collMod: ${JSON.stringify(cmd.collection)}`];\n if (cmd.validator) parts.push(`validator: ${JSON.stringify(cmd.validator)}`);\n if (cmd.validationLevel) parts.push(`validationLevel: ${JSON.stringify(cmd.validationLevel)}`);\n if (cmd.validationAction)\n parts.push(`validationAction: ${JSON.stringify(cmd.validationAction)}`);\n if (cmd.changeStreamPreAndPostImages)\n parts.push(\n `changeStreamPreAndPostImages: ${JSON.stringify(cmd.changeStreamPreAndPostImages)}`,\n );\n return `db.runCommand({ ${parts.join(', ')} })`;\n }\n}\n\nconst formatter = new MongoDdlCommandFormatter();\n\ninterface MongoExecuteStep {\n readonly command: { readonly accept: <R>(visitor: MongoDdlCommandVisitor<R>) => R };\n}\n\nexport function formatMongoOperations(operations: readonly MigrationPlanOperation[]): string[] {\n const statements: string[] = [];\n for (const operation of operations) {\n const candidate = operation as unknown as Record<string, unknown>;\n if (!('execute' in candidate) || !Array.isArray(candidate['execute'])) {\n continue;\n }\n for (const step of candidate['execute'] as MongoExecuteStep[]) {\n if (step.command && typeof step.command.accept === 'function') {\n statements.push(step.command.accept(formatter));\n }\n }\n }\n return statements;\n}\n\n/**\n * Wraps `formatMongoOperations` into the family-agnostic\n * `OperationPreview` shape. Each statement carries\n * `language: 'mongodb-shell'`. Mirrors `sqlOperationsToPreview`.\n */\nexport function mongoOperationsToPreview(\n operations: readonly MigrationPlanOperation[],\n): OperationPreview {\n return {\n statements: formatMongoOperations(operations).map((text) => ({\n text,\n language: 'mongodb-shell',\n })),\n };\n}\n","import type { CoreSchemaView } from '@prisma-next/framework-components/control';\nimport { SchemaTreeNode } from '@prisma-next/framework-components/control';\nimport type { MongoSchemaCollection, MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport function mongoSchemaToView(schema: MongoSchemaIR): CoreSchemaView {\n const collectionNodes = schema.collections.map((collection) =>\n collectionToSchemaNode(collection.name, collection),\n );\n\n return {\n root: new SchemaTreeNode({\n kind: 'root',\n id: 'mongo-schema',\n label: 'database',\n ...ifDefined('children', collectionNodes.length > 0 ? collectionNodes : undefined),\n }),\n };\n}\n\nfunction collectionToSchemaNode(name: string, collection: MongoSchemaCollection): SchemaTreeNode {\n const children: SchemaTreeNode[] = [];\n\n for (const index of collection.indexes) {\n const keysSummary = index.keys\n .map((k) => {\n if (k.direction === 1) return k.field;\n if (k.direction === -1) return `${k.field} desc`;\n return `${k.field} ${k.direction}`;\n })\n .join(', ');\n const prefix = index.unique ? 'unique index' : 'index';\n const options: string[] = [];\n if (index.sparse) options.push('sparse');\n if (index.expireAfterSeconds != null) options.push(`ttl: ${index.expireAfterSeconds}s`);\n if (index.partialFilterExpression) options.push('partial');\n const optsSuffix = options.length > 0 ? ` (${options.join(', ')})` : '';\n\n children.push(\n new SchemaTreeNode({\n kind: 'index',\n id: `index-${name}-${index.keys.map((k) => `${k.field}_${k.direction}`).join('_')}`,\n label: `${prefix} (${keysSummary})${optsSuffix}`,\n meta: {\n keys: index.keys,\n unique: index.unique,\n ...ifDefined('sparse', index.sparse || undefined),\n ...ifDefined('expireAfterSeconds', index.expireAfterSeconds ?? undefined),\n ...ifDefined('partialFilterExpression', index.partialFilterExpression ?? undefined),\n },\n }),\n );\n }\n\n if (collection.validator) {\n const validatorChildren: SchemaTreeNode[] = [];\n const jsonSchema = collection.validator.jsonSchema as Record<string, unknown>;\n const properties = jsonSchema['properties'] as\n | Record<string, Record<string, unknown>>\n | undefined;\n const required = new Set((jsonSchema['required'] as string[] | undefined) ?? []);\n\n if (properties) {\n for (const [propName, propDef] of Object.entries(properties)) {\n const bsonType = (propDef['bsonType'] as string) ?? 'unknown';\n const suffix = required.has(propName) ? ' (required)' : '';\n validatorChildren.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `field-${name}-${propName}`,\n label: `${propName}: ${bsonType}${suffix}`,\n }),\n );\n }\n }\n\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `validator-${name}`,\n label: `validator (level: ${collection.validator.validationLevel}, action: ${collection.validator.validationAction})`,\n meta: {\n validationLevel: collection.validator.validationLevel,\n validationAction: collection.validator.validationAction,\n jsonSchema: collection.validator.jsonSchema,\n },\n ...ifDefined('children', validatorChildren.length > 0 ? validatorChildren : undefined),\n }),\n );\n }\n\n if (collection.options) {\n const opts = collection.options;\n const optLabels: string[] = [];\n if (opts.capped) optLabels.push('capped');\n if (opts.timeseries) optLabels.push('timeseries');\n if (opts.collation) optLabels.push('collation');\n if (opts.changeStreamPreAndPostImages) optLabels.push('changeStreamPreAndPostImages');\n if (opts.clusteredIndex) optLabels.push('clusteredIndex');\n\n if (optLabels.length > 0) {\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `options-${name}`,\n label: `options (${optLabels.join(', ')})`,\n meta: {\n ...ifDefined('capped', opts.capped ?? undefined),\n ...ifDefined('timeseries', opts.timeseries ?? undefined),\n ...ifDefined('collation', opts.collation ?? undefined),\n ...ifDefined(\n 'changeStreamPreAndPostImages',\n opts.changeStreamPreAndPostImages ?? undefined,\n ),\n ...ifDefined('clusteredIndex', opts.clusteredIndex ?? undefined),\n },\n }),\n );\n }\n }\n\n return new SchemaTreeNode({\n kind: 'collection',\n id: `collection-${name}`,\n label: `collection ${name}`,\n ...ifDefined('children', children.length > 0 ? children : undefined),\n });\n}\n","import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n ControlStack,\n CoreSchemaView,\n MigrationPlanOperation,\n OperationPreview,\n OperationPreviewCapable,\n SchemaViewCapable,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport {\n APP_SPACE_ID,\n VERIFY_CODE_HASH_MISMATCH,\n VERIFY_CODE_MARKER_MISSING,\n VERIFY_CODE_TARGET_MISMATCH,\n} from '@prisma-next/framework-components/control';\nimport { assertDescriptorSelfConsistency } from '@prisma-next/migration-tools/spaces';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport { mongoContractCanonicalizationHooks } from '@prisma-next/mongo-contract/canonicalization-hooks';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { MongoControlAdapter, MongoControlAdapterDescriptor } from './control-adapter';\nimport type { MongoControlExtensionDescriptor } from './control-types';\nimport { MongoContractSerializer } from './ir/mongo-contract-serializer';\nimport { mongoOperationsToPreview } from './operation-preview';\nimport { mongoSchemaToView } from './schema-to-view';\nimport { verifyMongoSchema } from './schema-verify/verify-mongo-schema';\n\nexport interface MongoControlFamilyInstance\n extends ControlFamilyInstance<'mongo', MongoSchemaIR>,\n SchemaViewCapable<MongoSchemaIR>,\n OperationPreviewCapable {\n /**\n * The family seam-of-record for on-disk contract reads. Structurally\n * validates the JSON envelope, then casts to the framework `Contract`\n * shape; the per-target serializer (held on the Mongo target\n * descriptor) does the class-form wrap for downstream consumers, so\n * the family only needs the validated data. The single named entry\n * point every CLI on-disk read crosses (TML-2536) — `as Contract`\n * casts in production package sources are a serializer-bypass smell\n * guarded by `pnpm lint:no-contract-cast`.\n */\n deserializeContract(contractJson: unknown): Contract;\n}\n\nfunction deserializeMongoContract(contractJson: unknown): MongoContract {\n // Structural validation only — the per-target serializer wraps the\n // result in a class-form `MongoTargetContract` for downstream\n // consumers (CLI, runner). The family-instance methods only read\n // hash/target fields off the validated shape, so the unwrapped\n // `MongoContract` is sufficient here and avoids a family→target\n // runtime dep.\n return new MongoContractSerializer().deserializeContract(contractJson);\n}\n\n/**\n * Family-method contract input. By the time control-plane methods\n * (`verify`, `verifySchema`, `sign`, …) are invoked through the CLI\n * control client (`client.ts`), the input has already been threaded\n * through `familyInstance.deserializeContract`. The value is therefore a\n * class-form `MongoTargetContract` (or a structurally-equivalent\n * envelope post-deserialization) and must NOT be re-fed through\n * structural validation (arktype rejects extra keys like `namespaces`).\n *\n * The parameter type on the framework SPI is `unknown` for variance\n * reasons (so the family can express its own contract type without\n * leaking it to the framework). This helper recovers the validated\n * shape with a single narrow cast.\n */\nfunction asValidatedMongoContract(contract: unknown): MongoContract {\n return contract as MongoContract;\n}\n\nfunction isMongoControlAdapter(value: unknown): value is MongoControlAdapter<'mongo'> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'readMarker' in value &&\n typeof (value as { readMarker: unknown }).readMarker === 'function' &&\n 'readAllMarkers' in value &&\n typeof (value as { readAllMarkers: unknown }).readAllMarkers === 'function' &&\n 'introspectSchema' in value &&\n typeof (value as { introspectSchema: unknown }).introspectSchema === 'function'\n );\n}\n\nfunction buildVerifyResult(opts: {\n ok: boolean;\n code?: string;\n summary: string;\n contractStorageHash: string;\n contractProfileHash?: string;\n marker?: ContractMarkerRecord;\n expectedTargetId: string;\n actualTargetId?: string;\n contractPath: string;\n configPath?: string;\n totalTime: number;\n}): VerifyDatabaseResult {\n return {\n ok: opts.ok,\n ...ifDefined('code', opts.code),\n summary: opts.summary,\n contract: {\n storageHash: opts.contractStorageHash,\n ...ifDefined('profileHash', opts.contractProfileHash),\n },\n ...ifDefined(\n 'marker',\n opts.marker\n ? { storageHash: opts.marker.storageHash, profileHash: opts.marker.profileHash }\n : undefined,\n ),\n target: {\n expected: opts.expectedTargetId,\n ...ifDefined('actual', opts.actualTargetId),\n },\n meta: {\n contractPath: opts.contractPath,\n ...ifDefined('configPath', opts.configPath),\n },\n timings: { total: opts.totalTime },\n };\n}\n\nexport function createMongoFamilyInstance(controlStack: ControlStack): MongoControlFamilyInstance {\n // Descriptor self-consistency check.\n // Each extension that exposes a `contractSpace` must publish a\n // `headRef.hash` that matches the canonical hash recomputed from its\n // `contractJson`. A stale value would silently corrupt every downstream\n // boundary that trusts `headRef.hash` as the canonical identity (drift\n // detection, on-disk artefact emission, runner marker writes). Failing\n // fast at descriptor-load time turns \"extension author shipped an\n // inconsistent descriptor\" into an explicit, actionable error\n // (`MIGRATION.DESCRIPTOR_HEAD_HASH_MISMATCH`) rather than a confusing\n // mismatch surfacing several layers downstream. Mirrors the SQL family.\n const extensions = (controlStack.extensionPacks ??\n []) as readonly MongoControlExtensionDescriptor[];\n for (const extension of extensions) {\n if (extension.contractSpace) {\n const { contractJson, headRef } = extension.contractSpace;\n assertDescriptorSelfConsistency({\n extensionId: extension.id,\n target: contractJson.target,\n targetFamily: contractJson.targetFamily,\n storage: contractJson.storage,\n headRefHash: headRef.hash,\n ...mongoContractCanonicalizationHooks,\n });\n }\n }\n\n // Mongo dispatch surface. Every wire-level operation routes through\n // the adapter resolved from the control stack; the family carries no\n // direct imports of target/adapter/driver internals. Mirrors the SQL\n // family's `getControlAdapter()` helper.\n const adapter = controlStack.adapter as MongoControlAdapterDescriptor<'mongo'> | undefined;\n const getControlAdapter = (): MongoControlAdapter<'mongo'> => {\n if (!adapter) {\n throw new Error('Mongo family requires an adapter descriptor in ControlStack');\n }\n const controlAdapter = adapter.create(controlStack as ControlStack<'mongo', 'mongo'>);\n if (!isMongoControlAdapter(controlAdapter)) {\n throw new Error(\n 'Adapter does not implement MongoControlAdapter (missing readMarker, readAllMarkers, or introspectSchema)',\n );\n }\n return controlAdapter;\n };\n\n // The family-level driver type is `ControlDriverInstance<'mongo', string>`,\n // but the SPI methods are typed against `<'mongo', 'mongo'>`. Today's only\n // Mongo target is `'mongo'`, so the runtime values are identical; the cast\n // satisfies the structural type-system mismatch on `targetId`.\n const asMongoDriver = (\n driver: ControlDriverInstance<'mongo', string>,\n ): ControlDriverInstance<'mongo', 'mongo'> => driver as ControlDriverInstance<'mongo', 'mongo'>;\n\n return {\n familyId: 'mongo' as const,\n\n deserializeContract(contractJson: unknown): Contract {\n // The deserialized class form (MongoTargetContract, owned by\n // target-mongo) and the framework Contract are structurally\n // compatible — same fields, just a class instance on the storage\n // envelope. The cast preserves the framework signature.\n return deserializeMongoContract(contractJson) as unknown as Contract;\n },\n\n async verify(options): Promise<VerifyDatabaseResult> {\n const { driver, contract: rawContract, expectedTargetId, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const contract = asValidatedMongoContract(rawContract);\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n const contractTarget = contract.target;\n\n const baseOpts = {\n contractStorageHash,\n contractProfileHash,\n expectedTargetId,\n contractPath,\n ...ifDefined('configPath', configPath),\n };\n\n if (contractTarget !== expectedTargetId) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_TARGET_MISMATCH,\n summary: 'Target mismatch',\n actualTargetId: contractTarget,\n totalTime: Date.now() - startTime,\n });\n }\n\n const marker = await getControlAdapter().readMarker(asMongoDriver(driver), APP_SPACE_ID);\n\n if (!marker) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_MARKER_MISSING,\n summary: 'Marker missing',\n totalTime: Date.now() - startTime,\n });\n }\n\n if (marker.storageHash !== contractStorageHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n if (contractProfileHash && marker.profileHash !== contractProfileHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n return buildVerifyResult({\n ...baseOpts,\n ok: true,\n summary: 'Database matches contract',\n marker,\n totalTime: Date.now() - startTime,\n });\n },\n\n verifySchema(options: {\n readonly contract: unknown;\n readonly schema: MongoSchemaIR;\n readonly strict: boolean;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', string>>;\n }): VerifyDatabaseSchemaResult {\n const contract = asValidatedMongoContract(options.contract);\n return verifyMongoSchema({\n contract,\n schema: options.schema,\n strict: options.strict,\n frameworkComponents: options.frameworkComponents,\n });\n },\n\n async sign(options): Promise<SignDatabaseResult> {\n const { driver, contract: rawContract, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const contract = asValidatedMongoContract(rawContract);\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n\n const controlAdapter = getControlAdapter();\n const mongoDriver = asMongoDriver(driver);\n\n const existingMarker = await controlAdapter.readMarker(mongoDriver, APP_SPACE_ID);\n\n let markerCreated = false;\n let markerUpdated = false;\n let previousHashes: { storageHash?: string; profileHash?: string } | undefined;\n\n if (!existingMarker) {\n await controlAdapter.initMarker(mongoDriver, APP_SPACE_ID, {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n });\n markerCreated = true;\n } else {\n const storageHashMatches = existingMarker.storageHash === contractStorageHash;\n const profileHashMatches = existingMarker.profileHash === contractProfileHash;\n\n if (!storageHashMatches || !profileHashMatches) {\n previousHashes = {\n storageHash: existingMarker.storageHash,\n profileHash: existingMarker.profileHash,\n };\n const updated = await controlAdapter.updateMarker(\n mongoDriver,\n APP_SPACE_ID,\n existingMarker.storageHash,\n {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n },\n );\n if (!updated) {\n throw new Error('CAS conflict: marker was modified by another process during sign');\n }\n markerUpdated = true;\n }\n }\n\n let summary: string;\n if (markerCreated) {\n summary = 'Database signed (marker created)';\n } else if (markerUpdated) {\n summary = `Database signed (marker updated from ${previousHashes?.storageHash ?? 'unknown'})`;\n } else {\n summary = 'Database already signed with this contract';\n }\n\n return {\n ok: true,\n summary,\n contract: {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n },\n target: {\n expected: contract.target,\n actual: contract.target,\n },\n marker: {\n created: markerCreated,\n updated: markerUpdated,\n ...ifDefined('previous', previousHashes),\n },\n meta: {\n contractPath,\n ...ifDefined('configPath', configPath),\n },\n timings: {\n total: Date.now() - startTime,\n },\n };\n },\n\n async readMarker(options): Promise<ContractMarkerRecord | null> {\n return getControlAdapter().readMarker(asMongoDriver(options.driver), options.space);\n },\n\n async readAllMarkers(options): Promise<ReadonlyMap<string, ContractMarkerRecord>> {\n return getControlAdapter().readAllMarkers(asMongoDriver(options.driver));\n },\n\n async introspect(options): Promise<MongoSchemaIR> {\n return getControlAdapter().introspectSchema(asMongoDriver(options.driver));\n },\n\n toSchemaView(schema: MongoSchemaIR): CoreSchemaView {\n return mongoSchemaToView(schema);\n },\n\n toOperationPreview(operations: readonly MigrationPlanOperation[]): OperationPreview {\n return mongoOperationsToPreview(operations);\n },\n };\n}\n","import type {\n ControlFamilyDescriptor,\n ControlStack,\n} from '@prisma-next/framework-components/control';\nimport { mongoEmission } from '@prisma-next/mongo-emitter';\nimport { createMongoFamilyInstance, type MongoControlFamilyInstance } from './control-instance';\n\nclass MongoFamilyDescriptor\n implements ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance>\n{\n readonly kind = 'family' as const;\n readonly id = 'mongo';\n readonly familyId = 'mongo' as const;\n readonly version = '0.0.1';\n readonly emission = mongoEmission;\n\n create<TTargetId extends string>(\n stack: ControlStack<'mongo', TTargetId>,\n ): MongoControlFamilyInstance {\n return createMongoFamilyInstance(stack);\n }\n}\n\nexport const mongoFamilyDescriptor: ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance> =\n new MongoFamilyDescriptor();\n"],"mappings":";;;;;;;;AAcA,SAAS,cAAc,MAA4C;CAEjE,OAAO,KADS,KAAK,KAAK,MAAM,GAAG,KAAK,UAAU,EAAE,KAAK,EAAE,IAAI,KAAK,UAAU,EAAE,SAAS,GACvE,EAAE,KAAK,IAAI,EAAE;AACjC;AAEA,SAAS,cAAc,KAA6C;CAClE,MAAM,QAAkB,CAAC;CACzB,IAAI,IAAI,QAAQ,MAAM,KAAK,cAAc;CACzC,IAAI,IAAI,QAAQ,MAAM,KAAK,cAAc;CACzC,IAAI,IAAI,uBAAuB,KAAA,GAC7B,MAAM,KAAK,uBAAuB,IAAI,oBAAoB;CAC5D,IAAI,IAAI,MAAM,MAAM,KAAK,SAAS,KAAK,UAAU,IAAI,IAAI,GAAG;CAC5D,IAAI,IAAI,WAAW,MAAM,KAAK,cAAc,KAAK,UAAU,IAAI,SAAS,GAAG;CAC3E,IAAI,IAAI,SAAS,MAAM,KAAK,YAAY,KAAK,UAAU,IAAI,OAAO,GAAG;CACrE,IAAI,IAAI,kBAAkB,MAAM,KAAK,qBAAqB,KAAK,UAAU,IAAI,gBAAgB,GAAG;CAChG,IAAI,IAAI,mBACN,MAAM,KAAK,sBAAsB,KAAK,UAAU,IAAI,iBAAiB,GAAG;CAC1E,IAAI,IAAI,oBACN,MAAM,KAAK,uBAAuB,KAAK,UAAU,IAAI,kBAAkB,GAAG;CAC5E,IAAI,IAAI,yBACN,MAAM,KAAK,4BAA4B,KAAK,UAAU,IAAI,uBAAuB,GAAG;CACtF,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,KAAK,MAAM,KAAK,IAAI,EAAE;AAC/B;AAEA,SAAS,8BAA8B,KAAkD;CACvF,MAAM,QAAkB,CAAC;CACzB,IAAI,IAAI,QAAQ,MAAM,KAAK,cAAc;CACzC,IAAI,IAAI,SAAS,KAAA,GAAW,MAAM,KAAK,SAAS,IAAI,MAAM;CAC1D,IAAI,IAAI,QAAQ,KAAA,GAAW,MAAM,KAAK,QAAQ,IAAI,KAAK;CACvD,IAAI,IAAI,YAAY,MAAM,KAAK,eAAe,KAAK,UAAU,IAAI,UAAU,GAAG;CAC9E,IAAI,IAAI,WAAW,MAAM,KAAK,cAAc,KAAK,UAAU,IAAI,SAAS,GAAG;CAC3E,IAAI,IAAI,gBAAgB,MAAM,KAAK,mBAAmB,KAAK,UAAU,IAAI,cAAc,GAAG;CAC1F,IAAI,IAAI,WAAW,MAAM,KAAK,cAAc,KAAK,UAAU,IAAI,SAAS,GAAG;CAC3E,IAAI,IAAI,iBAAiB,MAAM,KAAK,oBAAoB,KAAK,UAAU,IAAI,eAAe,GAAG;CAC7F,IAAI,IAAI,kBAAkB,MAAM,KAAK,qBAAqB,KAAK,UAAU,IAAI,gBAAgB,GAAG;CAChG,IAAI,IAAI,8BACN,MAAM,KAAK,iCAAiC,KAAK,UAAU,IAAI,4BAA4B,GAAG;CAChG,IAAI,MAAM,WAAW,GAAG,OAAO,KAAA;CAC/B,OAAO,KAAK,MAAM,KAAK,IAAI,EAAE;AAC/B;AAEA,IAAM,2BAAN,MAAyE;CACvE,YAAY,KAAiC;EAC3C,MAAM,UAAU,cAAc,IAAI,IAAI;EACtC,MAAM,OAAO,cAAc,GAAG;EAC9B,OAAO,OACH,MAAM,IAAI,WAAW,eAAe,QAAQ,IAAI,KAAK,KACrD,MAAM,IAAI,WAAW,eAAe,QAAQ;CAClD;CAEA,UAAU,KAA+B;EACvC,OAAO,MAAM,IAAI,WAAW,aAAa,KAAK,UAAU,IAAI,IAAI,EAAE;CACpE;CAEA,iBAAiB,KAAsC;EACrD,MAAM,OAAO,8BAA8B,GAAG;EAC9C,OAAO,OACH,uBAAuB,KAAK,UAAU,IAAI,UAAU,EAAE,IAAI,KAAK,KAC/D,uBAAuB,KAAK,UAAU,IAAI,UAAU,EAAE;CAC5D;CAEA,eAAe,KAAoC;EACjD,OAAO,MAAM,IAAI,WAAW;CAC9B;CAEA,QAAQ,KAA6B;EACnC,MAAM,QAAkB,CAAC,YAAY,KAAK,UAAU,IAAI,UAAU,GAAG;EACrE,IAAI,IAAI,WAAW,MAAM,KAAK,cAAc,KAAK,UAAU,IAAI,SAAS,GAAG;EAC3E,IAAI,IAAI,iBAAiB,MAAM,KAAK,oBAAoB,KAAK,UAAU,IAAI,eAAe,GAAG;EAC7F,IAAI,IAAI,kBACN,MAAM,KAAK,qBAAqB,KAAK,UAAU,IAAI,gBAAgB,GAAG;EACxE,IAAI,IAAI,8BACN,MAAM,KACJ,iCAAiC,KAAK,UAAU,IAAI,4BAA4B,GAClF;EACF,OAAO,mBAAmB,MAAM,KAAK,IAAI,EAAE;CAC7C;AACF;AAEA,MAAM,YAAY,IAAI,yBAAyB;AAM/C,SAAgB,sBAAsB,YAAyD;CAC7F,MAAM,aAAuB,CAAC;CAC9B,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,YAAY;EAClB,IAAI,EAAE,aAAa,cAAc,CAAC,MAAM,QAAQ,UAAU,UAAU,GAClE;EAEF,KAAK,MAAM,QAAQ,UAAU,YAC3B,IAAI,KAAK,WAAW,OAAO,KAAK,QAAQ,WAAW,YACjD,WAAW,KAAK,KAAK,QAAQ,OAAO,SAAS,CAAC;CAGpD;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,yBACd,YACkB;CAClB,OAAO,EACL,YAAY,sBAAsB,UAAU,EAAE,KAAK,UAAU;EAC3D;EACA,UAAU;CACZ,EAAE,EACJ;AACF;;;AC7HA,SAAgB,kBAAkB,QAAuC;CACvE,MAAM,kBAAkB,OAAO,YAAY,KAAK,eAC9C,uBAAuB,WAAW,MAAM,UAAU,CACpD;CAEA,OAAO,EACL,MAAM,IAAI,eAAe;EACvB,MAAM;EACN,IAAI;EACJ,OAAO;EACP,GAAG,UAAU,YAAY,gBAAgB,SAAS,IAAI,kBAAkB,KAAA,CAAS;CACnF,CAAC,EACH;AACF;AAEA,SAAS,uBAAuB,MAAc,YAAmD;CAC/F,MAAM,WAA6B,CAAC;CAEpC,KAAK,MAAM,SAAS,WAAW,SAAS;EACtC,MAAM,cAAc,MAAM,KACvB,KAAK,MAAM;GACV,IAAI,EAAE,cAAc,GAAG,OAAO,EAAE;GAChC,IAAI,EAAE,cAAc,IAAI,OAAO,GAAG,EAAE,MAAM;GAC1C,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE;EACzB,CAAC,EACA,KAAK,IAAI;EACZ,MAAM,SAAS,MAAM,SAAS,iBAAiB;EAC/C,MAAM,UAAoB,CAAC;EAC3B,IAAI,MAAM,QAAQ,QAAQ,KAAK,QAAQ;EACvC,IAAI,MAAM,sBAAsB,MAAM,QAAQ,KAAK,QAAQ,MAAM,mBAAmB,EAAE;EACtF,IAAI,MAAM,yBAAyB,QAAQ,KAAK,SAAS;EACzD,MAAM,aAAa,QAAQ,SAAS,IAAI,KAAK,QAAQ,KAAK,IAAI,EAAE,KAAK;EAErE,SAAS,KACP,IAAI,eAAe;GACjB,MAAM;GACN,IAAI,SAAS,KAAK,GAAG,MAAM,KAAK,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,WAAW,EAAE,KAAK,GAAG;GAChF,OAAO,GAAG,OAAO,IAAI,YAAY,GAAG;GACpC,MAAM;IACJ,MAAM,MAAM;IACZ,QAAQ,MAAM;IACd,GAAG,UAAU,UAAU,MAAM,UAAU,KAAA,CAAS;IAChD,GAAG,UAAU,sBAAsB,MAAM,sBAAsB,KAAA,CAAS;IACxE,GAAG,UAAU,2BAA2B,MAAM,2BAA2B,KAAA,CAAS;GACpF;EACF,CAAC,CACH;CACF;CAEA,IAAI,WAAW,WAAW;EACxB,MAAM,oBAAsC,CAAC;EAC7C,MAAM,aAAa,WAAW,UAAU;EACxC,MAAM,aAAa,WAAW;EAG9B,MAAM,WAAW,IAAI,IAAK,WAAW,eAAwC,CAAC,CAAC;EAE/E,IAAI,YACF,KAAK,MAAM,CAAC,UAAU,YAAY,OAAO,QAAQ,UAAU,GAAG;GAC5D,MAAM,WAAY,QAAQ,eAA0B;GACpD,MAAM,SAAS,SAAS,IAAI,QAAQ,IAAI,gBAAgB;GACxD,kBAAkB,KAChB,IAAI,eAAe;IACjB,MAAM;IACN,IAAI,SAAS,KAAK,GAAG;IACrB,OAAO,GAAG,SAAS,IAAI,WAAW;GACpC,CAAC,CACH;EACF;EAGF,SAAS,KACP,IAAI,eAAe;GACjB,MAAM;GACN,IAAI,aAAa;GACjB,OAAO,qBAAqB,WAAW,UAAU,gBAAgB,YAAY,WAAW,UAAU,iBAAiB;GACnH,MAAM;IACJ,iBAAiB,WAAW,UAAU;IACtC,kBAAkB,WAAW,UAAU;IACvC,YAAY,WAAW,UAAU;GACnC;GACA,GAAG,UAAU,YAAY,kBAAkB,SAAS,IAAI,oBAAoB,KAAA,CAAS;EACvF,CAAC,CACH;CACF;CAEA,IAAI,WAAW,SAAS;EACtB,MAAM,OAAO,WAAW;EACxB,MAAM,YAAsB,CAAC;EAC7B,IAAI,KAAK,QAAQ,UAAU,KAAK,QAAQ;EACxC,IAAI,KAAK,YAAY,UAAU,KAAK,YAAY;EAChD,IAAI,KAAK,WAAW,UAAU,KAAK,WAAW;EAC9C,IAAI,KAAK,8BAA8B,UAAU,KAAK,8BAA8B;EACpF,IAAI,KAAK,gBAAgB,UAAU,KAAK,gBAAgB;EAExD,IAAI,UAAU,SAAS,GACrB,SAAS,KACP,IAAI,eAAe;GACjB,MAAM;GACN,IAAI,WAAW;GACf,OAAO,YAAY,UAAU,KAAK,IAAI,EAAE;GACxC,MAAM;IACJ,GAAG,UAAU,UAAU,KAAK,UAAU,KAAA,CAAS;IAC/C,GAAG,UAAU,cAAc,KAAK,cAAc,KAAA,CAAS;IACvD,GAAG,UAAU,aAAa,KAAK,aAAa,KAAA,CAAS;IACrD,GAAG,UACD,gCACA,KAAK,gCAAgC,KAAA,CACvC;IACA,GAAG,UAAU,kBAAkB,KAAK,kBAAkB,KAAA,CAAS;GACjE;EACF,CAAC,CACH;CAEJ;CAEA,OAAO,IAAI,eAAe;EACxB,MAAM;EACN,IAAI,cAAc;EAClB,OAAO,cAAc;EACrB,GAAG,UAAU,YAAY,SAAS,SAAS,IAAI,WAAW,KAAA,CAAS;CACrE,CAAC;AACH;;;AC7EA,SAAS,yBAAyB,cAAsC;CAOtE,OAAO,IAAI,wBAAwB,EAAE,oBAAoB,YAAY;AACvE;;;;;;;;;;;;;;;AAgBA,SAAS,yBAAyB,UAAkC;CAClE,OAAO;AACT;AAEA,SAAS,sBAAsB,OAAuD;CACpF,OACE,OAAO,UAAU,YACjB,UAAU,QACV,gBAAgB,SAChB,OAAQ,MAAkC,eAAe,cACzD,oBAAoB,SACpB,OAAQ,MAAsC,mBAAmB,cACjE,sBAAsB,SACtB,OAAQ,MAAwC,qBAAqB;AAEzE;AAEA,SAAS,kBAAkB,MAYF;CACvB,OAAO;EACL,IAAI,KAAK;EACT,GAAG,UAAU,QAAQ,KAAK,IAAI;EAC9B,SAAS,KAAK;EACd,UAAU;GACR,aAAa,KAAK;GAClB,GAAG,UAAU,eAAe,KAAK,mBAAmB;EACtD;EACA,GAAG,UACD,UACA,KAAK,SACD;GAAE,aAAa,KAAK,OAAO;GAAa,aAAa,KAAK,OAAO;EAAY,IAC7E,KAAA,CACN;EACA,QAAQ;GACN,UAAU,KAAK;GACf,GAAG,UAAU,UAAU,KAAK,cAAc;EAC5C;EACA,MAAM;GACJ,cAAc,KAAK;GACnB,GAAG,UAAU,cAAc,KAAK,UAAU;EAC5C;EACA,SAAS,EAAE,OAAO,KAAK,UAAU;CACnC;AACF;AAEA,SAAgB,0BAA0B,cAAwD;CAWhG,MAAM,aAAc,aAAa,kBAC/B,CAAC;CACH,KAAK,MAAM,aAAa,YACtB,IAAI,UAAU,eAAe;EAC3B,MAAM,EAAE,cAAc,YAAY,UAAU;EAC5C,gCAAgC;GAC9B,aAAa,UAAU;GACvB,QAAQ,aAAa;GACrB,cAAc,aAAa;GAC3B,SAAS,aAAa;GACtB,aAAa,QAAQ;GACrB,GAAG;EACL,CAAC;CACH;CAOF,MAAM,UAAU,aAAa;CAC7B,MAAM,0BAAwD;EAC5D,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,6DAA6D;EAE/E,MAAM,iBAAiB,QAAQ,OAAO,YAA8C;EACpF,IAAI,CAAC,sBAAsB,cAAc,GACvC,MAAM,IAAI,MACR,0GACF;EAEF,OAAO;CACT;CAMA,MAAM,iBACJ,WAC4C;CAE9C,OAAO;EACL,UAAU;EAEV,oBAAoB,cAAiC;GAKnD,OAAO,yBAAyB,YAAY;EAC9C;EAEA,MAAM,OAAO,SAAwC;GACnD,MAAM,EAAE,QAAQ,UAAU,aAAa,kBAAkB,cAAc,eAAe;GACtF,MAAM,YAAY,KAAK,IAAI;GAE3B,MAAM,WAAW,yBAAyB,WAAW;GAErD,MAAM,sBAAsB,SAAS,QAAQ;GAC7C,MAAM,sBAAsB,SAAS;GACrC,MAAM,iBAAiB,SAAS;GAEhC,MAAM,WAAW;IACf;IACA;IACA;IACA;IACA,GAAG,UAAU,cAAc,UAAU;GACvC;GAEA,IAAI,mBAAmB,kBACrB,OAAO,kBAAkB;IACvB,GAAG;IACH,IAAI;IACJ,MAAM;IACN,SAAS;IACT,gBAAgB;IAChB,WAAW,KAAK,IAAI,IAAI;GAC1B,CAAC;GAGH,MAAM,SAAS,MAAM,kBAAkB,EAAE,WAAW,cAAc,MAAM,GAAG,YAAY;GAEvF,IAAI,CAAC,QACH,OAAO,kBAAkB;IACvB,GAAG;IACH,IAAI;IACJ,MAAM;IACN,SAAS;IACT,WAAW,KAAK,IAAI,IAAI;GAC1B,CAAC;GAGH,IAAI,OAAO,gBAAgB,qBACzB,OAAO,kBAAkB;IACvB,GAAG;IACH,IAAI;IACJ,MAAM;IACN,SAAS;IACT;IACA,WAAW,KAAK,IAAI,IAAI;GAC1B,CAAC;GAGH,IAAI,uBAAuB,OAAO,gBAAgB,qBAChD,OAAO,kBAAkB;IACvB,GAAG;IACH,IAAI;IACJ,MAAM;IACN,SAAS;IACT;IACA,WAAW,KAAK,IAAI,IAAI;GAC1B,CAAC;GAGH,OAAO,kBAAkB;IACvB,GAAG;IACH,IAAI;IACJ,SAAS;IACT;IACA,WAAW,KAAK,IAAI,IAAI;GAC1B,CAAC;EACH;EAEA,aAAa,SAKkB;GAE7B,OAAO,kBAAkB;IACvB,UAFe,yBAAyB,QAAQ,QAEzC;IACP,QAAQ,QAAQ;IAChB,QAAQ,QAAQ;IAChB,qBAAqB,QAAQ;GAC/B,CAAC;EACH;EAEA,MAAM,KAAK,SAAsC;GAC/C,MAAM,EAAE,QAAQ,UAAU,aAAa,cAAc,eAAe;GACpE,MAAM,YAAY,KAAK,IAAI;GAE3B,MAAM,WAAW,yBAAyB,WAAW;GAErD,MAAM,sBAAsB,SAAS,QAAQ;GAC7C,MAAM,sBAAsB,SAAS;GAErC,MAAM,iBAAiB,kBAAkB;GACzC,MAAM,cAAc,cAAc,MAAM;GAExC,MAAM,iBAAiB,MAAM,eAAe,WAAW,aAAa,YAAY;GAEhF,IAAI,gBAAgB;GACpB,IAAI,gBAAgB;GACpB,IAAI;GAEJ,IAAI,CAAC,gBAAgB;IACnB,MAAM,eAAe,WAAW,aAAa,cAAc;KACzD,aAAa;KACb,aAAa;IACf,CAAC;IACD,gBAAgB;GAClB,OAAO;IACL,MAAM,qBAAqB,eAAe,gBAAgB;IAC1D,MAAM,qBAAqB,eAAe,gBAAgB;IAE1D,IAAI,CAAC,sBAAsB,CAAC,oBAAoB;KAC9C,iBAAiB;MACf,aAAa,eAAe;MAC5B,aAAa,eAAe;KAC9B;KAUA,IAAI,CAAC,MATiB,eAAe,aACnC,aACA,cACA,eAAe,aACf;MACE,aAAa;MACb,aAAa;KACf,CACF,GAEE,MAAM,IAAI,MAAM,kEAAkE;KAEpF,gBAAgB;IAClB;GACF;GAEA,IAAI;GACJ,IAAI,eACF,UAAU;QACL,IAAI,eACT,UAAU,wCAAwC,gBAAgB,eAAe,UAAU;QAE3F,UAAU;GAGZ,OAAO;IACL,IAAI;IACJ;IACA,UAAU;KACR,aAAa;KACb,aAAa;IACf;IACA,QAAQ;KACN,UAAU,SAAS;KACnB,QAAQ,SAAS;IACnB;IACA,QAAQ;KACN,SAAS;KACT,SAAS;KACT,GAAG,UAAU,YAAY,cAAc;IACzC;IACA,MAAM;KACJ;KACA,GAAG,UAAU,cAAc,UAAU;IACvC;IACA,SAAS,EACP,OAAO,KAAK,IAAI,IAAI,UACtB;GACF;EACF;EAEA,MAAM,WAAW,SAA+C;GAC9D,OAAO,kBAAkB,EAAE,WAAW,cAAc,QAAQ,MAAM,GAAG,QAAQ,KAAK;EACpF;EAEA,MAAM,eAAe,SAA6D;GAChF,OAAO,kBAAkB,EAAE,eAAe,cAAc,QAAQ,MAAM,CAAC;EACzE;EAEA,MAAM,WAAW,SAAiC;GAChD,OAAO,kBAAkB,EAAE,iBAAiB,cAAc,QAAQ,MAAM,CAAC;EAC3E;EAEA,aAAa,QAAuC;GAClD,OAAO,kBAAkB,MAAM;EACjC;EAEA,mBAAmB,YAAiE;GAClF,OAAO,yBAAyB,UAAU;EAC5C;CACF;AACF;;;AC1XA,IAAM,wBAAN,MAEA;CACE,OAAgB;CAChB,KAAc;CACd,WAAoB;CACpB,UAAmB;CACnB,WAAoB;CAEpB,OACE,OAC4B;EAC5B,OAAO,0BAA0B,KAAK;CACxC;AACF;AAEA,MAAa,wBACX,IAAI,sBAAsB"}
package/dist/ir.d.mts CHANGED
@@ -2,6 +2,7 @@ import { ContractSerializer, SchemaIssue, SchemaVerifier, SchemaVerifyOptions, S
2
2
  import { MongoContract, MongoStorage } from "@prisma-next/mongo-contract";
3
3
  import { Type } from "arktype";
4
4
  import { Namespace } from "@prisma-next/framework-components/ir";
5
+ import * as _$_prisma_next_contract_hashing0 from "@prisma-next/contract/hashing";
5
6
  import { JsonObject } from "@prisma-next/utils/json";
6
7
 
7
8
  //#region src/core/ir/mongo-contract-serializer-base.d.ts
@@ -31,6 +32,13 @@ declare abstract class MongoContractSerializerBase<TContract> implements Contrac
31
32
  constructor(validatorFragments?: ReadonlyMap<string, Type<unknown>>);
32
33
  deserializeContract<T extends TContract = TContract>(json: unknown): T;
33
34
  serializeContract(contract: TContract): JsonObject;
35
+ /**
36
+ * Preserve empty `collections` maps and per-collection payloads. Mongo
37
+ * collections legitimately serialize empty (a declared collection with no
38
+ * schema is valid); SQL tables never do — that asymmetry lives here rather
39
+ * than in the family-agnostic canonicalizer.
40
+ */
41
+ shouldPreserveEmpty: _$_prisma_next_contract_hashing0.PreserveEmptyPredicate;
34
42
  /**
35
43
  * Family-shared structural validation: parse against the Mongo
36
44
  * contract arktype schema, then run framework-shared domain + Mongo
package/dist/ir.d.mts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ir.d.mts","names":[],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts","../src/core/ir/mongo-schema-verifier-base.ts"],"mappings":";;;;;;;;;;AAkCA;;;;;;;;;;;;;;;;;;uBAAsB,2BAAA,uBACT,kBAAA,CAAmB,SAAA;EAAA,iBAEb,cAAA;cAEL,kBAAA,GAAqB,WAAA,SAAoB,IAAA;EAUrD,mBAAA,WAA8B,SAAA,GAAY,SAAA,CAAA,CAAW,IAAA,YAAgB,CAAA;EAKrE,iBAAA,CAAkB,QAAA,EAAU,SAAA,GAAY,UAAA;;;;;;;;;;;;;;;;;YAuB9B,2BAAA,CAA4B,IAAA,YAAgB,aAAA;EAmC5C;;;;;;EAAA,UAAA,oBAAA,CAAqB,QAAA,EAAU,aAAA,GAAgB,aAAA;EAsCa;;AAAS;;;;ACzIjF;;EDyIwE,mBAAnD,uBAAA,CAAwB,SAAA,EAAW,aAAA,GAAgB,SAAA;AAAA;;;;;;;;AApHxE;;;;;cCrBa,uBAAA,SAAgC,2BAAA,CAA4B,aAAA;EAAA,UAC7D,uBAAA,CAAwB,SAAA,EAAW,aAAA,GAAgB,aAAA;AAAA;;;;;;;ADoB/D;;;;;;;;;;;;;;;;;uBEJsB,uBAAA;EAAA,SACS,OAAA,EAAS,YAAA;AAAA,uBAE3B,cAAA,CAAe,SAAA,EAAW,OAAA;EAErC,YAAA,CAAa,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,IAAW,kBAAA;EAAA,UAOtD,uBAAA,CACR,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,WAAA;EFPK;;;;;;;;EAAA,mBEkCE,eAAA,CAAgB,OAAA;IAAA,SACxB,QAAA,EAAU,SAAA;IAAA,SACV,MAAA,EAAQ,OAAA;IAAA,SACR,WAAA;IAAA,SACA,SAAA,EAAW,SAAA;EAAA,aACT,WAAA;EFtB2B;;;;;EAAA,mBE6BrB,sBAAA,CACjB,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,WAAA;AAAA"}
1
+ {"version":3,"file":"ir.d.mts","names":[],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts","../src/core/ir/mongo-schema-verifier-base.ts"],"mappings":";;;;;;;;;;;;AAmCA;;;;;;;;;;;;;;;;;uBAAsB,2BAAA,uBACT,kBAAA,CAAmB,SAAA;EAAA,iBAEb,cAAA;cAEL,kBAAA,GAAqB,WAAA,SAAoB,IAAA;EAUrD,mBAAA,WAA8B,SAAA,GAAY,SAAA,CAAA,CAAW,IAAA,YAAgB,CAAA;EAKrE,iBAAA,CAAkB,QAAA,EAAU,SAAA,GAAY,UAAA;EAnBV;;;;;;EAgC9B,mBAAA,EAbkD,gCAAA,CAa/B,sBAAA;EAlBC;;;;;;;;;;;;;;;;EAAA,UAoCV,2BAAA,CAA4B,IAAA,YAAgB,aAAA;EAmCG;;;;;;EAAA,UAA/C,oBAAA,CAAqB,QAAA,EAAU,aAAA,GAAgB,aAAA;;;;AC5G3D;;;;;qBDkJqB,uBAAA,CAAwB,SAAA,EAAW,aAAA,GAAgB,SAAA;AAAA;;;;;;;;;AA5HxE;;;;cCtBa,uBAAA,SAAgC,2BAAA,CAA4B,aAAA;EAAA,UAC7D,uBAAA,CAAwB,SAAA,EAAW,aAAA,GAAgB,aAAA;AAAA;;;;;;;;ADqB/D;;;;;;;;;;;;;;;;uBELsB,uBAAA;EAAA,SACS,OAAA,EAAS,YAAA;AAAA,uBAE3B,cAAA,CAAe,SAAA,EAAW,OAAA;EAErC,YAAA,CAAa,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,IAAW,kBAAA;EAAA,UAOtD,uBAAA,CACR,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,WAAA;EFRD;;;;;;;;EAAA,mBEmCQ,eAAA,CAAgB,OAAA;IAAA,SACxB,QAAA,EAAU,SAAA;IAAA,SACV,MAAA,EAAQ,OAAA;IAAA,SACR,WAAA;IAAA,SACA,SAAA,EAAW,SAAA;EAAA,aACT,WAAA;EFrBe;;;;;EAAA,mBE4BT,sBAAA,CACjB,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,WAAA;AAAA"}
package/dist/ir.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as MongoContractSerializerBase, t as MongoContractSerializer } from "./mongo-contract-serializer-qnignx7Y.mjs";
1
+ import { n as MongoContractSerializerBase, t as MongoContractSerializer } from "./mongo-contract-serializer-DAoKJxiP.mjs";
2
2
  //#region src/core/ir/mongo-schema-verifier-base.ts
3
3
  /**
4
4
  * Mongo family `SchemaVerifier` abstract base. Commits the Mongo family
@@ -1,3 +1,4 @@
1
+ import { mongoContractCanonicalizationHooks } from "@prisma-next/mongo-contract/canonicalization-hooks";
1
2
  import { validateContractDomain } from "@prisma-next/contract/validate-domain";
2
3
  import { MongoCollection, MongoContractSchema, createMongoContractSchema, validateMongoStorage } from "@prisma-next/mongo-contract";
3
4
  import { type } from "arktype";
@@ -36,6 +37,13 @@ var MongoContractSerializerBase = class {
36
37
  return contract;
37
38
  }
38
39
  /**
40
+ * Preserve empty `collections` maps and per-collection payloads. Mongo
41
+ * collections legitimately serialize empty (a declared collection with no
42
+ * schema is valid); SQL tables never do — that asymmetry lives here rather
43
+ * than in the family-agnostic canonicalizer.
44
+ */
45
+ shouldPreserveEmpty = mongoContractCanonicalizationHooks.shouldPreserveEmpty;
46
+ /**
39
47
  * Family-shared structural validation: parse against the Mongo
40
48
  * contract arktype schema, then run framework-shared domain + Mongo
41
49
  * family storage checks, then hydrate the validated tree into Mongo
@@ -106,4 +114,4 @@ var MongoContractSerializer = class extends MongoContractSerializerBase {
106
114
  //#endregion
107
115
  export { MongoContractSerializerBase as n, MongoContractSerializer as t };
108
116
 
109
- //# sourceMappingURL=mongo-contract-serializer-qnignx7Y.mjs.map
117
+ //# sourceMappingURL=mongo-contract-serializer-DAoKJxiP.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongo-contract-serializer-DAoKJxiP.mjs","names":["arktypeType"],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts"],"sourcesContent":["import { validateContractDomain } from '@prisma-next/contract/validate-domain';\nimport type { ContractSerializer } from '@prisma-next/framework-components/control';\nimport {\n createMongoContractSchema,\n MongoCollection,\n type MongoCollectionInput,\n type MongoContract,\n MongoContractSchema,\n validateMongoStorage,\n} from '@prisma-next/mongo-contract';\nimport { mongoContractCanonicalizationHooks } from '@prisma-next/mongo-contract/canonicalization-hooks';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { type as arktypeType, type Type } from 'arktype';\n\n/**\n * Mongo family `ContractSerializer` abstract base. Owns the family-shared\n * deserialization pipeline:\n *\n * 1. Structural validation against the Mongo contract arktype schema\n * (`MongoContractSchema`).\n * 2. Framework-shared domain validation (`validateContractDomain`).\n * 3. Family-shared storage validation (`validateMongoStorage`).\n *\n * The validated value is handed to the target via the\n * `constructTargetContract` hook, which wraps the plain-JSON shape in\n * the family-layer `MongoStorage` class instance (carrying the\n * target-supplied `namespaces` map). Targets that need to add\n * structural checks beyond the family default can override\n * `parseMongoContractStructure`.\n *\n * Default `serializeContract` is identity over the contract — Mongo\n * target classes carry JSON-clean fields by construction, so the value\n * can be `JSON.stringify`'d directly. Targets that need on-the-way-out\n * canonicalization override `serializeContract`.\n */\nexport abstract class MongoContractSerializerBase<TContract>\n implements ContractSerializer<TContract>\n{\n private readonly contractSchema: Type<unknown> | undefined;\n\n constructor(validatorFragments?: ReadonlyMap<string, Type<unknown>>) {\n // Mirrors the SQL base: only build a fragments-aware schema when\n // pack contributions exist; otherwise the cached module-level\n // default in `contract-schema.ts` covers the validation path.\n this.contractSchema =\n validatorFragments !== undefined && validatorFragments.size > 0\n ? createMongoContractSchema(validatorFragments)\n : undefined;\n }\n\n deserializeContract<T extends TContract = TContract>(json: unknown): T {\n const validated = this.parseMongoContractStructure(json);\n return this.constructTargetContract(validated) as T;\n }\n\n serializeContract(contract: TContract): JsonObject {\n // Mongo contract class fields are JSON-clean by construction; the\n // cast asserts that. Targets that need to canonicalize on the way\n // out override this method.\n return contract as unknown as JsonObject;\n }\n\n /**\n * Preserve empty `collections` maps and per-collection payloads. Mongo\n * collections legitimately serialize empty (a declared collection with no\n * schema is valid); SQL tables never do — that asymmetry lives here rather\n * than in the family-agnostic canonicalizer.\n */\n shouldPreserveEmpty = mongoContractCanonicalizationHooks.shouldPreserveEmpty;\n\n /**\n * Family-shared structural validation: parse against the Mongo\n * contract arktype schema, then run framework-shared domain + Mongo\n * family storage checks, then hydrate the validated tree into Mongo\n * Contract IR class instances. Targets can override to add\n * target-specific structural checks; most targets accept the family\n * default.\n *\n * The returned `MongoContract` carries class instances under\n * `storage.namespaces[namespaceId].collections[collectionName]` (each value is a\n * `MongoCollection`, with nested `MongoIndex` / `MongoValidator` /\n * `MongoCollectionOptions` constructed by the `MongoCollection` constructor).\n * The rest of the contract envelope (models, valueObjects, capabilities, …)\n * remains in plain-JSON form; those IR layers are handled by sibling\n * subsystems and don't sit behind this SPI.\n */\n protected parseMongoContractStructure(json: unknown): MongoContract {\n const schema = this.contractSchema ?? MongoContractSchema;\n const parsed = schema(json);\n if (parsed instanceof arktypeType.errors) {\n throw new Error(`Contract structural validation failed: ${parsed.summary}`);\n }\n\n // arktype's `infer`d type for `MongoContractSchema` is structurally\n // equivalent to `MongoContract` (both describe the same on-disk JSON\n // envelope) but not nominally so: the arktype DSL produces a type whose\n // optional/readonly profile, narrowed string-literal positions, and\n // utility-type wrappings (`Type.infer`, `Out`, …) differ from the\n // hand-authored `MongoContract<S, M>` generic surface. The schema and\n // the type are kept in lockstep by the round-trip fixtures under\n // `test/validate.test.ts`. The hydration walk below additionally\n // re-shapes `storage.namespaces.*.collections` from plain data into IR-class\n // instances, so the `MongoContract` returned here carries class identity\n // under those collections maps (and transitively under `indexes` / `validator`\n // / `options`).\n const validatedShape = parsed as unknown as MongoContract;\n\n const hydratedContract = this.hydrateMongoContract(validatedShape);\n\n validateContractDomain(hydratedContract);\n validateMongoStorage(hydratedContract);\n\n return hydratedContract;\n }\n\n /**\n * Walk a structurally-validated Mongo contract and convert each\n * `storage.namespaces[nsId].collections[collectionName]` entry from plain\n * data into `MongoCollection` IR-class instances. Idempotent: already-class\n * instances pass through unchanged.\n */\n protected hydrateMongoContract(contract: MongoContract): MongoContract {\n const rawNamespaces = contract.storage.namespaces;\n const hydratedNamespaces = Object.fromEntries(\n Object.entries(rawNamespaces).map(([nsId, nsEnvelope]) => {\n const rawCollections = nsEnvelope.collections ?? {};\n const hydratedCollections = Object.fromEntries(\n Object.entries(rawCollections).map(([name, raw]) => [\n name,\n raw instanceof MongoCollection ? raw : new MongoCollection(raw as MongoCollectionInput),\n ]),\n );\n return [\n nsId,\n {\n ...nsEnvelope,\n id: nsEnvelope.id,\n collections: hydratedCollections,\n },\n ];\n }),\n );\n return {\n ...contract,\n storage: {\n ...contract.storage,\n namespaces: hydratedNamespaces,\n },\n };\n }\n\n /**\n * Target-specific class construction from the validated structural\n * data. The target wraps the contract envelope in the family-layer\n * `MongoStorage` class instance, supplying the `namespaces` map\n * (target concretions like `MongoTargetUnboundDatabase`). The\n * leaf collection / index shapes are already family-layer IR-class\n * instances after the hydration walk above.\n */\n protected abstract constructTargetContract(validated: MongoContract): TContract;\n}\n","import type { MongoContract } from '@prisma-next/mongo-contract';\nimport { MongoContractSerializerBase } from './mongo-contract-serializer-base';\n\n/**\n * Default Mongo family `ContractSerializer` concretion. Inherits the\n * Mongo-shared deserialization pipeline (structural validation +\n * collection-level hydration) and falls through `constructTargetContract`\n * with the validated `MongoContract` shape. Family-level call sites\n * (family-instance methods, family-layer tests that don't reach into\n * a target descriptor) instantiate this directly; targets with their\n * own storage concretion (`target-mongo`'s `MongoTargetContractSerializer`)\n * override `constructTargetContract` to wrap the storage shape.\n */\nexport class MongoContractSerializer extends MongoContractSerializerBase<MongoContract> {\n protected constructTargetContract(validated: MongoContract): MongoContract {\n return validated;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,IAAsB,8BAAtB,MAEA;CACE;CAEA,YAAY,oBAAyD;EAInE,KAAK,iBACH,uBAAuB,KAAA,KAAa,mBAAmB,OAAO,IAC1D,0BAA0B,kBAAkB,IAC5C,KAAA;CACR;CAEA,oBAAqD,MAAkB;EACrE,MAAM,YAAY,KAAK,4BAA4B,IAAI;EACvD,OAAO,KAAK,wBAAwB,SAAS;CAC/C;CAEA,kBAAkB,UAAiC;EAIjD,OAAO;CACT;;;;;;;CAQA,sBAAsB,mCAAmC;;;;;;;;;;;;;;;;;CAkBzD,4BAAsC,MAA8B;EAElE,MAAM,UADS,KAAK,kBAAkB,qBAChB,IAAI;EAC1B,IAAI,kBAAkBA,KAAY,QAChC,MAAM,IAAI,MAAM,0CAA0C,OAAO,SAAS;EAe5E,MAAM,iBAAiB;EAEvB,MAAM,mBAAmB,KAAK,qBAAqB,cAAc;EAEjE,uBAAuB,gBAAgB;EACvC,qBAAqB,gBAAgB;EAErC,OAAO;CACT;;;;;;;CAQA,qBAA+B,UAAwC;EACrE,MAAM,gBAAgB,SAAS,QAAQ;EACvC,MAAM,qBAAqB,OAAO,YAChC,OAAO,QAAQ,aAAa,EAAE,KAAK,CAAC,MAAM,gBAAgB;GACxD,MAAM,iBAAiB,WAAW,eAAe,CAAC;GAClD,MAAM,sBAAsB,OAAO,YACjC,OAAO,QAAQ,cAAc,EAAE,KAAK,CAAC,MAAM,SAAS,CAClD,MACA,eAAe,kBAAkB,MAAM,IAAI,gBAAgB,GAA2B,CACxF,CAAC,CACH;GACA,OAAO,CACL,MACA;IACE,GAAG;IACH,IAAI,WAAW;IACf,aAAa;GACf,CACF;EACF,CAAC,CACH;EACA,OAAO;GACL,GAAG;GACH,SAAS;IACP,GAAG,SAAS;IACZ,YAAY;GACd;EACF;CACF;AAWF;;;;;;;;;;;;;ACnJA,IAAa,0BAAb,cAA6C,4BAA2C;CACtF,wBAAkC,WAAyC;EACzE,OAAO;CACT;AACF"}
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
1
  {
2
2
  "name": "@prisma-next/family-mongo",
3
- "version": "0.11.0-dev.49",
3
+ "version": "0.11.0-dev.50",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Mongo family descriptor for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/contract": "0.11.0-dev.49",
10
- "@prisma-next/emitter": "0.11.0-dev.49",
11
- "@prisma-next/errors": "0.11.0-dev.49",
12
- "@prisma-next/framework-components": "0.11.0-dev.49",
13
- "@prisma-next/migration-tools": "0.11.0-dev.49",
14
- "@prisma-next/mongo-contract": "0.11.0-dev.49",
15
- "@prisma-next/mongo-emitter": "0.11.0-dev.49",
16
- "@prisma-next/mongo-query-ast": "0.11.0-dev.49",
17
- "@prisma-next/mongo-schema-ir": "0.11.0-dev.49",
18
- "@prisma-next/utils": "0.11.0-dev.49",
9
+ "@prisma-next/contract": "0.11.0-dev.50",
10
+ "@prisma-next/emitter": "0.11.0-dev.50",
11
+ "@prisma-next/errors": "0.11.0-dev.50",
12
+ "@prisma-next/framework-components": "0.11.0-dev.50",
13
+ "@prisma-next/migration-tools": "0.11.0-dev.50",
14
+ "@prisma-next/mongo-contract": "0.11.0-dev.50",
15
+ "@prisma-next/mongo-emitter": "0.11.0-dev.50",
16
+ "@prisma-next/mongo-query-ast": "0.11.0-dev.50",
17
+ "@prisma-next/mongo-schema-ir": "0.11.0-dev.50",
18
+ "@prisma-next/utils": "0.11.0-dev.50",
19
19
  "arktype": "^2.2.0",
20
20
  "pathe": "^2.0.3"
21
21
  },
22
22
  "devDependencies": {
23
- "@prisma-next/mongo-contract-ts": "0.11.0-dev.49",
24
- "@prisma-next/test-utils": "0.11.0-dev.49",
25
- "@prisma-next/tsconfig": "0.11.0-dev.49",
26
- "@prisma-next/tsdown": "0.11.0-dev.49",
23
+ "@prisma-next/mongo-contract-ts": "0.11.0-dev.50",
24
+ "@prisma-next/test-utils": "0.11.0-dev.50",
25
+ "@prisma-next/tsconfig": "0.11.0-dev.50",
26
+ "@prisma-next/tsdown": "0.11.0-dev.50",
27
27
  "tsdown": "0.22.0",
28
28
  "typescript": "5.9.3",
29
29
  "vitest": "4.1.6"
@@ -21,6 +21,7 @@ import {
21
21
  } from '@prisma-next/framework-components/control';
22
22
  import { assertDescriptorSelfConsistency } from '@prisma-next/migration-tools/spaces';
23
23
  import type { MongoContract } from '@prisma-next/mongo-contract';
24
+ import { mongoContractCanonicalizationHooks } from '@prisma-next/mongo-contract/canonicalization-hooks';
24
25
  import type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';
25
26
  import { ifDefined } from '@prisma-next/utils/defined';
26
27
  import type { MongoControlAdapter, MongoControlAdapterDescriptor } from './control-adapter';
@@ -149,6 +150,7 @@ export function createMongoFamilyInstance(controlStack: ControlStack): MongoCont
149
150
  targetFamily: contractJson.targetFamily,
150
151
  storage: contractJson.storage,
151
152
  headRefHash: headRef.hash,
153
+ ...mongoContractCanonicalizationHooks,
152
154
  });
153
155
  }
154
156
  }
@@ -8,6 +8,7 @@ import {
8
8
  MongoContractSchema,
9
9
  validateMongoStorage,
10
10
  } from '@prisma-next/mongo-contract';
11
+ import { mongoContractCanonicalizationHooks } from '@prisma-next/mongo-contract/canonicalization-hooks';
11
12
  import type { JsonObject } from '@prisma-next/utils/json';
12
13
  import { type as arktypeType, type Type } from 'arktype';
13
14
 
@@ -59,6 +60,14 @@ export abstract class MongoContractSerializerBase<TContract>
59
60
  return contract as unknown as JsonObject;
60
61
  }
61
62
 
63
+ /**
64
+ * Preserve empty `collections` maps and per-collection payloads. Mongo
65
+ * collections legitimately serialize empty (a declared collection with no
66
+ * schema is valid); SQL tables never do — that asymmetry lives here rather
67
+ * than in the family-agnostic canonicalizer.
68
+ */
69
+ shouldPreserveEmpty = mongoContractCanonicalizationHooks.shouldPreserveEmpty;
70
+
62
71
  /**
63
72
  * Family-shared structural validation: parse against the Mongo
64
73
  * contract arktype schema, then run framework-shared domain + Mongo
@@ -1 +0,0 @@
1
- {"version":3,"file":"mongo-contract-serializer-qnignx7Y.mjs","names":["arktypeType"],"sources":["../src/core/ir/mongo-contract-serializer-base.ts","../src/core/ir/mongo-contract-serializer.ts"],"sourcesContent":["import { validateContractDomain } from '@prisma-next/contract/validate-domain';\nimport type { ContractSerializer } from '@prisma-next/framework-components/control';\nimport {\n createMongoContractSchema,\n MongoCollection,\n type MongoCollectionInput,\n type MongoContract,\n MongoContractSchema,\n validateMongoStorage,\n} from '@prisma-next/mongo-contract';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { type as arktypeType, type Type } from 'arktype';\n\n/**\n * Mongo family `ContractSerializer` abstract base. Owns the family-shared\n * deserialization pipeline:\n *\n * 1. Structural validation against the Mongo contract arktype schema\n * (`MongoContractSchema`).\n * 2. Framework-shared domain validation (`validateContractDomain`).\n * 3. Family-shared storage validation (`validateMongoStorage`).\n *\n * The validated value is handed to the target via the\n * `constructTargetContract` hook, which wraps the plain-JSON shape in\n * the family-layer `MongoStorage` class instance (carrying the\n * target-supplied `namespaces` map). Targets that need to add\n * structural checks beyond the family default can override\n * `parseMongoContractStructure`.\n *\n * Default `serializeContract` is identity over the contract — Mongo\n * target classes carry JSON-clean fields by construction, so the value\n * can be `JSON.stringify`'d directly. Targets that need on-the-way-out\n * canonicalization override `serializeContract`.\n */\nexport abstract class MongoContractSerializerBase<TContract>\n implements ContractSerializer<TContract>\n{\n private readonly contractSchema: Type<unknown> | undefined;\n\n constructor(validatorFragments?: ReadonlyMap<string, Type<unknown>>) {\n // Mirrors the SQL base: only build a fragments-aware schema when\n // pack contributions exist; otherwise the cached module-level\n // default in `contract-schema.ts` covers the validation path.\n this.contractSchema =\n validatorFragments !== undefined && validatorFragments.size > 0\n ? createMongoContractSchema(validatorFragments)\n : undefined;\n }\n\n deserializeContract<T extends TContract = TContract>(json: unknown): T {\n const validated = this.parseMongoContractStructure(json);\n return this.constructTargetContract(validated) as T;\n }\n\n serializeContract(contract: TContract): JsonObject {\n // Mongo contract class fields are JSON-clean by construction; the\n // cast asserts that. Targets that need to canonicalize on the way\n // out override this method.\n return contract as unknown as JsonObject;\n }\n\n /**\n * Family-shared structural validation: parse against the Mongo\n * contract arktype schema, then run framework-shared domain + Mongo\n * family storage checks, then hydrate the validated tree into Mongo\n * Contract IR class instances. Targets can override to add\n * target-specific structural checks; most targets accept the family\n * default.\n *\n * The returned `MongoContract` carries class instances under\n * `storage.namespaces[namespaceId].collections[collectionName]` (each value is a\n * `MongoCollection`, with nested `MongoIndex` / `MongoValidator` /\n * `MongoCollectionOptions` constructed by the `MongoCollection` constructor).\n * The rest of the contract envelope (models, valueObjects, capabilities, …)\n * remains in plain-JSON form; those IR layers are handled by sibling\n * subsystems and don't sit behind this SPI.\n */\n protected parseMongoContractStructure(json: unknown): MongoContract {\n const schema = this.contractSchema ?? MongoContractSchema;\n const parsed = schema(json);\n if (parsed instanceof arktypeType.errors) {\n throw new Error(`Contract structural validation failed: ${parsed.summary}`);\n }\n\n // arktype's `infer`d type for `MongoContractSchema` is structurally\n // equivalent to `MongoContract` (both describe the same on-disk JSON\n // envelope) but not nominally so: the arktype DSL produces a type whose\n // optional/readonly profile, narrowed string-literal positions, and\n // utility-type wrappings (`Type.infer`, `Out`, …) differ from the\n // hand-authored `MongoContract<S, M>` generic surface. The schema and\n // the type are kept in lockstep by the round-trip fixtures under\n // `test/validate.test.ts`. The hydration walk below additionally\n // re-shapes `storage.namespaces.*.collections` from plain data into IR-class\n // instances, so the `MongoContract` returned here carries class identity\n // under those collections maps (and transitively under `indexes` / `validator`\n // / `options`).\n const validatedShape = parsed as unknown as MongoContract;\n\n const hydratedContract = this.hydrateMongoContract(validatedShape);\n\n validateContractDomain(hydratedContract);\n validateMongoStorage(hydratedContract);\n\n return hydratedContract;\n }\n\n /**\n * Walk a structurally-validated Mongo contract and convert each\n * `storage.namespaces[nsId].collections[collectionName]` entry from plain\n * data into `MongoCollection` IR-class instances. Idempotent: already-class\n * instances pass through unchanged.\n */\n protected hydrateMongoContract(contract: MongoContract): MongoContract {\n const rawNamespaces = contract.storage.namespaces;\n const hydratedNamespaces = Object.fromEntries(\n Object.entries(rawNamespaces).map(([nsId, nsEnvelope]) => {\n const rawCollections = nsEnvelope.collections ?? {};\n const hydratedCollections = Object.fromEntries(\n Object.entries(rawCollections).map(([name, raw]) => [\n name,\n raw instanceof MongoCollection ? raw : new MongoCollection(raw as MongoCollectionInput),\n ]),\n );\n return [\n nsId,\n {\n ...nsEnvelope,\n id: nsEnvelope.id,\n collections: hydratedCollections,\n },\n ];\n }),\n );\n return {\n ...contract,\n storage: {\n ...contract.storage,\n namespaces: hydratedNamespaces,\n },\n };\n }\n\n /**\n * Target-specific class construction from the validated structural\n * data. The target wraps the contract envelope in the family-layer\n * `MongoStorage` class instance, supplying the `namespaces` map\n * (target concretions like `MongoTargetUnboundDatabase`). The\n * leaf collection / index shapes are already family-layer IR-class\n * instances after the hydration walk above.\n */\n protected abstract constructTargetContract(validated: MongoContract): TContract;\n}\n","import type { MongoContract } from '@prisma-next/mongo-contract';\nimport { MongoContractSerializerBase } from './mongo-contract-serializer-base';\n\n/**\n * Default Mongo family `ContractSerializer` concretion. Inherits the\n * Mongo-shared deserialization pipeline (structural validation +\n * collection-level hydration) and falls through `constructTargetContract`\n * with the validated `MongoContract` shape. Family-level call sites\n * (family-instance methods, family-layer tests that don't reach into\n * a target descriptor) instantiate this directly; targets with their\n * own storage concretion (`target-mongo`'s `MongoTargetContractSerializer`)\n * override `constructTargetContract` to wrap the storage shape.\n */\nexport class MongoContractSerializer extends MongoContractSerializerBase<MongoContract> {\n protected constructTargetContract(validated: MongoContract): MongoContract {\n return validated;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,IAAsB,8BAAtB,MAEA;CACE;CAEA,YAAY,oBAAyD;EAInE,KAAK,iBACH,uBAAuB,KAAA,KAAa,mBAAmB,OAAO,IAC1D,0BAA0B,kBAAkB,IAC5C,KAAA;CACR;CAEA,oBAAqD,MAAkB;EACrE,MAAM,YAAY,KAAK,4BAA4B,IAAI;EACvD,OAAO,KAAK,wBAAwB,SAAS;CAC/C;CAEA,kBAAkB,UAAiC;EAIjD,OAAO;CACT;;;;;;;;;;;;;;;;;CAkBA,4BAAsC,MAA8B;EAElE,MAAM,UADS,KAAK,kBAAkB,qBAChB,IAAI;EAC1B,IAAI,kBAAkBA,KAAY,QAChC,MAAM,IAAI,MAAM,0CAA0C,OAAO,SAAS;EAe5E,MAAM,iBAAiB;EAEvB,MAAM,mBAAmB,KAAK,qBAAqB,cAAc;EAEjE,uBAAuB,gBAAgB;EACvC,qBAAqB,gBAAgB;EAErC,OAAO;CACT;;;;;;;CAQA,qBAA+B,UAAwC;EACrE,MAAM,gBAAgB,SAAS,QAAQ;EACvC,MAAM,qBAAqB,OAAO,YAChC,OAAO,QAAQ,aAAa,EAAE,KAAK,CAAC,MAAM,gBAAgB;GACxD,MAAM,iBAAiB,WAAW,eAAe,CAAC;GAClD,MAAM,sBAAsB,OAAO,YACjC,OAAO,QAAQ,cAAc,EAAE,KAAK,CAAC,MAAM,SAAS,CAClD,MACA,eAAe,kBAAkB,MAAM,IAAI,gBAAgB,GAA2B,CACxF,CAAC,CACH;GACA,OAAO,CACL,MACA;IACE,GAAG;IACH,IAAI,WAAW;IACf,aAAa;GACf,CACF;EACF,CAAC,CACH;EACA,OAAO;GACL,GAAG;GACH,SAAS;IACP,GAAG,SAAS;IACZ,YAAY;GACd;EACF;CACF;AAWF;;;;;;;;;;;;;AC1IA,IAAa,0BAAb,cAA6C,4BAA2C;CACtF,wBAAkC,WAAyC;EACzE,OAAO;CACT;AACF"}