@atscript/db 0.1.147 → 0.1.148

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 (55) hide show
  1. package/dist/{agg-CV7y8nC6.d.cts → agg-42CSpGDR.d.cts} +2 -2
  2. package/dist/{agg-D5DHsAby.d.mts → agg-Wo-smrYV.d.mts} +3 -3
  3. package/dist/agg.cjs +1 -1
  4. package/dist/agg.d.cts +2 -2
  5. package/dist/agg.d.mts +2 -2
  6. package/dist/agg.mjs +1 -1
  7. package/dist/{aggregate-fns-CtsRbZn9.cjs → aggregate-fns-C-UJRobm.cjs} +46 -7
  8. package/dist/{aggregate-fns-C0BymEqa.mjs → aggregate-fns-CyaZyb9I.mjs} +35 -8
  9. package/dist/aggregate-rules-D_bsCpUI.cjs +26 -0
  10. package/dist/aggregate-rules-jdPrxqWa.mjs +15 -0
  11. package/dist/{buckets-DYopfd2Y.d.mts → buckets-CNdTOnei.d.mts} +312 -18
  12. package/dist/{buckets-CjdPipCC.d.cts → buckets-DJiYlMXc.d.cts} +312 -18
  13. package/dist/{column-diff-Dt7EDJsA.cjs → column-diff-CUU4GvYg.cjs} +681 -87
  14. package/dist/{column-diff-CO06iQvI.mjs → column-diff-Cp6ZoyRE.mjs} +678 -96
  15. package/dist/{db-error-D5uilS_A.mjs → db-error-Az85UhTX.mjs} +32 -1
  16. package/dist/{db-error-DTkkeu5b.cjs → db-error-DRQH4sLY.cjs} +55 -0
  17. package/dist/{fk-diff-BtOmL4OW.d.mts → fk-diff-BQ4krij8.d.cts} +1 -1
  18. package/dist/{fk-diff-DLqwVkKG.d.cts → fk-diff-DsaIijVX.d.mts} +1 -1
  19. package/dist/index.cjs +70 -5
  20. package/dist/index.d.cts +58 -13
  21. package/dist/index.d.mts +58 -13
  22. package/dist/index.mjs +64 -6
  23. package/dist/{nested-writer-SqtYJHF0.cjs → nested-writer-BUQvk6QT.cjs} +68 -2
  24. package/dist/{nested-writer-D35UsDBT.mjs → nested-writer-CUBoq1ZO.mjs} +57 -3
  25. package/dist/numeric-operand-B1jKH7x5.mjs +21 -0
  26. package/dist/numeric-operand-DKfiRLYp.cjs +26 -0
  27. package/dist/ops.cjs +1 -1
  28. package/dist/ops.mjs +1 -1
  29. package/dist/plugin.cjs +36 -9
  30. package/dist/plugin.mjs +36 -9
  31. package/dist/rel.cjs +2 -2
  32. package/dist/rel.d.cts +2 -2
  33. package/dist/rel.d.mts +2 -2
  34. package/dist/rel.mjs +2 -2
  35. package/dist/{relation-helpers-C2bLtE-t.d.mts → relation-helpers-Ba0v49sn.d.mts} +1 -1
  36. package/dist/{relation-helpers-DH3LvwLV.d.cts → relation-helpers-kX7jjgME.d.cts} +1 -1
  37. package/dist/{relation-loader-C0qE8xgR.mjs → relation-loader-ByY1Byrl.mjs} +1 -1
  38. package/dist/{relation-loader-BFhVuJ-B.cjs → relation-loader-D8OrdH-r.cjs} +1 -1
  39. package/dist/shared.cjs +3 -0
  40. package/dist/shared.d.cts +12 -1
  41. package/dist/shared.d.mts +12 -1
  42. package/dist/shared.mjs +2 -1
  43. package/dist/sync.cjs +2 -2
  44. package/dist/sync.d.cts +2 -2
  45. package/dist/sync.d.mts +2 -2
  46. package/dist/sync.mjs +2 -2
  47. package/dist/{validator-Clu2q_7z.mjs → validator-BTiIOTKP.mjs} +9 -3
  48. package/dist/{validator-CVS-onRg.cjs → validator-UcuJxHNT.cjs} +8 -2
  49. package/dist/{validator-CMnTI6r7.d.cts → validator-tBNvM1qc.d.cts} +23 -2
  50. package/dist/{validator-CMnTI6r7.d.mts → validator-tBNvM1qc.d.mts} +23 -2
  51. package/dist/validator.cjs +2 -2
  52. package/dist/validator.d.cts +1 -1
  53. package/dist/validator.d.mts +1 -1
  54. package/dist/validator.mjs +2 -2
  55. package/package.json +8 -8
@@ -1,8 +1,41 @@
1
1
  import { f as TFieldOps } from "./ops-AqhV7s9o.mjs";
2
2
  import { AtscriptExprNode, AtscriptExprNode as AtscriptExprNode$1, AtscriptOrderItem, 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";
3
- 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, RelationOp, ResolvedBucket, TypedWithRelation, Uniquery, Uniquery as Uniquery$1, UniqueryControls, UniqueryControls as UniqueryControls$1, UniqueryInsights, WithRelation, WithRelation as WithRelation$1 } from "@uniqu/core";
3
+ 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, RelationOp, ResolvedBucket, ResolvedRowOrderKey, ResolvedSelectExpr, TypedWithRelation, Uniquery, Uniquery as Uniquery$1, UniqueryControls, UniqueryControls as UniqueryControls$1, UniqueryInsights, WithRelation, WithRelation as WithRelation$1 } from "@uniqu/core";
4
4
 
5
5
  //#region src/query/uniqu-select.d.ts
