@ignex/ninox 0.1.4 → 0.1.5
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/README.md +45 -2
- package/dist/index.d.ts +200 -66
- package/dist/index.js +1204 -340
- package/dist/index.js.map +1 -1
- package/dist/{timeout-BE_ULjdG.d.ts → timeout-B10CTiGm.d.ts} +13 -1
- package/dist/utils.d.ts +12 -7
- package/dist/utils.js +63 -8
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -556,6 +556,7 @@ topProducts.invalidate(); // drop all entries
|
|
|
556
556
|
topProducts.invalidate(3); // drop just the { limit: 3 } entry
|
|
557
557
|
hot.invalidateParams('topProducts', 3); // same, via dynamic name lookup
|
|
558
558
|
hot.invalidateCollection('products'); // drop every query watching that collection
|
|
559
|
+
hot.invalidateIds('products', [id]); // drop only entries depending on that document
|
|
559
560
|
await hot.stop(); // close streams + ticker
|
|
560
561
|
```
|
|
561
562
|
|
|
@@ -565,6 +566,47 @@ parameters. Loader errors are never cached (they retry on the next read), and
|
|
|
565
566
|
concurrent identical reads are in-flight-deduped. `has(name)` checks whether a
|
|
566
567
|
query is registered.
|
|
567
568
|
|
|
569
|
+
### Fine-tuned purges: watch ids (or groups of ids)
|
|
570
|
+
|
|
571
|
+
By default a write anywhere in a collection purges **every** query bound to it.
|
|
572
|
+
That's the safe contract — and often what you want for wide results like
|
|
573
|
+
aggregations. When a query's entries map cleanly onto specific documents,
|
|
574
|
+
declare `idsOf` on the watch ref and invalidation becomes surgical:
|
|
575
|
+
|
|
576
|
+
```ts
|
|
577
|
+
const userFeed = hot.register('userFeed', {
|
|
578
|
+
loader: async (userId: string) => db.findMany('orders', { customerId: userId }),
|
|
579
|
+
watch: [
|
|
580
|
+
// Only the entries whose feed includes THIS order are purged; other
|
|
581
|
+
// users' feeds stay warm. Works across processes via change streams.
|
|
582
|
+
{ db: db.client, collection: 'orders', idsOf: (userId: string) => feedOrderIds(userId) },
|
|
583
|
+
],
|
|
584
|
+
});
|
|
585
|
+
// …or drive it yourself (works in standalone mode too):
|
|
586
|
+
hot.invalidateIds('orders', [changedOrderId]);
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
- `idsOf` receives the query's loader arguments and returns **every document id
|
|
590
|
+
the result depends on** — one per doc, or many for grouped/aggregated rows.
|
|
591
|
+
Ids may be strings, numbers or ObjectIds; an ObjectId and its hex string
|
|
592
|
+
match. It must be pure and cheap (runs once per load).
|
|
593
|
+
- **Omit `idsOf` to keep burst semantics** — the right choice for whole-collection
|
|
594
|
+
aggregations (`countDocuments`, `$group`, …): any change to the collection
|
|
595
|
+
purges the query's entire LRU.
|
|
596
|
+
- A ref without an extractor next to one with (multi-collection watchers)
|
|
597
|
+
forces a full purge whenever *its* collection changes; the extracted
|
|
598
|
+
collection stays surgical.
|
|
599
|
+
- If `idsOf` throws or returns a non-array, affected entries are treated
|
|
600
|
+
conservatively: they are dropped by any targeted change instead of kept.
|
|
601
|
+
- Watch `stats().perQuery[name].idDrops` to see how many entries targeted
|
|
602
|
+
purges are saving you (each drop is a read that stayed warm elsewhere).
|
|
603
|
+
|
|
604
|
+
In replica mode the change stream feeds `invalidateIds` automatically from each
|
|
605
|
+
event's `documentKey._id`; in standalone mode call it from your ORM's
|
|
606
|
+
after-write hooks or wherever you learn about a change. Note that extraction
|
|
607
|
+
sees only the loader arguments — a by-sku lookup whose dependency `_id` isn't
|
|
608
|
+
derivable from `{ sku }` should either key entries by `_id` or stay coarse.
|
|
609
|
+
|
|
568
610
|
Set `autoRefresh: false` to **turn off the global auto-refetch** entirely — the
|
|
569
611
|
standalone ticker never runs, so data only updates via manual invalidation or
|
|
570
612
|
`ttlMs` expiry. This keeps the DB from being polled when you'd rather drive
|
|
@@ -578,8 +620,9 @@ const hot = createHotCache({ probe, autoRefresh: false, defaultTtlMs: 5_000 });
|
|
|
578
620
|
Pin the mode to skip the probe entirely when you already know the deployment
|
|
579
621
|
(`mode: 'replica'` / `mode: 'standalone'`), and use `hot.stats()` for
|
|
580
622
|
observability — it reports per-query `hits` / `misses` / `refreshes` /
|
|
581
|
-
`loadErrors` / `sizeSkips` plus current sizes. Cap runaway single
|
|
582
|
-
per-query `maxValueBytes` (larger values are still returned,
|
|
623
|
+
`loadErrors` / `sizeSkips` / `idDrops` plus current sizes. Cap runaway single
|
|
624
|
+
entries with a per-query `maxValueBytes` (larger values are still returned,
|
|
625
|
+
just not cached).
|
|
583
626
|
|
|
584
627
|
Cached results are shared **by reference** (zero clone overhead); if a caller might
|
|
585
628
|
mutate a result, enable per-query `clone: true` (or `cache: { clone: true }`) so
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as mongodb from 'mongodb';
|
|
2
2
|
import { Db, MongoClient, ClientSession, Document, Filter, Hint, CollationOptions, ReadPreferenceLike, ReadConcern, WriteConcern, WithId, Condition, RootFilterOperators, Collection, FindOptions, Sort, FindCursor, ObjectId, Decimal128, ReadConcernLike, AggregateOptions, AggregationCursor, CreateCollectionOptions, TransactionOptions, InsertOneOptions, InsertOneResult, InsertManyResult, UpdateOptions, UpdateResult, FindOneAndUpdateOptions, DeleteResult, ChangeStreamOptions, ChangeStream } from 'mongodb';
|
|
3
|
-
import { L as LoggerLike } from './timeout-
|
|
4
|
-
export { C as CacheOptions, a as LRU, b as LRUOptions, c as LogFn, d as LogLevel, e as createCachedAsyncFactory, f as createCachedFactory, g as createConsoleLogger, h as createNoopLogger, i as hashString, s as sleep, j as sleepJittered, k as stableHash, l as stableStringify, w as withTimeout } from './timeout-
|
|
3
|
+
import { L as LoggerLike } from './timeout-B10CTiGm.js';
|
|
4
|
+
export { C as CacheOptions, a as LRU, b as LRUOptions, c as LogFn, d as LogLevel, e as createCachedAsyncFactory, f as createCachedFactory, g as createConsoleLogger, h as createNoopLogger, i as hashString, s as sleep, j as sleepJittered, k as stableHash, l as stableStringify, w as withTimeout } from './timeout-B10CTiGm.js';
|
|
5
5
|
import * as bson from 'bson';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -13,10 +13,37 @@ import * as bson from 'bson';
|
|
|
13
13
|
|
|
14
14
|
/** A collection to watch for invalidation. `collection` is the PHYSICAL name. */
|
|
15
15
|
interface HotCollectionRef {
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Raw `Db` handle (e.g. `service.db.primaryClient.client`) — or a lazy
|
|
18
|
+
* `() => Db` accessor resolved when the watcher starts. The lazy form lets
|
|
19
|
+
* apps register queries at module scope (before the connection exists) and
|
|
20
|
+
* only touch the live handle at `start()` time.
|
|
21
|
+
*/
|
|
22
|
+
db: Db | (() => Db);
|
|
18
23
|
/** Physical collection name whose writes invalidate bound queries. */
|
|
19
24
|
collection: string;
|
|
25
|
+
/**
|
|
26
|
+
* Fine-tuned invalidation: extract the document ids (or groups of ids) that
|
|
27
|
+
* one loader call's arguments depend on. Without this, ANY write to the
|
|
28
|
+
* collection purges every entry of every query bound to it; with it, a
|
|
29
|
+
* change-stream event (replica mode) or a manual
|
|
30
|
+
* `invalidateIds(collection, [id])` call purges ONLY the entries that
|
|
31
|
+
* extracted this id — sibling entries stay warm.
|
|
32
|
+
*
|
|
33
|
+
* Return every id the result depends on (one per document, or many for
|
|
34
|
+
* grouped/aggregated results). Ids may be strings, numbers, ObjectIds… —
|
|
35
|
+
* they are compared after stable normalization, so an ObjectId and its hex
|
|
36
|
+
* string match.
|
|
37
|
+
*
|
|
38
|
+
* Must be pure and cheap (it runs once per load). If it throws or returns a
|
|
39
|
+
* non-array the entry is marked conservative: it is dropped by ANY targeted
|
|
40
|
+
* change on that collection instead of being kept incorrectly.
|
|
41
|
+
*
|
|
42
|
+
* Typed as `never`-params (contravariance-safe, same trick as `LRU`'s
|
|
43
|
+
* `onEvict`): declare your own parameter types and they line up with the
|
|
44
|
+
* query's loader args — `idsOf: (team: string) => members(team)`.
|
|
45
|
+
*/
|
|
46
|
+
idsOf?: (...args: never) => readonly unknown[];
|
|
20
47
|
}
|
|
21
48
|
/** Opt-in query registration. `TArgs` = the parameter tuple, `TResult` = loader result. */
|
|
22
49
|
interface HotQueryConfig<TArgs extends readonly unknown[], TResult> {
|
|
@@ -124,6 +151,8 @@ interface HotQueryStats {
|
|
|
124
151
|
loadErrors: number;
|
|
125
152
|
/** Values too large to cache (`maxValueBytes`). */
|
|
126
153
|
sizeSkips: number;
|
|
154
|
+
/** Entries dropped by id-targeted invalidation (`invalidateIds` / change events). */
|
|
155
|
+
idDrops: number;
|
|
127
156
|
/** Capacity evictions (LRU). */
|
|
128
157
|
evictions: number;
|
|
129
158
|
}
|
|
@@ -187,18 +216,41 @@ declare class HotCache {
|
|
|
187
216
|
* called manually to wire ORM write hooks in standalone mode.
|
|
188
217
|
*/
|
|
189
218
|
invalidateCollection(collection: string): void;
|
|
219
|
+
/**
|
|
220
|
+
* Fine-tuned purge: drop only the entries that depend on specific document
|
|
221
|
+
* ids of a watched collection. Two complementary mechanisms feed it:
|
|
222
|
+
*
|
|
223
|
+
* - **Replica mode** — the change stream extracts `documentKey._id` from
|
|
224
|
+
* each event and calls this with just that id, so a write to one document
|
|
225
|
+
* no longer purges every query bound to the whole collection.
|
|
226
|
+
* - **Manual (any mode)** — call it from ORM after-write hooks or anywhere
|
|
227
|
+
* else you know exactly which ids changed.
|
|
228
|
+
*
|
|
229
|
+
* Per query bound to `collection`, semantics are:
|
|
230
|
+
* - watch ref declares `idsOf` → only LRU entries whose extracted ids
|
|
231
|
+
* include one of `ids` are dropped (`depsAll` entries — where extraction
|
|
232
|
+
* failed — are dropped too; conservative). Sibling entries stay warm and
|
|
233
|
+
* `stats().perQuery[name].idDrops` counts what was dropped.
|
|
234
|
+
* - no `idsOf` on any bound ref → full purge of that query (the
|
|
235
|
+
* `invalidateCollection` behavior — the safe default).
|
|
236
|
+
*/
|
|
237
|
+
invalidateIds(collection: string, ids: readonly unknown[]): void;
|
|
190
238
|
/** Drop every cached entry for every query. */
|
|
191
239
|
clear(): void;
|
|
192
240
|
/**
|
|
193
241
|
* Runtime observability snapshot: total queries/entries plus per-query
|
|
194
|
-
* counters (`hits`, `misses`, background `refreshes`, `loadErrors`,
|
|
195
|
-
* `sizeSkips` for entries dropped by `maxValueBytes`
|
|
196
|
-
*
|
|
242
|
+
* counters (`hits`, `misses`, background `refreshes`, `loadErrors`,
|
|
243
|
+
* `sizeSkips` for entries dropped by `maxValueBytes`, and `idDrops` for
|
|
244
|
+
* entries dropped by id-targeted invalidation). Useful for dashboards and
|
|
245
|
+
* cache-effectiveness checks.
|
|
197
246
|
*/
|
|
198
247
|
stats(): HotCacheStats;
|
|
199
248
|
/**
|
|
200
249
|
* Probe replica support once, then start change-stream watchers (replica) or
|
|
201
250
|
* the global refresh ticker (standalone). Idempotent — resolves the mode.
|
|
251
|
+
* A FAILED start does not stick: the memoized promise is cleared so the next
|
|
252
|
+
* `get()`/`start()` retries probing (one transient blip during startup must
|
|
253
|
+
* not disable background freshness for the life of the instance).
|
|
202
254
|
*/
|
|
203
255
|
start(): Promise<HotCacheMode>;
|
|
204
256
|
/**
|
|
@@ -207,11 +259,25 @@ declare class HotCache {
|
|
|
207
259
|
* re-probes or re-opens background resources.
|
|
208
260
|
*/
|
|
209
261
|
stop(): Promise<void>;
|
|
262
|
+
/**
|
|
263
|
+
* A FAILED start does not stick: clear the memoized state so the next
|
|
264
|
+
* `get()`/`start()` retries probing (one transient blip during startup must
|
|
265
|
+
* not disable background freshness for the life of the instance).
|
|
266
|
+
*/
|
|
267
|
+
private forgetFailedStart;
|
|
210
268
|
private runStart;
|
|
211
269
|
private ensureStarted;
|
|
212
270
|
private defaultProbe;
|
|
213
271
|
private fetch;
|
|
214
272
|
private setEntry;
|
|
273
|
+
/**
|
|
274
|
+
* Extract the watched-document dependencies of one loader call. Namespaced by
|
|
275
|
+
* collection so multi-collection watchers never cross-invalidate. An extractor
|
|
276
|
+
* that throws or returns a non-array yields `{ depsAll: true }` — the entry is
|
|
277
|
+
* then dropped by ANY targeted change on its watched collections instead of
|
|
278
|
+
* being incorrectly kept.
|
|
279
|
+
*/
|
|
280
|
+
private computeDeps;
|
|
215
281
|
}
|
|
216
282
|
/** Create a new global hot-cache instance. */
|
|
217
283
|
declare const createHotCache: (options?: HotCacheOptions) => HotCache;
|
|
@@ -286,6 +352,12 @@ declare const cacheCollectionKey: (dbName: string, physical: string) => string;
|
|
|
286
352
|
declare class QueryCache {
|
|
287
353
|
private lru;
|
|
288
354
|
private index;
|
|
355
|
+
/**
|
|
356
|
+
* Reverse index: key → every collection it is registered under. Without it,
|
|
357
|
+
* keys dropped by TTL expiry / capacity eviction / version-guard would stay
|
|
358
|
+
* listed in `index` forever (unbounded growth + O(dead) invalidation scans).
|
|
359
|
+
*/
|
|
360
|
+
private keyCollections;
|
|
289
361
|
/** Per-collection version — bumped on every invalidation/clear of that collection. */
|
|
290
362
|
private colVersions;
|
|
291
363
|
private ttlMs;
|
|
@@ -309,6 +381,10 @@ declare class QueryCache {
|
|
|
309
381
|
*/
|
|
310
382
|
versionOf(collection: string): number;
|
|
311
383
|
get(key: string): unknown | undefined;
|
|
384
|
+
/** Remove a key from the LRU and every collection index that references it. */
|
|
385
|
+
private dropKey;
|
|
386
|
+
/** Remove `key` from `index` (called for evictions AND explicit drops). */
|
|
387
|
+
private unregisterKey;
|
|
312
388
|
set(key: string, value: unknown, ttlMs?: number, collections?: string[], versions?: Record<string, number>): void;
|
|
313
389
|
delete(key: string): void;
|
|
314
390
|
/** Drop every cached entry belonging to a physical collection. */
|
|
@@ -439,6 +515,14 @@ declare const TRANSIENT_MONGO_ERROR_CODES: ReadonlySet<number>;
|
|
|
439
515
|
declare const isMongoTransientError: (error: unknown) => boolean;
|
|
440
516
|
/** True when the error is a duplicate-key violation (code 11000 / E11000 message). */
|
|
441
517
|
declare const isMongoDuplicateKeyError: (error: unknown) => boolean;
|
|
518
|
+
/**
|
|
519
|
+
* True when the deployment cannot run transactions at all (standalone mongod,
|
|
520
|
+
* or storage engine without retryable writes) — callers fall back to
|
|
521
|
+
* non-transactional execution. Shared by BOTH fallback layers
|
|
522
|
+
* (`withGracefulMongoTransaction` and the service `transaction` op) so they
|
|
523
|
+
* classify identically.
|
|
524
|
+
*/
|
|
525
|
+
declare const isTransactionUnsupportedError: (error: unknown) => boolean;
|
|
442
526
|
|
|
443
527
|
/** Minimal transaction runner surface (satisfied by the service `transaction`). */
|
|
444
528
|
interface MongoTransactionRunner {
|
|
@@ -453,8 +537,14 @@ interface GracefulTransactionOptions {
|
|
|
453
537
|
}
|
|
454
538
|
/**
|
|
455
539
|
* Run `fn` inside a transaction when the deployment supports it; otherwise run
|
|
456
|
-
* with a `null` session (no-op fallback). Also handles
|
|
457
|
-
* unsupported"
|
|
540
|
+
* with a `null` session (no-op fallback). Also handles "transactions
|
|
541
|
+
* unsupported" errors by re-running with a null session.
|
|
542
|
+
*
|
|
543
|
+
* When capability state is UNKNOWN (probe failed/timed out and no URL hint or
|
|
544
|
+
* env override says otherwise), we ATTEMPT the real transaction rather than
|
|
545
|
+
* silently downgrading — a transient startup blip must not turn an atomic
|
|
546
|
+
* write into a non-atomic one. The catch path still falls back cleanly on a
|
|
547
|
+
* genuinely unsupported deployment (at the cost of one failed round trip).
|
|
458
548
|
*/
|
|
459
549
|
declare const withGracefulMongoTransaction: <T>(runner: MongoTransactionRunner, fn: (session: ClientSession | null) => Promise<T>, options?: GracefulTransactionOptions) => Promise<T>;
|
|
460
550
|
|
|
@@ -478,21 +568,6 @@ declare const HOOK_NAMES: readonly HookName[];
|
|
|
478
568
|
/** Run a single hook (no-op when the collection has none registered). */
|
|
479
569
|
declare const runHooks: <TSchema = Document>(registry: HooksRegistry | undefined, collection: string, name: HookName, ctx: HookContext<TSchema>) => Promise<void>;
|
|
480
570
|
|
|
481
|
-
/**
|
|
482
|
-
* A self-contained DataLoader — batch + cache loading that eliminates the
|
|
483
|
-
* N+1 query problem (the classic GraphQL utility, implemented from scratch so
|
|
484
|
-
* the ORM owns its batching behavior).
|
|
485
|
-
*
|
|
486
|
-
* Semantics:
|
|
487
|
-
* - concurrent `load(key)` calls made in the same tick are coalesced into ONE
|
|
488
|
-
* `batch(keys)` call (flushed on a microtask, or after `maxBatchDelayMs`)
|
|
489
|
-
* - the batch result is a Map (key → value); a missing key resolves `undefined`
|
|
490
|
-
* - results are cached per key (canonicalized via `keyOf`) for the loader's
|
|
491
|
-
* lifetime; `clear`/`clearAll` reset the cache
|
|
492
|
-
* - a FAILED batch is never cached: its keys are evicted so a later `load`
|
|
493
|
-
* runs a fresh batch instead of re-surfacing the same rejection
|
|
494
|
-
* - `maxBatchSize` splits oversized batches
|
|
495
|
-
*/
|
|
496
571
|
interface DataLoaderOptions<Key, Value> {
|
|
497
572
|
/** Resolve a batch of keys → one Map. Keys that have no row should be absent. */
|
|
498
573
|
batch: (keys: Key[]) => Promise<Map<Key, Value>>;
|
|
@@ -549,7 +624,10 @@ interface MigrationModule {
|
|
|
549
624
|
interface MongoMigrationRunner {
|
|
550
625
|
/** Apply every not-yet-applied migration in numeric order. */
|
|
551
626
|
up(): Promise<void>;
|
|
552
|
-
/**
|
|
627
|
+
/**
|
|
628
|
+
* Roll back applied migrations in reverse order (down to `targetName` when
|
|
629
|
+
* given). An unknown `targetName` throws BEFORE anything is rolled back.
|
|
630
|
+
*/
|
|
553
631
|
down(targetName?: string): Promise<void>;
|
|
554
632
|
status(): Promise<{
|
|
555
633
|
applied: string[];
|
|
@@ -561,6 +639,18 @@ interface MongoMigrationRunner {
|
|
|
561
639
|
interface MongoMigrationRunnerOptions {
|
|
562
640
|
migrationDir?: string;
|
|
563
641
|
logger?: LoggerLike;
|
|
642
|
+
/**
|
|
643
|
+
* Which connected database the journal + migrations target (key of the
|
|
644
|
+
* service's db map). Defaults to the FIRST connected key — set this explicitly
|
|
645
|
+
* in multi-database services to avoid silently migrating the wrong one.
|
|
646
|
+
*/
|
|
647
|
+
db?: string;
|
|
648
|
+
/**
|
|
649
|
+
* Migration claim lease in ms (default 120_000). A `running` claim older than
|
|
650
|
+
* the lease is considered orphaned (hard crash) and may be stolen by another
|
|
651
|
+
* runner; live claims are renewed automatically during long `up()`s.
|
|
652
|
+
*/
|
|
653
|
+
leaseMs?: number;
|
|
564
654
|
}
|
|
565
655
|
|
|
566
656
|
/**
|
|
@@ -568,11 +658,14 @@ interface MongoMigrationRunnerOptions {
|
|
|
568
658
|
* two sub-systems:
|
|
569
659
|
*
|
|
570
660
|
* - `./files.ts` — discover / load / scaffold `NNN_name.ts` files
|
|
571
|
-
* - `./journal.ts` —
|
|
661
|
+
* - `./journal.ts` — lease-based `_migrations` journal (atomic, crash-safe)
|
|
572
662
|
*
|
|
573
|
-
* `up()` applies pending migrations in numeric order
|
|
574
|
-
*
|
|
575
|
-
*
|
|
663
|
+
* `up()` applies pending migrations in numeric order under an exclusive claim
|
|
664
|
+
* with an auto-renewed lease (long migrations can't be stolen by another
|
|
665
|
+
* runner, but a crashed runner's claim expires); `down()` rolls back applied
|
|
666
|
+
* migrations in reverse order and validates its target BEFORE destroying
|
|
667
|
+
* anything; `status()` reports applied/pending; `create()` scaffolds a new file
|
|
668
|
+
* atomically (`wx` — concurrent scaffolds never clobber each other).
|
|
576
669
|
*/
|
|
577
670
|
|
|
578
671
|
/** The `service` shape the runner needs (one connected manager + config/logger). */
|
|
@@ -592,10 +685,12 @@ interface RetryOptions {
|
|
|
592
685
|
}
|
|
593
686
|
/**
|
|
594
687
|
* Retry `fn` while it throws a *transient* Mongo error (network / not-primary /
|
|
595
|
-
* sharding), with exponential backoff
|
|
596
|
-
*
|
|
688
|
+
* sharding / server-selection during failover), with exponential backoff
|
|
689
|
+
* `delayMs * 2^attempt` plus ±20% jitter — synchronized retry waves against a
|
|
690
|
+
* recovering cluster amplify outages instead of absorbing them. Domain errors
|
|
691
|
+
* and genuine data errors are never retried.
|
|
597
692
|
*/
|
|
598
|
-
declare const withRetry: <T>(fn: () => Promise<T>, options?: RetryOptions
|
|
693
|
+
declare const withRetry: <T>(fn: () => Promise<T>, options?: RetryOptions) => Promise<T>;
|
|
599
694
|
/**
|
|
600
695
|
* Manual transaction helper: start → run → commit, aborting + ending the
|
|
601
696
|
* session on any failure. Prefer the service-level `transaction()` which also
|
|
@@ -939,14 +1034,22 @@ interface QueryOptions {
|
|
|
939
1034
|
* a committed write whose ack was lost would be re-executed, so enable only
|
|
940
1035
|
* for idempotent writes). Default: off for write ops.
|
|
941
1036
|
*/
|
|
942
|
-
|
|
1037
|
+
/**
|
|
1038
|
+
* Per-op write-retry override. Reads always retry transient errors; writes
|
|
1039
|
+
* retry ONLY when `retryWrites: true` (retrying a write is at-least-once —
|
|
1040
|
+
* a committed write whose ack was lost would be re-executed, so enable only
|
|
1041
|
+
* for idempotent writes). Default: off for write ops.
|
|
1042
|
+
*/
|
|
1043
|
+
retryWrites?: boolean;
|
|
1044
|
+
/**
|
|
943
1045
|
* Per-op schema-drift policy override for reads (see `MongoServiceConfig.drift`).
|
|
944
1046
|
* `true` → `'throw'`, `false` → `'off'`, or an explicit `DriftMode` string.
|
|
945
1047
|
* Unset inherits the service-level default (`'report'`). Only checked when a
|
|
946
1048
|
* document is fetched from the DB (cache-miss); projected/partial reads are
|
|
947
1049
|
* skipped.
|
|
948
|
-
|
|
949
|
-
drift?: DriftMode | boolean
|
|
1050
|
+
*/
|
|
1051
|
+
drift?: DriftMode | boolean;
|
|
1052
|
+
/** Driver-level options forwarded untouched to the underlying call. */
|
|
950
1053
|
collation?: CollationOptions;
|
|
951
1054
|
readPreference?: ReadPreferenceLike;
|
|
952
1055
|
readConcern?: ReadConcern;
|
|
@@ -1013,16 +1116,16 @@ interface QueryBuilderContext<TDoc extends Document> {
|
|
|
1013
1116
|
run: <T>(opName: string, filter: FilterInput<TDoc>, execute: (driverOpts: FindOptions) => Promise<T>, options?: QueryOptions) => Promise<T>;
|
|
1014
1117
|
}
|
|
1015
1118
|
/**
|
|
1016
|
-
* Lazy, fluent, schema-typed query builder.
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1119
|
+
* Lazy, fluent, IMMUTABLE, schema-typed query builder. Every chain method
|
|
1120
|
+
* returns a NEW builder — a stored base query can be safely reused as a
|
|
1121
|
+
* template without derived chains contaminating it:
|
|
1122
|
+
*
|
|
1123
|
+
* const admins = users.query('users').where({ role: 'admin' });
|
|
1124
|
+
* await admins.where({ age: { $gte: 18 } }).many(); // base unchanged
|
|
1019
1125
|
*
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
1023
|
-
* .select(['_id', 'email'])
|
|
1024
|
-
* .limit(20)
|
|
1025
|
-
* .many();
|
|
1126
|
+
* Nothing hits the database until an executor (`one`/`many`/`cursor`/`count`/
|
|
1127
|
+
* `exists`) is called. Projections via `select` are pushed to the driver so
|
|
1128
|
+
* only requested fields are transferred.
|
|
1026
1129
|
*/
|
|
1027
1130
|
declare class QueryBuilder<TDoc extends Document, TSelection = TDoc> {
|
|
1028
1131
|
private readonly ctx;
|
|
@@ -1034,34 +1137,39 @@ declare class QueryBuilder<TDoc extends Document, TSelection = TDoc> {
|
|
|
1034
1137
|
private _options;
|
|
1035
1138
|
constructor(ctx: QueryBuilderContext<TDoc>);
|
|
1036
1139
|
get filter(): FilterInput<TDoc>;
|
|
1037
|
-
/**
|
|
1038
|
-
|
|
1039
|
-
/**
|
|
1040
|
-
|
|
1041
|
-
/**
|
|
1042
|
-
|
|
1140
|
+
/** Copy-on-write fork used by every chain method. */
|
|
1141
|
+
private fork;
|
|
1142
|
+
/** Merge a filter into the current one (operator docs deep-merge per key). */
|
|
1143
|
+
where(filter: FilterInput<TDoc>): QueryBuilder<TDoc, TSelection>;
|
|
1144
|
+
/** AND-combine the current filter with a new one (stays flat across repeats). */
|
|
1145
|
+
and(filter: FilterInput<TDoc>): QueryBuilder<TDoc, TSelection>;
|
|
1146
|
+
/**
|
|
1147
|
+
* OR-combine: current filter AND (filter1 OR filter2 ...). Requires at least
|
|
1148
|
+
* one filter — an empty `$or` is rejected by the server.
|
|
1149
|
+
*/
|
|
1150
|
+
or(...filters: Array<FilterInput<TDoc>>): QueryBuilder<TDoc, TSelection>;
|
|
1043
1151
|
/** Set the sort order (`{ field: 1 | -1 }`). */
|
|
1044
|
-
sort(sort: Sort):
|
|
1152
|
+
sort(sort: Sort): QueryBuilder<TDoc, TSelection>;
|
|
1045
1153
|
/** Skip the first `n` matching documents. */
|
|
1046
|
-
skip(n: number):
|
|
1154
|
+
skip(n: number): QueryBuilder<TDoc, TSelection>;
|
|
1047
1155
|
/** Limit the result set to `n` documents. */
|
|
1048
|
-
limit(n: number):
|
|
1156
|
+
limit(n: number): QueryBuilder<TDoc, TSelection>;
|
|
1049
1157
|
/** Project only the given fields — the projection is pushed to the driver. */
|
|
1050
1158
|
select<const K extends keyof TSelection>(fields: readonly K[]): QueryBuilder<TDoc, Pick<TSelection, K>>;
|
|
1051
1159
|
/** Raw projection document (`{ field: 1 }` / `{ field: 0 }`). */
|
|
1052
|
-
project(projection: Document):
|
|
1160
|
+
project(projection: Document): QueryBuilder<TDoc, TSelection>;
|
|
1053
1161
|
/** Set the index hint. */
|
|
1054
|
-
hint(hint: Hint):
|
|
1162
|
+
hint(hint: Hint): QueryBuilder<TDoc, TSelection>;
|
|
1055
1163
|
/** Set the driver cursor batch size. */
|
|
1056
|
-
batchSize(n: number):
|
|
1164
|
+
batchSize(n: number): QueryBuilder<TDoc, TSelection>;
|
|
1057
1165
|
/** Run the query inside a transaction session. */
|
|
1058
|
-
session(session: NonNullable<QueryOptions['session']>):
|
|
1166
|
+
session(session: NonNullable<QueryOptions['session']>): QueryBuilder<TDoc, TSelection>;
|
|
1059
1167
|
/** Cap server-side execution time. */
|
|
1060
|
-
maxTimeMS(ms: number):
|
|
1168
|
+
maxTimeMS(ms: number): QueryBuilder<TDoc, TSelection>;
|
|
1061
1169
|
/** Bypass the read cache for this query when `false`. */
|
|
1062
|
-
cache(on: boolean):
|
|
1170
|
+
cache(on: boolean): QueryBuilder<TDoc, TSelection>;
|
|
1063
1171
|
/** Override in-flight dedup for this query when set. */
|
|
1064
|
-
dedupe(on: boolean):
|
|
1172
|
+
dedupe(on: boolean): QueryBuilder<TDoc, TSelection>;
|
|
1065
1173
|
/** Compile the builder into driver options (does not execute). */
|
|
1066
1174
|
private compile;
|
|
1067
1175
|
private countOptions;
|
|
@@ -1069,11 +1177,11 @@ declare class QueryBuilder<TDoc extends Document, TSelection = TDoc> {
|
|
|
1069
1177
|
one(): Promise<WithId<TSelection> | null>;
|
|
1070
1178
|
/** Fetch many documents (default limit 100 when unset). */
|
|
1071
1179
|
many(): Promise<WithId<TSelection>[]>;
|
|
1072
|
-
/** Streaming cursor (raw driver cursor; no retry
|
|
1180
|
+
/** Streaming cursor (raw driver cursor; no retry/cache/drift — caller-owned). */
|
|
1073
1181
|
cursor(): FindCursor<WithId<TDoc>>;
|
|
1074
1182
|
/** Count matching documents. */
|
|
1075
1183
|
count(): Promise<number>;
|
|
1076
|
-
/** True when at least one document matches. */
|
|
1184
|
+
/** True when at least one document matches (`_id`-only projection — no doc transfer). */
|
|
1077
1185
|
exists(): Promise<boolean>;
|
|
1078
1186
|
}
|
|
1079
1187
|
|
|
@@ -2024,8 +2132,14 @@ interface DbHealthResult {
|
|
|
2024
2132
|
ok: boolean;
|
|
2025
2133
|
/** Ping round-trip time in ms (present when `ok`). */
|
|
2026
2134
|
latencyMs?: number;
|
|
2135
|
+
/** Why the ping failed — timeout vs auth vs network (present when `!ok`). */
|
|
2136
|
+
error?: string;
|
|
2027
2137
|
}
|
|
2028
2138
|
interface HealthReport {
|
|
2139
|
+
/**
|
|
2140
|
+
* Overall status. `false` when ANY db fails — including the degenerate
|
|
2141
|
+
* "no dbs connected" case (an empty manager record is NOT healthy).
|
|
2142
|
+
*/
|
|
2029
2143
|
ok: boolean;
|
|
2030
2144
|
/** Total elapsed time across all pings in ms. */
|
|
2031
2145
|
latencyMs: number;
|
|
@@ -2138,6 +2252,14 @@ interface PaginationConfig {
|
|
|
2138
2252
|
sort?: Record<string, 1 | -1>;
|
|
2139
2253
|
/** Cap on the requested limit (default DEFAULT_MAX_LIMIT=1000). */
|
|
2140
2254
|
maxLimit?: number;
|
|
2255
|
+
/**
|
|
2256
|
+
* Projection applied to the data facet (e.g. `{ _id: 1, updatedAt: 1 }`).
|
|
2257
|
+
*
|
|
2258
|
+
* Enables lean list reads — only the projected fields cross the wire. When
|
|
2259
|
+
* set, the schema-drift check is skipped for the page (projected docs
|
|
2260
|
+
* intentionally lack the collection's full field set).
|
|
2261
|
+
*/
|
|
2262
|
+
projection?: Document;
|
|
2141
2263
|
/** Stages prepended to the data facet (e.g. extra $match). */
|
|
2142
2264
|
prePipeline?: Document[];
|
|
2143
2265
|
/** Stages appended inside the data facet. */
|
|
@@ -2506,7 +2628,13 @@ declare const createResolveCollectionName: (config: ResolveCollectionConfig) =>
|
|
|
2506
2628
|
* explicit (mirrors sdk-db `normalizeMongoUrl`):
|
|
2507
2629
|
* - retryWrites=false unless already set (keeps semantics predictable)
|
|
2508
2630
|
* - w=1 unless another write concern is present
|
|
2509
|
-
* - directConnection=true for single-host non-
|
|
2631
|
+
* - directConnection=true ONLY for single-host, non-replica-set,
|
|
2632
|
+
* non-SRV URIs (`mongodb+srv` implies replica-set discovery — appending
|
|
2633
|
+
* `directConnection` there makes the driver reject the URI)
|
|
2634
|
+
*
|
|
2635
|
+
* Parsing is manual (string split + URLSearchParams) instead of WHATWG `new
|
|
2636
|
+
* URL`: the URL parser rejects multi-host seed lists (`h1:27017,h2:27017` —
|
|
2637
|
+
* it reads `,h2` as a port) and re-encodes userinfo/path in surprising ways.
|
|
2510
2638
|
*/
|
|
2511
2639
|
declare const normalizeMongoUrl: (rawUrl: string) => string;
|
|
2512
2640
|
|
|
@@ -2560,8 +2688,14 @@ declare const decodeCursor: (cursor: string) => KeysetCursor;
|
|
|
2560
2688
|
declare const buildKeysetFilter: (sort: Record<string, 1 | -1>, values: unknown[], direction: "after" | "before") => Document;
|
|
2561
2689
|
|
|
2562
2690
|
/**
|
|
2563
|
-
* Merge multiple Mongo filters into one. Empty/undefined parts are skipped
|
|
2564
|
-
*
|
|
2691
|
+
* Merge multiple Mongo filters into one. Empty/undefined parts are skipped.
|
|
2692
|
+
*
|
|
2693
|
+
* Conflicting keys deep-merge when BOTH sides are plain objects (operator
|
|
2694
|
+
* documents like `{ $gte: 18 }` / `{ $in: [...] }`), so
|
|
2695
|
+
* `merge({ role: 'admin', age: { $gte: 18 } }, { age: { $lte: 65 } })`
|
|
2696
|
+
* keeps both range clauses instead of the old shallow-assign silently
|
|
2697
|
+
* dropping `$gte`. Scalars replace (last write wins). Top-level `$and`/`$or`
|
|
2698
|
+
* arrays CONCATENATE — dropping a sibling branch would change semantics.
|
|
2565
2699
|
*/
|
|
2566
2700
|
declare const mergeMongoFilters: <T extends Document>(...parts: Array<Filter<T> | undefined>) => Filter<T>;
|
|
2567
2701
|
|
|
@@ -2617,4 +2751,4 @@ declare const createMongoToolkit: <TClients extends DbClientsDefinition = DbClie
|
|
|
2617
2751
|
};
|
|
2618
2752
|
type MongoToolkit<TClients extends DbClientsDefinition> = ReturnType<typeof createMongoToolkit<TClients>>;
|
|
2619
2753
|
|
|
2620
|
-
export { type AccumulatorResult, type AccumulatorSpec, type Added, type AggregationMeta, type AggregationStages, type AnyField, AppError, type ArrayField, BadRequest, type BelongsToRelation, type BooleanField, type CacheInvalidationRef, CacheInvalidator, type Chainable, type CollectionDefinition, type CollectionDoc, type CollectionLike, type CollectionManager, type CollectionMap, type CollectionName, type CollectionTimestamps, type CollectionValue, type CrudListQuery, type CrudOps, type CursorPage, type CursorPaginationConfig, type DBClientDefinition, DEFAULT_FIND_LIMIT, DEFAULT_MAX_LIMIT, DataLoader, type DataLoaderOptions, type DateField, type DateRangeConfig, type DbClientsDefinition, type DbHealthResult, type DecimalField, DomainError, type DoubleField, type DriftIssue, type DriftIssueCode, type DriftMode, ERROR_HTTP_STATUS, type EnumField, type ErrorContext, type ExprResult, type ExtractCollectionNames, type ExtractCollectionType, type ExtractDbNames, type FacetOutput, type FacetPipelineStage, type FacetSpec, type FieldFlags, type FieldMeta, type FieldName, type FieldRef, type FilterInput, type FindQueryOptions, type ForeignDocOf, type GeoNearOutput, type GeoNearSpec, type GeoPoint, type GeoPointField, type GracefulTransactionOptions, type GroupByConfig, type GroupSpec, type Grouped, HOOK_NAMES, type HasManyRelation, type HealthReport, type Hook, type HookContext, type HookMap, type HookName, type HooksRegistry, HotCache, type HotCacheMode, type HotCacheOptions, type HotCacheStats, type HotCollectionRef, type HotQueryAccessor, type HotQueryConfig, type HotQueryStats, InFlight, type IndexSpec, type InferDoc, type InferField, InfraError, type InsertInput, type IntegerField, type JoinedFields, type KeysetCursor, LoggerLike, type LongField, type LookupConfig, type LookupJoinedFor, type LookupParams, type LookupSpecFor, MAX_BATCH_OPS, type ManyToManyRelation, type MigrationContext, type MigrationModule, type MongoCapabilities, type MongoCapabilitiesStore, type MongoJsonSchema, type MongoMigrationRunner, type MongoMigrationRunnerOptions, type MongoService, type MongoServiceConfig, type MongoToolkit, type MongoToolkitOptions, type MongoTransactionRunner, type NamedCollection, type NamedCollectionDefinition, type NamedObjectField, type NormalizedPagination, type NullField, type NumberBounds, type NumberField, type ObjectField, type ObjectIdField, type PaginationConfig, type PaginationResult, PipelineBuilder, type PipelineCustomization, type PipelineStage, type PopulateOptions, type Projected, type Projection, QueryBuilder, type QueryBuilderContext, QueryCache, type QueryCacheOptions, type QueryCacheStats, type QueryOptions, type RawField, type RelationDef, type RelationDefBase, type RemoveIndexSignature, type Repository, type RepositoryOptions, type ResolvedQueryOptions, type RetryOptions, type SchemaType, type SearchConfig, type SortKeys, type StringField, type SyncIndexesResult, TRANSIENT_MONGO_ERROR_CODES, type TimestampsByCollection, type Unsetted, type UnwindSpec, type Unwound, type UpdateInput, type UpdateOperators, type ValidRelation, type VersionedUpdateResult, belongsTo, buildKeysetFilter, buildMongoActiveFilter, buildPaginationResult, cacheCollectionKey, canonicalKey, createHotCache, createMongoCapabilitiesStore, createMongoMigrationRunner, createMongoService, createMongoToolkit, createRepository, createResolveCollectionName, decodeCursor, defineCollection, defineCollections, encodeCursor, hasMany, httpStatusForError, isAppError, isDomainError, isInfraError, isMappedMongoError, isMongoDuplicateKeyError, isMongoTransientError, manyToMany, mapMongoDriverError, mergeMongoActiveFilter, mergeMongoFilters, normalizeMongoUrl, normalizePageLimit, optional, probeMongoCapabilities, runHooks, s, serializeError, stripDocumentId, stripPrimaryKey, toMongoSchema, toMongoValidator, validateDoc, withDefault, withGracefulMongoTransaction, withRetry, withTransaction };
|
|
2754
|
+
export { type AccumulatorResult, type AccumulatorSpec, type Added, type AggregationMeta, type AggregationStages, type AnyField, AppError, type ArrayField, BadRequest, type BelongsToRelation, type BooleanField, type CacheInvalidationRef, CacheInvalidator, type Chainable, type CollectionDefinition, type CollectionDoc, type CollectionLike, type CollectionManager, type CollectionMap, type CollectionName, type CollectionTimestamps, type CollectionValue, type CrudListQuery, type CrudOps, type CursorPage, type CursorPaginationConfig, type DBClientDefinition, DEFAULT_FIND_LIMIT, DEFAULT_MAX_LIMIT, DataLoader, type DataLoaderOptions, type DateField, type DateRangeConfig, type DbClientsDefinition, type DbHealthResult, type DecimalField, DomainError, type DoubleField, type DriftIssue, type DriftIssueCode, type DriftMode, ERROR_HTTP_STATUS, type EnumField, type ErrorContext, type ExprResult, type ExtractCollectionNames, type ExtractCollectionType, type ExtractDbNames, type FacetOutput, type FacetPipelineStage, type FacetSpec, type FieldFlags, type FieldMeta, type FieldName, type FieldRef, type FilterInput, type FindQueryOptions, type ForeignDocOf, type GeoNearOutput, type GeoNearSpec, type GeoPoint, type GeoPointField, type GracefulTransactionOptions, type GroupByConfig, type GroupSpec, type Grouped, HOOK_NAMES, type HasManyRelation, type HealthReport, type Hook, type HookContext, type HookMap, type HookName, type HooksRegistry, HotCache, type HotCacheMode, type HotCacheOptions, type HotCacheStats, type HotCollectionRef, type HotQueryAccessor, type HotQueryConfig, type HotQueryStats, InFlight, type IndexSpec, type InferDoc, type InferField, InfraError, type InsertInput, type IntegerField, type JoinedFields, type KeysetCursor, LoggerLike, type LongField, type LookupConfig, type LookupJoinedFor, type LookupParams, type LookupSpecFor, MAX_BATCH_OPS, type ManyToManyRelation, type MigrationContext, type MigrationModule, type MongoCapabilities, type MongoCapabilitiesStore, type MongoJsonSchema, type MongoMigrationRunner, type MongoMigrationRunnerOptions, type MongoService, type MongoServiceConfig, type MongoToolkit, type MongoToolkitOptions, type MongoTransactionRunner, type NamedCollection, type NamedCollectionDefinition, type NamedObjectField, type NormalizedPagination, type NullField, type NumberBounds, type NumberField, type ObjectField, type ObjectIdField, type PaginationConfig, type PaginationResult, PipelineBuilder, type PipelineCustomization, type PipelineStage, type PopulateOptions, type Projected, type Projection, QueryBuilder, type QueryBuilderContext, QueryCache, type QueryCacheOptions, type QueryCacheStats, type QueryOptions, type RawField, type RelationDef, type RelationDefBase, type RemoveIndexSignature, type Repository, type RepositoryOptions, type ResolvedQueryOptions, type RetryOptions, type SchemaType, type SearchConfig, type SortKeys, type StringField, type SyncIndexesResult, TRANSIENT_MONGO_ERROR_CODES, type TimestampsByCollection, type Unsetted, type UnwindSpec, type Unwound, type UpdateInput, type UpdateOperators, type ValidRelation, type VersionedUpdateResult, belongsTo, buildKeysetFilter, buildMongoActiveFilter, buildPaginationResult, cacheCollectionKey, canonicalKey, createHotCache, createMongoCapabilitiesStore, createMongoMigrationRunner, createMongoService, createMongoToolkit, createRepository, createResolveCollectionName, decodeCursor, defineCollection, defineCollections, encodeCursor, hasMany, httpStatusForError, isAppError, isDomainError, isInfraError, isMappedMongoError, isMongoDuplicateKeyError, isMongoTransientError, isTransactionUnsupportedError, manyToMany, mapMongoDriverError, mergeMongoActiveFilter, mergeMongoFilters, normalizeMongoUrl, normalizePageLimit, optional, probeMongoCapabilities, runHooks, s, serializeError, stripDocumentId, stripPrimaryKey, toMongoSchema, toMongoValidator, validateDoc, withDefault, withGracefulMongoTransaction, withRetry, withTransaction };
|