@atscript/db 0.1.54 → 0.1.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/db-error-Cepx-RsQ.mjs +29 -0
  2. package/dist/db-error-D8tQhNgM.cjs +40 -0
  3. package/dist/{db-readable-GBjlsJXR.d.mts → db-readable-B9Irll4q.d.mts} +151 -116
  4. package/dist/{db-readable-ktHqF277.d.cts → db-readable-Cwjbk6o_.d.cts} +150 -115
  5. package/dist/{db-space-DB0MT6B3.d.cts → db-space-CAcEB09Y.d.mts} +15 -2
  6. package/dist/{db-space-DpaXOdRp.d.mts → db-space-CQg17vv7.d.cts} +15 -2
  7. package/dist/{db-validator-plugin-Cz4QoDWg.d.cts → db-validator-plugin-DDvYyv5t.d.mts} +10 -0
  8. package/dist/{db-validator-plugin-KC4aNIQq.d.mts → db-validator-plugin-DRGMCEn3.d.cts} +10 -0
  9. package/dist/{db-view-DjDKgytJ.cjs → db-view-DIGN4079.cjs} +81 -19
  10. package/dist/{db-view-B1j_IKSf.mjs → db-view-DNwX6_4x.mjs} +72 -10
  11. package/dist/index.cjs +5 -4
  12. package/dist/index.d.cts +5 -5
  13. package/dist/index.d.mts +7 -7
  14. package/dist/index.mjs +4 -3
  15. package/dist/{nested-writer-CNDyhg2L.mjs → nested-writer-CT2rLURx.mjs} +9 -16
  16. package/dist/{nested-writer-BIQ6EfaR.cjs → nested-writer-v_LPR1yJ.cjs} +14 -27
  17. package/dist/ops.d.mts +1 -1
  18. package/dist/plugin.cjs +99 -9
  19. package/dist/plugin.mjs +99 -9
  20. package/dist/rel.cjs +1 -1
  21. package/dist/rel.d.cts +2 -2
  22. package/dist/rel.d.mts +2 -2
  23. package/dist/rel.mjs +1 -1
  24. package/dist/shared.cjs +1 -1
  25. package/dist/shared.mjs +1 -1
  26. package/dist/sync.cjs +5 -5
  27. package/dist/sync.d.cts +2 -2
  28. package/dist/sync.d.mts +2 -2
  29. package/dist/sync.mjs +5 -5
  30. package/dist/{validator-D_7Fqzs4.mjs → validator-BB5h1Le3.mjs} +42 -0
  31. package/dist/{validator-0iGuvGOD.cjs → validator-BIuw_T0k.cjs} +42 -0
  32. package/dist/validator.cjs +1 -1
  33. package/dist/validator.d.cts +1 -1
  34. package/dist/validator.d.mts +3 -3
  35. package/dist/validator.mjs +1 -1
  36. package/package.json +6 -6
  37. /package/dist/{control-D1QdBO21.cjs → control-CDnwVj4q.cjs} +0 -0
  38. /package/dist/{control-DBd_ff5-.mjs → control-ExEKWYBE.mjs} +0 -0
  39. /package/dist/{ops-DcHDxrjX.d.mts → ops-C61kelof.d.mts} +0 -0
  40. /package/dist/{validation-utils-BiG3pLP0.cjs → validation-utils-B9WJv9aH.cjs} +0 -0
  41. /package/dist/{validation-utils-aNrgK-cj.mjs → validation-utils-Bh7RVrVl.mjs} +0 -0
  42. /package/dist/{validator-BeXlQISk.d.mts → validator-DttN2e5_.d.mts} +0 -0
@@ -44,6 +44,120 @@ declare class UniquSelect {
44
44
  get hasAggregates(): boolean;
45
45
  }
46
46
  //#endregion
