@atscript/db 0.1.135 → 0.1.137

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 (49) hide show
  1. package/dist/{agg-DKuf_v2L.d.cts → agg-CV7y8nC6.d.cts} +13 -3
  2. package/dist/{agg-BtlGeRfj.d.mts → agg-D5DHsAby.d.mts} +13 -3
  3. package/dist/agg.cjs +35 -3
  4. package/dist/agg.d.cts +2 -2
  5. package/dist/agg.d.mts +2 -2
  6. package/dist/agg.mjs +34 -1
  7. package/dist/aggregate-fns-CGBv3E8S.cjs +87 -0
  8. package/dist/aggregate-fns-CfsveE1w.mjs +58 -0
  9. package/dist/{buckets-GruoVxH5.d.mts → buckets-BFG2RYRW.d.mts} +662 -31
  10. package/dist/{buckets-D6PBXKRJ.d.cts → buckets-C-27xmtq.d.cts} +662 -31
  11. package/dist/column-diff-BmqvgBWw.d.cts +24 -0
  12. package/dist/{db-view-Doh8vPg3.mjs → column-diff-BwOA5101.mjs} +795 -125
  13. package/dist/{db-view-Dm7MpUGB.cjs → column-diff-CgxgFKzx.cjs} +873 -125
  14. package/dist/column-diff-DPkbZIVE.d.mts +24 -0
  15. package/dist/index.cjs +79 -55
  16. package/dist/index.d.cts +12 -10
  17. package/dist/index.d.mts +12 -10
  18. package/dist/index.mjs +30 -11
  19. package/dist/{nested-writer-wk1EFUNY.cjs → nested-writer-BZNCuqI6.cjs} +16 -2
  20. package/dist/{nested-writer-CqL24ojl.mjs → nested-writer-FWD5oOYh.mjs} +11 -3
  21. package/dist/plugin.cjs +217 -77
  22. package/dist/plugin.mjs +217 -78
  23. package/dist/rel.cjs +2 -2
  24. package/dist/rel.d.cts +2 -20
  25. package/dist/rel.d.mts +2 -20
  26. package/dist/rel.mjs +2 -2
  27. package/dist/relation-helpers-D3Zu0Mta.d.mts +30 -0
  28. package/dist/relation-helpers-DxrvS6ar.d.cts +30 -0
  29. package/dist/{relation-loader-BD4xANQJ.cjs → relation-loader-6ZB_5KFq.cjs} +1 -1
  30. package/dist/{relation-loader-B68R1LET.mjs → relation-loader-CTFaZpVa.mjs} +1 -1
  31. package/dist/shared.cjs +5 -1
  32. package/dist/shared.d.cts +16 -3
  33. package/dist/shared.d.mts +16 -3
  34. package/dist/shared.mjs +2 -2
  35. package/dist/sync.cjs +38 -321
  36. package/dist/sync.d.cts +29 -17
  37. package/dist/sync.d.mts +29 -17
  38. package/dist/sync.mjs +6 -289
  39. package/dist/{validation-utils-MWOP1Ts4.mjs → validation-utils-B4h-GW4d.mjs} +29 -7
  40. package/dist/{validation-utils-B7SXPkm7.cjs → validation-utils-Dg0hW6dn.cjs} +52 -6
  41. package/dist/{validator-CewfnGZj.d.mts → validator-Drb2N-YL.d.cts} +1 -1
  42. package/dist/{validator-CewfnGZj.d.cts → validator-Drb2N-YL.d.mts} +1 -1
  43. package/dist/validator.d.cts +1 -1
  44. package/dist/validator.d.mts +1 -1
  45. package/package.json +1 -1
  46. package/dist/agg-CvXDGnKi.mjs +0 -61
  47. package/dist/agg-EcIbAFnJ.cjs +0 -78
  48. package/dist/db-space-9CH5neN7.d.mts +0 -439
  49. package/dist/db-space-BdtBNTeH.d.cts +0 -439
@@ -1,6 +1,6 @@
1
1
  import { f as TFieldOps } from "./ops-AqhV7s9o.cjs";
