@atscript/db-memory 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.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TFieldOps } from "@atscript/db";
1
+ import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, ResolvedRelationFilter, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TFieldOps, TViewCapability } from "@atscript/db";
2
2
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
3
3
 
4
4
  //#region src/lib/memory-adapter.d.ts
@@ -76,6 +76,17 @@ declare class MemoryAdapter extends BaseDbAdapter {
76
76
  * decomposes patches into dot-path `$set`s and loads relations app-level.
77
77
  */
78
78
  canFilterField(fd: TDbFieldMeta): boolean;
79
+ /**
80
+ * Relational predicates (`{ nav: { $some | $none: … } }`) are evaluated in
81
+ * reads AND mutation filters. Before a filter holding them is compiled, every
82
+ * related table (and junction) is loaded ONCE for that operation — through
83
+ * its own adapter's snapshot seam, so a provider-backed related table works
84
+ * too — and each predicate becomes a set of matching correlation keys
85
+ * (see {@link _relationSets}). Predicate-free filters take the plain path.
86
+ *
87
+ * @since 0.1.147
88
+ */
89
+ supportsRelationFilters(_mode: "read" | "write"): boolean;
79
90
  /**
80
91
  * Coerces a by-id value to the id field's declared leaf type. The framework
81
92
  * calls `adapter.prepareId(value, fieldType)` when building by-id filters, so a
@@ -107,7 +118,7 @@ declare class MemoryAdapter extends BaseDbAdapter {
107
118
  /**
108
119
  * Physical names of the primary-key field(s). Single `@meta.id` resolves via
109
120
  * {@link AtscriptDbReadable.metaIdPhysical}; a composite key maps each logical
110
- * PK path through `pathToPhysical` (falling back to the name itself). Memoized
121
+ * PK path through `physicalPath` (`@db.column` renames included). Memoized
111
122
  * because it is stable per adapter (1:1 with a fixed table) yet read on every
112
123
  * `pkKey`/projection — recomputing would re-read `this._table.*` and re-allocate.
113
124
  */
@@ -221,6 +232,22 @@ declare class MemoryAdapter extends BaseDbAdapter {
221
232
  * AND single-snapshot-per-`findManyWithCount` both fall out for free.
222
233
  */
223
234
  protected _loadRows(): Record<string, unknown>[] | Promise<Record<string, unknown>[]>;
235
+ /**
236
+ * A per-operation row loader for relational predicates: each related table
237
+ * is snapshotted at most once per operation (the first predicate that needs
238
+ * it loads it; later ones — and nested ones — reuse that snapshot), so a
239
+ * provider-backed related table is invoked once per read. `own` seeds this
240
+ * table's snapshot so a self relation reads the same rows as the outer scan.
241
+ */
242
+ private _snapshotLoader;
243
+ /**
244
+ * The prepared correlation sets for `filter`'s relational predicates.
245
+ * Callers only await it when `containsRelationFilter(filter)` — a
246
+ * predicate-free filter keeps its fully synchronous path. Write methods call
247
+ * it BEFORE reading the table state, so the related tables (a self relation
248
+ * included) are snapshotted before any row changes, like SQL.
249
+ */
250
+ private _relationSets;
224
251
  /**
225
252
  * Clones the payload in, applies the version default, generates any
226
253
  * `@db.default.increment` values, enforces PK + unique constraints, stores
@@ -242,6 +269,9 @@ declare class MemoryAdapter extends BaseDbAdapter {
242
269
  private _enforceUniqueIndexes;
243
270
  insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
244
271
  insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
272
+ supportsInsertIgnore(): boolean;
273
+ /** A row colliding on the primary key or a unique index (`CONFLICT`) becomes a skipped slot. */
274
+ insertManyIgnore(data: Array<Record<string, unknown>>): Promise<TDbInsertIgnoreSlot[]>;
245
275
  /**
246
276
  * Selects the stored rows a write should touch, as `{ key, row }` pairs so
247
277
  * callers can mutate in place and re-key. Stored mode scans the table's Map
@@ -257,6 +287,9 @@ declare class MemoryAdapter extends BaseDbAdapter {
257
287
  *
258
288
  * When `many` is `false` at most the first match is returned. A table that
259
289
  * does not exist (`state` undefined) matches nothing and is not created.
290
+ *
291
+ * A filter with relational predicates needs `relationSets`, prepared by the
292
+ * caller BEFORE it reads `state` (see {@link _relationSets}).
260
293
  */
261
294
  private _selectForWrite;
262
295
  /**
@@ -376,8 +409,12 @@ declare class MemoryAdapter extends BaseDbAdapter {
376
409
  * there is no zone this adapter cannot resolve).
377
410
  */
378
411
  calendarBucketUnits(): ReadonlySet<BucketUnit>;
379
- /** Every aggregate function, `countDistinct` included. */
412
+ /** Every aggregate function: `countDistinct`, `first` and `last` included. */
380
413
  aggregateFns(): ReadonlySet<AggregateFn>;
414
+ /** Arithmetic in an aggregate `$select` (`{ $expr }`, `{ $fn, $expr }`). */
415
+ supportsAggregateExpressions(): boolean;
416
+ /** Managed views render nothing (non-goal) — both are accepted like `aggregateFns()`. */
417
+ viewCapabilities(): ReadonlySet<TViewCapability>;
381
418
  updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
382
419
  replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
383
420
  deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
@@ -449,6 +486,14 @@ type Predicate = (row: Record<string, unknown>) => boolean;
449
486
  * later concern; the SQL/Mongo adapters flatten differently and we do not want
450
487
  * to fake a semantic the store can't back yet.
451
488
  */
489
+ /**
490
+ * Per-predicate correlation sets: for every {@link ResolvedRelationFilter} of
491
+ * a filter, the SOURCE correlation tuples (see {@link correlationKey}) that
492
+ * have at least one related row matching the predicate's inner filter.
493
+ * Built asynchronously by {@link prepareRelationSets}; consumed synchronously
494
+ * by {@link buildMemoryPredicate}.
495
+ */
496
+ type RelationSets = ReadonlyMap<ResolvedRelationFilter, ReadonlySet<string>>;
452
497
  /**
453
498
  * Compiles a {@link FilterExpr} into an in-memory row predicate
454
499
  * `(row) => boolean`, reusing the shared {@link walkFilter} walker so filter
@@ -456,8 +501,13 @@ type Predicate = (row: Record<string, unknown>) => boolean;
456
501
  *
457
502
  * An empty/absent filter (for which `walkFilter` returns `undefined`) compiles
458
503
  * to a match-everything predicate.
504
+ *
505
+ * A filter holding relational predicates (`ResolvedRelationFilter` operands,
506
+ * since 0.1.147) needs `relationSets` — prepared internally by
507
+ * `MemoryAdapter` from the related tables; a standalone call on such a
508
+ * filter throws `REL_FILTER_NOT_SUPPORTED`.
459
509
  */
460
- declare function buildMemoryPredicate(filter: FilterExpr): Predicate;
510
+ declare function buildMemoryPredicate(filter: FilterExpr, relationSets?: RelationSets): Predicate;
461
511
  //#endregion
462
512
  //#region src/lib/memory-engine.d.ts
463
513
  /**
@@ -525,4 +575,4 @@ declare function projectRow(row: Record<string, unknown>, projection?: Record<st
525
575
  */
526
576
  declare function createAdapter(): DbSpace;
527
577
  //#endregion
528
- export { MemoryAdapter, MemoryProviderFn, buildMemoryPredicate, createAdapter, projectRow, setMemoryProvider, sortRows };
578
+ export { MemoryAdapter, type MemoryProviderFn, buildMemoryPredicate, createAdapter, projectRow, setMemoryProvider, sortRows };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TFieldOps } from "@atscript/db";
1
+ import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, ResolvedRelationFilter, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TFieldOps, TViewCapability } from "@atscript/db";
2
2
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
3
3
 
