@atscript/db 0.1.146 → 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 (59) 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-CGBv3E8S.cjs → aggregate-fns-C-UJRobm.cjs} +52 -5
  8. package/dist/{aggregate-fns-CfsveE1w.mjs → aggregate-fns-CyaZyb9I.mjs} +35 -6
  9. package/dist/aggregate-rules-D_bsCpUI.cjs +26 -0
  10. package/dist/aggregate-rules-jdPrxqWa.mjs +15 -0
  11. package/dist/{buckets-DRycmhOW.d.mts → buckets-CNdTOnei.d.mts} +832 -33
  12. package/dist/{buckets-DYFu0eZ8.d.cts → buckets-DJiYlMXc.d.cts} +832 -33
  13. package/dist/{column-diff-D_Kyuh0S.cjs → column-diff-CUU4GvYg.cjs} +1546 -193
  14. package/dist/{column-diff-e2oHc71_.mjs → column-diff-Cp6ZoyRE.mjs} +1529 -200
  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/{column-diff-Q9UmWn5x.d.mts → fk-diff-BQ4krij8.d.cts} +48 -2
  18. package/dist/{column-diff-w-Mym_3w.d.cts → fk-diff-DsaIijVX.d.mts} +48 -2
  19. package/dist/index.cjs +95 -35
  20. package/dist/index.d.cts +92 -22
  21. package/dist/index.d.mts +92 -22
  22. package/dist/index.mjs +65 -35
  23. package/dist/{nested-writer-xfQwxplL.cjs → nested-writer-BUQvk6QT.cjs} +734 -2
  24. package/dist/{nested-writer-CnOOAehr.mjs → nested-writer-CUBoq1ZO.mjs} +598 -4
  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 +293 -5
  30. package/dist/plugin.mjs +294 -6
  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-B-0NRKat.d.mts → relation-helpers-Ba0v49sn.d.mts} +1 -1
  36. package/dist/{relation-helpers-BOMm_HUI.d.cts → relation-helpers-kX7jjgME.d.cts} +1 -1
  37. package/dist/relation-loader-ByY1Byrl.mjs +370 -0
  38. package/dist/relation-loader-D8OrdH-r.cjs +369 -0
  39. package/dist/shared.cjs +4 -1
  40. package/dist/shared.d.cts +16 -4
  41. package/dist/shared.d.mts +16 -4
  42. package/dist/shared.mjs +3 -2
  43. package/dist/sync.cjs +11 -7
  44. package/dist/sync.d.cts +2 -25
  45. package/dist/sync.d.mts +2 -25
  46. package/dist/sync.mjs +11 -7
  47. package/dist/{validation-utils-DOsB4e6G.cjs → validation-utils-Da2GjobR.cjs} +35 -24
  48. package/dist/{validation-utils-CMR4fe2M.mjs → validation-utils-Dq0uZ7ef.mjs} +35 -24
  49. package/dist/{validator-Clu2q_7z.mjs → validator-BTiIOTKP.mjs} +9 -3
  50. package/dist/{validator-CVS-onRg.cjs → validator-UcuJxHNT.cjs} +8 -2
  51. package/dist/{validator-Bw6ks9Hy.d.mts → validator-tBNvM1qc.d.cts} +31 -7
  52. package/dist/{validator-Bw6ks9Hy.d.cts → validator-tBNvM1qc.d.mts} +31 -7
  53. package/dist/validator.cjs +2 -2
  54. package/dist/validator.d.cts +1 -1
  55. package/dist/validator.d.mts +1 -1
  56. package/dist/validator.mjs +2 -2
  57. package/package.json +8 -8
  58. package/dist/relation-loader-CBPY6kM7.cjs +0 -461
  59. package/dist/relation-loader-D9XuXaMv.mjs +0 -462
@@ -10,6 +10,25 @@ var DbError = class extends Error {
10
10
  this.stack = void 0;
11
11
  }
12
12
  };