47
+ //#region src/logger.d.ts
48
+ interface TGenericLogger {
49
+ error(...messages: any[]): void;
50
+ warn(...messages: any[]): void;
51
+ log(...messages: any[]): void;
52
+ info(...messages: any[]): void;
53
+ debug(...messages: any[]): void;
54
+ }
55
+ declare const NoopLogger: TGenericLogger;
56
+ //#endregion
57
+ //#region src/table/table-metadata.d.ts
58
+ /**
59
+ * Computed metadata for a database table or view.
60
+ *
61
+ * Contains all field metadata, physical mapping indexes, relation definitions,
62
+ * and constraint information derived from Atscript annotations. Built lazily
63
+ * on first access via {@link build}, then immutable.
64
+ *
65
+ * This class owns the build pipeline that was previously part of
66
+ * `AtscriptDbReadable._flatten()`. The Readable delegates all metadata
67
+ * access to this class.
68
+ */
69
+ declare class TableMetadata {
70
+ readonly nestedObjects: boolean;
71
+ flatMap: Map<string, TAtscriptAnnotatedType>;
72
+ fieldDescriptors: readonly TDbFieldMeta[];
73
+ primaryKeys: string[];
74
+ originalMetaIdFields: string[];
75
+ indexes: Map<string, TDbIndex>;
76
+ foreignKeys: Map<string, TDbForeignKey>;
77
+ relations: Map<string, TDbRelation>;
78
+ navFields: Set<string>;
79
+ ignoredFields: Set<string>;
80
+ uniqueProps: Set<string>;
81
+ defaults: Map<string, TDbDefaultValue>;
82
+ columnMap: Map<string, string>;
83
+ dimensions: string[];
84
+ measures: string[];
85
+ pathToPhysical: Map<string, string>;
86
+ physicalToPath: Map<string, string>;
87
+ flattenedParents: Set<string>;
88
+ jsonFields: Set<string>;
89
+ selectExpansion: Map<string, string[]>;
90
+ booleanFields: Set<string>;
91
+ decimalFields: Set<string>;
92
+ allPhysicalFields: string[];
93
+ /** Precomputed parent path → child physical column names for fast null-setting. */
94
+ childrenByParent: Map<string, string[]>;
95
+ requiresMappings: boolean;
96
+ /** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
97
+ onlyColumnRenames: boolean;
98
+ toStorageFormatters?: Map<string, (value: unknown) => unknown>;
99
+ fromStorageFormatters?: Map<string, (value: unknown) => unknown>;
100
+ /** Leaf field descriptors indexed by physical column name (read path). */
101
+ leafByPhysical: Map<string, TDbFieldMeta>;
102
+ /** Leaf field descriptors indexed by logical path (write/patch/filter paths). */
103
+ leafByLogical: Map<string, TDbFieldMeta>;
104
+ private _built;
105
+ private _collateMap;
106
+ private _columnFromMap;
107
+ constructor(nestedObjects: boolean);
108
+ get isBuilt(): boolean;
109
+ /**
110
+ * Runs the full metadata compilation pipeline. Called once by
111
+ * `AtscriptDbReadable._ensureBuilt()` on first metadata access.
112
+ *
113
+ * Pipeline steps:
114
+ * 1. `adapter.onBeforeFlatten(type)` — adapter hook
115
+ * 2. `flattenAnnotatedType()` — collect field tuples, detect nav fields eagerly
116
+ * 3. Replay non-nav-descendant tuples through annotation scanning + adapter.onFieldScanned
117
+ * 4. Classify fields and build path maps (skipped for nested-objects adapters)
118
+ * 5. `adapter.getMetadataOverrides()` → `_applyOverrides()` (PK/unique/inject adjustments)
119
+ * 6. Build field descriptors (TDbFieldMeta[])
120
+ * 7. Build leaf field indexes (skipped for nested-objects adapters)
121
+ * 8. Finalize indexes (resolve field names to physical)
122
+ * 9. `adapter.onAfterFlatten()` — adapter hook (read-only bookkeeping)
123
+ * 10. Build allPhysicalFields list
124
+ */
125
+ build(type: TAtscriptAnnotatedType<TAtscriptTypeObject>, adapter: BaseDbAdapter, logger: TGenericLogger): void;
126
+ /**
127
+ * Applies adapter-provided metadata overrides atomically.
128
+ * Processing order: injectFields → removePrimaryKeys → addPrimaryKeys → addUniqueFields.
129
+ */
130
+ private _applyOverrides;
131
+ /**
132
+ * Scans `@db.*` and `@meta.id` annotations on a field during flattening.
133
+ */
134
+ private _scanGenericAnnotations;
135
+ private _addIndexField;
136
+ /**
137
+ * Classifies each field as column, flattened, json, or parent-object.
138
+ * Builds the bidirectional pathToPhysical / physicalToPath maps.
139
+ */
140
+ private _classifyFields;
141
+ /** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
142
+ private _flattenedPrefix;
143
+ /**
144
+ * Indexes `fieldDescriptors` into two lookup maps for unified
145
+ * read/write field classification in the RelationalFieldMapper.
146
+ */
147
+ private _buildLeafIndexes;
148
+ /**
149
+ * Builds field descriptors, physical-name lookup, and value formatters.
150
+ * Called once during build() — everything it needs
151
+ * (flatMap, indexes, columnMap, etc.) is already populated.
152
+ */
153
+ private _buildFieldDescriptors;
154
+ /**
155
+ * Resolves `fkTargetField` for FK fields in field descriptors.
156
+ */
157
+ private _resolveFkTargetFields;
158
+ private _finalizeIndexes;
159
+ }
160
+ //#endregion
47
161
  //#region src/types.d.ts
48
162
  /** Controls with resolved projection. Used in the adapter interface. */
