@danceroutine/tango-schema 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/internal/InternalReferentialAction.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/model/decorators/domain/RelationDecoratorConfig.d.ts +4 -0
- package/dist/model/decorators/domain/TangoFieldMeta.d.ts +59 -0
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.js +2 -2
- package/dist/model/registry/ModelRegistry.d.ts +1 -0
- package/dist/model/registry/ResolvedRelationGraphSnapshot.d.ts +84 -0
- package/dist/model/relations/ImplicitManyToManyIdentifier.d.ts +45 -0
- package/dist/model/relations/ImplicitManyToManyThroughFactory.d.ts +14 -0
- package/dist/model/relations/ResolvedRelationGraph.d.ts +6 -0
- package/dist/model/relations/ResolvedRelationGraphBuilder.d.ts +1 -1
- package/dist/model/relations/SchemaNaming.d.ts +1 -0
- package/dist/{model-BxkSqwrt.js → model-upj6jxaK.js} +291 -13
- package/dist/model-upj6jxaK.js.map +1 -0
- package/package.json +2 -2
- package/dist/model-BxkSqwrt.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getLogger, getLogger as getLogger$1 } from "@danceroutine/tango-core";
|
|
2
|
-
import { z } from "zod";
|
|
2
|
+
import { z, z as z$1 } from "zod";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
4
|
import { existsSync, existsSync as existsSync$1, readFileSync } from "node:fs";
|
|
5
5
|
import { dirname, extname, resolve, resolve as resolve$1 } from "node:path";
|
|
6
|
-
import { createHash } from "node:crypto";
|
|
6
|
+
import { createHash, createHash as createHash$1 } from "node:crypto";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
9
|
//#region rolldown:runtime
|
|
@@ -178,7 +178,7 @@ function foreignKey(target, schemaOrOptions, maybeOptions) {
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
const config = schemaOrOptions;
|
|
181
|
-
const schema = config?.field ?? z.number().int();
|
|
181
|
+
const schema = config?.field ?? z$1.number().int();
|
|
182
182
|
return applyRelationMetadata(schema, {
|
|
183
183
|
relationKind: InternalDecoratedFieldKind.FOREIGN_KEY,
|
|
184
184
|
references: {
|
|
@@ -201,7 +201,7 @@ function oneToOne(target, schemaOrOptions, maybeOptions) {
|
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
203
|
const config = schemaOrOptions;
|
|
204
|
-
const schema = config?.field ?? z.number().int();
|
|
204
|
+
const schema = config?.field ?? z$1.number().int();
|
|
205
205
|
return applyRelationMetadata(schema, {
|
|
206
206
|
relationKind: InternalDecoratedFieldKind.ONE_TO_ONE,
|
|
207
207
|
unique: true,
|
|
@@ -222,10 +222,17 @@ function manyToMany(target, schemaOrConfig) {
|
|
|
222
222
|
}
|
|
223
223
|
if (schemaOrConfig?.relatedName !== undefined) throw new Error("t.manyToMany(...) does not support relatedName yet.");
|
|
224
224
|
const config = schemaOrConfig;
|
|
225
|
-
const
|
|
225
|
+
const hasPartialThroughConfig = (config?.through !== undefined || config?.throughSourceFieldName !== undefined || config?.throughTargetFieldName !== undefined) && !(config?.through && config.throughSourceFieldName && config.throughTargetFieldName);
|
|
226
|
+
if (hasPartialThroughConfig) throw new Error("t.manyToMany(...) through config requires through, throughSourceFieldName, and throughTargetFieldName.");
|
|
227
|
+
const schema = config?.field ?? z$1.array(z$1.number().int());
|
|
226
228
|
return applyRelationMetadata(schema, {
|
|
227
229
|
relationKind: InternalDecoratedFieldKind.MANY_TO_MANY,
|
|
228
|
-
references: {
|
|
230
|
+
references: {
|
|
231
|
+
target,
|
|
232
|
+
through: config?.through,
|
|
233
|
+
throughSourceFieldName: config?.throughSourceFieldName,
|
|
234
|
+
throughTargetFieldName: config?.throughTargetFieldName
|
|
235
|
+
}
|
|
229
236
|
}, config);
|
|
230
237
|
}
|
|
231
238
|
const Decorators = {
|
|
@@ -597,6 +604,9 @@ var RelationDescriptorNormalizer = class RelationDescriptorNormalizer {
|
|
|
597
604
|
explicitForwardName: meta.forwardName,
|
|
598
605
|
explicitReverseName: meta.reverseName,
|
|
599
606
|
namingHint: this.deriveNamingHint(candidate.sourceSchemaFieldKey),
|
|
607
|
+
throughModelRef: meta.references.through,
|
|
608
|
+
throughSourceFieldName: meta.references.throughSourceFieldName,
|
|
609
|
+
throughTargetFieldName: meta.references.throughTargetFieldName,
|
|
600
610
|
provenance: "field-decorator"
|
|
601
611
|
};
|
|
602
612
|
}
|
|
@@ -623,6 +633,10 @@ function pluralize(value) {
|
|
|
623
633
|
function deriveTableName(name) {
|
|
624
634
|
return pluralize(toSnakeCase(name));
|
|
625
635
|
}
|
|
636
|
+
function decapitalizeModelName(name) {
|
|
637
|
+
if (name.length === 0) return name;
|
|
638
|
+
return `${name[0].toLowerCase()}${name.slice(1)}`;
|
|
639
|
+
}
|
|
626
640
|
|
|
627
641
|
//#endregion
|
|
628
642
|
//#region src/model/internal/InternalSchemaModel.ts
|
|
@@ -736,6 +750,14 @@ var InternalSchemaModel = class InternalSchemaModel {
|
|
|
736
750
|
}
|
|
737
751
|
};
|
|
738
752
|
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region src/model/relations/NormalizedRelationStorageDescriptor.ts
|
|
755
|
+
const InternalNormalizedRelationOrigin = {
|
|
756
|
+
FOREIGN_KEY: "foreignKey",
|
|
757
|
+
ONE_TO_ONE: "oneToOne",
|
|
758
|
+
MANY_TO_MANY: "manyToMany"
|
|
759
|
+
};
|
|
760
|
+
|
|
739
761
|
//#endregion
|
|
740
762
|
//#region src/model/relations/RelationSpec.ts
|
|
741
763
|
const InternalRelationPublicKind = {
|
|
@@ -759,6 +781,216 @@ const InternalRelationProvenance = {
|
|
|
759
781
|
SYNTHESIZED_REVERSE: "synthesized-reverse"
|
|
760
782
|
};
|
|
761
783
|
|
|
784
|
+
//#endregion
|
|
785
|
+
//#region src/model/relations/ImplicitManyToManyIdentifier.ts
|
|
786
|
+
var ImplicitManyToManyIdentifier = class ImplicitManyToManyIdentifier {
|
|
787
|
+
static NAMESPACE = "tango.implicit";
|
|
788
|
+
/**
|
|
789
|
+
* Stable model key for the synthesized through model connecting
|
|
790
|
+
* `sourceModelKey` to `targetModelKey` via the schema field
|
|
791
|
+
* `sourceSchemaFieldKey`.
|
|
792
|
+
*
|
|
793
|
+
* The returned key is deterministic across runs so storage and hydration
|
|
794
|
+
* artifacts stay stable as long as the inputs match.
|
|
795
|
+
*/
|
|
796
|
+
static getModelKey(sourceModelKey, sourceSchemaFieldKey, targetModelKey) {
|
|
797
|
+
const digest = ImplicitManyToManyIdentifier.digest(sourceModelKey, sourceSchemaFieldKey, targetModelKey, 32);
|
|
798
|
+
return `${ImplicitManyToManyIdentifier.NAMESPACE}/m2m_${digest}`;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Deterministic short digest used to derive the physical join-table name
|
|
802
|
+
* for a synthesized through model. Shorter than the model-key digest so
|
|
803
|
+
* table names stay within common SQL identifier limits.
|
|
804
|
+
*/
|
|
805
|
+
static getTableBaseDigest(sourceModelKey, sourceSchemaFieldKey, targetModelKey) {
|
|
806
|
+
return ImplicitManyToManyIdentifier.digest(sourceModelKey, sourceSchemaFieldKey, targetModelKey, 16);
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* True when `modelKey` was produced by {@link getModelKey} and therefore
|
|
810
|
+
* identifies a synthesized through model. Callers use this instead of
|
|
811
|
+
* comparing namespace prefixes so the namespace remains an implementation
|
|
812
|
+
* detail of this class.
|
|
813
|
+
*/
|
|
814
|
+
static isImplicitManyToManyModel(modelKey) {
|
|
815
|
+
return modelKey.startsWith(`${ImplicitManyToManyIdentifier.NAMESPACE}/`);
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Namespace under which synthesized through models are registered.
|
|
819
|
+
* Exposed so {@link ImplicitManyToManyThroughFactory} can construct the
|
|
820
|
+
* model with the correct namespace. External callers that want to ask
|
|
821
|
+
* "is this an implicit model" should prefer {@link isImplicitManyToManyModel}.
|
|
822
|
+
*/
|
|
823
|
+
static getNamespace() {
|
|
824
|
+
return ImplicitManyToManyIdentifier.NAMESPACE;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Extract the `m2m_<digest>` component of a synthesized model key so the
|
|
828
|
+
* factory can register the through model with a deterministic name while
|
|
829
|
+
* keeping the namespace owned by this class.
|
|
830
|
+
*/
|
|
831
|
+
static getModelName(modelKey) {
|
|
832
|
+
const prefix = `${ImplicitManyToManyIdentifier.NAMESPACE}/`;
|
|
833
|
+
if (!modelKey.startsWith(prefix)) throw new Error(`ImplicitManyToManyIdentifier.getModelName expected a key produced by getModelKey, received '${modelKey}'.`);
|
|
834
|
+
return modelKey.slice(prefix.length);
|
|
835
|
+
}
|
|
836
|
+
static digest(sourceModelKey, sourceSchemaFieldKey, targetModelKey, byteLength) {
|
|
837
|
+
return createHash$1("sha256").update(`${sourceModelKey}\0${sourceSchemaFieldKey}\0${targetModelKey}`, "utf8").digest("hex").slice(0, byteLength);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
//#endregion
|
|
842
|
+
//#region src/domain/internal/InternalReferentialAction.ts
|
|
843
|
+
const InternalReferentialAction = {
|
|
844
|
+
CASCADE: "CASCADE",
|
|
845
|
+
SET_NULL: "SET NULL",
|
|
846
|
+
RESTRICT: "RESTRICT",
|
|
847
|
+
NO_ACTION: "NO ACTION"
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region src/model/relations/ImplicitManyToManyThroughFactory.ts
|
|
852
|
+
var ImplicitManyToManyThroughFactory = class ImplicitManyToManyThroughFactory {
|
|
853
|
+
static throughFieldNames(sourceModel, targetModel) {
|
|
854
|
+
if (sourceModel.metadata.key === targetModel.metadata.key) return {
|
|
855
|
+
throughSourceFieldName: `from${sourceModel.metadata.name}`,
|
|
856
|
+
throughTargetFieldName: `to${targetModel.metadata.name}`
|
|
857
|
+
};
|
|
858
|
+
return {
|
|
859
|
+
throughSourceFieldName: `${decapitalizeModelName(sourceModel.metadata.name)}Id`,
|
|
860
|
+
throughTargetFieldName: `${decapitalizeModelName(targetModel.metadata.name)}Id`
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
static buildModels(registry) {
|
|
864
|
+
const descriptors = ImplicitManyToManyThroughFactory.collectImplicitDescriptors(registry);
|
|
865
|
+
const models = [];
|
|
866
|
+
const occupiedTables = new Set([...registry.values()].map((m) => m.metadata.table.trim().toLowerCase()));
|
|
867
|
+
for (const descriptor of descriptors) {
|
|
868
|
+
const sourceModel = registry.getByKey(descriptor.sourceModelKey);
|
|
869
|
+
const targetModel = registry.resolveRef(descriptor.targetRef);
|
|
870
|
+
const identityKey = ImplicitManyToManyIdentifier.getModelKey(descriptor.sourceModelKey, descriptor.sourceSchemaFieldKey, targetModel.metadata.key);
|
|
871
|
+
const pkSource = ImplicitManyToManyThroughFactory.readSinglePrimaryKey(sourceModel);
|
|
872
|
+
const pkTarget = ImplicitManyToManyThroughFactory.readSinglePrimaryKey(targetModel);
|
|
873
|
+
const digest = ImplicitManyToManyIdentifier.getTableBaseDigest(descriptor.sourceModelKey, descriptor.sourceSchemaFieldKey, targetModel.metadata.key);
|
|
874
|
+
const tableName = ImplicitManyToManyThroughFactory.allocateTableName(occupiedTables, digest);
|
|
875
|
+
const selfReferential = sourceModel.metadata.key === targetModel.metadata.key;
|
|
876
|
+
let throughSchema;
|
|
877
|
+
let indexes;
|
|
878
|
+
if (selfReferential) {
|
|
879
|
+
const leftKey = `from${sourceModel.metadata.name}`;
|
|
880
|
+
const rightKey = `to${sourceModel.metadata.name}`;
|
|
881
|
+
throughSchema = z.object({
|
|
882
|
+
id: Decorators.primaryKey(z.number().int()),
|
|
883
|
+
[leftKey]: Decorators.foreignKey(sourceModel, {
|
|
884
|
+
field: pkSource.zod,
|
|
885
|
+
onDelete: InternalReferentialAction.CASCADE,
|
|
886
|
+
onUpdate: InternalReferentialAction.CASCADE
|
|
887
|
+
}),
|
|
888
|
+
[rightKey]: Decorators.foreignKey(sourceModel, {
|
|
889
|
+
field: pkTarget.zod,
|
|
890
|
+
onDelete: InternalReferentialAction.CASCADE,
|
|
891
|
+
onUpdate: InternalReferentialAction.CASCADE
|
|
892
|
+
})
|
|
893
|
+
});
|
|
894
|
+
indexes = [{
|
|
895
|
+
name: `${tableName}_uniq_pair`,
|
|
896
|
+
on: [leftKey, rightKey],
|
|
897
|
+
unique: true
|
|
898
|
+
}];
|
|
899
|
+
} else {
|
|
900
|
+
const sourceIdKey = `${decapitalizeModelName(sourceModel.metadata.name)}Id`;
|
|
901
|
+
const targetIdKey = `${decapitalizeModelName(targetModel.metadata.name)}Id`;
|
|
902
|
+
throughSchema = z.object({
|
|
903
|
+
id: Decorators.primaryKey(z.number().int()),
|
|
904
|
+
[sourceIdKey]: Decorators.foreignKey(sourceModel, {
|
|
905
|
+
field: pkSource.zod,
|
|
906
|
+
onDelete: InternalReferentialAction.CASCADE,
|
|
907
|
+
onUpdate: InternalReferentialAction.CASCADE
|
|
908
|
+
}),
|
|
909
|
+
[targetIdKey]: Decorators.foreignKey(targetModel, {
|
|
910
|
+
field: pkTarget.zod,
|
|
911
|
+
onDelete: InternalReferentialAction.CASCADE,
|
|
912
|
+
onUpdate: InternalReferentialAction.CASCADE
|
|
913
|
+
})
|
|
914
|
+
});
|
|
915
|
+
indexes = [{
|
|
916
|
+
name: `${tableName}_uniq_pair`,
|
|
917
|
+
on: [sourceIdKey, targetIdKey],
|
|
918
|
+
unique: true
|
|
919
|
+
}];
|
|
920
|
+
}
|
|
921
|
+
const modelNamePart = ImplicitManyToManyIdentifier.getModelName(identityKey);
|
|
922
|
+
const throughModel = InternalSchemaModel.create({
|
|
923
|
+
namespace: ImplicitManyToManyIdentifier.getNamespace(),
|
|
924
|
+
name: modelNamePart,
|
|
925
|
+
table: tableName,
|
|
926
|
+
schema: throughSchema,
|
|
927
|
+
registry,
|
|
928
|
+
indexes,
|
|
929
|
+
managed: true
|
|
930
|
+
}, registry);
|
|
931
|
+
models.push(throughModel);
|
|
932
|
+
}
|
|
933
|
+
return models;
|
|
934
|
+
}
|
|
935
|
+
static unwrapForForeignKeyField(zodType) {
|
|
936
|
+
let inner = zodType;
|
|
937
|
+
while (isZodOptional(inner)) inner = inner.unwrap();
|
|
938
|
+
while (isZodNullable(inner)) inner = inner.unwrap();
|
|
939
|
+
while (isZodDefault(inner)) inner = inner.removeDefault();
|
|
940
|
+
return inner;
|
|
941
|
+
}
|
|
942
|
+
static clonePrimaryKeySchemaForForeignKey(zodType) {
|
|
943
|
+
const unwrapped = ImplicitManyToManyThroughFactory.unwrapForForeignKeyField(zodType);
|
|
944
|
+
if (isZodNumber(unwrapped)) {
|
|
945
|
+
const checks = unwrapped._zod.def.checks ?? [];
|
|
946
|
+
const isInt = checks.some((check) => "format" in check._zod.def && check._zod.def.format === "safeint");
|
|
947
|
+
return isInt ? z.number().int() : z.number();
|
|
948
|
+
}
|
|
949
|
+
if (isZodString(unwrapped)) return z.string();
|
|
950
|
+
if (isZodBoolean(unwrapped)) return z.boolean();
|
|
951
|
+
if (isZodDate(unwrapped)) return z.date();
|
|
952
|
+
if (isZodObject(unwrapped)) return z.object({});
|
|
953
|
+
if (isZodArray(unwrapped)) return z.array(z.unknown());
|
|
954
|
+
throw new Error("Implicit many-to-many primary keys must resolve to a clonable scalar Zod schema.");
|
|
955
|
+
}
|
|
956
|
+
static readSinglePrimaryKey(model) {
|
|
957
|
+
const keys = [];
|
|
958
|
+
for (const [fieldKey$1, zodType$1] of Object.entries(model.schema.shape)) {
|
|
959
|
+
const meta$1 = getFieldMetadata(zodType$1);
|
|
960
|
+
if (meta$1?.primaryKey) keys.push(fieldKey$1);
|
|
961
|
+
}
|
|
962
|
+
if (keys.length !== 1) throw new Error(`Implicit many-to-many requires model '${model.metadata.key}' to declare exactly one primary key field.`);
|
|
963
|
+
const fieldKey = keys[0];
|
|
964
|
+
const zodType = model.schema.shape[fieldKey];
|
|
965
|
+
const meta = getFieldMetadata(zodType);
|
|
966
|
+
return {
|
|
967
|
+
fieldKey,
|
|
968
|
+
zod: ImplicitManyToManyThroughFactory.clonePrimaryKeySchemaForForeignKey(zodType),
|
|
969
|
+
dbColumn: meta?.dbColumn ?? fieldKey
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
static collectImplicitDescriptors(registry) {
|
|
973
|
+
const out = [];
|
|
974
|
+
for (const model of registry.values()) for (const descriptor of InternalSchemaModel.getNormalizedRelations(model)) {
|
|
975
|
+
if (descriptor.origin !== InternalNormalizedRelationOrigin.MANY_TO_MANY) continue;
|
|
976
|
+
if (descriptor.throughModelRef && descriptor.throughSourceFieldName && descriptor.throughTargetFieldName) continue;
|
|
977
|
+
out.push(descriptor);
|
|
978
|
+
}
|
|
979
|
+
return out;
|
|
980
|
+
}
|
|
981
|
+
static allocateTableName(occupied, digest) {
|
|
982
|
+
const base = `m2m_${digest}`;
|
|
983
|
+
let candidate = base;
|
|
984
|
+
let suffix = 0;
|
|
985
|
+
while (occupied.has(candidate.toLowerCase())) {
|
|
986
|
+
suffix += 1;
|
|
987
|
+
candidate = `${base}_${suffix}`;
|
|
988
|
+
}
|
|
989
|
+
occupied.add(candidate.toLowerCase());
|
|
990
|
+
return candidate;
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
|
|
762
994
|
//#endregion
|
|
763
995
|
//#region src/model/relations/ResolvedRelationGraphBuilder.ts
|
|
764
996
|
const REFERENCE_CAPABILITIES = Object.freeze({
|
|
@@ -768,8 +1000,8 @@ const REFERENCE_CAPABILITIES = Object.freeze({
|
|
|
768
1000
|
});
|
|
769
1001
|
const MANY_TO_MANY_CAPABILITIES = Object.freeze({
|
|
770
1002
|
migratable: false,
|
|
771
|
-
queryable:
|
|
772
|
-
hydratable:
|
|
1003
|
+
queryable: true,
|
|
1004
|
+
hydratable: true
|
|
773
1005
|
});
|
|
774
1006
|
const RELATION_NAME_SEPARATOR = ":";
|
|
775
1007
|
var ResolvedRelationGraphBuilder = class ResolvedRelationGraphBuilder {
|
|
@@ -800,6 +1032,12 @@ var ResolvedRelationGraphBuilder = class ResolvedRelationGraphBuilder {
|
|
|
800
1032
|
cardinality: relation.cardinality,
|
|
801
1033
|
localFieldName: relation.localFieldName,
|
|
802
1034
|
targetFieldName: relation.targetFieldName,
|
|
1035
|
+
throughModelKey: relation.throughModelKey,
|
|
1036
|
+
throughTable: relation.throughTable,
|
|
1037
|
+
throughSourceFieldName: relation.throughSourceFieldName,
|
|
1038
|
+
throughTargetFieldName: relation.throughTargetFieldName,
|
|
1039
|
+
throughSourceKey: relation.throughSourceKey,
|
|
1040
|
+
throughTargetKey: relation.throughTargetKey,
|
|
803
1041
|
alias: relation.alias,
|
|
804
1042
|
capabilities: {
|
|
805
1043
|
migratable: relation.capabilities.migratable,
|
|
@@ -826,8 +1064,27 @@ var ResolvedRelationGraphBuilder = class ResolvedRelationGraphBuilder {
|
|
|
826
1064
|
const explicitRelations = InternalSchemaModel.getExplicitRelations(model) ?? {};
|
|
827
1065
|
for (const descriptor of InternalSchemaModel.getNormalizedRelations(model)) {
|
|
828
1066
|
const targetModel = this.options.resolveRef(descriptor.targetRef);
|
|
829
|
-
if (descriptor.origin ===
|
|
1067
|
+
if (descriptor.origin === InternalNormalizedRelationOrigin.MANY_TO_MANY) {
|
|
830
1068
|
const relationName = descriptor.explicitForwardName ?? descriptor.namingHint;
|
|
1069
|
+
let throughModel;
|
|
1070
|
+
let throughSourceFieldName;
|
|
1071
|
+
let throughTargetFieldName;
|
|
1072
|
+
if (descriptor.throughModelRef && descriptor.throughSourceFieldName && descriptor.throughTargetFieldName) {
|
|
1073
|
+
throughModel = this.options.resolveRef(descriptor.throughModelRef);
|
|
1074
|
+
throughSourceFieldName = descriptor.throughSourceFieldName;
|
|
1075
|
+
throughTargetFieldName = descriptor.throughTargetFieldName;
|
|
1076
|
+
} else {
|
|
1077
|
+
throughModel = this.options.resolveRef(ImplicitManyToManyIdentifier.getModelKey(model.metadata.key, descriptor.sourceSchemaFieldKey, targetModel.metadata.key));
|
|
1078
|
+
const implicitNames = ImplicitManyToManyThroughFactory.throughFieldNames(model, targetModel);
|
|
1079
|
+
throughSourceFieldName = implicitNames.throughSourceFieldName;
|
|
1080
|
+
throughTargetFieldName = implicitNames.throughTargetFieldName;
|
|
1081
|
+
}
|
|
1082
|
+
const throughNormalized = InternalSchemaModel.getNormalizedRelations(throughModel);
|
|
1083
|
+
const throughSource = throughNormalized.find((rel) => rel.sourceSchemaFieldKey === throughSourceFieldName);
|
|
1084
|
+
const throughTarget = throughNormalized.find((rel) => rel.sourceSchemaFieldKey === throughTargetFieldName);
|
|
1085
|
+
if (!throughSource || !throughTarget) throw new Error(`Many-to-many relation '${relationName}' on model '${model.metadata.key}' cannot find through fields on '${throughModel.metadata.key}'.`);
|
|
1086
|
+
const throughStorage = this.options.storage.byModel.get(throughModel.metadata.key);
|
|
1087
|
+
if (!throughStorage) throw new Error(`Many-to-many relation '${relationName}' on model '${model.metadata.key}' cannot resolve storage artifacts for through model '${throughModel.metadata.key}'.`);
|
|
831
1088
|
this.addResolvedRelation({
|
|
832
1089
|
edgeId: descriptor.edgeId,
|
|
833
1090
|
sourceModelKey: model.metadata.key,
|
|
@@ -838,7 +1095,13 @@ var ResolvedRelationGraphBuilder = class ResolvedRelationGraphBuilder {
|
|
|
838
1095
|
cardinality: InternalRelationCardinality.MANY,
|
|
839
1096
|
capabilities: MANY_TO_MANY_CAPABILITIES,
|
|
840
1097
|
provenance: InternalRelationProvenance.FIELD_DECORATOR,
|
|
841
|
-
alias: `${toSnakeCase(model.metadata.name)}_${relationName}
|
|
1098
|
+
alias: `${toSnakeCase(model.metadata.name)}_${relationName}`,
|
|
1099
|
+
throughModelKey: throughModel.metadata.key,
|
|
1100
|
+
throughTable: throughStorage.table,
|
|
1101
|
+
throughSourceFieldName,
|
|
1102
|
+
throughTargetFieldName,
|
|
1103
|
+
throughSourceKey: throughSource.dbColumnName,
|
|
1104
|
+
throughTargetKey: throughTarget.dbColumnName
|
|
842
1105
|
});
|
|
843
1106
|
continue;
|
|
844
1107
|
}
|
|
@@ -865,6 +1128,7 @@ var ResolvedRelationGraphBuilder = class ResolvedRelationGraphBuilder {
|
|
|
865
1128
|
provenance: InternalRelationProvenance.FIELD_DECORATOR,
|
|
866
1129
|
alias: `${toSnakeCase(targetModel.metadata.name)}_${forwardName}`
|
|
867
1130
|
});
|
|
1131
|
+
if (ImplicitManyToManyIdentifier.isImplicitManyToManyModel(sourceModel.metadata.key)) return;
|
|
868
1132
|
const reverseOverride = this.findReverseOverride(sourceModel, targetModel, descriptor);
|
|
869
1133
|
if (reverseOverride) this.markOverrideMatched(targetModel.metadata.key, reverseOverride[0]);
|
|
870
1134
|
const reverseKind = descriptor.unique ? InternalRelationPublicKind.HAS_ONE : InternalRelationPublicKind.HAS_MANY;
|
|
@@ -1087,6 +1351,10 @@ var ModelRegistry = class ModelRegistry {
|
|
|
1087
1351
|
*/
|
|
1088
1352
|
finalizeStorageArtifacts() {
|
|
1089
1353
|
if (this.storageCache?.version === this.version) return this.storageCache;
|
|
1354
|
+
const strippedImplicit = this.stripImplicitManyToManyModels();
|
|
1355
|
+
const implicitThroughModels = ImplicitManyToManyThroughFactory.buildModels(this);
|
|
1356
|
+
for (const model of implicitThroughModels) this.models.set(model.metadata.key, model);
|
|
1357
|
+
if (strippedImplicit || implicitThroughModels.length > 0) this.bumpVersion();
|
|
1090
1358
|
const primaryKeyByModel = new Map();
|
|
1091
1359
|
for (const model of this.models.values()) primaryKeyByModel.set(model.metadata.key, this.inferPrimaryKeyName(model));
|
|
1092
1360
|
const byModel = new Map();
|
|
@@ -1132,10 +1400,11 @@ var ModelRegistry = class ModelRegistry {
|
|
|
1132
1400
|
*/
|
|
1133
1401
|
getResolvedRelationGraph() {
|
|
1134
1402
|
if (this.relationGraphCache?.version === this.version) return this.relationGraphCache;
|
|
1403
|
+
const storage = this.finalizeStorageArtifacts();
|
|
1135
1404
|
const finalized = ResolvedRelationGraphBuilder.build({
|
|
1136
1405
|
version: this.version,
|
|
1137
1406
|
models: this.values(),
|
|
1138
|
-
storage
|
|
1407
|
+
storage,
|
|
1139
1408
|
resolveRef: (ref) => this.resolveRef(ref)
|
|
1140
1409
|
});
|
|
1141
1410
|
this.relationGraphCache = finalized;
|
|
@@ -1173,6 +1442,14 @@ var ModelRegistry = class ModelRegistry {
|
|
|
1173
1442
|
this.relationGraphCache = undefined;
|
|
1174
1443
|
this.lastRelationRegistryDriftCheckVersion = undefined;
|
|
1175
1444
|
}
|
|
1445
|
+
stripImplicitManyToManyModels() {
|
|
1446
|
+
let removed = false;
|
|
1447
|
+
for (const key of this.models.keys()) if (ImplicitManyToManyIdentifier.isImplicitManyToManyModel(key)) {
|
|
1448
|
+
this.models.delete(key);
|
|
1449
|
+
removed = true;
|
|
1450
|
+
}
|
|
1451
|
+
return removed;
|
|
1452
|
+
}
|
|
1176
1453
|
freezeFields(fields) {
|
|
1177
1454
|
return Object.freeze(fields.map((field$1) => Object.freeze({ ...field$1 })));
|
|
1178
1455
|
}
|
|
@@ -1310,6 +1587,7 @@ __export(model_exports, {
|
|
|
1310
1587
|
GENERATED_RELATION_REGISTRY_METADATA_FILENAME: () => GENERATED_RELATION_REGISTRY_METADATA_FILENAME,
|
|
1311
1588
|
GENERATED_RELATION_REGISTRY_METADATA_VERSION: () => GENERATED_RELATION_REGISTRY_METADATA_VERSION,
|
|
1312
1589
|
GENERATED_RELATION_REGISTRY_TYPES_FILENAME: () => GENERATED_RELATION_REGISTRY_TYPES_FILENAME,
|
|
1590
|
+
ImplicitManyToManyIdentifier: () => ImplicitManyToManyIdentifier,
|
|
1313
1591
|
Indexes: () => Indexes,
|
|
1314
1592
|
InternalDecoratedFieldKind: () => InternalDecoratedFieldKind,
|
|
1315
1593
|
Meta: () => Meta,
|
|
@@ -1334,5 +1612,5 @@ __export(model_exports, {
|
|
|
1334
1612
|
});
|
|
1335
1613
|
|
|
1336
1614
|
//#endregion
|
|
1337
|
-
export { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, Indexes, InternalDecoratedFieldKind, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, constraints_exports, createSchemaModuleAliases, createTypedModelRef, decorators_exports, isTypedModelRef, meta_exports, model_exports, registerModelAugmentor, registry_exports, relations_exports, resolveSchemaModuleEntrypoint };
|
|
1338
|
-
//# sourceMappingURL=model-
|
|
1615
|
+
export { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ImplicitManyToManyIdentifier, Indexes, InternalDecoratedFieldKind, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, constraints_exports, createSchemaModuleAliases, createTypedModelRef, decorators_exports, isTypedModelRef, meta_exports, model_exports, registerModelAugmentor, registry_exports, relations_exports, resolveSchemaModuleEntrypoint };
|
|
1616
|
+
//# sourceMappingURL=model-upj6jxaK.js.map
|