@atscript/db-memory 0.1.150 → 0.1.152

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
@@ -33,6 +33,8 @@ declare class MemoryAdapter extends BaseDbAdapter {
33
33
  private _db;
34
34
  /** Memoized physical PK field names — stable for the adapter's lifetime. */
35
35
  private _pkFieldsCache?;
36
+ /** Memoized compiled readers of {@link _physicalPkFields}. */
37
+ private _pkReadersCache?;
36
38
  /**
37
39
  * Memoized map of PHYSICAL field name → optional `start` for every field
38
40
  * carrying `@db.default.increment`. Stable per adapter (see
@@ -123,6 +125,8 @@ declare class MemoryAdapter extends BaseDbAdapter {
123
125
  * `pkKey`/projection — recomputing would re-read `this._table.*` and re-allocate.
124
126
  */
125
127
  private _physicalPkFields;
128
+ /** Compiled readers of {@link _physicalPkFields} (memoized like it). */
129
+ private _pkReaders;
126
130
  /**
127
131
  * PHYSICAL field name → optional `start` for every `@db.default.increment`
128
132
  * field. Discovered lazily from the table's field descriptors — each carries
@@ -175,6 +179,16 @@ declare class MemoryAdapter extends BaseDbAdapter {
175
179
  * `['a:b','c']` differ, and `1` differs from `'1'`.
176
180
  */
177
181
  private pkKey;
182
+ /**
183
+ * The storage key a filter pins by exact equality on EVERY primary-key field
184
+ * — `{ id: v }`, `{ id: { $eq: v } }`, also inside (nested) `$and` — or
185
+ * `undefined`. Only a string / number / boolean / `Date` value pins: for
186
+ * those, a row the filter matches is stored exactly under this key (strict
187
+ * equality ⇒ equal `JSON.stringify`), so a read can look the row up and
188
+ * still run the full predicate on it. Unavailable once the table holds an
189
+ * {@link MemoryTableState.opaquePk opaque} key.
190
+ */
191
+ private _pinnedKey;
178
192
  /**
179
193
  * Builds a DEFINED inserted-id for a table with NO single `@meta.id` — a
180
194
  * composite (or single non-meta) primary key, where
@@ -251,8 +265,9 @@ declare class MemoryAdapter extends BaseDbAdapter {
251
265
  /**
252
266
  * Clones the payload in, applies the version default, generates any
253
267
  * `@db.default.increment` values, enforces PK + unique constraints, stores
254
- * the row, and returns the resolved inserted id. Clone-in (`structuredClone`)
255
- * is what makes post-insert mutation of the caller's object never leak into
268
+ * the row, and returns the resolved inserted id. Clone-in ({@link cloneValue},
269
+ * `structuredClone`-equivalent) is what makes post-insert mutation of the
270
+ * caller's object never leak into
256
271
  * the store. Called once per item by both `insertOne` and `insertMany`, so
257
272
  * increment values advance sequentially across a batch.
258
273
  */
@@ -261,12 +276,21 @@ declare class MemoryAdapter extends BaseDbAdapter {
261
276
  * Enforces every recorded unique index against the current store. A row is
262
277
  * exempted from an index (present-only semantics) when ANY of that index's
263
278
  * optional fields is absent/`null`. Otherwise a stored row with an equal
264
- * tuple → `CONFLICT`.
279
+ * tuple → `CONFLICT`. Returns the row's tuple key per index (for
280
+ * {@link _storeRow}).
265
281
  *
266
282
  * `excludeKey` (when given) skips the row stored under that {@link pkKey} — so
267
283
  * a row updating its own unique value does not false-conflict with itself.
268
284
  */
269
285
  private _enforceUniqueIndexes;
286
+ /**
287
+ * Stores `row` under `key` and records it in the unique indexes (`tuples`:
288
+ * its precomputed tuple keys). The ONLY way a row enters the store, so the
289
+ * indexes and {@link MemoryTableState.opaquePk} never drift from it.
290
+ */
291
+ private _storeRow;
292
+ /** Removes the row stored under `key` (and its unique-index entries). */
293
+ private _removeRow;
270
294
  insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
271
295
  insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
272
296
  supportsInsertIgnore(): boolean;
@@ -311,7 +335,7 @@ declare class MemoryAdapter extends BaseDbAdapter {
311
335
  * because this adapter reports no {@link supportsNativePatch}), so they must
312
336
  * nest into the stored document — MERGING siblings — exactly like Mongo's
313
337
  * `$set: { "profile.city": v }`, not create a literal dotted key. Top-level
314
- * (dot-free) keys behave as a plain assignment. `data` is `structuredClone`d
338
+ * (dot-free) keys behave as a plain assignment. `data` is deep-cloned
315
339
  * first so nested subtrees from the caller never alias into the store.
316
340
  * - `ops.inc` / `ops.mul`: numeric increment / multiply on the (dot-path)
317
341
  * target, coercing a missing or non-numeric current value to `0` (parity with
@@ -323,10 +347,11 @@ declare class MemoryAdapter extends BaseDbAdapter {
323
347
  */
324
348
  private _applyUpdate;
325
349
  /**
326
- * Places `next` into the store under its (possibly changed) {@link pkKey},
327
- * re-keying when a mutation/replace moved the primary key. A collision on the
328
- * NEW key (some other row already owns it) throws `CONFLICT`. Throws BEFORE
329
- * touching the Map so a failed re-key leaves the store unchanged.
350
+ * Places `next` into the store under its (possibly changed) {@link pkKey} in
351
+ * place of `oldRow` (stored under `oldKey`), re-keying when a mutation/replace
352
+ * moved the primary key. A collision on the NEW key (some other row already
353
+ * owns it) throws `CONFLICT`. Throws BEFORE touching the Map so a failed
354
+ * re-key leaves the store unchanged. `tuples` are `next`'s unique-index keys.
330
355
  */
331
356
  private _commitRow;
332
357
  /**
@@ -346,12 +371,13 @@ declare class MemoryAdapter extends BaseDbAdapter {
346
371
  * Stable multi-key comparator from `$sort`, with a final tie-break on
347
372
  * {@link pkKey} for a deterministic total order. Returns the input unchanged
348
373
  * (insertion order) when there is no `$sort`. Delegates to the shared pure
349
- * {@link sortRows}, injecting {@link pkKey} as the total-order tie-break.
374
+ * {@link sortRows}, injecting {@link pkKey} as the total-order tie-break;
375
+ * `topK` asks for the head of that order only (`$skip + $limit`).
350
376
  */
351
377
  private _sortRows;
352
378
  /**
353
- * Projects a stored row per `$select` and returns a fresh, deep-cloned object
354
- * so the store can never be mutated through a returned value.
379
+ * Compiles the `$select` projection of one read: each returned row is a
380
+ * fresh, deep-cloned object so the store can never be mutated through it.
355
381
  *
356
382
  * - No projection → a full clone.
357
383
  * - INCLUSION form (`{ field: 1 }`) → a new object with exactly the selected
@@ -362,7 +388,7 @@ declare class MemoryAdapter extends BaseDbAdapter {
362
388
  * Top-level and nested dot-paths are supported; exotic Mongo projection
363
389
  * quirks (array positional, `$slice`, etc.) are intentionally NOT replicated.
364
390
  */
365
- private _projectAndClone;
391
+ private _projector;
366
392
  /**
367
393
  * Reads the pagination/sort/projection controls with their intended types.
368
394
  * `DbControls` carries a `[key: `$${string}`]: unknown` index signature, and
@@ -376,6 +402,10 @@ declare class MemoryAdapter extends BaseDbAdapter {
376
402
  * shares. Goes through the {@link _loadRows} seam exactly ONCE per call, so a
377
403
  * reader (and provider read-through mode) has one place that materializes the
378
404
  * working set — one provider invocation per logical read.
405
+ *
406
+ * Stored mode reads the table's Map in place (no snapshot copy), resolves a
407
+ * filter pinning the whole primary key by direct lookup, and stops after
408
+ * `limit` matches when given (an unsorted `findOne`).
379
409
  */
380
410
  private _filteredRows;
381
411
  findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
@@ -521,13 +551,16 @@ declare function buildMemoryPredicate(filter: FilterExpr, relationSets?: Relatio
521
551
  * rows with equal sort keys still order deterministically. When ABSENT the
522
552
  * sort falls back to preserving input order among equal keys (via each row's
523
553
  * original index), so a consumer with no primary key keeps insertion order.
554
+ * - `topK` (since 0.1.151), when supplied, asks for only the first `topK` rows
555
+ * of that order (e.g. `$skip + $limit`): a small one is selected in one pass
556
+ * instead of sorting everything. The rows returned are exactly the head of
557
+ * the full sort — same comparator, same stable tie handling.
524
558
  *
525
- * NEVER mutates the input array: `.map` decorates into a fresh array and
526
- * `.toSorted` returns another new sorted array (unlike `.sort`, which reorders
527
- * in place). The `tieBreak`/index is computed ONCE per row (O(n)), not inside
528
- * the O(n log n) comparator.
559
+ * NEVER mutates the input array. Each row's sort keys are read ONCE (O(n)) and
560
+ * its `tieBreak` only when two rows tie on every key — not inside the
561
+ * O(n log n) comparator.
529
562
  */
530
- declare function sortRows(rows: Record<string, unknown>[], $sort?: Partial<Record<string, 1 | -1>>, tieBreak?: (row: Record<string, unknown>) => string | number): Record<string, unknown>[];
563
+ declare function sortRows(rows: Record<string, unknown>[], $sort?: Partial<Record<string, 1 | -1>>, tieBreak?: (row: Record<string, unknown>) => string | number, topK?: number): Record<string, unknown>[];
531
564
  /** Options for {@link projectRow}. */
532
565
  interface ProjectRowOptions {
533
566
  /**
@@ -561,6 +594,9 @@ interface ProjectRowOptions {
561
594
  *
562
595
  * Top-level and nested dot-paths are supported; exotic Mongo projection quirks
563
596
  * (array positional, `$slice`, etc.) are intentionally NOT replicated.
597
+ *
598
+ * Clones are `structuredClone`-equivalent. To project many rows the same way,
599
+ * compile once with {@link compileProjection}.
564
600
  */
565
601
  declare function projectRow(row: Record<string, unknown>, projection?: Record<string, 0 | 1>, opts?: ProjectRowOptions): Record<string, unknown>;
566
602
  //#endregion
package/dist/index.d.mts CHANGED
@@ -33,6 +33,8 @@ declare class MemoryAdapter extends BaseDbAdapter {
33
33
  private _db;
34
34
  /** Memoized physical PK field names — stable for the adapter's lifetime. */
35
35
  private _pkFieldsCache?;
36
+ /** Memoized compiled readers of {@link _physicalPkFields}. */
37
+ private _pkReadersCache?;
36
38
  /**
37
39
  * Memoized map of PHYSICAL field name → optional `start` for every field
38
40
  * carrying `@db.default.increment`. Stable per adapter (see
@@ -123,6 +125,8 @@ declare class MemoryAdapter extends BaseDbAdapter {
123
125
  * `pkKey`/projection — recomputing would re-read `this._table.*` and re-allocate.
124
126
  */
125
127
  private _physicalPkFields;
128
+ /** Compiled readers of {@link _physicalPkFields} (memoized like it). */
129
+ private _pkReaders;
126
130
  /**
127
131
  * PHYSICAL field name → optional `start` for every `@db.default.increment`
128
132
  * field. Discovered lazily from the table's field descriptors — each carries
@@ -175,6 +179,16 @@ declare class MemoryAdapter extends BaseDbAdapter {
175
179
  * `['a:b','c']` differ, and `1` differs from `'1'`.
176
180
  */
177
181
  private pkKey;
182
+ /**
183
+ * The storage key a filter pins by exact equality on EVERY primary-key field
184
+ * — `{ id: v }`, `{ id: { $eq: v } }`, also inside (nested) `$and` — or
185
+ * `undefined`. Only a string / number / boolean / `Date` value pins: for
186
+ * those, a row the filter matches is stored exactly under this key (strict
187
+ * equality ⇒ equal `JSON.stringify`), so a read can look the row up and
188
+ * still run the full predicate on it. Unavailable once the table holds an
189
+ * {@link MemoryTableState.opaquePk opaque} key.
190
+ */
191
+ private _pinnedKey;
178
192
  /**
179
193
  * Builds a DEFINED inserted-id for a table with NO single `@meta.id` — a
180
194
  * composite (or single non-meta) primary key, where
@@ -251,8 +265,9 @@ declare class MemoryAdapter extends BaseDbAdapter {
251
265
  /**
252
266
  * Clones the payload in, applies the version default, generates any
253
267
  * `@db.default.increment` values, enforces PK + unique constraints, stores
254
- * the row, and returns the resolved inserted id. Clone-in (`structuredClone`)
255
- * is what makes post-insert mutation of the caller's object never leak into
268
+ * the row, and returns the resolved inserted id. Clone-in ({@link cloneValue},
269
+ * `structuredClone`-equivalent) is what makes post-insert mutation of the
270
+ * caller's object never leak into
256
271
  * the store. Called once per item by both `insertOne` and `insertMany`, so
257
272
  * increment values advance sequentially across a batch.
258
273
  */
@@ -261,12 +276,21 @@ declare class MemoryAdapter extends BaseDbAdapter {
261
276
  * Enforces every recorded unique index against the current store. A row is
262
277
  * exempted from an index (present-only semantics) when ANY of that index's
263
278
  * optional fields is absent/`null`. Otherwise a stored row with an equal
264
- * tuple → `CONFLICT`.
279
+ * tuple → `CONFLICT`. Returns the row's tuple key per index (for
280
+ * {@link _storeRow}).
265
281
  *
266
282
  * `excludeKey` (when given) skips the row stored under that {@link pkKey} — so
267
283
  * a row updating its own unique value does not false-conflict with itself.
268
284
  */
269
285
  private _enforceUniqueIndexes;
286
+ /**
287
+ * Stores `row` under `key` and records it in the unique indexes (`tuples`:
288
+ * its precomputed tuple keys). The ONLY way a row enters the store, so the
289
+ * indexes and {@link MemoryTableState.opaquePk} never drift from it.
290
+ */
291
+ private _storeRow;
292
+ /** Removes the row stored under `key` (and its unique-index entries). */
293
+ private _removeRow;
270
294
  insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
271
295
  insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
272
296
  supportsInsertIgnore(): boolean;
@@ -311,7 +335,7 @@ declare class MemoryAdapter extends BaseDbAdapter {
311
335
  * because this adapter reports no {@link supportsNativePatch}), so they must
312
336
  * nest into the stored document — MERGING siblings — exactly like Mongo's
313
337
  * `$set: { "profile.city": v }`, not create a literal dotted key. Top-level
314
- * (dot-free) keys behave as a plain assignment. `data` is `structuredClone`d
338
+ * (dot-free) keys behave as a plain assignment. `data` is deep-cloned
315
339
  * first so nested subtrees from the caller never alias into the store.
316
340
  * - `ops.inc` / `ops.mul`: numeric increment / multiply on the (dot-path)
317
341
  * target, coercing a missing or non-numeric current value to `0` (parity with
@@ -323,10 +347,11 @@ declare class MemoryAdapter extends BaseDbAdapter {
323
347
  */
324
348
  private _applyUpdate;
325
349
  /**
326
- * Places `next` into the store under its (possibly changed) {@link pkKey},
327
- * re-keying when a mutation/replace moved the primary key. A collision on the
328
- * NEW key (some other row already owns it) throws `CONFLICT`. Throws BEFORE
329
- * touching the Map so a failed re-key leaves the store unchanged.
350
+ * Places `next` into the store under its (possibly changed) {@link pkKey} in
351
+ * place of `oldRow` (stored under `oldKey`), re-keying when a mutation/replace
352
+ * moved the primary key. A collision on the NEW key (some other row already
353
+ * owns it) throws `CONFLICT`. Throws BEFORE touching the Map so a failed
354
+ * re-key leaves the store unchanged. `tuples` are `next`'s unique-index keys.
330
355
  */
331
356
  private _commitRow;
332
357
  /**
@@ -346,12 +371,13 @@ declare class MemoryAdapter extends BaseDbAdapter {
346
371
  * Stable multi-key comparator from `$sort`, with a final tie-break on
347
372
  * {@link pkKey} for a deterministic total order. Returns the input unchanged
348
373
  * (insertion order) when there is no `$sort`. Delegates to the shared pure
349
- * {@link sortRows}, injecting {@link pkKey} as the total-order tie-break.
374
+ * {@link sortRows}, injecting {@link pkKey} as the total-order tie-break;
375
+ * `topK` asks for the head of that order only (`$skip + $limit`).
350
376
  */
351
377
  private _sortRows;
352
378
  /**
353
- * Projects a stored row per `$select` and returns a fresh, deep-cloned object
354
- * so the store can never be mutated through a returned value.
379
+ * Compiles the `$select` projection of one read: each returned row is a
380
+ * fresh, deep-cloned object so the store can never be mutated through it.
355
381
  *
356
382
  * - No projection → a full clone.
357
383
  * - INCLUSION form (`{ field: 1 }`) → a new object with exactly the selected
@@ -362,7 +388,7 @@ declare class MemoryAdapter extends BaseDbAdapter {
362
388
  * Top-level and nested dot-paths are supported; exotic Mongo projection
363
389
  * quirks (array positional, `$slice`, etc.) are intentionally NOT replicated.
364
390
  */
365
- private _projectAndClone;
391
+ private _projector;
366
392
  /**
367
393
  * Reads the pagination/sort/projection controls with their intended types.
368
394
  * `DbControls` carries a `[key: `$${string}`]: unknown` index signature, and
@@ -376,6 +402,10 @@ declare class MemoryAdapter extends BaseDbAdapter {
376
402
  * shares. Goes through the {@link _loadRows} seam exactly ONCE per call, so a
377
403
  * reader (and provider read-through mode) has one place that materializes the
378
404
  * working set — one provider invocation per logical read.
405
+ *
406
+ * Stored mode reads the table's Map in place (no snapshot copy), resolves a
407
+ * filter pinning the whole primary key by direct lookup, and stops after
408
+ * `limit` matches when given (an unsorted `findOne`).
379
409
  */
380
410
  private _filteredRows;
381
411
  findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
@@ -521,13 +551,16 @@ declare function buildMemoryPredicate(filter: FilterExpr, relationSets?: Relatio
521
551
  * rows with equal sort keys still order deterministically. When ABSENT the
522
552
  * sort falls back to preserving input order among equal keys (via each row's
523
553
  * original index), so a consumer with no primary key keeps insertion order.
554
+ * - `topK` (since 0.1.151), when supplied, asks for only the first `topK` rows
555
+ * of that order (e.g. `$skip + $limit`): a small one is selected in one pass
556
+ * instead of sorting everything. The rows returned are exactly the head of
557
+ * the full sort — same comparator, same stable tie handling.
524
558
  *
525
- * NEVER mutates the input array: `.map` decorates into a fresh array and
526
- * `.toSorted` returns another new sorted array (unlike `.sort`, which reorders
527
- * in place). The `tieBreak`/index is computed ONCE per row (O(n)), not inside
528
- * the O(n log n) comparator.
559
+ * NEVER mutates the input array. Each row's sort keys are read ONCE (O(n)) and
560
+ * its `tieBreak` only when two rows tie on every key — not inside the
561
+ * O(n log n) comparator.
529
562
  */
530
- declare function sortRows(rows: Record<string, unknown>[], $sort?: Partial<Record<string, 1 | -1>>, tieBreak?: (row: Record<string, unknown>) => string | number): Record<string, unknown>[];
563
+ declare function sortRows(rows: Record<string, unknown>[], $sort?: Partial<Record<string, 1 | -1>>, tieBreak?: (row: Record<string, unknown>) => string | number, topK?: number): Record<string, unknown>[];
531
564
  /** Options for {@link projectRow}. */
532
565
  interface ProjectRowOptions {
533
566
  /**
@@ -561,6 +594,9 @@ interface ProjectRowOptions {
561
594
  *
562
595
  * Top-level and nested dot-paths are supported; exotic Mongo projection quirks
563
596
  * (array positional, `$slice`, etc.) are intentionally NOT replicated.
597
+ *
598
+ * Clones are `structuredClone`-equivalent. To project many rows the same way,
599
+ * compile once with {@link compileProjection}.
564
600
  */
565
601
  declare function projectRow(row: Record<string, unknown>, projection?: Record<string, 0 | 1>, opts?: ProjectRowOptions): Record<string, unknown>;
566
602
  //#endregion