49
163
  interface DbControls extends Omit<UniqueryControls, "$select"> {
@@ -86,6 +200,34 @@ interface TMetaResponse {
86
200
  relations: TRelationInfo[];
87
201
  fields: Record<string, TFieldMeta>;
88
202
  type: TSerializedAnnotatedType;
203
+ actions: TDbActionInfo[];
204
+ }
205
+ /** Where the action applies on the UI. */
206
+ type TDbActionLevel = "table" | "row" | "rows";
207
+ /** Semantic intent the UI maps to its own visual language (color, prominence). */
208
+ type TDbActionIntent = "positive" | "negative" | "primary" | "secondary";
209
+ /** How the UI client should handle the action when invoked. */
210
+ type TDbActionProcessor = "backend" | "navigate" | "custom";
211
+ /**
212
+ * Single action descriptor in the `/meta` envelope. Flat shape — `processor`
213
+ * is a string discriminator; `value` is its sibling and is always populated.
214
+ *
215
+ * - `processor: 'backend'` — UI POSTs to `value` (full HTTP path).
216
+ * - `processor: 'navigate'` — UI routes to `value` (URL template; `$1` is the row PK).
217
+ * - `processor: 'custom'` — UI dispatches `value` as an event name (defaults to action `name`).
218
+ */
219
+ interface TDbActionInfo {
220
+ name: string;
221
+ label: string;
222
+ level: TDbActionLevel;
223
+ processor: TDbActionProcessor;
224
+ value: string;
225
+ icon?: string;
226
+ intent?: TDbActionIntent;
227
+ description?: string;
228
+ order?: number;
229
+ default?: boolean;
230
+ promptText?: string;
89
231
  }
90
232
  interface TDbInsertResult {
91
233
  insertedId: unknown;
@@ -290,6 +432,7 @@ interface AtscriptDbTableLike {
290
432
  primaryKeys: readonly string[];
291
433
  relations: ReadonlyMap<string, TDbRelation>;
292
434
  foreignKeys: ReadonlyMap<string, TDbForeignKey>;
435
+ getMetadata(): TableMetadata;
293
436
  }
294
437
  /** Minimal writable table interface for nested creation/update. */
295
438
  interface AtscriptDbWritable {
@@ -375,120 +518,6 @@ interface TDbRelation {
375
518
  viaType?: () => TAtscriptAnnotatedType;
376
519
  }
377
520
  //#endregion
378
- //#region src/logger.d.ts
379
- interface TGenericLogger {
380
- error(...messages: any[]): void;
381
- warn(...messages: any[]): void;
382
- log(...messages: any[]): void;
383
- info(...messages: any[]): void;
384
- debug(...messages: any[]): void;
385
- }
386
- declare const NoopLogger: TGenericLogger;
387
- //#endregion
388
- //#region src/table/table-metadata.d.ts
389
- /**
390
- * Computed metadata for a database table or view.
391
- *
392
- * Contains all field metadata, physical mapping indexes, relation definitions,
393
- * and constraint information derived from Atscript annotations. Built lazily
394
- * on first access via {@link build}, then immutable.
395
- *
396
- * This class owns the build pipeline that was previously part of
397
- * `AtscriptDbReadable._flatten()`. The Readable delegates all metadata
398
- * access to this class.
399
- */
400
- declare class TableMetadata {
401
- readonly nestedObjects: boolean;
402
- flatMap: Map<string, TAtscriptAnnotatedType>;
403
- fieldDescriptors: readonly TDbFieldMeta[];
404
- primaryKeys: string[];
405
- originalMetaIdFields: string[];
406
- indexes: Map<string, TDbIndex>;
407
- foreignKeys: Map<string, TDbForeignKey>;
408
- relations: Map<string, TDbRelation>;
409
- navFields: Set<string>;
410
- ignoredFields: Set<string>;
411
- uniqueProps: Set<string>;
412
- defaults: Map<string, TDbDefaultValue>;
413
- columnMap: Map<string, string>;
414
- dimensions: string[];
415
- measures: string[];
416
- pathToPhysical: Map<string, string>;
417
- physicalToPath: Map<string, string>;
418
- flattenedParents: Set<string>;
419
- jsonFields: Set<string>;
420
- selectExpansion: Map<string, string[]>;
421
- booleanFields: Set<string>;
422
- decimalFields: Set<string>;
423
- allPhysicalFields: string[];
424
- /** Precomputed parent path → child physical column names for fast null-setting. */
425
- childrenByParent: Map<string, string[]>;
426
- requiresMappings: boolean;
427
- /** True when the only mappings needed are simple `@db.column` renames (no nesting/JSON). */
428
- onlyColumnRenames: boolean;
429
- toStorageFormatters?: Map<string, (value: unknown) => unknown>;
430
- fromStorageFormatters?: Map<string, (value: unknown) => unknown>;
431
- /** Leaf field descriptors indexed by physical column name (read path). */
432
- leafByPhysical: Map<string, TDbFieldMeta>;
433
- /** Leaf field descriptors indexed by logical path (write/patch/filter paths). */
434
- leafByLogical: Map<string, TDbFieldMeta>;
435
- private _built;
436
- private _collateMap;
437
- private _columnFromMap;
438
- constructor(nestedObjects: boolean);
439
- get isBuilt(): boolean;
440
- /**
441
- * Runs the full metadata compilation pipeline. Called once by
442
- * `AtscriptDbReadable._ensureBuilt()` on first metadata access.
443
- *
444
- * Pipeline steps:
445
- * 1. `adapter.onBeforeFlatten(type)` — adapter hook
446
- * 2. `flattenAnnotatedType()` — collect field tuples, detect nav fields eagerly
447
- * 3. Replay non-nav-descendant tuples through annotation scanning + adapter.onFieldScanned
448
- * 4. Classify fields and build path maps (skipped for nested-objects adapters)
449
- * 5. `adapter.getMetadataOverrides()` → `_applyOverrides()` (PK/unique/inject adjustments)
450
- * 6. Build field descriptors (TDbFieldMeta[])
451
- * 7. Build leaf field indexes (skipped for nested-objects adapters)
452
- * 8. Finalize indexes (resolve field names to physical)
453
- * 9. `adapter.onAfterFlatten()` — adapter hook (read-only bookkeeping)
454
- * 10. Build allPhysicalFields list
455
- */
456
- build(type: TAtscriptAnnotatedType<TAtscriptTypeObject>, adapter: BaseDbAdapter, logger: TGenericLogger): void;
457
- /**
458
- * Applies adapter-provided metadata overrides atomically.
459
- * Processing order: injectFields → removePrimaryKeys → addPrimaryKeys → addUniqueFields.
460
- */
461
- private _applyOverrides;
462
- /**
463
- * Scans `@db.*` and `@meta.id` annotations on a field during flattening.
464
- */
465
- private _scanGenericAnnotations;
466
- private _addIndexField;
467
- /**
468
- * Classifies each field as column, flattened, json, or parent-object.
469
- * Builds the bidirectional pathToPhysical / physicalToPath maps.
470
- */
471
- private _classifyFields;
472
- /** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
473
- private _flattenedPrefix;
474
- /**
475
- * Indexes `fieldDescriptors` into two lookup maps for unified
476
- * read/write field classification in the RelationalFieldMapper.
477
- */
478
- private _buildLeafIndexes;
479
- /**
480
- * Builds field descriptors, physical-name lookup, and value formatters.
481
- * Called once during build() — everything it needs
482
- * (flatMap, indexes, columnMap, etc.) is already populated.
483
- */
484
- private _buildFieldDescriptors;
485
- /**
486
- * Resolves `fkTargetField` for FK fields in field descriptors.
487
- */
488
- private _resolveFkTargetFields;
489
- private _finalizeIndexes;
490
- }
491
- //#endregion
492
521
  //#region src/base-adapter.d.ts
493
522
  /**
494
523
  * Abstract base class for database adapters.
@@ -1106,6 +1135,12 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
1106
1135
  constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver | undefined);
1107
1136
  /** Ensures metadata is built. Called before any metadata access. */
1108
1137
  protected _ensureBuilt(): void;
1138
+ /**
1139
+ * Built table metadata. Triggers a lazy build on first access — safe to call
1140
+ * from peer tables that need this one's relations / nav fields before any
1141
+ * operation has run against it directly.
1142
+ */
1143
+ getMetadata(): TableMetadata;
1109
1144
  protected _ensureSearchable(): void;
1110
1145
  /** Whether this readable is a view (overridden in AtscriptDbView). */
1111
1146
  get isView(): boolean;
@@ -1280,4 +1315,4 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
1280
1315
  }, thisTableName: string, alias?: string): TDbForeignKey | undefined;
1281
1316
  }
