@apisr/drizzle-model 0.0.1 → 0.0.2

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.
Files changed (70) hide show
  1. package/dist/index.d.mts +3 -0
  2. package/dist/index.mjs +4 -0
  3. package/dist/model/builder.d.mts +20 -0
  4. package/dist/model/builder.mjs +18 -0
  5. package/dist/model/config.d.mts +23 -0
  6. package/dist/model/core/joins.mjs +184 -0
  7. package/dist/model/core/projection.mjs +28 -0
  8. package/dist/model/core/runtime.mjs +198 -0
  9. package/dist/model/core/thenable.mjs +64 -0
  10. package/dist/model/core/transform.mjs +39 -0
  11. package/dist/model/core/where.mjs +130 -0
  12. package/dist/model/core/with.mjs +19 -0
  13. package/dist/model/dialect.d.mts +12 -0
  14. package/dist/model/format.d.mts +4 -0
  15. package/dist/model/index.d.mts +1 -0
  16. package/dist/model/index.mjs +3 -0
  17. package/dist/model/methods/exclude.d.mts +8 -0
  18. package/dist/model/methods/include.d.mts +6 -0
  19. package/dist/model/methods/insert.d.mts +7 -0
  20. package/dist/model/methods/query/where.d.mts +14 -0
  21. package/dist/model/methods/return.d.mts +12 -0
  22. package/dist/model/methods/select.d.mts +8 -0
  23. package/dist/model/methods/update.d.mts +7 -0
  24. package/dist/model/methods/upsert.d.mts +26 -0
  25. package/dist/model/methods/with.d.mts +16 -0
  26. package/dist/model/model.d.mts +105 -0
  27. package/dist/model/options.d.mts +28 -0
  28. package/dist/model/query/operations.d.mts +66 -0
  29. package/dist/model/relation.d.mts +68 -0
  30. package/dist/model/result.d.mts +34 -0
  31. package/dist/model/table.d.mts +69 -0
  32. package/dist/types.d.mts +10 -0
  33. package/drizzle.config.ts +6 -6
  34. package/package.json +1 -1
  35. package/src/model/builder.ts +37 -37
  36. package/src/model/config.ts +28 -25
  37. package/src/model/core/joins.ts +337 -252
  38. package/src/model/core/projection.ts +49 -35
  39. package/src/model/core/runtime.ts +302 -221
  40. package/src/model/core/thenable.ts +70 -61
  41. package/src/model/core/transform.ts +53 -33
  42. package/src/model/core/where.ts +228 -162
  43. package/src/model/core/with.ts +21 -21
  44. package/src/model/dialect.ts +12 -2
  45. package/src/model/foreigns.ts +6 -10
  46. package/src/model/format.ts +8 -8
  47. package/src/model/methods/include.ts +1 -1
  48. package/src/model/methods/insert.ts +13 -10
  49. package/src/model/methods/levels.ts +7 -1
  50. package/src/model/methods/query/where.ts +49 -36
  51. package/src/model/methods/return.ts +13 -12
  52. package/src/model/methods/select.ts +29 -29
  53. package/src/model/methods/update.ts +3 -1
  54. package/src/model/methods/upsert.ts +35 -36
  55. package/src/model/model.ts +115 -107
  56. package/src/model/options.ts +44 -37
  57. package/src/model/query/operations.ts +73 -72
  58. package/src/model/relation.ts +47 -46
  59. package/src/model/result.ts +79 -63
  60. package/src/model/shape.ts +5 -4
  61. package/src/model/table.ts +34 -33
  62. package/src/types.ts +3 -3
  63. package/tests/builder-v2-mysql.type-test.ts +31 -20
  64. package/tests/builder-v2.type-test.ts +246 -253
  65. package/tests/builder.test.ts +1 -1
  66. package/tests/db.ts +3 -3
  67. package/tests/find.test.ts +149 -138
  68. package/tests/insert.test.ts +217 -203
  69. package/tests/relations.ts +34 -34
  70. package/tests/schema.ts +34 -35
@@ -3,277 +3,362 @@ import { and, eq } from "drizzle-orm";
3
3
  type AnyObj = Record<string, any>;
4
4
 
