@atscript/db 0.1.146 → 0.1.148

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 (59) hide show
  1. package/dist/{agg-CV7y8nC6.d.cts → agg-42CSpGDR.d.cts} +2 -2
  2. package/dist/{agg-D5DHsAby.d.mts → agg-Wo-smrYV.d.mts} +3 -3
  3. package/dist/agg.cjs +1 -1
  4. package/dist/agg.d.cts +2 -2
  5. package/dist/agg.d.mts +2 -2
  6. package/dist/agg.mjs +1 -1
  7. package/dist/{aggregate-fns-CGBv3E8S.cjs → aggregate-fns-C-UJRobm.cjs} +52 -5
  8. package/dist/{aggregate-fns-CfsveE1w.mjs → aggregate-fns-CyaZyb9I.mjs} +35 -6
  9. package/dist/aggregate-rules-D_bsCpUI.cjs +26 -0
  10. package/dist/aggregate-rules-jdPrxqWa.mjs +15 -0
  11. package/dist/{buckets-DRycmhOW.d.mts → buckets-CNdTOnei.d.mts} +832 -33
  12. package/dist/{buckets-DYFu0eZ8.d.cts → buckets-DJiYlMXc.d.cts} +832 -33
  13. package/dist/{column-diff-D_Kyuh0S.cjs → column-diff-CUU4GvYg.cjs} +1546 -193
  14. package/dist/{column-diff-e2oHc71_.mjs → column-diff-Cp6ZoyRE.mjs} +1529 -200
  15. package/dist/{db-error-D5uilS_A.mjs → db-error-Az85UhTX.mjs} +32 -1
  16. package/dist/{db-error-DTkkeu5b.cjs → db-error-DRQH4sLY.cjs} +55 -0
  17. package/dist/{column-diff-Q9UmWn5x.d.mts → fk-diff-BQ4krij8.d.cts} +48 -2
  18. package/dist/{column-diff-w-Mym_3w.d.cts → fk-diff-DsaIijVX.d.mts} +48 -2
  19. package/dist/index.cjs +95 -35
  20. package/dist/index.d.cts +92 -22
  21. package/dist/index.d.mts +92 -22
  22. package/dist/index.mjs +65 -35
  23. package/dist/{nested-writer-xfQwxplL.cjs → nested-writer-BUQvk6QT.cjs} +734 -2
  24. package/dist/{nested-writer-CnOOAehr.mjs → nested-writer-CUBoq1ZO.mjs} +598 -4
  25. package/dist/numeric-operand-B1jKH7x5.mjs +21 -0
  26. package/dist/numeric-operand-DKfiRLYp.cjs +26 -0
  27. package/dist/ops.cjs +1 -1
  28. package/dist/ops.mjs +1 -1
  29. package/dist/plugin.cjs +293 -5
  30. package/dist/plugin.mjs +294 -6
  31. package/dist/rel.cjs +2 -2
  32. package/dist/rel.d.cts +2 -2
  33. package/dist/rel.d.mts +2 -2
  34. package/dist/rel.mjs +2 -2
  35. package/dist/{relation-helpers-B-0NRKat.d.mts → relation-helpers-Ba0v49sn.d.mts} +1 -1
  36. package/dist/{relation-helpers-BOMm_HUI.d.cts → relation-helpers-kX7jjgME.d.cts} +1 -1
  37. package/dist/relation-loader-ByY1Byrl.mjs +370 -0
  38. package/dist/relation-loader-D8OrdH-r.cjs +369 -0
  39. package/dist/shared.cjs +4 -1
  40. package/dist/shared.d.cts +16 -4
  41. package/dist/shared.d.mts +16 -4
  42. package/dist/shared.mjs +3 -2
  43. package/dist/sync.cjs +11 -7
  44. package/dist/sync.d.cts +2 -25
  45. package/dist/sync.d.mts +2 -25
  46. package/dist/sync.mjs +11 -7
  47. package/dist/{validation-utils-DOsB4e6G.cjs → validation-utils-Da2GjobR.cjs} +35 -24
  48. package/dist/{validation-utils-CMR4fe2M.mjs → validation-utils-Dq0uZ7ef.mjs} +35 -24
  49. package/dist/{validator-Clu2q_7z.mjs → validator-BTiIOTKP.mjs} +9 -3
  50. package/dist/{validator-CVS-onRg.cjs → validator-UcuJxHNT.cjs} +8 -2
  51. package/dist/{validator-Bw6ks9Hy.d.mts → validator-tBNvM1qc.d.cts} +31 -7
  52. package/dist/{validator-Bw6ks9Hy.d.cts → validator-tBNvM1qc.d.mts} +31 -7
  53. package/dist/validator.cjs +2 -2
  54. package/dist/validator.d.cts +1 -1
  55. package/dist/validator.d.mts +1 -1
  56. package/dist/validator.mjs +2 -2
  57. package/package.json +8 -8
  58. package/dist/relation-loader-CBPY6kM7.cjs +0 -461
  59. package/dist/relation-loader-D9XuXaMv.mjs +0 -462