1282
1317
  //#endregion
1283
- export { TValueFormatterPair as $, TDbForeignKey as A, TExistingColumn as B, TCascadeTarget as C, TDbDefaultValue as D, TDbDefaultFn as E, TDbInsertResult as F, TIdDescriptor as G, TFieldMeta as H, TDbReferentialAction as I, TRelationInfo as J, TMetaResponse as K, TDbRelation as L, TDbIndexField as M, TDbIndexType as N, TDbDeleteResult as O, TDbInsertManyResult as P, TTableResolver as Q, TDbStorageType as R, TCascadeResolver as S, TDbCollation as T, TFkLookupResolver as U, TExistingTableOption as V, TFkLookupTarget as W, TSyncColumnResult as X, TSearchIndexInfo as Y, TTableOptionDiff as Z, DbQuery as _, FieldMappingStrategy as a, UniquSelect as at, NavPropsOf$1 as b, NoopLogger as c, AggregateExpr$1 as d, TWriteTableResolver as et, AggregateFn as f, DbControls as g, AtscriptDbWritable as h, DocumentFieldMapper as i, WithRelation$1 as it, TDbIndex as j, TDbFieldMeta as k, TGenericLogger as l, AggregateResult as m, DbResponse as n, Uniquery$1 as nt, BaseDbAdapter as o, AggregateQuery$1 as p, TMetadataOverrides as q, resolveDesignType as r, UniqueryControls$1 as rt, TableMetadata as s, AtscriptDbReadable as t, TypedWithRelation as tt, AggregateControls as u, FieldOpsFor as v, TColumnDiff as w, OwnPropsOf$1 as x, FilterExpr$1 as y, TDbUpdateResult as z };
1318
+ export { TTableResolver as $, TDbFieldMeta as A, TDbUpdateResult as B, TDbActionIntent as C, TDbDefaultFn as D, TDbCollation as E, TDbInsertManyResult as F, TFkLookupTarget as G, TExistingTableOption as H, TDbInsertResult as I, TMetadataOverrides as J, TIdDescriptor as K, TDbReferentialAction as L, TDbIndex as M, TDbIndexField as N, TDbDefaultValue as O, TDbIndexType as P, TTableOptionDiff as Q, TDbRelation as R, TDbActionInfo as S, TDbActionProcessor as T, TFieldMeta as U, TExistingColumn as V, TFkLookupResolver as W, TSearchIndexInfo as X, TRelationInfo as Y, TSyncColumnResult as Z, NavPropsOf$1 as _, FieldMappingStrategy as a, WithRelation$1 as at, TCascadeTarget as b, AggregateExpr$1 as c, TGenericLogger as ct, AggregateResult as d, TValueFormatterPair as et, AtscriptDbWritable as f, FilterExpr$1 as g, FieldOpsFor as h, DocumentFieldMapper as i, UniqueryControls$1 as it, TDbForeignKey as j, TDbDeleteResult as k, AggregateFn as l, UniquSelect as lt, DbQuery as m, DbResponse as n, TypedWithRelation as nt, BaseDbAdapter as o, TableMetadata as ot, DbControls as p, TMetaResponse as q, resolveDesignType as r, Uniquery$1 as rt, AggregateControls as s, NoopLogger as st, AtscriptDbReadable as t, TWriteTableResolver as tt, AggregateQuery$1 as u, OwnPropsOf$1 as v, TDbActionLevel as w, TColumnDiff as x, TCascadeResolver as y, TDbStorageType as z };
@@ -1,6 +1,6 @@
1
- import { F as TDbInsertResult, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TTableResolver, S as TCascadeResolver, U as TFkLookupResolver, et as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata, t as AtscriptDbReadable, z as TDbUpdateResult } from "./db-readable-ktHqF277.cjs";
2
- import { FilterExpr } from "@uniqu/core";
1
+ import { $ as TTableResolver, B as TDbUpdateResult, F as TDbInsertManyResult, I as TDbInsertResult, W as TFkLookupResolver, ct as TGenericLogger, k as TDbDeleteResult, o as BaseDbAdapter, ot as TableMetadata, t as AtscriptDbReadable, tt as TWriteTableResolver, y as TCascadeResolver } from "./db-readable-B9Irll4q.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
  /**
@@ -29,6 +29,7 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
29
29
  protected _fkLookupResolver?: TFkLookupResolver;
30
30
  protected readonly _integrity: IntegrityStrategy;
31
31
  protected readonly validators: Map<string, Validator<T, DataType>>;
32
+ private _fromDepthMap?;
32
33
  constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver, _writeTableResolver?: TWriteTableResolver);
33
34
  /**
34
35
  * Sets the cascade resolver for application-level cascade deletes.
@@ -141,6 +142,18 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
141
142
  */
