@getstrata/core 0.5.99 → 0.5.101
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/CHANGELOG.md +14 -0
- package/dist/core/database/baseRepository.d.ts +5 -1
- package/dist/core/database/index.d.ts +3 -3
- package/dist/core/database/model.d.ts +23 -2
- package/dist/core/database/relationQuery.d.ts +22 -3
- package/dist/core/database/relationships.d.ts +25 -3
- package/dist/core/database/repositoryQuery.d.ts +4 -1
- package/dist/entries/database/model.js +225 -24
- package/dist/entries/database/relationships.js +114 -21
- package/dist/entries/database/repositoryQuery.js +268 -6
- package/dist/entries/openapi/generator.js +22 -2
- package/dist/index.js +330 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2078,32 +2078,80 @@ function belongsTo(definition) {
|
|
|
2078
2078
|
...definition
|
|
2079
2079
|
};
|
|
2080
2080
|
}
|
|
2081
|
+
function hasManyThrough(definition) {
|
|
2082
|
+
return {
|
|
2083
|
+
type: "hasManyThrough",
|
|
2084
|
+
throughParentKey: "__through_parent_id",
|
|
2085
|
+
...definition
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2081
2088
|
function belongsToMany(definition) {
|
|
2082
2089
|
return {
|
|
2083
2090
|
type: "belongsToMany",
|
|
2084
2091
|
...definition
|
|
2085
2092
|
};
|
|
2086
2093
|
}
|
|
2094
|
+
function relationMatchKey(value) {
|
|
2095
|
+
if (value === null || value === undefined) {
|
|
2096
|
+
return "";
|
|
2097
|
+
}
|
|
2098
|
+
if (typeof value === "bigint") {
|
|
2099
|
+
return value.toString();
|
|
2100
|
+
}
|
|
2101
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2102
|
+
return String(value);
|
|
2103
|
+
}
|
|
2104
|
+
if (typeof value === "string" && /^-?\d+$/.test(value)) {
|
|
2105
|
+
return BigInt(value).toString();
|
|
2106
|
+
}
|
|
2107
|
+
return String(value);
|
|
2108
|
+
}
|
|
2109
|
+
function getByRelationKey(map, key) {
|
|
2110
|
+
if (map.has(key)) {
|
|
2111
|
+
return map.get(key);
|
|
2112
|
+
}
|
|
2113
|
+
const want = relationMatchKey(key);
|
|
2114
|
+
if (want === "") {
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
for (const [existing, value] of map) {
|
|
2118
|
+
if (relationMatchKey(existing) === want) {
|
|
2119
|
+
return value;
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2087
2124
|
function indexHasManyRelation(parents, children, relation) {
|
|
2088
2125
|
const groups = new Map;
|
|
2126
|
+
const originalKeys = new Map;
|
|
2089
2127
|
for (const parent of parents) {
|
|
2090
|
-
|
|
2128
|
+
const key = relationMatchKey(parent[relation.localKey]);
|
|
2129
|
+
if (!groups.has(key)) {
|
|
2130
|
+
groups.set(key, []);
|
|
2131
|
+
originalKeys.set(key, parent[relation.localKey]);
|
|
2132
|
+
}
|
|
2091
2133
|
}
|
|
2092
2134
|
for (const child of children) {
|
|
2093
|
-
const
|
|
2094
|
-
const group = groups.get(key);
|
|
2135
|
+
const group = groups.get(relationMatchKey(child[relation.foreignKey]));
|
|
2095
2136
|
if (!group) {
|
|
2096
2137
|
continue;
|
|
2097
2138
|
}
|
|
2098
2139
|
group.push(child);
|
|
2099
2140
|
}
|
|
2100
|
-
|
|
2141
|
+
const result = new Map;
|
|
2142
|
+
for (const [key, group] of groups) {
|
|
2143
|
+
const original = originalKeys.get(key);
|
|
2144
|
+
if (original !== undefined) {
|
|
2145
|
+
result.set(original, group);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
return result;
|
|
2101
2149
|
}
|
|
2102
2150
|
function indexHasOneRelation(parents, children, relation) {
|
|
2103
2151
|
const grouped = indexHasManyRelation(parents, children, relation);
|
|
2104
2152
|
const result = new Map;
|
|
2105
2153
|
for (const parent of parents) {
|
|
2106
|
-
const matches = grouped
|
|
2154
|
+
const matches = getByRelationKey(grouped, parent[relation.localKey]) ?? [];
|
|
2107
2155
|
result.set(parent[relation.localKey], matches[0]);
|
|
2108
2156
|
}
|
|
2109
2157
|
return result;
|
|
@@ -2111,12 +2159,12 @@ function indexHasOneRelation(parents, children, relation) {
|
|
|
2111
2159
|
function indexBelongsToRelation(children, parents, relation) {
|
|
2112
2160
|
const parentsById = new Map;
|
|
2113
2161
|
for (const parent of parents) {
|
|
2114
|
-
parentsById.set(parent[relation.ownerKey], parent);
|
|
2162
|
+
parentsById.set(relationMatchKey(parent[relation.ownerKey]), parent);
|
|
2115
2163
|
}
|
|
2116
2164
|
const result = new Map;
|
|
2117
2165
|
for (const child of children) {
|
|
2118
2166
|
const foreignKey = child[relation.foreignKey];
|
|
2119
|
-
const parent = parentsById.get(foreignKey);
|
|
2167
|
+
const parent = parentsById.get(relationMatchKey(foreignKey));
|
|
2120
2168
|
if (parent) {
|
|
2121
2169
|
result.set(foreignKey, parent);
|
|
2122
2170
|
}
|
|
@@ -2126,23 +2174,33 @@ function indexBelongsToRelation(children, parents, relation) {
|
|
|
2126
2174
|
function indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation) {
|
|
2127
2175
|
const relatedById = new Map;
|
|
2128
2176
|
for (const related of relatedRows) {
|
|
2129
|
-
relatedById.set(related[relation.relatedKey], related);
|
|
2177
|
+
relatedById.set(relationMatchKey(related[relation.relatedKey]), related);
|
|
2130
2178
|
}
|
|
2131
2179
|
const groups = new Map;
|
|
2180
|
+
const originalKeys = new Map;
|
|
2132
2181
|
for (const parent of parents) {
|
|
2133
|
-
|
|
2182
|
+
const key = relationMatchKey(parent[relation.parentKey]);
|
|
2183
|
+
if (!groups.has(key)) {
|
|
2184
|
+
groups.set(key, []);
|
|
2185
|
+
originalKeys.set(key, parent[relation.parentKey]);
|
|
2186
|
+
}
|
|
2134
2187
|
}
|
|
2135
2188
|
for (const pivot of pivotRows) {
|
|
2136
|
-
const
|
|
2137
|
-
const
|
|
2138
|
-
const group = groups.get(parentId);
|
|
2139
|
-
const related = relatedById.get(relatedId);
|
|
2189
|
+
const group = groups.get(relationMatchKey(pivot[relation.foreignPivotKey]));
|
|
2190
|
+
const related = relatedById.get(relationMatchKey(pivot[relation.relatedPivotKey]));
|
|
2140
2191
|
if (!group || !related) {
|
|
2141
2192
|
continue;
|
|
2142
2193
|
}
|
|
2143
2194
|
group.push(related);
|
|
2144
2195
|
}
|
|
2145
|
-
|
|
2196
|
+
const result = new Map;
|
|
2197
|
+
for (const [key, group] of groups) {
|
|
2198
|
+
const original = originalKeys.get(key);
|
|
2199
|
+
if (original !== undefined) {
|
|
2200
|
+
result.set(original, group);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
return result;
|
|
2146
2204
|
}
|
|
2147
2205
|
function morphMany(definition) {
|
|
2148
2206
|
return {
|
|
@@ -2164,31 +2222,62 @@ function morphTo(definition) {
|
|
|
2164
2222
|
}
|
|
2165
2223
|
function indexMorphManyRelation(parents, children, relation) {
|
|
2166
2224
|
const groups = new Map;
|
|
2225
|
+
const originalKeys = new Map;
|
|
2167
2226
|
for (const parent of parents) {
|
|
2168
|
-
|
|
2227
|
+
const key = relationMatchKey(parent[relation.localKey]);
|
|
2228
|
+
if (!groups.has(key)) {
|
|
2229
|
+
groups.set(key, []);
|
|
2230
|
+
originalKeys.set(key, parent[relation.localKey]);
|
|
2231
|
+
}
|
|
2169
2232
|
}
|
|
2170
2233
|
for (const child of children) {
|
|
2171
2234
|
if (child[relation.morphTypeKey] !== relation.morphType) {
|
|
2172
2235
|
continue;
|
|
2173
2236
|
}
|
|
2174
|
-
const
|
|
2175
|
-
const group = groups.get(key);
|
|
2237
|
+
const group = groups.get(relationMatchKey(child[relation.morphIdKey]));
|
|
2176
2238
|
if (!group) {
|
|
2177
2239
|
continue;
|
|
2178
2240
|
}
|
|
2179
2241
|
group.push(child);
|
|
2180
2242
|
}
|
|
2181
|
-
|
|
2243
|
+
const result = new Map;
|
|
2244
|
+
for (const [key, group] of groups) {
|
|
2245
|
+
const original = originalKeys.get(key);
|
|
2246
|
+
if (original !== undefined) {
|
|
2247
|
+
result.set(original, group);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return result;
|
|
2182
2251
|
}
|
|
2183
2252
|
function indexMorphOneRelation(parents, children, relation) {
|
|
2184
2253
|
const grouped = indexMorphManyRelation(parents, children, relation);
|
|
2185
2254
|
const result = new Map;
|
|
2186
2255
|
for (const parent of parents) {
|
|
2187
|
-
const matches = grouped
|
|
2256
|
+
const matches = getByRelationKey(grouped, parent[relation.localKey]) ?? [];
|
|
2188
2257
|
result.set(parent[relation.localKey], matches[0]);
|
|
2189
2258
|
}
|
|
2190
2259
|
return result;
|
|
2191
2260
|
}
|
|
2261
|
+
function indexHasManyThroughRelation(parents, children, relation) {
|
|
2262
|
+
const throughKey = relation.throughParentKey ?? "__through_parent_id";
|
|
2263
|
+
const grouped = new Map;
|
|
2264
|
+
for (const child of children) {
|
|
2265
|
+
const key = relationMatchKey(child[throughKey]);
|
|
2266
|
+
if (key === "") {
|
|
2267
|
+
continue;
|
|
2268
|
+
}
|
|
2269
|
+
const existing = grouped.get(key) ?? [];
|
|
2270
|
+
const { [throughKey]: _through, ...far } = child;
|
|
2271
|
+
existing.push(far);
|
|
2272
|
+
grouped.set(key, existing);
|
|
2273
|
+
}
|
|
2274
|
+
const result = new Map;
|
|
2275
|
+
for (const parent of parents) {
|
|
2276
|
+
const key = relationMatchKey(parent[relation.localKey]);
|
|
2277
|
+
result.set(parent[relation.localKey], grouped.get(key) ?? []);
|
|
2278
|
+
}
|
|
2279
|
+
return result;
|
|
2280
|
+
}
|
|
2192
2281
|
function indexMorphToRelation(children, parentsByType, relation) {
|
|
2193
2282
|
const result = new Map;
|
|
2194
2283
|
for (const child of children) {
|
|
@@ -2197,7 +2286,7 @@ function indexMorphToRelation(children, parentsByType, relation) {
|
|
|
2197
2286
|
if (!parents) {
|
|
2198
2287
|
continue;
|
|
2199
2288
|
}
|
|
2200
|
-
const parent = parents
|
|
2289
|
+
const parent = getByRelationKey(parents, child[relation.morphIdKey]);
|
|
2201
2290
|
if (parent) {
|
|
2202
2291
|
result.set(child[relation.morphIdKey], parent);
|
|
2203
2292
|
}
|
|
@@ -2372,6 +2461,24 @@ class RepositoryQuery {
|
|
|
2372
2461
|
});
|
|
2373
2462
|
return this;
|
|
2374
2463
|
}
|
|
2464
|
+
withHasManyThrough(as, relation, farRepository, options = {}) {
|
|
2465
|
+
this.eagerLoads.push({
|
|
2466
|
+
kind: "hasManyThrough",
|
|
2467
|
+
as,
|
|
2468
|
+
relation,
|
|
2469
|
+
repository: farRepository,
|
|
2470
|
+
options
|
|
2471
|
+
});
|
|
2472
|
+
return this;
|
|
2473
|
+
}
|
|
2474
|
+
withTrashed() {
|
|
2475
|
+
this.queryOptions = { ...this.queryOptions, withTrashed: true };
|
|
2476
|
+
return this;
|
|
2477
|
+
}
|
|
2478
|
+
onlyTrashed() {
|
|
2479
|
+
this.queryOptions = { ...this.queryOptions, onlyTrashed: true };
|
|
2480
|
+
return this;
|
|
2481
|
+
}
|
|
2375
2482
|
async get() {
|
|
2376
2483
|
const rows = await this.repository.findAll(this.buildOptions());
|
|
2377
2484
|
return await this.attach(rows);
|
|
@@ -2435,7 +2542,7 @@ class RepositoryQuery {
|
|
|
2435
2542
|
const grouped2 = await load.repository.withConnection(this.repository.getConnection()).loadHasManyForParents(rows, relation2, load.options);
|
|
2436
2543
|
result = result.map((row) => ({
|
|
2437
2544
|
...row,
|
|
2438
|
-
[load.as]: grouped2
|
|
2545
|
+
[load.as]: getByRelationKey(grouped2, row[relation2.localKey]) ?? []
|
|
2439
2546
|
}));
|
|
2440
2547
|
continue;
|
|
2441
2548
|
}
|
|
@@ -2444,7 +2551,7 @@ class RepositoryQuery {
|
|
|
2444
2551
|
const grouped2 = await load.repository.withConnection(this.repository.getConnection()).loadMorphManyForParents(rows, relation2, load.options);
|
|
2445
2552
|
result = result.map((row) => ({
|
|
2446
2553
|
...row,
|
|
2447
|
-
[load.as]: grouped2
|
|
2554
|
+
[load.as]: getByRelationKey(grouped2, row[relation2.localKey]) ?? []
|
|
2448
2555
|
}));
|
|
2449
2556
|
continue;
|
|
2450
2557
|
}
|
|
@@ -2453,7 +2560,16 @@ class RepositoryQuery {
|
|
|
2453
2560
|
const grouped2 = await load.repository.withConnection(this.repository.getConnection()).loadMorphOneForParents(rows, relation2, load.options);
|
|
2454
2561
|
result = result.map((row) => ({
|
|
2455
2562
|
...row,
|
|
2456
|
-
[load.as]: grouped2
|
|
2563
|
+
[load.as]: getByRelationKey(grouped2, row[relation2.localKey])
|
|
2564
|
+
}));
|
|
2565
|
+
continue;
|
|
2566
|
+
}
|
|
2567
|
+
if (load.kind === "hasManyThrough") {
|
|
2568
|
+
const relation2 = load.relation;
|
|
2569
|
+
const grouped2 = await load.repository.withConnection(this.repository.getConnection()).loadHasManyThroughForParents(rows, relation2, load.options);
|
|
2570
|
+
result = result.map((row) => ({
|
|
2571
|
+
...row,
|
|
2572
|
+
[load.as]: getByRelationKey(grouped2, row[relation2.localKey]) ?? []
|
|
2457
2573
|
}));
|
|
2458
2574
|
continue;
|
|
2459
2575
|
}
|
|
@@ -2462,7 +2578,7 @@ class RepositoryQuery {
|
|
|
2462
2578
|
const grouped2 = await this.repository.loadBelongsToManyForParents(rows, relation2, load.repository, load.options);
|
|
2463
2579
|
result = result.map((row) => ({
|
|
2464
2580
|
...row,
|
|
2465
|
-
[load.as]: grouped2
|
|
2581
|
+
[load.as]: getByRelationKey(grouped2, row[relation2.parentKey]) ?? []
|
|
2466
2582
|
}));
|
|
2467
2583
|
continue;
|
|
2468
2584
|
}
|
|
@@ -2471,7 +2587,7 @@ class RepositoryQuery {
|
|
|
2471
2587
|
const grouped2 = await this.repository.loadMorphToForChildren(rows, relation2, load.morphRepositories ?? new Map, load.options);
|
|
2472
2588
|
result = result.map((row) => ({
|
|
2473
2589
|
...row,
|
|
2474
|
-
[load.as]: grouped2
|
|
2590
|
+
[load.as]: getByRelationKey(grouped2, row[relation2.morphIdKey])
|
|
2475
2591
|
}));
|
|
2476
2592
|
continue;
|
|
2477
2593
|
}
|
|
@@ -2479,7 +2595,7 @@ class RepositoryQuery {
|
|
|
2479
2595
|
const grouped = await this.repository.loadBelongsToForParents(rows, relation, load.repository, load.options);
|
|
2480
2596
|
result = result.map((row) => ({
|
|
2481
2597
|
...row,
|
|
2482
|
-
[load.as]: grouped
|
|
2598
|
+
[load.as]: getByRelationKey(grouped, row[relation.foreignKey])
|
|
2483
2599
|
}));
|
|
2484
2600
|
}
|
|
2485
2601
|
return result;
|
|
@@ -2745,6 +2861,55 @@ class BaseRepository {
|
|
|
2745
2861
|
}, options);
|
|
2746
2862
|
return indexHasManyRelation(parents, children, relation);
|
|
2747
2863
|
}
|
|
2864
|
+
async findHasManyThrough(parentId, relation, options = {}) {
|
|
2865
|
+
const grouped = await this.loadHasManyThroughForParents([{ [relation.localKey]: parentId }], relation, options);
|
|
2866
|
+
return getByRelationKey(grouped, parentId) ?? [];
|
|
2867
|
+
}
|
|
2868
|
+
async loadHasManyThroughForParents(parents, relation, options = {}) {
|
|
2869
|
+
if (parents.length === 0) {
|
|
2870
|
+
return indexHasManyThroughRelation(parents, [], relation);
|
|
2871
|
+
}
|
|
2872
|
+
const parentIds = [...new Set(parents.map((parent) => parent[relation.localKey]))];
|
|
2873
|
+
const throughParentKey = relation.throughParentKey ?? "__through_parent_id";
|
|
2874
|
+
const farTable = this.table.name;
|
|
2875
|
+
const columns = this.table.columns.map((column) => `${qualifyColumn(farTable, column)}`).join(", ");
|
|
2876
|
+
const placeholders = parentIds.map((_, index) => `$${index + 1}`).join(", ");
|
|
2877
|
+
const { text: extraWhere, params: extraParams } = this.buildThroughWhere(options, parentIds.length);
|
|
2878
|
+
const softDelete = this.throughSoftDeleteClause(options);
|
|
2879
|
+
const sql = `SELECT ${columns}, ${qualifyColumn(relation.throughTable, relation.firstKey)} AS ${throughParentKey} FROM ${quoteIdentifier(farTable)} INNER JOIN ${quoteIdentifier(relation.throughTable)} ON ${qualifyColumn(relation.throughTable, relation.secondLocalKey)} = ${qualifyColumn(farTable, relation.secondKey)} WHERE ${qualifyColumn(relation.throughTable, relation.firstKey)} IN (${placeholders})${softDelete}${extraWhere}`;
|
|
2880
|
+
const children = await this.connection.unsafe(sql, [
|
|
2881
|
+
...parentIds,
|
|
2882
|
+
...extraParams
|
|
2883
|
+
]);
|
|
2884
|
+
return indexHasManyThroughRelation(parents, children, relation);
|
|
2885
|
+
}
|
|
2886
|
+
throughSoftDeleteClause(options) {
|
|
2887
|
+
const column = resolveSoftDeleteColumn(this.table);
|
|
2888
|
+
if (!column) {
|
|
2889
|
+
return "";
|
|
2890
|
+
}
|
|
2891
|
+
const qualified = qualifyColumn(this.table.name, column);
|
|
2892
|
+
if (options.onlyTrashed) {
|
|
2893
|
+
return ` AND ${qualified} IS NOT NULL`;
|
|
2894
|
+
}
|
|
2895
|
+
if (options.withTrashed) {
|
|
2896
|
+
return "";
|
|
2897
|
+
}
|
|
2898
|
+
return ` AND ${qualified} IS NULL`;
|
|
2899
|
+
}
|
|
2900
|
+
buildThroughWhere(options, paramOffset = 1) {
|
|
2901
|
+
const where = options.where ?? {};
|
|
2902
|
+
const entries = Object.entries(where);
|
|
2903
|
+
if (entries.length === 0) {
|
|
2904
|
+
return { text: "", params: [] };
|
|
2905
|
+
}
|
|
2906
|
+
const params = [];
|
|
2907
|
+
const clauses = entries.map(([column, value], index) => {
|
|
2908
|
+
params.push(value);
|
|
2909
|
+
return `${qualifyColumn(this.table.name, column)} = $${paramOffset + index + 1}`;
|
|
2910
|
+
});
|
|
2911
|
+
return { text: ` AND ${clauses.join(" AND ")}`, params };
|
|
2912
|
+
}
|
|
2748
2913
|
async loadBelongsToForParents(children, relation, parentRepository, options = {}) {
|
|
2749
2914
|
if (children.length === 0) {
|
|
2750
2915
|
return new Map;
|
|
@@ -2770,7 +2935,7 @@ class BaseRepository {
|
|
|
2770
2935
|
const grouped = await this.loadMorphManyForParents(parents, relation, options);
|
|
2771
2936
|
const result = new Map;
|
|
2772
2937
|
for (const parent of parents) {
|
|
2773
|
-
const matches = grouped
|
|
2938
|
+
const matches = getByRelationKey(grouped, parent[relation.localKey]) ?? [];
|
|
2774
2939
|
result.set(parent[relation.localKey], matches[0]);
|
|
2775
2940
|
}
|
|
2776
2941
|
return result;
|
|
@@ -2799,7 +2964,7 @@ class BaseRepository {
|
|
|
2799
2964
|
}, options);
|
|
2800
2965
|
const indexed = new Map;
|
|
2801
2966
|
for (const parent of parents) {
|
|
2802
|
-
indexed.set(parent[ownerKey], parent);
|
|
2967
|
+
indexed.set(relationMatchKey(parent[ownerKey]), parent);
|
|
2803
2968
|
}
|
|
2804
2969
|
parentsByType.set(morphType, indexed);
|
|
2805
2970
|
}
|
|
@@ -2810,7 +2975,8 @@ class BaseRepository {
|
|
|
2810
2975
|
return indexBelongsToManyRelation(parents, [], [], relation);
|
|
2811
2976
|
}
|
|
2812
2977
|
const parentIds = [...new Set(parents.map((parent) => parent[relation.parentKey]))];
|
|
2813
|
-
const
|
|
2978
|
+
const placeholders = parentIds.map((_, index) => `$${index + 1}`).join(", ");
|
|
2979
|
+
const pivotRows = await this.connection.unsafe(`SELECT * FROM ${relation.pivotTable} WHERE ${String(relation.foreignPivotKey)} IN (${placeholders})`, parentIds);
|
|
2814
2980
|
if (pivotRows.length === 0) {
|
|
2815
2981
|
return indexBelongsToManyRelation(parents, [], [], relation);
|
|
2816
2982
|
}
|
|
@@ -3472,7 +3638,8 @@ class BelongsToManyRelationQuery {
|
|
|
3472
3638
|
return;
|
|
3473
3639
|
}
|
|
3474
3640
|
const list = Array.isArray(ids) ? ids : [ids];
|
|
3475
|
-
|
|
3641
|
+
const placeholders = list.map((_, index) => `$${index + 2}`).join(", ");
|
|
3642
|
+
await this.connection().unsafe(`DELETE FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1 AND ${String(this.relation.relatedPivotKey)} IN (${placeholders})`, [parentId, ...list]);
|
|
3476
3643
|
}
|
|
3477
3644
|
async sync(ids) {
|
|
3478
3645
|
await this.detach();
|
|
@@ -3654,6 +3821,65 @@ class MorphToRelationQuery {
|
|
|
3654
3821
|
}
|
|
3655
3822
|
}
|
|
3656
3823
|
|
|
3824
|
+
class HasManyThroughRelationQuery {
|
|
3825
|
+
parent;
|
|
3826
|
+
related;
|
|
3827
|
+
relation;
|
|
3828
|
+
kind = "hasManyThrough";
|
|
3829
|
+
extraWhere = {};
|
|
3830
|
+
extraOptions = {};
|
|
3831
|
+
constructor(parent, related, relation) {
|
|
3832
|
+
this.parent = parent;
|
|
3833
|
+
this.related = related;
|
|
3834
|
+
this.relation = relation;
|
|
3835
|
+
}
|
|
3836
|
+
where(where) {
|
|
3837
|
+
this.extraWhere = { ...this.extraWhere, ...where };
|
|
3838
|
+
return this;
|
|
3839
|
+
}
|
|
3840
|
+
orderBy(orderBy) {
|
|
3841
|
+
this.extraOptions = { ...this.extraOptions, orderBy };
|
|
3842
|
+
return this;
|
|
3843
|
+
}
|
|
3844
|
+
limit(limit) {
|
|
3845
|
+
this.extraOptions = { ...this.extraOptions, limit };
|
|
3846
|
+
return this;
|
|
3847
|
+
}
|
|
3848
|
+
applyEagerLoad(query, alias) {
|
|
3849
|
+
query.withHasManyThrough(alias, this.relation, this.related.repository(), this.extraOptions);
|
|
3850
|
+
}
|
|
3851
|
+
hydrateEager(row, alias) {
|
|
3852
|
+
const value = row[alias] ?? [];
|
|
3853
|
+
const rows = Array.isArray(value) ? value : [];
|
|
3854
|
+
return rows.map((item) => this.related.newFromRecord(item));
|
|
3855
|
+
}
|
|
3856
|
+
toExistsClause(parentTable) {
|
|
3857
|
+
const farTable = this.related.repository().getTable().name;
|
|
3858
|
+
const extra = buildAdvancedWhereClause(farTable, this.extraWhere, [], []);
|
|
3859
|
+
const extraSql = extra.clause.replace(/^ WHERE /, "");
|
|
3860
|
+
const sql = `SELECT 1 FROM ${quoteIdentifier(farTable)} INNER JOIN ${quoteIdentifier(this.relation.throughTable)} ON ${qualifyColumn(this.relation.throughTable, this.relation.secondLocalKey)} = ${qualifyColumn(farTable, this.relation.secondKey)} WHERE ${qualifyColumn(this.relation.throughTable, this.relation.firstKey)} = ${qualifyColumn(parentTable, this.relation.localKey)}${extraSql ? ` AND ${extraSql}` : ""}`;
|
|
3861
|
+
return { sql, params: extra.params };
|
|
3862
|
+
}
|
|
3863
|
+
async get() {
|
|
3864
|
+
const rows = await this.related.repository().withConnection(this.parent.getRepository().getConnection()).findHasManyThrough(this.parent.get(this.relation.localKey), this.relation, {
|
|
3865
|
+
...this.extraOptions,
|
|
3866
|
+
where: this.extraWhere
|
|
3867
|
+
});
|
|
3868
|
+
return rows.map((row) => this.related.newFromRecord(row));
|
|
3869
|
+
}
|
|
3870
|
+
async first() {
|
|
3871
|
+
const rows = await this.limit(1).get();
|
|
3872
|
+
return rows[0] ?? null;
|
|
3873
|
+
}
|
|
3874
|
+
async count() {
|
|
3875
|
+
const rows = await this.get();
|
|
3876
|
+
return rows.length;
|
|
3877
|
+
}
|
|
3878
|
+
then(onfulfilled, onrejected) {
|
|
3879
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3657
3883
|
// ../../src/core/database/model.ts
|
|
3658
3884
|
var modelRepositories = new WeakMap;
|
|
3659
3885
|
var namedModels = new Map;
|
|
@@ -3982,6 +4208,18 @@ class ModelQuery {
|
|
|
3982
4208
|
this.query.withMorphTo(...args);
|
|
3983
4209
|
return this;
|
|
3984
4210
|
}
|
|
4211
|
+
withHasManyThrough(...args) {
|
|
4212
|
+
this.query.withHasManyThrough(...args);
|
|
4213
|
+
return this;
|
|
4214
|
+
}
|
|
4215
|
+
withTrashed() {
|
|
4216
|
+
this.query.withTrashed();
|
|
4217
|
+
return this;
|
|
4218
|
+
}
|
|
4219
|
+
onlyTrashed() {
|
|
4220
|
+
this.query.onlyTrashed();
|
|
4221
|
+
return this;
|
|
4222
|
+
}
|
|
3985
4223
|
async get() {
|
|
3986
4224
|
const statics = modelStatics(this.modelClass);
|
|
3987
4225
|
const rows = await this.query.get();
|
|
@@ -4189,6 +4427,31 @@ class Model {
|
|
|
4189
4427
|
static with(...relations) {
|
|
4190
4428
|
return Model.query.call(this).with(...relations);
|
|
4191
4429
|
}
|
|
4430
|
+
static withTrashed() {
|
|
4431
|
+
return Model.query.call(this).withTrashed();
|
|
4432
|
+
}
|
|
4433
|
+
static onlyTrashed() {
|
|
4434
|
+
return Model.query.call(this).onlyTrashed();
|
|
4435
|
+
}
|
|
4436
|
+
static async chunk(count, callback) {
|
|
4437
|
+
const statics = modelStatics(this);
|
|
4438
|
+
ensureBooted(this);
|
|
4439
|
+
await resolveModelRepository(this).chunk(count, async (rows) => {
|
|
4440
|
+
return await callback(rows.map((row) => statics.newFromRecord(row, true)));
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4443
|
+
static async cursorPaginate(options) {
|
|
4444
|
+
const statics = modelStatics(this);
|
|
4445
|
+
ensureBooted(this);
|
|
4446
|
+
const page = await resolveModelRepository(this).cursorPaginate({
|
|
4447
|
+
perPage: options.perPage,
|
|
4448
|
+
cursor: options.cursor
|
|
4449
|
+
});
|
|
4450
|
+
return {
|
|
4451
|
+
data: page.data.map((row) => statics.newFromRecord(row, true)),
|
|
4452
|
+
meta: page.meta
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4192
4455
|
static whereHas(name, constrain) {
|
|
4193
4456
|
return Model.query.call(this).whereHas(name, constrain);
|
|
4194
4457
|
}
|
|
@@ -4313,7 +4576,7 @@ class Model {
|
|
|
4313
4576
|
}
|
|
4314
4577
|
async loadHasMany(as, relation, childRepository, options = {}) {
|
|
4315
4578
|
const grouped = await childRepository.withConnection(this.repository.getConnection()).loadHasManyForParents([this.attributes], relation, options);
|
|
4316
|
-
const loaded = grouped
|
|
4579
|
+
const loaded = getByRelationKey(grouped, this.attributes[relation.localKey]) ?? [];
|
|
4317
4580
|
return Object.assign(this, { [as]: loaded });
|
|
4318
4581
|
}
|
|
4319
4582
|
async loadHasOne(as, relation, childRepository, options = {}) {
|
|
@@ -4323,7 +4586,7 @@ class Model {
|
|
|
4323
4586
|
}
|
|
4324
4587
|
async loadBelongsTo(as, relation, parentRepository, options = {}) {
|
|
4325
4588
|
const grouped = await this.repository.loadBelongsToForParents([this.attributes], relation, parentRepository, options);
|
|
4326
|
-
const loaded = grouped
|
|
4589
|
+
const loaded = getByRelationKey(grouped, this.attributes[relation.foreignKey]);
|
|
4327
4590
|
return Object.assign(this, { [as]: loaded });
|
|
4328
4591
|
}
|
|
4329
4592
|
async loadBelongsToMany(as, relation, relatedRepository, options = {}) {
|
|
@@ -4343,7 +4606,7 @@ class Model {
|
|
|
4343
4606
|
}
|
|
4344
4607
|
});
|
|
4345
4608
|
const grouped = indexBelongsToManyRelation([this.attributes], pivotRows, relatedRows, relation);
|
|
4346
|
-
const loaded = grouped
|
|
4609
|
+
const loaded = getByRelationKey(grouped, parentId) ?? [];
|
|
4347
4610
|
return Object.assign(this, { [as]: loaded });
|
|
4348
4611
|
}
|
|
4349
4612
|
hasMany(related, foreignKey, localKey) {
|
|
@@ -4373,6 +4636,20 @@ class Model {
|
|
|
4373
4636
|
ownerKey: ownerKey ?? relatedTable.primaryKey
|
|
4374
4637
|
}));
|
|
4375
4638
|
}
|
|
4639
|
+
hasManyThrough(related, through, firstKey, secondKey, localKey, secondLocalKey) {
|
|
4640
|
+
const table = this.repository.getTable();
|
|
4641
|
+
const relatedClass = resolveRelated(related);
|
|
4642
|
+
const throughClass = resolveRelated(through);
|
|
4643
|
+
const throughTable = throughClass.repository().getTable();
|
|
4644
|
+
return new HasManyThroughRelationQuery(this, relatedClass, hasManyThrough({
|
|
4645
|
+
name: relatedClass.repository().getTable().name,
|
|
4646
|
+
throughTable: throughTable.name,
|
|
4647
|
+
localKey: localKey ?? table.primaryKey,
|
|
4648
|
+
firstKey: firstKey ?? foreignKeyFromTable(table.name),
|
|
4649
|
+
secondLocalKey: secondLocalKey ?? throughTable.primaryKey,
|
|
4650
|
+
secondKey: secondKey ?? foreignKeyFromTable(throughTable.name)
|
|
4651
|
+
}));
|
|
4652
|
+
}
|
|
4376
4653
|
belongsToMany(related, pivotTable, foreignPivotKey, relatedPivotKey) {
|
|
4377
4654
|
const table = this.repository.getTable();
|
|
4378
4655
|
const relatedClass = resolveRelated(related);
|
|
@@ -8014,17 +8291,33 @@ async function resolveTenantForRequest(request) {
|
|
|
8014
8291
|
if (Number.isInteger(parsedHeader) && parsedHeader > 0 && parsedHeader !== userTenantId) {
|
|
8015
8292
|
throw new ForbiddenError("Tenant header does not match your account.");
|
|
8016
8293
|
}
|
|
8017
|
-
|
|
8294
|
+
const memberTenant = await resolveTenant(userTenantId);
|
|
8295
|
+
if (memberTenant) {
|
|
8296
|
+
return memberTenant;
|
|
8297
|
+
}
|
|
8298
|
+
return DEFAULT_TENANT;
|
|
8018
8299
|
}
|
|
8019
8300
|
if (Number.isInteger(parsedHeader) && parsedHeader > 0) {
|
|
8020
|
-
|
|
8301
|
+
const headerTenant = await resolveTenant(parsedHeader);
|
|
8302
|
+
if (headerTenant) {
|
|
8303
|
+
return headerTenant;
|
|
8304
|
+
}
|
|
8305
|
+
return DEFAULT_TENANT;
|
|
8021
8306
|
}
|
|
8022
|
-
|
|
8307
|
+
const adminTenant = await resolveTenant(userTenantId);
|
|
8308
|
+
if (adminTenant) {
|
|
8309
|
+
return adminTenant;
|
|
8310
|
+
}
|
|
8311
|
+
return DEFAULT_TENANT;
|
|
8023
8312
|
}
|
|
8024
8313
|
}
|
|
8025
8314
|
const headerTenantId = Number.isInteger(parsedHeader) && parsedHeader > 0 ? parsedHeader : null;
|
|
8026
8315
|
const tenantId = isPublicReadsEnabled() && headerTenantId !== null ? headerTenantId : DEFAULT_TENANT.id;
|
|
8027
|
-
|
|
8316
|
+
const guestTenant = await resolveTenant(tenantId);
|
|
8317
|
+
if (guestTenant) {
|
|
8318
|
+
return guestTenant;
|
|
8319
|
+
}
|
|
8320
|
+
return DEFAULT_TENANT;
|
|
8028
8321
|
}
|
|
8029
8322
|
function createTenantMiddleware() {
|
|
8030
8323
|
return async (request, next) => {
|