13
+ /** Whether `error` is a `DbError` with code `CONFLICT` (unique / primary-key violation). */
14
+ function isConflict(error) {
15
+ return error instanceof DbError && error.code === "CONFLICT";
16
+ }
17
+ /** @internal The error every handle of a closed `DbSpace` throws. */
18
+ function spaceClosedError() {
19
+ return new DbError("SPACE_CLOSED", [{
20
+ path: "",
21
+ message: "The DbSpace is closed; table and view handles can no longer be used"
22
+ }]);
23
+ }
24
+ /**
25
+ * Collapses the failures of a multi-step shutdown into one `AggregateError`
26
+ * (message lists every failure).
27
+ * @internal
28
+ */
29
+ function aggregateFailure(label, errors) {
30
+ return new AggregateError(errors, `${label}: ${errors.map((e) => e instanceof Error ? e.message : String(e)).join("; ")}`);
31
+ }
13
32
  /**
14
33
  * Thrown when a write payload nests deeper than the table's
15
34
  * declared `@db.depth.limit N`. Surfaced as HTTP 400 in moost-db.
@@ -83,5 +102,17 @@ function bucketTimeZoneUnavailable(message) {
83
102
  message
84
103
  }]);
85
104
  }
105
+ /**
106
+ * The adapter's `supportsAggregateExpressions()` is false (or its SQL dialect
107
+ * cannot cast to a double) and the query uses arithmetic in an aggregate
108
+ * `$select` (`AGG_EXPR_NOT_SUPPORTED`, `path` `$select`; moost-db: 400).
109
+ * @since 0.1.148
110
+ */
111
+ function aggregateExpressionsNotSupported() {
112
+ return new DbError("AGG_EXPR_NOT_SUPPORTED", [{
113
+ path: "$select",
114
+ message: "Aggregate expressions are not supported by this adapter"
115
+ }]);
116
+ }
86
117
  //#endregion
87
- export { bucketTimeZoneUnavailable as a, DepthLimitExceededError as i, CasMismatchError as n, DbError as r, CasExhaustedError as t };
118
+ export { aggregateExpressionsNotSupported as a, isConflict as c, DepthLimitExceededError as i, spaceClosedError as l, CasMismatchError as n, aggregateFailure as o, DbError as r, bucketTimeZoneUnavailable as s, CasExhaustedError as t };
@@ -10,6 +10,25 @@ var DbError = class extends Error {
10
10
  this.stack = void 0;
11
11
  }
12
12
  };
13
+ /** Whether `error` is a `DbError` with code `CONFLICT` (unique / primary-key violation). */
14
+ function isConflict(error) {
15
+ return error instanceof DbError && error.code === "CONFLICT";
16
+ }
17
+ /** @internal The error every handle of a closed `DbSpace` throws. */
18
+ function spaceClosedError() {
19
+ return new DbError("SPACE_CLOSED", [{
20
+ path: "",
21
+ message: "The DbSpace is closed; table and view handles can no longer be used"
22
+ }]);
23
+ }
24
+ /**
25
+ * Collapses the failures of a multi-step shutdown into one `AggregateError`
26
+ * (message lists every failure).
27
+ * @internal
28
+ */
29
+ function aggregateFailure(label, errors) {
30
+ return new AggregateError(errors, `${label}: ${errors.map((e) => e instanceof Error ? e.message : String(e)).join("; ")}`);
31
+ }
13
32
  /**
14
33
  * Thrown when a write payload nests deeper than the table's
15
34
  * declared `@db.depth.limit N`. Surfaced as HTTP 400 in moost-db.
@@ -83,6 +102,18 @@ function bucketTimeZoneUnavailable(message) {
83
102
  message
84
103
  }]);
85
104
  }
105
+ /**
106
+ * The adapter's `supportsAggregateExpressions()` is false (or its SQL dialect
107
+ * cannot cast to a double) and the query uses arithmetic in an aggregate
108
+ * `$select` (`AGG_EXPR_NOT_SUPPORTED`, `path` `$select`; moost-db: 400).
109
+ * @since 0.1.148
110
+ */
111
+ function aggregateExpressionsNotSupported() {
112
+ return new DbError("AGG_EXPR_NOT_SUPPORTED", [{
113
+ path: "$select",
114
+ message: "Aggregate expressions are not supported by this adapter"
115
+ }]);
116
+ }
86
117
  //#endregion