142
143
  protected _extractRecordFilter(payload: Record<string, unknown>): FilterExpr;
143
144
  private _prepareFilterValue;
145
+ /**
146
+ * Lazy — builds a `normalized-path → from-depth` map from `this._meta.flatMap`
147
+ * on first use. Only paths reachable through an unbroken chain of `db.rel.from`
148
+ * nav fields from the root are included (chains crossing `to`/`via` are excluded).
149
+ */
150
+ private _getFromDepthMap;
151
+ /**
152
+ * Populate the depth-limit bundle on a `DbValidationContext`. Only the root
153
+ * write call (`depth === 0`) enforces — nested re-entries leave `depthCheck`
154
+ * unset so the full tree is validated once at the root.
155
+ */
156
+ private _applyDepthCtx;
144
157
  /**
145
158
  * Pre-validate items (type validation + FK constraints) without inserting them.
146
159
  * Used by parent tables to validate FROM children before the main insert,
@@ -1,6 +1,6 @@
1
- import { F as TDbInsertResult, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TTableResolver, S as TCascadeResolver, U as TFkLookupResolver, et as TWriteTableResolver, l as TGenericLogger, o as BaseDbAdapter, s as TableMetadata, t as AtscriptDbReadable, z as TDbUpdateResult } from "./db-readable-GBjlsJXR.mjs";
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 { $ as TTableResolver, B as TDbUpdateResult, F as TDbInsertManyResult, I as TDbInsertResult, W as TFkLookupResolver, ct as TGenericLogger, k as TDbDeleteResult, o as BaseDbAdapter, ot as TableMetadata, t as AtscriptDbReadable, tt as TWriteTableResolver, y as TCascadeResolver } from "./db-readable-Cwjbk6o_.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
  /**
@@ -29,6 +29,7 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
29
29
  protected _fkLookupResolver?: TFkLookupResolver;
30
30
  protected readonly _integrity: IntegrityStrategy;
31
31
  protected readonly validators: Map<string, Validator<T, DataType>>;
32
+ private _fromDepthMap?;
32
33
  constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver, _writeTableResolver?: TWriteTableResolver);
33
34
  /**
34
35
  * Sets the cascade resolver for application-level cascade deletes.
@@ -141,6 +142,18 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
141
142
  */
142
143
  protected _extractRecordFilter(payload: Record<string, unknown>): FilterExpr;
143
144
  private _prepareFilterValue;