@@ -0,0 +1,370 @@
1
+ import { B as slicePerGroup, F as compositeKey, L as findFKForRelation, N as relationStaticFilter, R as findRemoteFK, T as andFilters, z as resolveRelationTargetTable } from "./nested-writer-CUBoq1ZO.mjs";
2
+ //#region \0rolldown/runtime.js
3
+ var __defProp = Object.defineProperty;
4
+ var __exportAll = (all, no_symbols) => {
5
+ let target = {};
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
11
+ return target;
12
+ };
13
+ //#endregion
14
+ //#region src/rel/relation-loader.ts
15
+ var relation_loader_exports = /* @__PURE__ */ __exportAll({
16
+ ensureSelectIncludesFields: () => ensureSelectIncludesFields,
17
+ loadRelationsImpl: () => loadRelationsImpl
18
+ });
19
+ /**
20
+ * Loads related data for `$with` relations and attaches them to result rows.
21
+ */
22
+ async function loadRelationsImpl(rows, withRelations, host) {
23
+ if (rows.length === 0 || withRelations.length === 0) return;
24
+ if (host.adapter.supportsNativeRelations()) return host.adapter.loadRelations(rows, withRelations, host._meta.relations, host._meta.foreignKeys, host._tableResolver);
25
+ if (!host._tableResolver) return;
26
+ const tasks = [];
27
+ for (const withRel of withRelations) {
28
+ const relName = withRel.name;
29
+ if (relName.includes(".")) continue;
30
+ const relation = host._meta.relations.get(relName);
31
+ if (!relation) throw new Error(`Unknown relation "${relName}" in $with. Available relations: ${[...host._meta.relations.keys()].join(", ") || "(none)"}`);
32
+ const targetType = relation.targetType();
33
+ if (!targetType) continue;
34
+ const targetTable = host._tableResolver(targetType);
35
+ if (!targetTable) {
36
+ host.logger.warn(`Could not resolve table for relation "${relName}" — skipping`);
37
+ continue;
38
+ }
39
+ const statics = relationStaticFilter(relation, relName);
40
+ const merged = andFilters(statics.target, withRel.filter);
41
+ const filter = Object.keys(merged).length > 0 ? merged : void 0;
42
+ const flatRel = withRel;
43
+ const controls = { ...withRel.controls || {} };
44
+ if (flatRel.$sort && !controls.$sort) controls.$sort = flatRel.$sort;
45
+ if (flatRel.$limit !== null && flatRel.$limit !== void 0 && (controls.$limit === null || controls.$limit === void 0)) controls.$limit = flatRel.$limit;
46
+ if (flatRel.$skip !== null && flatRel.$skip !== void 0 && (controls.$skip === null || controls.$skip === void 0)) controls.$skip = flatRel.$skip;
47
+ if (flatRel.$select && !controls.$select) controls.$select = flatRel.$select;
48
+ if (flatRel.$with && !controls.$with) controls.$with = flatRel.$with;
49
+ const { $skip, $limit, ...pageless } = controls;
50
+ const skip = asCount($skip) || void 0;
51
+ const limit = asCount($limit);
52
+ const relQuery = {
53
+ filter,
54
+ controls: pageless,
55
+ page: skip === void 0 && limit === void 0 ? void 0 : {
56
+ skip,
57
+ limit
58
+ },
59
+ junctionFilter: statics.junction
60
+ };
61
+ if (relation.direction === "to") tasks.push(loadToRelation(rows, {
62
+ relName,
63
+ relation,
64
+ targetTable,
65
+ relQuery
66
+ }, host));
67
+ else if (relation.direction === "via") tasks.push(loadViaRelation(rows, {
68
+ relName,
69
+ relation,
70
+ targetTable,
71
+ relQuery
72
+ }, host));
73
+ else tasks.push(loadFromRelation(rows, {
74
+ relName,
75
+ relation,
76
+ targetTable,
77
+ relQuery
78
+ }, host));
79
+ }
80
+ await Promise.all(tasks);
81
+ }
82
+ /**
83
+ * Loads a `@db.rel.to` relation (FK is on this table). Single-valued: the
84
+ * relation's `$skip` / `$limit` page each row's (at most one) target row.
85
+ */
86
+ async function loadToRelation(rows, opts, host) {
87
+ const { relName, relation, targetTable, relQuery } = opts;
88
+ const fkEntry = findFKForRelation(relation, host._meta.foreignKeys);
89
+ if (!fkEntry) return;
90
+ const { localFields, targetFields } = fkEntry;
91
+ const index = indexFirst(await readRelatedByKeys(rows, {
92
+ localFields,
93
+ targetFields,
94
+ targetTable,
95
+ relQuery
96
+ }), targetFields);
97
+ for (const row of rows) row[relName] = index.get(compositeKey(localFields, row)) ?? null;
98
+ }
99
+ /**
100
+ * Loads a `@db.rel.from` relation (FK is on the target table). `$sort`,
101
+ * `$skip` and `$limit` apply to the related rows of each row.
102
+ */
103
+ async function loadFromRelation(rows, opts, host) {
104
+ const { relName, relation, targetTable, relQuery } = opts;
105
+ const remoteFK = findRemoteFK(targetTable, host.tableName, relation.alias);
106
+ if (!remoteFK) {
107
+ host.logger.warn(`Could not find FK on target table for relation "${relName}"`);
108
+ return;
109
+ }
110
+ const localFields = remoteFK.targetFields;
111
+ const remoteFields = remoteFK.fields;
112
+ const related = await readRelatedByKeys(rows, {
113
+ localFields,
114
+ targetFields: remoteFields,
115
+ targetTable,
116
+ relQuery
117
+ });
118
+ if (relation.isArray) {
119
+ const groups = groupBy(related, (item) => compositeKey(remoteFields, item));
120
+ for (const row of rows) row[relName] = groups.get(compositeKey(localFields, row)) ?? [];
121
+ } else {
122
+ const index = indexFirst(related, remoteFields);
123
+ for (const row of rows) row[relName] = index.get(compositeKey(localFields, row)) ?? null;
124
+ }
125
+ }
126
+ /**
127
+ * Loads a `@db.rel.via` relation (M:N through a junction table). The target
128
+ * rows are read once for all rows; `$sort` orders, and `$skip` / `$limit`
129
+ * page, the target rows of each row.
130
+ */
131
+ async function loadViaRelation(rows, opts, host) {
132
+ const { relName, relation, targetTable, relQuery } = opts;
133
+ if (!relation.viaType || !host._tableResolver) return;
134
+ const junctionType = relation.viaType();
135
+ if (!junctionType) return;
136
+ const junctionTable = host._tableResolver(junctionType);
137
+ if (!junctionTable) {
138
+ host.logger.warn(`Could not resolve junction table for via relation "${relName}"`);
139
+ return;
140
+ }
141
+ const fkToThis = findRemoteFK(junctionTable, host.tableName);
142
+ if (!fkToThis) {
143
+ host.logger.warn(`Could not find FK on junction table pointing to "${host.tableName}" for via relation "${relName}"`);
144
+ return;
145
+ }
146
+ const targetTableName = resolveRelationTargetTable(relation);
147
+ const fkToTarget = findRemoteFK(junctionTable, targetTableName);
148
+ if (!fkToTarget) {
149
+ host.logger.warn(`Could not find FK on junction table pointing to target "${targetTableName}" for via relation "${relName}"`);
150
+ return;
151
+ }
152
+ const localPKFields = fkToThis.targetFields;
153
+ const fields = {
154
+ junctionLocalFields: fkToThis.fields,
155
+ junctionTargetFields: fkToTarget.fields,
156
+ targetPKFields: fkToTarget.targetFields
157
+ };
158
+ const parentFilter = matchAnyFilter(rows, localPKFields, fields.junctionLocalFields);
159
+ const junctionRows = parentFilter ? await junctionTable.findMany({
160
+ filter: andFilters(parentFilter, relQuery.junctionFilter),
161
+ controls: { $select: [...fields.junctionLocalFields, ...fields.junctionTargetFields] }
162
+ }) : [];
163
+ const targetFilter = matchAnyFilter(junctionRows, fields.junctionTargetFields, fields.targetPKFields);
164
+ if (!targetFilter) {
165
+ for (const row of rows) row[relName] = relation.isArray ? [] : null;
166
+ return;
167
+ }
168
+ const sorted = hasSort(relQuery.controls);
169
+ let groups = /* @__PURE__ */ new Map();
170
+ const pick = (targets) => {
171
+ groups = groupViaTargets(targets, junctionRows, fields, sorted);
172
+ const page = relQuery.page;
173
+ if (!page) return targets;
174
+ const start = page.skip ?? 0;
175
+ const end = page.limit === void 0 ? void 0 : start + page.limit;
176
+ const kept = /* @__PURE__ */ new Set();
177
+ for (const [key, group] of groups) {
178
+ const slice = group.slice(start, end);
179
+ groups.set(key, slice);
180
+ for (const target of slice) kept.add(target);
181
+ }
182
+ return targets.filter((target) => kept.has(target));
183
+ };
184
+ await readRelated(targetTable, {
185
+ filter: relQuery.filter ? { $and: [targetFilter, relQuery.filter] } : targetFilter,
186
+ controls: ensureSelectIncludesFields(relQuery.controls, fields.targetPKFields)
187
+ }, { pick });
188
+ for (const row of rows) {
189
+ const group = groups.get(compositeKey(localPKFields, row));
190
+ row[relName] = relation.isArray ? group ?? [] : group?.[0] ?? null;
191
+ }
192
+ }
193
+ /**
194
+ * Each row's targets (keyed by the junction's local key), once per junction
195
+ * row. The junction rows only map rows to targets: with a `$sort` the targets
196
+ * keep the order the target read returned them in, else junction order.
197
+ */
198
+ function groupViaTargets(targets, junctionRows, fields, sorted) {
199
+ const groups = /* @__PURE__ */ new Map();
200
+ if (sorted) {
201
+ const linksByTarget = groupBy(junctionRows, (jRow) => compositeKey(fields.junctionTargetFields, jRow));
202
+ for (const target of targets) for (const jRow of linksByTarget.get(compositeKey(fields.targetPKFields, target)) ?? []) appendTo(groups, compositeKey(fields.junctionLocalFields, jRow), target);
203
+ } else {
204
+ const index = indexFirst(targets, fields.targetPKFields);
205
+ for (const jRow of junctionRows) {
206
+ const target = index.get(compositeKey(fields.junctionTargetFields, jRow));
207
+ if (target) appendTo(groups, compositeKey(fields.junctionLocalFields, jRow), target);
208
+ }
209
+ }
210
+ return groups;
211
+ }
212
+ /**
213
+ * Reads the target rows whose `targetFields` match `localFields` of any of
214
+ * `rows`, the relation's `$skip` / `$limit` applied per such row.
215
+ */
216
+ async function readRelatedByKeys(rows, opts) {
217
+ const { localFields, targetFields, targetTable, relQuery } = opts;
218
+ const keyFilter = matchAnyFilter(rows, localFields, targetFields);
219
+ if (!keyFilter) return [];
220
+ return readRelated(targetTable, {
221
+ filter: relQuery.filter ? { $and: [keyFilter, relQuery.filter] } : keyFilter,
222
+ controls: ensureSelectIncludesFields(relQuery.controls, targetFields)
223
+ }, {
224
+ partitionBy: targetFields,
225
+ page: relQuery.page
226
+ });
227
+ }
228
+ /**
229
+ * Reads related rows. With `partitionBy` and a `page`, the page applies per
230
+ * group of rows sharing the `partitionBy` values; `pick` keeps a subset of
231
+ * the rows before their own (nested) `$with` relations load.
232
+ */
233
+ async function readRelated(table, query, opts) {
234
+ const { partitionBy, page, pick } = opts;
235
+ const partitioned = partitionBy && page;
236
+ if (table._findManyForRelation) {
237
+ const controls = partitioned ? {
238
+ ...query.controls,
239
+ ...pageControls(page)
240
+ } : query.controls;
241
+ return table._findManyForRelation({
242
+ filter: query.filter,
243
+ controls
244
+ }, {
245
+ partitionBy: partitioned ? partitionBy : void 0,
246
+ pick
247
+ });
248
+ }
249
+ let rows = await table.findMany(query);
250
+ if (partitioned) rows = slicePerGroup(rows, (row) => compositeKey(partitionBy, row), page);
251
+ return pick ? pick(rows) : rows;
252
+ }
253
+ /** A `$skip` / `$limit` value as a number (`undefined` when unset or not a number). */
254
+ function asCount(value) {
255
+ if (value === null || value === void 0 || value === "") return;
256
+ const n = Number(value);
257
+ return Number.isFinite(n) ? n : void 0;
258
+ }
259
+ /** `$skip` / `$limit` controls of a page (only the ones set). */
260
+ function pageControls(page) {
261
+ const controls = {};
262
+ if (page.skip !== void 0) controls.$skip = page.skip;
263
+ if (page.limit !== void 0) controls.$limit = page.limit;
264
+ return controls;
265
+ }
266
+ /** Whether `controls` carry a non-empty `$sort`. */
267
+ function hasSort(controls) {
268
+ const sort = controls.$sort;
269
+ return !!sort && typeof sort === "object" && Object.keys(sort).length > 0;
270
+ }
271
+ /**
272
+ * A filter matching the rows whose `toFields` equal `fromFields` of any of
273
+ * `rows` — `$in` for a single field, `$or` of conditions for a composite key.
274
+ * `undefined` when no row has every `fromFields` value.
275
+ */
276
+ function matchAnyFilter(rows, fromFields, toFields) {
277
+ if (fromFields.length === 1) {
278
+ const values = /* @__PURE__ */ new Set();
279
+ for (const row of rows) {
280
+ const v = row[fromFields[0]];
281
+ if (v !== null && v !== void 0) values.add(v);
282
+ }
283
+ return values.size > 0 ? { [toFields[0]]: { $in: [...values] } } : void 0;
284
+ }
285
+ const seen = /* @__PURE__ */ new Set();
286
+ const conditions = [];
287
+ for (const row of rows) {
288
+ const key = compositeKey(fromFields, row);
289
+ if (seen.has(key)) continue;
290
+ seen.add(key);
291
+ const condition = {};
292
+ let valid = true;
293
+ for (let i = 0; i < fromFields.length; i++) {
294
+ const v = row[fromFields[i]];
295
+ if (v === null || v === void 0) {
296
+ valid = false;
297
+ break;
298
+ }
299
+ condition[toFields[i]] = v;
300
+ }
301
+ if (valid) conditions.push(condition);
302
+ }
303
+ if (conditions.length === 0) return;
304
+ return conditions.length === 1 ? conditions[0] : { $or: conditions };
305
+ }
306
+ /** The first of `items` per `fields` key. */
307
+ function indexFirst(items, fields) {
308
+ const index = /* @__PURE__ */ new Map();
309
+ for (const item of items) {
310
+ const key = compositeKey(fields, item);
311
+ if (!index.has(key)) index.set(key, item);
312
+ }
313
+ return index;
314
+ }
315
+ /** `items` grouped by `keyOf`, each group in the order of `items`. */
316
+ function groupBy(items, keyOf) {
317
+ const groups = /* @__PURE__ */ new Map();
318
+ for (const item of items) appendTo(groups, keyOf(item), item);
319
+ return groups;
320
+ }
321
+ function appendTo(groups, key, item) {
322
+ const group = groups.get(key);
323
+ if (group) group.push(item);
324
+ else groups.set(key, [item]);
325
+ }
326
+ /**
327
+ * Ensure the given join fields survive the user's $select on a $with relation
328
+ * read. Without this, the JS-side join would key off undefined and silently
329
+ * return empty relations.
330
+ *
331
+ * - Array $select: append missing FK fields.
332
+ * - Include-mode object $select (any value is 1/true, or empty object): set
333
+ * missing FK fields to 1.
334
+ * - Exclude-mode object $select: leave alone — FK survives by default. If the
335
+ * FK is explicitly excluded, throw rather than silently override user intent.
336
+ */
337
+ function ensureSelectIncludesFields(controls, fields) {
338
+ if (!controls) return controls;
339
+ const sel = controls.$select;
340
+ if (sel === void 0 || sel === null) return controls;
341
+ if (Array.isArray(sel)) {
342
+ const augmented = [...sel];
343
+ for (const f of fields) if (!augmented.includes(f)) augmented.push(f);
344
+ return {
345
+ ...controls,
346
+ $select: augmented
347
+ };
348
+ }
349
+ if (typeof sel === "object") {
350
+ const selObj = sel;
351
+ const isInclude = Object.keys(selObj).length === 0 || Object.values(selObj).some((v) => v === 1 || v === true);
352
+ const augmented = { ...selObj };
353
+ let mutated = false;
354
+ for (const f of fields) {
355
+ const v = augmented[f];
356
+ if (v === 0 || v === false) throw new Error(`Cannot exclude join column "${f}" from $select on $with relation`);
357
+ if (v === void 0 && isInclude) {
358
+ augmented[f] = 1;
359
+ mutated = true;
360
+ }
361
+ }
362
+ return mutated ? {
363
+ ...controls,
364
+ $select: augmented
365
+ } : controls;
366
+ }
367
+ return controls;
368
+ }
369
+ //#endregion
370
+ export { relation_loader_exports as n, loadRelationsImpl as t };