@atscript/db 0.1.41 → 0.1.42
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/{db-readable-BQQzfguJ.d.cts → db-readable-C3C-qPcP.d.cts} +14 -3
- package/dist/{db-readable-Bbr4CjMb.d.mts → db-readable-Cc868K54.d.mts} +14 -3
- package/dist/{db-space-BUrQ5BFm.d.mts → db-space-Dgf-CphX.d.cts} +12 -4
- package/dist/{db-space-Vxpcnyt5.d.cts → db-space-crCFv3DF.d.mts} +12 -4
- package/dist/{db-validator-plugin-07kDiis2.d.cts → db-validator-plugin-BLMVdi9z.d.mts} +2 -0
- package/dist/{db-validator-plugin-CiqsHTI_.d.mts → db-validator-plugin-Cz4QoDWg.d.cts} +2 -0
- package/dist/{db-view-CMI9TOo1.cjs → db-view-CcUjETIF.cjs} +165 -51
- package/dist/{db-view-Esy2fDxw.mjs → db-view-DSK76uc9.mjs} +165 -51
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +8 -3
- package/dist/index.d.mts +8 -3
- package/dist/index.mjs +2 -2
- package/dist/{nested-writer-BDXsDMPP.cjs → nested-writer-BIQ6EfaR.cjs} +1 -1
- package/dist/{nested-writer-Dmm1gbZV.mjs → nested-writer-CNDyhg2L.mjs} +1 -1
- package/dist/rel.cjs +1 -1
- package/dist/rel.d.cts +3 -3
- package/dist/rel.d.mts +3 -3
- package/dist/rel.mjs +1 -1
- package/dist/sync.cjs +2 -2
- package/dist/sync.d.cts +2 -2
- package/dist/sync.d.mts +2 -2
- package/dist/sync.mjs +1 -1
- package/package.json +6 -6
|
@@ -399,7 +399,11 @@ declare class TableMetadata {
|
|
|
399
399
|
booleanFields: Set<string>;
|
|
400
400
|
decimalFields: Set<string>;
|
|
401
401
|
allPhysicalFields: string[];
|
|
402
|
+
/** Precomputed parent path → child physical column names for fast null-setting. */
|
|
403
|
+
childrenByParent: Map<string, string[]>;
|
|
402
404
|
requiresMappings: boolean;
|
|
405
|
+
/** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
|
|
406
|
+
onlyColumnRenames: boolean;
|
|
403
407
|
toStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
404
408
|
fromStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
405
409
|
/** Leaf field descriptors indexed by physical column name (read path). */
|
|
@@ -958,13 +962,20 @@ declare abstract class FieldMappingStrategy {
|
|
|
958
962
|
abstract translateQuery(query: Uniquery, meta: TableMetadata): DbQuery;
|
|
959
963
|
abstract translateAggregateQuery(query: AggregateQuery, meta: TableMetadata): DbQuery;
|
|
960
964
|
/**
|
|
961
|
-
* Recursively walks a filter expression, applying
|
|
962
|
-
*
|
|
963
|
-
*
|
|
965
|
+
* Recursively walks a filter expression, applying `@db.column` key renames
|
|
966
|
+
* via `columnMap` and adapter-specific value formatting via `formatFilterValue`.
|
|
967
|
+
*
|
|
968
|
+
* The relational mapper overrides this to use `leafByLogical` for deeper
|
|
969
|
+
* key resolution (flattened nested paths).
|
|
964
970
|
*/
|
|
965
971
|
translateFilter(filter: FilterExpr, meta: TableMetadata): FilterExpr;
|
|
966
972
|
abstract prepareForWrite(payload: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): Record<string, unknown>;
|
|
967
973
|
abstract translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
974
|
+
/**
|
|
975
|
+
* Reverse-maps `@db.column` renames on a row read from storage.
|
|
976
|
+
* Renames physical keys back to logical names in-place.
|
|
977
|
+
*/
|
|
978
|
+
protected reverseColumnRenames(row: Record<string, unknown>, meta: TableMetadata): void;
|
|
968
979
|
/**
|
|
969
980
|
* Coerces field values from storage representation to JS types
|
|
970
981
|
* (booleans from 0/1, decimals from number to string).
|
|
@@ -399,7 +399,11 @@ declare class TableMetadata {
|
|
|
399
399
|
booleanFields: Set<string>;
|
|
400
400
|
decimalFields: Set<string>;
|
|
401
401
|
allPhysicalFields: string[];
|
|
402
|
+
/** Precomputed parent path → child physical column names for fast null-setting. */
|
|
403
|
+
childrenByParent: Map<string, string[]>;
|
|
402
404
|
requiresMappings: boolean;
|
|
405
|
+
/** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
|
|
406
|
+
onlyColumnRenames: boolean;
|
|
403
407
|
toStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
404
408
|
fromStorageFormatters?: Map<string, (value: unknown) => unknown>;
|
|
405
409
|
/** Leaf field descriptors indexed by physical column name (read path). */
|
|
@@ -958,13 +962,20 @@ declare abstract class FieldMappingStrategy {
|
|
|
958
962
|
abstract translateQuery(query: Uniquery, meta: TableMetadata): DbQuery;
|
|
959
963
|
abstract translateAggregateQuery(query: AggregateQuery, meta: TableMetadata): DbQuery;
|
|
960
964
|
/**
|
|
961
|
-
* Recursively walks a filter expression, applying
|
|
962
|
-
*
|
|
963
|
-
*
|
|
965
|
+
* Recursively walks a filter expression, applying `@db.column` key renames
|
|
966
|
+
* via `columnMap` and adapter-specific value formatting via `formatFilterValue`.
|
|
967
|
+
*
|
|
968
|
+
* The relational mapper overrides this to use `leafByLogical` for deeper
|
|
969
|
+
* key resolution (flattened nested paths).
|
|
964
970
|
*/
|
|
965
971
|
translateFilter(filter: FilterExpr, meta: TableMetadata): FilterExpr;
|
|
966
972
|
abstract prepareForWrite(payload: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): Record<string, unknown>;
|
|
967
973
|
abstract translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
974
|
+
/**
|
|
975
|
+
* Reverse-maps `@db.column` renames on a row read from storage.
|
|
976
|
+
* Renames physical keys back to logical names in-place.
|
|
977
|
+
*/
|
|
978
|
+
protected reverseColumnRenames(row: Record<string, unknown>, meta: TableMetadata): void;
|
|
968
979
|
/**
|
|
969
980
|
* Coerces field values from storage representation to JS types
|
|
970
981
|
* (booleans from 0/1, decimals from number to string).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { F as TDbInsertResult, H as TFkLookupResolver, O as TDbDeleteResult, P as TDbInsertManyResult, S as TCascadeResolver, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata, t as AtscriptDbReadable, z as TDbUpdateResult } from "./db-readable-
|
|
2
|
-
import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
|
|
1
|
+
import { F as TDbInsertResult, H as TFkLookupResolver, O as TDbDeleteResult, P as TDbInsertManyResult, S as TCascadeResolver, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata, t as AtscriptDbReadable, z as TDbUpdateResult } from "./db-readable-C3C-qPcP.cjs";
|
|
3
2
|
import { FilterExpr } from "@uniqu/core";
|
|
3
|
+
import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/strategies/integrity.d.ts
|
|
6
6
|
/**
|
|
@@ -130,9 +130,17 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
|
|
|
130
130
|
*/
|
|
131
131
|
protected _applyDefaults(data: Record<string, unknown>): Record<string, unknown>;
|
|
132
132
|
/**
|
|
133
|
-
* Extracts
|
|
133
|
+
* Extracts a record-identifying filter from a payload.
|
|
134
|
+
*
|
|
135
|
+
* Resolution order:
|
|
136
|
+
* 1. Primary key field(s) — if all PK fields are present in the payload.
|
|
137
|
+
* 2. Single-field unique index — first `@db.index.unique` field found.
|
|
138
|
+
* 3. Compound unique index — first compound unique index whose fields are all present.
|
|
139
|
+
*
|
|
140
|
+
* Throws when no identifying fields can be found.
|
|
134
141
|
*/
|
|
135
|
-
protected
|
|
142
|
+
protected _extractRecordFilter(payload: Record<string, unknown>): FilterExpr;
|
|
143
|
+
private _prepareFilterValue;
|
|
136
144
|
/**
|
|
137
145
|
* Pre-validate items (type validation + FK constraints) without inserting them.
|
|
138
146
|
* Used by parent tables to validate FROM children before the main insert,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { F as TDbInsertResult, H as TFkLookupResolver, O as TDbDeleteResult, P as TDbInsertManyResult, S as TCascadeResolver, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata, t as AtscriptDbReadable, z as TDbUpdateResult } from "./db-readable-
|
|
2
|
-
import { FilterExpr } from "@uniqu/core";
|
|
1
|
+
import { F as TDbInsertResult, H as TFkLookupResolver, O as TDbDeleteResult, P as TDbInsertManyResult, S as TCascadeResolver, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata, t as AtscriptDbReadable, z as TDbUpdateResult } from "./db-readable-Cc868K54.mjs";
|
|
3
2
|
import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
|
|
3
|
+
import { FilterExpr } from "@uniqu/core";
|
|
4
4
|
|
|
5
5
|
//#region src/strategies/integrity.d.ts
|
|
6
6
|
/**
|
|
@@ -130,9 +130,17 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
|
|
|
130
130
|
*/
|
|
131
131
|
protected _applyDefaults(data: Record<string, unknown>): Record<string, unknown>;
|
|
132
132
|
/**
|
|
133
|
-
* Extracts
|
|
133
|
+
* Extracts a record-identifying filter from a payload.
|
|
134
|
+
*
|
|
135
|
+
* Resolution order:
|
|
136
|
+
* 1. Primary key field(s) — if all PK fields are present in the payload.
|
|
137
|
+
* 2. Single-field unique index — first `@db.index.unique` field found.
|
|
138
|
+
* 3. Compound unique index — first compound unique index whose fields are all present.
|
|
139
|
+
*
|
|
140
|
+
* Throws when no identifying fields can be found.
|
|
134
141
|
*/
|
|
135
|
-
protected
|
|
142
|
+
protected _extractRecordFilter(payload: Record<string, unknown>): FilterExpr;
|
|
143
|
+
private _prepareFilterValue;
|
|
136
144
|
/**
|
|
137
145
|
* Pre-validate items (type validation + FK constraints) without inserting them.
|
|
138
146
|
* Used by parent tables to validate FROM children before the main insert,
|
|
@@ -5,6 +5,8 @@ interface DbValidationContext {
|
|
|
5
5
|
mode: "insert" | "replace" | "patch";
|
|
6
6
|
/** Flat map from the table — used to check if an array is a top-level array. */
|
|
7
7
|
flatMap?: Map<string, TAtscriptAnnotatedType>;
|
|
8
|
+
/** Precomputed nav field names — used to skip field-op validation inside TO/FROM/VIA relations. */
|
|
9
|
+
navFields?: ReadonlySet<string>;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Validator plugin for database operations.
|
|
@@ -5,6 +5,8 @@ interface DbValidationContext {
|
|
|
5
5
|
mode: "insert" | "replace" | "patch";
|
|
6
6
|
/** Flat map from the table — used to check if an array is a top-level array. */
|
|
7
7
|
flatMap?: Map<string, TAtscriptAnnotatedType>;
|
|
8
|
+
/** Precomputed nav field names — used to skip field-op validation inside TO/FROM/VIA relations. */
|
|
9
|
+
navFields?: ReadonlySet<string>;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Validator plugin for database operations.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_nested_writer = require("./nested-writer-
|
|
1
|
+
const require_nested_writer = require("./nested-writer-BIQ6EfaR.cjs");
|
|
2
2
|
const require_agg = require("./agg.cjs");
|
|
3
3
|
const require_relation_helpers = require("./relation-helpers-BYvsE1tR.cjs");
|
|
4
4
|
const require_ops = require("./ops.cjs");
|
|
@@ -69,7 +69,11 @@ var TableMetadata = class {
|
|
|
69
69
|
booleanFields = /* @__PURE__ */ new Set();
|
|
70
70
|
decimalFields = /* @__PURE__ */ new Set();
|
|
71
71
|
allPhysicalFields = [];
|
|
72
|
+
/** Precomputed parent path → child physical column names for fast null-setting. */
|
|
73
|
+
childrenByParent = /* @__PURE__ */ new Map();
|
|
72
74
|
requiresMappings = false;
|
|
75
|
+
/** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
|
|
76
|
+
onlyColumnRenames = false;
|
|
73
77
|
toStorageFormatters;
|
|
74
78
|
fromStorageFormatters;
|
|
75
79
|
/** Leaf field descriptors indexed by physical column name (read path). */
|
|
@@ -300,7 +304,8 @@ var TableMetadata = class {
|
|
|
300
304
|
for (const [path, physical] of this.pathToPhysical) if (path.startsWith(prefix)) leaves.push(physical);
|
|
301
305
|
if (leaves.length > 0) this.selectExpansion.set(parentPath, leaves);
|
|
302
306
|
}
|
|
303
|
-
this.
|
|
307
|
+
this.onlyColumnRenames = this.columnMap.size > 0 && this.flattenedParents.size === 0 && this.jsonFields.size === 0;
|
|
308
|
+
this.requiresMappings = this.flattenedParents.size > 0 || this.jsonFields.size > 0 || this.onlyColumnRenames;
|
|
304
309
|
}
|
|
305
310
|
/** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
|
|
306
311
|
_flattenedPrefix(path) {
|
|
@@ -317,6 +322,12 @@ var TableMetadata = class {
|
|
|
317
322
|
this.leafByPhysical.set(fd.physicalName, fd);
|
|
318
323
|
this.leafByLogical.set(fd.path, fd);
|
|
319
324
|
}
|
|
325
|
+
for (const parentPath of this.flattenedParents) {
|
|
326
|
+
const prefix = `${parentPath}.`;
|
|
327
|
+
const children = [];
|
|
328
|
+
for (const [path, fd] of this.leafByLogical.entries()) if (path.startsWith(prefix)) children.push(fd.physicalName);
|
|
329
|
+
if (children.length > 0) this.childrenByParent.set(parentPath, children);
|
|
330
|
+
}
|
|
320
331
|
}
|
|
321
332
|
/**
|
|
322
333
|
* Builds field descriptors, physical-name lookup, and value formatters.
|
|
@@ -540,21 +551,36 @@ function toDecimalString(value) {
|
|
|
540
551
|
*/
|
|
541
552
|
var FieldMappingStrategy = class {
|
|
542
553
|
/**
|
|
543
|
-
* Recursively walks a filter expression, applying
|
|
544
|
-
*
|
|
545
|
-
*
|
|
554
|
+
* Recursively walks a filter expression, applying `@db.column` key renames
|
|
555
|
+
* via `columnMap` and adapter-specific value formatting via `formatFilterValue`.
|
|
556
|
+
*
|
|
557
|
+
* The relational mapper overrides this to use `leafByLogical` for deeper
|
|
558
|
+
* key resolution (flattened nested paths).
|
|
546
559
|
*/
|
|
547
560
|
translateFilter(filter, meta) {
|
|
548
561
|
if (!filter || typeof filter !== "object") return filter;
|
|
549
|
-
if (!meta.toStorageFormatters) return filter;
|
|
562
|
+
if (!meta.toStorageFormatters && meta.columnMap.size === 0) return filter;
|
|
550
563
|
const result = {};
|
|
551
564
|
for (const [key, value] of Object.entries(filter)) if (key === "$and" || key === "$or") result[key] = value.map((f) => this.translateFilter(f, meta));
|
|
552
565
|
else if (key === "$not") result[key] = this.translateFilter(value, meta);
|
|
553
566
|
else if (key.startsWith("$")) result[key] = value;
|
|
554
|
-
else
|
|
567
|
+
else {
|
|
568
|
+
const physical = meta.columnMap.get(key) ?? key;
|
|
569
|
+
result[physical] = this.formatFilterValue(physical, value, meta);
|
|
570
|
+
}
|
|
555
571
|
return result;
|
|
556
572
|
}
|
|
557
573
|
/**
|
|
574
|
+
* Reverse-maps `@db.column` renames on a row read from storage.
|
|
575
|
+
* Renames physical keys back to logical names in-place.
|
|
576
|
+
*/
|
|
577
|
+
reverseColumnRenames(row, meta) {
|
|
578
|
+
for (const [logical, physical] of meta.columnMap.entries()) if (physical in row) {
|
|
579
|
+
row[logical] = row[physical];
|
|
580
|
+
delete row[physical];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
558
584
|
* Coerces field values from storage representation to JS types
|
|
559
585
|
* (booleans from 0/1, decimals from number to string).
|
|
560
586
|
*/
|
|
@@ -662,12 +688,15 @@ var FieldMappingStrategy = class {
|
|
|
662
688
|
*/
|
|
663
689
|
var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
664
690
|
reconstructFromRead(row, meta) {
|
|
665
|
-
|
|
691
|
+
this.coerceFieldValues(row, meta);
|
|
692
|
+
this.applyFromStorageFormatters(row, meta);
|
|
693
|
+
if (meta.columnMap.size > 0) this.reverseColumnRenames(row, meta);
|
|
694
|
+
return row;
|
|
666
695
|
}
|
|
667
696
|
translateQuery(query, meta) {
|
|
668
697
|
const controls = query.controls;
|
|
669
698
|
return {
|
|
670
|
-
filter:
|
|
699
|
+
filter: this.translateFilter(query.filter, meta),
|
|
671
700
|
controls: {
|
|
672
701
|
...controls,
|
|
673
702
|
$with: void 0,
|
|
@@ -679,7 +708,7 @@ var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
|
679
708
|
translateAggregateQuery(query, meta) {
|
|
680
709
|
const controls = query.controls;
|
|
681
710
|
return {
|
|
682
|
-
filter:
|
|
711
|
+
filter: this.translateFilter(query.filter ?? {}, meta),
|
|
683
712
|
controls: {
|
|
684
713
|
...controls,
|
|
685
714
|
$with: void 0,
|
|
@@ -698,6 +727,11 @@ var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
|
698
727
|
return this.formatWriteValues(data, meta);
|
|
699
728
|
}
|
|
700
729
|
translatePatchKeys(update, meta) {
|
|
730
|
+
if (meta.columnMap.size > 0) {
|
|
731
|
+
const result = {};
|
|
732
|
+
for (const key of Object.keys(update)) result[meta.columnMap.get(key) ?? key] = update[key];
|
|
733
|
+
return this.formatWriteValues(result, meta);
|
|
734
|
+
}
|
|
701
735
|
return this.formatWriteValues(update, meta);
|
|
702
736
|
}
|
|
703
737
|
};
|
|
@@ -712,6 +746,12 @@ var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
|
712
746
|
var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
713
747
|
reconstructFromRead(row, meta) {
|
|
714
748
|
if (!meta.requiresMappings) return this.applyFromStorageFormatters(this.coerceFieldValues(row, meta), meta);
|
|
749
|
+
if (meta.onlyColumnRenames) {
|
|
750
|
+
this.coerceFieldValues(row, meta);
|
|
751
|
+
this.applyFromStorageFormatters(row, meta);
|
|
752
|
+
this.reverseColumnRenames(row, meta);
|
|
753
|
+
return row;
|
|
754
|
+
}
|
|
715
755
|
const result = {};
|
|
716
756
|
const fromFmts = meta.fromStorageFormatters;
|
|
717
757
|
for (const physical of Object.keys(row)) {
|
|
@@ -794,6 +834,15 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
794
834
|
};
|
|
795
835
|
}
|
|
796
836
|
/**
|
|
837
|
+
* Overrides the base `translateFilter` to use `leafByLogical` for key resolution
|
|
838
|
+
* (handles flattened nested paths like `contact.email` → `contact__email`).
|
|
839
|
+
*/
|
|
840
|
+
translateFilter(filter, meta) {
|
|
841
|
+
if (!filter || typeof filter !== "object") return filter;
|
|
842
|
+
if (!meta.requiresMappings && !meta.toStorageFormatters) return filter;
|
|
843
|
+
return this.translateFilterWithRename(filter, meta);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
797
846
|
* Translates filter with key renaming from logical to physical names.
|
|
798
847
|
* Used by the relational query path where field paths must be mapped
|
|
799
848
|
* to `__`-separated column names.
|
|
@@ -813,7 +862,8 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
813
862
|
prepareForWrite(payload, meta, adapter) {
|
|
814
863
|
const data = { ...payload };
|
|
815
864
|
this.prepareCommon(data, meta, adapter);
|
|
816
|
-
if (!meta.requiresMappings)
|
|
865
|
+
if (!meta.requiresMappings) return this.formatWriteValues(data, meta);
|
|
866
|
+
if (meta.onlyColumnRenames) {
|
|
817
867
|
for (const [logical, physical] of meta.columnMap.entries()) if (logical in data) {
|
|
818
868
|
data[physical] = data[logical];
|
|
819
869
|
delete data[logical];
|
|
@@ -824,6 +874,11 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
824
874
|
}
|
|
825
875
|
translatePatchKeys(update, meta) {
|
|
826
876
|
if (!meta.requiresMappings && !meta.toStorageFormatters) return update;
|
|
877
|
+
if (meta.onlyColumnRenames && !meta.toStorageFormatters) {
|
|
878
|
+
const result = {};
|
|
879
|
+
for (const key of Object.keys(update)) result[meta.leafByLogical.get(key)?.physicalName ?? key] = update[key];
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
827
882
|
const result = {};
|
|
828
883
|
const updateKeys = Object.keys(update);
|
|
829
884
|
for (const key of updateKeys) {
|
|
@@ -921,8 +976,8 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
921
976
|
* When a parent object is null/undefined, set all its flattened children to null.
|
|
922
977
|
*/
|
|
923
978
|
setFlattenedChildrenNull(parentPath, result, meta) {
|
|
924
|
-
const
|
|
925
|
-
for (const
|
|
979
|
+
const children = meta.childrenByParent.get(parentPath);
|
|
980
|
+
if (children) for (const physical of children) result[physical] = null;
|
|
926
981
|
}
|
|
927
982
|
};
|
|
928
983
|
//#endregion
|
|
@@ -2108,28 +2163,42 @@ function createDbValidatorPlugin() {
|
|
|
2108
2163
|
const isFrom = def.metadata.has("db.rel.from");
|
|
2109
2164
|
const isVia = def.metadata.has("db.rel.via");
|
|
2110
2165
|
if (isTo || isFrom || isVia) return handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia);
|
|
2111
|
-
if (dbCtx.mode === "patch"
|
|
2112
|
-
if (
|
|
2113
|
-
|
|
2114
|
-
|
|
2166
|
+
if (dbCtx.mode === "patch") {
|
|
2167
|
+
if (require_ops.isDbFieldOp(value)) {
|
|
2168
|
+
if (dbCtx.flatMap && !isFieldOpAllowed(ctx.path, dbCtx.flatMap, dbCtx.navFields)) {
|
|
2169
|
+
ctx.error("Field operations ($inc/$dec/$mul) are not supported inside @db.json fields or nested objects without @db.patch.strategy \"merge\"");
|
|
2170
|
+
return false;
|
|
2171
|
+
}
|
|
2172
|
+
if (!(def.type.kind === "" && def.type.designType === "number")) {
|
|
2173
|
+
ctx.error("Field operations ($inc/$dec/$mul) can only be applied to numeric fields");
|
|
2174
|
+
return false;
|
|
2175
|
+
}
|
|
2176
|
+
return true;
|
|
2177
|
+
}
|
|
2178
|
+
if (def.type.kind === "array" && dbCtx.flatMap) {
|
|
2179
|
+
const flatEntry = dbCtx.flatMap.get(ctx.path);
|
|
2180
|
+
if (flatEntry?.metadata?.has("db.__topLevelArray") && !flatEntry.metadata.has("db.json")) return handleArrayPatch(ctx, def, value);
|
|
2115
2181
|
}
|
|
2116
|
-
return true;
|
|
2117
|
-
}
|
|
2118
|
-
if (dbCtx.mode === "patch" && def.type.kind === "array" && dbCtx.flatMap) {
|
|
2119
|
-
const flatEntry = dbCtx.flatMap.get(ctx.path);
|
|
2120
|
-
if (flatEntry?.metadata?.has("db.__topLevelArray") && !flatEntry.metadata.has("db.json")) return handleArrayPatch(ctx, def, value);
|
|
2121
2182
|
}
|
|
2122
2183
|
};
|
|
2123
2184
|
}
|
|
2124
2185
|
/**
|
|
2125
|
-
* Checks whether a field op is valid at `path
|
|
2126
|
-
*
|
|
2127
|
-
*
|
|
2186
|
+
* Checks whether a field op is valid at `path`.
|
|
2187
|
+
*
|
|
2188
|
+
* Rejects when:
|
|
2189
|
+
* - The field itself is `@db.json` (ops on opaque blobs are meaningless).
|
|
2190
|
+
* - Any ancestor is `@db.json`.
|
|
2191
|
+
* - Any ancestor is a nested object without `@db.patch.strategy "merge"`.
|
|
2192
|
+
*
|
|
2193
|
+
* Accepts immediately when an ancestor is a navigation field (TO/FROM/VIA) —
|
|
2194
|
+
* the nested data is extracted and validated against its own table separately.
|
|
2128
2195
|
*/
|
|
2129
|
-
function isFieldOpAllowed(path, flatMap) {
|
|
2196
|
+
function isFieldOpAllowed(path, flatMap, navFields) {
|
|
2197
|
+
if (flatMap.get(path)?.metadata.has("db.json")) return false;
|
|
2130
2198
|
let pos = path.length;
|
|
2131
2199
|
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) {
|
|
2132
2200
|
const ancestor = path.slice(0, pos);
|
|
2201
|
+
if (navFields?.has(ancestor)) return true;
|
|
2133
2202
|
const entry = flatMap.get(ancestor);
|
|
2134
2203
|
if (!entry) continue;
|
|
2135
2204
|
if (entry.metadata.has("db.json")) return false;
|
|
@@ -2138,8 +2207,8 @@ function isFieldOpAllowed(path, flatMap) {
|
|
|
2138
2207
|
return true;
|
|
2139
2208
|
}
|
|
2140
2209
|
function handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia) {
|
|
2141
|
-
const
|
|
2142
|
-
const fieldName =
|
|
2210
|
+
const dotIdx = ctx.path.lastIndexOf(".");
|
|
2211
|
+
const fieldName = dotIdx === -1 ? ctx.path : ctx.path.slice(dotIdx + 1);
|
|
2143
2212
|
if (value === null) {
|
|
2144
2213
|
ctx.error(`Cannot process null navigation property '${fieldName}'`);
|
|
2145
2214
|
return false;
|
|
@@ -2576,7 +2645,10 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2576
2645
|
if (!canNest && this._meta.navFields.size > 0) require_nested_writer.checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
2577
2646
|
return require_nested_writer.enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
|
|
2578
2647
|
const items = payloads.map((p) => this._applyDefaults({ ...p }));
|
|
2579
|
-
require_nested_writer.validateBatch(this.getValidator("insert"), items, {
|
|
2648
|
+
require_nested_writer.validateBatch(this.getValidator("insert"), items, {
|
|
2649
|
+
mode: "insert",
|
|
2650
|
+
navFields: this._meta.navFields
|
|
2651
|
+
});
|
|
2580
2652
|
const host = this;
|
|
2581
2653
|
if (canNest) await require_nested_writer.batchInsertNestedTo(host, items, maxDepth, depth);
|
|
2582
2654
|
const prepared = [];
|
|
@@ -2616,7 +2688,10 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2616
2688
|
return require_nested_writer.enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
|
|
2617
2689
|
const items = payloads.map((p) => this._applyDefaults({ ...p }));
|
|
2618
2690
|
const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
|
|
2619
|
-
require_nested_writer.validateBatch(this.getValidator("bulkReplace"), items, {
|
|
2691
|
+
require_nested_writer.validateBatch(this.getValidator("bulkReplace"), items, {
|
|
2692
|
+
mode: "replace",
|
|
2693
|
+
navFields: this._meta.navFields
|
|
2694
|
+
});
|
|
2620
2695
|
const host = this;
|
|
2621
2696
|
if (canNest) await require_nested_writer.batchReplaceNestedTo(host, items, maxDepth, depth);
|
|
2622
2697
|
await this._integrity.validateForeignKeys(items, this._meta, this._fkLookupResolver, this._writeTableResolver);
|
|
@@ -2625,7 +2700,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2625
2700
|
let modifiedCount = 0;
|
|
2626
2701
|
for (const data of items) {
|
|
2627
2702
|
for (const navField of this._meta.navFields) delete data[navField];
|
|
2628
|
-
const filter = this.
|
|
2703
|
+
const filter = this._extractRecordFilter(data);
|
|
2629
2704
|
const prepared = this._fieldMapper.prepareForWrite(data, this._meta, this.adapter);
|
|
2630
2705
|
const result = await this.adapter.replaceOne(this._fieldMapper.translateFilter(filter, this._meta), prepared);
|
|
2631
2706
|
matchedCount += result.matchedCount;
|
|
@@ -2662,7 +2737,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2662
2737
|
return require_nested_writer.enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
|
|
2663
2738
|
require_nested_writer.validateBatch(this.getValidator("bulkUpdate"), payloads, {
|
|
2664
2739
|
mode: "patch",
|
|
2665
|
-
flatMap: this.flatMap
|
|
2740
|
+
flatMap: this.flatMap,
|
|
2741
|
+
navFields: this._meta.navFields
|
|
2666
2742
|
});
|
|
2667
2743
|
const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
|
|
2668
2744
|
const host = this;
|
|
@@ -2673,8 +2749,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2673
2749
|
for (const payload of payloads) {
|
|
2674
2750
|
const data = { ...payload };
|
|
2675
2751
|
for (const navField of this._meta.navFields) delete data[navField];
|
|
2676
|
-
const filter = this.
|
|
2677
|
-
for (const
|
|
2752
|
+
const filter = this._extractRecordFilter(data);
|
|
2753
|
+
for (const key of Object.keys(filter)) delete data[key];
|
|
2678
2754
|
if (_isEmptyObj(data)) {
|
|
2679
2755
|
matchedCount += 1;
|
|
2680
2756
|
modifiedCount += 0;
|
|
@@ -2685,7 +2761,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2685
2761
|
if (this.adapter.supportsNativePatch()) {
|
|
2686
2762
|
const ops = require_ops.separateFieldOps(data);
|
|
2687
2763
|
const translatedOps = ops ? _translateOpsKeys(ops, this._meta) : void 0;
|
|
2688
|
-
|
|
2764
|
+
const translatedData = this._fieldMapper.translatePatchKeys(data, this._meta);
|
|
2765
|
+
result = await this.adapter.nativePatch(translatedFilter, translatedData, translatedOps);
|
|
2689
2766
|
} else {
|
|
2690
2767
|
const update = decomposePatch(data, this);
|
|
2691
2768
|
const ops = require_ops.separateFieldOps(update);
|
|
@@ -2730,10 +2807,11 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2730
2807
|
async updateMany(filter, data) {
|
|
2731
2808
|
this._ensureBuilt();
|
|
2732
2809
|
await this._integrity.validateForeignKeys([data], this._meta, this._fkLookupResolver, this._writeTableResolver, true);
|
|
2733
|
-
const
|
|
2734
|
-
const ops = require_ops.separateFieldOps(
|
|
2810
|
+
const update = decomposePatch({ ...data }, this);
|
|
2811
|
+
const ops = require_ops.separateFieldOps(update);
|
|
2735
2812
|
const translatedOps = ops ? _translateOpsKeys(ops, this._meta) : void 0;
|
|
2736
|
-
|
|
2813
|
+
const translatedUpdate = this._fieldMapper.translatePatchKeys(update, this._meta);
|
|
2814
|
+
return require_nested_writer.enrichFkViolation(this._meta, () => this.adapter.updateMany(this._fieldMapper.translateFilter(filter, this._meta), translatedUpdate, translatedOps));
|
|
2737
2815
|
}
|
|
2738
2816
|
async replaceMany(filter, data) {
|
|
2739
2817
|
this._ensureBuilt();
|
|
@@ -2786,24 +2864,55 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2786
2864
|
return data;
|
|
2787
2865
|
}
|
|
2788
2866
|
/**
|
|
2789
|
-
* Extracts
|
|
2867
|
+
* Extracts a record-identifying filter from a payload.
|
|
2868
|
+
*
|
|
2869
|
+
* Resolution order:
|
|
2870
|
+
* 1. Primary key field(s) — if all PK fields are present in the payload.
|
|
2871
|
+
* 2. Single-field unique index — first `@db.index.unique` field found.
|
|
2872
|
+
* 3. Compound unique index — first compound unique index whose fields are all present.
|
|
2873
|
+
*
|
|
2874
|
+
* Throws when no identifying fields can be found.
|
|
2790
2875
|
*/
|
|
2791
|
-
|
|
2876
|
+
_extractRecordFilter(payload) {
|
|
2792
2877
|
const pkFields = this.primaryKeys;
|
|
2878
|
+
if (pkFields.length > 0) {
|
|
2879
|
+
let allPresent = true;
|
|
2880
|
+
for (const field of pkFields) if (payload[field] === void 0) {
|
|
2881
|
+
allPresent = false;
|
|
2882
|
+
break;
|
|
2883
|
+
}
|
|
2884
|
+
if (allPresent) {
|
|
2885
|
+
const filter = {};
|
|
2886
|
+
for (const field of pkFields) filter[field] = this._prepareFilterValue(field, payload[field]);
|
|
2887
|
+
return filter;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
for (const prop of this.uniqueProps) if (payload[prop] !== void 0) return { [prop]: this._prepareFilterValue(prop, payload[prop]) };
|
|
2891
|
+
for (const index of this._meta.indexes.values()) {
|
|
2892
|
+
if (index.type !== "unique" || index.fields.length < 2) continue;
|
|
2893
|
+
let allPresent = true;
|
|
2894
|
+
for (const indexField of index.fields) if (payload[indexField.name] === void 0) {
|
|
2895
|
+
allPresent = false;
|
|
2896
|
+
break;
|
|
2897
|
+
}
|
|
2898
|
+
if (allPresent) {
|
|
2899
|
+
const filter = {};
|
|
2900
|
+
for (const indexField of index.fields) filter[indexField.name] = this._prepareFilterValue(indexField.name, payload[indexField.name]);
|
|
2901
|
+
return filter;
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2793
2904
|
if (pkFields.length === 0) throw new require_nested_writer.DbError("NOT_FOUND", [{
|
|
2794
2905
|
path: "",
|
|
2795
2906
|
message: "No primary key defined — cannot extract filter"
|
|
2796
2907
|
}]);
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
}
|
|
2806
|
-
return filter;
|
|
2908
|
+
throw new require_nested_writer.DbError("NOT_FOUND", [{
|
|
2909
|
+
path: pkFields[0],
|
|
2910
|
+
message: `Missing primary key field "${pkFields[0]}" in payload`
|
|
2911
|
+
}]);
|
|
2912
|
+
}
|
|
2913
|
+
_prepareFilterValue(field, value) {
|
|
2914
|
+
const fieldType = this.flatMap.get(field);
|
|
2915
|
+
return fieldType ? this.adapter.prepareId(value, fieldType) : value;
|
|
2807
2916
|
}
|
|
2808
2917
|
/**
|
|
2809
2918
|
* Pre-validate items (type validation + FK constraints) without inserting them.
|
|
@@ -2814,7 +2923,12 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2814
2923
|
*/
|
|
2815
2924
|
async preValidateItems(items, opts) {
|
|
2816
2925
|
this._ensureBuilt();
|
|
2817
|
-
|
|
2926
|
+
const validator = this.getValidator("insert");
|
|
2927
|
+
const ctx = {
|
|
2928
|
+
mode: "insert",
|
|
2929
|
+
navFields: this._meta.navFields
|
|
2930
|
+
};
|
|
2931
|
+
require_nested_writer.validateBatch(validator, items.map((raw) => this._applyDefaults({ ...raw })), ctx);
|
|
2818
2932
|
await this._integrity.validateForeignKeys(items, this._meta, this._fkLookupResolver, this._writeTableResolver, false, opts?.excludeFkTargetTable);
|
|
2819
2933
|
}
|
|
2820
2934
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, h as DbError, i as batchPatchNestedFrom, l as batchReplaceNestedVia, m as remapDeleteFkViolation, n as batchInsertNestedTo, o as batchPatchNestedVia, p as enrichFkViolation, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-
|
|
1
|
+
import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, h as DbError, i as batchPatchNestedFrom, l as batchReplaceNestedVia, m as remapDeleteFkViolation, n as batchInsertNestedTo, o as batchPatchNestedVia, p as enrichFkViolation, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-CNDyhg2L.mjs";
|
|
2
2
|
import { resolveAlias } from "./agg.mjs";
|
|
3
3
|
import { n as findRemoteFK, t as findFKForRelation } from "./relation-helpers-CLasawQq.mjs";
|
|
4
4
|
import { isDbFieldOp, separateFieldOps } from "./ops.mjs";
|
|
@@ -69,7 +69,11 @@ var TableMetadata = class {
|
|
|
69
69
|
booleanFields = /* @__PURE__ */ new Set();
|
|
70
70
|
decimalFields = /* @__PURE__ */ new Set();
|
|
71
71
|
allPhysicalFields = [];
|
|
72
|
+
/** Precomputed parent path → child physical column names for fast null-setting. */
|
|
73
|
+
childrenByParent = /* @__PURE__ */ new Map();
|
|
72
74
|
requiresMappings = false;
|
|
75
|
+
/** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
|
|
76
|
+
onlyColumnRenames = false;
|
|
73
77
|
toStorageFormatters;
|
|
74
78
|
fromStorageFormatters;
|
|
75
79
|
/** Leaf field descriptors indexed by physical column name (read path). */
|
|
@@ -300,7 +304,8 @@ var TableMetadata = class {
|
|
|
300
304
|
for (const [path, physical] of this.pathToPhysical) if (path.startsWith(prefix)) leaves.push(physical);
|
|
301
305
|
if (leaves.length > 0) this.selectExpansion.set(parentPath, leaves);
|
|
302
306
|
}
|
|
303
|
-
this.
|
|
307
|
+
this.onlyColumnRenames = this.columnMap.size > 0 && this.flattenedParents.size === 0 && this.jsonFields.size === 0;
|
|
308
|
+
this.requiresMappings = this.flattenedParents.size > 0 || this.jsonFields.size > 0 || this.onlyColumnRenames;
|
|
304
309
|
}
|
|
305
310
|
/** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
|
|
306
311
|
_flattenedPrefix(path) {
|
|
@@ -317,6 +322,12 @@ var TableMetadata = class {
|
|
|
317
322
|
this.leafByPhysical.set(fd.physicalName, fd);
|
|
318
323
|
this.leafByLogical.set(fd.path, fd);
|
|
319
324
|
}
|
|
325
|
+
for (const parentPath of this.flattenedParents) {
|
|
326
|
+
const prefix = `${parentPath}.`;
|
|
327
|
+
const children = [];
|
|
328
|
+
for (const [path, fd] of this.leafByLogical.entries()) if (path.startsWith(prefix)) children.push(fd.physicalName);
|
|
329
|
+
if (children.length > 0) this.childrenByParent.set(parentPath, children);
|
|
330
|
+
}
|
|
320
331
|
}
|
|
321
332
|
/**
|
|
322
333
|
* Builds field descriptors, physical-name lookup, and value formatters.
|
|
@@ -540,21 +551,36 @@ function toDecimalString(value) {
|
|
|
540
551
|
*/
|
|
541
552
|
var FieldMappingStrategy = class {
|
|
542
553
|
/**
|
|
543
|
-
* Recursively walks a filter expression, applying
|
|
544
|
-
*
|
|
545
|
-
*
|
|
554
|
+
* Recursively walks a filter expression, applying `@db.column` key renames
|
|
555
|
+
* via `columnMap` and adapter-specific value formatting via `formatFilterValue`.
|
|
556
|
+
*
|
|
557
|
+
* The relational mapper overrides this to use `leafByLogical` for deeper
|
|
558
|
+
* key resolution (flattened nested paths).
|
|
546
559
|
*/
|
|
547
560
|
translateFilter(filter, meta) {
|
|
548
561
|
if (!filter || typeof filter !== "object") return filter;
|
|
549
|
-
if (!meta.toStorageFormatters) return filter;
|
|
562
|
+
if (!meta.toStorageFormatters && meta.columnMap.size === 0) return filter;
|
|
550
563
|
const result = {};
|
|
551
564
|
for (const [key, value] of Object.entries(filter)) if (key === "$and" || key === "$or") result[key] = value.map((f) => this.translateFilter(f, meta));
|
|
552
565
|
else if (key === "$not") result[key] = this.translateFilter(value, meta);
|
|
553
566
|
else if (key.startsWith("$")) result[key] = value;
|
|
554
|
-
else
|
|
567
|
+
else {
|
|
568
|
+
const physical = meta.columnMap.get(key) ?? key;
|
|
569
|
+
result[physical] = this.formatFilterValue(physical, value, meta);
|
|
570
|
+
}
|
|
555
571
|
return result;
|
|
556
572
|
}
|
|
557
573
|
/**
|
|
574
|
+
* Reverse-maps `@db.column` renames on a row read from storage.
|
|
575
|
+
* Renames physical keys back to logical names in-place.
|
|
576
|
+
*/
|
|
577
|
+
reverseColumnRenames(row, meta) {
|
|
578
|
+
for (const [logical, physical] of meta.columnMap.entries()) if (physical in row) {
|
|
579
|
+
row[logical] = row[physical];
|
|
580
|
+
delete row[physical];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
558
584
|
* Coerces field values from storage representation to JS types
|
|
559
585
|
* (booleans from 0/1, decimals from number to string).
|
|
560
586
|
*/
|
|
@@ -662,12 +688,15 @@ var FieldMappingStrategy = class {
|
|
|
662
688
|
*/
|
|
663
689
|
var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
664
690
|
reconstructFromRead(row, meta) {
|
|
665
|
-
|
|
691
|
+
this.coerceFieldValues(row, meta);
|
|
692
|
+
this.applyFromStorageFormatters(row, meta);
|
|
693
|
+
if (meta.columnMap.size > 0) this.reverseColumnRenames(row, meta);
|
|
694
|
+
return row;
|
|
666
695
|
}
|
|
667
696
|
translateQuery(query, meta) {
|
|
668
697
|
const controls = query.controls;
|
|
669
698
|
return {
|
|
670
|
-
filter:
|
|
699
|
+
filter: this.translateFilter(query.filter, meta),
|
|
671
700
|
controls: {
|
|
672
701
|
...controls,
|
|
673
702
|
$with: void 0,
|
|
@@ -679,7 +708,7 @@ var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
|
679
708
|
translateAggregateQuery(query, meta) {
|
|
680
709
|
const controls = query.controls;
|
|
681
710
|
return {
|
|
682
|
-
filter:
|
|
711
|
+
filter: this.translateFilter(query.filter ?? {}, meta),
|
|
683
712
|
controls: {
|
|
684
713
|
...controls,
|
|
685
714
|
$with: void 0,
|
|
@@ -698,6 +727,11 @@ var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
|
698
727
|
return this.formatWriteValues(data, meta);
|
|
699
728
|
}
|
|
700
729
|
translatePatchKeys(update, meta) {
|
|
730
|
+
if (meta.columnMap.size > 0) {
|
|
731
|
+
const result = {};
|
|
732
|
+
for (const key of Object.keys(update)) result[meta.columnMap.get(key) ?? key] = update[key];
|
|
733
|
+
return this.formatWriteValues(result, meta);
|
|
734
|
+
}
|
|
701
735
|
return this.formatWriteValues(update, meta);
|
|
702
736
|
}
|
|
703
737
|
};
|
|
@@ -712,6 +746,12 @@ var DocumentFieldMapper = class extends FieldMappingStrategy {
|
|
|
712
746
|
var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
713
747
|
reconstructFromRead(row, meta) {
|
|
714
748
|
if (!meta.requiresMappings) return this.applyFromStorageFormatters(this.coerceFieldValues(row, meta), meta);
|
|
749
|
+
if (meta.onlyColumnRenames) {
|
|
750
|
+
this.coerceFieldValues(row, meta);
|
|
751
|
+
this.applyFromStorageFormatters(row, meta);
|
|
752
|
+
this.reverseColumnRenames(row, meta);
|
|
753
|
+
return row;
|
|
754
|
+
}
|
|
715
755
|
const result = {};
|
|
716
756
|
const fromFmts = meta.fromStorageFormatters;
|
|
717
757
|
for (const physical of Object.keys(row)) {
|
|
@@ -794,6 +834,15 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
794
834
|
};
|
|
795
835
|
}
|
|
796
836
|
/**
|
|
837
|
+
* Overrides the base `translateFilter` to use `leafByLogical` for key resolution
|
|
838
|
+
* (handles flattened nested paths like `contact.email` → `contact__email`).
|
|
839
|
+
*/
|
|
840
|
+
translateFilter(filter, meta) {
|
|
841
|
+
if (!filter || typeof filter !== "object") return filter;
|
|
842
|
+
if (!meta.requiresMappings && !meta.toStorageFormatters) return filter;
|
|
843
|
+
return this.translateFilterWithRename(filter, meta);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
797
846
|
* Translates filter with key renaming from logical to physical names.
|
|
798
847
|
* Used by the relational query path where field paths must be mapped
|
|
799
848
|
* to `__`-separated column names.
|
|
@@ -813,7 +862,8 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
813
862
|
prepareForWrite(payload, meta, adapter) {
|
|
814
863
|
const data = { ...payload };
|
|
815
864
|
this.prepareCommon(data, meta, adapter);
|
|
816
|
-
if (!meta.requiresMappings)
|
|
865
|
+
if (!meta.requiresMappings) return this.formatWriteValues(data, meta);
|
|
866
|
+
if (meta.onlyColumnRenames) {
|
|
817
867
|
for (const [logical, physical] of meta.columnMap.entries()) if (logical in data) {
|
|
818
868
|
data[physical] = data[logical];
|
|
819
869
|
delete data[logical];
|
|
@@ -824,6 +874,11 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
824
874
|
}
|
|
825
875
|
translatePatchKeys(update, meta) {
|
|
826
876
|
if (!meta.requiresMappings && !meta.toStorageFormatters) return update;
|
|
877
|
+
if (meta.onlyColumnRenames && !meta.toStorageFormatters) {
|
|
878
|
+
const result = {};
|
|
879
|
+
for (const key of Object.keys(update)) result[meta.leafByLogical.get(key)?.physicalName ?? key] = update[key];
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
827
882
|
const result = {};
|
|
828
883
|
const updateKeys = Object.keys(update);
|
|
829
884
|
for (const key of updateKeys) {
|
|
@@ -921,8 +976,8 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
921
976
|
* When a parent object is null/undefined, set all its flattened children to null.
|
|
922
977
|
*/
|
|
923
978
|
setFlattenedChildrenNull(parentPath, result, meta) {
|
|
924
|
-
const
|
|
925
|
-
for (const
|
|
979
|
+
const children = meta.childrenByParent.get(parentPath);
|
|
980
|
+
if (children) for (const physical of children) result[physical] = null;
|
|
926
981
|
}
|
|
927
982
|
};
|
|
928
983
|
//#endregion
|
|
@@ -2108,28 +2163,42 @@ function createDbValidatorPlugin() {
|
|
|
2108
2163
|
const isFrom = def.metadata.has("db.rel.from");
|
|
2109
2164
|
const isVia = def.metadata.has("db.rel.via");
|
|
2110
2165
|
if (isTo || isFrom || isVia) return handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia);
|
|
2111
|
-
if (dbCtx.mode === "patch"
|
|
2112
|
-
if (
|
|
2113
|
-
|
|
2114
|
-
|
|
2166
|
+
if (dbCtx.mode === "patch") {
|
|
2167
|
+
if (isDbFieldOp(value)) {
|
|
2168
|
+
if (dbCtx.flatMap && !isFieldOpAllowed(ctx.path, dbCtx.flatMap, dbCtx.navFields)) {
|
|
2169
|
+
ctx.error("Field operations ($inc/$dec/$mul) are not supported inside @db.json fields or nested objects without @db.patch.strategy \"merge\"");
|
|
2170
|
+
return false;
|
|
2171
|
+
}
|
|
2172
|
+
if (!(def.type.kind === "" && def.type.designType === "number")) {
|
|
2173
|
+
ctx.error("Field operations ($inc/$dec/$mul) can only be applied to numeric fields");
|
|
2174
|
+
return false;
|
|
2175
|
+
}
|
|
2176
|
+
return true;
|
|
2177
|
+
}
|
|
2178
|
+
if (def.type.kind === "array" && dbCtx.flatMap) {
|
|
2179
|
+
const flatEntry = dbCtx.flatMap.get(ctx.path);
|
|
2180
|
+
if (flatEntry?.metadata?.has("db.__topLevelArray") && !flatEntry.metadata.has("db.json")) return handleArrayPatch(ctx, def, value);
|
|
2115
2181
|
}
|
|
2116
|
-
return true;
|
|
2117
|
-
}
|
|
2118
|
-
if (dbCtx.mode === "patch" && def.type.kind === "array" && dbCtx.flatMap) {
|
|
2119
|
-
const flatEntry = dbCtx.flatMap.get(ctx.path);
|
|
2120
|
-
if (flatEntry?.metadata?.has("db.__topLevelArray") && !flatEntry.metadata.has("db.json")) return handleArrayPatch(ctx, def, value);
|
|
2121
2182
|
}
|
|
2122
2183
|
};
|
|
2123
2184
|
}
|
|
2124
2185
|
/**
|
|
2125
|
-
* Checks whether a field op is valid at `path
|
|
2126
|
-
*
|
|
2127
|
-
*
|
|
2186
|
+
* Checks whether a field op is valid at `path`.
|
|
2187
|
+
*
|
|
2188
|
+
* Rejects when:
|
|
2189
|
+
* - The field itself is `@db.json` (ops on opaque blobs are meaningless).
|
|
2190
|
+
* - Any ancestor is `@db.json`.
|
|
2191
|
+
* - Any ancestor is a nested object without `@db.patch.strategy "merge"`.
|
|
2192
|
+
*
|
|
2193
|
+
* Accepts immediately when an ancestor is a navigation field (TO/FROM/VIA) —
|
|
2194
|
+
* the nested data is extracted and validated against its own table separately.
|
|
2128
2195
|
*/
|
|
2129
|
-
function isFieldOpAllowed(path, flatMap) {
|
|
2196
|
+
function isFieldOpAllowed(path, flatMap, navFields) {
|
|
2197
|
+
if (flatMap.get(path)?.metadata.has("db.json")) return false;
|
|
2130
2198
|
let pos = path.length;
|
|
2131
2199
|
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) {
|
|
2132
2200
|
const ancestor = path.slice(0, pos);
|
|
2201
|
+
if (navFields?.has(ancestor)) return true;
|
|
2133
2202
|
const entry = flatMap.get(ancestor);
|
|
2134
2203
|
if (!entry) continue;
|
|
2135
2204
|
if (entry.metadata.has("db.json")) return false;
|
|
@@ -2138,8 +2207,8 @@ function isFieldOpAllowed(path, flatMap) {
|
|
|
2138
2207
|
return true;
|
|
2139
2208
|
}
|
|
2140
2209
|
function handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia) {
|
|
2141
|
-
const
|
|
2142
|
-
const fieldName =
|
|
2210
|
+
const dotIdx = ctx.path.lastIndexOf(".");
|
|
2211
|
+
const fieldName = dotIdx === -1 ? ctx.path : ctx.path.slice(dotIdx + 1);
|
|
2143
2212
|
if (value === null) {
|
|
2144
2213
|
ctx.error(`Cannot process null navigation property '${fieldName}'`);
|
|
2145
2214
|
return false;
|
|
@@ -2576,7 +2645,10 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2576
2645
|
if (!canNest && this._meta.navFields.size > 0) checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
2577
2646
|
return enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
|
|
2578
2647
|
const items = payloads.map((p) => this._applyDefaults({ ...p }));
|
|
2579
|
-
validateBatch(this.getValidator("insert"), items, {
|
|
2648
|
+
validateBatch(this.getValidator("insert"), items, {
|
|
2649
|
+
mode: "insert",
|
|
2650
|
+
navFields: this._meta.navFields
|
|
2651
|
+
});
|
|
2580
2652
|
const host = this;
|
|
2581
2653
|
if (canNest) await batchInsertNestedTo(host, items, maxDepth, depth);
|
|
2582
2654
|
const prepared = [];
|
|
@@ -2616,7 +2688,10 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2616
2688
|
return enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
|
|
2617
2689
|
const items = payloads.map((p) => this._applyDefaults({ ...p }));
|
|
2618
2690
|
const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
|
|
2619
|
-
validateBatch(this.getValidator("bulkReplace"), items, {
|
|
2691
|
+
validateBatch(this.getValidator("bulkReplace"), items, {
|
|
2692
|
+
mode: "replace",
|
|
2693
|
+
navFields: this._meta.navFields
|
|
2694
|
+
});
|
|
2620
2695
|
const host = this;
|
|
2621
2696
|
if (canNest) await batchReplaceNestedTo(host, items, maxDepth, depth);
|
|
2622
2697
|
await this._integrity.validateForeignKeys(items, this._meta, this._fkLookupResolver, this._writeTableResolver);
|
|
@@ -2625,7 +2700,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2625
2700
|
let modifiedCount = 0;
|
|
2626
2701
|
for (const data of items) {
|
|
2627
2702
|
for (const navField of this._meta.navFields) delete data[navField];
|
|
2628
|
-
const filter = this.
|
|
2703
|
+
const filter = this._extractRecordFilter(data);
|
|
2629
2704
|
const prepared = this._fieldMapper.prepareForWrite(data, this._meta, this.adapter);
|
|
2630
2705
|
const result = await this.adapter.replaceOne(this._fieldMapper.translateFilter(filter, this._meta), prepared);
|
|
2631
2706
|
matchedCount += result.matchedCount;
|
|
@@ -2662,7 +2737,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2662
2737
|
return enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
|
|
2663
2738
|
validateBatch(this.getValidator("bulkUpdate"), payloads, {
|
|
2664
2739
|
mode: "patch",
|
|
2665
|
-
flatMap: this.flatMap
|
|
2740
|
+
flatMap: this.flatMap,
|
|
2741
|
+
navFields: this._meta.navFields
|
|
2666
2742
|
});
|
|
2667
2743
|
const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
|
|
2668
2744
|
const host = this;
|
|
@@ -2673,8 +2749,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2673
2749
|
for (const payload of payloads) {
|
|
2674
2750
|
const data = { ...payload };
|
|
2675
2751
|
for (const navField of this._meta.navFields) delete data[navField];
|
|
2676
|
-
const filter = this.
|
|
2677
|
-
for (const
|
|
2752
|
+
const filter = this._extractRecordFilter(data);
|
|
2753
|
+
for (const key of Object.keys(filter)) delete data[key];
|
|
2678
2754
|
if (_isEmptyObj(data)) {
|
|
2679
2755
|
matchedCount += 1;
|
|
2680
2756
|
modifiedCount += 0;
|
|
@@ -2685,7 +2761,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2685
2761
|
if (this.adapter.supportsNativePatch()) {
|
|
2686
2762
|
const ops = separateFieldOps(data);
|
|
2687
2763
|
const translatedOps = ops ? _translateOpsKeys(ops, this._meta) : void 0;
|
|
2688
|
-
|
|
2764
|
+
const translatedData = this._fieldMapper.translatePatchKeys(data, this._meta);
|
|
2765
|
+
result = await this.adapter.nativePatch(translatedFilter, translatedData, translatedOps);
|
|
2689
2766
|
} else {
|
|
2690
2767
|
const update = decomposePatch(data, this);
|
|
2691
2768
|
const ops = separateFieldOps(update);
|
|
@@ -2730,10 +2807,11 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2730
2807
|
async updateMany(filter, data) {
|
|
2731
2808
|
this._ensureBuilt();
|
|
2732
2809
|
await this._integrity.validateForeignKeys([data], this._meta, this._fkLookupResolver, this._writeTableResolver, true);
|
|
2733
|
-
const
|
|
2734
|
-
const ops = separateFieldOps(
|
|
2810
|
+
const update = decomposePatch({ ...data }, this);
|
|
2811
|
+
const ops = separateFieldOps(update);
|
|
2735
2812
|
const translatedOps = ops ? _translateOpsKeys(ops, this._meta) : void 0;
|
|
2736
|
-
|
|
2813
|
+
const translatedUpdate = this._fieldMapper.translatePatchKeys(update, this._meta);
|
|
2814
|
+
return enrichFkViolation(this._meta, () => this.adapter.updateMany(this._fieldMapper.translateFilter(filter, this._meta), translatedUpdate, translatedOps));
|
|
2737
2815
|
}
|
|
2738
2816
|
async replaceMany(filter, data) {
|
|
2739
2817
|
this._ensureBuilt();
|
|
@@ -2786,24 +2864,55 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2786
2864
|
return data;
|
|
2787
2865
|
}
|
|
2788
2866
|
/**
|
|
2789
|
-
* Extracts
|
|
2867
|
+
* Extracts a record-identifying filter from a payload.
|
|
2868
|
+
*
|
|
2869
|
+
* Resolution order:
|
|
2870
|
+
* 1. Primary key field(s) — if all PK fields are present in the payload.
|
|
2871
|
+
* 2. Single-field unique index — first `@db.index.unique` field found.
|
|
2872
|
+
* 3. Compound unique index — first compound unique index whose fields are all present.
|
|
2873
|
+
*
|
|
2874
|
+
* Throws when no identifying fields can be found.
|
|
2790
2875
|
*/
|
|
2791
|
-
|
|
2876
|
+
_extractRecordFilter(payload) {
|
|
2792
2877
|
const pkFields = this.primaryKeys;
|
|
2878
|
+
if (pkFields.length > 0) {
|
|
2879
|
+
let allPresent = true;
|
|
2880
|
+
for (const field of pkFields) if (payload[field] === void 0) {
|
|
2881
|
+
allPresent = false;
|
|
2882
|
+
break;
|
|
2883
|
+
}
|
|
2884
|
+
if (allPresent) {
|
|
2885
|
+
const filter = {};
|
|
2886
|
+
for (const field of pkFields) filter[field] = this._prepareFilterValue(field, payload[field]);
|
|
2887
|
+
return filter;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
for (const prop of this.uniqueProps) if (payload[prop] !== void 0) return { [prop]: this._prepareFilterValue(prop, payload[prop]) };
|
|
2891
|
+
for (const index of this._meta.indexes.values()) {
|
|
2892
|
+
if (index.type !== "unique" || index.fields.length < 2) continue;
|
|
2893
|
+
let allPresent = true;
|
|
2894
|
+
for (const indexField of index.fields) if (payload[indexField.name] === void 0) {
|
|
2895
|
+
allPresent = false;
|
|
2896
|
+
break;
|
|
2897
|
+
}
|
|
2898
|
+
if (allPresent) {
|
|
2899
|
+
const filter = {};
|
|
2900
|
+
for (const indexField of index.fields) filter[indexField.name] = this._prepareFilterValue(indexField.name, payload[indexField.name]);
|
|
2901
|
+
return filter;
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2793
2904
|
if (pkFields.length === 0) throw new DbError("NOT_FOUND", [{
|
|
2794
2905
|
path: "",
|
|
2795
2906
|
message: "No primary key defined — cannot extract filter"
|
|
2796
2907
|
}]);
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
}
|
|
2806
|
-
return filter;
|
|
2908
|
+
throw new DbError("NOT_FOUND", [{
|
|
2909
|
+
path: pkFields[0],
|
|
2910
|
+
message: `Missing primary key field "${pkFields[0]}" in payload`
|
|
2911
|
+
}]);
|
|
2912
|
+
}
|
|
2913
|
+
_prepareFilterValue(field, value) {
|
|
2914
|
+
const fieldType = this.flatMap.get(field);
|
|
2915
|
+
return fieldType ? this.adapter.prepareId(value, fieldType) : value;
|
|
2807
2916
|
}
|
|
2808
2917
|
/**
|
|
2809
2918
|
* Pre-validate items (type validation + FK constraints) without inserting them.
|
|
@@ -2814,7 +2923,12 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
2814
2923
|
*/
|
|
2815
2924
|
async preValidateItems(items, opts) {
|
|
2816
2925
|
this._ensureBuilt();
|
|
2817
|
-
|
|
2926
|
+
const validator = this.getValidator("insert");
|
|
2927
|
+
const ctx = {
|
|
2928
|
+
mode: "insert",
|
|
2929
|
+
navFields: this._meta.navFields
|
|
2930
|
+
};
|
|
2931
|
+
validateBatch(validator, items.map((raw) => this._applyDefaults({ ...raw })), ctx);
|
|
2818
2932
|
await this._integrity.validateForeignKeys(items, this._meta, this._fkLookupResolver, this._writeTableResolver, false, opts?.excludeFkTargetTable);
|
|
2819
2933
|
}
|
|
2820
2934
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_nested_writer = require("./nested-writer-
|
|
3
|
-
const require_db_view = require("./db-view-
|
|
2
|
+
const require_nested_writer = require("./nested-writer-BIQ6EfaR.cjs");
|
|
3
|
+
const require_db_view = require("./db-view-CcUjETIF.cjs");
|
|
4
4
|
const require_ops = require("./ops.cjs");
|
|
5
5
|
let _uniqu_core = require("@uniqu/core");
|
|
6
6
|
//#region src/table/db-space.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { $ as Uniquery, A as TDbForeignKey, B as TExistingColumn, C as TCascadeTarget, D as TDbDefaultValue, E as TDbDefaultFn, F as TDbInsertResult, G as TMetadataOverrides, H as TFkLookupResolver, I as TDbReferentialAction, J as TTableOptionDiff, K as TSearchIndexInfo, L as TDbRelation, M as TDbIndexField, N as TDbIndexType, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TypedWithRelation, R as TDbStorageType, S as TCascadeResolver, T as TDbCollation, U as TFkLookupTarget, V as TExistingTableOption, W as TIdDescriptor, X as TValueFormatterPair, Y as TTableResolver, Z as TWriteTableResolver, _ as DbQuery, a as FieldMappingStrategy, b as NavPropsOf, c as NoopLogger, d as AggregateExpr, et as UniqueryControls, f as AggregateFn, g as DbControls, h as AtscriptDbWritable, i as DocumentFieldMapper, j as TDbIndex, k as TDbFieldMeta, l as TGenericLogger, m as AggregateResult, n as DbResponse, nt as UniquSelect, o as BaseDbAdapter, p as AggregateQuery, q as TSyncColumnResult, r as resolveDesignType, s as TableMetadata, t as AtscriptDbReadable, tt as WithRelation, u as AggregateControls, v as FieldOpsFor, w as TColumnDiff, x as OwnPropsOf, y as FilterExpr, z as TDbUpdateResult } from "./db-readable-
|
|
1
|
+
import { $ as Uniquery, A as TDbForeignKey, B as TExistingColumn, C as TCascadeTarget, D as TDbDefaultValue, E as TDbDefaultFn, F as TDbInsertResult, G as TMetadataOverrides, H as TFkLookupResolver, I as TDbReferentialAction, J as TTableOptionDiff, K as TSearchIndexInfo, L as TDbRelation, M as TDbIndexField, N as TDbIndexType, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TypedWithRelation, R as TDbStorageType, S as TCascadeResolver, T as TDbCollation, U as TFkLookupTarget, V as TExistingTableOption, W as TIdDescriptor, X as TValueFormatterPair, Y as TTableResolver, Z as TWriteTableResolver, _ as DbQuery, a as FieldMappingStrategy, b as NavPropsOf, c as NoopLogger, d as AggregateExpr, et as UniqueryControls, f as AggregateFn, g as DbControls, h as AtscriptDbWritable, i as DocumentFieldMapper, j as TDbIndex, k as TDbFieldMeta, l as TGenericLogger, m as AggregateResult, n as DbResponse, nt as UniquSelect, o as BaseDbAdapter, p as AggregateQuery, q as TSyncColumnResult, r as resolveDesignType, s as TableMetadata, t as AtscriptDbReadable, tt as WithRelation, u as AggregateControls, v as FieldOpsFor, w as TColumnDiff, x as OwnPropsOf, y as FilterExpr, z as TDbUpdateResult } from "./db-readable-C3C-qPcP.cjs";
|
|
2
2
|
import { d as getDbFieldOp, f as isDbFieldOp, l as TDbFieldOp, p as separateFieldOps, u as TFieldOps } from "./ops-DXJ4Zw0P.cjs";
|
|
3
|
-
import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-
|
|
4
|
-
import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-
|
|
3
|
+
import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-Dgf-CphX.cjs";
|
|
4
|
+
import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-Cz4QoDWg.cjs";
|
|
5
5
|
import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, Uniquery as Uniquery$1, computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
|
|
6
6
|
import { TAtscriptAnnotatedType, TAtscriptTypeArray } from "@atscript/typescript/utils";
|
|
7
7
|
|
|
@@ -16,6 +16,11 @@ declare class RelationalFieldMapper extends FieldMappingStrategy {
|
|
|
16
16
|
reconstructFromRead(row: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
17
17
|
translateQuery(query: Uniquery$1, meta: TableMetadata): DbQuery;
|
|
18
18
|
translateAggregateQuery(query: AggregateQuery$1, meta: TableMetadata): DbQuery;
|
|
19
|
+
/**
|
|
20
|
+
* Overrides the base `translateFilter` to use `leafByLogical` for key resolution
|
|
21
|
+
* (handles flattened nested paths like `contact.email` → `contact__email`).
|
|
22
|
+
*/
|
|
23
|
+
translateFilter(filter: FilterExpr$1, meta: TableMetadata): FilterExpr$1;
|
|
19
24
|
/**
|
|
20
25
|
* Translates filter with key renaming from logical to physical names.
|
|
21
26
|
* Used by the relational query path where field paths must be mapped
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { $ as Uniquery, A as TDbForeignKey, B as TExistingColumn, C as TCascadeTarget, D as TDbDefaultValue, E as TDbDefaultFn, F as TDbInsertResult, G as TMetadataOverrides, H as TFkLookupResolver, I as TDbReferentialAction, J as TTableOptionDiff, K as TSearchIndexInfo, L as TDbRelation, M as TDbIndexField, N as TDbIndexType, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TypedWithRelation, R as TDbStorageType, S as TCascadeResolver, T as TDbCollation, U as TFkLookupTarget, V as TExistingTableOption, W as TIdDescriptor, X as TValueFormatterPair, Y as TTableResolver, Z as TWriteTableResolver, _ as DbQuery, a as FieldMappingStrategy, b as NavPropsOf, c as NoopLogger, d as AggregateExpr, et as UniqueryControls, f as AggregateFn, g as DbControls, h as AtscriptDbWritable, i as DocumentFieldMapper, j as TDbIndex, k as TDbFieldMeta, l as TGenericLogger, m as AggregateResult, n as DbResponse, nt as UniquSelect, o as BaseDbAdapter, p as AggregateQuery, q as TSyncColumnResult, r as resolveDesignType, s as TableMetadata, t as AtscriptDbReadable, tt as WithRelation, u as AggregateControls, v as FieldOpsFor, w as TColumnDiff, x as OwnPropsOf, y as FilterExpr, z as TDbUpdateResult } from "./db-readable-
|
|
1
|
+
import { $ as Uniquery, A as TDbForeignKey, B as TExistingColumn, C as TCascadeTarget, D as TDbDefaultValue, E as TDbDefaultFn, F as TDbInsertResult, G as TMetadataOverrides, H as TFkLookupResolver, I as TDbReferentialAction, J as TTableOptionDiff, K as TSearchIndexInfo, L as TDbRelation, M as TDbIndexField, N as TDbIndexType, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TypedWithRelation, R as TDbStorageType, S as TCascadeResolver, T as TDbCollation, U as TFkLookupTarget, V as TExistingTableOption, W as TIdDescriptor, X as TValueFormatterPair, Y as TTableResolver, Z as TWriteTableResolver, _ as DbQuery, a as FieldMappingStrategy, b as NavPropsOf, c as NoopLogger, d as AggregateExpr, et as UniqueryControls, f as AggregateFn, g as DbControls, h as AtscriptDbWritable, i as DocumentFieldMapper, j as TDbIndex, k as TDbFieldMeta, l as TGenericLogger, m as AggregateResult, n as DbResponse, nt as UniquSelect, o as BaseDbAdapter, p as AggregateQuery, q as TSyncColumnResult, r as resolveDesignType, s as TableMetadata, t as AtscriptDbReadable, tt as WithRelation, u as AggregateControls, v as FieldOpsFor, w as TColumnDiff, x as OwnPropsOf, y as FilterExpr, z as TDbUpdateResult } from "./db-readable-Cc868K54.mjs";
|
|
2
2
|
import { d as getDbFieldOp, f as isDbFieldOp, l as TDbFieldOp, p as separateFieldOps, u as TFieldOps } from "./ops-BdRAFLKY.mjs";
|
|
3
|
-
import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-
|
|
4
|
-
import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-
|
|
3
|
+
import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-crCFv3DF.mjs";
|
|
4
|
+
import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BLMVdi9z.mjs";
|
|
5
5
|
import { TAtscriptAnnotatedType, TAtscriptTypeArray } from "@atscript/typescript/utils";
|
|
6
6
|
import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, Uniquery as Uniquery$1, computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
|
|
7
7
|
|
|
@@ -16,6 +16,11 @@ declare class RelationalFieldMapper extends FieldMappingStrategy {
|
|
|
16
16
|
reconstructFromRead(row: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
|
|
17
17
|
translateQuery(query: Uniquery$1, meta: TableMetadata): DbQuery;
|
|
18
18
|
translateAggregateQuery(query: AggregateQuery$1, meta: TableMetadata): DbQuery;
|
|
19
|
+
/**
|
|
20
|
+
* Overrides the base `translateFilter` to use `leafByLogical` for key resolution
|
|
21
|
+
* (handles flattened nested paths like `contact.email` → `contact__email`).
|
|
22
|
+
*/
|
|
23
|
+
translateFilter(filter: FilterExpr$1, meta: TableMetadata): FilterExpr$1;
|
|
19
24
|
/**
|
|
20
25
|
* Translates filter with key renaming from logical to physical names.
|
|
21
26
|
* Used by the relational query path where field paths must be mapped
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { h as DbError } from "./nested-writer-
|
|
2
|
-
import { _ as NoopLogger, a as getKeyProps, c as IntegrityStrategy, d as resolveDesignType, f as RelationalFieldMapper, g as TableMetadata, h as UniquSelect, i as createDbValidatorPlugin, l as NativeIntegrity, m as FieldMappingStrategy, n as AtscriptDbTable, o as ApplicationIntegrity, p as DocumentFieldMapper, r as decomposePatch, s as BaseDbAdapter, t as AtscriptDbView, u as AtscriptDbReadable } from "./db-view-
|
|
1
|
+
import { h as DbError } from "./nested-writer-CNDyhg2L.mjs";
|
|
2
|
+
import { _ as NoopLogger, a as getKeyProps, c as IntegrityStrategy, d as resolveDesignType, f as RelationalFieldMapper, g as TableMetadata, h as UniquSelect, i as createDbValidatorPlugin, l as NativeIntegrity, m as FieldMappingStrategy, n as AtscriptDbTable, o as ApplicationIntegrity, p as DocumentFieldMapper, r as decomposePatch, s as BaseDbAdapter, t as AtscriptDbView, u as AtscriptDbReadable } from "./db-view-DSK76uc9.mjs";
|
|
3
3
|
import { getDbFieldOp, isDbFieldOp, separateFieldOps } from "./ops.mjs";
|
|
4
4
|
import { computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
|
|
5
5
|
//#region src/table/db-space.ts
|
|
@@ -316,7 +316,7 @@ async function batchPatchNestedTo(host, items, maxDepth, depth) {
|
|
|
316
316
|
const patch = { ...nested };
|
|
317
317
|
let fkValue = fk.localFields.length === 1 ? item[fk.localFields[0]] : void 0;
|
|
318
318
|
if (fkValue === void 0) {
|
|
319
|
-
const pkFilter = host.
|
|
319
|
+
const pkFilter = host._extractRecordFilter(item);
|
|
320
320
|
const current = await host.findOne({
|
|
321
321
|
filter: pkFilter,
|
|
322
322
|
controls: {}
|
|
@@ -316,7 +316,7 @@ async function batchPatchNestedTo(host, items, maxDepth, depth) {
|
|
|
316
316
|
const patch = { ...nested };
|
|
317
317
|
let fkValue = fk.localFields.length === 1 ? item[fk.localFields[0]] : void 0;
|
|
318
318
|
if (fkValue === void 0) {
|
|
319
|
-
const pkFilter = host.
|
|
319
|
+
const pkFilter = host._extractRecordFilter(item);
|
|
320
320
|
const current = await host.findOne({
|
|
321
321
|
filter: pkFilter,
|
|
322
322
|
controls: {}
|
package/dist/rel.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_relation_loader = require("./relation-loader-CRC5LcqM.cjs");
|
|
3
|
-
const require_nested_writer = require("./nested-writer-
|
|
3
|
+
const require_nested_writer = require("./nested-writer-BIQ6EfaR.cjs");
|
|
4
4
|
const require_relation_helpers = require("./relation-helpers-BYvsE1tR.cjs");
|
|
5
5
|
exports.batchInsertNestedFrom = require_nested_writer.batchInsertNestedFrom;
|
|
6
6
|
exports.batchInsertNestedTo = require_nested_writer.batchInsertNestedTo;
|
package/dist/rel.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as TDbForeignKey, L as TDbRelation, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata } from "./db-readable-
|
|
2
|
-
import { t as DbValidationContext } from "./db-validator-plugin-
|
|
1
|
+
import { A as TDbForeignKey, L as TDbRelation, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata } from "./db-readable-C3C-qPcP.cjs";
|
|
2
|
+
import { t as DbValidationContext } from "./db-validator-plugin-Cz4QoDWg.cjs";
|
|
3
3
|
import { FilterExpr, WithRelation } from "@uniqu/core";
|
|
4
4
|
import { Validator } from "@atscript/typescript/utils";
|
|
5
5
|
|
|
@@ -55,7 +55,7 @@ interface TNestedWriterHost {
|
|
|
55
55
|
_findRemoteFK(targetTable: {
|
|
56
56
|
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
57
57
|
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
58
|
-
|
|
58
|
+
_extractRecordFilter(payload: Record<string, unknown>): FilterExpr;
|
|
59
59
|
findOne(query: {
|
|
60
60
|
filter: FilterExpr;
|
|
61
61
|
controls: Record<string, never>;
|
package/dist/rel.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as TDbForeignKey, L as TDbRelation, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata } from "./db-readable-
|
|
2
|
-
import { t as DbValidationContext } from "./db-validator-plugin-
|
|
1
|
+
import { A as TDbForeignKey, L as TDbRelation, Y as TTableResolver, Z as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata } from "./db-readable-Cc868K54.mjs";
|
|
2
|
+
import { t as DbValidationContext } from "./db-validator-plugin-BLMVdi9z.mjs";
|
|
3
3
|
import { Validator } from "@atscript/typescript/utils";
|
|
4
4
|
import { FilterExpr, WithRelation } from "@uniqu/core";
|
|
5
5
|
|
|
@@ -55,7 +55,7 @@ interface TNestedWriterHost {
|
|
|
55
55
|
_findRemoteFK(targetTable: {
|
|
56
56
|
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
57
57
|
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
58
|
-
|
|
58
|
+
_extractRecordFilter(payload: Record<string, unknown>): FilterExpr;
|
|
59
59
|
findOne(query: {
|
|
60
60
|
filter: FilterExpr;
|
|
61
61
|
controls: Record<string, never>;
|
package/dist/rel.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { t as loadRelationsImpl } from "./relation-loader-BEOTXNcq.mjs";
|
|
2
|
-
import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, i as batchPatchNestedFrom, l as batchReplaceNestedVia, n as batchInsertNestedTo, o as batchPatchNestedVia, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-
|
|
2
|
+
import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, i as batchPatchNestedFrom, l as batchReplaceNestedVia, n as batchInsertNestedTo, o as batchPatchNestedVia, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-CNDyhg2L.mjs";
|
|
3
3
|
import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-CLasawQq.mjs";
|
|
4
4
|
export { batchInsertNestedFrom, batchInsertNestedTo, batchInsertNestedVia, batchPatchNestedFrom, batchPatchNestedTo, batchPatchNestedVia, batchReplaceNestedFrom, batchReplaceNestedTo, batchReplaceNestedVia, checkDepthOverflow, findFKForRelation, findRemoteFK, loadRelationsImpl, preValidateNestedFrom, resolveRelationTargetTable, validateBatch };
|
package/dist/sync.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./nested-writer-
|
|
3
|
-
const require_db_view = require("./db-view-
|
|
2
|
+
require("./nested-writer-BIQ6EfaR.cjs");
|
|
3
|
+
const require_db_view = require("./db-view-CcUjETIF.cjs");
|
|
4
4
|
//#region src/schema/schema-hash.ts
|
|
5
5
|
/** Extracts sorted field snapshots from a readable's field descriptors. */
|
|
6
6
|
function extractFieldSnapshots(fields, typeMapper) {
|
package/dist/sync.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as TDbForeignKey, B as TExistingColumn, D as TDbDefaultValue, J as TTableOptionDiff, R as TDbStorageType, V as TExistingTableOption, k as TDbFieldMeta, l as TGenericLogger, t as AtscriptDbReadable, w as TColumnDiff } from "./db-readable-
|
|
2
|
-
import { r as AtscriptDbView, t as DbSpace } from "./db-space-
|
|
1
|
+
import { A as TDbForeignKey, B as TExistingColumn, D as TDbDefaultValue, J as TTableOptionDiff, R as TDbStorageType, V as TExistingTableOption, k as TDbFieldMeta, l as TGenericLogger, t as AtscriptDbReadable, w as TColumnDiff } from "./db-readable-C3C-qPcP.cjs";
|
|
2
|
+
import { r as AtscriptDbView, t as DbSpace } from "./db-space-Dgf-CphX.cjs";
|
|
3
3
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/schema/sync-entry.d.ts
|
package/dist/sync.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as TDbForeignKey, B as TExistingColumn, D as TDbDefaultValue, J as TTableOptionDiff, R as TDbStorageType, V as TExistingTableOption, k as TDbFieldMeta, l as TGenericLogger, t as AtscriptDbReadable, w as TColumnDiff } from "./db-readable-
|
|
2
|
-
import { r as AtscriptDbView, t as DbSpace } from "./db-space-
|
|
1
|
+
import { A as TDbForeignKey, B as TExistingColumn, D as TDbDefaultValue, J as TTableOptionDiff, R as TDbStorageType, V as TExistingTableOption, k as TDbFieldMeta, l as TGenericLogger, t as AtscriptDbReadable, w as TColumnDiff } from "./db-readable-Cc868K54.mjs";
|
|
2
|
+
import { r as AtscriptDbView, t as DbSpace } from "./db-space-crCFv3DF.mjs";
|
|
3
3
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/schema/sync-entry.d.ts
|
package/dist/sync.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as NoopLogger } from "./db-view-
|
|
1
|
+
import { _ as NoopLogger } from "./db-view-DSK76uc9.mjs";
|
|
2
2
|
//#region src/schema/schema-hash.ts
|
|
3
3
|
/** Extracts sorted field snapshots from a readable's field descriptors. */
|
|
4
4
|
function extractFieldSnapshots(fields, typeMapper) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.42",
|
|
4
4
|
"description": "Database adapter utilities for atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -62,14 +62,14 @@
|
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@atscript/core": "^0.1.
|
|
66
|
-
"@atscript/typescript": "^0.1.
|
|
65
|
+
"@atscript/core": "^0.1.41",
|
|
66
|
+
"@atscript/typescript": "^0.1.41",
|
|
67
67
|
"@uniqu/core": "^0.1.2",
|
|
68
|
-
"unplugin-atscript": "^0.1.
|
|
68
|
+
"unplugin-atscript": "^0.1.41"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@atscript/core": "^0.1.
|
|
72
|
-
"@atscript/typescript": "^0.1.
|
|
71
|
+
"@atscript/core": "^0.1.41",
|
|
72
|
+
"@atscript/typescript": "^0.1.41",
|
|
73
73
|
"@uniqu/core": "^0.1.2"
|
|
74
74
|
},
|
|
75
75
|
"scripts": {
|