2
- import { AggregateControls, AggregateExpr, AggregateExpr as AggregateExpr$1, AggregateFn as AggregateFn$1, AggregateQuery, AggregateQuery as AggregateQuery$1, AggregateResult, BucketUnit, FieldOpsFor, FilterExpr, FilterExpr as FilterExpr$1, ResolvedBucket, TypedWithRelation, Uniquery, Uniquery as Uniquery$1, UniqueryControls, UniqueryControls as UniqueryControls$1, UniqueryInsights, WithRelation, WithRelation as WithRelation$1 } from "@uniqu/core";
3
- import { FlatOf, FlatOf as FlatOf$1, NavPropsOf, NavPropsOf as NavPropsOf$1, OwnPropsOf, OwnPropsOf as OwnPropsOf$1, PrimaryKeyOf, PrimaryKeyOf as PrimaryKeyOf$1, TAtscriptAnnotatedType, TAtscriptDataType, TAtscriptTypeObject, TMetadataMap, TSerializedAnnotatedType, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
2
+ import { AggregateControls, AggregateExpr, AggregateExpr as AggregateExpr$1, AggregateFn, AggregateFn as AggregateFn$1, AggregateQuery, AggregateQuery as AggregateQuery$1, AggregateResult, BucketUnit, FieldOpsFor, FilterExpr, FilterExpr as FilterExpr$1, ResolvedBucket, TypedWithRelation, Uniquery, Uniquery as Uniquery$1, UniqueryControls, UniqueryControls as UniqueryControls$1, UniqueryInsights, WithRelation, WithRelation as WithRelation$1 } from "@uniqu/core";
3
+ import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, FlatOf as FlatOf$1, NavPropsOf, NavPropsOf as NavPropsOf$1, OwnPropsOf, OwnPropsOf as OwnPropsOf$1, PrimaryKeyOf, PrimaryKeyOf as PrimaryKeyOf$1, TAtscriptAnnotatedType, TAtscriptDataType, TAtscriptTypeObject, TMetadataMap, TSerializedAnnotatedType, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
4
4
 
5
5
  //#region src/query/uniqu-select.d.ts
6
6
  /**
@@ -15,7 +15,7 @@ import { FlatOf, FlatOf as FlatOf$1, NavPropsOf, NavPropsOf as NavPropsOf$1, Own
15
15
  * An array `$select` holds plain field names and computed entries —
16
16
  * aggregates (`{ $fn, $field }`, {@link aggregates}) and calendar buckets
17
17
  * (`{ $bucket, $field }`, {@link buckets}). Entries arrive normalized
18
- * (`resolveCalendarBuckets` rejects any other shape before translation).
18
+ * (`normalizeComputedSelect` rejects any other shape before translation).
19
19
  */
20
20
  declare class UniquSelect {
21
21
  private static readonly UNRESOLVED;
@@ -101,7 +101,7 @@ declare abstract class FieldMappingStrategy {
101
101
  * equals a field name).
102
102
  *
103
103
  * `buckets` are the query's calendar buckets as the core's normalizer
104
- * resolved them (`resolveCalendarBuckets` — `AtscriptDbReadable.aggregate`
104
+ * resolved them (`normalizeComputedSelect` — `AtscriptDbReadable.aggregate`
105
105
  * runs it before the guards); they reach adapters with `field` made
106
106
  * physical and the source descriptor as `fd`.
107
107
  */
@@ -111,7 +111,10 @@ declare abstract class FieldMappingStrategy {
111
111
  /**
112
112
  * `$select` with its field paths made physical: array-form names and
113
113
  * computed `$field`s (`'*'` kept), or the keys of the object
114
- * (inclusion / exclusion) form.
114
+ * (inclusion / exclusion) form. An aggregate's output alias is fixed
115
+ * (`$as`) from its LOGICAL field first, so a default alias never leaks a
116
+ * physical name (`sum(amount)` over `@db.column 'amount_cents'` stays
117
+ * `sum_amount`); a bucket's alias is already resolved.
115
118
  */
116
119
  protected physicalSelect(select: NonNullable<UniqueryControls["$select"]>, meta: TableMetadata): NonNullable<UniqueryControls["$select"]>;
117
120
  /** `$sort` with physical keys; computed `aliases` (grouped queries) pass through. */
@@ -127,6 +130,8 @@ declare abstract class FieldMappingStrategy {
127
130
  translateFilter(filter: FilterExpr, meta: TableMetadata): FilterExpr;
128
131
  abstract prepareForWrite(payload: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): Record<string, unknown>;
129
132
  abstract translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
133
+ /** `$inc` / `$mul` field-op keys to physical names ({@link physicalPath}). */
134
+ translateOpsKeys(ops: TFieldOps, meta: TableMetadata): TFieldOps;
130
135
  /**
131
136
  * Reverse-maps `@db.column` renames on a row read from storage.
132
137
  * Renames physical keys back to logical names in-place.
@@ -182,6 +187,7 @@ declare class DocumentFieldMapper extends FieldMappingStrategy {
182
187
  protected physicalPath(logical: string, meta: TableMetadata): string;
183
188
  protected renamesPaths(meta: TableMetadata): boolean;
184
189
  prepareForWrite(payload: Record<string, unknown>, meta: TableMetadata, adapter: BaseDbAdapter): Record<string, unknown>;
190
+ /** Patch keys (top-level or decomposed dotted) to document paths. */
185
191
  translatePatchKeys(update: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
186
192
  }
187
193
  //#endregion
@@ -389,7 +395,7 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
389
395
  get measures(): readonly string[];
390
396
  /** Sync method for structural changes: 'drop' (lossy), 'recreate' (lossless), or undefined (manual). */
391
397
  get syncMethod(): "drop" | "recreate" | undefined;
392
- /** Logical → physical column name mapping from `@db.column`. */
398
+ /** Logical → physical column name mapping from `@db.column` (top-level only on document storage). */
393
399
  get columnMap(): ReadonlyMap<string, string>;
394
400
  /** Default values from `@db.default.*`. */
395
401
  get defaults(): ReadonlyMap<string, TDbDefaultValue>;
@@ -478,13 +484,15 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
478
484
  *
479
485
  * Validates:
480
486
  * - `$select` computed entries and calendar buckets (the shared normalizer,
481
- * `resolveCalendarBuckets`: shapes, unit, zone, alias, grouping)
487
+ * `normalizeComputedSelect`: shapes, unit, zone, alias, grouping)
482
488
  * - Plain fields in $select are a subset of $groupBy
483
489
  * - When dimensions/measures are defined (strict mode): $groupBy fields
484
- * must be dimensions, aggregate $field values must be measures (or '*')
490
+ * must be dimensions, aggregate $field values must be measures (or '*';
491
+ * a `countDistinct` field may also be a dimension)
485
492
  * - the path guard (a bucket source must pass `bucketSourceVerdict` —
486
493
  * timestamp type, no JSON ancestor, a dimension in strict mode, an
487
- * adapter with calendar buckets) and the adapter's calendar-bucket units
494
+ * adapter with calendar buckets), the adapter's aggregate functions
495
+ * (`AGG_FN_NOT_SUPPORTED`) and calendar-bucket units
488
496
  * (`BUCKET_NOT_SUPPORTED`)
489
497
  *
490
498
  * Translates field names, delegates to adapter.aggregate(),
@@ -497,6 +505,8 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
497
505
  canFilterField(fd: TDbFieldMeta): boolean;
498
506
  /** Calendar-bucket units the adapter can group by (proxies adapter capability; empty = none). */
499
507
  calendarBucketUnits(): ReadonlySet<BucketUnit>;
508
+ /** Aggregate functions the adapter renders (proxies adapter capability). @since 0.1.136 */
509
+ aggregateFns(): ReadonlySet<AggregateFn>;
500
510
  /** Whether the adapter can sort by a given field (proxies adapter capability). */
501
511
  canSortField(fd: TDbFieldMeta): boolean;
502
512
  /** Returns available search indexes from the adapter. */
@@ -629,6 +639,566 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
629
639
  }, thisTableName: string, alias?: string): TDbForeignKey | undefined;
630
640
  }
631
641
  //#endregion
