@prisma-next/target-mongo 0.11.0-dev.5 → 0.11.0-dev.51

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-factories-BX6N1O0e.mjs","names":[],"sources":["../src/core/migration-factories.ts"],"sourcesContent":["import type {\n MongoDataTransformCheck,\n MongoDataTransformOperation,\n MongoFilterExpr,\n MongoIndexKey,\n} from '@prisma-next/mongo-query-ast/control';\nimport {\n buildIndexOpId,\n CollModCommand,\n type CollModOptions,\n CreateCollectionCommand,\n type CreateCollectionOptions,\n CreateIndexCommand,\n type CreateIndexOptions,\n DropCollectionCommand,\n DropIndexCommand,\n defaultMongoIndexName,\n keysToKeySpec,\n ListCollectionsCommand,\n ListIndexesCommand,\n MongoAndExpr,\n MongoExistsExpr,\n MongoFieldFilter,\n type MongoMigrationPlanOperation,\n} from '@prisma-next/mongo-query-ast/control';\nimport type { MongoQueryPlan } from '@prisma-next/mongo-query-ast/execution';\nimport type { MongoValue } from '@prisma-next/mongo-value';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { CollModMeta } from './op-factory-call';\n\ninterface Buildable {\n build(): MongoQueryPlan;\n}\n\nfunction isBuildable(value: unknown): value is Buildable {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'build' in value &&\n typeof (value as { build: unknown }).build === 'function'\n );\n}\n\nfunction resolveQuery(value: MongoQueryPlan | Buildable): MongoQueryPlan {\n return isBuildable(value) ? value.build() : value;\n}\n\n// Every MongoDB document carries `_id`, so `exists('_id')` is equivalent to\n// \"match all\". The filter AST has no identity/always-true expression.\nconst MATCH_ALL_FILTER: MongoFilterExpr = MongoExistsExpr.exists('_id');\n\nexport function dataTransform(\n name: string,\n options: {\n /**\n * Optional opt-in routing identity. Presence opts the transform into\n * invariant-aware routing; absence means it is path-dependent and\n * not referenceable from refs.\n */\n invariantId?: string;\n check?: {\n source: () => MongoQueryPlan | Buildable;\n filter?: MongoFilterExpr;\n expect?: 'exists' | 'notExists';\n description?: string;\n };\n run: () => MongoQueryPlan | Buildable;\n },\n): MongoDataTransformOperation {\n let precheck: readonly MongoDataTransformCheck[] = [];\n let postcheck: readonly MongoDataTransformCheck[] = [];\n\n if (options.check) {\n const source = resolveQuery(options.check.source());\n const filter = options.check.filter ?? MATCH_ALL_FILTER;\n const description = options.check.description ?? `Check for data transform: ${name}`;\n const precheckExpect = options.check.expect ?? 'exists';\n const postcheckExpect: 'exists' | 'notExists' =\n precheckExpect === 'exists' ? 'notExists' : 'exists';\n\n precheck = [{ description, source, filter, expect: precheckExpect }];\n postcheck = [{ description, source, filter, expect: postcheckExpect }];\n }\n\n const run: MongoQueryPlan[] = [resolveQuery(options.run())];\n\n return {\n id: `data_transform.${name}`,\n label: `Data transform: ${name}`,\n operationClass: 'data',\n name,\n ...ifDefined('invariantId', options.invariantId),\n precheck,\n run,\n postcheck,\n };\n}\n\nfunction formatKeys(keys: ReadonlyArray<MongoIndexKey>): string {\n return keys.map((k) => `${k.field}:${k.direction}`).join(', ');\n}\n\nfunction isTextIndex(keys: ReadonlyArray<MongoIndexKey>): boolean {\n return keys.some((k) => k.direction === 'text');\n}\n\nfunction keyFilter(keys: ReadonlyArray<MongoIndexKey>) {\n return isTextIndex(keys)\n ? MongoFieldFilter.eq('key._fts', 'text')\n : MongoFieldFilter.eq('key', keysToKeySpec(keys));\n}\n\nexport function createIndex(\n collection: string,\n keys: ReadonlyArray<MongoIndexKey>,\n options?: CreateIndexOptions,\n): MongoMigrationPlanOperation {\n const name = defaultMongoIndexName(keys);\n const filter = keyFilter(keys);\n const fullFilter = options?.unique\n ? MongoAndExpr.of([filter, MongoFieldFilter.eq('unique', true)])\n : filter;\n\n return {\n id: buildIndexOpId('create', collection, keys),\n label: `Create index on ${collection} (${formatKeys(keys)})`,\n operationClass: 'additive',\n precheck: [\n {\n description: `index does not already exist on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'notExists',\n },\n ],\n execute: [\n {\n description: `create index on ${collection}`,\n command: new CreateIndexCommand(collection, keys, {\n ...options,\n unique: options?.unique ?? undefined,\n name,\n }),\n },\n ],\n postcheck: [\n {\n description: `index exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter: fullFilter,\n expect: 'exists',\n },\n ],\n };\n}\n\nexport function dropIndex(\n collection: string,\n keys: ReadonlyArray<MongoIndexKey>,\n): MongoMigrationPlanOperation {\n const indexName = defaultMongoIndexName(keys);\n const filter = keyFilter(keys);\n\n return {\n id: buildIndexOpId('drop', collection, keys),\n label: `Drop index on ${collection} (${formatKeys(keys)})`,\n operationClass: 'destructive',\n precheck: [\n {\n description: `index exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'exists',\n },\n ],\n execute: [\n {\n description: `drop index on ${collection}`,\n command: new DropIndexCommand(collection, indexName),\n },\n ],\n postcheck: [\n {\n description: `index no longer exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'notExists',\n },\n ],\n };\n}\n\nexport function createCollection(\n collection: string,\n options?: CreateCollectionOptions,\n): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.create`,\n label: `Create collection ${collection}`,\n operationClass: 'additive',\n precheck: [\n {\n description: `collection ${collection} does not exist`,\n source: new ListCollectionsCommand(),\n filter: MongoFieldFilter.eq('name', collection),\n expect: 'notExists',\n },\n ],\n execute: [\n {\n description: `create collection ${collection}`,\n command: new CreateCollectionCommand(collection, options),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function dropCollection(collection: string): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.drop`,\n label: `Drop collection ${collection}`,\n operationClass: 'destructive',\n precheck: [],\n execute: [\n {\n description: `drop collection ${collection}`,\n command: new DropCollectionCommand(collection),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function setValidation(\n collection: string,\n schema: Record<string, unknown>,\n options?: { validationLevel?: 'strict' | 'moderate'; validationAction?: 'error' | 'warn' },\n): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.setValidation`,\n label: `Set validation on ${collection}`,\n operationClass: 'destructive',\n precheck: [],\n execute: [\n {\n description: `set validation on ${collection}`,\n command: new CollModCommand(collection, {\n validator: { $jsonSchema: schema },\n validationLevel: options?.validationLevel,\n validationAction: options?.validationAction,\n }),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function collMod(\n collection: string,\n options: CollModOptions,\n meta?: CollModMeta,\n): MongoMigrationPlanOperation {\n const hasValidator = options.validator != null && Object.keys(options.validator).length > 0;\n\n return {\n id: meta?.id ?? `collection.${collection}.collMod`,\n label: meta?.label ?? `Modify collection ${collection}`,\n operationClass: meta?.operationClass ?? 'destructive',\n precheck:\n options.validator != null\n ? [\n {\n description: `collection ${collection} exists`,\n source: new ListCollectionsCommand(),\n filter: MongoFieldFilter.eq('name', collection),\n expect: 'exists' as const,\n },\n ]\n : [],\n execute: [\n {\n description: `modify ${collection}`,\n command: new CollModCommand(collection, options),\n },\n ],\n postcheck: hasValidator\n ? [\n {\n description: `validator applied on ${collection}`,\n source: new ListCollectionsCommand(),\n filter: MongoAndExpr.of([\n MongoFieldFilter.eq('name', collection),\n ...(options.validationLevel\n ? [MongoFieldFilter.eq('options.validationLevel', options.validationLevel)]\n : []),\n ...(options.validationAction\n ? [MongoFieldFilter.eq('options.validationAction', options.validationAction)]\n : []),\n // Include the $jsonSchema body so the idempotency probe only skips when the\n // live validator body genuinely already equals the target — not merely when\n // level/action happen to be unchanged (which was the silent-skip bug for widen ops).\n // MongoFieldFilter.eq compares with order-sensitive deepEqual, not canonicalize.\n // That is safe here because MongoDB preserves BSON key order when round-tripping\n // the $jsonSchema through listCollections (confirmed by the MMS integration test),\n // so a matching live validator compares equal. The only consequence of skipping\n // canonicalization is a safe false-negative on the skip: a validator installed\n // out-of-band with a different key order simply re-runs the collMod harmlessly.\n // The cast is safe: CollModOptions.validator is Record<string,unknown>, and its\n // $jsonSchema value is always a plain BSON object (MongoDocument at runtime).\n ...(options.validator?.['$jsonSchema'] !== undefined\n ? [\n MongoFieldFilter.eq(\n 'options.validator.$jsonSchema',\n blindCast<\n MongoValue,\n 'options.validator.$jsonSchema is a plain BSON object — the factory only populates this from a MongoSchemaValidator.jsonSchema record, which is a MongoDocument at runtime'\n >(options.validator?.['$jsonSchema']),\n ),\n ]\n : []),\n ]),\n expect: 'exists' as const,\n },\n ]\n : [],\n };\n}\n\nexport function validatedCollection(\n name: string,\n schema: Record<string, unknown>,\n indexes: ReadonlyArray<{ keys: MongoIndexKey[]; unique?: boolean }>,\n): MongoMigrationPlanOperation[] {\n return [\n createCollection(name, {\n validator: { $jsonSchema: schema },\n validationLevel: 'strict',\n validationAction: 'error',\n }),\n ...indexes.map((idx) => createIndex(name, idx.keys, { unique: idx.unique })),\n ];\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"migration.d.mts","names":[],"sources":["../src/core/migration-factories.ts"],"mappings":";;;;;;UA6BU,SAAA;EACR,KAAA,IAAS,cAAA;AAAA;AAAA,iBAoBK,aAAA,CACd,IAAA,UACA,OAAA;;;;AAFF;;EAQI,WAAA;EACA,KAAA;IACE,MAAA,QAAc,cAAA,GAAiB,SAAA;IAC/B,MAAA,GAAS,eAAA;IACT,MAAA;IACA,WAAA;EAAA;EAEF,GAAA,QAAW,cAAA,GAAiB,SAAA;AAAA,IAE7B,2BAAA;AAAA,iBA4Ca,WAAA,CACd,UAAA,UACA,IAAA,EAAM,aAAA,CAAc,aAAA,GACpB,OAAA,GAAU,kBAAA,GACT,2BAAA;AAAA,iBAwCa,SAAA,CACd,UAAA,UACA,IAAA,EAAM,aAAA,CAAc,aAAA,IACnB,2BAAA;AAAA,iBAiCa,gBAAA,CACd,UAAA,UACA,OAAA,GAAU,uBAAA,GACT,2BAAA;AAAA,iBAuBa,cAAA,CAAe,UAAA,WAAqB,2BAAA;AAAA,iBAgBpC,aAAA,CACd,UAAA,UACA,MAAA,EAAQ,MAAA,mBACR,OAAA;EAAY,eAAA;EAAyC,gBAAA;AAAA,IACpD,2BAAA;AAAA,iBAoBa,OAAA,CACd,UAAA,UACA,OAAA,EAAS,cAAA,EACT,IAAA,GAAO,WAAA,GACN,2BAAA;AAAA,iBA6Ca,mBAAA,CACd,IAAA,UACA,MAAA,EAAQ,MAAA,mBACR,OAAA,EAAS,aAAA;EAAgB,IAAA,EAAM,aAAA;EAAiB,MAAA;AAAA,KAC/C,2BAAA"}
1
+ {"version":3,"file":"migration.d.mts","names":[],"sources":["../src/core/migration-factories.ts"],"mappings":";;;;;;UA+BU,SAAA;EACR,KAAA,IAAS,cAAc;AAAA;AAAA,iBAoBT,aAAA,CACd,IAAA,UACA,OAAA;;;AAtBuB;AAoBzB;;EAQI,WAAA;EACA,KAAA;IACE,MAAA,QAAc,cAAA,GAAiB,SAAA;IAC/B,MAAA,GAAS,eAAA;IACT,MAAA;IACA,WAAA;EAAA;EAEF,GAAA,QAAW,cAAA,GAAiB,SAAA;AAAA,IAE7B,2BAAA;AAAA,iBA4Ca,WAAA,CACd,UAAA,UACA,IAAA,EAAM,aAAA,CAAc,aAAA,GACpB,OAAA,GAAU,kBAAA,GACT,2BAAA;AAAA,iBAwCa,SAAA,CACd,UAAA,UACA,IAAA,EAAM,aAAA,CAAc,aAAA,IACnB,2BAAA;AAAA,iBAiCa,gBAAA,CACd,UAAA,UACA,OAAA,GAAU,uBAAA,GACT,2BAA2B;AAAA,iBAuBd,cAAA,CAAe,UAAA,WAAqB,2BAA2B;AAAA,iBAgB/D,aAAA,CACd,UAAA,UACA,MAAA,EAAQ,MAAA,mBACR,OAAA;EAAY,eAAA;EAAyC,gBAAA;AAAA,IACpD,2BAA2B;AAAA,iBAoBd,OAAA,CACd,UAAA,UACA,OAAA,EAAS,cAAA,EACT,IAAA,GAAO,WAAA,GACN,2BAAA;AAAA,iBAmEa,mBAAA,CACd,IAAA,UACA,MAAA,EAAQ,MAAA,mBACR,OAAA,EAAS,aAAA;EAAgB,IAAA,EAAM,aAAA;EAAiB,MAAA;AAAA,KAC/C,2BAAA"}
@@ -1,3 +1,3 @@
1
- import { a as dropCollection, c as validatedCollection, i as dataTransform, n as createCollection, o as dropIndex, r as createIndex, s as setValidation, t as collMod } from "./migration-factories-BRBKKZia.mjs";
1
+ import { a as dropCollection, c as validatedCollection, i as dataTransform, n as createCollection, o as dropIndex, r as createIndex, s as setValidation, t as collMod } from "./migration-factories-BX6N1O0e.mjs";
2
2
  import { placeholder } from "@prisma-next/errors/migration";
