@ignex/ninox 0.1.6 → 0.1.7

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/utils.d.ts CHANGED
@@ -16,21 +16,10 @@ export { C as CacheOptions, a as LRU, b as LRUOptions, c as LogFn, d as LogLevel
16
16
  declare const cloneDeep: <T>(value: T) => T;
17
17
 
18
18
  /**
19
- * Permanent change-stream error classification, shared by every `$changeStream`
20
- * watcher (service `CacheInvalidator` + hot-cache `WatchCoordinator`).
19
+ * True when retrying this change-stream error can never succeed.
21
20
  *
22
- * These errors can NEVER be fixed by retrying — retrying only logs the same
23
- * warning every 1–5s forever and (with an un-unref'd timer) keeps the process
24
- * alive, which is wrong for a background watcher whose host server or build
25
- * should own the process lifetime. Watchers must classify them once, log once,
26
- * and stop (disable the watcher / fall back to the standalone ticker).
27
- *
28
- * Deliberately NARROW on purpose: replica errors that ARE transient — e.g.
29
- * "change stream history lost" (resume-token expiry, collection dropped),
30
- * network drops, failovers — must stay in the retry path, where
31
- * invalidate-on-reopen restores correctness. Broad patterns (like `not
32
- * supported` or `ChangeStream`) are rejected here because they can misfire on
33
- * those recoverable errors.
21
+ * @param message - The driver's error message.
22
+ * @returns `true` for a definitive standalone/authorization verdict.
34
23
  */
35
24
  declare const isPermanentWatchError: (message: string) => boolean;
36
25
 
package/dist/utils.js CHANGED
@@ -277,16 +277,26 @@ var sleep = (ms, unref = false) => new Promise((resolve) => {
277
277
  var sleepJittered = (ms, jitterMs = 1e3, unref = false) => sleep(ms + Math.floor(Math.random() * Math.min(ms, jitterMs)), unref);
278
278
 
279
279
  // src/utils/watch-errors.ts
280
- var isPermanentWatchError = (message) => (
281
- // Standalone server: `$changeStream` is not supported at all.
282
- /only supported on replica sets/i.test(message) || // Credentials/authorization problems: the user cannot run `aggregate` on
283
- // this collection/server. Wrong creds, missing roles, or a user created
284
- // without the `read`/`changeStream` privilege will never start working by
285
- // retrying — fail once and stop instead of retrying forever.
286
- /requires authentication|not authorized|unauthorized|authentication failed|Command .* requires authentication/i.test(
287
- message
288
- )
289
- );
280
+ var STANDALONE = /only supported on replica sets|not supported on (?:a )?standalone/i;
281
+ var CREDENTIALS = /requires authentication|not authorized|unauthorized|authentication failed|Command .* requires authentication/i;
282
+ var explainPermanentWatchError = (message) => {
283
+ if (STANDALONE.test(message)) {
284
+ return {
285
+ reason: "standalone",
286
+ headline: "change streams need a replica set",
287
+ hint: "Point the connection at a replica set (a single-node replica set is fine for local dev), or set cacheWatch: false / cache: { ttlMs } to run write-through only."
288
+ };
289
+ }
290
+ if (CREDENTIALS.test(message)) {
291
+ return {
292
+ reason: "credentials",
293
+ headline: "the connection is not authorized for change streams",
294
+ hint: "Change streams need an authenticated user with the read + changeStream privilege (usually granted on the authSource database) \u2014 fix the credentials in .env, then restart."
295
+ };
296
+ }
297
+ return void 0;
298
+ };
299
+ var isPermanentWatchError = (message) => explainPermanentWatchError(message) !== void 0;
290
300
 
291
301
  export { LRU, cloneDeep, createCachedAsyncFactory, createCachedFactory, createConsoleLogger, createNoopLogger, hashString, isPermanentWatchError, sleep, sleepJittered, stableHash, stableStringify, withTimeout };
292
302
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/clone.ts","../src/utils/hash.ts","../src/utils/logger.ts","../src/utils/lru.ts","../src/utils/memoize.ts","../src/utils/timeout.ts","../src/utils/watch-errors.ts"],"names":[],"mappings":";;;AA2BO,IAAM,SAAA,GAAY,CAAI,KAAA,KAAgB;AAC3C,EAAA,IAAI,iBAAiB,IAAA,EAAM,OAAO,IAAI,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA;AAC1D,EAAA,IAAI,iBAAiB,QAAA,EAAU,OAAO,IAAI,QAAA,CAAS,KAAA,CAAM,aAAa,CAAA;AACtE,EAAA,IAAI,iBAAiB,UAAA,EAAY,OAAO,WAAW,UAAA,CAAW,KAAA,CAAM,UAAU,CAAA;AAC9E,EAAA,IAAI,iBAAiB,IAAA,EAAM,OAAO,KAAK,UAAA,CAAW,KAAA,CAAM,UAAU,CAAA;AAClE,EAAA,IAAI,iBAAiB,KAAA,EAAO,OAAO,IAAI,KAAA,CAAM,KAAA,CAAM,SAAS,CAAA;AAC5D,EAAA,IAAI,iBAAiB,MAAA,EAAQ,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,SAAS,CAAA;AAE9D,EAAA,IAAI,KAAA,YAAiB,SAAA;AACnB,IAAA,OAAO,UAAU,QAAA,CAAS,KAAA,CAAM,YAAW,EAAG,KAAA,CAAM,aAAa,CAAA;AACnE,EAAA,IAAI,iBAAiB,MAAA,EAAQ;AAC3B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AACtC,IAAA,OAAO,IAAI,MAAA,CAAO,KAAA,EAAO,KAAA,CAAM,QAAQ,CAAA;AAAA,EACzC;AACA,EAAA,IAAI,KAAA,YAAiB,MAAA,EAAQ,OAAO,IAAI,MAAA,EAAO;AAC/C,EAAA,IAAI,KAAA,YAAiB,MAAA,EAAQ,OAAO,IAAI,MAAA,EAAO;AAC/C,EAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,IAAA,OAAO,IAAI,MAAM,KAAA,CAAM,UAAA,EAAY,UAAU,KAAA,CAAM,GAAG,CAAA,EAAG,KAAA,CAAM,EAAE,CAAA;AAAA,EACnE;AACA,EAAA,IAAI,iBAAiB,IAAA,EAAM;AACzB,IAAA,OAAO,IAAI,IAAA;AAAA,MACT,KAAA,CAAM,IAAA;AAAA,MACN,MAAM,KAAA,KAAU,MAAA,GAAY,SAAA,CAAU,KAAA,CAAM,KAAK,CAAA,GAAI;AAAA,KACvD;AAAA,EACF;AACA,EAAA,IAAI,KAAA,YAAiB,QAAQ,OAAO,IAAI,OAAO,KAAA,CAAM,MAAA,EAAQ,MAAM,KAAK,CAAA;AACxE,EAAA,IAAI,iBAAiB,GAAA,EAAK;AACxB,IAAA,MAAM,GAAA,uBAAU,GAAA,EAAsB;AACtC,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,KAAA,EAAO,GAAA,CAAI,GAAA,CAAI,SAAA,CAAU,CAAC,CAAA,EAAG,SAAA,CAAU,CAAC,CAAC,CAAA;AAC9D,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,IAAI,iBAAiB,GAAA,EAAK;AACxB,IAAA,MAAM,GAAA,uBAAU,GAAA,EAAa;AAC7B,IAAA,KAAA,MAAW,KAAK,KAAA,EAAO,GAAA,CAAI,GAAA,CAAI,SAAA,CAAU,CAAC,CAAC,CAAA;AAC3C,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAO,QAAA,CAAS,KAAK,GAAG,OAAO,MAAA,CAAO,KAAK,KAAK,CAAA;AACpD,EAAA,IAAI,WAAA,CAAY,MAAA,CAAO,KAAK,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAA,GAAO,KAAA;AACb,IAAA,IAAI,gBAAgB,QAAA,EAAU;AAC5B,MAAA,MAAM,KAAA,GAAQ,KAAK,MAAA,CAAO,KAAA,CAAM,KAAK,UAAA,EAAY,IAAA,CAAK,UAAA,GAAa,IAAA,CAAK,UAAU,CAAA;AAClF,MAAA,OAAO,IAAI,SAAS,KAAK,CAAA;AAAA,IAC3B;AACA,IAAA,OAAO,OAAO,IAAA,CAAK,KAAA,KAAU,UAAA,GAAc,IAAA,CAAK,OAAM,GAAU,KAAA;AAAA,EAClE;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS,SAAA,CAAU,IAAI,CAAC,CAAA;AACpE,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,EAAU;AAC/C,IAAA,MAAM,MAA+B,EAAC;AACtC,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAgC,CAAA,EAAG;AACrE,MAAA,GAAA,CAAI,CAAC,CAAA,GAAI,SAAA,CAAU,CAAC,CAAA;AAAA,IACtB;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;;;AC1EA,IAAM,gBAAgB,CAAC,CAAA,KACrB,OAAO,CAAA,KAAM,YACb,CAAA,KAAM,IAAA,IACN,CAAC,KAAA,CAAM,QAAQ,CAAC,CAAA,IAChB,EAAE,CAAA,YAAa,IAAA,CAAA,IACf,EAAE,CAAA,YAAa,MAAA,CAAA;AAAA;AAAA;AAIf,OAAQ,EAAgC,WAAA,KAAgB,UAAA;AAE1D,IAAM,WAAA,GAAc,CAAC,CAAA,KAA0B;AAC7C,EAAA,IAAI,CAAA,KAAM,MAAA,IAAa,CAAA,KAAM,IAAA,EAAM,OAAO,IAAA;AAC1C,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,EAAE,QAAA,EAAS;AAC7C,EAAA,IAAI,aAAa,IAAA,EAAM,OAAO,CAAA,KAAA,EAAQ,CAAA,CAAE,SAAS,CAAA,CAAA;AACjD,EAAA,IAAI,OAAO,MAAM,QAAA,EAAU;AAEzB,IAAA,MAAM,IAAA,GAAO,CAAA;AACb,IAAA,IAAI,OAAO,KAAK,WAAA,KAAgB,UAAA,SAAmB,CAAA,IAAA,EAAO,IAAA,CAAK,aAAa,CAAA,CAAA;AAC5E,IAAA,IAAI,IAAA,YAAgB,QAAQ,OAAO,CAAA,GAAA,EAAM,KAAK,MAAM,CAAA,CAAA,EAAI,KAAK,KAAK,CAAA,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,CAAA;AACT,CAAA;AAMO,IAAM,eAAA,GAAkB,CAAC,KAAA,KAA2B;AACzD,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAgE;AAC7E,IAAA,IAAI,MAAM,OAAA,CAAQ,CAAC,GAAG,OAAO,CAAA,CAAE,IAAI,KAAK,CAAA;AACxC,IAAA,IAAI,aAAA,CAAc,CAAC,CAAA,EAAG;AACpB,MAAA,MAAM,MAA+B,EAAC;AACtC,MAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,CAAE,IAAA,EAAK,EAAG,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA,CAAM,CAAA,CAAE,GAAG,CAAC,CAAA;AAChE,MAAA,OAAO,GAAA;AAAA,IACT;AACA,IAAA,OAAO,YAAY,CAAC,CAAA;AAAA,EACtB,CAAA;AACA,EAAA,OAAO,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,KAAK,CAAC,CAAA;AACpC;AASO,IAAM,UAAA,GAAa,CAAC,KAAA,KAA0B;AACnD,EAAA,IAAI,EAAA,GAAK,IAAA;AACT,EAAA,IAAI,EAAA,GAAK,UAAA;AACT,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,CAAA,GAAI,KAAA,CAAM,UAAA,CAAW,CAAC,CAAA;AAC5B,IAAA,EAAA,GAAA,CAAO,EAAA,IAAM,CAAA,IAAK,EAAA,GAAK,CAAA,GAAK,CAAA;AAC5B,IAAA,EAAA,GAAA,CAAO,EAAA,GAAK,KAAK,QAAA,GAAc,CAAA;AAE/B,IAAA,EAAA,GAAM,EAAA,IAAM,IAAM,EAAA,KAAO,EAAA;AAAA,EAC3B;AACA,EAAA,OAAO,CAAA,EAAA,CAAI,EAAA,KAAO,CAAA,EAAG,QAAA,CAAS,EAAE,CAAC,CAAA,EAAA,CAAI,EAAA,KAAO,CAAA,EAAG,QAAA,CAAS,EAAE,CAAC,CAAA,CAAA;AAC7D;AAGO,IAAM,aAAa,CAAC,KAAA,KAA2B,UAAA,CAAW,eAAA,CAAgB,KAAK,CAAC;;;ACpDhF,IAAM,mBAAmB,OAAmB;AAAA,EACjD,OAAO,MAAM;AAAA,EAAC,CAAA;AAAA,EACd,MAAM,MAAM;AAAA,EAAC,CAAA;AAAA,EACb,MAAM,MAAM;AAAA,EAAC,CAAA;AAAA,EACb,OAAO,MAAM;AAAA,EAAC;AAChB,CAAA;AAKA,IAAM,UAAA,GAAuC,EAAE,KAAA,EAAO,EAAA,EAAI,MAAM,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI,KAAA,EAAO,EAAA,EAAG;AAExF,IAAM,MAAA,GAAS,CAAC,GAAA,KAAyC;AACvD,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,CAAK,UAAU,GAAG,CAAA;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,OAAO,GAAG,CAAA;AAAA,EACnB;AACF,CAAA;AAKO,IAAM,mBAAA,GAAsB,CAAC,KAAA,GAAkB,MAAA,KAAuB;AAC3E,EAAA,MAAM,SAAA,GAAY,WAAW,KAAK,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,CAAC,MAAA,KAAgD;AAC5D,IAAA,MAAM,IAAA,GAAO,WAAW,MAAM,CAAA;AAC9B,IAAA,MAAM,EAAA,GAAY,CAAC,CAAA,EAAY,CAAA,KAAe;AAC5C,MAAA,IAAI,OAAO,SAAA,EAAW;AACtB,MAAA,MAAM,GAAA,GAAM,OAAO,CAAA,KAAM,QAAA,GAAW,MAAA,GAAa,CAAA;AACjD,MAAA,MAAM,GAAA,GAAM,OAAO,CAAA,KAAM,QAAA,GAAW,CAAA,GAAI,CAAA;AACxC,MAAA,MAAM,MAAA,GAAS,GAAA,KAAQ,MAAA,GAAY,MAAA,CAAO,GAAG,CAAA,GAAI,EAAA;AACjD,MAAA,IAAI,OAAO,MAAA,EAAQ;AAEjB,QAAA,OAAA,CAAQ,MAAM,EAAE,CAAA,CAAA,EAAI,MAAA,CAAO,aAAa,CAAA,CAAA,CAAA,EAAK,QAAQ,GAAG,CAAA;AAAA,MAC1D,CAAA,MAAO;AAEL,QAAA,OAAA,CAAQ,MAAM,EAAE,CAAA,CAAA,EAAI,MAAA,CAAO,aAAa,CAAA,CAAA,CAAA,EAAM,OAAO,MAAiB,CAAA;AAAA,MACxE;AAAA,IACF,CAAA;AACA,IAAA,OAAO,EAAA;AAAA,EACT,CAAA;AACA,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,KAAK,OAAO,CAAA;AAAA,IACnB,IAAA,EAAM,KAAK,MAAM,CAAA;AAAA,IACjB,IAAA,EAAM,KAAK,MAAM,CAAA;AAAA,IACjB,KAAA,EAAO,KAAK,OAAO;AAAA,GACrB;AACF;;;ACpDO,IAAM,MAAN,MAAgB;AAAA,EACZ,GAAA;AAAA,EACD,GAAA,uBAAU,GAAA,EAAU;AAAA,EACpB,OAAA;AAAA;AAAA,EAER,SAAA,GAAY,CAAA;AAAA,EAEZ,YAAY,OAAA,EAAqB;AAC/B,IAAA,IAAI,QAAQ,GAAA,IAAO,CAAA,EAAG,MAAM,IAAI,MAAM,qBAAqB,CAAA;AAC3D,IAAA,IAAA,CAAK,MAAM,OAAA,CAAQ,GAAA;AAEnB,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AAAA,EACzB;AAAA,EAEA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,GAAA,CAAI,IAAA;AAAA,EAClB;AAAA,EAEA,IAAI,GAAA,EAAuB;AACzB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AAC9B,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,MAAA;AAEhC,IAAA,IAAA,CAAK,GAAA,CAAI,OAAO,GAAG,CAAA;AACnB,IAAA,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACvB,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,KAAK,GAAA,EAAuB;AAC1B,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AAAA,EACzB;AAAA,EAEA,IAAI,GAAA,EAAiB;AACnB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AAAA,EACzB;AAAA,EAEA,GAAA,CAAI,KAAQ,KAAA,EAAgB;AAC1B,IAAA,IAAI,IAAA,CAAK,IAAI,GAAA,CAAI,GAAG,GAAG,IAAA,CAAK,GAAA,CAAI,OAAO,GAAG,CAAA;AAC1C,IAAA,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACvB,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAA,GAAO,IAAA,CAAK,GAAA,EAAK;AAC5B,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,GAAO,IAAA,EAAK;AAClC,MAAA,IAAI,IAAA,CAAK,SAAS,IAAA,EAAM;AACtB,QAAA,MAAM,aAAa,IAAA,CAAK,KAAA;AACxB,QAAA,MAAM,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,UAAU,CAAA;AAC5C,QAAA,IAAA,CAAK,GAAA,CAAI,OAAO,UAAU,CAAA;AAC1B,QAAA,IAAA,CAAK,SAAA,EAAA;AACL,QAAA,IAAI;AAEF,UAAA,IAAA,CAAK,OAAA,GAAU,YAAY,YAAY,CAAA;AAAA,QACzC,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,OAAO,GAAA,EAAiB;AACtB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA;AAAA,EAC5B;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,IAAI,KAAA,EAAM;AAAA,EACjB;AAAA,EAEA,IAAA,GAA4B;AAC1B,IAAA,OAAO,IAAA,CAAK,IAAI,IAAA,EAAK;AAAA,EACvB;AAAA,EAEA,OAAA,GAAoC;AAClC,IAAA,OAAO,IAAA,CAAK,IAAI,OAAA,EAAQ;AAAA,EAC1B;AAAA,EAEA,CAAC,MAAA,CAAO,QAAQ,CAAA,GAA8B;AAC5C,IAAA,OAAO,IAAA,CAAK,IAAI,OAAA,EAAQ;AAAA,EAC1B;AACF;;;ACpEA,IAAM,SAAA,GAAY,CAAO,OAAA,KAAwC;AAC/D,EAAA,IAAI,WAAW,CAAA,EAAG;AAChB,IAAA,MAAM,GAAA,uBAAU,GAAA,EAAU;AAC1B,IAAA,OAAO;AAAA,MACL,GAAA,EAAK,CAAC,CAAA,KAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,MACrB,GAAA,EAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACb,QAAA,GAAA,CAAI,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,MACd,CAAA;AAAA,MACA,GAAA,EAAK,CAAC,CAAA,KAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,MACrB,MAAA,EAAQ,CAAC,CAAA,KAAM;AACb,QAAA,GAAA,CAAI,OAAO,CAAC,CAAA;AAAA,MACd;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,IAAI,GAAA,CAAU,EAAE,GAAA,EAAK,SAAS,CAAA;AACvC,CAAA;AAMO,IAAM,mBAAA,GAAsB,CACjC,OAAA,EACA,OAAA,GAAwB,EAAC,KACL;AACpB,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,IAAW,GAAA;AACnC,EAAA,MAAM,KAAA,GAAQ,UAAgB,OAAO,CAAA;AACrC,EAAA,OAAO,CAAC,GAAA,KAAc;AACpB,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC9B,IAAA,IAAI,QAAA,KAAa,QAAW,OAAO,QAAA;AACnC,IAAA,MAAM,KAAA,GAAQ,QAAQ,GAAG,CAAA;AACzB,IAAA,KAAA,CAAM,GAAA,CAAI,KAAK,KAAK,CAAA;AACpB,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF;AAOO,IAAM,wBAAA,GAA2B,CACtC,OAAA,EACA,OAAA,GAAwB,EAAC,KACI;AAC7B,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,IAAW,GAAA;AACnC,EAAA,MAAM,KAAA,GAAQ,UAAyB,OAAO,CAAA;AAC9C,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAmB;AAExC,EAAA,OAAO,CAAC,GAAA,KAAuB;AAC7B,IAAA,MAAM,QAAA,GAAW,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA;AACjC,IAAA,IAAI,UAAU,OAAO,QAAA;AAErB,IAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC5B,IAAA,IAAI,QAAQ,OAAO,MAAA;AAEnB,IAAA,MAAM,OAAA,GAAU,QAAQ,GAAG,CAAA;AAC3B,IAAA,QAAA,CAAS,GAAA,CAAI,KAAK,OAAO,CAAA;AACzB,IAAA,KAAA,CAAM,GAAA,CAAI,KAAK,OAAO,CAAA;AACtB,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,MAAM;AACJ,QAAA,QAAA,CAAS,OAAO,GAAG,CAAA;AAAA,MACrB,CAAA;AAAA,MACA,MAAM;AACJ,QAAA,QAAA,CAAS,OAAO,GAAG,CAAA;AAGnB,QAAA,IAAI,MAAM,GAAA,CAAI,GAAG,MAAM,OAAA,EAAS,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,MAClD;AAAA,KACF;AACA,IAAA,OAAO,OAAA;AAAA,EACT,CAAA;AACF;;;ACzFO,IAAM,WAAA,GAAc,CACzB,OAAA,EACA,SAAA,EACA,KAAA,KACe;AACf,EAAA,IAAI,SAAA,IAAa,GAAG,OAAO,OAAA;AAC3B,EAAA,IAAI,KAAA;AACJ,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AAChD,IAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,SAAS,CAAA,IAAA,EAAO,KAAK,EAAE,CAAC,CAAA;AAAA,IACxE,GAAG,SAAS,CAAA;AAAA,EACd,CAAC,CAAA;AACD,EAAA,OAAO,OAAA,CAAQ,KAAK,CAAC,OAAA,EAAS,OAAO,CAAC,CAAA,CAAE,QAAQ,MAAM;AACpD,IAAA,IAAI,KAAA,KAAU,MAAA,EAAW,YAAA,CAAa,KAAK,CAAA;AAAA,EAC7C,CAAC,CAAA;AACH;AAUO,IAAM,KAAA,GAAQ,CAAC,EAAA,EAAY,KAAA,GAAQ,UACxC,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AACvB,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,OAAA,EAAS,EAAE,CAAA;AACpC,EAAA,IAAI,KAAA,QAAa,KAAA,IAAQ;AAC3B,CAAC;AAOI,IAAM,aAAA,GAAgB,CAAC,EAAA,EAAY,QAAA,GAAW,KAAM,KAAA,GAAQ,KAAA,KACjE,MAAM,EAAA,GAAK,IAAA,CAAK,MAAM,IAAA,CAAK,MAAA,KAAW,IAAA,CAAK,GAAA,CAAI,IAAI,QAAQ,CAAC,GAAG,KAAK;;;ACxB/D,IAAM,wBAAwB,CAAC,OAAA;AAAA;AAAA,EAEpC,iCAAA,CAAkC,KAAK,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA,EAK9C,+GAAA,CAAgH,IAAA;AAAA,IAC9G;AAAA;AACF","file":"utils.js","sourcesContent":["import {\n Binary,\n Code,\n DBRef,\n Decimal128,\n Double,\n Int32,\n Long,\n MaxKey,\n MinKey,\n ObjectId,\n Timestamp,\n} from 'mongodb';\n\n/**\n * Deep-clone a value while preserving the types `JSON.parse(JSON.stringify())`\n * would corrupt or flatten: `Date`, `ObjectId`, `RegExp`, the numeric/binary\n * BSON wrappers (`Decimal128`, `Long`, `Int32`, `Double`, `Timestamp`,\n * `Binary`, `MinKey`, `MaxKey`, `Code`, `DBRef`), and the JS containers\n * `Map` / `Set` / `Buffer` / typed arrays — without this, cached money fields\n * (`Decimal128`) or int64 values (`Long`) would clone into `{}`, and a\n * `Map` (no enumerable own properties) or `Buffer` would silently lose both\n * type and data in the generic-object fallback. Arrays and plain objects are\n * copied recursively; primitives pass through.\n * Used by the optional `clone` mode on `QueryCache` / `HotCache` so a caller\n * can't mutate a cached result and poison the shared cache.\n */\nexport const cloneDeep = <T>(value: T): T => {\n if (value instanceof Date) return new Date(value.getTime()) as T;\n if (value instanceof ObjectId) return new ObjectId(value.toHexString()) as T;\n if (value instanceof Decimal128) return Decimal128.fromString(value.toString()) as T;\n if (value instanceof Long) return Long.fromString(value.toString()) as T;\n if (value instanceof Int32) return new Int32(value.valueOf()) as T;\n if (value instanceof Double) return new Double(value.valueOf()) as T;\n // fromBits round-trips exactly; fromNumber loses low bits above 2^53.\n if (value instanceof Timestamp)\n return Timestamp.fromBits(value.getLowBits(), value.getHighBits()) as T;\n if (value instanceof Binary) {\n const bytes = Buffer.from(value.buffer);\n return new Binary(bytes, value.sub_type) as T;\n }\n if (value instanceof MinKey) return new MinKey() as T;\n if (value instanceof MaxKey) return new MaxKey() as T;\n if (value instanceof DBRef) {\n return new DBRef(value.collection, cloneDeep(value.oid), value.db) as T;\n }\n if (value instanceof Code) {\n return new Code(\n value.code,\n value.scope !== undefined ? cloneDeep(value.scope) : undefined,\n ) as T;\n }\n if (value instanceof RegExp) return new RegExp(value.source, value.flags) as T;\n if (value instanceof Map) {\n const out = new Map<unknown, unknown>();\n for (const [k, v] of value) out.set(cloneDeep(k), cloneDeep(v));\n return out as T;\n }\n if (value instanceof Set) {\n const out = new Set<unknown>();\n for (const v of value) out.add(cloneDeep(v));\n return out as T;\n }\n // Buffer first (a Uint8Array subclass); then any other typed array view.\n if (Buffer.isBuffer(value)) return Buffer.from(value) as T;\n if (ArrayBuffer.isView(value)) {\n const view = value as unknown as ArrayBufferView & { slice?: () => unknown };\n if (view instanceof DataView) {\n const bytes = view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);\n return new DataView(bytes) as T;\n }\n return typeof view.slice === 'function' ? (view.slice() as T) : value;\n }\n if (Array.isArray(value)) return value.map((item) => cloneDeep(item)) as T;\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = cloneDeep(v);\n }\n return out as T;\n }\n return value;\n};\n","/**\n * Deterministic hashing utilities used for cache keys / in-flight dedup keys.\n * `stableStringify` produces an order-insensitive serialization so two logically\n * identical filters yield the same hash regardless of key insertion order.\n */\n\ntype Primitive = string | number | boolean | bigint | null | undefined;\n\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' &&\n v !== null &&\n !Array.isArray(v) &&\n !(v instanceof Date) &&\n !(v instanceof RegExp) &&\n // Mongo driver ObjectId (and other BSON values with a toHexString marker)\n // must fall through to `toPrimitive` — treating them as plain objects\n // would serialize every ObjectId as `{}` and collapse distinct values.\n typeof (v as { toHexString?: unknown }).toHexString !== 'function';\n\nconst toPrimitive = (v: unknown): Primitive => {\n if (v === undefined || v === null) return null;\n if (typeof v === 'bigint') return v.toString();\n if (v instanceof Date) return `date:${v.getTime()}`;\n if (typeof v === 'object') {\n // Mongo driver ObjectId has toHexString(); RegExp serializes as a string.\n const anyV = v as { toHexString?: () => string; source?: string; flags?: string };\n if (typeof anyV.toHexString === 'function') return `oid:${anyV.toHexString()}`;\n if (anyV instanceof RegExp) return `re:${anyV.source}/${anyV.flags}`;\n }\n return v as Primitive;\n};\n\n/**\n * Stable stringify: recursively sorts object keys, coerces Date/ObjectId/RegExp\n * into primitive markers. Result is deterministic for a given logical value.\n */\nexport const stableStringify = (value: unknown): string => {\n const visit = (v: unknown): Primitive | Record<string, unknown> | unknown[] => {\n if (Array.isArray(v)) return v.map(visit);\n if (isPlainObject(v)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(v).sort()) out[key] = visit(v[key]);\n return out;\n }\n return toPrimitive(v);\n };\n return JSON.stringify(visit(value));\n};\n\n/**\n * Dual-round 32-bit digest → ~64 bits of collision resistance, base-36.\n * Round 1 is djb2; round 2 mixes FNV-1a over the same input so two distinct\n * strings colliding in one round almost never collide in both. Cache keys\n * store ONLY this digest, so collision resistance matters: a hit must never\n * be able to serve another query's rows.\n */\nexport const hashString = (input: string): string => {\n let h1 = 5381;\n let h2 = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n const c = input.charCodeAt(i);\n h1 = ((h1 << 5) + h1 + c) | 0;\n h2 = ((h2 ^ c) * 0x01000193) | 0;\n // Extra diffusion so low-entropy inputs still spread across rounds.\n h2 = (h2 << 3) | (h2 >>> 29);\n }\n return `${(h1 >>> 0).toString(36)}${(h2 >>> 0).toString(36)}`;\n};\n\n/** Convenience: hash any value through a stable stringification. */\nexport const stableHash = (value: unknown): string => hashString(stableStringify(value));\n","/**\n * Logger abstraction. The ORM never logs directly — it accepts any logger that\n * satisfies `LoggerLike` (pino, a console wrapper, a test stub, ...). This keeps\n * the package free of logging dependencies while remaining observability-agnostic.\n */\nexport interface LogFn {\n (obj: Record<string, unknown>, msg?: string): void;\n (msg: string): void;\n}\n\nexport interface LoggerLike {\n debug: LogFn;\n info: LogFn;\n warn: LogFn;\n error: LogFn;\n}\n\n/** A logger that swallows everything. Useful in tests / perf runs. */\nexport const createNoopLogger = (): LoggerLike => ({\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n});\n\nconst LEVELS = ['debug', 'info', 'warn', 'error'] as const;\nexport type LogLevel = (typeof LEVELS)[number];\n\nconst LEVEL_RANK: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };\n\nconst fmtObj = (obj: Record<string, unknown>): string => {\n try {\n return JSON.stringify(obj);\n } catch {\n return String(obj);\n }\n};\n\n/**\n * A tiny console logger matching the `LoggerLike` shape. `level` filters output.\n */\nexport const createConsoleLogger = (level: LogLevel = 'info'): LoggerLike => {\n const threshold = LEVEL_RANK[level];\n const make = (method: 'debug' | 'info' | 'warn' | 'error') => {\n const rank = LEVEL_RANK[method];\n const fn: LogFn = (a: unknown, b?: string) => {\n if (rank < threshold) return;\n const obj = typeof a === 'string' ? undefined : (a as Record<string, unknown>);\n const msg = typeof a === 'string' ? a : b;\n const prefix = obj !== undefined ? fmtObj(obj) : '';\n if (msg && prefix) {\n // eslint-disable-next-line no-console\n console[method](`[${method.toUpperCase()}]`, prefix, msg);\n } else {\n // eslint-disable-next-line no-console\n console[method](`[${method.toUpperCase()}]`, (msg ?? prefix) as string);\n }\n };\n return fn;\n };\n return {\n debug: make('debug'),\n info: make('info'),\n warn: make('warn'),\n error: make('error'),\n };\n};\n","/**\n * Minimal dependency-free LRU cache backed by a Map (recency = insertion order).\n * get() re-inserts the entry so the most-recently-used item is always last.\n */\nexport interface LRUOptions {\n /** Maximum number of entries before the least-recently-used item is evicted. Must be > 0. */\n max: number;\n /**\n * Called when an entry is evicted by capacity pressure (NOT on explicit\n * delete/clear). Lets owners release side structures keyed by the entry.\n */\n onEvict?: (key: never, value: never) => void;\n}\n\nexport class LRU<K, V> {\n readonly max: number;\n private map = new Map<K, V>();\n private onEvict: ((key: K, value: V) => void) | undefined;\n /** Number of entries evicted to make room for new ones (capacity evictions only). */\n evictions = 0;\n\n constructor(options: LRUOptions) {\n if (options.max <= 0) throw new Error('LRU max must be > 0');\n this.max = options.max;\n // Contravariance-safe: `(key: never) => void` accepts any handler shape.\n this.onEvict = options.onEvict as ((key: K, value: V) => void) | undefined;\n }\n\n get size(): number {\n return this.map.size;\n }\n\n get(key: K): V | undefined {\n const value = this.map.get(key);\n if (value === undefined) return undefined;\n // Refresh recency.\n this.map.delete(key);\n this.map.set(key, value);\n return value;\n }\n\n peek(key: K): V | undefined {\n return this.map.get(key);\n }\n\n has(key: K): boolean {\n return this.map.has(key);\n }\n\n set(key: K, value: V): this {\n if (this.map.has(key)) this.map.delete(key);\n this.map.set(key, value);\n if (this.map.size > this.max) {\n const next = this.map.keys().next();\n if (next.done !== true) {\n const evictedKey = next.value;\n const evictedValue = this.map.get(evictedKey) as V;\n this.map.delete(evictedKey);\n this.evictions++;\n try {\n // Eviction hooks must never break the cache write path.\n this.onEvict?.(evictedKey, evictedValue);\n } catch {\n /* ignore */\n }\n }\n }\n return this;\n }\n\n delete(key: K): boolean {\n return this.map.delete(key);\n }\n\n clear(): void {\n this.map.clear();\n }\n\n keys(): IterableIterator<K> {\n return this.map.keys();\n }\n\n entries(): IterableIterator<[K, V]> {\n return this.map.entries();\n }\n\n [Symbol.iterator](): IterableIterator<[K, V]> {\n return this.map.entries();\n }\n}\n","/**\n * Generic keyed factory caches (memoization). Unlike the ORM's `QueryCache`\n * (per-collection, TTL, invalidation-aware) and `HotCache` (read-through,\n * event/timer-refreshed), these are dependency-free building blocks for\n * memoizing any expensive computation by key — including in-flight dedup so a\n * keyed factory never runs twice concurrently.\n */\nimport { LRU } from './lru.ts';\n\nexport interface CacheOptions {\n /** Max cached entries. `0` or negative → unbounded Map (default 100). */\n maxSize?: number;\n}\n\ntype BackingStore<K, V> = {\n get(k: K): V | undefined;\n set(k: K, v: V): void;\n has(k: K): boolean;\n delete(k: K): void;\n};\n\nconst makeStore = <K, V>(maxSize: number): BackingStore<K, V> => {\n if (maxSize <= 0) {\n const map = new Map<K, V>();\n return {\n get: (k) => map.get(k),\n set: (k, v) => {\n map.set(k, v);\n },\n has: (k) => map.has(k),\n delete: (k) => {\n map.delete(k);\n },\n };\n }\n return new LRU<K, V>({ max: maxSize });\n};\n\n/**\n * Synchronous keyed factory cache: one value per key, LRU-bounded.\n * Used for cheap singleton factories (connection pools, resolvers, ...).\n */\nexport const createCachedFactory = <K, V>(\n factory: (key: K) => V,\n options: CacheOptions = {},\n): ((key: K) => V) => {\n const maxSize = options.maxSize ?? 100;\n const cache = makeStore<K, V>(maxSize);\n return (key: K): V => {\n const existing = cache.get(key);\n if (existing !== undefined) return existing;\n const value = factory(key);\n cache.set(key, value);\n return value;\n };\n};\n\n/**\n * Async keyed factory cache with **in-flight deduplication**: concurrent calls\n * with the same key share a single Promise so the factory runs only once.\n * On settle (success or failure) the in-flight entry is removed.\n */\nexport const createCachedAsyncFactory = <K, V>(\n factory: (key: K) => Promise<V>,\n options: CacheOptions = {},\n): ((key: K) => Promise<V>) => {\n const maxSize = options.maxSize ?? 100;\n const cache = makeStore<K, Promise<V>>(maxSize);\n const inFlight = new Map<K, Promise<V>>();\n\n return (key: K): Promise<V> => {\n const inflight = inFlight.get(key);\n if (inflight) return inflight;\n\n const cached = cache.get(key);\n if (cached) return cached;\n\n const promise = factory(key);\n inFlight.set(key, promise);\n cache.set(key, promise);\n promise.then(\n () => {\n inFlight.delete(key);\n },\n () => {\n inFlight.delete(key);\n // Never cache a failure — evict (identity-guarded) so the next call\n // retries instead of re-surfacing the same rejected promise.\n if (cache.get(key) === promise) cache.delete(key);\n },\n );\n return promise;\n };\n};\n","/**\n * Race a promise against a timeout. A `timeoutMs` <= 0 disables the timer and\n * returns the original promise untouched (zero-overhead fast path).\n */\nexport const withTimeout = <T>(\n promise: Promise<T>,\n timeoutMs: number,\n label: string,\n): Promise<T> => {\n if (timeoutMs <= 0) return promise;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`Operation timed out after ${timeoutMs}ms: ${label}`));\n }, timeoutMs);\n });\n return Promise.race([promise, timeout]).finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n });\n};\n\n/**\n * Sleep helper used by retry backoff.\n *\n * `unref` (default false) detaches the timer from the event loop, so a\n * background retry loop never keeps the process alive on its own (the\n * consumer's server/sockets do that). Watcher backoff passes `unref: true`;\n * awaited query retries keep the default so a pending call is never dropped.\n */\nexport const sleep = (ms: number, unref = false): Promise<void> =>\n new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n if (unref) timer.unref?.();\n });\n\n/**\n * Sleep for `ms` plus random jitter (0..jitterMs, capped at `ms`) so retry\n * loops don't resynchronize into a thundering herd after a replica failover.\n * Accepts the same `unref` option as {@link sleep}.\n */\nexport const sleepJittered = (ms: number, jitterMs = 1000, unref = false): Promise<void> =>\n sleep(ms + Math.floor(Math.random() * Math.min(ms, jitterMs)), unref);\n","/**\n * Permanent change-stream error classification, shared by every `$changeStream`\n * watcher (service `CacheInvalidator` + hot-cache `WatchCoordinator`).\n *\n * These errors can NEVER be fixed by retrying — retrying only logs the same\n * warning every 1–5s forever and (with an un-unref'd timer) keeps the process\n * alive, which is wrong for a background watcher whose host server or build\n * should own the process lifetime. Watchers must classify them once, log once,\n * and stop (disable the watcher / fall back to the standalone ticker).\n *\n * Deliberately NARROW on purpose: replica errors that ARE transient — e.g.\n * \"change stream history lost\" (resume-token expiry, collection dropped),\n * network drops, failovers — must stay in the retry path, where\n * invalidate-on-reopen restores correctness. Broad patterns (like `not\n * supported` or `ChangeStream`) are rejected here because they can misfire on\n * those recoverable errors.\n */\nexport const isPermanentWatchError = (message: string): boolean =>\n // Standalone server: `$changeStream` is not supported at all.\n /only supported on replica sets/i.test(message) ||\n // Credentials/authorization problems: the user cannot run `aggregate` on\n // this collection/server. Wrong creds, missing roles, or a user created\n // without the `read`/`changeStream` privilege will never start working by\n // retrying — fail once and stop instead of retrying forever.\n /requires authentication|not authorized|unauthorized|authentication failed|Command .* requires authentication/i.test(\n message,\n );\n"]}
1
+ {"version":3,"sources":["../src/utils/clone.ts","../src/utils/hash.ts","../src/utils/logger.ts","../src/utils/lru.ts","../src/utils/memoize.ts","../src/utils/timeout.ts","../src/utils/watch-errors.ts"],"names":[],"mappings":"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","file":"utils.js","sourcesContent":["import {\n Binary,\n Code,\n DBRef,\n Decimal128,\n Double,\n Int32,\n Long,\n MaxKey,\n MinKey,\n ObjectId,\n Timestamp,\n} from 'mongodb';\n\n/**\n * Deep-clone a value while preserving the types `JSON.parse(JSON.stringify())`\n * would corrupt or flatten: `Date`, `ObjectId`, `RegExp`, the numeric/binary\n * BSON wrappers (`Decimal128`, `Long`, `Int32`, `Double`, `Timestamp`,\n * `Binary`, `MinKey`, `MaxKey`, `Code`, `DBRef`), and the JS containers\n * `Map` / `Set` / `Buffer` / typed arrays — without this, cached money fields\n * (`Decimal128`) or int64 values (`Long`) would clone into `{}`, and a\n * `Map` (no enumerable own properties) or `Buffer` would silently lose both\n * type and data in the generic-object fallback. Arrays and plain objects are\n * copied recursively; primitives pass through.\n * Used by the optional `clone` mode on `QueryCache` / `HotCache` so a caller\n * can't mutate a cached result and poison the shared cache.\n */\nexport const cloneDeep = <T>(value: T): T => {\n if (value instanceof Date) return new Date(value.getTime()) as T;\n if (value instanceof ObjectId) return new ObjectId(value.toHexString()) as T;\n if (value instanceof Decimal128) return Decimal128.fromString(value.toString()) as T;\n if (value instanceof Long) return Long.fromString(value.toString()) as T;\n if (value instanceof Int32) return new Int32(value.valueOf()) as T;\n if (value instanceof Double) return new Double(value.valueOf()) as T;\n // fromBits round-trips exactly; fromNumber loses low bits above 2^53.\n if (value instanceof Timestamp)\n return Timestamp.fromBits(value.getLowBits(), value.getHighBits()) as T;\n if (value instanceof Binary) {\n const bytes = Buffer.from(value.buffer);\n return new Binary(bytes, value.sub_type) as T;\n }\n if (value instanceof MinKey) return new MinKey() as T;\n if (value instanceof MaxKey) return new MaxKey() as T;\n if (value instanceof DBRef) {\n return new DBRef(value.collection, cloneDeep(value.oid), value.db) as T;\n }\n if (value instanceof Code) {\n return new Code(\n value.code,\n value.scope !== undefined ? cloneDeep(value.scope) : undefined,\n ) as T;\n }\n if (value instanceof RegExp) return new RegExp(value.source, value.flags) as T;\n if (value instanceof Map) {\n const out = new Map<unknown, unknown>();\n for (const [k, v] of value) out.set(cloneDeep(k), cloneDeep(v));\n return out as T;\n }\n if (value instanceof Set) {\n const out = new Set<unknown>();\n for (const v of value) out.add(cloneDeep(v));\n return out as T;\n }\n // Buffer first (a Uint8Array subclass); then any other typed array view.\n if (Buffer.isBuffer(value)) return Buffer.from(value) as T;\n if (ArrayBuffer.isView(value)) {\n const view = value as unknown as ArrayBufferView & { slice?: () => unknown };\n if (view instanceof DataView) {\n const bytes = view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);\n return new DataView(bytes) as T;\n }\n return typeof view.slice === 'function' ? (view.slice() as T) : value;\n }\n if (Array.isArray(value)) return value.map((item) => cloneDeep(item)) as T;\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = cloneDeep(v);\n }\n return out as T;\n }\n return value;\n};\n","/**\n * Deterministic hashing utilities used for cache keys / in-flight dedup keys.\n * `stableStringify` produces an order-insensitive serialization so two logically\n * identical filters yield the same hash regardless of key insertion order.\n */\n\ntype Primitive = string | number | boolean | bigint | null | undefined;\n\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' &&\n v !== null &&\n !Array.isArray(v) &&\n !(v instanceof Date) &&\n !(v instanceof RegExp) &&\n // Mongo driver ObjectId (and other BSON values with a toHexString marker)\n // must fall through to `toPrimitive` — treating them as plain objects\n // would serialize every ObjectId as `{}` and collapse distinct values.\n typeof (v as { toHexString?: unknown }).toHexString !== 'function';\n\nconst toPrimitive = (v: unknown): Primitive => {\n if (v === undefined || v === null) return null;\n if (typeof v === 'bigint') return v.toString();\n if (v instanceof Date) return `date:${v.getTime()}`;\n if (typeof v === 'object') {\n // Mongo driver ObjectId has toHexString(); RegExp serializes as a string.\n const anyV = v as { toHexString?: () => string; source?: string; flags?: string };\n if (typeof anyV.toHexString === 'function') return `oid:${anyV.toHexString()}`;\n if (anyV instanceof RegExp) return `re:${anyV.source}/${anyV.flags}`;\n }\n return v as Primitive;\n};\n\n/**\n * Stable stringify: recursively sorts object keys, coerces Date/ObjectId/RegExp\n * into primitive markers. Result is deterministic for a given logical value.\n */\nexport const stableStringify = (value: unknown): string => {\n const visit = (v: unknown): Primitive | Record<string, unknown> | unknown[] => {\n if (Array.isArray(v)) return v.map(visit);\n if (isPlainObject(v)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(v).sort()) out[key] = visit(v[key]);\n return out;\n }\n return toPrimitive(v);\n };\n return JSON.stringify(visit(value));\n};\n\n/**\n * Dual-round 32-bit digest → ~64 bits of collision resistance, base-36.\n * Round 1 is djb2; round 2 mixes FNV-1a over the same input so two distinct\n * strings colliding in one round almost never collide in both. Cache keys\n * store ONLY this digest, so collision resistance matters: a hit must never\n * be able to serve another query's rows.\n */\nexport const hashString = (input: string): string => {\n let h1 = 5381;\n let h2 = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n const c = input.charCodeAt(i);\n h1 = ((h1 << 5) + h1 + c) | 0;\n h2 = ((h2 ^ c) * 0x01000193) | 0;\n // Extra diffusion so low-entropy inputs still spread across rounds.\n h2 = (h2 << 3) | (h2 >>> 29);\n }\n return `${(h1 >>> 0).toString(36)}${(h2 >>> 0).toString(36)}`;\n};\n\n/** Convenience: hash any value through a stable stringification. */\nexport const stableHash = (value: unknown): string => hashString(stableStringify(value));\n","/**\n * Logger abstraction. The ORM never logs directly — it accepts any logger that\n * satisfies `LoggerLike` (pino, a console wrapper, a test stub, ...). This keeps\n * the package free of logging dependencies while remaining observability-agnostic.\n */\nexport interface LogFn {\n (obj: Record<string, unknown>, msg?: string): void;\n (msg: string): void;\n}\n\nexport interface LoggerLike {\n debug: LogFn;\n info: LogFn;\n warn: LogFn;\n error: LogFn;\n}\n\n/** A logger that swallows everything. Useful in tests / perf runs. */\nexport const createNoopLogger = (): LoggerLike => ({\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n});\n\nconst LEVELS = ['debug', 'info', 'warn', 'error'] as const;\nexport type LogLevel = (typeof LEVELS)[number];\n\nconst LEVEL_RANK: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };\n\nconst fmtObj = (obj: Record<string, unknown>): string => {\n try {\n return JSON.stringify(obj);\n } catch {\n return String(obj);\n }\n};\n\n/**\n * A tiny console logger matching the `LoggerLike` shape. `level` filters output.\n */\nexport const createConsoleLogger = (level: LogLevel = 'info'): LoggerLike => {\n const threshold = LEVEL_RANK[level];\n const make = (method: 'debug' | 'info' | 'warn' | 'error') => {\n const rank = LEVEL_RANK[method];\n const fn: LogFn = (a: unknown, b?: string) => {\n if (rank < threshold) return;\n const obj = typeof a === 'string' ? undefined : (a as Record<string, unknown>);\n const msg = typeof a === 'string' ? a : b;\n const prefix = obj !== undefined ? fmtObj(obj) : '';\n if (msg && prefix) {\n // eslint-disable-next-line no-console\n console[method](`[${method.toUpperCase()}]`, prefix, msg);\n } else {\n // eslint-disable-next-line no-console\n console[method](`[${method.toUpperCase()}]`, (msg ?? prefix) as string);\n }\n };\n return fn;\n };\n return {\n debug: make('debug'),\n info: make('info'),\n warn: make('warn'),\n error: make('error'),\n };\n};\n","/**\n * Minimal dependency-free LRU cache backed by a Map (recency = insertion order).\n * get() re-inserts the entry so the most-recently-used item is always last.\n */\nexport interface LRUOptions {\n /** Maximum number of entries before the least-recently-used item is evicted. Must be > 0. */\n max: number;\n /**\n * Called when an entry is evicted by capacity pressure (NOT on explicit\n * delete/clear). Lets owners release side structures keyed by the entry.\n */\n onEvict?: (key: never, value: never) => void;\n}\n\nexport class LRU<K, V> {\n readonly max: number;\n private map = new Map<K, V>();\n private onEvict: ((key: K, value: V) => void) | undefined;\n /** Number of entries evicted to make room for new ones (capacity evictions only). */\n evictions = 0;\n\n constructor(options: LRUOptions) {\n if (options.max <= 0) throw new Error('LRU max must be > 0');\n this.max = options.max;\n // Contravariance-safe: `(key: never) => void` accepts any handler shape.\n this.onEvict = options.onEvict as ((key: K, value: V) => void) | undefined;\n }\n\n get size(): number {\n return this.map.size;\n }\n\n get(key: K): V | undefined {\n const value = this.map.get(key);\n if (value === undefined) return undefined;\n // Refresh recency.\n this.map.delete(key);\n this.map.set(key, value);\n return value;\n }\n\n peek(key: K): V | undefined {\n return this.map.get(key);\n }\n\n has(key: K): boolean {\n return this.map.has(key);\n }\n\n set(key: K, value: V): this {\n if (this.map.has(key)) this.map.delete(key);\n this.map.set(key, value);\n if (this.map.size > this.max) {\n const next = this.map.keys().next();\n if (next.done !== true) {\n const evictedKey = next.value;\n const evictedValue = this.map.get(evictedKey) as V;\n this.map.delete(evictedKey);\n this.evictions++;\n try {\n // Eviction hooks must never break the cache write path.\n this.onEvict?.(evictedKey, evictedValue);\n } catch {\n /* ignore */\n }\n }\n }\n return this;\n }\n\n delete(key: K): boolean {\n return this.map.delete(key);\n }\n\n clear(): void {\n this.map.clear();\n }\n\n keys(): IterableIterator<K> {\n return this.map.keys();\n }\n\n entries(): IterableIterator<[K, V]> {\n return this.map.entries();\n }\n\n [Symbol.iterator](): IterableIterator<[K, V]> {\n return this.map.entries();\n }\n}\n","/**\n * Generic keyed factory caches (memoization). Unlike the ORM's `QueryCache`\n * (per-collection, TTL, invalidation-aware) and `HotCache` (read-through,\n * event/timer-refreshed), these are dependency-free building blocks for\n * memoizing any expensive computation by key — including in-flight dedup so a\n * keyed factory never runs twice concurrently.\n */\nimport { LRU } from './lru.ts';\n\nexport interface CacheOptions {\n /** Max cached entries. `0` or negative → unbounded Map (default 100). */\n maxSize?: number;\n}\n\ntype BackingStore<K, V> = {\n get(k: K): V | undefined;\n set(k: K, v: V): void;\n has(k: K): boolean;\n delete(k: K): void;\n};\n\nconst makeStore = <K, V>(maxSize: number): BackingStore<K, V> => {\n if (maxSize <= 0) {\n const map = new Map<K, V>();\n return {\n get: (k) => map.get(k),\n set: (k, v) => {\n map.set(k, v);\n },\n has: (k) => map.has(k),\n delete: (k) => {\n map.delete(k);\n },\n };\n }\n return new LRU<K, V>({ max: maxSize });\n};\n\n/**\n * Synchronous keyed factory cache: one value per key, LRU-bounded.\n * Used for cheap singleton factories (connection pools, resolvers, ...).\n */\nexport const createCachedFactory = <K, V>(\n factory: (key: K) => V,\n options: CacheOptions = {},\n): ((key: K) => V) => {\n const maxSize = options.maxSize ?? 100;\n const cache = makeStore<K, V>(maxSize);\n return (key: K): V => {\n const existing = cache.get(key);\n if (existing !== undefined) return existing;\n const value = factory(key);\n cache.set(key, value);\n return value;\n };\n};\n\n/**\n * Async keyed factory cache with **in-flight deduplication**: concurrent calls\n * with the same key share a single Promise so the factory runs only once.\n * On settle (success or failure) the in-flight entry is removed.\n */\nexport const createCachedAsyncFactory = <K, V>(\n factory: (key: K) => Promise<V>,\n options: CacheOptions = {},\n): ((key: K) => Promise<V>) => {\n const maxSize = options.maxSize ?? 100;\n const cache = makeStore<K, Promise<V>>(maxSize);\n const inFlight = new Map<K, Promise<V>>();\n\n return (key: K): Promise<V> => {\n const inflight = inFlight.get(key);\n if (inflight) return inflight;\n\n const cached = cache.get(key);\n if (cached) return cached;\n\n const promise = factory(key);\n inFlight.set(key, promise);\n cache.set(key, promise);\n promise.then(\n () => {\n inFlight.delete(key);\n },\n () => {\n inFlight.delete(key);\n // Never cache a failure — evict (identity-guarded) so the next call\n // retries instead of re-surfacing the same rejected promise.\n if (cache.get(key) === promise) cache.delete(key);\n },\n );\n return promise;\n };\n};\n","/**\n * Race a promise against a timeout. A `timeoutMs` <= 0 disables the timer and\n * returns the original promise untouched (zero-overhead fast path).\n */\nexport const withTimeout = <T>(\n promise: Promise<T>,\n timeoutMs: number,\n label: string,\n): Promise<T> => {\n if (timeoutMs <= 0) return promise;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`Operation timed out after ${timeoutMs}ms: ${label}`));\n }, timeoutMs);\n });\n return Promise.race([promise, timeout]).finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n });\n};\n\n/**\n * Sleep helper used by retry backoff.\n *\n * `unref` (default false) detaches the timer from the event loop, so a\n * background retry loop never keeps the process alive on its own (the\n * consumer's server/sockets do that). Watcher backoff passes `unref: true`;\n * awaited query retries keep the default so a pending call is never dropped.\n */\nexport const sleep = (ms: number, unref = false): Promise<void> =>\n new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n if (unref) timer.unref?.();\n });\n\n/**\n * Sleep for `ms` plus random jitter (0..jitterMs, capped at `ms`) so retry\n * loops don't resynchronize into a thundering herd after a replica failover.\n * Accepts the same `unref` option as {@link sleep}.\n */\nexport const sleepJittered = (ms: number, jitterMs = 1000, unref = false): Promise<void> =>\n sleep(ms + Math.floor(Math.random() * Math.min(ms, jitterMs)), unref);\n","/**\n * Permanent change-stream error classification, shared by every `$changeStream`\n * watcher (service `CacheInvalidator` + hot-cache `WatchCoordinator`).\n *\n * These errors can NEVER be fixed by retrying — retrying only logs the same\n * warning every 1–5s forever and (with an un-unref'd timer) keeps the process\n * alive, which is wrong for a background watcher whose host server or build\n * should own the process lifetime. Watchers must classify them once, log once,\n * and stop (disable the watcher / fall back to the standalone ticker).\n *\n * Detection alone is not enough for the operator reading the log, though: \"change\n * streams unavailable permanently\" says nothing about *why* or *what to change*.\n * {@link explainPermanentWatchError} returns the cause-specific headline and the\n * one-line fix, so the warning that disables a watcher tells you it was the\n * credentials (fix the URL and restart) or a standalone server (use a replica\n * set or turn `cacheWatch` off).\n *\n * Deliberately NARROW on purpose: replica errors that ARE transient — e.g.\n * \"change stream history lost\" (resume-token expiry, collection dropped),\n * network drops, failovers — must stay in the retry path, where\n * invalidate-on-reopen restores correctness. Broad patterns (like `not\n * supported` or `ChangeStream`) are rejected here because they can misfire on\n * those recoverable errors.\n */\n\n/** Why a change stream can never succeed. */\nexport type PermanentWatchReason = 'standalone' | 'credentials';\n\n/** A cause-specific explanation of a permanent change-stream failure. */\nexport interface PermanentWatchExplanation {\n /** The classified reason. */\n readonly reason: PermanentWatchReason;\n /** Short headline for a log line (`change streams need a replica set`). */\n readonly headline: string;\n /** The fix, in one line. */\n readonly hint: string;\n}\n\n/** Standalone server: `$changeStream` is not supported at all. */\nconst STANDALONE = /only supported on replica sets|not supported on (?:a )?standalone/i;\n\n/**\n * Credentials/authorization problems: the user cannot run `aggregate` on this\n * collection/server. Wrong creds, missing roles, or a user created without the\n * `read`/`changeStream` privilege will never start working by retrying.\n */\nconst CREDENTIALS =\n /requires authentication|not authorized|unauthorized|authentication failed|Command .* requires authentication/i;\n\n/**\n * Explain a permanent change-stream failure, or return `undefined` when the\n * error is retryable (the caller must keep retrying with backoff in that case).\n *\n * @param message - The driver's error message.\n * @returns The reason + headline + fix, or `undefined` when retrying may work.\n */\nexport const explainPermanentWatchError = (\n message: string,\n): PermanentWatchExplanation | undefined => {\n if (STANDALONE.test(message)) {\n return {\n reason: 'standalone',\n headline: 'change streams need a replica set',\n hint:\n 'Point the connection at a replica set (a single-node replica set is fine for local dev), ' +\n 'or set cacheWatch: false / cache: { ttlMs } to run write-through only.',\n };\n }\n if (CREDENTIALS.test(message)) {\n return {\n reason: 'credentials',\n headline: 'the connection is not authorized for change streams',\n hint:\n 'Change streams need an authenticated user with the read + changeStream privilege ' +\n '(usually granted on the authSource database) — fix the credentials in .env, then restart.',\n };\n }\n return undefined;\n};\n\n/**\n * True when retrying this change-stream error can never succeed.\n *\n * @param message - The driver's error message.\n * @returns `true` for a definitive standalone/authorization verdict.\n */\nexport const isPermanentWatchError = (message: string): boolean =>\n explainPermanentWatchError(message) !== undefined;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ignex/ninox",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Schema-first MongoDB ORM with DataLoader-batched relations, query caching, in-flight dedup, and $facet pagination",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,8 +43,8 @@
43
43
  "lint:fix": "biome check --write .",
44
44
  "format": "biome format --write .",
45
45
  "prepublishOnly": "bun run typecheck && bun run lint && bun run build",
46
- "deploy": "bun run scripts/deploy.ts",
47
- "deploy:dry": "bun run scripts/deploy.ts --dry-run"
46
+ "release": "bun scripts/release.ts",
47
+ "release:dry": "bun scripts/release.ts --dry-run"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@biomejs/biome": "^2.0.0",