@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.
- package/dist/db-error-Cepx-RsQ.mjs +29 -0
- package/dist/db-error-D8tQhNgM.cjs +40 -0
- package/dist/{db-readable-GBjlsJXR.d.mts → db-readable-B9Irll4q.d.mts} +151 -116
- package/dist/{db-readable-ktHqF277.d.cts → db-readable-Cwjbk6o_.d.cts} +150 -115
- package/dist/{db-space-DB0MT6B3.d.cts → db-space-CAcEB09Y.d.mts} +15 -2
- package/dist/{db-space-DpaXOdRp.d.mts → db-space-CQg17vv7.d.cts} +15 -2
- package/dist/{db-validator-plugin-Cz4QoDWg.d.cts → db-validator-plugin-DDvYyv5t.d.mts} +10 -0
- package/dist/{db-validator-plugin-KC4aNIQq.d.mts → db-validator-plugin-DRGMCEn3.d.cts} +10 -0
- package/dist/{db-view-DjDKgytJ.cjs → db-view-DIGN4079.cjs} +81 -19
- package/dist/{db-view-B1j_IKSf.mjs → db-view-DNwX6_4x.mjs} +72 -10
- package/dist/index.cjs +5 -4
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +4 -3
- package/dist/{nested-writer-CNDyhg2L.mjs → nested-writer-CT2rLURx.mjs} +9 -16
- package/dist/{nested-writer-BIQ6EfaR.cjs → nested-writer-v_LPR1yJ.cjs} +14 -27
- package/dist/ops.d.mts +1 -1
- package/dist/plugin.cjs +99 -9
- package/dist/plugin.mjs +99 -9
- package/dist/rel.cjs +1 -1
- package/dist/rel.d.cts +2 -2
- package/dist/rel.d.mts +2 -2
- package/dist/rel.mjs +1 -1
- package/dist/shared.cjs +1 -1
- package/dist/shared.mjs +1 -1
- package/dist/sync.cjs +5 -5
- package/dist/sync.d.cts +2 -2
- package/dist/sync.d.mts +2 -2
- package/dist/sync.mjs +5 -5
- package/dist/{validator-D_7Fqzs4.mjs → validator-BB5h1Le3.mjs} +42 -0
- package/dist/{validator-0iGuvGOD.cjs → validator-BIuw_T0k.cjs} +42 -0
- package/dist/validator.cjs +1 -1
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +3 -3
- package/dist/validator.mjs +1 -1
- package/package.json +6 -6
- /package/dist/{control-D1QdBO21.cjs → control-CDnwVj4q.cjs} +0 -0
- /package/dist/{control-DBd_ff5-.mjs → control-ExEKWYBE.mjs} +0 -0
- /package/dist/{ops-DcHDxrjX.d.mts → ops-C61kelof.d.mts} +0 -0
- /package/dist/{validation-utils-BiG3pLP0.cjs → validation-utils-B9WJv9aH.cjs} +0 -0
- /package/dist/{validation-utils-aNrgK-cj.mjs → validation-utils-Bh7RVrVl.mjs} +0 -0
- /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 {
|
|
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 {
|
|
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 {
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2415
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|