145
+ /**
146
+ * Lazy — builds a `normalized-path → from-depth` map from `this._meta.flatMap`
147
+ * on first use. Only paths reachable through an unbroken chain of `db.rel.from`
148
+ * nav fields from the root are included (chains crossing `to`/`via` are excluded).
149
+ */
150
+ private _getFromDepthMap;
151
+ /**
152
+ * Populate the depth-limit bundle on a `DbValidationContext`. Only the root
153
+ * write call (`depth === 0`) enforces — nested re-entries leave `depthCheck`
154
+ * unset so the full tree is validated once at the root.
155
+ */
156
+ private _applyDepthCtx;
144
157
  /**
145
158
  * Pre-validate items (type validation + FK constraints) without inserting them.
146
159
  * Used by parent tables to validate FROM children before the main insert,
@@ -7,6 +7,16 @@ interface DbValidationContext {
7
7
  flatMap?: Map<string, TAtscriptAnnotatedType>;
8
8
  /** Precomputed nav field names — used to skip field-op validation inside TO/FROM/VIA relations. */
9
9
  navFields?: ReadonlySet<string>;
10
+ /**
11
+ * Depth-limit enforcement bundle. Set only on the root write call; nested
12
+ * re-entries leave it unset so the root's check isn't repeated. `limit` is
13
+ * `@db.depth.limit N`; `fromDepthMap` maps normalized paths (no array
14
+ * indices) to from-chain depth.
15
+ */
16
+ depthCheck?: {
17
+ limit: number;
18
+ fromDepthMap: ReadonlyMap<string, number>;
19
+ };
10
20
  }
11
21
  /**
12
22
  * Validator plugin for database operations.
@@ -7,6 +7,16 @@ interface DbValidationContext {
7
7
  flatMap?: Map<string, TAtscriptAnnotatedType>;
8
8
  /** Precomputed nav field names — used to skip field-op validation inside TO/FROM/VIA relations. */
9
9
  navFields?: ReadonlySet<string>;
10
+ /**
11
+ * Depth-limit enforcement bundle. Set only on the root write call; nested
12
+ * re-entries leave it unset so the root's check isn't repeated. `limit` is
13
+ * `@db.depth.limit N`; `fromDepthMap` maps normalized paths (no array
14
+ * indices) to from-chain depth.
15
+ */
16
+ depthCheck?: {
17
+ limit: number;
18
+ fromDepthMap: ReadonlyMap<string, number>;
19
+ };
10
20
  }