6
+ /** A row-level expression aggregate: `fn(<expr>)` over each row (physical field leaves). */
7
+ interface TExprAggregate {
8
+ fn: "sum" | "avg" | "min" | "max";
9
+ alias: string;
10
+ expr: AtscriptExprNode;
11
+ /** The distinct physical columns the expression reads. */
12
+ names: readonly string[];
13
+ }
14
+ /** A group-level expression: leaves name aliases of other entries or grouped columns. */
15
+ interface TSelectExpr {
16
+ alias: string;
17
+ expr: AtscriptExprNode;
18
+ /** The distinct names the expression reads (aliases of other entries, or grouped columns). */
19
+ names: readonly string[];
20
+ }
21
+ /** A `first` / `last` entry: the value of PHYSICAL `column` on the group's representative row. */
22
+ interface TFirstLast {
23
+ fn: "first" | "last";
24
+ column: string;
25
+ alias: string;
26
+ }
27
+ /** One key of the representative-row order (PHYSICAL column). */
28
+ interface TRowOrderKey {
29
+ column: string;
30
+ desc: boolean;
31
+ }
32
+ /** The arithmetic and representative-row parts of an aggregate `$select`, as the field mapper resolves them. */
33
+ interface TUniquComputed {
34
+ exprAggregates?: readonly TExprAggregate[];
35
+ exprs?: readonly TSelectExpr[];
36
+ rowOrder?: readonly TRowOrderKey[];
37
+ sources?: ReadonlyMap<string, TDbFieldMeta>;
38
+ }
6
39
  /**
7
40
  * Wraps a raw `$select` value and provides lazy-cached conversions
8
41
  * to the forms different adapters need.
@@ -13,8 +46,9 @@ import { AggregateControls, AggregateExpr, AggregateExpr as AggregateExpr$1, Agg
13
46
  * For exclusion → inclusion inversion, pass `allFields` (physical field names).
14
47
  *
15
48
  * An array `$select` holds plain field names and computed entries —
16
- * aggregates (`{ $fn, $field }`, {@link aggregates}) and calendar buckets
17
- * (`{ $bucket, $field }`, {@link buckets}). Entries arrive normalized
49
+ * aggregates (`{ $fn, $field }`, {@link aggregates}), `first` / `last`
50
+ * ({@link firstLast}), arithmetic ({@link exprAggregates}, {@link exprs}) and
51
+ * calendar buckets (`{ $bucket, $field }`, {@link buckets}). Entries arrive normalized
18
52
  * (`normalizeComputedSelect` rejects any other shape before translation).
19
53
  */
