@prisma/orm-target-mongo 8.0.0-rc.18-dev.31 → 8.0.0-rc.18-dev.32

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.
@@ -11,17 +11,17 @@ import { AggregateCommand, MongoAddFieldsStage, MongoLimitStage, MongoLookupStag
11
11
  import { canonicalizeSchemasForVerification, contractToMongoSchemaIR, diffMongoSchemas, fieldRewriteOf } from "@prisma/orm-family-mongo/family/control";
12
12
  import { notOk, ok } from "@prisma/orm-framework/utils/result";
13
13
  import { MongoContractSerializerBase, MongoSchemaVerifierBase } from "@prisma/orm-family-mongo/family/ir";
14
- import { MongoCollection, MongoStorage, collectionControlPoliciesOf, collectionControlPolicyOf, collectionRootModelsOf, storedFieldNameOf, storesField, validatorFieldNamesOf, validatorRootShapesOf } from "@prisma/orm-family-mongo/contract";
14
+ import { MongoCollection, MongoStorage, collectionControlPoliciesOf, collectionControlPolicyOf, collectionRootModelsOf, hasOwnFieldStoredAs, modelAndBasesOf, mongoModelsOf, ownEmbedStoredNamesOf, ownStoredNameOf, ownStoredNamesOf, validatorFieldNamesOf, validatorRootShapesOf } from "@prisma/orm-family-mongo/contract";
15
15
  import { composeMongoEntityKinds } from "@prisma/orm-family-mongo/contract/entity-kinds";
16
16
  import { asNamespaceId, governingControlPolicy } from "@prisma/orm-framework/contract/types";
17
17
  import { APP_SPACE_ID, VERIFY_CODE_SCHEMA_FAILURE, contractSnapshotJsonSpecifier, contractSnapshotTypesSpecifier, describeMigrationStatement, issueOutcome, modelDisplayName } from "@prisma/orm-framework/components/control";
18
+ import { InternalError } from "@prisma/orm-framework/utils/internal-error";
18
19
  import { TsExpression, jsonToTsSource, renderImports } from "@prisma/orm-framework/ts-render";
19
20
  import { Migration } from "@prisma/orm-toolchain/migration-tools/migration";
20
21
  import { resolveRequirementSpecifiers } from "@prisma/orm-framework/components/emission";
21
22
  import { detectScaffoldRuntime, shebangLineFor } from "@prisma/orm-toolchain/migration-tools/migration-ts";
22
23
  import { errorRunnerFailed } from "@prisma/orm-framework/errors/execution";
23
24
  import { verifyMongoSchema } from "@prisma/orm-family-mongo/family/schema-verify";
24
- import { InternalError } from "@prisma/orm-framework/utils/internal-error";
25
25
  //#region ../../../3-mongo-target/1-mongo-target/dist/control.mjs
26
26
  /**
27
27
  * Mongo target `Namespace` concretion. In Mongo the "namespace" concept
@@ -384,8 +384,19 @@ function collectionOf(contract, coordinate) {
384
384
  return typeof collection === "string" ? collection : coordinate.model;
385
385
  }
386
386
  function modelAt(contract, coordinate) {
387
- const namespace = contract.domain.namespaces[coordinate.namespaceId];
388
- return namespace !== void 0 && Object.hasOwn(namespace.models, coordinate.model) ? namespace.models[coordinate.model] : void 0;
387
+ const models = mongoModelsOf(contract.domain, coordinate.namespaceId);
388
+ return Object.hasOwn(models, coordinate.model) ? models[coordinate.model] : void 0;
389
+ }
390
+ /** The stored name of `field` of the model at `coordinate`, or undefined when the model does not declare it. */
391
+ function storedFieldNameAt(contract, coordinate) {
392
+ const model = modelAt(contract, coordinate);
393
+ return model === void 0 || !Object.hasOwn(model.fields, coordinate.field) ? void 0 : ownStoredNameOf(model, coordinate.field);
394
+ }
395
+ /** The stored name of a field the statement's resolution found on the model at `coordinate`. */
396
+ function resolvedStoredNameAt(contract, coordinate) {
397
+ const storedName = storedFieldNameAt(contract, coordinate);
398
+ if (storedName === void 0) throw new InternalError(`Field "${coordinate.model}.${coordinate.field}" a statement names has no stored name`);
399
+ return storedName;
389
400
  }
390
401
  /**
391
402
  * The condition that selects a variant's documents in the collection it shares with its base: the
@@ -394,10 +405,13 @@ function modelAt(contract, coordinate) {
394
405
  function variantFilter(contract, coordinate) {
395
406
  const base = modelAt(contract, coordinate)?.base;
396
407
  if (base === void 0) return void 0;
397
- const baseModel = contract.domain.namespaces[base.namespace]?.models[base.model];
408
+ const baseModel = modelAt(contract, {
409
+ namespaceId: asNamespaceId(base.namespace),
410
+ model: base.model
411
+ });
398
412
  const field = baseModel?.discriminator?.field;
399
413
  const value = Object.entries(baseModel?.variants ?? {}).find(([, variant]) => variant.namespace === coordinate.namespaceId && variant.model === coordinate.model)?.[0];
400
- return field === void 0 || value === void 0 ? void 0 : { [field]: value };
414
+ return baseModel === void 0 || field === void 0 || value === void 0 ? void 0 : { [ownStoredNameOf(baseModel, field)]: value };
401
415
  }
402
416
  function declaredCollection(contract, collection) {
403
417
  return Object.values(contract.storage.namespaces).map((namespace) => namespace.entries["collection"]?.[collection]).find((entry) => entry instanceof MongoCollection);
@@ -467,21 +481,18 @@ function discriminatorValueOf(filter) {
467
481
  * The other models stored in `collection`, root models and their variants, whose documents a change of `storedName` with `filter` reaches, and which store the field themselves or through their base. No discriminator tells the documents of several root models apart, so without a filter, or with a base model's filter, every other root model's documents are reached. A variant's filter, one discriminator value, reaches a model only when its documents can hold that value: a variant of another root model with the same discriminator field and value, or a model that declares the discriminator field itself.
468
482
  */
469
483
  function otherModelsReached(contract, model, collection, filter, storedName) {
470
- const models = contract.domain.namespaces[model.namespaceId]?.models ?? {};
484
+ const models = mongoModelsOf(contract.domain, model.namespaceId);
471
485
  const baseOf = (name) => {
472
486
  const base = models[name]?.base;
473
487
  return base === void 0 ? void 0 : models[base.model];
474
488
  };
475
- const storesOrInherits = (name, field) => {
476
- const each = models[name];
477
- const base = baseOf(name);
478
- return each !== void 0 && storesField(each, field) || base !== void 0 && storesField(base, field);
479
- };
489
+ const storesOrInherits = (name, storedName) => modelAndBasesOf(models, name).some((each) => ownStores(each, storedName));
480
490
  const selected = discriminatorValueOf(filter);
481
491
  const canHold = (name) => {
482
492
  if (selected === void 0) return true;
483
493
  const base = baseOf(name);
484
- if (base?.discriminator?.field === selected.field) return Object.entries(base.variants ?? {}).some(([value, variant]) => variant.model === name && value === selected.value);
494
+ const discriminator = base?.discriminator?.field;
495
+ if (base !== void 0 && discriminator !== void 0 && ownStoredNameOf(base, discriminator) === selected.field) return Object.entries(base.variants ?? {}).some(([value, variant]) => variant.model === name && value === selected.value);
485
496
  return storesOrInherits(name, selected.field);
486
497
  };
487
498
  const subjectRoot = modelAt(contract, model)?.base?.model ?? model.model;
@@ -633,28 +644,28 @@ function planFieldStatement(statement, label, renamedCollections, schema, input)
633
644
  if (fromField.kind === "embed" || toField.kind === "embed") return refuse("the field is an embedded relation", `Renaming an embedded relation is not supported in this release. ${LEAVE_OUT}`);
634
645
  if (fromField.kind !== "field" && toField.kind !== "field") return { kind: "unchanged" };
635
646
  if (fromField.kind !== "field" || toField.kind !== "field") return refuse("the field is stored in documents on one side of the statement only", `A relation is not stored in documents, so a statement cannot rename a field to or from one. ${LEAVE_OUT}`);
636
- if (statement.from.field === "_id" || statement.to.field === "_id") return refuse("it renames _id", `MongoDB does not let a document change its _id. ${LEAVE_OUT}`);
637
- const unsafeFrom = unrewritableFieldNameReason(statement.from.field);
638
- if (unsafeFrom !== void 0) return refuse(`the stored field name "${statement.from.field}" cannot be rewritten`, `${unsafeFrom}, so a statement cannot move its values. Rename it in the database yourself, or ${LEAVE_OUT.toLowerCase()}`);
639
- const unsafeTo = unrewritableFieldNameReason(statement.to.field);
640
- if (unsafeTo !== void 0) return refuse(`the stored field name "${statement.to.field}" cannot be written`, `${unsafeTo}. Give the field a stored name without "." that does not start with "$" (with @map), or ${LEAVE_OUT.toLowerCase()}`);
647
+ const fromStored = resolvedStoredNameAt(input.fromContract, statement.from);
648
+ const toStored = resolvedStoredNameAt(input.contract, statement.to);
641
649
  const originCollection = collectionOf(input.fromContract, statement.from);
642
650
  const collection = renamedCollections.get(originCollection) ?? originCollection;
643
651
  const destinationCollection = collectionOf(input.contract, statement.to);
652
+ if (fromStored === toStored && collection === destinationCollection) return { kind: "unchanged" };
653
+ if (fromStored === "_id" || toStored === "_id") return refuse("it renames _id", `MongoDB does not let a document change its _id. ${LEAVE_OUT}`);
654
+ const unsafeFrom = unrewritableFieldNameReason(fromStored);
655
+ if (unsafeFrom !== void 0) return refuse(`the stored name "${fromStored}" cannot be rewritten`, `${unsafeFrom}, so a statement cannot move its values. Rename it in the database yourself, or ${LEAVE_OUT.toLowerCase()}`);
656
+ const unsafeTo = unrewritableFieldNameReason(toStored);
657
+ if (unsafeTo !== void 0) return refuse(`the stored name "${toStored}" cannot be written`, `${unsafeTo}. Give the field a stored name without "." that does not start with "$" (with @map), or ${LEAVE_OUT.toLowerCase()}`);
644
658
  if (collection !== destinationCollection) return refuse(`the model's collection changes from "${originCollection}" to "${destinationCollection}", and no statement renames it`, collectionChangeAdvice(statement, originCollection, destinationCollection));
645
- if (statement.from.field === statement.to.field) return { kind: "unchanged" };
646
- if (schema.collection(collection)?.options?.timeseries !== void 0) return refuse(`collection "${collection}" is a time-series collection`, `${TIME_SERIES_UPDATES}, so a statement cannot move the values of a field in them. Keep the stored name: name the field ${JSON.stringify(statement.from.field)} in the contract (with @map(${JSON.stringify(statement.from.field)}) in PSL), then run this command again without this statement.`);
659
+ if (schema.collection(collection)?.options?.timeseries !== void 0) return refuse(`collection "${collection}" is a time-series collection`, `${TIME_SERIES_UPDATES}, so a statement cannot move the values of a field in them. Keep the stored name: store the field as ${JSON.stringify(fromStored)} (with @map(${JSON.stringify(fromStored)}) in PSL), then run this command again without this statement.`);
647
660
  const policyRefusal = controlPolicyRefusal(statement, label, input.contract, collection);
648
661
  if (policyRefusal !== void 0) return policyRefusal;
649
662
  const variant = variantFilter(input.fromContract, statement.from);
650
- const fromModel = modelAt(input.fromContract, statement.from);
651
- const renamedTogether = new Set(input.statements.flatMap((other) => other.entity === "field" && other.from.namespaceId === statement.from.namespaceId && other.from.field === statement.from.field && other.to.field === statement.to.field ? [other.from.model] : []));
652
- const others = otherModelsReached(input.fromContract, statement.from, originCollection, variant, fromModel === void 0 ? statement.from.field : storedFieldNameOf(fromModel, statement.from.field)).filter((name) => !renamedTogether.has(name));
653
- if (others.length > 0) return refuse(`model ${others.map((name) => JSON.stringify(name)).join(", ")} stores field ${JSON.stringify(statement.from.field)} in the same collection, ${JSON.stringify(collection)}`, `${sharedCollectionWhy(collection, others, "renaming the field would rename it in the documents")} To change it, ${SEPARATE_THE_MODELS}.`);
654
- const filter = variant ?? rootDocumentsLosing(input.contract, statement.to, statement.from.field)?.filter;
663
+ const renamedTogether = new Set(input.statements.flatMap((other) => other.entity === "field" && other.from.namespaceId === statement.from.namespaceId && storedFieldNameAt(input.fromContract, other.from) === fromStored && storedFieldNameAt(input.contract, other.to) === toStored ? [other.from.model] : []));
664
+ const others = otherModelsReached(input.fromContract, statement.from, originCollection, variant, fromStored).filter((name) => !renamedTogether.has(name));
665
+ if (others.length > 0) return refuse(`model ${others.map((name) => JSON.stringify(name)).join(", ")} stores field ${JSON.stringify(fromStored)} in the same collection, ${JSON.stringify(collection)}`, `${sharedCollectionWhy(collection, others, "renaming the field would rename it in the documents")} To change it, ${SEPARATE_THE_MODELS}.`);
655
666
  return {
656
667
  kind: "renameField",
657
- call: new RenameFieldCall(collection, statement.from.field, statement.to.field, filter)
668
+ call: new RenameFieldCall(collection, fromStored, toStored, variant ?? rootDocumentsLosing(input.contract, statement.to, fromStored)?.filter)
658
669
  };
659
670
  }
660
671
  function collectionChangeAdvice(statement, from, to) {
@@ -697,30 +708,33 @@ function destinationModelOf(origin, input) {
697
708
  function renamedByStatement(input, field) {
698
709
  return input.statements.some((statement) => statement.entity === "field" && sameModel(statement.from, field) && statement.from.field === field.field);
699
710
  }
711
+ /** Whether one of `model`'s own fields or embed relations is stored under `storedName`. */
712
+ function ownStores(model, storedName) {
713
+ return hasOwnFieldStoredAs(model, storedName) || Object.values(ownEmbedStoredNamesOf(model)).includes(storedName);
714
+ }
715
+ /** The stored names of the documents of the model at `coordinate`: its fields and embed relations, its own and those it inherits. */
700
716
  function storedFieldsOf(contract, coordinate) {
701
- const model = modelAt(contract, coordinate);
702
- if (model === void 0) return /* @__PURE__ */ new Set();
703
- const base = model.base === void 0 ? void 0 : modelAt(contract, {
704
- namespaceId: asNamespaceId(model.base.namespace),
705
- model: model.base.model
706
- });
707
- return /* @__PURE__ */ new Set([...Object.keys(model.fields), ...Object.keys(base?.fields ?? {})]);
717
+ const models = mongoModelsOf(contract.domain, coordinate.namespaceId);
718
+ return new Set(modelAndBasesOf(models, coordinate.model).flatMap((model) => [...Object.values(ownStoredNamesOf(model)), ...Object.values(ownEmbedStoredNamesOf(model))]));
708
719
  }
709
720
  /**
710
721
  * The documents of a root model that lose `field`: all of them, or, when some of its destination
711
722
  * variants now store the field themselves, those whose discriminator value is none of theirs.
712
723
  * `undefined` when the root model itself still stores it.
713
724
  */
714
- function rootDocumentsLosing(contract, root, field) {
725
+ function rootDocumentsLosing(contract, root, storedName) {
715
726
  const model = modelAt(contract, root);
716
- if (model === void 0 || Object.hasOwn(model.fields, field)) return void 0;
727
+ if (model === void 0 || ownStores(model, storedName)) return void 0;
717
728
  const discriminator = model.discriminator?.field;
718
- const keeping = Object.entries(model.variants ?? {}).filter(([, variant]) => Object.hasOwn(modelAt(contract, {
719
- namespaceId: variant.namespace,
720
- model: variant.model
721
- })?.fields ?? {}, field)).map(([value]) => value).sort();
729
+ const keeping = Object.entries(model.variants ?? {}).filter(([, variant]) => {
730
+ const variantModel = modelAt(contract, {
731
+ namespaceId: variant.namespace,
732
+ model: variant.model
733
+ });
734
+ return variantModel !== void 0 && ownStores(variantModel, storedName);
735
+ }).map(([value]) => value).sort();
722
736
  if (discriminator === void 0 || keeping.length === 0) return { filter: void 0 };
723
- return { filter: { [discriminator]: { $nin: keeping } } };
737
+ return { filter: { [ownStoredNameOf(model, discriminator)]: { $nin: keeping } } };
724
738
  }
725
739
  /**
726
740
  * Each field an origin model stores that its destination model no longer stores, in a collection
@@ -739,17 +753,17 @@ function planFieldRemovals(input) {
739
753
  const model = modelAt(input.fromContract, origin);
740
754
  if (model === void 0) return [];
741
755
  const variant = variantFilter(input.fromContract, origin);
742
- const losing = (field) => {
743
- if (variant !== void 0) return storedFieldsOf(input.contract, destination).has(field) ? void 0 : { filter: variant };
744
- return rootDocumentsLosing(input.contract, destination, field);
756
+ const losing = (storedName) => {
757
+ if (variant !== void 0) return storedFieldsOf(input.contract, destination).has(storedName) ? void 0 : { filter: variant };
758
+ return rootDocumentsLosing(input.contract, destination, storedName);
745
759
  };
746
- return Object.keys(model.fields).filter((field) => unrewritableFieldNameReason(field) === void 0 && !renamedByStatement(input, {
760
+ return Object.entries(ownStoredNamesOf(model)).filter(([field, storedName]) => unrewritableFieldNameReason(storedName) === void 0 && !renamedByStatement(input, {
747
761
  ...origin,
748
762
  field
749
- })).sort().flatMap((field) => {
750
- const lost = losing(field);
763
+ })).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).flatMap(([field, storedName]) => {
764
+ const lost = losing(storedName);
751
765
  return lost === void 0 ? [] : [{
752
- call: new UnsetFieldCall(collection, field, lost.filter),
766
+ call: new UnsetFieldCall(collection, storedName, lost.filter),
753
767
  subject: {
754
768
  kind: "field",
755
769
  ...origin,
@@ -2360,4 +2374,4 @@ const mongoTargetDescriptor = {
2360
2374
  //#endregion
2361
2375
  export { schemaIndexToCreateIndexOptions as C, schemaCollectionToCreateCollectionOptions as S, deserializeMongoOp as _, DropIndexCall as a, renderCallsToTypeScript as b, MongoMigrationRunner as c, MongoTargetSchemaVerifier as d, MongoTargetUnboundDatabase as f, UnsetFieldCall as g, RenameFieldCall as h, DropCollectionCall as i, MongoTargetContractSerializer as l, RenameCollectionCall as m, CreateCollectionCall as n, FilterEvaluator as o, PlannerProducedMongoMigration as p, CreateIndexCall as r, MongoMigrationPlanner as s, CollModCall as t, MongoTargetDatabase as u, deserializeMongoOps as v, serializeMongoOps as w, renderOps as x, mongoTargetDescriptor as y };
2362
2376
 
2363
- //# sourceMappingURL=control-lAuWLUE_.mjs.map
2377
+ //# sourceMappingURL=control-CfztdRLD.mjs.map