642
+ //#region src/strategies/integrity.d.ts
643
+ /**
644
+ * Strategy for referential integrity enforcement.
645
+ * Two implementations: {@link NativeIntegrity} (DB handles FK constraints)
646
+ * and `ApplicationIntegrity` (generic layer validates + cascades).
647
+ */
648
+ declare abstract class IntegrityStrategy {
649
+ abstract validateForeignKeys(items: Array<Record<string, unknown>>, meta: TableMetadata, fkLookupResolver: TFkLookupResolver | undefined, writeTableResolver: TWriteTableResolver | undefined, partial?: boolean, excludeTargetTable?: string): Promise<void>;
650
+ abstract cascadeBeforeDelete(filter: FilterExpr, tableName: string, meta: TableMetadata, cascadeResolver: TCascadeResolver, translateFilter: (f: FilterExpr) => FilterExpr, adapter: BaseDbAdapter): Promise<void>;
651
+ abstract needsCascade(cascadeResolver: TCascadeResolver | undefined): boolean;
652
+ }
653
+ /**
654
+ * Integrity strategy for adapters with native FK support (e.g. SQLite, MySQL).
655
+ * All operations are no-ops — the database engine enforces constraints.
656
+ */
657
+ declare class NativeIntegrity extends IntegrityStrategy {
658
+ validateForeignKeys(): Promise<void>;
659
+ cascadeBeforeDelete(): Promise<void>;
660
+ needsCascade(): boolean;
661
+ }
662
+ //#endregion
663
+ //#region src/table/db-table.d.ts
664
+ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>, FlatType = NullableOptional<FlatOf<T>>, A extends BaseDbAdapter = BaseDbAdapter, IdType = PrimaryKeyOf<T>, OwnProps = NullableOptional<OwnPropsOf<T>>, NavType extends Record<string, unknown> = NavPropsOf<T>> extends AtscriptDbReadable<T, DataType, FlatType, A, IdType, OwnProps, NavType> {
665
+ protected _cascadeResolver?: TCascadeResolver;
666
+ protected _fkLookupResolver?: TFkLookupResolver;
667
+ protected readonly _integrity: IntegrityStrategy;
668
+ protected readonly validators: Map<string, Validator<T, DataType>>;
669
+ private _fromDepthMap?;
670
+ constructor(_type: T, adapter: A, logger?: TGenericLogger, _tableResolver?: TTableResolver, _writeTableResolver?: TWriteTableResolver);
671
+ /**
672
+ * Sets the cascade resolver for application-level cascade deletes.
673
+ * Called by DbSpace after table creation.
674
+ */
675
+ setCascadeResolver(resolver: TCascadeResolver): void;
676
+ /**
677
+ * Sets the FK lookup resolver for application-level FK validation.
678
+ * Called by DbSpace after table creation.
679
+ */
680
+ setFkLookupResolver(resolver: TFkLookupResolver): void;
681
+ /**
682
+ * Returns a cached validator for the given purpose.
683
+ * Built with adapter plugins from {@link BaseDbAdapter.getValidatorPlugins}.
684
+ *
685
+ * Standard purposes: `'insert'`, `'update'`, `'patch'`.
686
+ * Adapters may define additional purposes.
687
+ */
688
+ getValidator(purpose: string): Validator<T, DataType>;
689
+ /**
690
+ * Inserts a single record. Delegates to {@link insertMany} for unified
691
+ * nested creation support.
692
+ */
693
+ insertOne(payload: DbPatch<DataType>, opts?: TWriteOptions<DataType>): Promise<TDbInsertResult>;
694
+ /**
695
+ * Inserts multiple records with batch-optimized nested creation.
696
+ *
697
+ * Supports **nested creation**: if payloads include data for navigation
698
+ * fields (`@db.rel.to` / `@db.rel.from`), related records are created
699
+ * automatically in batches. TO dependencies are batch-created first
700
+ * (their PKs become our FKs), FROM dependents are batch-created after
701
+ * (they receive our PKs as their FKs). Fully recursive — nested records
702
+ * with their own nav data trigger further batch inserts at each level.
703
+ * Recursive up to `maxDepth` (default 3).
704
+ *
705
+ * `opts.guard` (since 0.1.128) runs once inside the transaction, after
706
+ * defaults + validation, with the prepared rows — see {@link TWriteOptions}.
707
+ */
708
+ insertMany(payloads: Array<DbPatch<DataType>>, opts?: TWriteOptions<DataType>): Promise<TDbInsertManyResult>;
709
+ /**
710
+ * Replaces a single record identified by primary key(s).
711
+ * Delegates to {@link bulkReplace} for unified nested relation support.
712
+ */
713
+ replaceOne(payload: DbRow<DataType>, opts?: TWriteOptions<DataType>): Promise<TDbUpdateResult>;
714
+ /**
715
+ * Replaces multiple records with deep nested relation support.
716
+ *
717
+ * Supports all relation types (TO, FROM, VIA). TO dependencies are
718
+ * replaced first (their PKs become our FKs), FROM dependents are replaced
719
+ * after (they receive our PKs as their FKs), VIA relations clear and
720
+ * re-create junction rows. Fully recursive up to `maxDepth` (default 3).
721
+ *
722
+ * `opts.guard` (since 0.1.128) runs once inside the transaction, after
723
+ * `$cas` extraction, defaults + validation — see {@link TWriteOptions}.
724
+ */
725
+ bulkReplace(payloads: Array<DbRow<DataType>>, opts?: TWriteOptions<DataType>): Promise<TDbUpdateResult>;
726
+ /**
727
+ * Partially updates a single record identified by primary key(s).
728
+ * Delegates to {@link bulkUpdate} for unified nested relation support.
729
+ */
730
+ updateOne(payload: DbPatch<DataType>, opts?: TWriteOptions<DataType>): Promise<TDbUpdateResult>;
731
+ /**
732
+ * Partially updates multiple records with deep nested relation support.
733
+ *
734
+ * Only TO relations (1:1, N:1) are supported for patching. FROM/VIA
735
+ * relations will error — use {@link bulkReplace} for those.
736
+ * Recursive up to `maxDepth` (default 3).
737
+ *
738
+ * `opts.guard` (since 0.1.128) runs once inside the transaction, after
739
+ * `$cas` extraction and validation, with the patches (identifying fields
740
+ * present, `$cas` removed) — see {@link TWriteOptions}.
741
+ */
742
+ bulkUpdate(payloads: Array<DbPatch<DataType>>, opts?: TWriteOptions<DataType>): Promise<TDbUpdateResult>;
743
+ /**
744
+ * Batch versioned touch (since 0.1.129): bumps the version of every listed
745
+ * row by exactly one, each row guarded by its own expected version. This is
746
+ * the batch fence `updateMany(orFilter, {})` used to be before 0.1.128 (an
747
+ * empty patch is a no-op since then and takes no lock).
748
+ *
749
+ * Each key carries the primary key field(s) (composite supported) plus the
750
+ * version column and NOTHING else — a touch has no payload. Unique indexes
751
+ * do not identify a touch key. `undefined`-valued properties are ignored,
752
+ * like in every write payload. Empty `keys` → `{ 0, 0 }` without a statement.
753
+ *
754
+ * `require: 'all'` (default): one count over the whole key set runs FIRST;
755
+ * a stale or missing row throws {@link CasMismatchError} before any write.
756
+ * The bumps then run as `updateMany(orFilter, {})` chunks of at most
757
+ * {@link TOUCH_MANY_CHUNK} keys inside one adapter transaction; a summed
758
+ * `matchedCount` short of `keys.length` (a row moved between the count and
759
+ * the bump) throws the same error — SQL engines roll every bump back. The
760
+ * pre-count is therefore a deliberate double check on SQL: it is what makes
761
+ * the guarantee hold on adapters whose `withTransaction` is a passthrough
762
+ * (the memory adapter, a Mongo standalone topology) — there it covers the
763
+ * common stale case and the residual race window is accepted.
764
+ * `require: 'any'`: no pre-count, the honest summed result is returned.
765
+ *
766
+ * No `guard`, no `onWrite`; not exposed over HTTP.
767
+ */
768
+ touchMany(keys: Array<DbPatch<DataType>>, opts?: TTouchManyOptions): Promise<TDbUpdateResult>;
769
+ /**
770
+ * Deletes a single record by any type-compatible identifier — primary key
771
+ * or single-field unique index. Uses the same resolution logic as `findById`.
772
+ *
773
+ * When the adapter does not support native foreign keys (e.g. MongoDB),
774
+ * cascade and setNull actions are applied before the delete.
775
+ *
776
+ * `opts.guard` (since 0.1.128) runs inside the transaction once the id has
777
+ * resolved to a filter, before cascade / delete — see {@link TDeleteOptions}.
778
+ * An id that resolves to no filter answers `{ deletedCount: 0 }` without
779
+ * calling the guard.
780
+ */
781
+ deleteOne(id: IdType, opts?: TDeleteOptions<DataType>): Promise<TDbDeleteResult>;
782
+ updateMany(filter: FilterExpr<FlatType>, data: DbPatch<DataType>): Promise<TDbUpdateResult>;
783
+ replaceMany(filter: FilterExpr<FlatType>, data: DbRow<DataType>): Promise<TDbUpdateResult>;
784
+ deleteMany(filter: FilterExpr<FlatType>): Promise<TDbDeleteResult>;
785
+ /**
786
+ * Synchronizes indexes between Atscript definitions and the database.
787
+ */
788
+ syncIndexes(): Promise<void>;
789
+ /**
790
+ * Ensures the table/collection exists in the database.
791
+ */
792
+ ensureTable(): Promise<void>;
793
+ /** Engine-agnostic guard for user-supplied mutation filters (updateMany/deleteMany/…). */
794
+ protected _guardMutationFilter(filter: FilterExpr): void;
795
+ /**
796
+ * Encrypts `@db.encrypted` field values in place on (already validated)
797
+ * write payloads — between validation and `prepareForWrite`, so adapters
798
+ * only ever see envelope strings.
799
+ *
800
+ * Parent objects along an encrypted path are shallow-cloned before
801
+ * mutation so caller-shared nested objects are never modified.
802
+ *
803
+ * In `patch` mode, operator objects (`$inc`, `$insert`, …) targeting an
804
+ * encrypted field are rejected with `ENC_FIELD_PATCH_OP` — ciphertext is
805
+ * opaque; only plain re-assignment (which re-encrypts) is allowed.
806
+ */
807
+ protected _encryptItems(items: Array<Record<string, unknown>>, mode: "write" | "patch"): Promise<void>;
808
+ /**
809
+ * Lazy pre-image read for a guard's `current(i)`: `null` when the row has
810
+ * no identifying key (e.g. an auto-increment insert) or the key cannot be
811
+ * resolved — never throws for a missing key.
812
+ * @internal
813
+ */
814
+ _readPreImage(row: unknown): Promise<DataType | null>;
815
+ /**
816
+ * Applies `@db.default` values in place to a row's absent fields — the
817
+ * defaults pass every insert / replace path runs before validation.
818
+ * Static value defaults (`@db.default 'x'`) are filled on EVERY adapter
819
+ * (since 0.1.128 — writing the column's own default explicitly is
820
+ * equivalent to leaving it to the DDL `DEFAULT`, and write guards see the
821
+ * full row). Function defaults (`now` / `uuid` / `increment` / custom) the
822
+ * adapter handles natively are NOT filled — the field stays absent so the
823
+ * engine's own default applies. The version column is never touched.
824
+ */
825
+ protected _applyDefaults(data: Record<string, unknown>): Record<string, unknown>;
826
+ /**
827
+ * The JS value for a `@db.default 'literal'`: strings (including unions of
828
+ * string literals) are used as-is, every other design type is parsed as
829
+ * JSON — the same value the SQL adapters put into the DDL `DEFAULT` clause.
830
+ * A literal that is not valid JSON falls back to the raw string so the
831
+ * validator reports it against the field instead of a bare `SyntaxError`.
832
+ */
833
+ private _parseValueDefault;
834
+ /**
835
+ * Extracts a record-identifying filter from a payload.
836
+ *
837
+ * Resolution order:
838
+ * 1. Primary key field(s) — if all PK fields are present in the payload.
839
+ * 2. Single-field unique index — first `@db.index.unique` field found.
840
+ * 3. Compound unique index — first compound unique index whose fields are all present.
841
+ *
842
+ * Throws when no identifying fields can be found. With `isFieldVisible`
843
+ * (since 0.1.134), a unique index over a hidden field is skipped, as if it
844
+ * did not exist — see {@link identificationsVisibleTo}.
845
+ */
846
+ protected _extractRecordFilter(payload: Record<string, unknown>, opts?: TIdResolveOptions): FilterExpr;
847
+ private _prepareFilterValue;
848
+ /**
849
+ * Lazy — builds a `normalized-path → from-depth` map from `this._meta.flatMap`
850
+ * on first use. Only paths reachable through an unbroken chain of `db.rel.from`
851
+ * nav fields from the root are included (chains crossing `to`/`via` are excluded).
852
+ */
853
+ private _getFromDepthMap;
854
+ /**
855
+ * Populate the depth-limit bundle on a `DbValidationContext`. Only the root
856
+ * write call (`depth === 0`) enforces — nested re-entries leave `depthCheck`
857
+ * unset so the full tree is validated once at the root.
858
+ */
859
+ private _applyDepthCtx;
860
+ /**
861
+ * Pre-validate items (type validation + FK constraints) without inserting them.
862
+ * Used by parent tables to validate FROM children before the main insert,
863
+ * ensuring errors are caught before the parent is committed.
864
+ *
865
+ * @param opts.excludeFkTargetTable - Skip FK validation to this table (the parent).
866
+ */
867
+ preValidateItems(items: Array<Record<string, unknown>>, opts?: {
868
+ excludeFkTargetTable?: string;
869
+ }): Promise<void>;
870
+ /**
871
+ * Builds a validator for a given purpose with adapter plugins.
872
+ *
873
+ * Uses annotation-based `replace` callback to make `@meta.id` and
874
+ * `@db.default` fields optional — works at all nesting levels
875
+ * (including inside nav field target types).
876
+ */
877
+ protected _buildValidator(purpose: string): Validator<T, DataType>;
878
+ }
879
+ //#endregion
880
+ //#region src/query/query-tree.d.ts
881
+ /**
882
+ * `true` when a query-tree operand is a field reference (`{ field, type? }`)
883
+ * rather than a literal — e.g. the right side of a field-to-field comparison.
884
+ * @since 0.1.136
885
+ */
886
+ declare function isFieldRef(value: unknown): value is AtscriptQueryFieldRef;
887
+ /** A single join in a view query plan. */
888
+ interface TViewJoin {
889
+ targetType: () => TAtscriptAnnotatedType;
890
+ targetTable: string;
891
+ condition: AtscriptQueryNode;
892
+ /**
893
+ * `inner` (default) drops entry rows without a match; `left` keeps them
894
+ * with the target's columns as NULL. Joins apply in declaration order.
895
+ * @since 0.1.136
896
+ */
897
+ kind: "inner" | "left";
898
+ }
899
+ /** Resolved view query plan produced by AtscriptDbView. */
900
+ interface TViewPlan {
901
+ entryType: () => TAtscriptAnnotatedType;
902
+ entryTable: string;
903
+ joins: TViewJoin[];
904
+ filter?: AtscriptQueryNode;
905
+ having?: AtscriptQueryNode;
906
+ materialized: boolean;
907
+ }
908
+ /**
909
+ * Translates a JS-emitted query tree into a FilterExpr.
910
+ * Resolves field references (type + field path) to physical column names
911
+ * via the provided resolver function.
912
+ */
913
+ declare function translateQueryTree(node: AtscriptQueryNode, resolveField: (ref: AtscriptQueryFieldRef) => string): FilterExpr;
914
+ //#endregion
915
+ //#region src/table/view-source.d.ts
916
+ /**
917
+ * Where a view reads one logical source path from, in PHYSICAL terms.
918
+ *
919
+ * Produced by {@link resolveViewSource} — a pure function of the source
920
+ * table's annotated type that lays the table out with `TableMetadata`'s
921
+ * rules (flattened `__` columns, `@db.column` renames, `@db.json` / array
922
+ * JSON columns, document paths on nested-object adapters).
923
+ * @since 0.1.136
924
+ */
925
+ interface TViewSource {
926
+ /**
927
+ * Physical column (relational) or document path (nested-object adapters).
928
+ * For a path inside a JSON column this is the JSON column; for a flattened
929
+ * object it is the object's `__` prefix (no such column exists — its leaves do).
930
+ */
931
+ column: string;
932
+ /** Segments inside {@link column} when the path descends into a JSON column (relational only). */
933
+ jsonPath?: string[];
934
+ /** Design type of the addressed node (`string`, `number`, `object`, `array`, …; `unknown` when undeclared). */
935
+ designType: string;
936
+ /** Set for a relational object stored as one column per leaf. */
937
+ flattened?: true;
938
+ /**
939
+ * `true` when the value may be absent: the path or an ancestor segment is
940
+ * optional, or it reads inside a JSON-stored value.
941
+ */
942
+ optional: boolean;
943
+ }
944
+ //#endregion
945
+ //#region src/table/db-view.d.ts
946
+ /** Primitive result type of a JSON-leaf extraction. */
947
+ type TViewJsonType = "string" | "number" | "boolean";
948
+ interface TViewColumnMapping {
949
+ /**
950
+ * The view's own physical column — its `@db.column` / flattened `__` name
951
+ * on relational adapters, its document key on nested-object adapters.
952
+ */
953
+ viewColumn: string;
954
+ /**
955
+ * Logical view field path (`address.city` for a flattened object leaf) —
956
+ * how `@db.view.having` refers to the column.
957
+ * @since 0.1.136
958
+ */
959
+ viewPath: string;
960
+ sourceTable: string;
961
+ /** Physical source column (or document path). `"*"` for `COUNT(*)`. */
962
+ sourceColumn: string;
963
+ /**
964
+ * Set when the source value may be missing for some row: an optional
965
+ * source field, a leaf inside a JSON-stored value, an undeclared path, or a
966
+ * column of a left-joined table. Document adapters coalesce such a source
967
+ * to `null`; a required source is read as a plain path (index-friendly).
968
+ * @since 0.1.136
969
+ */
970
+ nullable?: true;
971
+ /**
972
+ * Set when the source is a primitive leaf inside a JSON column
973
+ * ({@link sourceColumn}): the path within it and the leaf's declared type.
974
+ * @since 0.1.136
975
+ */
976
+ json?: {
977
+ path: string[];
978
+ type: TViewJsonType;
979
+ };
980
+ /**
981
+ * Aggregate function name (`sum` | `avg` | `count` | `countDistinct` |
982
+ * `min` | `max`) if this is an aggregate column.
983
+ */
984
+ aggFn?: string;
985
+ /** Source field for the aggregate function ('*' for COUNT(*)). */
986
+ aggField?: string;
987
+ /**
988
+ * Row predicate of a conditional aggregate — the `@db.agg.*` 2nd argument:
989
+ * only rows where it holds are aggregated. Refs resolve like
990
+ * `@db.view.filter` (entry table + joins). @since 0.1.136
991
+ */
992
+ aggFilter?: AtscriptQueryNode;
993
+ }
994
+ /**
995
+ * Database view abstraction driven by Atscript `@db.view.*` annotations.
996
+ *
997
+ * Extends {@link AtscriptDbReadable} with view plan resolution — entry table,
998
+ * joins, filter, and materialization flag. Read operations are inherited;
999
+ * write operations are not available on views.
1000
+ *
1001
+ * ```typescript
1002
+ * const adapter = new SqliteAdapter(db)
1003
+ * const activeUsers = new AtscriptDbView(ActiveUsersType, adapter)
1004
+ * const users = await activeUsers.findMany({ filter: {}, controls: {} })
1005
+ * ```
1006
+ */
1007
+ declare class AtscriptDbView<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>, FlatType = NullableOptional<FlatOf<T>>, A extends BaseDbAdapter = BaseDbAdapter, IdType = PrimaryKeyOf<T>, OwnProps = NullableOptional<OwnPropsOf<T>>, NavType extends Record<string, unknown> = NavPropsOf<T>> extends AtscriptDbReadable<T, DataType, FlatType, A, IdType, OwnProps, NavType> {
1008
+ private _viewPlan?;
1009
+ private _columnMappings?;
1010
+ get isView(): boolean;
1011
+ /**
1012
+ * Whether this is an external view — declared with `@db.view` only,
1013
+ * without `@db.view.for`. External views reference pre-existing DB views
1014
+ * and are not managed (created/dropped) by schema sync.
1015
+ */
1016
+ get isExternal(): boolean;
1017
+ /**
1018
+ * Lazily resolves the view plan from `@db.view.*` metadata.
1019
+ *
1020
+ * - `db.view.for` → entry type ref (required)
1021
+ * - `db.view.joins` → array of `{ target, condition }` (optional, multiple)
1022
+ * - `db.view.filter` → query tree (optional)
1023
+ * - `db.view.materialized` → boolean (optional)
1024
+ */
1025
+ get viewPlan(): TViewPlan;
1026
+ /** Whether the adapter stores nested objects natively (document paths, no JSON columns). */
1027
+ private get _nested();
1028
+ /**
1029
+ * Resolves a view query field ref (join condition, `@db.view.filter`,
1030
+ * conditional-aggregate predicate) to its table name and PHYSICAL source on
1031
+ * this view's adapter — the column (or document path) with `TableMetadata`'s
1032
+ * layout rules, the path inside a JSON column, and whether the value may be
1033
+ * absent. An unqualified ref resolves against the entry table.
1034
+ * @throws for a ref without storage (`@db.ignore`, navigation relation) or
1035
+ * inside an `@db.encrypted` field (relational adapters).
1036
+ * @since 0.1.136
1037
+ */
1038
+ resolveRefSource(ref: AtscriptQueryFieldRef): {
1039
+ table: string;
1040
+ source: TViewSource;
1041
+ };
1042
+ /**
1043
+ * Resolves a query field ref (join condition, `@db.view.filter`) to a
1044
+ * quoted `table.column` SQL fragment — the PHYSICAL column (flattened
1045
+ * `__` name, `@db.column` rename). An unqualified ref
1046
+ * resolves against the entry table.
1047
+ *
1048
+ * @param ref - The field reference from the query tree.
1049
+ * @param qi - Identifier quoting function (e.g. backtick for MySQL, double-quote for SQLite).
1050
+ * Defaults to double-quote wrapping for backwards compatibility.
1051
+ * @throws when the ref reads inside a JSON column (not supported in view conditions).
1052
+ */
1053
+ resolveFieldRef(ref: AtscriptQueryFieldRef, qi?: (name: string) => string): string;
1054
+ /**
1055
+ * Maps each view column to its source table and PHYSICAL source column.
1056
+ *
1057
+ * View fields resolve through their chain ref; fields without a ref read
1058
+ * the entry table under the same name; aggregates read their `@db.agg.*`
1059
+ * field from the entry table (or their ref). Source names are
1060
+ * physical (flattened `__` names, `@db.column`, document paths), `viewColumn`
1061
+ * is the view's own physical name, an object field whose source is a
1062
+ * flattened object expands to one mapping per leaf, and a primitive leaf
1063
+ * inside a JSON column carries `json` (rendered by adapters that support
1064
+ * JSON extraction).
1065
+ *
1066
+ * Computed once per view (the plan and the type are immutable).
1067
+ *
1068
+ * @throws for an object field over a JSON column without `@db.json` on the
1069
+ * view field, a JSON leaf that is not a string / number / boolean, or an
1070
+ * aggregate other than `count` over `'*'`.
1071
+ */
1072
+ getViewColumnMappings(): TViewColumnMapping[];
1073
+ private _buildColumnMappings;
1074
+ /** One view column over one physical source (a column or a JSON leaf). */
1075
+ private _leafMapping;
1076
+ }
1077
+ /**
1078
+ * Structural type guard for views: `true` when the readable reports
1079
+ * `isView`, whether or not it is an `AtscriptDbView` instance of THIS copy
1080
+ * of `@atscript/db`. Adapters must use this (or `readable.isView`) instead of
1081
+ * `instanceof AtscriptDbView` — in a bundle that carries two copies of the
1082
+ * core (app bundle + external adapter), `instanceof` is false and the adapter
1083
+ * would create an empty physical table under the view's name.
1084
+ * @since 0.1.128
1085
+ */
1086
+ declare function isAtscriptDbView(readable: AtscriptDbReadable<any, any, any, any, any, any, any>): readable is AtscriptDbView<any, any, any, any, any, any, any>;
1087
+ //#endregion
1088
+ //#region src/table/db-space.d.ts
1089
+ /**
1090
+ * Adapter factory function. Called once per table/view to create a fresh adapter instance.
1091
+ * Each readable gets its own adapter (1:1 relationship required by BaseDbAdapter).
1092
+ */
1093
+ type TAdapterFactory = () => BaseDbAdapter;
1094
+ /** Options bag for {@link DbSpace} (second constructor argument). */
1095
+ interface TDbSpaceOptions {
1096
+ /** Logger shared by all tables/views in the space. */
1097
+ logger?: TGenericLogger;
1098
+ /** Field-level encryption configuration for `@db.encrypted` fields. */
1099
+ encryption?: TDbEncryptionOptions;
1100
+ }
1101
+ /**
1102
+ * A database space — a registry of tables and views sharing the same adapter type and driver.
1103
+ *
1104
+ * `DbSpace` solves the cross-table discovery problem: when table A has a relation
1105
+ * to table B, it needs to find and query table B. The space acts as the registry
1106
+ * that makes this possible via the table resolver callback.
1107
+ *
1108
+ * Each table/view gets its own adapter instance (created by the factory), but all
1109
+ * share the same space and can discover each other for `$with` relation loading.
1110
+ *
1111
+ * ```typescript
1112
+ * // SQLite
1113
+ * const driver = new BetterSqlite3Driver(':memory:')
1114
+ * const db = new DbSpace(() => new SqliteAdapter(driver))
1115
+ * const users = db.getTable(UsersType)
1116
+ * const activeUsers = db.getView(ActiveUsersType)
1117
+ * ```
1118
+ */
1119
+ declare class DbSpace {
1120
+ protected readonly adapterFactory: TAdapterFactory;
1121
+ private _readables;
1122
+ /** All tables created in this space — used for reverse FK lookup during cascade. */
1123
+ private _allTables;
1124
+ /** Lazily created adapter for administrative ops (drop table/view) that don't need a registered readable. */
1125
+ private _adminAdapter?;
1126
+ protected readonly logger: TGenericLogger;
1127
+ /** Encryption service for `@db.encrypted` fields — validated eagerly at construction. */
1128
+ protected readonly _encryption?: DbEncryption;
1129
+ /**
1130
+ * @param adapterFactory - Creates a fresh adapter per table/view.
1131
+ * @param loggerOrOptions - Either a logger (legacy signature) or a
1132
+ * {@link TDbSpaceOptions} bag carrying `logger` and/or `encryption`.
1133
+ */
1134
+ constructor(adapterFactory: TAdapterFactory, loggerOrOptions?: TGenericLogger | TDbSpaceOptions);
1135
+ /**
1136
+ * Auto-detects whether the type is a table or view and returns the
1137
+ * appropriate instance. Uses `@db.view` or `@db.view.for` presence to distinguish.
1138
+ */
1139
+ get<T extends TAtscriptAnnotatedType>(type: T, logger?: TGenericLogger): AtscriptDbReadable<T>;
1140
+ /**
1141
+ * Returns the table for the given annotated type.
1142
+ * Creates the table + adapter on first access, caches for subsequent calls.
1143
+ */
1144
+ getTable<T extends TAtscriptAnnotatedType>(type: T, logger?: TGenericLogger): AtscriptDbTable<T>;
1145
+ /**
1146
+ * Returns the view for the given annotated type.
1147
+ * Creates the view + adapter on first access, caches for subsequent calls.
1148
+ */
1149
+ getView<T extends TAtscriptAnnotatedType>(type: T, logger?: TGenericLogger): AtscriptDbView<T>;
1150
+ /**
1151
+ * Returns the adapter for the given annotated type.
1152
+ * Creates the table/view + adapter on first access if needed.
1153
+ */
1154
+ getAdapter(type: TAtscriptAnnotatedType): BaseDbAdapter;
1155
+ /**
1156
+ * Drops a table by name. Used by schema sync to remove tables no longer in the schema.
1157
+ * See `BaseDbAdapter.dropTableByName` for an adapter that does not support it.
1158
+ */
1159
+ dropTableByName(tableName: string): Promise<void>;
1160
+ /**
1161
+ * Drops a view by name. Used by schema sync to remove views no longer in the schema.
1162
+ * See `BaseDbAdapter.dropViewByName` for an adapter that does not support it.
1163
+ */
1164
+ dropViewByName(viewName: string): Promise<void>;
1165
+ /**
1166
+ * Drops a group of mutually referencing tables as one operation.
1167
+ * Used by schema sync to remove a foreign-key cycle no longer in the schema.
1168
+ * @since 0.1.128
1169
+ */
1170
+ dropTablesByName(tableNames: string[]): Promise<void>;
1171
+ /**
1172
+ * Live foreign keys referencing `tableName`, or `undefined` when the
1173
+ * adapter cannot introspect them. Used by schema sync for drop ordering
1174
+ * and surviving-reference checks of tables without a registered readable.
1175
+ * @since 0.1.128
1176
+ */
1177
+ getReferencingForeignKeys(tableName: string): Promise<TReferencingForeignKey[] | undefined>;
1178
+ /**
1179
+ * A factory-fresh adapter with NO registered readable. Only the name-taking
1180
+ * primitives may run on it (`dropTableByName`, `dropViewByName`,
1181
+ * `dropTablesByName`, `getReferencingForeignKeys`) — adapters derive the
1182
+ * schema for those from the driver/connection, not from a bound table.
1183
+ */
1184
+ private _getAdminAdapter;
1185
+ /**
1186
+ * A factory-fresh adapter that knows its space (see `BaseDbAdapter.registerSpace`).
1187
+ * Optional call: an adapter built against an older `@atscript/db` copy lacks it.
1188
+ */
1189
+ private _createAdapter;
1190
+ /**
1191
+ * Finds all child tables with FKs pointing to the given parent table name.
1192
+ * Accesses `table.foreignKeys` which triggers `_flatten()` if needed.
1193
+ */
1194
+ private _getCascadeTargets;
1195
+ /**
1196
+ * Resolves a table name to a queryable target for FK validation.
1197
+ * Searches all registered tables for one with the matching table name.
1198
+ */
1199
+ private _getFkLookupTarget;
1200
+ }
1201
+ //#endregion
632
1202
  //#region src/base-adapter.d.ts