20
54
  declare class UniquSelect {
@@ -23,7 +57,6 @@ declare class UniquSelect {
23
57
  private _allFields?;
24
58
  private _array;
25
59
  private _projection;
26
- private _aggregates;
27
60
  /**
28
61
  * The calendar buckets of an aggregate `$select`, normalized (canonical
29
62
  * zone, week start, alias) with the PHYSICAL source `field` and its
@@ -32,14 +65,56 @@ declare class UniquSelect {
32
65
  * columns). Since 0.1.132.
33
66
  */
34
67
  readonly buckets: readonly TResolvedBucket[] | undefined;
68
+ /**
69
+ * The plain aggregates (`{ $fn, $field }`) of an array-form `$select`;
70
+ * `undefined` when there are none or the `$select` is object form.
71
+ * `first` / `last` are listed separately ({@link firstLast}), so an adapter
72
+ * written before them never meets one here.
73
+ */
74
+ readonly aggregates: AggregateExpr[] | undefined;
75
+ /** The `first` / `last` entries of an array-form `$select` (since 0.1.148); `undefined` when none. */
76
+ readonly firstLast: readonly TFirstLast[] | undefined;
77
+ /**
78
+ * Row-level expression aggregates (`sum(price*qty)`) — leaves are physical
79
+ * columns; render `fn(<expr>)` per row. `undefined` when there are none.
80
+ * Only reaches adapters whose `supportsAggregateExpressions()` is true.
81
+ * Since 0.1.148.
82
+ */
83
+ readonly exprAggregates: readonly TExprAggregate[] | undefined;
84
+ /**
85
+ * Group-level expressions in dependency order: each leaf names an alias
86
+ * defined by an aggregate / `first` / `last` / expression entry or a
87
+ * grouped column; evaluate after grouping. `undefined` when there are none.
88
+ * Since 0.1.148.
89
+ */
90
+ readonly exprs: readonly TSelectExpr[] | undefined;
91
+ /**
92
+ * The order of the rows inside each group that `first` / `last` read, as
93
+ * physical columns, with the primary key appended as the final ascending
94
+ * tie-break. `undefined` without `first` / `last`. Since 0.1.148.
95
+ */
96
+ readonly rowOrder: readonly TRowOrderKey[] | undefined;
97
+ /**
98
+ * The descriptors of the PHYSICAL columns a `min` / `max` / `first` / `last`
99
+ * reads — for an engine that cannot aggregate a type directly (PostgreSQL
100
+ * has no `MIN(boolean)`). Since 0.1.148.
101
+ */
102
+ readonly sources: ReadonlyMap<string, TDbFieldMeta>;
103
+ /**
104
+ * The output alias of every computed entry but the calendar buckets, in the
105
+ * order adapters emit them: {@link aggregates}, {@link exprAggregates},
106
+ * {@link firstLast}, then {@link exprs} (which read the others). Since 0.1.148.
107
+ */
108
+ readonly computedAliases: readonly string[];
35
109
  /**
36
110
  * @param raw - the `$select` value (field paths already physical).
37
111
  * @param allFields - physical field names, for exclusion-form inversion.
38
112
  * @param buckets - the resolved calendar buckets of the raw `$select`'s
39
113
  * `{ $bucket }` entries (the field mappers supply them — physical `field`,
40
114
  * source `fd`).
115
+ * @param computed - the resolved arithmetic and `$rowOrder` parts (physical).
41
116
  */
42
- constructor(raw: UniqueryControls["$select"], allFields?: string[], buckets?: readonly TResolvedBucket[]);
117
+ constructor(raw: UniqueryControls["$select"], allFields?: string[], buckets?: readonly TResolvedBucket[], computed?: TUniquComputed);
43
118
  /**
44
119
  * Resolved inclusion array of plain field names (strings only).
45
120
  * Computed entries (aggregates, calendar buckets) are filtered out.
@@ -53,11 +128,6 @@ declare class UniquSelect {
53
128
  * AggregateExpr objects in array form are ignored.
54
129
  */
55
130
  get asProjection(): Record<string, 0 | 1> | undefined;
56
- /**
57
- * Extracts AggregateExpr entries from array-form $select.
58
- * Returns undefined if no aggregates present or if $select is object form.
59
- */
60
- get aggregates(): AggregateExpr[] | undefined;
61
131
  /** Whether the $select contains any AggregateExpr entries. */
62
132
  get hasAggregates(): boolean;
63
133
  /** The calendar bucket whose alias is `key`, if any — how adapters resolve a `$groupBy` / `$having` key. */
@@ -129,11 +199,28 @@ declare abstract class FieldMappingStrategy {
129
199
  * `buckets` are the query's calendar buckets as the core's normalizer
130
200
  * resolved them (`normalizeComputedSelect` — `AtscriptDbReadable.aggregate`
131
201
  * runs it before the guards); they reach adapters with `field` made
132
- * physical and the source descriptor as `fd`.
202
+ * physical and the source descriptor as `fd`. `exprs` / `rowOrder` are the
203
+ * arithmetic entries and `$rowOrder` keys of the same normalizer
204
+ * (`resolveComputedSelect`): they reach adapters as `$select.exprAggregates`
205
+ * / `.exprs` / `.rowOrder` with physical names (the primary key appended to
206
+ * the order); `$rowOrder` itself is not forwarded (since 0.1.148).
207
+ */
208
+ translateAggregateQuery(query: AggregateQuery, meta: TableMetadata, buckets: readonly ResolvedBucket[], exprs?: readonly ResolvedSelectExpr[], rowOrder?: readonly ResolvedRowOrderKey[]): DbQuery;
209
+ /**
210
+ * `$having` with physical keys — except the computed output `aliases`,
211
+ * which stay as written (an alias equal to a renamed field's name,
212
+ * `first(raisedAt):raisedAt`, is the alias, exactly as in `$sort`).
133
213
  */
134
- translateAggregateQuery(query: AggregateQuery, meta: TableMetadata, buckets: readonly ResolvedBucket[]): DbQuery;
135
- /** Output aliases of the computed `$select` entries (aggregates and calendar buckets). */
214
+ private translateHaving;
215
+ /** Output aliases of the computed `$select` entries (aggregates, expressions and calendar buckets). */
136
216
  private computedAliasSet;
217
+ /**
218
+ * The arithmetic and `$rowOrder` parts of a grouped query with PHYSICAL
219
+ * names: a row-level operand is a column; a group-level operand stays an
220
+ * alias, or becomes the physical name of a `$groupBy` field. The primary
221
+ * key is appended to the order as the final ascending tie-break.
222
+ */
223
+ private physicalComputed;
137
224
  /**
138
225
  * `$select` with its field paths made physical: array-form names and
139
226
  * computed `$field`s (`'*'` kept), or the keys of the object
@@ -535,6 +622,8 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
535
622
  * configured with an `encryption` options block.
536
623
  */
537
624
  setEncryption(encryption: DbEncryption | undefined): void;
625
+ /** @internal Set by the owning `DbSpace` when it closes. */
626
+ _spaceClosed: boolean;
538
627
  /** Ensures metadata is built. Called before any metadata access. */
539
628
  protected _ensureBuilt(): void;
540
629
  /**
@@ -612,6 +701,12 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
612
701
  get indexes(): Map<string, TDbIndex>;
613
702
  /** Primary key field names from `@meta.id`. */
614
703
  get primaryKeys(): readonly string[];
704
+ /**
705
+ * Physical column lists that must be unique: the primary key (when declared)
706
+ * followed by every unique index and every adapter-contributed unique field. Used by conflict-ignoring inserts.
707
+ * @since 0.1.148
708
+ */
709
+ get uniqueKeySets(): string[][];
615
710
  /** Preferred row identifier field names. Defaults to primary keys. */
616
711
  get preferredId(): readonly string[];
617
712
  /** Legitimate row-identifier shapes (primary key + every unique index). */
@@ -850,12 +945,20 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
850
945
  * then reverse-maps and applies fromStorage formatters on results.
851
946
  */
852
947
  aggregate(query: AggregateQuery): Promise<Array<Record<string, unknown>>>;
948
+ /**
949
+ * The computed aliases of one source field that is a boolean or a decimal —
950
+ * `min` / `max` / `first` / `last` — with that field's descriptor: the
951
+ * aggregate row's value is coerced like the column's own on read.
952
+ */
953
+ private _aliasFields;
853
954
  /** Whether the underlying adapter supports text search. */
854
955
  isSearchable(): boolean;
855
956
  /** Whether the adapter can filter on a given field (proxies adapter capability). */
856
957
  canFilterField(fd: TDbFieldMeta): boolean;
857
958
  /** Calendar-bucket units the adapter can group by (proxies adapter capability; empty = none). */
858
959
  calendarBucketUnits(): ReadonlySet<BucketUnit>;
960
+ /** Whether the adapter renders aggregate arithmetic (proxies adapter capability). @since 0.1.148 */
961
+ supportsAggregateExpressions(): boolean;
859
962
  /** Aggregate functions the adapter renders (proxies adapter capability). @since 0.1.136 */
860
963
  aggregateFns(): ReadonlySet<AggregateFn>;
861
964
  /** Whether the adapter can sort by a given field (proxies adapter capability). */
@@ -1132,7 +1235,10 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
1132
1235
  * Inserts a single record. Delegates to {@link insertMany} for unified
1133
1236
  * nested creation support.
1134
1237
  */
1135
- insertOne(payload: DbPatch<DataType>, opts?: TWriteOptions<DataType>): Promise<TDbInsertResult>;
1238
+ insertOne(payload: DbPatch<DataType>, opts: TInsertOptions<DataType> & {
1239
+ onConflict: "ignore";
1240
+ }): Promise<TDbInsertIgnoreResult>;
1241
+ insertOne(payload: DbPatch<DataType>, opts?: TInsertOptions<DataType>): Promise<TDbInsertResult>;
1136
1242
  /**
1137
1243
  * Inserts multiple records with batch-optimized nested creation.
1138
1244
  *
@@ -1149,7 +1255,11 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
1149
1255
  * 0.1.143) runs once after every phase with the inserted rows' primary-key
1150
1256
  * filters — see {@link TWriteOptions}.
1151
1257
  */
1152
- insertMany(payloads: Array<DbPatch<DataType>>, opts?: TWriteOptions<DataType>): Promise<TDbInsertManyResult>;
1258
+ insertMany(payloads: Array<DbPatch<DataType>>, opts: TInsertOptions<DataType> & {
1259
+ onConflict: "ignore";
1260
+ }): Promise<TDbInsertManyIgnoreResult>;
1261
+ insertMany(payloads: Array<DbPatch<DataType>>, opts?: TInsertOptions<DataType>): Promise<TDbInsertManyResult>;
1262
+ private _insertMany;
1153
1263
  /**
1154
1264
  * Replaces a single record identified by primary key(s).
1155
1265
  * Delegates to {@link bulkReplace} for unified nested relation support.
@@ -1303,6 +1413,24 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
1303
1413
  private _writtenPkFilters;
1304
1414
  /** Invokes a {@link TWriteOptions.check} with de-duplicated PK filters (inside the transaction). */
1305
1415
  private _runWriteCheck;
1416
+ /**
1417
+ * Ignore mode never writes a related parent — it would be orphaned when the
1418
+ * row is skipped. A TO object that names only the target's key fields
1419
+ * (`{ org: { id: 9 } }`) creates nothing: it is a reference to an existing
1420
+ * parent, so it becomes the row's foreign key (the existence check follows
1421
+ * with the other FK validation). Runs before validation (which would demand
1422
+ * the parent's required fields); any other TO object is left for
1423
+ * {@link _rejectNestedToInIgnoreMode}.
1424
+ */
1425
+ private _linkToByKeyInIgnoreMode;
1426
+ /** Ignore mode: whatever TO object is left would create a related parent (see {@link _linkToByKeyInIgnoreMode}). */
1427
+ private _rejectNestedToInIgnoreMode;
1428
+ /**
1429
+ * Conflict-ignoring main insert: marks rows repeating an earlier row's
1430
+ * primary / unique key tuple (NULL components never collide), sends the rest
1431
+ * to the adapter and assembles one slot per input row.
1432
+ */
1433
+ private _insertIgnoring;
1306
1434
  /**
1307
1435
  * The exact primary-key filter of each inserted row: the logical key from
1308
1436
  * the row (SDK defaults applied), else the stored key the adapter wrote into
@@ -1451,6 +1579,14 @@ interface TViewPlan {
1451
1579
  * via the provided resolver function.
1452
1580
  */
1453
1581
  declare function translateQueryTree(node: AtscriptQueryNode, resolveField: (ref: AtscriptQueryFieldRef) => string): FilterExpr;
1582
+ /**
1583
+ * Evaluates a computed expression in process (the memory adapter, a test, a
1584
+ * third-party adapter) with the semantics every adapter shares: IEEE double,
1585
+ * a leaf is `Number()`-ed, NULL / undefined propagates as `null`, `/` by zero
1586
+ * is `null`, `coalesce` returns its first non-null value.
1587
+ * @since 0.1.148
1588
+ */
1589
+ declare function evaluateExpr(expr: AtscriptExprNode, leaf: (field: string) => unknown): number | null;
1454
1590
  //#endregion
1455
1591
  //#region src/table/view-source.d.ts
1456
1592
  /**
@@ -1680,6 +1816,14 @@ interface TDbSpaceOptions {
1680
1816
  logger?: TGenericLogger;
1681
1817
  /** Field-level encryption configuration for `@db.encrypted` fields. */
1682
1818
  encryption?: TDbEncryptionOptions;
1819
+ /**
1820
+ * Runs once from {@link DbSpace.close}, after every adapter was disposed.
1821
+ * Pass the resource the space does not own by itself, e.g.
1822
+ * `() => driver.close()`. The `createAdapter` helpers of the adapter
1823
+ * packages set it to close the driver they built.
1824
+ * @since 0.1.148
1825
+ */
1826
+ onClose?: () => void | Promise<void>;
1683
1827
  }
1684
1828
  /**
1685
1829
  * A database space — a registry of tables and views sharing the same adapter type and driver.
@@ -1699,13 +1843,21 @@ interface TDbSpaceOptions {
1699
1843
  * const activeUsers = db.getView(ActiveUsersType)
1700
1844
  * ```
1701
1845
  */
1702
- declare class DbSpace {
1846
+ declare class DbSpace implements AsyncDisposable {
1703
1847
  protected readonly adapterFactory: TAdapterFactory;
1848
+ /** `await using` support — an alias for {@link close}, installed below where the runtime has the symbol. */
1849
+ [Symbol.asyncDispose]: () => Promise<void>;
1704
1850
  private _readables;
1705
1851
  /** All tables created in this space — used for reverse FK lookup during cascade. */
1706
1852
  private _allTables;
1853
+ /** Every table / view handle the space created — flagged closed on {@link close}. */
1854
+ private _handles;
1707
1855
  /** Lazily created adapter for administrative ops (drop table/view) that don't need a registered readable. */
1708
1856
  private _adminAdapter?;
1857
+ /** Every adapter this space created (tables, views, admin) — disposed on close. */
1858
+ private _adapters;
1859
+ private _onClose?;
1860
+ private _closing?;
1709
1861
  protected readonly logger: TGenericLogger;
1710
1862
  /** Encryption service for `@db.encrypted` fields — validated eagerly at construction. */
1711
1863
  protected readonly _encryption?: DbEncryption;
@@ -1770,6 +1922,23 @@ declare class DbSpace {
1770
1922
  * Optional call: an adapter built against an older `@atscript/db` copy lacks it.
1771
1923
  */
1772
1924
  private _createAdapter;
1925
+ /** `true` once {@link close} was called. @since 0.1.148 */
1926
+ get closed(): boolean;
1927
+ /** Throws `SPACE_CLOSED` once the space is closed. */
1928
+ private _assertOpen;
1929
+ /**
1930
+ * Closes the space: marks it closed, disposes every adapter it created, then
1931
+ * runs the `onClose` hook (the `createAdapter` helpers close their driver
1932
+ * there). Idempotent — every call returns the first call's promise. Every
1933
+ * step is attempted; failures are collected into an `AggregateError`.
1934
+ *
1935
+ * It does not cancel or drain in-flight queries: call it after the server
1936
+ * stopped accepting requests. After close, `get*` and operations on existing
1937
+ * table/view handles throw `DbError("SPACE_CLOSED")`.
1938
+ * @since 0.1.148
1939
+ */
1940
+ close(): Promise<void>;
1941
+ private _doClose;
1773
1942
  /**
1774
1943
  * Finds all child tables with FKs pointing to the given parent table name.
1775
1944
  * Accesses `table.foreignKeys` which triggers `_flatten()` if needed.
@@ -1863,6 +2032,13 @@ declare abstract class BaseDbAdapter {
1863
2032
  * @since 0.1.137
1864
2033
  */
1865
2034
  registerSpace(_space: DbSpace): void;
2035
+ /**
2036
+ * Releases timers, caches or change streams the adapter holds. Called once
2037
+ * by {@link DbSpace.close}; the driver itself is closed by the space's
2038
+ * `onClose` hook, never here. No-op by default.
2039
+ * @since 0.1.148
2040
+ */
2041
+ dispose?(): void | Promise<void>;
1866
2042
  /**
1867
2043
  * Enables or disables verbose (debug-level) logging for this adapter.
1868
2044
  * When disabled, no log strings are constructed — zero overhead.
@@ -2003,6 +2179,20 @@ declare abstract class BaseDbAdapter {
2003
2179
  * @since 0.1.136
2004
2180
  */
2005
2181
  aggregateFns(): ReadonlySet<AggregateFn>;
2182
+ /**
2183
+ * Whether this adapter renders arithmetic in an aggregate `$select`
2184
+ * (`{ $expr }`, `{ $fn, $expr }`) — IEEE double, NULL propagating, `/` by
2185
+ * zero NULL, the same semantics as `@db.compute`. Default `false`
2186
+ * (fail-closed): the core rejects such a query with `AGG_EXPR_NOT_SUPPORTED`
2187
+ * before dispatch. An adapter returning `true` also receives
2188
+ * `controls.$select.exprAggregates` / `.exprs` (see `UniquSelect`), and
2189
+ * `first` / `last` (`aggregateFns()`) with `.firstLast` and `.rowOrder`;
2190
+ * the shared `evaluateExpr` evaluates an expression tree in process.
2191
+ * moost-db advertises it as `/meta.aggregateExpressions`.
2192
+ *
2193
+ * @since 0.1.148
2194
+ */
2195
+ supportsAggregateExpressions(): boolean;
2006
2196
  /**
2007
2197
  * Revision of how this adapter renders a managed view (its SQL / pipeline)
2008
2198
  * from an unchanged view definition. Stored in each managed view's sync
@@ -2357,6 +2547,21 @@ declare abstract class BaseDbAdapter {
2357
2547
  aggregate(_query: DbQuery): Promise<Array<Record<string, unknown>>>;
2358
2548
  abstract insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
2359
2549
  abstract insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
2550
+ /**
2551
+ * Conflict-ignoring batch insert (`insertMany(rows, { onConflict: 'ignore' })`).
2552
+ * Returns ONE SLOT PER INPUT ROW, in order: `{ insertedId }` for an inserted
2553
+ * row, `null` for a row skipped because it collided with a STORED row on the
2554
+ * primary key or a unique index. (The core already removed duplicates inside
2555
+ * the batch.) Only uniqueness collisions are skipped — NOT NULL, FK, check
2556
+ * and every other error must throw — and a skipped row must never abort the
2557
+ * surrounding transaction. Fail-closed by default: throws
2558
+ * `DbError("ON_CONFLICT_NOT_SUPPORTED")`; also override
2559
+ * {@link supportsInsertIgnore}.
2560
+ * @since 0.1.148
2561
+ */
2562
+ insertManyIgnore(_data: Array<Record<string, unknown>>): Promise<TDbInsertIgnoreSlot[]>;
2563
+ /** Whether {@link insertManyIgnore} is implemented (drives `crud.insert: ["onConflict"]` in `/meta`). @since 0.1.148 */
2564
+ supportsInsertIgnore(): boolean;
2360
2565
  abstract replaceOne(filter: FilterExpr, data: Record<string, unknown>, expectedVersion?: number): Promise<TDbUpdateResult>;
2361
2566
  abstract updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number): Promise<TDbUpdateResult>;
2362
2567
  abstract deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
@@ -2855,7 +3060,7 @@ declare class TableMetadata {
2855
3060
  //#endregion
2856
3061
  //#region src/types.d.ts
2857
3062
  /** Controls with resolved projection. Used in the adapter interface. */
2858
- interface DbControls extends Omit<UniqueryControls, "$select"> {
3063
+ interface DbControls extends Omit<UniqueryControls, "$select" | "$rowOrder"> {
2859
3064
  $select?: UniquSelect;
2860
3065
  }
2861
3066
  /** Query object with resolved projection. Passed to adapter methods. */
@@ -2933,6 +3138,13 @@ interface TFieldMeta {
2933
3138
  * (`bucketUnits`). Since 0.1.132.
2934
3139
  */
2935
3140
  bucketable?: true;
3141
+ /**
3142
+ * Present (true) exactly when the field may be an operand of query-time
3143
+ * arithmetic (`sum(price*qty)`, `expr(a/b)`) — a plain `number` (not a
3144
+ * decimal or timestamp) that is visible and physically aggregatable — on an
3145
+ * adapter with `aggregateExpressions`. Since 0.1.148.
3146
+ */
3147
+ numeric?: true;
2936
3148
  /**
2937
3149
  * Present (true) when the field is a `@db.column.derived` column: its value
2938
3150
  * is computed from a `@db.json` field of the same row and is never written
@@ -2947,6 +3159,20 @@ interface TFieldMeta {
2947
3159
  * Since 0.1.147.
2948
3160
  */
2949
3161
  computed?: true;
3162
+ /**
3163
+ * Present (true) exactly when `$groupBy` on this field passes the gate:
3164
+ * physically filterable (adapter, not write-only, not encrypted) and, on a
3165
+ * table declaring dimensions or measures, a dimension. Since 0.1.148.
3166
+ */
3167
+ groupable?: true;
3168
+ /**
3169
+ * Present (true) for a declared display-only decoration (`@DbDecorations`):
3170
+ * a value the controller computes (`decorateRows`) and attaches to rows. Name
3171
+ * it in `$select` to receive it; it is never filterable, sortable or
3172
+ * groupable, and its column definition is in {@link TMetaResponse.decorations}
3173
+ * (not in `type`). Since 0.1.148.
3174
+ */
3175
+ decoration?: true;
2950
3176
  }
2951
3177
  /** Built-in CRUD operation names; map 1:1 to public method names. */
2952
3178
  type TCrudOp = "query" | "pages" | "one" | "geo" | "insert" | "update" | "replace" | "remove";
@@ -2969,6 +3195,14 @@ interface TMetaResponse {
2969
3195
  relations: TRelationInfo[];
2970
3196
  fields: Record<string, TFieldMeta>;
2971
3197
  type: TSerializedAnnotatedType;
3198
+ /**
3199
+ * The declared display-only fields (`@DbDecorations`): an object type whose
3200
+ * props are the decorations (with their `@meta.*`, `@expect.*` and `@ui.*`
3201
+ * annotations), each also listed in {@link fields} with `decoration: true`.
3202
+ * Absent when none is declared (or none is visible to the caller). Not part
3203
+ * of {@link type}, so forms and write validation never see it. Since 0.1.148.
3204
+ */
3205
+ decorations?: TSerializedAnnotatedType;
2972
3206
  actions: TDbActionInfo[];
2973
3207
  crud: TCrudPermissions;
2974
3208
  /**
@@ -2993,6 +3227,15 @@ interface TMetaResponse {
2993
3227
  * @since 0.1.136
2994
3228
  */
2995
3229
  aggregateFns?: AggregateFn[];
3230
+ /**
3231
+ * Whether the adapter renders arithmetic in an aggregate `$select`
3232
+ * (`{ $expr }`, URL `expr(a/b):alias`, `sum(price*qty):alias`) — see
3233
+ * `BaseDbAdapter.supportsAggregateExpressions()`. Fields that may be
3234
+ * operands are flagged `fields[P].numeric`.
3235
+ *
3236
+ * @since 0.1.148
3237
+ */
3238
+ aggregateExpressions?: boolean;
2996
3239
  }
2997
3240
  /** Where the action applies on the UI. */
2998
3241
  type TDbActionLevel = "table" | "row" | "rows";
@@ -3170,6 +3413,35 @@ interface TDbInsertManyResult {
3170
3413
  insertedCount: number;
3171
3414
  insertedIds: unknown[];
3172
3415
  }
3416
+ /**
3417
+ * `insertOne` result in conflict-ignoring mode (`onConflict: 'ignore'`).
3418
+ * @since 0.1.148
3419
+ */
3420
+ interface TDbInsertIgnoreResult {
3421
+ /** Id of the inserted row; absent when the row was skipped. */
3422
+ insertedId?: unknown;
3423
+ /** `true` when the row collided on the primary key or a unique index and was skipped. */
3424
+ conflict: boolean;
3425
+ }
3426
+ /**
3427
+ * `insertMany` result in conflict-ignoring mode (`onConflict: 'ignore'`):
3428
+ * `insertedCount` / `insertedIds` cover the INSERTED rows only (dense, input
3429
+ * order); `inserted` and `conflicts` map back to input indices.
3430
+ * @since 0.1.148
3431
+ */
3432
+ interface TDbInsertManyIgnoreResult extends TDbInsertManyResult {
3433
+ /** Input index of each `insertedIds` entry. */
3434
+ inserted: number[];
3435
+ /** Input indices skipped because of a unique / primary-key conflict, ascending. */
3436
+ conflicts: number[];
3437
+ }
3438
+ /**
3439
+ * One slot per input row of `BaseDbAdapter.insertManyIgnore`: the inserted id,
3440
+ * or `null` for a skipped (conflicting) row.
3441
+ */
3442
+ type TDbInsertIgnoreSlot = {
3443
+ insertedId: unknown;
3444
+ } | null;
3173
3445
  interface TDbUpdateResult {
3174
3446
  matchedCount: number;
3175
3447
  modifiedCount: number;
@@ -3800,6 +4072,21 @@ interface TWriteOptions<Row = Record<string, unknown>> extends TIdResolveOptions
3800
4072
  */
3801
4073
  check?: TDbWriteCheck;
3802
4074
  }
4075
+ /**
4076
+ * Options of `insertOne` / `insertMany`.
4077
+ * @since 0.1.148
4078
+ */
4079
+ interface TInsertOptions<Row = Record<string, unknown>> extends TWriteOptions<Row> {
4080
+ /**
4081
+ * `'error'` (default): a unique / primary-key violation throws `CONFLICT`.
4082
+ * `'ignore'`: rows that collide on the primary key or any unique index —
4083
+ * with a stored row or an earlier row of the same batch — are skipped and
4084
+ * reported by input index. Everything else (validation, NOT NULL, FK, check,
4085
+ * guard) still throws and rolls the call back. A row that creates a related
4086
+ * parent (`@db.rel.to` nested object) is rejected in this mode.
4087
+ */
4088
+ onConflict?: "error" | "ignore";
4089
+ }
3803
4090
  /**
3804
4091
  * Context handed to a write {@link TWriteOptions.check} (since 0.1.143) — and
3805
4092
  * through it to `AsDbController.checkWrite()`. Lets a permission layer verify
@@ -3938,7 +4225,14 @@ interface TBucketFieldSource {
3938
4225
  declare function normalizeComputedSelect(controls: {
3939
4226
  $select?: unknown;
3940
4227
  $groupBy?: unknown;
4228
+ $rowOrder?: unknown;
3941
4229
  } | undefined, fields: TBucketFieldSource, aggregate?: boolean): ResolvedBucket[];
4230
+ /** The normalized computed `$select` entries of a grouped query — see {@link resolveComputedSelect}. */
4231
+ interface TComputedSelect {
4232
+ buckets: ResolvedBucket[];
4233
+ exprs: ResolvedSelectExpr[];
4234
+ rowOrder?: ResolvedRowOrderKey[];
4235
+ }
3942
4236
  /**
3943
4237
  * @deprecated since 0.1.136 — renamed {@link normalizeComputedSelect} (it
3944
4238
  * normalizes every computed `$select` entry, aggregates included).
@@ -3969,4 +4263,4 @@ declare function isJsonValueField(fd: TDbFieldMeta): boolean;
3969
4263
  */
3970
4264
  declare function jsonValueAncestor(path: string, jsonValueParents: ReadonlySet<string>): string | undefined;
3971
4265
  //#endregion
3972
- export { TDbStorageType as $, AtscriptExprNode$1 as $t, TCrudPermissions as A, TDbEncryptionOptions as An, TValueFormatterPair as At, TDbDeleteResult as B, isGeoPointType as Bt, NullableOptional as C, andFilters as Cn, TRelationInfo as Ct, TCascadeTarget as D, isResolvedRelationFilter as Dn, TTableOptionDiff as Dt, TCascadeResolver as E, hasRelationOp as En, TSyncColumnResult as Et, TDbActionTargetSummary as F, TGenericLogger as Fn, Uniquery$1 as Ft, TDbIndexType as G, DbSpace as Gt, TDbForeignKey as H, ALL_VIEW_CAPABILITIES as Ht, TDbAvailableActions as I, UniquSelect as In, UniqueryControls$1 as It, TDbObjectKind as J, AtscriptDbView as Jt, TDbInsertManyResult as K, TAdapterFactory as Kt, TDbCollation as L, WithRelation$1 as Lt, TDbActionIntent as M, FieldMappingStrategy as Mn, TWriteOptions as Mt, TDbActionLevel as N, TReadControls as Nn, TWriteTableResolver as Nt, TColumnDiff as O, relationStaticFilter as On, TTableResolver as Ot, TDbActionProcessor as P, NoopLogger as Pn, TypedWithRelation as Pt, TDbRemoveGuardContext as Q, aliasTargetOf as Qt, TDbDefaultFn as R, TableMetadata as Rt, NavPropsOf$1 as S, TRelationStaticFilter as Sn, TReferencingForeignKey as St, PrimaryKeyOf$1 as T, forEachResolvedRelation as Tn, TSearchIndexInfo as Tt, TDbIndex as U, BaseDbAdapter as Ut, TDbFieldMeta as V, ALL_BUCKET_UNITS as Vt, TDbIndexField as W, TViewCapability as Wt, TDbRelation as X, isAtscriptDbView as Xt, TDbReferentialAction as Y, TViewColumnMapping as Yt, TDbRemoveGuard as Z, isViewType as Zt, DbQuery as _, REL_FILTER_MAX_NODES as _n, TIdResolveOptions as _t, jsonValueAncestor as a, TViewJoin as an, TDbWriteGuardContext as at, FilterExpr$1 as b, TRelationFilterJunction as bn, TMetadataOverrides as bt, AggregateControls as c, translateQueryTree as cn, TDerivedColumn as ct, AggregateQuery$1 as d, NativeIntegrity as dn, TExistingForeignKey as dt, AtscriptOrderItem as en, TDbUpdateResult as et, AggregateResult as f, TCascadePin as fn, TExistingTableOption as ft, DbPatch as g, REL_FILTER_MAX_DEPTH as gn, TIdDescriptor as gt, DbControls as h, resolveDesignType as hn, TFkLookupTarget as ht, isJsonValueField as i, AtscriptRef as in, TDbWriteGuard as it, TDbActionInfo as j, DocumentFieldMapper as jn, TViewJsonType as jt, TCrudOp as k, DbEncryption as kn, TTouchManyOptions as kt, AggregateExpr$1 as l, AtscriptDbTable as ln, TEnsureTableOptions as lt, AtscriptDbWritable as m, DbResponse as mn, TFkLookupResolver as mt, TResolvedBucket as n, AtscriptQueryFieldRef$1 as nn, TDbWriteCheck as nt, normalizeComputedSelect as o, TViewPlan as on, TDeleteOptions as ot, AtscriptDbTableLike as p, AtscriptDbReadable as pn, TFieldMeta as pt, TDbInsertResult as q, TDbSpaceOptions as qt, isBucketableField as r, AtscriptQueryNode$1 as rn, TDbWriteCheckContext as rt, resolveCalendarBuckets as s, isFieldRef as sn, TDerivedChangeReason as st, TBucketFieldSource as t, AtscriptQueryComparison as tn, TDbWriteAction as tt, AggregateFn$1 as u, IntegrityStrategy as un, TExistingColumn as ut, DbRow as v, ResolvedRelationFilter as vn, TIdentification as vt, OwnPropsOf$1 as w, containsRelationFilter as wn, TRowResolveOptions as wt, FlatOf$1 as x, TRelationFilterTable as xn, TPrimaryKeyChange as xt, FieldOpsFor as y, TRelGuardState as yn, TMetaResponse as yt, TDbDefaultValue as z, isGeoIndexableType as zt };
4266
+ export { TDbReferentialAction as $, AtscriptDbView as $t, TCrudOp as A, containsRelationFilter as An, TSearchIndexInfo as At, TDbDefaultValue as B, NoopLogger as Bn, Uniquery$1 as Bt, NavPropsOf$1 as C, REL_FILTER_MAX_NODES as Cn, TInsertOptions as Ct, TCascadeResolver as D, TRelationFilterTable as Dn, TReferencingForeignKey as Dt, PrimaryKeyOf$1 as E, TRelationFilterJunction as En, TPrimaryKeyChange as Et, TDbActionProcessor as F, DbEncryption as Fn, TValueFormatterPair as Ft, TDbIndexField as G, UniquSelect as Gn, isGeoPointType as Gt, TDbFieldMeta as H, TExprAggregate as Hn, WithRelation$1 as Ht, TDbActionTargetSummary as I, TDbEncryptionOptions as In, TViewJsonType as It, TDbInsertIgnoreSlot as J, BaseDbAdapter as Jt, TDbIndexType as K, ALL_BUCKET_UNITS as Kt, TDbAvailableActions as L, DocumentFieldMapper as Ln, TWriteOptions as Lt, TDbActionInfo as M, hasRelationOp as Mn, TTableOptionDiff as Mt, TDbActionIntent as N, isResolvedRelationFilter as Nn, TTableResolver as Nt, TCascadeTarget as O, TRelationStaticFilter as On, TRelationInfo as Ot, TDbActionLevel as P, relationStaticFilter as Pn, TTouchManyOptions as Pt, TDbObjectKind as Q, TDbSpaceOptions as Qt, TDbCollation as R, FieldMappingStrategy as Rn, TWriteTableResolver as Rt, FlatOf$1 as S, REL_FILTER_MAX_DEPTH as Sn, TIdentification as St, OwnPropsOf$1 as T, TRelGuardState as Tn, TMetadataOverrides as Tt, TDbForeignKey as U, TFirstLast as Un, TableMetadata as Ut, TDbDeleteResult as V, TGenericLogger as Vn, UniqueryControls$1 as Vt, TDbIndex as W, TRowOrderKey as Wn, isGeoIndexableType as Wt, TDbInsertManyResult as X, DbSpace as Xt, TDbInsertManyIgnoreResult as Y, TViewCapability as Yt, TDbInsertResult as Z, TAdapterFactory as Zt, DbPatch as _, NativeIntegrity as _n, TFieldMeta as _t, isJsonValueField as a, AtscriptOrderItem as an, TDbWriteAction as at, FieldOpsFor as b, DbResponse as bn, TIdDescriptor as bt, resolveCalendarBuckets as c, AtscriptQueryNode$1 as cn, TDbWriteGuard as ct, AggregateFn$1 as d, TViewPlan as dn, TDerivedChangeReason as dt, TViewColumnMapping as en, TDbRelation as et, AggregateQuery$1 as f, evaluateExpr as fn, TDerivedColumn as ft, DbControls as g, IntegrityStrategy as gn, TExistingTableOption as gt, AtscriptDbWritable as h, AtscriptDbTable as hn, TExistingForeignKey as ht, isBucketableField as i, AtscriptExprNode$1 as in, TDbUpdateResult as it, TCrudPermissions as j, forEachResolvedRelation as jn, TSyncColumnResult as jt, TColumnDiff as k, andFilters as kn, TRowResolveOptions as kt, AggregateControls as l, AtscriptRef as ln, TDbWriteGuardContext as lt, AtscriptDbTableLike as m, translateQueryTree as mn, TExistingColumn as mt, TComputedSelect as n, isViewType as nn, TDbRemoveGuardContext as nt, jsonValueAncestor as o, AtscriptQueryComparison as on, TDbWriteCheck as ot, AggregateResult as p, isFieldRef as pn, TEnsureTableOptions as pt, TDbInsertIgnoreResult as q, ALL_VIEW_CAPABILITIES as qt, TResolvedBucket as r, aliasTargetOf as rn, TDbStorageType as rt, normalizeComputedSelect as s, AtscriptQueryFieldRef$1 as sn, TDbWriteCheckContext as st, TBucketFieldSource as t, isAtscriptDbView as tn, TDbRemoveGuard as tt, AggregateExpr$1 as u, TViewJoin as un, TDeleteOptions as ut, DbQuery as v, TCascadePin as vn, TFkLookupResolver as vt, NullableOptional as w, ResolvedRelationFilter as wn, TMetaResponse as wt, FilterExpr$1 as x, resolveDesignType as xn, TIdResolveOptions as xt, DbRow as y, AtscriptDbReadable as yn, TFkLookupTarget as yt, TDbDefaultFn as z, TReadControls as zn, TypedWithRelation as zt };