4
4
  //#region src/lib/memory-adapter.d.ts
@@ -76,6 +76,17 @@ declare class MemoryAdapter extends BaseDbAdapter {
76
76
  * decomposes patches into dot-path `$set`s and loads relations app-level.
77
77
  */
78
78
  canFilterField(fd: TDbFieldMeta): boolean;
79
+ /**
80
+ * Relational predicates (`{ nav: { $some | $none: … } }`) are evaluated in
81
+ * reads AND mutation filters. Before a filter holding them is compiled, every
82
+ * related table (and junction) is loaded ONCE for that operation — through
83
+ * its own adapter's snapshot seam, so a provider-backed related table works
84
+ * too — and each predicate becomes a set of matching correlation keys
85
+ * (see {@link _relationSets}). Predicate-free filters take the plain path.
86
+ *
87
+ * @since 0.1.147
88
+ */
89
+ supportsRelationFilters(_mode: "read" | "write"): boolean;
79
90
  /**
80
91
  * Coerces a by-id value to the id field's declared leaf type. The framework
81
92
  * calls `adapter.prepareId(value, fieldType)` when building by-id filters, so a
@@ -107,7 +118,7 @@ declare class MemoryAdapter extends BaseDbAdapter {
107
118
  /**
108
119
  * Physical names of the primary-key field(s). Single `@meta.id` resolves via
109
120
  * {@link AtscriptDbReadable.metaIdPhysical}; a composite key maps each logical
110
- * PK path through `pathToPhysical` (falling back to the name itself). Memoized
121
+ * PK path through `physicalPath` (`@db.column` renames included). Memoized
111
122
  * because it is stable per adapter (1:1 with a fixed table) yet read on every
112
123
  * `pkKey`/projection — recomputing would re-read `this._table.*` and re-allocate.
113
124
  */
@@ -221,6 +232,22 @@ declare class MemoryAdapter extends BaseDbAdapter {
221
232
  * AND single-snapshot-per-`findManyWithCount` both fall out for free.
222
233
  */
223
234
  protected _loadRows(): Record<string, unknown>[] | Promise<Record<string, unknown>[]>;
235
+ /**
236
+ * A per-operation row loader for relational predicates: each related table
237
+ * is snapshotted at most once per operation (the first predicate that needs
238
+ * it loads it; later ones — and nested ones — reuse that snapshot), so a
239
+ * provider-backed related table is invoked once per read. `own` seeds this
240
+ * table's snapshot so a self relation reads the same rows as the outer scan.
241
+ */
242
+ private _snapshotLoader;
243
+ /**
244
+ * The prepared correlation sets for `filter`'s relational predicates.
245
+ * Callers only await it when `containsRelationFilter(filter)` — a
246
+ * predicate-free filter keeps its fully synchronous path. Write methods call
247
+ * it BEFORE reading the table state, so the related tables (a self relation
248
+ * included) are snapshotted before any row changes, like SQL.
249
+ */
250
+ private _relationSets;
224
251
  /**
225
252
  * Clones the payload in, applies the version default, generates any
226
253
  * `@db.default.increment` values, enforces PK + unique constraints, stores
@@ -242,6 +269,9 @@ declare class MemoryAdapter extends BaseDbAdapter {
242
269
  private _enforceUniqueIndexes;
243
270
  insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
244
271
  insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
272
+ supportsInsertIgnore(): boolean;
273
+ /** A row colliding on the primary key or a unique index (`CONFLICT`) becomes a skipped slot. */
274
+ insertManyIgnore(data: Array<Record<string, unknown>>): Promise<TDbInsertIgnoreSlot[]>;
245
275
  /**
246
276
  * Selects the stored rows a write should touch, as `{ key, row }` pairs so
247
277
  * callers can mutate in place and re-key. Stored mode scans the table's Map
@@ -257,6 +287,9 @@ declare class MemoryAdapter extends BaseDbAdapter {
257
287
  *
258
288
  * When `many` is `false` at most the first match is returned. A table that
259
289
  * does not exist (`state` undefined) matches nothing and is not created.
290
+ *
291
+ * A filter with relational predicates needs `relationSets`, prepared by the
292
+ * caller BEFORE it reads `state` (see {@link _relationSets}).
260
293
  */
261
294
  private _selectForWrite;
262
295
  /**
@@ -376,8 +409,12 @@ declare class MemoryAdapter extends BaseDbAdapter {
376
409
  * there is no zone this adapter cannot resolve).
377
410
  */
378
411
  calendarBucketUnits(): ReadonlySet<BucketUnit>;
379
- /** Every aggregate function, `countDistinct` included. */
412
+ /** Every aggregate function: `countDistinct`, `first` and `last` included. */
380
413
  aggregateFns(): ReadonlySet<AggregateFn>;
414
+ /** Arithmetic in an aggregate `$select` (`{ $expr }`, `{ $fn, $expr }`). */
415
+ supportsAggregateExpressions(): boolean;
416
+ /** Managed views render nothing (non-goal) — both are accepted like `aggregateFns()`. */
417
+ viewCapabilities(): ReadonlySet<TViewCapability>;
381
418
  updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
382
419
  replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
383
420
  deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
@@ -449,6 +486,14 @@ type Predicate = (row: Record<string, unknown>) => boolean;
449
486
  * later concern; the SQL/Mongo adapters flatten differently and we do not want
450
487
  * to fake a semantic the store can't back yet.
451
488
  */
489
+ /**
490
+ * Per-predicate correlation sets: for every {@link ResolvedRelationFilter} of
491
+ * a filter, the SOURCE correlation tuples (see {@link correlationKey}) that
492
+ * have at least one related row matching the predicate's inner filter.
493
+ * Built asynchronously by {@link prepareRelationSets}; consumed synchronously
494
+ * by {@link buildMemoryPredicate}.
495
+ */
496
+ type RelationSets = ReadonlyMap<ResolvedRelationFilter, ReadonlySet<string>>;
452
497
  /**
453
498
  * Compiles a {@link FilterExpr} into an in-memory row predicate
454
499
  * `(row) => boolean`, reusing the shared {@link walkFilter} walker so filter
@@ -456,8 +501,13 @@ type Predicate = (row: Record<string, unknown>) => boolean;
456
501
  *
457
502
  * An empty/absent filter (for which `walkFilter` returns `undefined`) compiles
458
503
  * to a match-everything predicate.
504
+ *
505
+ * A filter holding relational predicates (`ResolvedRelationFilter` operands,
506
+ * since 0.1.147) needs `relationSets` — prepared internally by
507
+ * `MemoryAdapter` from the related tables; a standalone call on such a
508
+ * filter throws `REL_FILTER_NOT_SUPPORTED`.
459
509
  */
460
- declare function buildMemoryPredicate(filter: FilterExpr): Predicate;
510
+ declare function buildMemoryPredicate(filter: FilterExpr, relationSets?: RelationSets): Predicate;
461
511
  //#endregion
462
512
  //#region src/lib/memory-engine.d.ts
463
513
  /**
@@ -525,4 +575,4 @@ declare function projectRow(row: Record<string, unknown>, projection?: Record<st
525
575
  */
526
576
  declare function createAdapter(): DbSpace;
527
577
  //#endregion
528
- export { MemoryAdapter, MemoryProviderFn, buildMemoryPredicate, createAdapter, projectRow, setMemoryProvider, sortRows };
578
+ export { MemoryAdapter, type MemoryProviderFn, buildMemoryPredicate, createAdapter, projectRow, setMemoryProvider, sortRows };