11
21
  /**
12
22
  * Validator plugin for database operations.
@@ -1,8 +1,9 @@
1
- const require_nested_writer = require("./nested-writer-BIQ6EfaR.cjs");
1
+ const require_db_error = require("./db-error-D8tQhNgM.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");
5
- const require_validator = require("./validator-0iGuvGOD.cjs");
5
+ const require_validator = require("./validator-BIuw_T0k.cjs");
6
+ const require_nested_writer = require("./nested-writer-v_LPR1yJ.cjs");
6
7
  let _atscript_typescript_utils = require("@atscript/typescript/utils");
7
8
  let node_async_hooks = require("node:async_hooks");
8
9
  //#region src/logger.ts
@@ -1096,8 +1097,17 @@ var AtscriptDbReadable = class {
1096
1097
  _ensureBuilt() {
1097
1098
  if (!this._meta.isBuilt) this._meta.build(this.type, this.adapter, this.logger);
1098
1099
  }
1100
+ /**
1101
+ * Built table metadata. Triggers a lazy build on first access — safe to call
1102
+ * from peer tables that need this one's relations / nav fields before any
1103
+ * operation has run against it directly.
1104
+ */
1105
+ getMetadata() {
1106
+ this._ensureBuilt();
1107
+ return this._meta;
1108
+ }
1099
1109
  _ensureSearchable() {
1100
- if (!this.adapter.isSearchable()) throw new require_nested_writer.DbError("INVALID_QUERY", [{
1110
+ if (!this.adapter.isSearchable()) throw new require_db_error.DbError("INVALID_QUERY", [{
1101
1111
  path: "$search",
1102
1112
  message: `Table "${this.tableName}" has no search indexes defined`
1103
1113
  }]);
@@ -1295,7 +1305,7 @@ var AtscriptDbReadable = class {
1295
1305
  const { $groupBy, $select } = query.controls;
1296
1306
  if ($select) {
1297
1307
  const groupBySet = new Set($groupBy);
1298
- for (const item of $select) if (typeof item === "string" && !groupBySet.has(item)) throw new require_nested_writer.DbError("INVALID_QUERY", [{
1308
+ for (const item of $select) if (typeof item === "string" && !groupBySet.has(item)) throw new require_db_error.DbError("INVALID_QUERY", [{
1299
1309
  path: "$select",
1300
1310
  message: `Plain field "${item}" in $select must also appear in $groupBy`
1301
1311
  }]);
@@ -1304,12 +1314,12 @@ var AtscriptDbReadable = class {
1304
1314
  if (dimensions.length > 0 || measures.length > 0) {
1305
1315
  const dimSet = new Set(dimensions);
1306
1316
  const measSet = new Set(measures);
1307
- for (const field of $groupBy) if (!dimSet.has(field)) throw new require_nested_writer.DbError("INVALID_QUERY", [{
1317
+ for (const field of $groupBy) if (!dimSet.has(field)) throw new require_db_error.DbError("INVALID_QUERY", [{
1308
1318
  path: "$groupBy",
1309
1319
  message: `Field "${field}" is not a dimension`
1310
1320
  }]);
1311
1321
  if ($select) {
1312
- for (const item of $select) if (typeof item !== "string" && item.$field !== "*" && !measSet.has(item.$field)) throw new require_nested_writer.DbError("INVALID_QUERY", [{
1322
+ for (const item of $select) if (typeof item !== "string" && item.$field !== "*" && !measSet.has(item.$field)) throw new require_db_error.DbError("INVALID_QUERY", [{
1313
1323
  path: "$select",
1314
1324
  message: `Aggregate field "${item.$field}" is not a measure`
1315
1325
  }]);
@@ -1977,7 +1987,7 @@ var ApplicationIntegrity = class extends IntegrityStrategy {
1977
1987
  if (await target.count(filter) < expectedCount) {
1978
1988
  const sample = firstValues.slice(0, 3).join(", ");
1979
1989
  const suffix = firstValues.length > 3 ? `, ... (${firstValues.length} total)` : "";
1980
- throw new require_nested_writer.DbError("FK_VIOLATION", [{
1990
+ throw new require_db_error.DbError("FK_VIOLATION", [{
1981
1991
  path: fk.fields.join(", "),
1982
1992
  message: `FK constraint violation: "${fk.fields.join(", ")}" references non-existent record in "${fk.targetTable}" (values: ${sample}${suffix})`
1983
1993
  }]);
@@ -1998,7 +2008,7 @@ var ApplicationIntegrity = class extends IntegrityStrategy {
1998
2008
  const parentCtx = cascadeStorage.getStore();
1999
2009
  const visited = parentCtx?.visited ?? /* @__PURE__ */ new Set();
2000
2010
  const depth = (parentCtx?.depth ?? 0) + 1;
2001
- if (depth > MAX_CASCADE_DEPTH) throw new require_nested_writer.DbError("CASCADE_CYCLE", [{
2011
+ if (depth > MAX_CASCADE_DEPTH) throw new require_db_error.DbError("CASCADE_CYCLE", [{
2002
2012
  path: tableName,
2003
2013
  message: `Cascade delete aborted: chain exceeded ${MAX_CASCADE_DEPTH} levels, likely caused by a circular or deeply nested cascade relationship`
2004
2014
  }]);
@@ -2046,7 +2056,7 @@ var ApplicationIntegrity = class extends IntegrityStrategy {
2046
2056
  const childFilter = this.buildCascadeChildFilter(records, target.fk);
2047
2057
  if (!childFilter) continue;
2048
2058
  restrictChecks.push(target.count(childFilter).then((count) => {
2049
- if (count > 0) throw new require_nested_writer.DbError("CONFLICT", [{
2059
+ if (count > 0) throw new require_db_error.DbError("CONFLICT", [{
2050
2060
  path: tableName,
2051
2061
  message: `Cannot delete from "${tableName}": ${count} record(s) in "${target.childTable}" (${target.fk.fields.join(", ")}) reference it (RESTRICT)`
2052
2062
  }]);
@@ -2358,6 +2368,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2358
2368
  _fkLookupResolver;
2359
2369
  _integrity;
2360
2370
  validators = /* @__PURE__ */ new Map();
2371
+ _fromDepthMap;
2361
2372
  constructor(_type, adapter, logger, _tableResolver, _writeTableResolver) {
2362
2373
  super(_type, adapter, logger, _tableResolver);
2363
2374
  if (_writeTableResolver) this._writeTableResolver = _writeTableResolver;
@@ -2411,16 +2422,20 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2411
2422
  */
2412
2423
  async insertMany(payloads, opts) {
2413
2424
  this._ensureBuilt();
2414
- const maxDepth = opts?.maxDepth ?? 3;
2415
- const depth = opts?._depth ?? 0;
2425
+ const { _depth, maxDepth: userMax } = opts ?? {};
2426
+ const maxDepth = userMax ?? 3;
2427
+ const depth = _depth ?? 0;
2416
2428
  const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
2417
2429
  if (!canNest && this._meta.navFields.size > 0) require_nested_writer.checkDepthOverflow(payloads, maxDepth, this._meta);
2418
2430
  return require_nested_writer.enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
2419
2431
  const items = payloads.map((p) => this._applyDefaults({ ...p }));
2420
- require_nested_writer.validateBatch(this.getValidator("insert"), items, {
2432
+ const validator = this.getValidator("insert");
2433
+ const ctx = {
2421
2434
  mode: "insert",
2422
2435
  navFields: this._meta.navFields
2423
- });
2436
+ };
2437
+ this._applyDepthCtx(ctx, depth);
2438
+ require_nested_writer.validateBatch(validator, items, ctx);
2424
2439
  const host = this;
2425
2440
  if (canNest) await require_nested_writer.batchInsertNestedTo(host, items, maxDepth, depth);
2426
2441
  const prepared = [];
@@ -2460,10 +2475,13 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2460
2475
  return require_nested_writer.enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
2461
2476
  const items = payloads.map((p) => this._applyDefaults({ ...p }));
2462
2477
  const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
2463
- require_nested_writer.validateBatch(this.getValidator("bulkReplace"), items, {
2478
+ const validator = this.getValidator("bulkReplace");
2479
+ const ctx = {
2464
2480
  mode: "replace",
2465
2481
  navFields: this._meta.navFields
2466
- });
2482
+ };
2483
+ this._applyDepthCtx(ctx, depth);
2484
+ require_nested_writer.validateBatch(validator, items, ctx);
2467
2485
  const host = this;
2468
2486
  if (canNest) await require_nested_writer.batchReplaceNestedTo(host, items, maxDepth, depth);
2469
2487
  await this._integrity.validateForeignKeys(items, this._meta, this._fkLookupResolver, this._writeTableResolver);
@@ -2507,11 +2525,14 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2507
2525
  const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
2508
2526
  if (!canNest && this._meta.navFields.size > 0) require_nested_writer.checkDepthOverflow(payloads, maxDepth, this._meta);
2509
2527
  return require_nested_writer.enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
2510
- require_nested_writer.validateBatch(this.getValidator("bulkUpdate"), payloads, {
2528
+ const validator = this.getValidator("bulkUpdate");
2529
+ const ctx = {
2511
2530
  mode: "patch",
2512
2531
  flatMap: this.flatMap,
2513
2532
  navFields: this._meta.navFields
2514
- });
2533
+ };
2534
+ this._applyDepthCtx(ctx, depth);
2535
+ require_nested_writer.validateBatch(validator, payloads, ctx);
2515
2536
  const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
2516
2537
  const host = this;
2517
2538
  if (canNest) await require_nested_writer.batchPatchNestedTo(host, payloads, maxDepth, depth);
@@ -2673,11 +2694,11 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2673
2694
  return filter;
2674
2695
  }
2675
2696
  }
2676
- if (pkFields.length === 0) throw new require_nested_writer.DbError("NOT_FOUND", [{
2697
+ if (pkFields.length === 0) throw new require_db_error.DbError("NOT_FOUND", [{
2677
2698
  path: "",
2678
2699
  message: "No primary key defined — cannot extract filter"
2679
2700
  }]);
2680
- throw new require_nested_writer.DbError("NOT_FOUND", [{
2701
+ throw new require_db_error.DbError("NOT_FOUND", [{
2681
2702
  path: pkFields[0],
2682
2703
  message: `Missing primary key field "${pkFields[0]}" in payload`
2683
2704
  }]);
@@ -2687,6 +2708,47 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2687
2708
  return fieldType ? this.adapter.prepareId(value, fieldType) : value;
2688
2709
  }
2689
2710
  /**
2711
+ * Lazy — builds a `normalized-path → from-depth` map from `this._meta.flatMap`
2712
+ * on first use. Only paths reachable through an unbroken chain of `db.rel.from`
2713
+ * nav fields from the root are included (chains crossing `to`/`via` are excluded).
2714
+ */
2715
+ _getFromDepthMap() {
2716
+ if (!this._fromDepthMap) {
2717
+ const out = /* @__PURE__ */ new Map();
2718
+ for (const [path, def] of this._meta.flatMap) {
2719
+ if (!def.metadata?.has("db.rel.from")) continue;
2720
+ const segments = path.split(".");
2721
+ let prefix = "";
2722
+ let depth = 0;
2723
+ let valid = true;
2724
+ for (let i = 0; i < segments.length; i++) {
2725
+ prefix = prefix ? `${prefix}.${segments[i]}` : segments[i];
2726
+ const pmd = this._meta.flatMap.get(prefix)?.metadata;
2727
+ if (pmd?.has("db.rel.to") || pmd?.has("db.rel.via")) {
2728
+ valid = false;
2729
+ break;
2730
+ }
2731
+ if (pmd?.has("db.rel.from")) depth++;
2732
+ }
2733
+ if (valid) out.set(path, depth);
2734
+ }
2735
+ this._fromDepthMap = out;
2736
+ }
2737
+ return this._fromDepthMap;
2738
+ }
2739
+ /**
2740
+ * Populate the depth-limit bundle on a `DbValidationContext`. Only the root
2741
+ * write call (`depth === 0`) enforces — nested re-entries leave `depthCheck`
2742
+ * unset so the full tree is validated once at the root.
2743
+ */
2744
+ _applyDepthCtx(ctx, depth) {
2745
+ if (depth !== 0 || this._meta.navFields.size === 0) return;
2746
+ ctx.depthCheck = {
2747
+ limit: this.type.metadata.get("db.depth.limit") ?? 0,
2748
+ fromDepthMap: this._getFromDepthMap()
2749
+ };
2750
+ }
2751
+ /**
2690
2752
  * Pre-validate items (type validation + FK constraints) without inserting them.
2691
2753
  * Used by parent tables to validate FROM children before the main insert,
2692
2754
  * ensuring errors are caught before the parent is committed.