87
118
  Object.defineProperty(exports, "CasExhaustedError", {
88
119
  enumerable: true,
@@ -108,9 +139,33 @@ Object.defineProperty(exports, "DepthLimitExceededError", {
108
139
  return DepthLimitExceededError;
109
140
  }
110
141
  });
142
+ Object.defineProperty(exports, "aggregateExpressionsNotSupported", {
143
+ enumerable: true,
144
+ get: function() {
145
+ return aggregateExpressionsNotSupported;
146
+ }
147
+ });
148
+ Object.defineProperty(exports, "aggregateFailure", {
149
+ enumerable: true,
150
+ get: function() {
151
+ return aggregateFailure;
152
+ }
153
+ });
111
154
  Object.defineProperty(exports, "bucketTimeZoneUnavailable", {
112
155
  enumerable: true,
113
156
  get: function() {
114
157
  return bucketTimeZoneUnavailable;
115
158
  }
116
159
  });
160
+ Object.defineProperty(exports, "isConflict", {
161
+ enumerable: true,
162
+ get: function() {
163
+ return isConflict;
164
+ }
165
+ });
166
+ Object.defineProperty(exports, "spaceClosedError", {
167
+ enumerable: true,
168
+ get: function() {
169
+ return spaceClosedError;
170
+ }
171
+ });
@@ -1,4 +1,4 @@
1
- import { B as TDbFieldMeta, Gt as AtscriptDbView, O as TColumnDiff, Q as TDbStorageType, Qt as AtscriptQueryNode, R as TDbDefaultValue, Zt as AtscriptQueryFieldRef, dt as TExistingTableOption, lt as TExistingColumn, sn as AtscriptDbReadable, st as TDerivedColumn } from "./buckets-DRycmhOW.mjs";
1
+ import { $t as AtscriptDbView, B as TDbDefaultValue, H as TDbFieldMeta, U as TDbForeignKey, cn as AtscriptQueryNode, ft as TDerivedColumn, gt as TExistingTableOption, k as TColumnDiff, mt as TExistingColumn, rt as TDbStorageType, sn as AtscriptQueryFieldRef, yn as AtscriptDbReadable } from "./buckets-DJiYlMXc.cjs";
2
2
 
3
3
  //#region src/schema/schema-hash.d.ts
4
4
  interface TFieldSnapshot {
@@ -66,6 +66,11 @@ interface TViewJoinSnapshot {
66
66
  condition: string;
67
67
  /** Emitted only for `"left"` — an inner join (the default) carries no key. @since 0.1.136 */
68
68
  kind?: "inner" | "left";
69
+ /**
70
+ * First-row joins only: canonical JSON `[["<scope>.<column>", 1 | -1], …]`
71
+ * of the ordering, the appended primary key included. @since 0.1.147
72
+ */
73
+ order?: string;
69
74
  }
70
75
  /**
71
76
  * One column of a managed view as stored in its snapshot — the PHYSICAL
@@ -84,6 +89,12 @@ interface TViewColumnSnapshot {
84
89
  aggField?: string;
85
90
  /** Canonical JSON of a conditional aggregate's predicate. */
86
91
  aggFilter?: string;
92
+ /**
93
+ * Computed columns only: canonical JSON of the `@db.compute` expression —
94
+ * leaves `{c: "<viewColumn>"}`, literals `{n: x}`, nodes `{op, a: [...]}`.
95
+ * @since 0.1.147
96
+ */
97
+ expr?: string;
87
98
  }
88
99
  interface TViewSnapshot {
89
100
  tableName: string;
@@ -208,4 +219,39 @@ declare function computeColumnDiff(desired: readonly TDbFieldMeta[], existing: T
208
219
  snapshot?: TTableSnapshot | null;
209
220
  }): TColumnDiff;
210
221
  //#endregion
211
- export { TTableSnapshot as a, TViewSnapshot as c, computeTableHash as d, computeTableSnapshot as f, snapshotToExistingTableOptions as h, TForeignKeySnapshot as i, canonicalizeQueryNode as l, snapshotToExistingColumns as m, isColumnTypeChanged as n, TViewColumnSnapshot as o, computeViewSnapshot as p, TFieldSnapshot as r, TViewJoinSnapshot as s, computeColumnDiff as t, computeSchemaHash as u };
222
+ //#region src/schema/fk-diff.d.ts
223
+ /**
224
+ * Desired FKs are keyed and compared by their PHYSICAL columns
225
+ * ({@link fkColumns}) — the snapshot side is physical too.
226
+ */
227
+ interface TForeignKeyDiff {
228
+ /** FKs present in desired but not in stored snapshot (new). */
229
+ added: TDbForeignKey[];
230
+ /** FKs present in stored snapshot but not in desired (removed). */
231
+ removed: TForeignKeySnapshot[];
232
+ /** FKs where fields match but target, onDelete, or onUpdate differ. */
233
+ changed: Array<{
234
+ desired: TDbForeignKey;
235
+ existing: TForeignKeySnapshot;
236
+ }>;
237
+ }
238
+ /** Canonical key for an FK: sorted local field names, comma-joined. */
239
+ declare function fkKey(fields: readonly string[]): string;
240
+ /**
241
+ * Physical local / target column names of a desired FK (`@db.column` renames
242
+ * applied) — what DDL, constraint sync, the FK diff and the snapshot compare.
243
+ */
244
+ declare function fkColumns(fk: TDbForeignKey): {
245
+ fields: string[];
246
+ targetFields: string[];
247
+ };
248
+ /**
249
+ * Compares desired FK constraints against stored snapshot to detect
250
+ * additions, removals, and property changes (target table, target fields,
251
+ * onDelete, onUpdate).
252
+ */
253
+ declare function computeForeignKeyDiff(desired: ReadonlyMap<string, TDbForeignKey>, existingSnapshot: readonly TForeignKeySnapshot[]): TForeignKeyDiff;
254
+ /** Whether the FK diff contains any changes. */
255
+ declare function hasForeignKeyChanges(diff: TForeignKeyDiff): boolean;
256
+ //#endregion
257
+ export { computeTableSnapshot as _, hasForeignKeyChanges as a, snapshotToExistingTableOptions as b, TFieldSnapshot as c, TViewColumnSnapshot as d, TViewJoinSnapshot as f, computeTableHash as g, computeSchemaHash as h, fkKey as i, TForeignKeySnapshot as l, canonicalizeQueryNode as m, computeForeignKeyDiff as n, computeColumnDiff as o, TViewSnapshot as p, fkColumns as r, isColumnTypeChanged as s, TForeignKeyDiff as t, TTableSnapshot as u, computeViewSnapshot as v, snapshotToExistingColumns as y };
@@ -1,4 +1,4 @@
1
- import { B as TDbFieldMeta, Gt as AtscriptDbView, O as TColumnDiff, Q as TDbStorageType, Qt as AtscriptQueryNode, R as TDbDefaultValue, Zt as AtscriptQueryFieldRef, dt as TExistingTableOption, lt as TExistingColumn, sn as AtscriptDbReadable, st as TDerivedColumn } from "./buckets-DYFu0eZ8.cjs";
1
+ import { $t as AtscriptDbView, B as TDbDefaultValue, H as TDbFieldMeta, U as TDbForeignKey, cn as AtscriptQueryNode, ft as TDerivedColumn, gt as TExistingTableOption, k as TColumnDiff, mt as TExistingColumn, rt as TDbStorageType, sn as AtscriptQueryFieldRef, yn as AtscriptDbReadable } from "./buckets-CNdTOnei.mjs";
2
2
 
3
3
  //#region src/schema/schema-hash.d.ts
4
4
  interface TFieldSnapshot {
@@ -66,6 +66,11 @@ interface TViewJoinSnapshot {
66
66
  condition: string;
67
67
  /** Emitted only for `"left"` — an inner join (the default) carries no key. @since 0.1.136 */
68
68
  kind?: "inner" | "left";
69
+ /**
70
+ * First-row joins only: canonical JSON `[["<scope>.<column>", 1 | -1], …]`
71
+ * of the ordering, the appended primary key included. @since 0.1.147
72
+ */
73
+ order?: string;
69
74
  }
70
75
  /**
71
76
  * One column of a managed view as stored in its snapshot — the PHYSICAL
@@ -84,6 +89,12 @@ interface TViewColumnSnapshot {
84
89
  aggField?: string;
85
90
  /** Canonical JSON of a conditional aggregate's predicate. */
86
91
  aggFilter?: string;
92
+ /**
93
+ * Computed columns only: canonical JSON of the `@db.compute` expression —
94
+ * leaves `{c: "<viewColumn>"}`, literals `{n: x}`, nodes `{op, a: [...]}`.
95
+ * @since 0.1.147
96
+ */
97
+ expr?: string;
87
98
  }
88
99
  interface TViewSnapshot {
89
100
  tableName: string;
@@ -208,4 +219,39 @@ declare function computeColumnDiff(desired: readonly TDbFieldMeta[], existing: T
208
219
  snapshot?: TTableSnapshot | null;
209
220
  }): TColumnDiff;
210
221
  //#endregion
211
- export { TTableSnapshot as a, TViewSnapshot as c, computeTableHash as d, computeTableSnapshot as f, snapshotToExistingTableOptions as h, TForeignKeySnapshot as i, canonicalizeQueryNode as l, snapshotToExistingColumns as m, isColumnTypeChanged as n, TViewColumnSnapshot as o, computeViewSnapshot as p, TFieldSnapshot as r, TViewJoinSnapshot as s, computeColumnDiff as t, computeSchemaHash as u };
222
+ //#region src/schema/fk-diff.d.ts
223
+ /**
224
+ * Desired FKs are keyed and compared by their PHYSICAL columns
225
+ * ({@link fkColumns}) — the snapshot side is physical too.
226
+ */
227
+ interface TForeignKeyDiff {
228
+ /** FKs present in desired but not in stored snapshot (new). */
229
+ added: TDbForeignKey[];
230
+ /** FKs present in stored snapshot but not in desired (removed). */
231
+ removed: TForeignKeySnapshot[];
232
+ /** FKs where fields match but target, onDelete, or onUpdate differ. */
233
+ changed: Array<{
234
+ desired: TDbForeignKey;
235
+ existing: TForeignKeySnapshot;
236
+ }>;
237
+ }
238
+ /** Canonical key for an FK: sorted local field names, comma-joined. */
239
+ declare function fkKey(fields: readonly string[]): string;
240
+ /**
241
+ * Physical local / target column names of a desired FK (`@db.column` renames
242
+ * applied) — what DDL, constraint sync, the FK diff and the snapshot compare.
243
+ */
244
+ declare function fkColumns(fk: TDbForeignKey): {
245
+ fields: string[];
246
+ targetFields: string[];
247
+ };
248
+ /**
249
+ * Compares desired FK constraints against stored snapshot to detect
250
+ * additions, removals, and property changes (target table, target fields,
251
+ * onDelete, onUpdate).
252
+ */
253
+ declare function computeForeignKeyDiff(desired: ReadonlyMap<string, TDbForeignKey>, existingSnapshot: readonly TForeignKeySnapshot[]): TForeignKeyDiff;
254
+ /** Whether the FK diff contains any changes. */
255
+ declare function hasForeignKeyChanges(diff: TForeignKeyDiff): boolean;
256
+ //#endregion
257
+ export { computeTableSnapshot as _, hasForeignKeyChanges as a, snapshotToExistingTableOptions as b, TFieldSnapshot as c, TViewColumnSnapshot as d, TViewJoinSnapshot as f, computeTableHash as g, computeSchemaHash as h, fkKey as i, TForeignKeySnapshot as l, canonicalizeQueryNode as m, computeForeignKeyDiff as n, computeColumnDiff as o, TViewSnapshot as p, fkColumns as r, isColumnTypeChanged as s, TForeignKeyDiff as t, TTableSnapshot as u, computeViewSnapshot as v, snapshotToExistingColumns as y };
package/dist/index.cjs CHANGED
@@ -1,13 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_db_error = require("./db-error-DTkkeu5b.cjs");
3
- const require_column_diff = require("./column-diff-D_Kyuh0S.cjs");
4
- const require_aggregate_fns = require("./aggregate-fns-CGBv3E8S.cjs");
5
- const require_nested_writer = require("./nested-writer-xfQwxplL.cjs");
2
+ const require_db_error = require("./db-error-DRQH4sLY.cjs");
3
+ const require_column_diff = require("./column-diff-CUU4GvYg.cjs");
4
+ const require_aggregate_fns = require("./aggregate-fns-C-UJRobm.cjs");
5
+ const require_nested_writer = require("./nested-writer-BUQvk6QT.cjs");
6
6
  const require_derived_rules = require("./derived-rules-YstgIxG-.cjs");
7
7
  const require_object = require("./object-Djg28csK.cjs");
8
8
  const require_consts = require("./consts-BzRfCcH2.cjs");
9
9
  const require_ops = require("./ops.cjs");
10
- const require_validator = require("./validator-CVS-onRg.cjs");
10
+ const require_validator = require("./validator-UcuJxHNT.cjs");
11
11
  let _atscript_typescript_utils = require("@atscript/typescript/utils");
12
12
  let _uniqu_core = require("@uniqu/core");
13
13
  let node_crypto = require("node:crypto");
@@ -236,8 +236,14 @@ var DbSpace = class {
236
236
  _readables = /* @__PURE__ */ new WeakMap();
237
237
  /** All tables created in this space — used for reverse FK lookup during cascade. */
238
238
  _allTables = /* @__PURE__ */ new Set();
239
+ /** Every table / view handle the space created — flagged closed on {@link close}. */
240
+ _handles = /* @__PURE__ */ new Set();
239
241
  /** Lazily created adapter for administrative ops (drop table/view) that don't need a registered readable. */
240
242
  _adminAdapter;
243
+ /** Every adapter this space created (tables, views, admin) — disposed on close. */
244
+ _adapters = /* @__PURE__ */ new Set();
245
+ _onClose;
246
+ _closing;
241
247
  logger;
242
248
  /** Encryption service for `@db.encrypted` fields — validated eagerly at construction. */
243
249
  _encryption;
@@ -252,6 +258,7 @@ var DbSpace = class {
252
258
  else {
253
259
  const options = loggerOrOptions ?? {};
254
260
  this.logger = options.logger ?? require_column_diff.NoopLogger;
261
+ this._onClose = options.onClose;
255
262
  if (options.encryption) this._encryption = new DbEncryption(options.encryption);
256
263
  }
257
264
  }
@@ -260,6 +267,7 @@ var DbSpace = class {
260
267
  * appropriate instance. Uses `@db.view` or `@db.view.for` presence to distinguish.
261
268
  */
262
269
  get(type, logger) {
270
+ this._assertOpen();
263
271
  if (require_column_diff.isViewType(type)) return this.getView(type, logger);
264
272
  return this.getTable(type, logger);
265
273
  }
@@ -268,6 +276,7 @@ var DbSpace = class {
268
276
  * Creates the table + adapter on first access, caches for subsequent calls.
269
277
  */
270
278
  getTable(type, logger) {
279
+ this._assertOpen();
271
280
  let readable = this._readables.get(type);
272
281
  if (!readable) {
273
282
  assertNotAlias(type);
@@ -280,6 +289,7 @@ var DbSpace = class {
280
289
  readable.setFkLookupResolver((tableName) => this._getFkLookupTarget(tableName));
281
290
  readable.setEncryption(this._encryption);
282
291
  this._readables.set(type, readable);
292
+ this._handles.add(readable);
283
293
  }
284
294
  return readable;
285
295
  }
@@ -288,12 +298,14 @@ var DbSpace = class {
288
298
  * Creates the view + adapter on first access, caches for subsequent calls.
289
299
  */
290
300
  getView(type, logger) {
301
+ this._assertOpen();
291
302
  let readable = this._readables.get(type);
292
303
  if (!readable) {
293
304
  assertNotAlias(type);
294
305
  readable = new require_column_diff.AtscriptDbView(type, this._createAdapter(), logger || this.logger, (t) => this.get(t));
295
306
  readable.setEncryption(this._encryption);
296
307
  this._readables.set(type, readable);
308
+ this._handles.add(readable);
297
309
  }
298
310
  return readable;
299
311
  }
@@ -302,6 +314,7 @@ var DbSpace = class {
302
314
  * Creates the table/view + adapter on first access if needed.
303
315
  */
304
316
  getAdapter(type) {
317
+ this._assertOpen();
305
318
  return this.get(type).dbAdapter;
306
319
  }
307
320
  /**
@@ -309,6 +322,7 @@ var DbSpace = class {
309
322
  * See `BaseDbAdapter.dropTableByName` for an adapter that does not support it.
310
323
  */
311
324
  async dropTableByName(tableName) {
325
+ this._assertOpen();
312
326
  await this._getAdminAdapter().dropTableByName(tableName);
313
327
  }
314
328
  /**
@@ -316,6 +330,7 @@ var DbSpace = class {
316
330
  * See `BaseDbAdapter.dropViewByName` for an adapter that does not support it.
317
331
  */
318
332
  async dropViewByName(viewName) {
333
+ this._assertOpen();
319
334
  await this._getAdminAdapter().dropViewByName(viewName);
320
335
  }
321
336
  /**
@@ -324,6 +339,7 @@ var DbSpace = class {
324
339
  * @since 0.1.128
325
340
  */
326
341
  async dropTablesByName(tableNames) {
342
+ this._assertOpen();
327
343
  await this._getAdminAdapter().dropTablesByName(tableNames);
328
344
  }
329
345
  /**
@@ -333,6 +349,7 @@ var DbSpace = class {
333
349
  * @since 0.1.128
334
350
  */
335
351
  async getReferencingForeignKeys(tableName) {
352
+ this._assertOpen();
336
353
  return this._getAdminAdapter().getReferencingForeignKeys?.(tableName);
337
354
  }
338
355
  /**
@@ -351,8 +368,46 @@ var DbSpace = class {
351
368
  _createAdapter() {
352
369
  const adapter = this.adapterFactory();
353
370
  adapter.registerSpace?.(this);
371
+ this._adapters.add(adapter);
354
372
  return adapter;
355
373
  }
374
+ /** `true` once {@link close} was called. @since 0.1.148 */
375
+ get closed() {
376
+ return this._closing !== void 0;
377
+ }
378
+ /** Throws `SPACE_CLOSED` once the space is closed. */
379
+ _assertOpen() {
380
+ if (this._closing) throw require_db_error.spaceClosedError();
381
+ }
382
+ /**
383
+ * Closes the space: marks it closed, disposes every adapter it created, then
384
+ * runs the `onClose` hook (the `createAdapter` helpers close their driver
385
+ * there). Idempotent — every call returns the first call's promise. Every
386
+ * step is attempted; failures are collected into an `AggregateError`.
387
+ *
388
+ * It does not cancel or drain in-flight queries: call it after the server
389
+ * stopped accepting requests. After close, `get*` and operations on existing
390
+ * table/view handles throw `DbError("SPACE_CLOSED")`.
391
+ * @since 0.1.148
392
+ */
393
+ close() {
394
+ return this._closing ??= this._doClose();
395
+ }
396
+ async _doClose() {
397
+ for (const readable of this._handles) readable._spaceClosed = true;
398
+ const errors = [];
399
+ for (const adapter of this._adapters) try {
400
+ await adapter.dispose?.();
401
+ } catch (error) {
402
+ errors.push(error);
403
+ }
404
+ try {
405
+ await this._onClose?.();
406
+ } catch (error) {
407
+ errors.push(error);
408
+ }
409
+ if (errors.length > 0) throw require_db_error.aggregateFailure("DbSpace close failed", errors);
410
+ }
356
411
  /**
357
412
  * Finds all child tables with FKs pointing to the given parent table name.
358
413
  * Accesses `table.foreignKeys` which triggers `_flatten()` if needed.
@@ -376,34 +431,9 @@ var DbSpace = class {
376
431
  for (const table of this._allTables) if (table.tableName === tableName) return { count: (filter) => table.count({ filter }) };
377
432
  }
378
433
  };
379
- //#endregion
380
- //#region src/query/query-tree.ts
381
- /**
382
- * `true` when a query-tree operand is a field reference (`{ field, type? }`)
383
- * rather than a literal — e.g. the right side of a field-to-field comparison.
384
- * @since 0.1.136
385
- */
386
- function isFieldRef(value) {
387
- return value !== null && typeof value === "object" && "field" in value;
388
- }
389
- /**
390
- * Translates a JS-emitted query tree into a FilterExpr.
391
- * Resolves field references (type + field path) to physical column names
392
- * via the provided resolver function.
393
- */
394
- function translateQueryTree(node, resolveField) {
395
- if ("$and" in node) return { $and: node.$and.map((n) => translateQueryTree(n, resolveField)) };
396
- if ("$or" in node) return { $or: node.$or.map((n) => translateQueryTree(n, resolveField)) };
397
- if ("$not" in node) return { $not: translateQueryTree(node.$not, resolveField) };
398
- const comp = node;
399
- const leftField = resolveField(comp.left);
400
- if (isFieldRef(comp.right)) {
401
- const rightField = resolveField(comp.right);
402
- return { [leftField]: { [comp.op]: { $field: rightField } } };
403
- }
404
- if (comp.op === "$exists") return { [leftField]: { $exists: comp.right !== false } };
405
- return { [leftField]: { [comp.op]: comp.right } };
406
- }
434
+ if (typeof Symbol.asyncDispose === "symbol") DbSpace.prototype[Symbol.asyncDispose] = function() {
435
+ return this.close();
436
+ };
407
437
  //#endregion
408
438
  //#region src/table/db-entity.ts
409
439
  /**
@@ -435,6 +465,7 @@ exports.$upsert = require_ops.$upsert;
435
465
  exports.ADAPTER_FILTER_REASON = require_column_diff.ADAPTER_FILTER_REASON;
436
466
  exports.ALL_AGGREGATE_FNS = require_aggregate_fns.ALL_AGGREGATE_FNS;
437
467
  exports.ALL_BUCKET_UNITS = require_column_diff.ALL_BUCKET_UNITS;
468
+ exports.ALL_VIEW_CAPABILITIES = require_column_diff.ALL_VIEW_CAPABILITIES;
438
469
  exports.ApplicationIntegrity = require_column_diff.ApplicationIntegrity;
439
470
  exports.AtscriptDbReadable = require_column_diff.AtscriptDbReadable;
440
471
  exports.AtscriptDbTable = require_column_diff.AtscriptDbTable;
@@ -452,11 +483,23 @@ exports.FieldMappingStrategy = require_column_diff.FieldMappingStrategy;
452
483
  exports.IntegrityStrategy = require_column_diff.IntegrityStrategy;
453
484
  exports.NativeIntegrity = require_column_diff.NativeIntegrity;
454
485
  exports.NoopLogger = require_column_diff.NoopLogger;
486
+ Object.defineProperty(exports, "RELATION_OPS", {
487
+ enumerable: true,
488
+ get: function() {
489
+ return _uniqu_core.RELATION_OPS;
490
+ }
491
+ });
492
+ exports.REL_FILTER_MAX_DEPTH = require_nested_writer.REL_FILTER_MAX_DEPTH;
493
+ exports.REL_FILTER_MAX_NODES = require_nested_writer.REL_FILTER_MAX_NODES;
455
494
  exports.RelationalFieldMapper = require_column_diff.RelationalFieldMapper;
495
+ exports.ResolvedRelationFilter = require_nested_writer.ResolvedRelationFilter;
456
496
  exports.TableMetadata = require_column_diff.TableMetadata;
457
497
  exports.UniquSelect = require_column_diff.UniquSelect;
458
498
  exports.acceptedOperatorsHint = require_column_diff.acceptedOperatorsHint;
499
+ exports.aggregateExpressionsNotSupported = require_db_error.aggregateExpressionsNotSupported;
500
+ exports.aggregateFailure = require_db_error.aggregateFailure;
459
501
  exports.aliasTargetOf = require_column_diff.aliasTargetOf;
502
+ exports.andFilters = require_nested_writer.andFilters;
460
503
  exports.assertGeoPoint = require_column_diff.assertGeoPoint;
461
504
  exports.assertNoVersionWrites = require_column_diff.assertNoVersionWrites;
462
505
  exports.bucketSourceVerdict = require_column_diff.bucketSourceVerdict;
@@ -473,28 +516,35 @@ Object.defineProperty(exports, "computeInsights", {
473
516
  return _uniqu_core.computeInsights;
474
517
  }
475
518
  });
519
+ exports.containsRelationFilter = require_nested_writer.containsRelationFilter;
476
520
  exports.createDbValidatorPlugin = require_validator.createDbValidatorPlugin;
477
521
  exports.createFailureCollector = require_column_diff.createFailureCollector;
478
522
  exports.decomposePatch = require_column_diff.decomposePatch;
479
523
  exports.deletePath = require_object.deletePath;
524
+ exports.evaluateExpr = require_nested_writer.evaluateExpr;
480
525
  exports.findAncestorInSet = require_object.findAncestorInSet;
526
+ exports.fkColumns = require_column_diff.fkColumns;
527
+ exports.forEachResolvedRelation = require_nested_writer.forEachResolvedRelation;
481
528
  exports.forceNavNonOptional = require_validator.forceNavNonOptional;
482
529
  exports.geoIndexNotFoundMessage = require_column_diff.geoIndexNotFoundMessage;
483
530
  exports.getDbFieldOp = require_ops.getDbFieldOp;
484
531
  exports.getKeyProps = require_validator.getKeyProps;
485
532
  exports.getPath = require_object.getPath;
533
+ exports.groupSourceVerdict = require_column_diff.groupSourceVerdict;
486
534
  exports.guardAggregate = require_column_diff.guardAggregate;
487
535
  exports.guardFilter = require_column_diff.guardFilter;
488
536
  exports.guardPath = require_column_diff.guardPath;
489
537
  exports.guardPaths = require_column_diff.guardPaths;
490
538
  exports.guardQuery = require_column_diff.guardQuery;
539
+ exports.hasRelationOp = require_nested_writer.hasRelationOp;
491
540
  exports.isAtscriptDbView = require_column_diff.isAtscriptDbView;
492
541
  exports.isBucketableField = require_column_diff.isBucketableField;
493
542
  exports.isColumnTypeChanged = require_column_diff.isColumnTypeChanged;
543
+ exports.isConflict = require_db_error.isConflict;
494
544
  exports.isDbEntityType = isDbEntityType;
495
545
  exports.isDbFieldOp = require_ops.isDbFieldOp;
496
546
  exports.isEmptyObject = require_object.isEmptyObject;
497
- exports.isFieldRef = isFieldRef;
547
+ exports.isFieldRef = require_nested_writer.isFieldRef;
498
548
  exports.isGeoIndexableType = require_column_diff.isGeoIndexableType;
499
549
  exports.isGeoPointType = require_column_diff.isGeoPointType;
500
550
  exports.isJsonValueField = require_column_diff.isJsonValueField;
@@ -506,11 +556,20 @@ Object.defineProperty(exports, "isPrimitive", {
506
556
  return _uniqu_core.isPrimitive;
507
557
  }
508
558
  });
559
+ Object.defineProperty(exports, "isRelationOp", {
560
+ enumerable: true,
561
+ get: function() {
562
+ return _uniqu_core.isRelationOp;
563
+ }
564
+ });
565
+ exports.isResolvedRelationFilter = require_nested_writer.isResolvedRelationFilter;
509
566
  exports.isViewType = require_column_diff.isViewType;
510
567
  exports.jsonValueAncestor = require_column_diff.jsonValueAncestor;
511
568
  exports.narrowerFilterOps = require_column_diff.narrowerFilterOps;
512
569
  exports.normalizeComputedSelect = require_column_diff.normalizeComputedSelect;
570
+ exports.numericOperandProblem = require_column_diff.numericOperandProblem;
513
571
  exports.reconcileCas = require_ops.reconcileCas;
572
+ exports.relationStaticFilter = require_nested_writer.relationStaticFilter;
514
573
  exports.resolveCalendarBuckets = require_column_diff.resolveCalendarBuckets;
515
574
  exports.resolveDesignType = require_column_diff.resolveDesignType;
516
575
  exports.searchIndexNotFoundMessage = require_column_diff.searchIndexNotFoundMessage;
@@ -519,7 +578,8 @@ exports.separateCas = require_ops.separateCas;
519
578
  exports.separateFieldOps = require_ops.separateFieldOps;
520
579
  exports.sortFieldNames = require_column_diff.sortFieldNames;
521
580
  exports.tableNameOf = require_nested_writer.tableNameOf;
522
- exports.translateQueryTree = translateQueryTree;
581
+ exports.translateQueryTree = require_nested_writer.translateQueryTree;
582
+ exports.uniqueKeyTuple = require_nested_writer.uniqueKeyTuple;
523
583
  exports.unsupportedOperatorMessage = require_column_diff.unsupportedOperatorMessage;
524
584
  exports.vectorIndexNotFoundMessage = require_column_diff.vectorIndexNotFoundMessage;
525
585
  Object.defineProperty(exports, "walkFilter", {