@atscript/db 0.1.150 → 0.1.152
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/agg.d.cts +1 -1
- package/dist/agg.d.mts +1 -1
- package/dist/{buckets-XOz0Bw5x.d.mts → buckets-Cb0zmhDc.d.mts} +38 -1
- package/dist/{buckets-24Ac4sIS.d.cts → buckets-tzd8iIM2.d.cts} +38 -1
- package/dist/{column-diff-CKKBVDvx.cjs → column-diff-CU7KHj9-.cjs} +270 -79
- package/dist/{column-diff-CKLDWBbo.mjs → column-diff-CnyLjbQ8.mjs} +270 -79
- package/dist/{fk-diff-CCLiJ9sJ.d.cts → fk-diff-Bw26lwSc.d.cts} +8 -1
- package/dist/{fk-diff-R8eU7gq-.d.mts → fk-diff-lcAnwvm9.d.mts} +8 -1
- package/dist/index.cjs +24 -2
- package/dist/index.d.cts +15 -16
- package/dist/index.d.mts +15 -16
- package/dist/index.mjs +24 -2
- package/dist/{nested-writer-BUQvk6QT.cjs → nested-writer-BzbOPSUp.cjs} +129 -7
- package/dist/{nested-writer-CUBoq1ZO.mjs → nested-writer-cvbVAkNo.mjs} +124 -8
- package/dist/rel.cjs +2 -2
- package/dist/rel.d.cts +2 -2
- package/dist/rel.d.mts +2 -2
- package/dist/rel.mjs +2 -2
- package/dist/{relation-helpers-DjVJN2ng.d.cts → relation-helpers-Bdo4NHPC.d.mts} +1 -1
- package/dist/{relation-helpers-H7UEVkHs.d.mts → relation-helpers-DxIZKvnJ.d.cts} +1 -1
- package/dist/{relation-loader-ByY1Byrl.mjs → relation-loader-DYab07lw.mjs} +1 -1
- package/dist/{relation-loader-D8OrdH-r.cjs → relation-loader-fNjHgBMs.cjs} +1 -1
- package/dist/sync.cjs +1 -1
- package/dist/sync.d.cts +2 -2
- package/dist/sync.d.mts +2 -2
- package/dist/sync.mjs +1 -1
- package/package.json +8 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as aggregateExpressionsNotSupported, l as spaceClosedError, n as CasMismatchError, r as DbError } from "./db-error-Az85UhTX.mjs";
|
|
2
2
|
import { a as SOURCE_VALUE_FNS, c as isFirstLast, o as SUPPORTED_AGGREGATE_FNS, r as BASE_AGGREGATE_FNS, t as AGG_ANNOTATIONS } from "./aggregate-fns-CyaZyb9I.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { A as hasRelationOp, D as containsRelationFilter, F as resolveRelationFilterTree, H as tableNameOf, I as compositeKey, J as viewExprNullable, L as findFKEntryForRelation, M as noteRelationFilter, N as relGuardState, O as createRelationFilterHost, R as findFKForRelation, S as uniqueKeyTuple, U as computeOf, V as slicePerGroup, W as computedOperands, Y as walkViewExpr, _ as remapDeleteFkViolation, a as batchPatchNestedFrom, b as rowMatchesKey, c as batchReplaceNestedFrom, d as checkDepthOverflow, f as planNestedFromVia, g as enrichFkViolation, h as validateBatch, i as batchInsertNestedVia, l as batchReplaceNestedTo, m as preValidateNestedFrom, n as batchInsertNestedFrom, p as planPatchNestedTo, r as batchInsertNestedTo, s as batchPatchNestedVia, t as applyPatchNestedTo, u as batchReplaceNestedVia, v as findRowsByKeys, x as sameKey, y as pkTupleKey, z as findRemoteFK } from "./nested-writer-cvbVAkNo.mjs";
|
|
4
4
|
import { a as DERIVED_INCOMPATIBLE, n as searchMemberKind, r as numericTypeProblem, s as isJsonLeafType } from "./search-fields-DBIZKwyb.mjs";
|
|
5
5
|
import { a as isPlainObject, i as isEmptyObject, n as findAncestorInSet, o as selfOrAncestor, r as getPath, t as deletePath } from "./object-TkiJQ-Dp.mjs";
|
|
6
6
|
import { resolveAlias as resolveAlias$1 } from "./agg.mjs";
|
|
@@ -2520,23 +2520,37 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2520
2520
|
this.reverseColumnRenames(row, meta);
|
|
2521
2521
|
return row;
|
|
2522
2522
|
}
|
|
2523
|
+
const plan = readPlanFor(meta);
|
|
2524
|
+
const cols = plan.columns;
|
|
2523
2525
|
const result = {};
|
|
2524
|
-
const fromFmts = meta.fromStorageFormatters;
|
|
2525
2526
|
for (const physical of Object.keys(row)) {
|
|
2526
|
-
const
|
|
2527
|
-
if (
|
|
2527
|
+
const col = cols.get(physical);
|
|
2528
|
+
if (col === void 0) {
|
|
2528
2529
|
result[physical] = row[physical];
|
|
2529
2530
|
continue;
|
|
2530
2531
|
}
|
|
2531
|
-
let
|
|
2532
|
-
|
|
2533
|
-
if (
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2532
|
+
let value = row[physical];
|
|
2533
|
+
if (col.fromFmt !== void 0 && value !== null && value !== void 0) value = col.fromFmt(value);
|
|
2534
|
+
if (col.coerce !== void 0) value = col.coerce(value);
|
|
2535
|
+
if (col.json && typeof value === "string") value = JSON.parse(value);
|
|
2536
|
+
const parents = col.parents;
|
|
2537
|
+
if (parents === void 0) {
|
|
2538
|
+
result[col.path] = value;
|
|
2539
|
+
continue;
|
|
2540
|
+
}
|
|
2541
|
+
let current = result;
|
|
2542
|
+
for (let i = 0; i < parents.length; i++) {
|
|
2543
|
+
const part = parents[i];
|
|
2544
|
+
const next = current[part];
|
|
2545
|
+
if (next === void 0 || next === null) {
|
|
2546
|
+
const created = {};
|
|
2547
|
+
current[part] = created;
|
|
2548
|
+
current = created;
|
|
2549
|
+
} else current = next;
|
|
2550
|
+
}
|
|
2551
|
+
current[col.last] = value;
|
|
2552
|
+
}
|
|
2553
|
+
for (const parent of plan.parents) collapseNullParent(result, parent);
|
|
2540
2554
|
return result;
|
|
2541
2555
|
}
|
|
2542
2556
|
translateQuery(query, meta) {
|
|
@@ -2595,17 +2609,16 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2595
2609
|
return result;
|
|
2596
2610
|
}
|
|
2597
2611
|
prepareForWrite(payload, meta, adapter) {
|
|
2612
|
+
if (meta.requiresMappings && !meta.onlyColumnRenames) return this.formatWriteValues(this.flattenWritePayload(payload, meta, adapter), meta);
|
|
2598
2613
|
const data = { ...payload };
|
|
2599
2614
|
this.prepareCommon(data, meta, adapter);
|
|
2600
|
-
if (!meta.requiresMappings) return this.formatWriteValues(data, meta);
|
|
2601
2615
|
if (meta.onlyColumnRenames) {
|
|
2602
2616
|
for (const [logical, physical] of meta.columnMap.entries()) if (logical in data) {
|
|
2603
2617
|
data[physical] = data[logical];
|
|
2604
2618
|
delete data[logical];
|
|
2605
2619
|
}
|
|
2606
|
-
return this.formatWriteValues(data, meta);
|
|
2607
2620
|
}
|
|
2608
|
-
return this.formatWriteValues(
|
|
2621
|
+
return this.formatWriteValues(data, meta);
|
|
2609
2622
|
}
|
|
2610
2623
|
translatePatchKeys(update, meta) {
|
|
2611
2624
|
if (!meta.requiresMappings && !meta.toStorageFormatters) return update;
|
|
@@ -2680,41 +2693,160 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2680
2693
|
return result;
|
|
2681
2694
|
}
|
|
2682
2695
|
/**
|
|
2683
|
-
* Flattens nested
|
|
2684
|
-
*
|
|
2696
|
+
* Flattens nested objects into __-separated keys and JSON-stringifies
|
|
2697
|
+
* @db.json / array fields — with `prepareCommon` folded in: primary-key
|
|
2698
|
+
* values prepared (`adapter.prepareId`), top-level ignored fields (skipped
|
|
2699
|
+
* by {@link writeFlattenedField}) and derived fields left out. `payload`
|
|
2700
|
+
* itself is not modified.
|
|
2685
2701
|
*/
|
|
2686
|
-
|
|
2702
|
+
flattenWritePayload(payload, meta, adapter) {
|
|
2687
2703
|
const result = {};
|
|
2688
|
-
|
|
2704
|
+
const primaryKeys = meta.primaryKeys;
|
|
2705
|
+
const root = writePlanFor(meta);
|
|
2706
|
+
for (const key of Object.keys(payload)) {
|
|
2707
|
+
if (meta.derivedFields.has(key)) continue;
|
|
2708
|
+
let value = payload[key];
|
|
2709
|
+
if (value !== void 0 && primaryKeys.includes(key)) {
|
|
2710
|
+
const fieldType = meta.flatMap?.get(key);
|
|
2711
|
+
if (fieldType) value = adapter.prepareId(value, fieldType);
|
|
2712
|
+
}
|
|
2713
|
+
writeFlattenedField(root, "", key, value, result, meta);
|
|
2714
|
+
}
|
|
2689
2715
|
return result;
|
|
2690
2716
|
}
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2717
|
+
};
|
|
2718
|
+
/**
|
|
2719
|
+
* Per-table read plan of {@link RelationalFieldMapper.reconstructFromRead}:
|
|
2720
|
+
* the per-column decisions (coercion, JSON, nesting — the dot-path pre-split)
|
|
2721
|
+
* and the parent-collapse list, compiled once per built metadata instead of
|
|
2722
|
+
* per row. Metadata is immutable after `build()`, which precedes any read.
|
|
2723
|
+
*/
|
|
2724
|
+
const readPlans = /* @__PURE__ */ new WeakMap();
|
|
2725
|
+
function readPlanFor(meta) {
|
|
2726
|
+
let plan = readPlans.get(meta);
|
|
2727
|
+
if (plan === void 0) {
|
|
2728
|
+
plan = compileReadPlan(meta);
|
|
2729
|
+
readPlans.set(meta, plan);
|
|
2730
|
+
}
|
|
2731
|
+
return plan;
|
|
2732
|
+
}
|
|
2733
|
+
function compileReadPlan(meta) {
|
|
2734
|
+
const fromFmts = meta.fromStorageFormatters;
|
|
2735
|
+
const columns = /* @__PURE__ */ new Map();
|
|
2736
|
+
for (const [physical, fd] of meta.leafByPhysical) {
|
|
2737
|
+
const segs = (fd.storage === "json" || fd.storage === "flattened") && fd.path.includes(".") ? fd.path.split(".") : void 0;
|
|
2738
|
+
columns.set(physical, {
|
|
2739
|
+
path: fd.path,
|
|
2740
|
+
parents: segs?.slice(0, -1),
|
|
2741
|
+
last: segs ? segs[segs.length - 1] : fd.path,
|
|
2742
|
+
json: fd.storage === "json",
|
|
2743
|
+
coerce: fd.designType === "boolean" ? toBool : fd.designType === "decimal" ? toDecimalString : void 0,
|
|
2744
|
+
fromFmt: fromFmts?.get(physical)
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
const parents = [];
|
|
2748
|
+
for (const parentPath of meta.flattenedParents) {
|
|
2749
|
+
const segs = parentPath.split(".");
|
|
2750
|
+
parents.push({
|
|
2751
|
+
ancestors: segs.slice(0, -1),
|
|
2752
|
+
last: segs[segs.length - 1],
|
|
2753
|
+
optional: !!meta.flatMap?.get(parentPath)?.optional
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
return {
|
|
2757
|
+
columns,
|
|
2758
|
+
parents
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
/** If every child of a flattened parent is null / undefined, collapse it (`null` when optional, else `{}`). */
|
|
2762
|
+
function collapseNullParent(obj, parent) {
|
|
2763
|
+
let current = obj;
|
|
2764
|
+
const ancestors = parent.ancestors;
|
|
2765
|
+
for (let i = 0; i < ancestors.length; i++) {
|
|
2766
|
+
const next = current[ancestors[i]];
|
|
2767
|
+
if (next === void 0 || next === null) return;
|
|
2768
|
+
current = next;
|
|
2769
|
+
}
|
|
2770
|
+
const parentObj = current[parent.last];
|
|
2771
|
+
if (typeof parentObj !== "object" || parentObj === null) return;
|
|
2772
|
+
for (const k of Object.keys(parentObj)) {
|
|
2773
|
+
const v = parentObj[k];
|
|
2774
|
+
if (v !== null && v !== void 0) return;
|
|
2775
|
+
}
|
|
2776
|
+
current[parent.last] = parent.optional ? null : {};
|
|
2777
|
+
}
|
|
2778
|
+
/**
|
|
2779
|
+
* Per-table write plan of the relational flatten: each logical path's
|
|
2780
|
+
* classification (ignored / flattened parent / json / plain column) and
|
|
2781
|
+
* physical column, resolved once per path instead of per row (no per-key
|
|
2782
|
+
* path concatenation and map lookups). Only schema paths are memoised — a
|
|
2783
|
+
* key the metadata does not know is resolved per call, so arbitrary payload
|
|
2784
|
+
* keys cannot grow the plan.
|
|
2785
|
+
*/
|
|
2786
|
+
const writePlans = /* @__PURE__ */ new WeakMap();
|
|
2787
|
+
function writePlanFor(meta) {
|
|
2788
|
+
let root = writePlans.get(meta);
|
|
2789
|
+
if (root === void 0) {
|
|
2790
|
+
root = /* @__PURE__ */ new Map();
|
|
2791
|
+
writePlans.set(meta, root);
|
|
2792
|
+
}
|
|
2793
|
+
return root;
|
|
2794
|
+
}
|
|
2795
|
+
function resolveWriteNode(path, meta) {
|
|
2796
|
+
if (meta.ignoredFields.has(path)) return {
|
|
2797
|
+
node: {
|
|
2798
|
+
kind: "ignored",
|
|
2799
|
+
path,
|
|
2800
|
+
physical: ""
|
|
2801
|
+
},
|
|
2802
|
+
known: true
|
|
2803
|
+
};
|
|
2804
|
+
if (meta.flattenedParents.has(path)) return {
|
|
2805
|
+
node: {
|
|
2806
|
+
kind: "parent",
|
|
2807
|
+
path,
|
|
2808
|
+
physical: "",
|
|
2809
|
+
children: /* @__PURE__ */ new Map(),
|
|
2810
|
+
nullChildren: meta.childrenByParent.get(path) ?? []
|
|
2811
|
+
},
|
|
2812
|
+
known: true
|
|
2813
|
+
};
|
|
2814
|
+
const fd = meta.leafByLogical.get(path);
|
|
2815
|
+
return {
|
|
2816
|
+
node: {
|
|
2817
|
+
kind: fd?.storage === "json" ? "json" : "leaf",
|
|
2818
|
+
path,
|
|
2819
|
+
physical: fd?.physicalName ?? path.replace(/\./g, "__")
|
|
2820
|
+
},
|
|
2821
|
+
known: fd !== void 0
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
/**
|
|
2825
|
+
* Classifies and writes a single field to the result object — recursing into
|
|
2826
|
+
* nested objects that are flattened; JSON-stringifies @db.json / array fields.
|
|
2827
|
+
*/
|
|
2828
|
+
function writeFlattenedField(level, prefix, key, value, result, meta) {
|
|
2829
|
+
let node = level.get(key);
|
|
2830
|
+
if (node === void 0) {
|
|
2831
|
+
const resolved = resolveWriteNode(prefix ? `${prefix}.${key}` : key, meta);
|
|
2832
|
+
node = resolved.node;
|
|
2833
|
+
if (resolved.known) level.set(key, node);
|
|
2834
|
+
}
|
|
2835
|
+
switch (node.kind) {
|
|
2836
|
+
case "ignored": return;
|
|
2837
|
+
case "parent":
|
|
2838
|
+
if (value === null || value === void 0) for (const physical of node.nullChildren) result[physical] = null;
|
|
2699
2839
|
else if (typeof value === "object" && !Array.isArray(value)) {
|
|
2700
2840
|
const obj = value;
|
|
2701
|
-
for (const
|
|
2841
|
+
for (const childKey of Object.keys(obj)) writeFlattenedField(node.children, node.path, childKey, obj[childKey], result, meta);
|
|
2702
2842
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
/**
|
|
2711
|
-
* When a parent object is null/undefined, set all its flattened children to null.
|
|
2712
|
-
*/
|
|
2713
|
-
setFlattenedChildrenNull(parentPath, result, meta) {
|
|
2714
|
-
const children = meta.childrenByParent.get(parentPath);
|
|
2715
|
-
if (children) for (const physical of children) result[physical] = null;
|
|
2843
|
+
return;
|
|
2844
|
+
case "json":
|
|
2845
|
+
result[node.physical] = value !== void 0 && value !== null ? JSON.stringify(value) : value;
|
|
2846
|
+
return;
|
|
2847
|
+
default: result[node.physical] = value;
|
|
2716
2848
|
}
|
|
2717
|
-
}
|
|
2849
|
+
}
|
|
2718
2850
|
//#endregion
|
|
2719
2851
|
//#region src/query/query-guards.ts
|
|
2720
2852
|
/**
|
|
@@ -3712,6 +3844,11 @@ var AtscriptDbReadable = class {
|
|
|
3712
3844
|
*/
|
|
3713
3845
|
get uniqueKeySets() {
|
|
3714
3846
|
this._ensureBuilt();
|
|
3847
|
+
this._uniqueKeySets ??= this._computeUniqueKeySets();
|
|
3848
|
+
return this._uniqueKeySets;
|
|
3849
|
+
}
|
|
3850
|
+
_uniqueKeySets;
|
|
3851
|
+
_computeUniqueKeySets() {
|
|
3715
3852
|
const sets = [];
|
|
3716
3853
|
if (this._meta.primaryKeys.length > 0) sets.push(this._meta.primaryKeys.map((f) => this._meta.physicalPath(f)));
|
|
3717
3854
|
for (const index of this._meta.indexes.values()) if (index.type === "unique") sets.push(index.fields.map((f) => f.name));
|
|
@@ -3719,7 +3856,8 @@ var AtscriptDbReadable = class {
|
|
|
3719
3856
|
const column = this._meta.physicalPath(prop);
|
|
3720
3857
|
if (!sets.some((s) => s.length === 1 && s[0] === column)) sets.push([column]);
|
|
3721
3858
|
}
|
|
3722
|
-
|
|
3859
|
+
for (const set of sets) Object.freeze(set);
|
|
3860
|
+
return Object.freeze(sets);
|
|
3723
3861
|
}
|
|
3724
3862
|
/** Preferred row identifier field names. Defaults to primary keys. */
|
|
3725
3863
|
get preferredId() {
|
|
@@ -4693,7 +4831,7 @@ var AtscriptDbReadable = class {
|
|
|
4693
4831
|
* Public entry point for relation loading. Used by adapters for nested $with delegation.
|
|
4694
4832
|
*/
|
|
4695
4833
|
async loadRelations(rows, withRelations) {
|
|
4696
|
-
const { loadRelationsImpl } = await import("./relation-loader-
|
|
4834
|
+
const { loadRelationsImpl } = await import("./relation-loader-DYab07lw.mjs").then((n) => n.n);
|
|
4697
4835
|
return loadRelationsImpl(rows, withRelations, this);
|
|
4698
4836
|
}
|
|
4699
4837
|
/**
|
|
@@ -5721,8 +5859,14 @@ var ApplicationIntegrity = class extends IntegrityStrategy {
|
|
|
5721
5859
|
}
|
|
5722
5860
|
return pin;
|
|
5723
5861
|
}
|
|
5724
|
-
|
|
5725
|
-
|
|
5862
|
+
/**
|
|
5863
|
+
* A resolver is set and — when `tableName` is given (since 0.1.151) — some
|
|
5864
|
+
* table references `tableName` with an `onDelete` action: a delete from a
|
|
5865
|
+
* table nothing references needs no cascade pass (nor its transaction).
|
|
5866
|
+
*/
|
|
5867
|
+
needsCascade(cascadeResolver, tableName) {
|
|
5868
|
+
if (!cascadeResolver) return false;
|
|
5869
|
+
return tableName === void 0 || cascadeResolver(tableName).length > 0;
|
|
5726
5870
|
}
|
|
5727
5871
|
recordKey(tableName, pkFields, record) {
|
|
5728
5872
|
let key = tableName;
|
|
@@ -5918,10 +6062,6 @@ function matchByKeys(a, b, keys) {
|
|
|
5918
6062
|
for (const key of keys) if (a[key] !== b[key]) return false;
|
|
5919
6063
|
return true;
|
|
5920
6064
|
}
|
|
5921
|
-
//#endregion
|
|
5922
|
-
//#region src/patch/patch-decomposer.ts
|
|
5923
|
-
/** Metadata tag the flattener puts on top-level array fields (shared with `isVersionExemptPatch`). */
|
|
5924
|
-
const TOP_LEVEL_ARRAY_TAG = "db.__topLevelArray";
|
|
5925
6065
|
/**
|
|
5926
6066
|
* Decomposes a patch payload into a flat update object for adapters
|
|
5927
6067
|
* that don't support native patch operations.
|
|
@@ -5940,21 +6080,25 @@ const TOP_LEVEL_ARRAY_TAG = "db.__topLevelArray";
|
|
|
5940
6080
|
*/
|
|
5941
6081
|
function decomposePatch(payload, table) {
|
|
5942
6082
|
const update = {};
|
|
5943
|
-
flattenPatchPayload(payload, "", update, table,
|
|
6083
|
+
flattenPatchPayload(payload, "", update, table.getMetadata(), table);
|
|
5944
6084
|
return update;
|
|
5945
6085
|
}
|
|
5946
|
-
function flattenPatchPayload(payload, prefix, update,
|
|
5947
|
-
const
|
|
5948
|
-
|
|
6086
|
+
function flattenPatchPayload(payload, prefix, update, meta, table) {
|
|
6087
|
+
const primaryKeys = meta.primaryKeys;
|
|
6088
|
+
const flatMap = meta.flatMap;
|
|
6089
|
+
for (const _key of Object.keys(payload)) {
|
|
6090
|
+
const value = payload[_key];
|
|
5949
6091
|
const key = prefix ? `${prefix}.${_key}` : _key;
|
|
5950
|
-
if (
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
6092
|
+
if (primaryKeys.includes(key)) continue;
|
|
6093
|
+
if (!(typeof value === "object" && value !== null && !Array.isArray(value))) {
|
|
6094
|
+
update[key] = value;
|
|
6095
|
+
continue;
|
|
6096
|
+
}
|
|
6097
|
+
const flatType = flatMap.get(key);
|
|
6098
|
+
if (flatType?.metadata?.get("db.__topLevelArray") && !flatType?.metadata?.has("db.json")) decomposeArrayPatch(key, value, flatType, update, table);
|
|
6099
|
+
else if (flatType?.metadata?.get("db.patch.strategy") === "merge") flattenPatchPayload(value, key, update, meta, table);
|
|
6100
|
+
else if (meta.flattenedParents.has(key)) {
|
|
6101
|
+
flattenPatchPayload(value, key, update, meta, table);
|
|
5958
6102
|
const optLeaves = meta.optionalLeavesByLogicalParent.get(key);
|
|
5959
6103
|
if (optLeaves) {
|
|
5960
6104
|
for (const lp of optLeaves) if (!(lp in update)) update[lp] = null;
|
|
@@ -6037,16 +6181,18 @@ function assertNoVersionWrites(data, versionColumn) {
|
|
|
6037
6181
|
function isVersionExemptPatch(data, table) {
|
|
6038
6182
|
const meta = table.getMetadata();
|
|
6039
6183
|
if (meta.versionExemptPaths.size === 0) return false;
|
|
6184
|
+
const primaryKeys = meta.primaryKeys;
|
|
6185
|
+
const flatMap = meta.flatMap;
|
|
6040
6186
|
const walk = (obj, prefix) => {
|
|
6041
6187
|
let result = "none";
|
|
6042
6188
|
for (const [k, value] of Object.entries(obj)) {
|
|
6043
6189
|
const key = prefix ? `${prefix}.${k}` : k;
|
|
6044
|
-
if (
|
|
6190
|
+
if (primaryKeys.includes(key)) continue;
|
|
6045
6191
|
if (meta.isVersionExemptPath(key)) {
|
|
6046
6192
|
result = "exempt";
|
|
6047
6193
|
continue;
|
|
6048
6194
|
}
|
|
6049
|
-
const flatType =
|
|
6195
|
+
const flatType = flatMap.get(key);
|
|
6050
6196
|
if (typeof value === "object" && value !== null && !Array.isArray(value) && !isDbFieldOp(value) && !flatType?.metadata?.get("db.__topLevelArray") && !flatType?.metadata?.has("db.json") && flatType?.metadata?.get("db.patch.strategy") === "merge") {
|
|
6051
6197
|
const inner = walk(value, key);
|
|
6052
6198
|
if (inner === "versioned") return "versioned";
|
|
@@ -6211,8 +6357,7 @@ var WriteGuardContext = class {
|
|
|
6211
6357
|
controls: {}
|
|
6212
6358
|
});
|
|
6213
6359
|
const used = /* @__PURE__ */ new Set();
|
|
6214
|
-
const out = filters.map((
|
|
6215
|
-
const row = rows.find((r) => rowMatchesKey(r, filter));
|
|
6360
|
+
const out = findRowsByKeys(rows, filters).map((row) => {
|
|
6216
6361
|
if (row) used.add(row);
|
|
6217
6362
|
return row ?? null;
|
|
6218
6363
|
});
|
|
@@ -6346,7 +6491,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6346
6491
|
const depth = _depth ?? 0;
|
|
6347
6492
|
const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
|
|
6348
6493
|
if (!canNest && this._meta.navFields.size > 0) checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
6349
|
-
|
|
6494
|
+
const singleStatement = !ignore && this._isSingleStatementWrite(payloads, depth, guard, check);
|
|
6495
|
+
return enrichFkViolation(this._meta, () => this._inWriteTransaction(singleStatement, async () => {
|
|
6350
6496
|
const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
|
|
6351
6497
|
let items = payloads.map((p) => this._applyDefaults(clone(p)));
|
|
6352
6498
|
if (ignore) this._linkToByKeyInIgnoreMode(items);
|
|
@@ -6419,7 +6565,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6419
6565
|
const depth = _depth ?? 0;
|
|
6420
6566
|
const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
|
|
6421
6567
|
if (!canNest && this._meta.navFields.size > 0) checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
6422
|
-
|
|
6568
|
+
const singleStatement = this._isSingleStatementWrite(payloads, depth, guard, check);
|
|
6569
|
+
return enrichFkViolation(this._meta, () => this._inWriteTransaction(singleStatement, async () => {
|
|
6423
6570
|
const versionColumn = this.versionColumn;
|
|
6424
6571
|
const expectedVersions = Array.from({ length: payloads.length });
|
|
6425
6572
|
const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
|
|
@@ -6505,7 +6652,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6505
6652
|
const depth = _depth ?? 0;
|
|
6506
6653
|
const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
|
|
6507
6654
|
if (!canNest && this._meta.navFields.size > 0) checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
6508
|
-
|
|
6655
|
+
const singleStatement = this._isSingleStatementWrite(payloads, depth, guard, check);
|
|
6656
|
+
return enrichFkViolation(this._meta, () => this._inWriteTransaction(singleStatement, async () => {
|
|
6509
6657
|
const versionColumn = this.versionColumn;
|
|
6510
6658
|
const expectedVersions = Array.from({ length: payloads.length });
|
|
6511
6659
|
const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
|
|
@@ -6569,13 +6717,14 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6569
6717
|
const ops = separateFieldOps(update);
|
|
6570
6718
|
const translatedOps = ops ? this._fieldMapper.translateOpsKeys(ops, this._meta) : void 0;
|
|
6571
6719
|
const translatedUpdate = this._fieldMapper.translatePatchKeys(update, this._meta);
|
|
6572
|
-
if (getArrayOpsFields(translatedUpdate).size > 0) {
|
|
6720
|
+
if (getArrayOpsFields(translatedUpdate).size > 0) result = await this.adapter.withTransaction(async () => {
|
|
6573
6721
|
const resolved = resolveArrayOps(translatedUpdate, await this.adapter.findOne({
|
|
6574
6722
|
filter: translatedFilter,
|
|
6575
6723
|
controls: {}
|
|
6576
6724
|
}), this);
|
|
6577
|
-
|
|
6578
|
-
}
|
|
6725
|
+
return this.adapter.updateOne(translatedFilter, resolved, translatedOps, expectedVersion, ...updateOpts);
|
|
6726
|
+
});
|
|
6727
|
+
else result = await this.adapter.updateOne(translatedFilter, translatedUpdate, translatedOps, expectedVersion, ...updateOpts);
|
|
6579
6728
|
}
|
|
6580
6729
|
modifiedCount += result.modifiedCount;
|
|
6581
6730
|
matched[i] = result.matchedCount > 0;
|
|
@@ -6696,7 +6845,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6696
6845
|
const candidates = this._idCandidates(id, opts);
|
|
6697
6846
|
if (candidates.length === 0) return { deletedCount: 0 };
|
|
6698
6847
|
const guard = opts?.guard;
|
|
6699
|
-
const needsCascade = this._integrity.needsCascade(this._cascadeResolver);
|
|
6848
|
+
const needsCascade = this._integrity.needsCascade(this._cascadeResolver, this.tableName);
|
|
6700
6849
|
const run = async () => {
|
|
6701
6850
|
const pinned = await this._pinIdCandidates(candidates, opts?.scope);
|
|
6702
6851
|
const filter = this._andScope(pinned, opts?.scope);
|
|
@@ -6749,7 +6898,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6749
6898
|
async deleteMany(filter) {
|
|
6750
6899
|
this._ensureBuilt();
|
|
6751
6900
|
this._guardMutationFilter(filter);
|
|
6752
|
-
if (this._integrity.needsCascade(this._cascadeResolver)) return remapDeleteFkViolation(this.tableName, () => this.adapter.withTransaction(async () => {
|
|
6901
|
+
if (this._integrity.needsCascade(this._cascadeResolver, this.tableName)) return remapDeleteFkViolation(this.tableName, () => this.adapter.withTransaction(async () => {
|
|
6753
6902
|
const pin = await this._integrity.cascadeBeforeDelete(filter, this.tableName, this._meta, this._cascadeResolver, (f) => this._fieldMapper.translateFilter(f, this._meta), this.adapter);
|
|
6754
6903
|
if (pin) {
|
|
6755
6904
|
let deletedCount = 0;
|
|
@@ -6837,6 +6986,33 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6837
6986
|
};
|
|
6838
6987
|
});
|
|
6839
6988
|
}
|
|
6989
|
+
/**
|
|
6990
|
+
* Whether a `*One` / `bulk*` write of `payloads` is a single statement that
|
|
6991
|
+
* needs no wrapping transaction (since 0.1.151): one root-level row, no
|
|
6992
|
+
* `guard` / `check` (their reads and the write must share one snapshot),
|
|
6993
|
+
* no nested navigation data (multi-table phases), and FK integrity enforced
|
|
6994
|
+
* by the database (application-level FK checks read before writing). The
|
|
6995
|
+
* write then calls the adapter outside any transaction of its own — one
|
|
6996
|
+
* `insertMany([row])` / `replaceOne` / `updateOne` / `nativePatch`, or a
|
|
6997
|
+
* read-only `count` for an empty patch; the array-operator read-modify-write
|
|
6998
|
+
* still opens a transaction for its pair. Adapters may rely on it: a
|
|
6999
|
+
* single-row `insertMany` reached this way runs outside a transaction.
|
|
7000
|
+
*/
|
|
7001
|
+
_isSingleStatementWrite(payloads, depth, guard, check) {
|
|
7002
|
+
if (payloads.length !== 1 || depth !== 0 || guard || check) return false;
|
|
7003
|
+
if (this._meta.foreignKeys.size > 0 && !this.adapter.supportsNativeForeignKeys()) return false;
|
|
7004
|
+
const payload = payloads[0];
|
|
7005
|
+
if (!payload || typeof payload !== "object") return false;
|
|
7006
|
+
for (const navField of this._meta.navFields) {
|
|
7007
|
+
const value = payload[navField];
|
|
7008
|
+
if (value !== void 0 && value !== null) return false;
|
|
7009
|
+
}
|
|
7010
|
+
return true;
|
|
7011
|
+
}
|
|
7012
|
+
/** Runs a write body inside the adapter transaction — or directly for a single-statement write. */
|
|
7013
|
+
_inWriteTransaction(singleStatement, fn) {
|
|
7014
|
+
return singleStatement ? fn() : this.adapter.withTransaction(fn);
|
|
7015
|
+
}
|
|
6840
7016
|
/** Whether `filter` names every primary-key field (so it IS the row's exact key). */
|
|
6841
7017
|
_isPkFilter(filter) {
|
|
6842
7018
|
const pkFields = this.primaryKeys;
|
|
@@ -6898,11 +7074,12 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
6898
7074
|
controls
|
|
6899
7075
|
});
|
|
6900
7076
|
const used = /* @__PURE__ */ new Set();
|
|
6901
|
-
|
|
6902
|
-
|
|
7077
|
+
const found = findRowsByKeys(rows, filters);
|
|
7078
|
+
pending.forEach((i, k) => {
|
|
7079
|
+
const row = found[k];
|
|
6903
7080
|
targets[i] = row ?? null;
|
|
6904
7081
|
if (row) used.add(row);
|
|
6905
|
-
}
|
|
7082
|
+
});
|
|
6906
7083
|
if (used.size < rows.length) {
|
|
6907
7084
|
for (const i of pending) if (targets[i] === null) targets[i] = await this.findOne({
|
|
6908
7085
|
filter: rowFilters[i],
|
|
@@ -7092,7 +7269,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7092
7269
|
for (const [field, def] of this._meta.defaults.entries()) {
|
|
7093
7270
|
if (field === versionField) continue;
|
|
7094
7271
|
if (data[field] === void 0) {
|
|
7095
|
-
if (def.kind === "value") data[field] = this.
|
|
7272
|
+
if (def.kind === "value") data[field] = this._valueDefault(field, def.value);
|
|
7096
7273
|
else if (def.kind === "fn" && !nativeFns.has(def.fn)) switch (def.fn) {
|
|
7097
7274
|
case "now":
|
|
7098
7275
|
data[field] = Date.now();
|
|
@@ -7106,6 +7283,19 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7106
7283
|
return data;
|
|
7107
7284
|
}
|
|
7108
7285
|
/**
|
|
7286
|
+
* Parsed `@db.default 'literal'` values that are primitives — reused for
|
|
7287
|
+
* every row (an object / array default is parsed per row: each row gets
|
|
7288
|
+
* its own instance).
|
|
7289
|
+
*/
|
|
7290
|
+
_primitiveDefaults;
|
|
7291
|
+
_valueDefault(field, literal) {
|
|
7292
|
+
const cache = this._primitiveDefaults ??= /* @__PURE__ */ new Map();
|
|
7293
|
+
if (cache.has(field)) return cache.get(field);
|
|
7294
|
+
const value = this._parseValueDefault(field, literal);
|
|
7295
|
+
if (value === null || typeof value !== "object") cache.set(field, value);
|
|
7296
|
+
return value;
|
|
7297
|
+
}
|
|
7298
|
+
/**
|
|
7109
7299
|
* The JS value for a `@db.default 'literal'`: strings (including unions of
|
|
7110
7300
|
* string literals) are used as-is, every other design type is parsed as
|
|
7111
7301
|
* JSON — the same value the SQL adapters put into the DDL `DEFAULT` clause.
|
|
@@ -7789,6 +7979,7 @@ function extractFieldSnapshots(readable, typeMapper) {
|
|
|
7789
7979
|
jsonPath: [...f.derived.jsonPath],
|
|
7790
7980
|
type: f.derived.type
|
|
7791
7981
|
};
|
|
7982
|
+
if (f.collate && f.collate !== "binary") snap.collate = f.collate;
|
|
7792
7983
|
return snap;
|
|
7793
7984
|
}).toSorted((a, b) => a.physicalName.localeCompare(b.physicalName));
|
|
7794
7985
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as TDbDefaultValue, H as TDbFieldMeta, U as TDbForeignKey, _t as TExistingTableOption, bn as AtscriptDbReadable, cn as AtscriptQueryFieldRef, en as AtscriptDbView, ht as TExistingColumn, k as TColumnDiff, ln as AtscriptQueryNode, pt as TDerivedColumn, rt as TDbStorageType } from "./buckets-
|
|
1
|
+
import { B as TDbDefaultValue, H as TDbFieldMeta, R as TDbCollation, U as TDbForeignKey, _t as TExistingTableOption, bn as AtscriptDbReadable, cn as AtscriptQueryFieldRef, en as AtscriptDbView, ht as TExistingColumn, k as TColumnDiff, ln as AtscriptQueryNode, pt as TDerivedColumn, rt as TDbStorageType } from "./buckets-tzd8iIM2.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/schema/schema-hash.d.ts
|
|
4
4
|
interface TFieldSnapshot {
|
|
@@ -21,6 +21,13 @@ interface TFieldSnapshot {
|
|
|
21
21
|
* @since 0.1.141
|
|
22
22
|
*/
|
|
23
23
|
derived?: Omit<TDerivedColumn, "sourcePath">;
|
|
24
|
+
/**
|
|
25
|
+
* A non-binary `@db.column.collate` — changing it changes the snapshot hash
|
|
26
|
+
* on every adapter (MongoDB builds the field's indexes with its collation).
|
|
27
|
+
* Absent for byte-wise fields, so a table without one hashes as before.
|
|
28
|
+
* @since 0.1.151
|
|
29
|
+
*/
|
|
30
|
+
collate?: TDbCollation;
|
|
24
31
|
}
|
|
25
32
|
interface TIndexSnapshot {
|
|
26
33
|
key: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as TDbDefaultValue, H as TDbFieldMeta, U as TDbForeignKey, _t as TExistingTableOption, bn as AtscriptDbReadable, cn as AtscriptQueryFieldRef, en as AtscriptDbView, ht as TExistingColumn, k as TColumnDiff, ln as AtscriptQueryNode, pt as TDerivedColumn, rt as TDbStorageType } from "./buckets-
|
|
1
|
+
import { B as TDbDefaultValue, H as TDbFieldMeta, R as TDbCollation, U as TDbForeignKey, _t as TExistingTableOption, bn as AtscriptDbReadable, cn as AtscriptQueryFieldRef, en as AtscriptDbView, ht as TExistingColumn, k as TColumnDiff, ln as AtscriptQueryNode, pt as TDerivedColumn, rt as TDbStorageType } from "./buckets-Cb0zmhDc.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/schema/schema-hash.d.ts
|
|
4
4
|
interface TFieldSnapshot {
|
|
@@ -21,6 +21,13 @@ interface TFieldSnapshot {
|
|
|
21
21
|
* @since 0.1.141
|
|
22
22
|
*/
|
|
23
23
|
derived?: Omit<TDerivedColumn, "sourcePath">;
|
|
24
|
+
/**
|
|
25
|
+
* A non-binary `@db.column.collate` — changing it changes the snapshot hash
|
|
26
|
+
* on every adapter (MongoDB builds the field's indexes with its collation).
|
|
27
|
+
* Absent for byte-wise fields, so a table without one hashes as before.
|
|
28
|
+
* @since 0.1.151
|
|
29
|
+
*/
|
|
30
|
+
collate?: TDbCollation;
|
|
24
31
|
}
|
|
25
32
|
interface TIndexSnapshot {
|
|
26
33
|
key: string;
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_db_error = require("./db-error-DRQH4sLY.cjs");
|
|
3
|
-
const require_column_diff = require("./column-diff-
|
|
3
|
+
const require_column_diff = require("./column-diff-CU7KHj9-.cjs");
|
|
4
4
|
const require_aggregate_fns = require("./aggregate-fns-C-UJRobm.cjs");
|
|
5
|
-
const require_nested_writer = require("./nested-writer-
|
|
5
|
+
const require_nested_writer = require("./nested-writer-BzbOPSUp.cjs");
|
|
6
6
|
const require_search_fields = require("./search-fields-DwrsJUC1.cjs");
|
|
7
7
|
const require_object = require("./object-Djg28csK.cjs");
|
|
8
8
|
const require_consts = require("./consts-BzRfCcH2.cjs");
|
|
@@ -236,6 +236,12 @@ var DbSpace = class {
|
|
|
236
236
|
_readables = /* @__PURE__ */ new WeakMap();
|
|
237
237
|
/** All tables created in this space — used for reverse FK lookup during cascade. */
|
|
238
238
|
_allTables = /* @__PURE__ */ new Set();
|
|
239
|
+
/**
|
|
240
|
+
* Per-table-name memo of {@link _getCascadeTargets} / {@link _getFkLookupTarget}
|
|
241
|
+
* (since 0.1.151) — both scan every table; cleared whenever a table is added.
|
|
242
|
+
*/
|
|
243
|
+
_cascadeTargets = /* @__PURE__ */ new Map();
|
|
244
|
+
_fkLookupTargets = /* @__PURE__ */ new Map();
|
|
239
245
|
/** Every table / view handle the space created — flagged closed on {@link close}. */
|
|
240
246
|
_handles = /* @__PURE__ */ new Set();
|
|
241
247
|
/** Lazily created adapter for administrative ops (drop table/view) that don't need a registered readable. */
|
|
@@ -285,6 +291,8 @@ var DbSpace = class {
|
|
|
285
291
|
return resolved instanceof require_column_diff.AtscriptDbTable ? resolved : void 0;
|
|
286
292
|
});
|
|
287
293
|
this._allTables.add(readable);
|
|
294
|
+
this._cascadeTargets.clear();
|
|
295
|
+
this._fkLookupTargets.clear();
|
|
288
296
|
readable.setCascadeResolver((tableName) => this._getCascadeTargets(tableName));
|
|
289
297
|
readable.setFkLookupResolver((tableName) => this._getFkLookupTarget(tableName));
|
|
290
298
|
readable.setEncryption(this._encryption);
|
|
@@ -413,6 +421,14 @@ var DbSpace = class {
|
|
|
413
421
|
* Accesses `table.foreignKeys` which triggers `_flatten()` if needed.
|
|
414
422
|
*/
|
|
415
423
|
_getCascadeTargets(tableName) {
|
|
424
|
+
let targets = this._cascadeTargets.get(tableName);
|
|
425
|
+
if (targets === void 0) {
|
|
426
|
+
targets = this._scanCascadeTargets(tableName);
|
|
427
|
+
this._cascadeTargets.set(tableName, targets);
|
|
428
|
+
}
|
|
429
|
+
return targets;
|
|
430
|
+
}
|
|
431
|
+
_scanCascadeTargets(tableName) {
|
|
416
432
|
const targets = [];
|
|
417
433
|
for (const table of this._allTables) for (const fk of table.foreignKeys.values()) if (fk.targetTable === tableName && fk.onDelete) targets.push({
|
|
418
434
|
fk,
|
|
@@ -428,6 +444,12 @@ var DbSpace = class {
|
|
|
428
444
|
* Searches all registered tables for one with the matching table name.
|
|
429
445
|
*/
|
|
430
446
|
_getFkLookupTarget(tableName) {
|
|
447
|
+
if (this._fkLookupTargets.has(tableName)) return this._fkLookupTargets.get(tableName);
|
|
448
|
+
const target = this._scanFkLookupTarget(tableName);
|
|
449
|
+
this._fkLookupTargets.set(tableName, target);
|
|
450
|
+
return target;
|
|
451
|
+
}
|
|
452
|
+
_scanFkLookupTarget(tableName) {
|
|
431
453
|
for (const table of this._allTables) if (table.tableName === tableName) return { count: (filter) => table.count({ filter }) };
|
|
432
454
|
}
|
|
433
455
|
};
|