metal-orm 1.0.71 → 1.0.73
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/index.cjs +163 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +163 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/orm/entity-relations.ts +19 -19
- package/src/orm/entity.ts +74 -47
- package/src/orm/execute.ts +23 -19
- package/src/orm/relation-preload.ts +82 -0
- package/src/query-builder/relation-include-tree.ts +98 -0
- package/src/query-builder/select.ts +108 -64
package/dist/index.cjs
CHANGED
|
@@ -4893,6 +4893,58 @@ var resolveSelectQueryBuilderDependencies = (overrides = {}) => {
|
|
|
4893
4893
|
};
|
|
4894
4894
|
var defaultSelectQueryBuilderDependencies = resolveSelectQueryBuilderDependencies();
|
|
4895
4895
|
|
|
4896
|
+
// src/query-builder/relation-include-tree.ts
|
|
4897
|
+
var isObject = (value) => Boolean(value && typeof value === "object");
|
|
4898
|
+
var normalizeRelationIncludeNode = (value) => {
|
|
4899
|
+
if (!value || value === true) {
|
|
4900
|
+
return {};
|
|
4901
|
+
}
|
|
4902
|
+
if (!isObject(value)) {
|
|
4903
|
+
return {};
|
|
4904
|
+
}
|
|
4905
|
+
const { include, ...rest } = value;
|
|
4906
|
+
const options = Object.keys(rest).length ? rest : void 0;
|
|
4907
|
+
const normalizedInclude = isObject(include) ? normalizeRelationInclude(include) : void 0;
|
|
4908
|
+
if (normalizedInclude && Object.keys(normalizedInclude).length > 0) {
|
|
4909
|
+
return { options, include: normalizedInclude };
|
|
4910
|
+
}
|
|
4911
|
+
return { options };
|
|
4912
|
+
};
|
|
4913
|
+
var normalizeRelationInclude = (input) => {
|
|
4914
|
+
if (!input) return {};
|
|
4915
|
+
const tree = {};
|
|
4916
|
+
for (const [key, value] of Object.entries(input)) {
|
|
4917
|
+
tree[key] = normalizeRelationIncludeNode(value);
|
|
4918
|
+
}
|
|
4919
|
+
return tree;
|
|
4920
|
+
};
|
|
4921
|
+
var mergeRelationIncludeTrees = (base, next) => {
|
|
4922
|
+
const merged = { ...base };
|
|
4923
|
+
for (const [key, node] of Object.entries(next)) {
|
|
4924
|
+
const existing = merged[key];
|
|
4925
|
+
if (!existing) {
|
|
4926
|
+
merged[key] = node;
|
|
4927
|
+
continue;
|
|
4928
|
+
}
|
|
4929
|
+
const include = existing.include && node.include ? mergeRelationIncludeTrees(existing.include, node.include) : node.include ?? existing.include;
|
|
4930
|
+
merged[key] = {
|
|
4931
|
+
options: node.options ?? existing.options,
|
|
4932
|
+
...include ? { include } : {}
|
|
4933
|
+
};
|
|
4934
|
+
}
|
|
4935
|
+
return merged;
|
|
4936
|
+
};
|
|
4937
|
+
var cloneRelationIncludeTree = (tree) => {
|
|
4938
|
+
const cloned = {};
|
|
4939
|
+
for (const [key, node] of Object.entries(tree)) {
|
|
4940
|
+
cloned[key] = {
|
|
4941
|
+
options: node.options,
|
|
4942
|
+
...node.include ? { include: cloneRelationIncludeTree(node.include) } : {}
|
|
4943
|
+
};
|
|
4944
|
+
}
|
|
4945
|
+
return cloned;
|
|
4946
|
+
};
|
|
4947
|
+
|
|
4896
4948
|
// src/orm/hydration.ts
|
|
4897
4949
|
var hydrateRows = (rows, plan) => {
|
|
4898
4950
|
if (!plan || !rows.length) return rows;
|
|
@@ -6025,14 +6077,14 @@ var getRelationWrapper = (meta, relationName, owner, createEntity) => {
|
|
|
6025
6077
|
};
|
|
6026
6078
|
var instantiateWrapper = (meta, relationName, relation, owner, createEntity) => {
|
|
6027
6079
|
const metaBase = meta;
|
|
6028
|
-
const lazyOptions = meta.lazyRelationOptions.get(relationName);
|
|
6029
6080
|
const loadCached = (factory) => relationLoaderCache(metaBase, relationName, factory);
|
|
6081
|
+
const resolveOptions = () => meta.lazyRelationOptions.get(relationName);
|
|
6030
6082
|
switch (relation.type) {
|
|
6031
6083
|
case RelationKinds.HasOne: {
|
|
6032
6084
|
const hasOne2 = relation;
|
|
6033
6085
|
const localKey = hasOne2.localKey || findPrimaryKey(meta.table);
|
|
6034
6086
|
const loader = () => loadCached(
|
|
6035
|
-
() => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2,
|
|
6087
|
+
() => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2, resolveOptions())
|
|
6036
6088
|
);
|
|
6037
6089
|
return new DefaultHasOneReference(
|
|
6038
6090
|
meta.ctx,
|
|
@@ -6050,7 +6102,7 @@ var instantiateWrapper = (meta, relationName, relation, owner, createEntity) =>
|
|
|
6050
6102
|
const hasMany2 = relation;
|
|
6051
6103
|
const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
|
|
6052
6104
|
const loader = () => loadCached(
|
|
6053
|
-
() => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2,
|
|
6105
|
+
() => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2, resolveOptions())
|
|
6054
6106
|
);
|
|
6055
6107
|
return new DefaultHasManyCollection(
|
|
6056
6108
|
meta.ctx,
|
|
@@ -6068,7 +6120,7 @@ var instantiateWrapper = (meta, relationName, relation, owner, createEntity) =>
|
|
|
6068
6120
|
const belongsTo2 = relation;
|
|
6069
6121
|
const targetKey = belongsTo2.localKey || findPrimaryKey(belongsTo2.target);
|
|
6070
6122
|
const loader = () => loadCached(
|
|
6071
|
-
() => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2,
|
|
6123
|
+
() => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2, resolveOptions())
|
|
6072
6124
|
);
|
|
6073
6125
|
return new DefaultBelongsToReference(
|
|
6074
6126
|
meta.ctx,
|
|
@@ -6086,7 +6138,7 @@ var instantiateWrapper = (meta, relationName, relation, owner, createEntity) =>
|
|
|
6086
6138
|
const many = relation;
|
|
6087
6139
|
const localKey = many.localKey || findPrimaryKey(meta.table);
|
|
6088
6140
|
const loader = () => loadCached(
|
|
6089
|
-
() => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many,
|
|
6141
|
+
() => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, resolveOptions())
|
|
6090
6142
|
);
|
|
6091
6143
|
return new DefaultManyToManyCollection(
|
|
6092
6144
|
meta.ctx,
|
|
@@ -6106,6 +6158,10 @@ var instantiateWrapper = (meta, relationName, relation, owner, createEntity) =>
|
|
|
6106
6158
|
};
|
|
6107
6159
|
|
|
6108
6160
|
// src/orm/entity.ts
|
|
6161
|
+
var isRelationWrapperLoaded = (value) => {
|
|
6162
|
+
if (!value || typeof value !== "object") return false;
|
|
6163
|
+
return Boolean(value.loaded);
|
|
6164
|
+
};
|
|
6109
6165
|
var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
|
|
6110
6166
|
const target = { ...row };
|
|
6111
6167
|
const meta = {
|
|
@@ -6118,6 +6174,19 @@ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOption
|
|
|
6118
6174
|
relationWrappers: /* @__PURE__ */ new Map()
|
|
6119
6175
|
};
|
|
6120
6176
|
const createRelationEntity = (relationTable, relationRow) => createEntityFromRow(meta.ctx, relationTable, relationRow);
|
|
6177
|
+
const buildJson = (self) => {
|
|
6178
|
+
const json = {};
|
|
6179
|
+
for (const key of Object.keys(target)) {
|
|
6180
|
+
json[key] = self[key];
|
|
6181
|
+
}
|
|
6182
|
+
for (const [relationName, wrapper] of meta.relationWrappers) {
|
|
6183
|
+
if (Object.prototype.hasOwnProperty.call(json, relationName)) continue;
|
|
6184
|
+
if (isRelationWrapperLoaded(wrapper)) {
|
|
6185
|
+
json[relationName] = wrapper;
|
|
6186
|
+
}
|
|
6187
|
+
}
|
|
6188
|
+
return json;
|
|
6189
|
+
};
|
|
6121
6190
|
Object.defineProperty(target, ENTITY_META, {
|
|
6122
6191
|
value: meta,
|
|
6123
6192
|
enumerable: false,
|
|
@@ -6137,6 +6206,12 @@ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOption
|
|
|
6137
6206
|
return void 0;
|
|
6138
6207
|
};
|
|
6139
6208
|
}
|
|
6209
|
+
if (prop === "toJSON") {
|
|
6210
|
+
if (prop in targetObj) {
|
|
6211
|
+
return Reflect.get(targetObj, prop, receiver);
|
|
6212
|
+
}
|
|
6213
|
+
return () => buildJson(receiver);
|
|
6214
|
+
}
|
|
6140
6215
|
if (typeof prop === "string" && table.relations[prop]) {
|
|
6141
6216
|
return getRelationWrapper(meta, prop, receiver, createRelationEntity);
|
|
6142
6217
|
}
|
|
@@ -6170,6 +6245,58 @@ var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOpti
|
|
|
6170
6245
|
return entity;
|
|
6171
6246
|
};
|
|
6172
6247
|
|
|
6248
|
+
// src/orm/relation-preload.ts
|
|
6249
|
+
var collectEntities = (value) => {
|
|
6250
|
+
if (!value) return [];
|
|
6251
|
+
if (Array.isArray(value)) {
|
|
6252
|
+
return value.filter((item) => item && typeof item === "object");
|
|
6253
|
+
}
|
|
6254
|
+
if (typeof value === "object") {
|
|
6255
|
+
return [value];
|
|
6256
|
+
}
|
|
6257
|
+
return [];
|
|
6258
|
+
};
|
|
6259
|
+
var loadRelation = async (entity, relationName) => {
|
|
6260
|
+
const wrapper = entity[relationName];
|
|
6261
|
+
if (!wrapper) return [];
|
|
6262
|
+
if (typeof wrapper.load === "function") {
|
|
6263
|
+
const loaded = await wrapper.load();
|
|
6264
|
+
return collectEntities(loaded);
|
|
6265
|
+
}
|
|
6266
|
+
if (typeof wrapper.getItems === "function") {
|
|
6267
|
+
return collectEntities(wrapper.getItems());
|
|
6268
|
+
}
|
|
6269
|
+
if (typeof wrapper.get === "function") {
|
|
6270
|
+
return collectEntities(wrapper.get());
|
|
6271
|
+
}
|
|
6272
|
+
return collectEntities(wrapper);
|
|
6273
|
+
};
|
|
6274
|
+
var setLazyOptionsIfEmpty = (entity, relationName, options) => {
|
|
6275
|
+
if (!options) return;
|
|
6276
|
+
const meta = getEntityMeta(entity);
|
|
6277
|
+
if (!meta || meta.lazyRelationOptions.has(relationName)) return;
|
|
6278
|
+
meta.lazyRelationOptions.set(relationName, options);
|
|
6279
|
+
};
|
|
6280
|
+
var preloadRelationIncludes = async (entities, includeTree, depth = 0) => {
|
|
6281
|
+
if (!entities.length) return;
|
|
6282
|
+
const entries = Object.entries(includeTree);
|
|
6283
|
+
if (!entries.length) return;
|
|
6284
|
+
for (const [relationName, node] of entries) {
|
|
6285
|
+
const shouldLoad = depth > 0 || Boolean(node.include);
|
|
6286
|
+
if (!shouldLoad) continue;
|
|
6287
|
+
for (const entity of entities) {
|
|
6288
|
+
setLazyOptionsIfEmpty(entity, relationName, node.options);
|
|
6289
|
+
}
|
|
6290
|
+
const loaded = await Promise.all(
|
|
6291
|
+
entities.map((entity) => loadRelation(entity, relationName))
|
|
6292
|
+
);
|
|
6293
|
+
const relatedEntities = loaded.flat();
|
|
6294
|
+
if (node.include && relatedEntities.length) {
|
|
6295
|
+
await preloadRelationIncludes(relatedEntities, node.include, depth + 1);
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
};
|
|
6299
|
+
|
|
6173
6300
|
// src/orm/execute.ts
|
|
6174
6301
|
var flattenResults = (results) => {
|
|
6175
6302
|
const rows = [];
|
|
@@ -6192,14 +6319,17 @@ var executeWithContexts = async (execCtx, entityCtx, qb) => {
|
|
|
6192
6319
|
const rows = flattenResults(executed);
|
|
6193
6320
|
const lazyRelations = qb.getLazyRelations();
|
|
6194
6321
|
const lazyRelationOptions = qb.getLazyRelationOptions();
|
|
6322
|
+
const includeTree = qb.getIncludeTree();
|
|
6195
6323
|
if (ast.setOps && ast.setOps.length > 0) {
|
|
6196
6324
|
const proxies = rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
|
|
6197
6325
|
await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
|
|
6326
|
+
await preloadRelationIncludes(proxies, includeTree);
|
|
6198
6327
|
return proxies;
|
|
6199
6328
|
}
|
|
6200
6329
|
const hydrated = hydrateRows(rows, qb.getHydrationPlan());
|
|
6201
6330
|
const entities = hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
|
|
6202
6331
|
await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
|
|
6332
|
+
await preloadRelationIncludes(entities, includeTree);
|
|
6203
6333
|
return entities;
|
|
6204
6334
|
};
|
|
6205
6335
|
var executePlainWithContexts = async (execCtx, qb) => {
|
|
@@ -6826,6 +6956,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6826
6956
|
lazyRelations;
|
|
6827
6957
|
lazyRelationOptions;
|
|
6828
6958
|
entityConstructor;
|
|
6959
|
+
includeTree;
|
|
6829
6960
|
/**
|
|
6830
6961
|
* Creates a new SelectQueryBuilder instance
|
|
6831
6962
|
* @param table - Table definition to query
|
|
@@ -6833,7 +6964,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6833
6964
|
* @param hydration - Optional hydration manager
|
|
6834
6965
|
* @param dependencies - Optional query builder dependencies
|
|
6835
6966
|
*/
|
|
6836
|
-
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor) {
|
|
6967
|
+
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree) {
|
|
6837
6968
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
6838
6969
|
this.env = { table, deps };
|
|
6839
6970
|
const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
|
|
@@ -6846,6 +6977,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6846
6977
|
this.lazyRelations = new Set(lazyRelations ?? []);
|
|
6847
6978
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
6848
6979
|
this.entityConstructor = entityConstructor;
|
|
6980
|
+
this.includeTree = includeTree ?? {};
|
|
6849
6981
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
6850
6982
|
const relationManager = deps.createRelationManager(this.env);
|
|
6851
6983
|
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
@@ -6862,7 +6994,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6862
6994
|
* @param lazyRelations - Updated lazy relations set
|
|
6863
6995
|
* @returns New SelectQueryBuilder instance
|
|
6864
6996
|
*/
|
|
6865
|
-
clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions)) {
|
|
6997
|
+
clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions), includeTree = this.includeTree) {
|
|
6866
6998
|
return new _SelectQueryBuilder(
|
|
6867
6999
|
this.env.table,
|
|
6868
7000
|
context.state,
|
|
@@ -6870,7 +7002,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6870
7002
|
this.env.deps,
|
|
6871
7003
|
lazyRelations,
|
|
6872
7004
|
lazyRelationOptions,
|
|
6873
|
-
this.entityConstructor
|
|
7005
|
+
this.entityConstructor,
|
|
7006
|
+
includeTree
|
|
6874
7007
|
);
|
|
6875
7008
|
}
|
|
6876
7009
|
/**
|
|
@@ -7158,24 +7291,22 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7158
7291
|
const nextContext = this.relationFacet.joinRelation(this.context, relationName, joinKind, extraCondition);
|
|
7159
7292
|
return this.clone(nextContext);
|
|
7160
7293
|
}
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
const nextContext = this.relationFacet.include(this.context, relationName, options);
|
|
7178
|
-
return this.clone(nextContext);
|
|
7294
|
+
include(relationNameOrRelations, options) {
|
|
7295
|
+
if (typeof relationNameOrRelations === "object" && relationNameOrRelations !== null) {
|
|
7296
|
+
const normalized = normalizeRelationInclude(relationNameOrRelations);
|
|
7297
|
+
let nextContext2 = this.context;
|
|
7298
|
+
for (const [relationName2, node] of Object.entries(normalized)) {
|
|
7299
|
+
nextContext2 = this.relationFacet.include(nextContext2, relationName2, node.options);
|
|
7300
|
+
}
|
|
7301
|
+
const nextTree2 = mergeRelationIncludeTrees(this.includeTree, normalized);
|
|
7302
|
+
return this.clone(nextContext2, void 0, void 0, nextTree2);
|
|
7303
|
+
}
|
|
7304
|
+
const relationName = relationNameOrRelations;
|
|
7305
|
+
const normalizedNode = normalizeRelationIncludeNode(options);
|
|
7306
|
+
const nextContext = this.relationFacet.include(this.context, relationName, normalizedNode.options);
|
|
7307
|
+
const shouldStore = Boolean(normalizedNode.include || normalizedNode.options);
|
|
7308
|
+
const nextTree = shouldStore ? mergeRelationIncludeTrees(this.includeTree, { [relationName]: normalizedNode }) : this.includeTree;
|
|
7309
|
+
return this.clone(nextContext, void 0, void 0, nextTree);
|
|
7179
7310
|
}
|
|
7180
7311
|
/**
|
|
7181
7312
|
* Includes a relation lazily in the query results
|
|
@@ -7259,6 +7390,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7259
7390
|
getLazyRelationOptions() {
|
|
7260
7391
|
return new Map(this.lazyRelationOptions);
|
|
7261
7392
|
}
|
|
7393
|
+
/**
|
|
7394
|
+
* Gets normalized nested include information for runtime preloading.
|
|
7395
|
+
*/
|
|
7396
|
+
getIncludeTree() {
|
|
7397
|
+
return cloneRelationIncludeTree(this.includeTree);
|
|
7398
|
+
}
|
|
7262
7399
|
/**
|
|
7263
7400
|
* Gets the table definition for this query builder
|
|
7264
7401
|
* @returns Table definition
|