5
5
  type JoinNode = {
6
- path: string[];
7
- key: string;
8
- relationType: "one" | "many";
9
- sourceTableName: string;
10
- targetTableName: string;
11
- sourceTable: AnyObj;
12
- targetTable: AnyObj;
13
- targetAliasTable: AnyObj;
14
- aliasKey: string;
15
- sourceColumns: any[];
16
- targetColumns: any[];
17
- pkField: string;
18
- parent?: JoinNode;
19
- children: JoinNode[];
6
+ path: string[];
7
+ key: string;
8
+ relationType: "one" | "many";
9
+ sourceTableName: string;
10
+ targetTableName: string;
11
+ sourceTable: AnyObj;
12
+ targetTable: AnyObj;
13
+ targetAliasTable: AnyObj;
14
+ aliasKey: string;
15
+ sourceColumns: any[];
16
+ targetColumns: any[];
17
+ pkField: string;
18
+ parent?: JoinNode;
19
+ children: JoinNode[];
20
20
  };
21
21
 
22
22
  function isDrizzleColumn(value: any): boolean {
23
- return !!value && typeof value === "object" && typeof value.getSQL === "function";
23
+ return (
24
+ !!value && typeof value === "object" && typeof value.getSQL === "function"
25
+ );
24
26
  }
25
27
 
26
28
  function getPrimaryKeyField(table: AnyObj): string {
27
- for (const [k, v] of Object.entries(table)) {
28
- if (!isDrizzleColumn(v)) continue;
29
- if ((v as any).primary === true) return k;
30
- if ((v as any).config?.primaryKey === true) return k;
31
- }
32
- if ("id" in table) return "id";
33
- return Object.keys(table).find((k) => isDrizzleColumn((table as any)[k])) ?? "id";
29
+ for (const [k, v] of Object.entries(table)) {
30
+ if (!isDrizzleColumn(v)) {
31
+ continue;
32
+ }
33
+ if ((v as any).primary === true) {
34
+ return k;
35
+ }
36
+ if ((v as any).config?.primaryKey === true) {
37
+ return k;
38
+ }
39
+ }
40
+ if ("id" in table) {
41
+ return "id";
42
+ }
43
+ return (
44
+ Object.keys(table).find((k) => isDrizzleColumn((table as any)[k])) ?? "id"
45
+ );
34
46
  }
35
47
 
36
48
  function isAllNullRow(obj: AnyObj | null | undefined): boolean {
37
- if (!obj || typeof obj !== "object") return true;
38
- for (const v of Object.values(obj)) {
39
- if (v !== null && v !== undefined) return false;
40
- }
41
- return true;
49
+ if (!obj || typeof obj !== "object") {
50
+ return true;
51
+ }
52
+ for (const v of Object.values(obj)) {
53
+ if (v !== null && v !== undefined) {
54
+ return false;
55
+ }
56
+ }
57
+ return true;
42
58
  }
43
59
 