633
1203
  /** Every calendar-bucket unit — what an adapter that renders them all returns from `calendarBucketUnits()`. */
634
1204
  declare const ALL_BUCKET_UNITS: ReadonlySet<BucketUnit>;
@@ -681,6 +1251,15 @@ declare abstract class BaseDbAdapter {
681
1251
  * index sync, etc.
682
1252
  */
683
1253
  registerReadable(readable: AtscriptDbReadable<any, any, any, any, any, any, any>, logger?: TGenericLogger): void;
1254
+ /**
1255
+ * Called by {@link DbSpace} right after its factory builds this adapter —
1256
+ * the administrative one included — before {@link registerReadable}. No-op
1257
+ * by default: override it to share state across a space's adapters when the
1258
+ * adapter has no driver to share it through (the memory adapter keeps one
1259
+ * store per space).
1260
+ * @since 0.1.137
1261
+ */
1262
+ registerSpace(_space: DbSpace): void;
684
1263
  /**
685
1264
  * Enables or disables verbose (debug-level) logging for this adapter.
686
1265
  * When disabled, no log strings are constructed — zero overhead.
@@ -799,6 +1378,29 @@ declare abstract class BaseDbAdapter {
799
1378
  * out-of-range source, uniqu's `bucketLabel` semantics). Since 0.1.132.
800
1379
  */
801
1380
  calendarBucketUnits(): ReadonlySet<BucketUnit>;
1381
+ /**
1382
+ * Aggregate functions (`{ $fn, $field }` in an aggregate `$select`) this
1383
+ * adapter renders. The default is `sum`, `count`, `avg`, `min` and `max`;
1384
+ * an adapter that also implements `countDistinct` (distinct non-null
1385
+ * values) returns `ALL_AGGREGATE_FNS`. The core rejects a known function
1386
+ * missing from this set with `AGG_FN_NOT_SUPPORTED` before dispatch, so
1387
+ * `aggregate()` only ever receives functions listed here; moost-db's
1388
+ * `/meta` advertises the set as `aggregateFns`.
1389
+ *
1390
+ * @since 0.1.136
1391
+ */
1392
+ aggregateFns(): ReadonlySet<AggregateFn>;
1393
+ /**
1394
+ * Revision of how this adapter renders a managed view (its SQL / pipeline)
1395
+ * from an unchanged view definition. Stored in each managed view's sync
1396
+ * snapshot when defined, so bumping it recreates every managed view of the
1397
+ * adapter once on the next sync — return a new value whenever a rendering
1398
+ * fix changes what an existing view returns. `undefined` (the default)
1399
+ * leaves the snapshot, and its hash, as before. External views ignore it.
1400
+ *
1401
+ * @since 0.1.137
1402
+ */
1403
+ viewRenderRevision(): string | undefined;
802
1404
  /**
803
1405
  * Whether this adapter enforces foreign key constraints natively.
804
1406
  * When `true`, the generic layer skips application-level cascade/setNull
@@ -1212,16 +1814,20 @@ declare abstract class BaseDbAdapter {
1212
1814
  dropIndexesForColumns?(columns: string[]): Promise<void>;
1213
1815
  /**
1214
1816
  * Drops a table by name (without needing a registered readable).
1215
- * Used by schema sync to remove tables no longer in the schema.
1216
- * Optional — only relational adapters implement this.
1817
+ * Used by schema sync to remove tables no longer in the schema. A missing
1818
+ * table is not an error (`DROP TABLE IF EXISTS`). The default throws —
1819
+ * schema sync then reports the removed table as an `error` entry and keeps
1820
+ * it tracked. Since 0.1.137; before, the method was optional and a drop the
1821
+ * adapter lacked was skipped silently while sync reported it done.
1217
1822
  */
1218
- dropTableByName?(tableName: string): Promise<void>;
1823
+ dropTableByName(tableName: string): Promise<void>;
1219
1824
  /**
1220
1825
  * Drops a view by name (without needing a registered readable).
1221
- * Used by schema sync to remove views no longer in the schema.
1222
- * Optional — only relational adapters implement this.
1826
+ * Used by schema sync to remove views no longer in the schema, and to drop
1827
+ * a managed view before recreating it. A missing view is not an error. The
1828
+ * default throws, with the same history as {@link dropTableByName}.
1223
1829
  */
1224
- dropViewByName?(viewName: string): Promise<void>;
1830
+ dropViewByName(viewName: string): Promise<void>;
1225
1831
  /**
1226
1832
  * Drops several tables that reference each other (a foreign-key cycle) as
1227
1833
  * one operation. Schema sync only calls this for cycles whose members are
@@ -1359,6 +1965,7 @@ declare class TableMetadata {
1359
1965
  ignoredFields: Set<string>;
1360
1966
  uniqueProps: Set<string>;
1361
1967
  defaults: Map<string, TDbDefaultValue>;
1968
+ /** Logical path → `@db.column` override (top-level keys only on document storage). */
1362
1969
  columnMap: Map<string, string>;
1363
1970
  dimensions: string[];
1364
1971
  measures: string[];
@@ -1420,14 +2027,14 @@ declare class TableMetadata {
1420
2027
  private _columnFromMap;
1421
2028
  constructor(nestedObjects: boolean);
1422
2029
  get isBuilt(): boolean;
2030
+ /** {@link documentPath} over this table's `columnMap`. */
2031
+ documentPath(path: string): string;
1423
2032
  /**
1424
- * Logical field path → its physical path in document storage (nested
1425
- * objects kept inline). `@db.column` renames apply to the annotated key,
1426
- * and a document renames the TOP-LEVEL key only — nested keys are stored
1427
- * as-is — so a dotted path under a renamed top-level object renames its
1428
- * first segment: `profile.bio` under `@db.column 'prof'` → `prof.bio`.
2033
+ * Physical name of a logical path: the document path on nested-object
2034
+ * adapters, else the relational column (`pathToPhysical`, then the
2035
+ * `@db.column` override).
1429
2036
  */
1430
- documentPath(path: string): string;
2037
+ physicalPath(logical: string): string;
1431
2038
  /**
1432
2039
  * Runs the full metadata compilation pipeline. Called once by
1433
2040
  * `AtscriptDbReadable._ensureBuilt()` on first metadata access.
@@ -1468,8 +2075,6 @@ declare class TableMetadata {
1468
2075
  * Builds the bidirectional pathToPhysical / physicalToPath maps.
1469
2076
  */
1470
2077
  private _classifyFields;
1471
- /** Returns the `__`-separated parent prefix for a dot-separated path, or empty string for top-level paths. */
1472
- private _flattenedPrefix;
1473
2078
  /** Nearest `@db.encrypted` ancestor of `path` (exclusive), or `undefined`. */
1474
2079
  /**
1475
2080
  * Indexes non-ignored descriptors by logical path and retains the JSON-parent
@@ -1619,6 +2224,14 @@ interface TMetaResponse {
1619
2224
  * none. Since 0.1.132.
1620
2225
  */
1621
2226
  bucketUnits?: BucketUnit[];
2227
+ /**
2228
+ * Aggregate functions the adapter renders (`{ $fn }` in an aggregate
2229
+ * `$select`, URL `sum(field)` / `countDistinct(field)` …) — see
2230
+ * `BaseDbAdapter.aggregateFns()`.
2231
+ *
2232
+ * @since 0.1.136
2233
+ */
2234
+ aggregateFns?: AggregateFn[];
1622
2235
  }
1623
2236
  /** Where the action applies on the UI. */
1624
2237
  type TDbActionLevel = "table" | "row" | "rows";
@@ -1684,12 +2297,24 @@ interface TDbActionInfo {
1684
2297
  disabled?: string;
1685
2298
  /**
1686
2299
  * Name of the `.as` interface the action's `@InputForm()` parameter expects
1687
- * (the compiled class's `.name`). Present only when the handler declares an
1688
- * `@InputForm(FormType)` parameter. Clients fetch the serialized schema via
1689
- * `GET /meta/form/:name` on the same controller and render a form to
1690
- * collect the `input` field of the action's request envelope.
2300
+ * (the compiled class's `.name`). Present for an `@InputForm(FormType)`
2301
+ * parameter or a class-level `inputForm` entry. Clients fetch the
2302
+ * serialized schema via `GET /meta/form/:name` on the same controller and
2303
+ * render a form to collect the `input` field of the action's request
2304
+ * envelope. A class-level entry may name a form served elsewhere — then
2305
+ * {@link formUrl} is present and clients fetch it instead of `meta/form/:name`.
1691
2306
  */
1692
2307
  inputForm?: string;
2308
+ /**
2309
+ * Server-absolute path of the serialized form schema —
2310
+ * same convention as `value` for `'backend'` actions: clients prefix
2311
+ * their base URL. Present only together with {@link inputForm}, when the
2312
+ * form is served by another endpoint than this controller's
2313
+ * `meta/form/:name`; clients fetch it instead of the relative route.
2314
+ *
2315
+ * @since 0.1.136
2316
+ */
2317
+ formUrl?: string;
1693
2318
  }
1694
2319
  interface TDbInsertResult {
1695
2320
  insertedId: unknown;
@@ -2217,14 +2842,15 @@ interface TBucketFieldSource {
2217
2842
  navFields: ReadonlySet<string>;
2218
2843
  }
2219
2844
  /**
2220
- * The one normalizer of `$select` computed entries (since 0.1.132) — uniqu's
2845
+ * The one normalizer of `$select` computed entries — uniqu's
2221
2846
  * `resolveBuckets` (entry shapes, unit, time zone canonicalization, week
2222
2847
  * start, alias syntax and uniqueness, "grouped queries only", "must also
2223
2848
  * appear in $groupBy", string `$groupBy` entries) with the table's names as
2224
2849
  * the collision set: a bucket alias may not equal a logical path, a physical
2225
2850
  * column or a navigation field, so a label is never reverse-mapped as a
2226
2851
  * column. Aggregate entries are checked against `SUPPORTED_AGGREGATE_FNS`
2227
- * (and `'*'` is `count`'s only).
2852
+ * (and `'*'` is `count`'s only); whether the adapter renders a function is
2853
+ * `guardAggregate`'s (`aggregateFns()` → `AGG_FN_NOT_SUPPORTED`).
2228
2854
  *
2229
2855
  * Which layer validates what:
2230
2856
  * - **Shapes** (this normalizer) run FIRST at every entry point — the core's
@@ -2246,10 +2872,15 @@ interface TBucketFieldSource {
2246
2872
  *
2247
2873
  * @throws DbError `INVALID_QUERY` carrying every issue (`path` `$select` / `$groupBy`).
2248
2874
  */
2249
- declare function resolveCalendarBuckets(controls: {
2875
+ declare function normalizeComputedSelect(controls: {
2250
2876
  $select?: unknown;
2251
2877
  $groupBy?: unknown;
2252
2878
  } | undefined, fields: TBucketFieldSource, aggregate?: boolean): ResolvedBucket[];
2879
+ /**
2880
+ * @deprecated since 0.1.136 — renamed {@link normalizeComputedSelect} (it
2881
+ * normalizes every computed `$select` entry, aggregates included).
2882
+ */
2883
+ declare const resolveCalendarBuckets: typeof normalizeComputedSelect;
2253
2884
  /**
2254
2885
  * Whether a field's TYPE allows it to be the source of a calendar bucket: a
2255
2886
  * `number` / `integer` leaf carrying the `timestamp` tag
@@ -2275,4 +2906,4 @@ declare function isJsonValueField(fd: TDbFieldMeta): boolean;
2275
2906
  */
2276
2907
  declare function jsonValueAncestor(path: string, jsonValueParents: ReadonlySet<string>): string | undefined;
2277
2908
  //#endregion
2278
- export { TDbWriteGuardContext as $, TDbActionIntent as A, isGeoIndexableType as At, TDbIndexField as B, FieldMappingStrategy as Bt, PrimaryKeyOf$1 as C, TWriteTableResolver as Ct, TCrudOp as D, WithRelation$1 as Dt, TColumnDiff as E, UniqueryControls$1 as Et, TDbDefaultValue as F, DbResponse as Ft, TDbReferentialAction as G, TDbInsertManyResult as H, TGenericLogger as Ht, TDbDeleteResult as I, resolveDesignType as It, TDbRemoveGuardContext as J, TDbRelation as K, TDbFieldMeta as L, DbEncryption as Lt, TDbActionProcessor as M, ALL_BUCKET_UNITS as Mt, TDbCollation as N, BaseDbAdapter as Nt, TCrudPermissions as O, TableMetadata as Ot, TDbDefaultFn as P, AtscriptDbReadable as Pt, TDbWriteGuard as Q, TDbForeignKey as R, TDbEncryptionOptions as Rt, OwnPropsOf$1 as S, TWriteOptions as St, TCascadeTarget as T, Uniquery$1 as Tt, TDbInsertResult as U, UniquSelect as Ut, TDbIndexType as V, NoopLogger as Vt, TDbObjectKind as W, TDbUpdateResult as X, TDbStorageType as Y, TDbWriteAction as Z, FieldOpsFor as _, TSyncColumnResult as _t, jsonValueAncestor as a, TFieldMeta as at, NavPropsOf$1 as b, TTouchManyOptions as bt, AggregateExpr$1 as c, TIdDescriptor as ct, AggregateResult as d, TMetaResponse as dt, TDeleteOptions as et, AtscriptDbWritable as f, TMetadataOverrides as ft, DbRow as g, TSearchIndexInfo as gt, DbQuery as h, TRelationInfo as ht, isJsonValueField as i, TExistingTableOption as it, TDbActionLevel as j, isGeoPointType as jt, TDbActionInfo as k, findAncestorInSet as kt, AggregateFn$1 as l, TIdResolveOptions as lt, DbPatch as m, TReferencingForeignKey as mt, TResolvedBucket as n, TExistingColumn as nt, resolveCalendarBuckets as o, TFkLookupResolver as ot, DbControls as p, TPrimaryKeyChange as pt, TDbRemoveGuard as q, isBucketableField as r, TExistingForeignKey as rt, AggregateControls as s, TFkLookupTarget as st, TBucketFieldSource as t, TEnsureTableOptions as tt, AggregateQuery$1 as u, TIdentification as ut, FilterExpr$1 as v, TTableOptionDiff as vt, TCascadeResolver as w, TypedWithRelation as wt, NullableOptional as x, TValueFormatterPair as xt, FlatOf$1 as y, TTableResolver as yt, TDbIndex as z, DocumentFieldMapper as zt };
2909
+ export { TDbWriteGuard as $, AtscriptDbReadable as $t, TDbActionInfo as A, findAncestorInSet as At, TDbIndex as B, TViewJsonType as Bt, OwnPropsOf$1 as C, TWriteOptions as Ct, TColumnDiff as D, UniqueryControls$1 as Dt, TCascadeTarget as E, Uniquery$1 as Et, TDbDefaultFn as F, DbSpace as Ft, TDbObjectKind as G, AtscriptRef as Gt, TDbIndexType as H, AtscriptQueryComparison as Ht, TDbDefaultValue as I, TAdapterFactory as It, TDbRemoveGuard as J, isFieldRef as Jt, TDbReferentialAction as K, TViewJoin as Kt, TDbDeleteResult as L, TDbSpaceOptions as Lt, TDbActionLevel as M, isGeoPointType as Mt, TDbActionProcessor as N, ALL_BUCKET_UNITS as Nt, TCrudOp as O, WithRelation$1 as Ot, TDbCollation as P, BaseDbAdapter as Pt, TDbWriteAction as Q, NativeIntegrity as Qt, TDbFieldMeta as R, AtscriptDbView as Rt, NullableOptional as S, TValueFormatterPair as St, TCascadeResolver as T, TypedWithRelation as Tt, TDbInsertManyResult as U, AtscriptQueryFieldRef$1 as Ut, TDbIndexField as V, isAtscriptDbView as Vt, TDbInsertResult as W, AtscriptQueryNode$1 as Wt, TDbStorageType as X, AtscriptDbTable as Xt, TDbRemoveGuardContext as Y, translateQueryTree as Yt, TDbUpdateResult as Z, IntegrityStrategy as Zt, DbRow as _, TSearchIndexInfo as _t, jsonValueAncestor as a, FieldMappingStrategy as an, TExistingTableOption as at, FlatOf$1 as b, TTableResolver as bt, AggregateControls as c, UniquSelect as cn, TFkLookupTarget as ct, AggregateQuery$1 as d, TIdentification as dt, DbResponse as en, TDbWriteGuardContext as et, AggregateResult as f, TMetaResponse as ft, DbQuery as g, TRelationInfo as gt, DbPatch as h, TReferencingForeignKey as ht, isJsonValueField as i, DocumentFieldMapper as in, TExistingForeignKey as it, TDbActionIntent as j, isGeoIndexableType as jt, TCrudPermissions as k, TableMetadata as kt, AggregateExpr$1 as l, TIdDescriptor as lt, DbControls as m, TPrimaryKeyChange as mt, TResolvedBucket as n, DbEncryption as nn, TEnsureTableOptions as nt, normalizeComputedSelect as o, NoopLogger as on, TFieldMeta as ot, AtscriptDbWritable as p, TMetadataOverrides as pt, TDbRelation as q, TViewPlan as qt, isBucketableField as r, TDbEncryptionOptions as rn, TExistingColumn as rt, resolveCalendarBuckets as s, TGenericLogger as sn, TFkLookupResolver as st, TBucketFieldSource as t, resolveDesignType as tn, TDeleteOptions as tt, AggregateFn$1 as u, TIdResolveOptions as ut, FieldOpsFor as v, TSyncColumnResult as vt, PrimaryKeyOf$1 as w, TWriteTableResolver as wt, NavPropsOf$1 as x, TTouchManyOptions as xt, FilterExpr$1 as y, TTableOptionDiff as yt, TDbForeignKey as z, TViewColumnMapping as zt };