3
3
  export { collMod, createCollection, createIndex, dataTransform, dropCollection, dropIndex, placeholder, setValidation, validatedCollection };
@@ -1 +1 @@
1
- {"version":3,"file":"op-factory-call-BC-llGKt.d.mts","names":[],"sources":["../src/core/op-factory-call.ts"],"mappings":";;;;;;UA6CiB,WAAA;EAAA,SACN,EAAA;EAAA,SACA,KAAA;EAAA,SACA,cAAA,GAAiB,uBAAA;AAAA;AAAA,uBAKb,iBAAA,SAA0B,YAAA,YAAwB,aAAA;EAAA,kBAC7C,WAAA;EAAA,kBACA,cAAA,EAAgB,uBAAA;EAAA,kBAChB,KAAA;EAAA,SACT,IAAA,CAAA,GAAQ,2BAAA;EAEjB,kBAAA,CAAA,YAA+B,iBAAA;EAAA,UAIrB,MAAA,CAAA;AAAA;AAAA,cASC,eAAA,SAAwB,iBAAA;EAAA,SAC1B,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,IAAA,EAAM,aAAA,CAAc,aAAA;EAAA,SACpB,OAAA,EAAS,kBAAA;EAAA,SACT,KAAA;cAGP,UAAA,UACA,IAAA,EAAM,aAAA,CAAc,aAAA,GACpB,OAAA,GAAU,kBAAA;EAUZ,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAOW,aAAA,SAAsB,iBAAA;EAAA,SACxB,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,IAAA,EAAM,aAAA,CAAc,aAAA;EAAA,SACpB,KAAA;cAEG,UAAA,UAAoB,IAAA,EAAM,aAAA,CAAc,aAAA;EAQpD,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAKW,oBAAA,SAA6B,iBAAA;EAAA,SAC/B,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA,EAAS,uBAAA;EAAA,SACT,KAAA;cAEG,UAAA,UAAoB,OAAA,GAAU,uBAAA;EAQ1C,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAOW,kBAAA,SAA2B,iBAAA;EAAA,SAC7B,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,KAAA;cAEG,UAAA;EAOZ,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAKW,WAAA,SAAoB,iBAAA;EAAA,SACtB,WAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA,EAAS,cAAA;EAAA,SACT,IAAA,EAAM,WAAA;EAAA,SACN,cAAA,EAAgB,uBAAA;EAAA,SAChB,KAAA;cAEG,UAAA,UAAoB,OAAA,EAAS,cAAA,EAAgB,IAAA,GAAO,WAAA;EAUhE,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,KAOU,eAAA,GACR,eAAA,GACA,aAAA,GACA,oBAAA,GACA,kBAAA,GACA,WAAA;AAAA,iBAEY,+BAAA,CAAgC,KAAA,EAAO,gBAAA,GAAmB,kBAAA;AAAA,iBAc1D,yCAAA,CACd,IAAA,EAAM,qBAAA,GACL,uBAAA"}
1
+ {"version":3,"file":"op-factory-call-BC-llGKt.d.mts","names":[],"sources":["../src/core/op-factory-call.ts"],"mappings":";;;;;;UA6CiB,WAAA;EAAA,SACN,EAAA;EAAA,SACA,KAAA;EAAA,SACA,cAAA,GAAiB,uBAAuB;AAAA;AAAA,uBAKpC,iBAAA,SAA0B,YAAA,YAAwB,aAAA;EAAA,kBAC7C,WAAA;EAAA,kBACA,cAAA,EAAgB,uBAAA;EAAA,kBAChB,KAAA;EAAA,SACT,IAAA,CAAA,GAAQ,2BAAA;EAEjB,kBAAA,CAAA,YAA+B,iBAAA;EAAA,UAIrB,MAAA,CAAA;AAAA;AAAA,cASC,eAAA,SAAwB,iBAAA;EAAA,SAC1B,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,IAAA,EAAM,aAAA,CAAc,aAAA;EAAA,SACpB,OAAA,EAAS,kBAAA;EAAA,SACT,KAAA;cAGP,UAAA,UACA,IAAA,EAAM,aAAA,CAAc,aAAA,GACpB,OAAA,GAAU,kBAAA;EAUZ,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAOW,aAAA,SAAsB,iBAAA;EAAA,SACxB,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,IAAA,EAAM,aAAA,CAAc,aAAA;EAAA,SACpB,KAAA;cAEG,UAAA,UAAoB,IAAA,EAAM,aAAA,CAAc,aAAA;EAQpD,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAKW,oBAAA,SAA6B,iBAAA;EAAA,SAC/B,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA,EAAS,uBAAA;EAAA,SACT,KAAA;cAEG,UAAA,UAAoB,OAAA,GAAU,uBAAA;EAQ1C,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAOW,kBAAA,SAA2B,iBAAiB;EAAA,SAC9C,WAAA;EAAA,SACA,cAAA;EAAA,SACA,UAAA;EAAA,SACA,KAAA;cAEG,UAAA;EAOZ,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,cAKW,WAAA,SAAoB,iBAAA;EAAA,SACtB,WAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA,EAAS,cAAA;EAAA,SACT,IAAA,EAAM,WAAA;EAAA,SACN,cAAA,EAAgB,uBAAA;EAAA,SAChB,KAAA;cAEG,UAAA,UAAoB,OAAA,EAAS,cAAA,EAAgB,IAAA,GAAO,WAAA;EAUhE,IAAA,CAAA,GAAQ,2BAAA;EAIR,gBAAA,CAAA;AAAA;AAAA,KAOU,eAAA,GACR,eAAA,GACA,aAAA,GACA,oBAAA,GACA,kBAAA,GACA,WAAA;AAAA,iBAEY,+BAAA,CAAgC,KAAA,EAAO,gBAAA,GAAmB,kBAAkB;AAAA,iBAc5E,yCAAA,CACd,IAAA,EAAM,qBAAA,GACL,uBAAuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"pack.d.mts","names":[],"sources":["../src/exports/pack.ts"],"mappings":";;;cAGM,eAAA;EAAA,SACK,IAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;EAAA,SACA,EAAA;EAAA,SACA,OAAA;EAAA,SACA,YAAA,EAAc,MAAA;EAAA,SACd,YAAA,GAAe,UAAA;AAAA"}
1
+ {"version":3,"file":"pack.d.mts","names":[],"sources":["../src/exports/pack.ts"],"mappings":";;;cAGM,eAAA;EAAA,SACK,IAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;EAAA,SACA,EAAA;EAAA,SACA,OAAA;EAAA,SACA,YAAA,EAAc,MAAA;EAAA,SACd,YAAA,GAAe,UAAU;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/exports/runtime.ts"],"mappings":";;;;UAOiB,0BAAA,SAAmC,qBAAA;AAApD;;;;AAAA,cAMM,4BAAA,EAA8B,uBAAA,mBAGlC,0BAAA;EAAA,SAES,MAAA,QAAc,kBAAA;AAAA"}
1
+ {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/exports/runtime.ts"],"mappings":";;;;UAOiB,0BAAA,SAAmC,qBAAqB;AAAzE;;;;AAAA,cAMM,4BAAA,EAA8B,uBAAA,mBAGlC,0BAAA;EAAA,SAES,MAAA,QAAc,kBAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.mjs","names":[],"sources":["../src/exports/runtime.ts"],"sourcesContent":["import type {\n RuntimeTargetDescriptor,\n RuntimeTargetInstance,\n} from '@prisma-next/framework-components/execution';\nimport { type MongoCodecRegistry, newMongoCodecRegistry } from '@prisma-next/mongo-codec';\nimport { mongoTargetDescriptorMeta } from '../core/descriptor-meta';\n\nexport interface MongoRuntimeTargetInstance extends RuntimeTargetInstance<'mongo', 'mongo'> {}\n\n/**\n * Target-mongo deliberately does NOT import `MongoRuntimeTargetDescriptor` from `@prisma-next/mongo-runtime`. The target package is a control-plane residence and must not pull the Mongo execution-plane package into its dependency closure. The runtime descriptor here is shaped to satisfy the framework's `RuntimeTargetDescriptor` plus the structural `MongoStaticContributions` (`codecs`) that `@prisma-next/mongo-runtime`\n * consumers narrow to at composition time.\n */\nconst mongoRuntimeTargetDescriptor: RuntimeTargetDescriptor<\n 'mongo',\n 'mongo',\n MongoRuntimeTargetInstance\n> & {\n readonly codecs: () => MongoCodecRegistry;\n} = {\n ...mongoTargetDescriptorMeta,\n // The target descriptor itself contributes no codecs — the standard set lives on the adapter descriptor (see `@prisma-next/adapter-mongo/runtime`).\n codecs: () => newMongoCodecRegistry(),\n create(): MongoRuntimeTargetInstance {\n return {\n familyId: 'mongo',\n targetId: 'mongo',\n };\n },\n};\n\nexport default mongoRuntimeTargetDescriptor;\n"],"mappings":";;;;;;;AAaA,MAAM,+BAMF;CACF,GAAG;CAEH,cAAc,uBAAuB;CACrC,SAAqC;EACnC,OAAO;GACL,UAAU;GACV,UAAU;GACX;;CAEJ"}
1
+ {"version":3,"file":"runtime.mjs","names":[],"sources":["../src/exports/runtime.ts"],"sourcesContent":["import type {\n RuntimeTargetDescriptor,\n RuntimeTargetInstance,\n} from '@prisma-next/framework-components/execution';\nimport { type MongoCodecRegistry, newMongoCodecRegistry } from '@prisma-next/mongo-codec';\nimport { mongoTargetDescriptorMeta } from '../core/descriptor-meta';\n\nexport interface MongoRuntimeTargetInstance extends RuntimeTargetInstance<'mongo', 'mongo'> {}\n\n/**\n * Target-mongo deliberately does NOT import `MongoRuntimeTargetDescriptor` from `@prisma-next/mongo-runtime`. The target package is a control-plane residence and must not pull the Mongo execution-plane package into its dependency closure. The runtime descriptor here is shaped to satisfy the framework's `RuntimeTargetDescriptor` plus the structural `MongoStaticContributions` (`codecs`) that `@prisma-next/mongo-runtime`\n * consumers narrow to at composition time.\n */\nconst mongoRuntimeTargetDescriptor: RuntimeTargetDescriptor<\n 'mongo',\n 'mongo',\n MongoRuntimeTargetInstance\n> & {\n readonly codecs: () => MongoCodecRegistry;\n} = {\n ...mongoTargetDescriptorMeta,\n // The target descriptor itself contributes no codecs — the standard set lives on the adapter descriptor (see `@prisma-next/adapter-mongo/runtime`).\n codecs: () => newMongoCodecRegistry(),\n create(): MongoRuntimeTargetInstance {\n return {\n familyId: 'mongo',\n targetId: 'mongo',\n };\n },\n};\n\nexport default mongoRuntimeTargetDescriptor;\n"],"mappings":";;;;;;;AAaA,MAAM,+BAMF;CACF,GAAG;CAEH,cAAc,sBAAsB;CACpC,SAAqC;EACnC,OAAO;GACL,UAAU;GACV,UAAU;EACZ;CACF;AACF"}
package/package.json CHANGED
@@ -1,35 +1,35 @@
1
1
  {
2
2
  "name": "@prisma-next/target-mongo",
3
- "version": "0.11.0-dev.5",
3
+ "version": "0.11.0-dev.51",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "MongoDB target pack for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/adapter-mongo": "0.11.0-dev.5",
10
- "@prisma-next/contract": "0.11.0-dev.5",
11
- "@prisma-next/driver-mongo": "0.11.0-dev.5",
12
- "@prisma-next/errors": "0.11.0-dev.5",
13
- "@prisma-next/family-mongo": "0.11.0-dev.5",
14
- "@prisma-next/framework-components": "0.11.0-dev.5",
15
- "@prisma-next/migration-tools": "0.11.0-dev.5",
16
- "@prisma-next/ts-render": "0.11.0-dev.5",
17
- "@prisma-next/mongo-codec": "0.11.0-dev.5",
18
- "@prisma-next/mongo-contract": "0.11.0-dev.5",
19
- "@prisma-next/mongo-lowering": "0.11.0-dev.5",
20
- "@prisma-next/mongo-query-ast": "0.11.0-dev.5",
21
- "@prisma-next/mongo-schema-ir": "0.11.0-dev.5",
22
- "@prisma-next/mongo-value": "0.11.0-dev.5",
23
- "@prisma-next/utils": "0.11.0-dev.5",
24
- "arktype": "^2.2.0",
25
- "mongodb": "^6.16.0"
9
+ "@prisma-next/adapter-mongo": "0.11.0-dev.51",
10
+ "@prisma-next/contract": "0.11.0-dev.51",
11
+ "@prisma-next/driver-mongo": "0.11.0-dev.51",
12
+ "@prisma-next/errors": "0.11.0-dev.51",
13
+ "@prisma-next/family-mongo": "0.11.0-dev.51",
14
+ "@prisma-next/framework-components": "0.11.0-dev.51",
15
+ "@prisma-next/migration-tools": "0.11.0-dev.51",
16
+ "@prisma-next/ts-render": "0.11.0-dev.51",
17
+ "@prisma-next/mongo-codec": "0.11.0-dev.51",
18
+ "@prisma-next/mongo-contract": "0.11.0-dev.51",
19
+ "@prisma-next/mongo-lowering": "0.11.0-dev.51",
20
+ "@prisma-next/mongo-query-ast": "0.11.0-dev.51",
21
+ "@prisma-next/mongo-schema-ir": "0.11.0-dev.51",
22
+ "@prisma-next/mongo-value": "0.11.0-dev.51",
23
+ "@prisma-next/utils": "0.11.0-dev.51",
24
+ "arktype": "^2.2.0"
26
25
  },
27
26
  "devDependencies": {
28
- "@prisma-next/mongo-contract-psl": "0.11.0-dev.5",
29
- "@prisma-next/psl-parser": "0.11.0-dev.5",
30
- "@prisma-next/test-utils": "0.11.0-dev.5",
31
- "@prisma-next/tsconfig": "0.11.0-dev.5",
32
- "@prisma-next/tsdown": "0.11.0-dev.5",
27
+ "mongodb": "^7.2.0",
28
+ "@prisma-next/mongo-contract-psl": "0.11.0-dev.51",
29
+ "@prisma-next/psl-parser": "0.11.0-dev.51",
30
+ "@prisma-next/test-utils": "0.11.0-dev.51",
31
+ "@prisma-next/tsconfig": "0.11.0-dev.51",
32
+ "@prisma-next/tsdown": "0.11.0-dev.51",
33
33
  "mongodb-memory-server": "11.1.0",
34
34
  "pathe": "^2.0.3",
35
35
  "tsdown": "0.22.0",
@@ -20,12 +20,13 @@ import type {
20
20
  MultiSpaceRunnerResult,
21
21
  } from '@prisma-next/framework-components/control';
22
22
  import {
23
- type ContractSpaceMember,
23
+ createContractSpaceMember,
24
24
  projectSchemaToSpace,
25
25
  } from '@prisma-next/migration-tools/aggregate';
26
26
  import type { MongoContract } from '@prisma-next/mongo-contract';
27
27
  import type { MongoSchemaCollection } from '@prisma-next/mongo-schema-ir';
28
28
  import { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';
29
+ import { blindCast } from '@prisma-next/utils/casts';
29
30
  import { notOk, ok } from '@prisma-next/utils/result';
30
31
  import { mongoTargetDescriptorMeta } from './descriptor-meta';
31
32
  import { MongoMigrationPlanner } from './mongo-planner';
@@ -156,37 +157,20 @@ export const mongoTargetDescriptor: MongoControlTargetDescriptor<MongoTargetCont
156
157
  };
157
158
 
158
159
  /**
159
- * Synthesise the minimum {@link projectSchemaToSpace}-compatible
160
- * `ContractSpaceMember` shape from a per-space option entry. The
161
- * projector only reads `spaceId` and `contract.storage`; the rest of
162
- * `ContractSpaceMember` (head ref invariants, hydrated migration
163
- * graph) is irrelevant at runner time and stubbed with sentinels.
164
- *
165
- * The `as unknown as ContractSpaceMember` cast is the load-bearing bit
166
- * — the projector duck-types its members so a sentinel-shaped graph
167
- * never gets read, but the framework type carries a richer shape.
160
+ * Synthesise a {@link projectSchemaToSpace}-compatible member from a
161
+ * per-space option entry. The projector only reads `spaceId` and
162
+ * `contract()`; migration graph state is empty because the runner
163
+ * consumes the destination contract directly.
168
164
  */
169
- function toSpaceMember(
170
- opts: MultiSpaceRunnerPerSpaceOptions<'mongo', 'mongo'>,
171
- ): ContractSpaceMember {
172
- return {
165
+ function toSpaceMember(opts: MultiSpaceRunnerPerSpaceOptions<'mongo', 'mongo'>) {
166
+ return createContractSpaceMember({
173
167
  spaceId: opts.space,
174
- // Blind cast: `MultiSpaceRunnerPerSpaceOptions.destinationContract`
175
- // is intentionally typed `unknown` at the framework boundary
176
- // (the framework does not know which family's `Contract` shape
177
- // a runner consumes). The caller is the aggregate runner,
178
- // which only forwards a value already validated through the
179
- // family `deserializeContract` seam at the aggregate boundary.
180
- contract: opts.destinationContract as unknown as Contract,
181
- headRef: { hash: '', invariants: [] },
182
- migrations: {
183
- graph: {
184
- nodes: new Set<string>(),
185
- forwardChain: new Map(),
186
- reverseChain: new Map(),
187
- migrationByHash: new Map(),
188
- },
189
- packagesByMigrationHash: new Map(),
190
- },
191
- } as unknown as ContractSpaceMember;
168
+ packages: [],
169
+ refs: {},
170
+ headRef: null,
171
+ resolveContract: () =>
172
+ blindCast<Contract, 'destinationContract validated at aggregate boundary'>(
173
+ opts.destinationContract,
174
+ ),
175
+ });
192
176
  }
@@ -24,6 +24,8 @@ import {
24
24
  type MongoMigrationPlanOperation,
25
25
  } from '@prisma-next/mongo-query-ast/control';
26
26
  import type { MongoQueryPlan } from '@prisma-next/mongo-query-ast/execution';
27
+ import type { MongoValue } from '@prisma-next/mongo-value';
28
+ import { blindCast } from '@prisma-next/utils/casts';
27
29
  import { ifDefined } from '@prisma-next/utils/defined';
28
30
  import type { CollModMeta } from './op-factory-call';
29
31
 
@@ -296,6 +298,28 @@ export function collMod(
296
298
  ...(options.validationAction
297
299
  ? [MongoFieldFilter.eq('options.validationAction', options.validationAction)]
298
300
  : []),
301
+ // Include the $jsonSchema body so the idempotency probe only skips when the
302
+ // live validator body genuinely already equals the target — not merely when
303
+ // level/action happen to be unchanged (which was the silent-skip bug for widen ops).
304
+ // MongoFieldFilter.eq compares with order-sensitive deepEqual, not canonicalize.
305
+ // That is safe here because MongoDB preserves BSON key order when round-tripping
306
+ // the $jsonSchema through listCollections (confirmed by the MMS integration test),
307
+ // so a matching live validator compares equal. The only consequence of skipping
308
+ // canonicalization is a safe false-negative on the skip: a validator installed
309
+ // out-of-band with a different key order simply re-runs the collMod harmlessly.
310
+ // The cast is safe: CollModOptions.validator is Record<string,unknown>, and its
311
+ // $jsonSchema value is always a plain BSON object (MongoDocument at runtime).
312
+ ...(options.validator?.['$jsonSchema'] !== undefined
313
+ ? [
314
+ MongoFieldFilter.eq(
315
+ 'options.validator.$jsonSchema',
316
+ blindCast<
317
+ MongoValue,
318
+ 'options.validator.$jsonSchema is a plain BSON object — the factory only populates this from a MongoSchemaValidator.jsonSchema record, which is a MongoDocument at runtime'
319
+ >(options.validator?.['$jsonSchema']),
320
+ ),
321
+ ]
322
+ : []),
299
323
  ]),
300
324
  expect: 'exists' as const,
301
325
  },
@@ -489,16 +489,19 @@ function deserializeDdlCommand(json: unknown): AnyMongoDdlCommand {
489
489
  case 'createIndex': {
490
490
  const data = validate(CreateIndexJson, json, 'createIndex command');
491
491
  return new CreateIndexCommand(data.collection, data.keys, {
492
- unique: data.unique,
493
- sparse: data.sparse,
494
- expireAfterSeconds: data.expireAfterSeconds,
495
- partialFilterExpression: data.partialFilterExpression,
496
- name: data.name,
497
- wildcardProjection: data.wildcardProjection as Record<string, 0 | 1> | undefined,
498
- collation: data.collation,
499
- weights: data.weights as Record<string, number> | undefined,
500
- default_language: data.default_language,
501
- language_override: data.language_override,
492
+ ...ifDefined('unique', data.unique),
493
+ ...ifDefined('sparse', data.sparse),
494
+ ...ifDefined('expireAfterSeconds', data.expireAfterSeconds),
495
+ ...ifDefined('partialFilterExpression', data.partialFilterExpression),
496
+ ...ifDefined('name', data.name),
497
+ ...ifDefined(
498
+ 'wildcardProjection',
499
+ data.wildcardProjection as Record<string, 0 | 1> | undefined,
500
+ ),
501
+ ...ifDefined('collation', data.collation),
502
+ ...ifDefined('weights', data.weights as Record<string, number> | undefined),
503
+ ...ifDefined('default_language', data.default_language),
504
+ ...ifDefined('language_override', data.language_override),
502
505
  });
503
506
  }
504
507
  case 'dropIndex': {
@@ -66,21 +66,66 @@ function classifyValidatorUpdate(
66
66
  origin: MongoSchemaValidator,
67
67
  dest: MongoSchemaValidator,
68
68
  ): 'widening' | 'destructive' {
69
- let hasDestructive = false;
69
+ // Moving to a stricter action or level narrows the accepted value space.
70
+ if (origin.validationAction !== dest.validationAction && dest.validationAction === 'error') {
71
+ return 'destructive';
72
+ }
73
+ if (origin.validationLevel !== dest.validationLevel && dest.validationLevel === 'strict') {
74
+ return 'destructive';
75
+ }
76
+
77
+ if (canonicalize(origin.jsonSchema) === canonicalize(dest.jsonSchema)) {
78
+ return 'widening';
79
+ }
80
+
81
+ // Check whether the schema change only adds non-required properties (widening).
82
+ return isWideningSchemaChange(origin.jsonSchema, dest.jsonSchema) ? 'widening' : 'destructive';
83
+ }
70
84
 
71
- if (canonicalize(origin.jsonSchema) !== canonicalize(dest.jsonSchema)) {
72
- hasDestructive = true;
85
+ function isPlainObject(v: unknown): v is Record<string, unknown> {
86
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
87
+ }
88
+
89
+ /**
90
+ * Returns true when `dest` is a structural superset of `origin` for the common
91
+ * additive case: adding non-required properties to a top-level object schema.
92
+ * Anything uncertain falls through to the safe `destructive` default.
93
+ */
94
+ function isWideningSchemaChange(
95
+ origin: Record<string, unknown>,
96
+ dest: Record<string, unknown>,
97
+ ): boolean {
98
+ // Only handle top-level object schemas.
99
+ if (origin['bsonType'] !== 'object' || dest['bsonType'] !== 'object') {
100
+ return false;
73
101
  }
74
102
 
75
- if (origin.validationAction !== dest.validationAction) {
76
- if (dest.validationAction === 'error') hasDestructive = true;
103
+ // Any change to keys besides 'required' and 'properties' is uncertain → destructive.
104
+ const allKeys = new Set([...Object.keys(origin), ...Object.keys(dest)]);
105
+ for (const key of allKeys) {
106
+ if (key === 'required' || key === 'properties') continue;
107
+ if (canonicalize(origin[key]) !== canonicalize(dest[key])) return false;
108
+ }
109
+
110
+ // dest.required must be a subset of origin.required — no new required fields.
111
+ const originRequired = new Set<unknown>(
112
+ Array.isArray(origin['required']) ? origin['required'] : [],
113
+ );
114
+ const destRequired = Array.isArray(dest['required']) ? dest['required'] : [];
115
+ for (const field of destRequired) {
116
+ if (!originRequired.has(field)) return false;
77
117
  }
78
118
 
79
- if (origin.validationLevel !== dest.validationLevel) {
80
- if (dest.validationLevel === 'strict') hasDestructive = true;
119
+ // All properties that existed in origin must still exist unchanged.
120
+ // New properties in dest (absent from origin) are allowed — widening.
121
+ const originProps = isPlainObject(origin['properties']) ? origin['properties'] : {};
122
+ const destProps = isPlainObject(dest['properties']) ? dest['properties'] : {};
123
+ for (const field of Object.keys(originProps)) {
124
+ if (!Object.hasOwn(destProps, field)) return false; // Property removed → destructive.
125
+ if (canonicalize(originProps[field]) !== canonicalize(destProps[field])) return false; // Property narrowed → destructive.
81
126
  }
82
127
 
83
- return hasDestructive ? 'destructive' : 'widening';
128
+ return true;
84
129
  }
85
130
 
86
131
  function hasImmutableOptionChange(
@@ -1,7 +1,6 @@
1
1
  import type { MigrationPlanWithAuthoringSurface } from '@prisma-next/framework-components/control';
2
2
  import { Migration, type MigrationMeta } from '@prisma-next/migration-tools/migration';
3
3
  import type { AnyMongoMigrationOperation } from '@prisma-next/mongo-query-ast/control';
4
- import { ifDefined } from '@prisma-next/utils/defined';
5
4
  import type { OpFactoryCall } from './op-factory-call';
6
5
  import { renderOps } from './render-ops';
7
6
  import { renderCallsToTypeScript } from './render-typescript';
@@ -47,7 +46,6 @@ export class PlannerProducedMongoMigration
47
46
  return renderCallsToTypeScript(this.calls, {
48
47
  from: this.meta.from,
49
48
  to: this.meta.to,
50
- ...ifDefined('labels', this.meta.labels),
51
49
  });
52
50
  }
53
51
  }
@@ -1,11 +1,10 @@
1
1
  import { detectScaffoldRuntime, shebangLineFor } from '@prisma-next/migration-tools/migration-ts';
2
- import { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-next/ts-render';
2
+ import { type ImportRequirement, renderImports } from '@prisma-next/ts-render';
3
3
  import type { OpFactoryCall } from './op-factory-call';
4
4
 
5
5
  export interface RenderMigrationMeta {
6
6
  readonly from: string | null;
7
7
  readonly to: string;
8
- readonly labels?: readonly string[];
9
8
  }
10
9
 
11
10
  /**
@@ -88,9 +87,6 @@ function buildDescribeMethod(meta: RenderMigrationMeta): string {
88
87
  lines.push(' return {');
89
88
  lines.push(` from: ${JSON.stringify(meta.from)},`);
90
89
  lines.push(` to: ${JSON.stringify(meta.to)},`);
91
- if (meta.labels && meta.labels.length > 0) {
92
- lines.push(` labels: ${jsonToTsSource(meta.labels)},`);
93
- }
94
90
  lines.push(' };');
95
91
  lines.push(' }');
96
92
  lines.push('');
@@ -1 +0,0 @@
1
- {"version":3,"file":"migration-factories-BRBKKZia.mjs","names":[],"sources":["../src/core/migration-factories.ts"],"sourcesContent":["import type {\n MongoDataTransformCheck,\n MongoDataTransformOperation,\n MongoFilterExpr,\n MongoIndexKey,\n} from '@prisma-next/mongo-query-ast/control';\nimport {\n buildIndexOpId,\n CollModCommand,\n type CollModOptions,\n CreateCollectionCommand,\n type CreateCollectionOptions,\n CreateIndexCommand,\n type CreateIndexOptions,\n DropCollectionCommand,\n DropIndexCommand,\n defaultMongoIndexName,\n keysToKeySpec,\n ListCollectionsCommand,\n ListIndexesCommand,\n MongoAndExpr,\n MongoExistsExpr,\n MongoFieldFilter,\n type MongoMigrationPlanOperation,\n} from '@prisma-next/mongo-query-ast/control';\nimport type { MongoQueryPlan } from '@prisma-next/mongo-query-ast/execution';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { CollModMeta } from './op-factory-call';\n\ninterface Buildable {\n build(): MongoQueryPlan;\n}\n\nfunction isBuildable(value: unknown): value is Buildable {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'build' in value &&\n typeof (value as { build: unknown }).build === 'function'\n );\n}\n\nfunction resolveQuery(value: MongoQueryPlan | Buildable): MongoQueryPlan {\n return isBuildable(value) ? value.build() : value;\n}\n\n// Every MongoDB document carries `_id`, so `exists('_id')` is equivalent to\n// \"match all\". The filter AST has no identity/always-true expression.\nconst MATCH_ALL_FILTER: MongoFilterExpr = MongoExistsExpr.exists('_id');\n\nexport function dataTransform(\n name: string,\n options: {\n /**\n * Optional opt-in routing identity. Presence opts the transform into\n * invariant-aware routing; absence means it is path-dependent and\n * not referenceable from refs.\n */\n invariantId?: string;\n check?: {\n source: () => MongoQueryPlan | Buildable;\n filter?: MongoFilterExpr;\n expect?: 'exists' | 'notExists';\n description?: string;\n };\n run: () => MongoQueryPlan | Buildable;\n },\n): MongoDataTransformOperation {\n let precheck: readonly MongoDataTransformCheck[] = [];\n let postcheck: readonly MongoDataTransformCheck[] = [];\n\n if (options.check) {\n const source = resolveQuery(options.check.source());\n const filter = options.check.filter ?? MATCH_ALL_FILTER;\n const description = options.check.description ?? `Check for data transform: ${name}`;\n const precheckExpect = options.check.expect ?? 'exists';\n const postcheckExpect: 'exists' | 'notExists' =\n precheckExpect === 'exists' ? 'notExists' : 'exists';\n\n precheck = [{ description, source, filter, expect: precheckExpect }];\n postcheck = [{ description, source, filter, expect: postcheckExpect }];\n }\n\n const run: MongoQueryPlan[] = [resolveQuery(options.run())];\n\n return {\n id: `data_transform.${name}`,\n label: `Data transform: ${name}`,\n operationClass: 'data',\n name,\n ...ifDefined('invariantId', options.invariantId),\n precheck,\n run,\n postcheck,\n };\n}\n\nfunction formatKeys(keys: ReadonlyArray<MongoIndexKey>): string {\n return keys.map((k) => `${k.field}:${k.direction}`).join(', ');\n}\n\nfunction isTextIndex(keys: ReadonlyArray<MongoIndexKey>): boolean {\n return keys.some((k) => k.direction === 'text');\n}\n\nfunction keyFilter(keys: ReadonlyArray<MongoIndexKey>) {\n return isTextIndex(keys)\n ? MongoFieldFilter.eq('key._fts', 'text')\n : MongoFieldFilter.eq('key', keysToKeySpec(keys));\n}\n\nexport function createIndex(\n collection: string,\n keys: ReadonlyArray<MongoIndexKey>,\n options?: CreateIndexOptions,\n): MongoMigrationPlanOperation {\n const name = defaultMongoIndexName(keys);\n const filter = keyFilter(keys);\n const fullFilter = options?.unique\n ? MongoAndExpr.of([filter, MongoFieldFilter.eq('unique', true)])\n : filter;\n\n return {\n id: buildIndexOpId('create', collection, keys),\n label: `Create index on ${collection} (${formatKeys(keys)})`,\n operationClass: 'additive',\n precheck: [\n {\n description: `index does not already exist on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'notExists',\n },\n ],\n execute: [\n {\n description: `create index on ${collection}`,\n command: new CreateIndexCommand(collection, keys, {\n ...options,\n unique: options?.unique ?? undefined,\n name,\n }),\n },\n ],\n postcheck: [\n {\n description: `index exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter: fullFilter,\n expect: 'exists',\n },\n ],\n };\n}\n\nexport function dropIndex(\n collection: string,\n keys: ReadonlyArray<MongoIndexKey>,\n): MongoMigrationPlanOperation {\n const indexName = defaultMongoIndexName(keys);\n const filter = keyFilter(keys);\n\n return {\n id: buildIndexOpId('drop', collection, keys),\n label: `Drop index on ${collection} (${formatKeys(keys)})`,\n operationClass: 'destructive',\n precheck: [\n {\n description: `index exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'exists',\n },\n ],\n execute: [\n {\n description: `drop index on ${collection}`,\n command: new DropIndexCommand(collection, indexName),\n },\n ],\n postcheck: [\n {\n description: `index no longer exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'notExists',\n },\n ],\n };\n}\n\nexport function createCollection(\n collection: string,\n options?: CreateCollectionOptions,\n): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.create`,\n label: `Create collection ${collection}`,\n operationClass: 'additive',\n precheck: [\n {\n description: `collection ${collection} does not exist`,\n source: new ListCollectionsCommand(),\n filter: MongoFieldFilter.eq('name', collection),\n expect: 'notExists',\n },\n ],\n execute: [\n {\n description: `create collection ${collection}`,\n command: new CreateCollectionCommand(collection, options),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function dropCollection(collection: string): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.drop`,\n label: `Drop collection ${collection}`,\n operationClass: 'destructive',\n precheck: [],\n execute: [\n {\n description: `drop collection ${collection}`,\n command: new DropCollectionCommand(collection),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function setValidation(\n collection: string,\n schema: Record<string, unknown>,\n options?: { validationLevel?: 'strict' | 'moderate'; validationAction?: 'error' | 'warn' },\n): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.setValidation`,\n label: `Set validation on ${collection}`,\n operationClass: 'destructive',\n precheck: [],\n execute: [\n {\n description: `set validation on ${collection}`,\n command: new CollModCommand(collection, {\n validator: { $jsonSchema: schema },\n validationLevel: options?.validationLevel,\n validationAction: options?.validationAction,\n }),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function collMod(\n collection: string,\n options: CollModOptions,\n meta?: CollModMeta,\n): MongoMigrationPlanOperation {\n const hasValidator = options.validator != null && Object.keys(options.validator).length > 0;\n\n return {\n id: meta?.id ?? `collection.${collection}.collMod`,\n label: meta?.label ?? `Modify collection ${collection}`,\n operationClass: meta?.operationClass ?? 'destructive',\n precheck:\n options.validator != null\n ? [\n {\n description: `collection ${collection} exists`,\n source: new ListCollectionsCommand(),\n filter: MongoFieldFilter.eq('name', collection),\n expect: 'exists' as const,\n },\n ]\n : [],\n execute: [\n {\n description: `modify ${collection}`,\n command: new CollModCommand(collection, options),\n },\n ],\n postcheck: hasValidator\n ? [\n {\n description: `validator applied on ${collection}`,\n source: new ListCollectionsCommand(),\n filter: MongoAndExpr.of([\n MongoFieldFilter.eq('name', collection),\n ...(options.validationLevel\n ? [MongoFieldFilter.eq('options.validationLevel', options.validationLevel)]\n : []),\n ...(options.validationAction\n ? [MongoFieldFilter.eq('options.validationAction', options.validationAction)]\n : []),\n ]),\n expect: 'exists' as const,\n },\n ]\n : [],\n };\n}\n\nexport function validatedCollection(\n name: string,\n schema: Record<string, unknown>,\n indexes: ReadonlyArray<{ keys: MongoIndexKey[]; unique?: boolean }>,\n): MongoMigrationPlanOperation[] {\n return [\n createCollection(name, {\n validator: { $jsonSchema: schema },\n validationLevel: 'strict',\n validationAction: 'error',\n }),\n ...indexes.map((idx) => createIndex(name, idx.keys, { unique: idx.unique })),\n ];\n}\n"],"mappings":"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"}