44
- async function aliasTable(table: AnyObj, aliasName: string, dialect: string): Promise<AnyObj> {
45
- // Drizzle exports `alias()` from dialect-specific core modules.
46
- // We keep this dynamic to avoid hard dependency on a single dialect.
47
- if (dialect === "PostgreSQL") {
48
- const mod: any = await import("drizzle-orm/pg-core");
49
- if (typeof mod.alias === "function") return mod.alias(table, aliasName);
50
- }
51
- if (dialect === "MySQL") {
52
- const mod: any = await import("drizzle-orm/mysql-core");
53
- if (typeof mod.alias === "function") return mod.alias(table, aliasName);
54
- }
55
- if (dialect === "SQLite") {
56
- const mod: any = await import("drizzle-orm/sqlite-core");
57
- if (typeof mod.alias === "function") return mod.alias(table, aliasName);
58
- }
59
-
60
- return table;
60
+ async function aliasTable(
61
+ table: AnyObj,
62
+ aliasName: string,
63
+ dialect: string
64
+ ): Promise<AnyObj> {
65
+ // Drizzle exports `alias()` from dialect-specific core modules.
66
+ // We keep this dynamic to avoid hard dependency on a single dialect.
67
+ if (dialect === "PostgreSQL") {
68
+ const mod: any = await import("drizzle-orm/pg-core");
69
+ if (typeof mod.alias === "function") {
70
+ return mod.alias(table, aliasName);
71
+ }
72
+ }
73
+ if (dialect === "MySQL") {
74
+ const mod: any = await import("drizzle-orm/mysql-core");
75
+ if (typeof mod.alias === "function") {
76
+ return mod.alias(table, aliasName);
77
+ }
78
+ }
79
+ if (dialect === "SQLite") {
80
+ const mod: any = await import("drizzle-orm/sqlite-core");
81
+ if (typeof mod.alias === "function") {
82
+ return mod.alias(table, aliasName);
83
+ }
84
+ }
85
+
86
+ return table;
61
87
  }
62
88
 
63
89
  function buildJoinOn(node: JoinNode): any {
64
- const parts = node.sourceColumns.map((src, i) => {
65
- const tgt = node.targetColumns[i];
66
- // tgt is a column bound to the *original* target table; we need the one from alias table.
67
- const tgtKey = Object.entries(node.targetTable).find(([, v]) => v === tgt)?.[0];
68
- const tgtCol = tgtKey ? (node.targetAliasTable as any)[tgtKey] : tgt;
69
- return eq(tgtCol, src);
70
- });
71
- return parts.length === 1 ? parts[0] : and(...parts);
90
+ const parts = node.sourceColumns.map((src, i) => {
91
+ const tgt = node.targetColumns[i];
92
+ // tgt is a column bound to the *original* target table; we need the one from alias table.
93
+ const tgtKey = Object.entries(node.targetTable).find(
94
+ ([, v]) => v === tgt
95
+ )?.[0];
96
+ const tgtCol = tgtKey ? (node.targetAliasTable as any)[tgtKey] : tgt;
97
+ return eq(tgtCol, src);
98
+ });
99
+ return parts.length === 1 ? parts[0] : and(...parts);
72
100
  }
73
101
 
74
102
  function buildSelectMapForTable(table: AnyObj): AnyObj {
75
- const out: AnyObj = {};
76
- for (const [k, v] of Object.entries(table)) {
77
- if (isDrizzleColumn(v)) out[k] = v;
78
- }
79
- return out;
103
+ const out: AnyObj = {};
104
+ for (const [k, v] of Object.entries(table)) {
105
+ if (isDrizzleColumn(v)) {
106
+ out[k] = v;
107
+ }
108
+ }
109
+ return out;
80
110
  }
81
111
 
82
112
  export async function executeWithJoins(args: {
83
- db: any;
84
- schema: Record<string, any>;
85
- relations: Record<string, any>;
86
- baseTableName: string;
87
- baseTable: AnyObj;
88
- dialect: string;
89
- whereSql?: any;
90
- withValue: AnyObj;
91
- limitOne?: boolean;
113
+ db: any;
114
+ schema: Record<string, any>;
115
+ relations: Record<string, any>;
116
+ baseTableName: string;
117
+ baseTable: AnyObj;
118
+ dialect: string;
119
+ whereSql?: any;
120
+ withValue: AnyObj;
121
+ limitOne?: boolean;
92
122
  }): Promise<any> {
93
- const { db, schema, relations, baseTableName, baseTable, dialect, whereSql, withValue, limitOne } = args;
94
-
95
- const usedAliasKeys = new Set<string>();
96
-
97
- const buildNode = async (
98
- parent: JoinNode | undefined,
99
- currentTableName: string,
100
- currentTable: AnyObj,
101
- key: string,
102
- value: any,
103
- path: string[],
104
- ): Promise<JoinNode> => {
105
- const relMeta = (relations as any)[currentTableName]?.relations?.[key];
106
- if (!relMeta) throw new Error(`Unknown relation '${key}' on table '${currentTableName}'.`);
107
-
108
- const targetTableName: string = relMeta.targetTableName;
109
- const targetTable: AnyObj = (schema as any)[targetTableName];
110
- const aliasKeyBase = [...path, key].join("__");
111
- let aliasKey = aliasKeyBase;
112
- let idx = 1;
113
- while (usedAliasKeys.has(aliasKey)) {
114
- aliasKey = `${aliasKeyBase}_${idx++}`;
115
- }
116
- usedAliasKeys.add(aliasKey);
117
-
118
- const needsAlias = targetTableName === currentTableName || usedAliasKeys.has(`table:${targetTableName}`);
119
- usedAliasKeys.add(`table:${targetTableName}`);
120
-
121
- const targetAliasTable = needsAlias ? await aliasTable(targetTable, aliasKey, dialect) : targetTable;
122
-
123
- const node: JoinNode = {
124
- path: [...path, key],
125
- key,
126
- relationType: relMeta.relationType,
127
- sourceTableName: currentTableName,
128
- targetTableName,
129
- sourceTable: currentTable,
130
- targetTable,
131
- targetAliasTable,
132
- aliasKey,
133
- sourceColumns: relMeta.sourceColumns ?? [],
134
- targetColumns: relMeta.targetColumns ?? [],
135
- pkField: getPrimaryKeyField(targetAliasTable),
136
- parent,
137
- children: [],
138
- };
139
-
140
- if (value && typeof value === "object" && value !== true) {
141
- for (const [childKey, childVal] of Object.entries(value)) {
142
- if (childVal !== true && (typeof childVal !== "object" || childVal == null)) continue;
143
- const child = await buildNode(node, targetTableName, targetAliasTable, childKey, childVal, [...path, key]);
144
- node.children.push(child);
145
- }
146
- }
147
-
148
- return node;
149
- };
150
-
151
- const root: JoinNode = {
152
- path: [],
153
- key: "$root",
154
- relationType: "one",
155
- sourceTableName: baseTableName,
156
- targetTableName: baseTableName,
157
- sourceTable: baseTable,
158
- targetTable: baseTable,
159
- targetAliasTable: baseTable,
160
- aliasKey: "$base",
161
- sourceColumns: [],
162
- targetColumns: [],
163
- pkField: getPrimaryKeyField(baseTable),
164
- children: [],
165
- };
166
-
167
- for (const [key, value] of Object.entries(withValue)) {
168
- if (value !== true && (typeof value !== "object" || value == null)) continue;
169
- const child = await buildNode(undefined, baseTableName, baseTable, key, value, []);
170
- root.children.push(child);
171
- }
172
-
173
- // Flatten nodes in join order (preorder)
174
- const nodes: JoinNode[] = [];
175
- const walk = (n: JoinNode) => {
176
- for (const c of n.children) {
177
- nodes.push(c);
178
- walk(c);
179
- }
180
- };
181
- walk(root);
182
-
183
- // Build select map: base + each joined alias
184
- const selectMap: AnyObj = {
185
- base: buildSelectMapForTable(baseTable),
186
- };
187
- for (const n of nodes) {
188
- selectMap[n.aliasKey] = buildSelectMapForTable(n.targetAliasTable);
189
- }
190
-
191
- let q = db.select(selectMap).from(baseTable);
192
- if (whereSql) q = q.where(whereSql);
193
-
194
- // Apply joins
195
- for (const n of nodes) {
196
- const on = buildJoinOn(n);
197
- q = q.leftJoin(n.targetAliasTable, on);
198
- }
199
-
200
- if (limitOne) q = q.limit(1);
201
-
202
- const rows = await q;
203
-
204
- // Group rows into nested objects.
205
- const basePk = root.pkField;
206
- const baseMap = new Map<any, AnyObj>();
207
-
208
- const ensureManyContainer = (obj: AnyObj, key: string) => {
209
- if (!Array.isArray(obj[key])) obj[key] = [];
210
- };
211
-
212
- const ensureOneContainer = (obj: AnyObj, key: string) => {
213
- if (!(key in obj)) obj[key] = null;
214
- };
215
-
216
- const manyIndexByPath = new Map<string, Map<any, AnyObj>>();
217
-
218
- for (const row of rows) {
219
- const baseRow = (row as any).base;
220
- const baseId = (baseRow as any)[basePk];
221
- if (baseId === undefined) continue;
222
-
223
- const baseObj = (() => {
224
- const existing = baseMap.get(baseId);
225
- if (existing) return existing;
226
- const created = { ...baseRow };
227
- baseMap.set(baseId, created);
228
- return created;
229
- })();
230
-
231
- // Walk nodes, attach to parent objects.
232
- for (const n of nodes) {
233
- const data = (row as any)[n.aliasKey];
234
- const relPath = n.path.join(".");
235
-
236
- // Resolve parent container
237
- const parentPath = n.parent ? n.parent.path.join(".") : "";
238
- let parentObj: AnyObj = baseObj;
239
- if (parentPath) {
240
- // parent might be many; we attach to the last inserted parent instance.
241
- const parentIndex = manyIndexByPath.get(parentPath);
242
- if (parentIndex && parentIndex.size) {
243
- // pick last inserted (Map preserves insertion order)
244
- parentObj = Array.from(parentIndex.values()).at(-1) as AnyObj;
245
- } else {
246
- const parentKey = n.parent?.key;
247
- parentObj = parentKey ? ((baseObj as any)[parentKey] ?? baseObj) : baseObj;
248
- }
249
- }
250
-
251
- if (isAllNullRow(data)) {
252
- if (n.relationType === "one") {
253
- ensureOneContainer(parentObj, n.key);
254
- } else {
255
- ensureManyContainer(parentObj, n.key);
256
- }
257
- continue;
258
- }
259
-
260
- const pk = (data as any)[n.pkField];
261
- if (n.relationType === "one") {
262
- parentObj[n.key] = { ...(data as any) };
263
- } else {
264
- ensureManyContainer(parentObj, n.key);
265
- const indexKey = relPath;
266
- if (!manyIndexByPath.has(indexKey)) manyIndexByPath.set(indexKey, new Map());
267
- const idxMap = manyIndexByPath.get(indexKey)!;
268
- if (!idxMap.has(pk)) {
269
- const obj = { ...(data as any) };
270
- idxMap.set(pk, obj);
271
- (parentObj[n.key] as any[]).push(obj);
272
- }
273
- }
274
- }
275
- }
276
-
277
- const out = Array.from(baseMap.values());
278
- return limitOne ? out[0] : out;
123
+ const {
124
+ db,
125
+ schema,
126
+ relations,
127
+ baseTableName,
128
+ baseTable,
129
+ dialect,
130
+ whereSql,
131
+ withValue,
132
+ limitOne,
133
+ } = args;
134
+
135
+ const usedAliasKeys = new Set<string>();
136
+
137
+ const buildNode = async (
138
+ parent: JoinNode | undefined,
139
+ currentTableName: string,
140
+ currentTable: AnyObj,
141
+ key: string,
142
+ value: any,
143
+ path: string[]
144
+ ): Promise<JoinNode> => {
145
+ const relMeta = (relations as any)[currentTableName]?.relations?.[key];
146
+ if (!relMeta) {
147
+ throw new Error(
148
+ `Unknown relation '${key}' on table '${currentTableName}'.`
149
+ );
150
+ }
151
+
152
+ const targetTableName: string = relMeta.targetTableName;
153
+ const targetTable: AnyObj = (schema as any)[targetTableName];
154
+ const aliasKeyBase = [...path, key].join("__");
155
+ let aliasKey = aliasKeyBase;
156
+ let idx = 1;
157
+ while (usedAliasKeys.has(aliasKey)) {
158
+ aliasKey = `${aliasKeyBase}_${idx++}`;
159
+ }
160
+ usedAliasKeys.add(aliasKey);
161
+
162
+ const needsAlias =
163
+ targetTableName === currentTableName ||
164
+ usedAliasKeys.has(`table:${targetTableName}`);
165
+ usedAliasKeys.add(`table:${targetTableName}`);
166
+
167
+ const targetAliasTable = needsAlias
168
+ ? await aliasTable(targetTable, aliasKey, dialect)
169
+ : targetTable;
170
+
171
+ const node: JoinNode = {
172
+ path: [...path, key],
173
+ key,
174
+ relationType: relMeta.relationType,
175
+ sourceTableName: currentTableName,
176
+ targetTableName,
177
+ sourceTable: currentTable,
178
+ targetTable,
179
+ targetAliasTable,
180
+ aliasKey,
181
+ sourceColumns: relMeta.sourceColumns ?? [],
182
+ targetColumns: relMeta.targetColumns ?? [],
183
+ pkField: getPrimaryKeyField(targetAliasTable),
184
+ parent,
185
+ children: [],
186
+ };
187
+
188
+ if (value && typeof value === "object" && value !== true) {
189
+ for (const [childKey, childVal] of Object.entries(value)) {
190
+ if (
191
+ childVal !== true &&
192
+ (typeof childVal !== "object" || childVal == null)
193
+ ) {
194
+ continue;
195
+ }
196
+ const child = await buildNode(
197
+ node,
198
+ targetTableName,
199
+ targetAliasTable,
200
+ childKey,
201
+ childVal,
202
+ [...path, key]
203
+ );
204
+ node.children.push(child);
205
+ }
206
+ }
207
+
208
+ return node;
209
+ };
210
+
211
+ const root: JoinNode = {
212
+ path: [],
213
+ key: "$root",
214
+ relationType: "one",
215
+ sourceTableName: baseTableName,
216
+ targetTableName: baseTableName,
217
+ sourceTable: baseTable,
218
+ targetTable: baseTable,
219
+ targetAliasTable: baseTable,
220
+ aliasKey: "$base",
221
+ sourceColumns: [],
222
+ targetColumns: [],
223
+ pkField: getPrimaryKeyField(baseTable),
224
+ children: [],
225
+ };
226
+
227
+ for (const [key, value] of Object.entries(withValue)) {
228
+ if (value !== true && (typeof value !== "object" || value == null)) {
229
+ continue;
230
+ }
231
+ const child = await buildNode(
232
+ undefined,
233
+ baseTableName,
234
+ baseTable,
235
+ key,
236
+ value,
237
+ []
238
+ );
239
+ root.children.push(child);
240
+ }
241
+
242
+ // Flatten nodes in join order (preorder)
243
+ const nodes: JoinNode[] = [];
244
+ const walk = (n: JoinNode) => {
245
+ for (const c of n.children) {
246
+ nodes.push(c);
247
+ walk(c);
248
+ }
249
+ };
250
+ walk(root);
251
+
252
+ // Build select map: base + each joined alias
253
+ const selectMap: AnyObj = {
254
+ base: buildSelectMapForTable(baseTable),
255
+ };
256
+ for (const n of nodes) {
257
+ selectMap[n.aliasKey] = buildSelectMapForTable(n.targetAliasTable);
258
+ }
259
+
260
+ let q = db.select(selectMap).from(baseTable);
261
+ if (whereSql) {
262
+ q = q.where(whereSql);
263
+ }
264
+
265
+ // Apply joins
266
+ for (const n of nodes) {
267
+ const on = buildJoinOn(n);
268
+ q = q.leftJoin(n.targetAliasTable, on);
269
+ }
270
+
271
+ if (limitOne) {
272
+ q = q.limit(1);
273
+ }
274
+
275
+ const rows = await q;
276
+
277
+ // Group rows into nested objects.
278
+ const basePk = root.pkField;
279
+ const baseMap = new Map<any, AnyObj>();
280
+
281
+ const ensureManyContainer = (obj: AnyObj, key: string) => {
282
+ if (!Array.isArray(obj[key])) {
283
+ obj[key] = [];
284
+ }
285
+ };
286
+
287
+ const ensureOneContainer = (obj: AnyObj, key: string) => {
288
+ if (!(key in obj)) {
289
+ obj[key] = null;
290
+ }
291
+ };
292
+
293
+ const manyIndexByPath = new Map<string, Map<any, AnyObj>>();
294
+
295
+ for (const row of rows) {
296
+ const baseRow = (row as any).base;
297
+ const baseId = (baseRow as any)[basePk];
298
+ if (baseId === undefined) {
299
+ continue;
300
+ }
301
+
302
+ const baseObj = (() => {
303
+ const existing = baseMap.get(baseId);
304
+ if (existing) {
305
+ return existing;
306
+ }
307
+ const created = { ...baseRow };
308
+ baseMap.set(baseId, created);
309
+ return created;
310
+ })();
311
+
312
+ // Walk nodes, attach to parent objects.
313
+ for (const n of nodes) {
314
+ const data = (row as any)[n.aliasKey];
315
+ const relPath = n.path.join(".");
316
+
317
+ // Resolve parent container
318
+ const parentPath = n.parent ? n.parent.path.join(".") : "";
319
+ let parentObj: AnyObj = baseObj;
320
+ if (parentPath) {
321
+ // parent might be many; we attach to the last inserted parent instance.
322
+ const parentIndex = manyIndexByPath.get(parentPath);
323
+ if (parentIndex && parentIndex.size) {
324
+ // pick last inserted (Map preserves insertion order)
325
+ parentObj = Array.from(parentIndex.values()).at(-1) as AnyObj;
326
+ } else {
327
+ const parentKey = n.parent?.key;
328
+ parentObj = parentKey
329
+ ? ((baseObj as any)[parentKey] ?? baseObj)
330
+ : baseObj;
331
+ }
332
+ }
333
+
334
+ if (isAllNullRow(data)) {
335
+ if (n.relationType === "one") {
336
+ ensureOneContainer(parentObj, n.key);
337
+ } else {
338
+ ensureManyContainer(parentObj, n.key);
339
+ }
340
+ continue;
341
+ }
342
+
343
+ const pk = (data as any)[n.pkField];
344
+ if (n.relationType === "one") {
345
+ parentObj[n.key] = { ...(data as any) };
346
+ } else {
347
+ ensureManyContainer(parentObj, n.key);
348
+ const indexKey = relPath;
349
+ if (!manyIndexByPath.has(indexKey)) {
350
+ manyIndexByPath.set(indexKey, new Map());
351
+ }
352
+ const idxMap = manyIndexByPath.get(indexKey)!;
353
+ if (!idxMap.has(pk)) {
354
+ const obj = { ...(data as any) };
355
+ idxMap.set(pk, obj);
356
+ (parentObj[n.key] as any[]).push(obj);
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ const out = Array.from(baseMap.values());
363
+ return limitOne ? out[0] : out;
279
364
  }