linkgress-orm 0.4.66 → 0.4.68
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/entity/db-context.d.ts +10 -0
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +68 -67
- package/dist/entity/db-context.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/query/mock-row-cache.d.ts +19 -11
- package/dist/query/mock-row-cache.d.ts.map +1 -1
- package/dist/query/mock-row-cache.js +23 -15
- package/dist/query/mock-row-cache.js.map +1 -1
- package/dist/query/navigation-path-cache.d.ts +41 -0
- package/dist/query/navigation-path-cache.d.ts.map +1 -0
- package/dist/query/navigation-path-cache.js +66 -0
- package/dist/query/navigation-path-cache.js.map +1 -0
- package/dist/query/query-builder.d.ts +2 -0
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +82 -35
- package/dist/query/query-builder.js.map +1 -1
- package/package.json +80 -80
|
@@ -16,6 +16,7 @@ const collection_strategy_factory_1 = require("./collection-strategy.factory");
|
|
|
16
16
|
const union_builder_1 = require("./union-builder");
|
|
17
17
|
const future_query_1 = require("./future-query");
|
|
18
18
|
const mock_row_cache_1 = require("./mock-row-cache");
|
|
19
|
+
const navigation_path_cache_1 = require("./navigation-path-cache");
|
|
19
20
|
const join_utils_1 = require("./join-utils");
|
|
20
21
|
/**
|
|
21
22
|
* Field type categories for optimized result transformation
|
|
@@ -79,6 +80,39 @@ const MOCK_ROW_NAV_CACHE = Symbol('linkgressMockNavCache');
|
|
|
79
80
|
const navigationPathSignature = (path) => path
|
|
80
81
|
.map(step => `${step.alias}:${step.targetTable}:${(step.foreignKeys ?? []).join('+')}:${(step.matches ?? []).join('+')}:${step.isMandatory ? 1 : 0}:${step.sourceAlias ?? ''}`)
|
|
81
82
|
.join('>');
|
|
83
|
+
/**
|
|
84
|
+
* Whether `value` is a reference mock row minted by `ReferenceQueryBuilder.createMockTargetRow`.
|
|
85
|
+
* Those rows INHERIT their column/relation getters from a shared prototype (see MockRowCache),
|
|
86
|
+
* so `Object.getPrototypeOf(row) !== Object.prototype` and own-property APIs see no columns —
|
|
87
|
+
* the row's own state slots are the reliable marker.
|
|
88
|
+
*/
|
|
89
|
+
const isReferenceMockRow = (value) => value != null && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, MOCK_ROW_FIELD_REFS);
|
|
90
|
+
/** `Object.getOwnPropertyDescriptor` that walks the prototype chain (stops before Object.prototype). */
|
|
91
|
+
const findPropertyDescriptor = (value, key) => {
|
|
92
|
+
let current = value;
|
|
93
|
+
while (current != null && current !== Object.prototype) {
|
|
94
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
95
|
+
if (descriptor) {
|
|
96
|
+
return descriptor;
|
|
97
|
+
}
|
|
98
|
+
current = Object.getPrototypeOf(current);
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* First enumerable key (own or inherited) backed by a getter — the "is this a mock row" probe
|
|
104
|
+
* shared by the selection resolvers. Root mocks define their getters as own properties,
|
|
105
|
+
* reference mocks inherit them; column getters are enumerable on both, so the first
|
|
106
|
+
* enumerable getter is always a column and `row[key]` yields its FieldRef.
|
|
107
|
+
*/
|
|
108
|
+
const findFirstGetterKey = (value) => {
|
|
109
|
+
for (const key in value) {
|
|
110
|
+
if (findPropertyDescriptor(value, key)?.get != null) {
|
|
111
|
+
return key;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
};
|
|
82
116
|
/**
|
|
83
117
|
* Performance utility: Get target schema for a relation, using cached version if available
|
|
84
118
|
*/
|
|
@@ -1702,7 +1736,10 @@ class SelectQueryBuilder {
|
|
|
1702
1736
|
// tryBuildFlatNestedSelect flattens. Class instances (collections, SqlFragment,
|
|
1703
1737
|
// Subquery) and collection-result markers must not be walked.
|
|
1704
1738
|
const proto = Object.getPrototypeOf(value);
|
|
1705
|
-
|
|
1739
|
+
// Reference mock rows inherit their getters from a shared prototype (see
|
|
1740
|
+
// createMockTargetRow) yet must keep being walked exactly like the plain-object mocks
|
|
1741
|
+
// they replaced.
|
|
1742
|
+
if ((proto === Object.prototype || proto === null || isReferenceMockRow(value)) && !('__collectionResult' in value)) {
|
|
1706
1743
|
this.collectJsonRowRevivals(value, pathPrefix ? `${pathPrefix}__${key}` : `__nested__${key}`, revivals, textColumns);
|
|
1707
1744
|
}
|
|
1708
1745
|
}
|
|
@@ -4097,19 +4134,15 @@ ${joinClauses.join('\n')}`;
|
|
|
4097
4134
|
continue;
|
|
4098
4135
|
}
|
|
4099
4136
|
}
|
|
4100
|
-
// Check if it's a mock object with
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
const
|
|
4105
|
-
if (
|
|
4137
|
+
// Check if it's a mock object with getter-backed properties (navigation property
|
|
4138
|
+
// mock). The getters may be OWN (root mocks) or INHERITED from the shared
|
|
4139
|
+
// prototype (reference mocks) — findFirstGetterKey walks both.
|
|
4140
|
+
{
|
|
4141
|
+
const tableAlias = findFirstGetterKey(value);
|
|
4142
|
+
if (tableAlias) {
|
|
4106
4143
|
// This object has getter properties - likely a navigation mock
|
|
4107
4144
|
// Try to determine if this is a reference navigation by checking the schema relations
|
|
4108
|
-
|
|
4109
|
-
const desc = Object.getOwnPropertyDescriptor(value, k);
|
|
4110
|
-
return desc && desc.get && typeof desc.get === 'function';
|
|
4111
|
-
});
|
|
4112
|
-
if (tableAlias) {
|
|
4145
|
+
{
|
|
4113
4146
|
// Try to get the first property to check if it has __tableAlias
|
|
4114
4147
|
try {
|
|
4115
4148
|
const firstValue = value[tableAlias];
|
|
@@ -4761,15 +4794,12 @@ ${joinClauses.join('\n')}`;
|
|
|
4761
4794
|
// These are treated as SIMPLE because the actual value comes from json_build_object in the row
|
|
4762
4795
|
// The navigation mock is just a placeholder - actual data processing happens via FieldType.SIMPLE
|
|
4763
4796
|
if (value && typeof value === 'object' && !('__dbColumnName' in value) && !('__fieldName' in value) && !('__collectionResult' in value) && !('__isAggregationArray' in value)) {
|
|
4764
|
-
|
|
4765
|
-
if (
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
fieldConfigs.push({ key, type: 8 /* FieldType.SIMPLE */, value });
|
|
4771
|
-
continue;
|
|
4772
|
-
}
|
|
4797
|
+
// Getter-backed row (own getters on a root mock, inherited ones on a reference mock).
|
|
4798
|
+
if (findFirstGetterKey(value) != null) {
|
|
4799
|
+
// Navigation mock - treat as simple, data will come from row via json_build_object
|
|
4800
|
+
// If row has no data, convertValue will return undefined
|
|
4801
|
+
fieldConfigs.push({ key, type: 8 /* FieldType.SIMPLE */, value });
|
|
4802
|
+
continue;
|
|
4773
4803
|
}
|
|
4774
4804
|
}
|
|
4775
4805
|
// Collection types
|
|
@@ -5598,20 +5628,24 @@ class ReferenceQueryBuilder {
|
|
|
5598
5628
|
*/
|
|
5599
5629
|
createMockTargetRow() {
|
|
5600
5630
|
if (this.targetTableSchema) {
|
|
5601
|
-
//
|
|
5602
|
-
//
|
|
5603
|
-
//
|
|
5604
|
-
//
|
|
5605
|
-
//
|
|
5606
|
-
//
|
|
5607
|
-
//
|
|
5608
|
-
//
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5631
|
+
// Prototype-level cache — see MockRowCache's doc. Everything the getters close over
|
|
5632
|
+
// is fully determined by (target schema object identity, relationName, sourceAlias,
|
|
5633
|
+
// navigation-path content), so rows with the same signature can share one prebuilt
|
|
5634
|
+
// PROTOTYPE carrying every column/relation getter; a new row is then `Object.create`
|
|
5635
|
+
// plus its two own state slots — O(1) instead of one property definition per column.
|
|
5636
|
+
// The cross-row cache is OPT-IN via the static switch (MockRowCache.setEnabled — the
|
|
5637
|
+
// host app flips it from its own config): with it off, each row gets a FRESH
|
|
5638
|
+
// prototype (the pre-0.4.66 memory profile — nothing retained beyond the row's
|
|
5639
|
+
// lifetime), while the per-row FieldRef and navigation slots stay row-scoped either
|
|
5640
|
+
// way (the shared getters read them through `this`).
|
|
5641
|
+
//
|
|
5642
|
+
// Consumers must not probe these rows with OWN-property APIs (`Object.keys`,
|
|
5643
|
+
// `getOwnPropertyNames`, `getOwnPropertyDescriptor` on the row itself) — the getters
|
|
5644
|
+
// are inherited. Use `isReferenceMockRow` / `findFirstGetterKey` instead.
|
|
5645
|
+
const prototype = mock_row_cache_1.MockRowCache.getOrBuild(`${this.targetTable}|${this.relationName}|${this.sourceAlias ?? ''}|${navigationPathSignature(this.navigationPath)}`, () => Object.defineProperties({}, this.buildMockRowDescriptors()));
|
|
5646
|
+
const mock = Object.create(prototype);
|
|
5647
|
+
mock[MOCK_ROW_FIELD_REFS] = {};
|
|
5648
|
+
mock[MOCK_ROW_NAV_CACHE] = {};
|
|
5615
5649
|
return mock;
|
|
5616
5650
|
}
|
|
5617
5651
|
else {
|
|
@@ -6491,6 +6525,19 @@ class CollectionQueryBuilder {
|
|
|
6491
6525
|
* Uses BFS to find the shortest path through the schema graph
|
|
6492
6526
|
*/
|
|
6493
6527
|
findNavigationPath(targetAlias, joinedSchemas, _startSchema) {
|
|
6528
|
+
if (!this.schemaRegistry) {
|
|
6529
|
+
return [];
|
|
6530
|
+
}
|
|
6531
|
+
// Memoised per registry — the BFS below depends only on the target alias and the ORDERED
|
|
6532
|
+
// joined alias→table pairs (order decides which of several equal-length paths wins), so
|
|
6533
|
+
// the same query shape resolves to the same path on every build. See NavigationPathCache.
|
|
6534
|
+
const signature = `${targetAlias}|${[...joinedSchemas]
|
|
6535
|
+
.map(([alias, schema]) => `${alias}:${schema.schema ?? ''}.${schema.name}`)
|
|
6536
|
+
.join(',')}`;
|
|
6537
|
+
return navigation_path_cache_1.NavigationPathCache.getOrBuild(this.schemaRegistry, signature, () => this.computeNavigationPath(targetAlias, joinedSchemas));
|
|
6538
|
+
}
|
|
6539
|
+
/** The uncached BFS behind {@link findNavigationPath}. */
|
|
6540
|
+
computeNavigationPath(targetAlias, joinedSchemas) {
|
|
6494
6541
|
if (!this.schemaRegistry) {
|
|
6495
6542
|
return [];
|
|
6496
6543
|
}
|