@c9up/echo 0.1.14 → 0.1.15

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.
@@ -71,8 +71,14 @@ export declare class Store {
71
71
  export declare function store(options?: Omit<StoreConfig, "driver">): Store;
72
72
  /** Driver factory helpers (bento `drivers.memory` / `drivers.redis`). */
73
73
  export declare const drivers: {
74
+ /**
75
+ * `maxItems` bounds the store by COUNT, which a TTL cannot: a cache keyed by
76
+ * a user id or a search term grows until the process runs out of memory,
77
+ * however short each entry's life. Omit it for no ceiling.
78
+ */
74
79
  memory(options?: {
75
80
  sweepIntervalMs?: number;
81
+ maxItems?: number;
76
82
  }): DriverFactory;
77
83
  /**
78
84
  * Either hand it a client, or name a quasar connection — the AdonisJS shape,
@@ -87,6 +93,18 @@ export declare const drivers: {
87
93
  connection?: string;
88
94
  prefix?: string;
89
95
  }): DriverFactory;
96
+ /**
97
+ * Redis pub/sub as the bus that keeps each instance's L1 in step.
98
+ *
99
+ * Named alongside `redis` and taking the same `connection`, because it is
100
+ * the same deployment decision: a two-layer store without a bus is wrong as
101
+ * soon as a second instance exists, each process serving its own L1 copy of
102
+ * a key another has already deleted.
103
+ */
104
+ redisBus(options?: {
105
+ connection?: string;
106
+ channel?: string;
107
+ }): CacheBus;
90
108
  tiered(options: {
91
109
  l1: DriverFactory;
92
110
  l2: DriverFactory;
@@ -1 +1 @@
1
- {"version":3,"file":"StoreManager.d.ts","sourceRoot":"","sources":["../src/StoreManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StoreManager.d.ts","sourceRoot":"","sources":["../src/StoreManager.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@ import { CacheManager } from "./CacheManager.js";
18
18
  import { MemoryDriver } from "./drivers/MemoryDriver.js";
19
19
  import { RedisDriver } from "./drivers/RedisDriver.js";
20
20
  import { TieredDriver } from "./drivers/TieredDriver.js";
21
- import { quasarConnection } from "./quasar.js";
21
+ import { quasarBus, quasarConnection } from "./quasar.js";
22
22
  /**
23
23
  * A store, described the way a cache config describes one: a layer at a time.
24
24
  *
@@ -85,8 +85,13 @@ function entryOf(config) {
85
85
  }
86
86
  /** Driver factory helpers (bento `drivers.memory` / `drivers.redis`). */
87
87
  export const drivers = {
88
+ /**
89
+ * `maxItems` bounds the store by COUNT, which a TTL cannot: a cache keyed by
90
+ * a user id or a search term grows until the process runs out of memory,
91
+ * however short each entry's life. Omit it for no ceiling.
92
+ */
88
93
  memory(options) {
89
- return () => new MemoryDriver(options?.sweepIntervalMs);
94
+ return () => new MemoryDriver(options?.sweepIntervalMs, options?.maxItems);
90
95
  },
91
96
  /**
92
97
  * Either hand it a client, or name a quasar connection — the AdonisJS shape,
@@ -100,6 +105,17 @@ export const drivers = {
100
105
  }
101
106
  return () => new RedisDriver(quasarConnection(options.connection), options.prefix);
102
107
  },
108
+ /**
109
+ * Redis pub/sub as the bus that keeps each instance's L1 in step.
110
+ *
111
+ * Named alongside `redis` and taking the same `connection`, because it is
112
+ * the same deployment decision: a two-layer store without a bus is wrong as
113
+ * soon as a second instance exists, each process serving its own L1 copy of
114
+ * a key another has already deleted.
115
+ */
116
+ redisBus(options) {
117
+ return quasarBus(options);
118
+ },
103
119
  tiered(options) {
104
120
  return () => new TieredDriver({
105
121
  l1: options.l1(),
@@ -1 +1 @@
1
- {"version":3,"file":"StoreManager.js","sourceRoot":"","sources":["../src/StoreManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAoB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAiB,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA6B/C;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,KAAK;IACR,QAAQ,CAA8B;IAC/C,GAAG,CAA4B;IAC/B,GAAG,CAA4B;IAC/B,IAAI,CAAuB;IAE3B,YAAY,UAAuC,EAAE;QACpD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,sCAAsC;IACtC,UAAU,CAAC,MAAqB;QAC/B,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC;QAClB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,gFAAgF;IAChF,UAAU,CAAC,MAAqB;QAC/B,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC;QAClB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,oEAAoE;IACpE,MAAM,CAAC,GAAa;QACnB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,uDAAuD;IACvD,KAAK;QACJ,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACd,wEAAwE,CACxE,CAAC;QACH,CAAC;QACD,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;YACd,OAAO;gBACN,GAAG,IAAI,CAAC,QAAQ;gBAChB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;aAClD,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,CAAkB,CAAC;QACzC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACd,6HAA6H,CAC7H,CAAC;QACH,CAAC;QACD,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3C,CAAC;CACD;AAED,uDAAuD;AACvD,MAAM,UAAU,KAAK,CAAC,OAAqC;IAC1D,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,mEAAmE;AACnE,SAAS,OAAO,CAAC,MAA2B;IAC3C,OAAO,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1D,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,OAAO,GAAG;IACtB,MAAM,CAAC,OAAsC;QAC5C,OAAO,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IACzD,CAAC;IACD;;;;;OAKG;IACH,KAAK,CACJ,OAE2C;QAE3C,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;YACzB,OAAO,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,GAAG,EAAE,CACX,IAAI,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,CAAC,OAIN;QACA,OAAO,GAAG,EAAE,CACX,IAAI,YAAY,CAAC;YAChB,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE;YAChB,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE;YAChB,GAAG,EAAE,OAAO,CAAC,GAAG;SAChB,CAAC,CAAC;IACL,CAAC;CACD,CAAC;AAEF,MAAM,OAAO,iBAAiB;IAC7B,OAAO,CAAmB;IAC1B,MAAM,GAA8B,IAAI,GAAG,EAAE,CAAC;IAC9C,wEAAwE;IACxE,wCAAwC;IAC/B,UAAU,GAEf;QACH,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,eAAe,EAAE,IAAI,GAAG,EAAE;QAC1B,eAAe,EAAE,IAAI,GAAG,EAAE;QAC1B,eAAe,EAAE,IAAI,GAAG,EAAE;KAC1B,CAAC;IAEF,YAAY,MAAwB;QACnC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACd,wBAAwB,MAAM,CAAC,OAAO,4BAA4B,CAClE,CAAC;QACH,CAAC;IACF,CAAC;IAED,qFAAqF;IACrF,GAAG,CAAC,IAAa;QAChB,MAAM,KAAK,GAAG,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,GAAG,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE;YAC9C,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;SAC7B,CAAC,CAAC;QACH,sEAAsE;QACtE,wEAAwE;QACxE,wEAAwE;QACxE,MAAM,MAAM,GAAG,CAAgC,KAAQ,EAAQ,EAAE;YAChE,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC/C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC7B,CAAC;QACF,CAAC,CAAC;QACF,MAAM,CAAC,WAAW,CAAC,CAAC;QACpB,MAAM,CAAC,YAAY,CAAC,CAAC;QACrB,MAAM,CAAC,eAAe,CAAC,CAAC;QACxB,MAAM,CAAC,eAAe,CAAC,CAAC;QACxB,MAAM,CAAC,eAAe,CAAC,CAAC;QACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAChC,OAAO,OAAO,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,EAAE,CACD,KAAQ,EACR,QAA6C;QAE7C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACb,CAAC;IAED,2EAA2E;IAC3E,IAAI,CACH,KAAQ,EACR,QAA6C;QAE7C,MAAM,OAAO,GAAG,CAAC,OAAyB,EAAQ,EAAE;YACnD,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACzB,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnB,CAAC,CAAC;QACF,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,6EAA6E;IAC7E,GAAG,CACF,KAAQ,EACR,QAA8C;QAE9C,IAAI,QAAQ,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;;YACtD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC;IACb,CAAC;IAED,qEAAqE;IACrE,KAAK,CAAC,QAAQ;QACb,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,KAAK;QACV,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,aAAa;QAClB,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACzE,CAAC;CACD"}
1
+ {"version":3,"file":"StoreManager.js","sourceRoot":"","sources":["../src/StoreManager.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  import type { CacheEntry, DriverSetOptions, TaggableDriver } from "../types.js";
7
7
  export declare class MemoryDriver implements TaggableDriver {
8
8
  #private;
9
- constructor(sweepIntervalMs?: number);
9
+ constructor(sweepIntervalMs?: number, maxItems?: number);
10
10
  /**
11
11
  * Drop expired entries now, without waiting for the sweep (bentocache
12
12
  * `prune`). Useful before measuring size, or in a test that must not depend
@@ -1 +1 @@
1
- {"version":3,"file":"MemoryDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWhF,qBAAa,YAAa,YAAW,cAAc;;gBAKtC,eAAe,SAAS;IAoBpC;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,OAAO,IAAI,IAAI;IAIT,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAMhD,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAIvE,+FAA+F;IAC/F,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAqEtD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,QAAQ,CACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC;IAWV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,qEAAqE;IAC/D,WAAW,CAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAIhB,gEAAgE;IAC1D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBhD,gDAAgD;IAC1C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9C"}
1
+ {"version":3,"file":"MemoryDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWhF,qBAAa,YAAa,YAAW,cAAc;;gBAetC,eAAe,SAAS,EAAE,QAAQ,SAAI;IA2ClD;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,OAAO,IAAI,IAAI;IAIT,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAMhD,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAIvE,+FAA+F;IAC/F,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IA0EtD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,QAAQ,CACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC;IAWV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxC,qEAAqE;IAC/D,WAAW,CAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAIhB,gEAAgE;IAC1D,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBhD,gDAAgD;IAC1C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9C"}
@@ -7,13 +7,48 @@ export class MemoryDriver {
7
7
  #store = new Map();
8
8
  #tagIndex = new Map();
9
9
  #sweepInterval;
10
- constructor(sweepIntervalMs = 60_000) {
10
+ /**
11
+ * How many entries this driver holds before it starts dropping the oldest.
12
+ *
13
+ * Eviction was by TTL alone, which bounds how long an entry lives and not
14
+ * how many there are: a cache keyed by anything unbounded — a user id, a
15
+ * search term — grew until the process ran out of memory, and only a key
16
+ * that was written ever left. `0` keeps the old behaviour for a caller that
17
+ * genuinely wants no ceiling.
18
+ */
19
+ #maxItems;
20
+ constructor(sweepIntervalMs = 60_000, maxItems = 0) {
21
+ this.#maxItems = maxItems > 0 ? maxItems : 0;
11
22
  this.#sweepInterval = setInterval(() => this.#sweep(), sweepIntervalMs);
12
23
  if (typeof this.#sweepInterval === "object" &&
13
24
  "unref" in this.#sweepInterval) {
14
25
  this.#sweepInterval.unref();
15
26
  }
16
27
  }
28
+ /**
29
+ * Drop the oldest entries until the store fits under its ceiling.
30
+ *
31
+ * Expired ones go first — evicting a live entry while a dead one sits in
32
+ * the map would throw away a value someone still wants. Only if that is not
33
+ * enough does it take the least recently written.
34
+ */
35
+ #enforceCeiling() {
36
+ if (this.#maxItems === 0 || this.#store.size <= this.#maxItems)
37
+ return;
38
+ const now = Date.now();
39
+ for (const [key, entry] of this.#store) {
40
+ if (this.#store.size <= this.#maxItems)
41
+ return;
42
+ if (entry.staleUntil > 0 && entry.staleUntil < now) {
43
+ this.#evict(key, entry);
44
+ }
45
+ }
46
+ for (const [key, entry] of this.#store) {
47
+ if (this.#store.size <= this.#maxItems)
48
+ return;
49
+ this.#evict(key, entry);
50
+ }
51
+ }
17
52
  /** Evict every entry past its grace window. */
18
53
  #sweep() {
19
54
  const now = Date.now();
@@ -100,7 +135,12 @@ export class MemoryDriver {
100
135
  for (const t of prev.tags)
101
136
  this.#tagIndex.get(t)?.delete(key);
102
137
  }
138
+ // Re-inserting moves the key to the end of the Map's iteration order, so
139
+ // the first entry is always the least recently WRITTEN — which is the
140
+ // order this evicts in.
141
+ this.#store.delete(key);
103
142
  this.#store.set(key, { value, expiresAt, staleUntil, tags });
143
+ this.#enforceCeiling();
104
144
  for (const tag of tags) {
105
145
  let set = this.#tagIndex.get(tag);
106
146
  if (!set) {
@@ -1 +1 @@
1
- {"version":3,"file":"MemoryDriver.js","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MemoryDriver.js","sourceRoot":"","sources":["../../src/drivers/MemoryDriver.ts"],"names":[],"mappings":"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"}
package/dist/quasar.d.ts CHANGED
@@ -10,9 +10,26 @@
10
10
  * duck-types its host framework.
11
11
  */
12
12
  import type { RedisClient } from "./drivers/RedisDriver.js";
13
+ import type { CacheBus } from "./drivers/TieredDriver.js";
13
14
  /**
14
15
  * A resolver for `drivers.redis({ connection })` — quasar is loaded on the
15
16
  * first cache command, not at config time.
16
17
  */
17
18
  export declare function quasarConnection(name?: string): () => Promise<RedisClient>;
19
+ /**
20
+ * Redis pub/sub as a cache bus, for keeping each instance's L1 in step.
21
+ *
22
+ * A two-layer store without one is wrong the moment a second instance exists:
23
+ * each process keeps serving its own L1 copy of a key another process has
24
+ * already deleted, and nothing ever tells it. That is what the bus is for, and
25
+ * it is why `useBus` sits next to `useL2Layer` in the generated config.
26
+ *
27
+ * Quasar opens the subscriber socket lazily and on its own connection — Redis
28
+ * puts a subscribed client into a mode where it accepts nothing else, so a
29
+ * connection that both publishes and listens needs two.
30
+ */
31
+ export declare function quasarBus(options?: {
32
+ connection?: string;
33
+ channel?: string;
34
+ }): CacheBus;
18
35
  //# sourceMappingURL=quasar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quasar.d.ts","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAyC5D;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CA+B1E"}
1
+ {"version":3,"file":"quasar.d.ts","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAyCtE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CA+B1E;AAuCD;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,QAAQ,CA8CX"}
package/dist/quasar.js CHANGED
@@ -66,4 +66,81 @@ export function quasarConnection(name) {
66
66
  return connection;
67
67
  };
68
68
  }
69
+ function isPubSubSource(value) {
70
+ return (typeof value === "object" &&
71
+ value !== null &&
72
+ typeof Reflect.get(value, "publish") === "function" &&
73
+ typeof Reflect.get(value, "subscribe") === "function");
74
+ }
75
+ /**
76
+ * Resolve quasar's manager, loading it on first use.
77
+ *
78
+ * Shared by the connection resolver and the bus: echo declares quasar an
79
+ * optional peer and never imports it statically, so an application that caches
80
+ * in memory neither installs it nor pays for it.
81
+ */
82
+ async function loadQuasar(context) {
83
+ const specifier = "@c9up/quasar/services/main";
84
+ try {
85
+ const loaded = await import(/* @vite-ignore */ specifier);
86
+ return isConnectionSource(loaded) || isPubSubSource(loaded)
87
+ ? loaded
88
+ : Reflect.get(Object(loaded), "default");
89
+ }
90
+ catch (cause) {
91
+ throw new Error(`Echo: ${context}, but @c9up/quasar is not installed.\n pnpm add @c9up/quasar`, { cause });
92
+ }
93
+ }
94
+ /**
95
+ * Redis pub/sub as a cache bus, for keeping each instance's L1 in step.
96
+ *
97
+ * A two-layer store without one is wrong the moment a second instance exists:
98
+ * each process keeps serving its own L1 copy of a key another process has
99
+ * already deleted, and nothing ever tells it. That is what the bus is for, and
100
+ * it is why `useBus` sits next to `useL2Layer` in the generated config.
101
+ *
102
+ * Quasar opens the subscriber socket lazily and on its own connection — Redis
103
+ * puts a subscribed client into a mode where it accepts nothing else, so a
104
+ * connection that both publishes and listens needs two.
105
+ */
106
+ export function quasarBus(options) {
107
+ const channel = options?.channel ?? "echo::invalidate";
108
+ let ready;
109
+ const manager = () => {
110
+ ready ??= loadQuasar(`the cache bus asks for the "${options?.connection ?? "default"}" quasar connection`).then((loaded) => {
111
+ const source = isConnectionSource(loaded)
112
+ ? loaded.connection(options?.connection)
113
+ : loaded;
114
+ if (!isPubSubSource(source)) {
115
+ throw new Error("Echo: the quasar connection does not expose publish()/subscribe()");
116
+ }
117
+ return source;
118
+ });
119
+ return ready;
120
+ };
121
+ return {
122
+ async publish(message) {
123
+ const source = await manager();
124
+ await source.publish(channel, JSON.stringify(message));
125
+ },
126
+ subscribe(handler) {
127
+ // Not awaited: `subscribe` is synchronous in the bus contract, and the
128
+ // socket opens on quasar's own schedule. A failure to reach Redis must
129
+ // not take down the store — a bus that is down costs staleness, and
130
+ // throwing here would cost every cache read.
131
+ void manager()
132
+ .then((source) => source.subscribe(channel, (raw) => {
133
+ try {
134
+ handler(JSON.parse(raw));
135
+ }
136
+ catch {
137
+ /* a malformed frame is not a reason to stop listening */
138
+ }
139
+ }))
140
+ .catch(() => {
141
+ /* reported by the first publish, which does surface its error */
142
+ });
143
+ },
144
+ };
145
+ }
69
146
  //# sourceMappingURL=quasar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"quasar.js","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,SAAS,kBAAkB,CAAC,KAAc;IACzC,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,UAAU,CACtD,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,2EAA2E;IAC3E,qDAAqD;IACrD,EAAE;IACF,4EAA4E;IAC5E,4EAA4E;IAC5E,0EAA0E;IAC1E,uEAAuE;IACvE,yEAAyE;IACzE,qCAAqC;IACrC,MAAM,QAAQ,GAAG;QAChB,KAAK;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,MAAM;QACN,UAAU;QACV,QAAQ;QACR,KAAK;KACL,CAAC;IACF,OAAO,QAAQ,CAAC,KAAK,CACpB,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,UAAU,CACxD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAa;IAC7C,OAAO,KAAK,IAAI,EAAE;QACjB,MAAM,SAAS,GAAG,4BAA4B,CAAC;QAC/C,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACd,cAAc,IAAI,IAAI,SAAS,kFAAkF;gBAChH,yBAAyB,EAC1B,EAAE,KAAK,EAAE,CACT,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC;YACzC,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACd,wEAAwE,CACxE,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACd,4BAA4B,IAAI,IAAI,SAAS,gDAAgD,CAC7F,CAAC;QACH,CAAC;QACD,OAAO,UAAU,CAAC;IACnB,CAAC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"quasar.js","sourceRoot":"","sources":["../src/quasar.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c9up/echo",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Cache — pluggable cache contract with memory + Redis drivers for the Ream framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,7 +20,7 @@ import { MemoryDriver } from "./drivers/MemoryDriver.js";
20
20
  import { type RedisClient, RedisDriver } from "./drivers/RedisDriver.js";
21
21
  import { type CacheBus, TieredDriver } from "./drivers/TieredDriver.js";
22
22
  import type { Duration } from "./duration.js";
23
- import { quasarConnection } from "./quasar.js";
23
+ import { quasarBus, quasarConnection } from "./quasar.js";
24
24
  import type { CacheDriver, CacheEmitter, CacheEventMap } from "./types.js";
25
25
 
26
26
  /** A lazily-instantiated driver (built once per store, on first `use`). */
@@ -127,8 +127,16 @@ function entryOf(config: StoreConfig | Store): StoreConfig {
127
127
 
128
128
  /** Driver factory helpers (bento `drivers.memory` / `drivers.redis`). */
129
129
  export const drivers = {
130
- memory(options?: { sweepIntervalMs?: number }): DriverFactory {
131
- return () => new MemoryDriver(options?.sweepIntervalMs);
130
+ /**
131
+ * `maxItems` bounds the store by COUNT, which a TTL cannot: a cache keyed by
132
+ * a user id or a search term grows until the process runs out of memory,
133
+ * however short each entry's life. Omit it for no ceiling.
134
+ */
135
+ memory(options?: {
136
+ sweepIntervalMs?: number;
137
+ maxItems?: number;
138
+ }): DriverFactory {
139
+ return () => new MemoryDriver(options?.sweepIntervalMs, options?.maxItems);
132
140
  },
133
141
  /**
134
142
  * Either hand it a client, or name a quasar connection — the AdonisJS shape,
@@ -147,6 +155,17 @@ export const drivers = {
147
155
  return () =>
148
156
  new RedisDriver(quasarConnection(options.connection), options.prefix);
149
157
  },
158
+ /**
159
+ * Redis pub/sub as the bus that keeps each instance's L1 in step.
160
+ *
161
+ * Named alongside `redis` and taking the same `connection`, because it is
162
+ * the same deployment decision: a two-layer store without a bus is wrong as
163
+ * soon as a second instance exists, each process serving its own L1 copy of
164
+ * a key another has already deleted.
165
+ */
166
+ redisBus(options?: { connection?: string; channel?: string }): CacheBus {
167
+ return quasarBus(options);
168
+ },
150
169
  tiered(options: {
151
170
  l1: DriverFactory;
152
171
  l2: DriverFactory;
@@ -19,8 +19,19 @@ export class MemoryDriver implements TaggableDriver {
19
19
  #store: Map<string, StoredEntry> = new Map();
20
20
  #tagIndex: Map<string, Set<string>> = new Map();
21
21
  #sweepInterval: ReturnType<typeof setInterval>;
22
+ /**
23
+ * How many entries this driver holds before it starts dropping the oldest.
24
+ *
25
+ * Eviction was by TTL alone, which bounds how long an entry lives and not
26
+ * how many there are: a cache keyed by anything unbounded — a user id, a
27
+ * search term — grew until the process ran out of memory, and only a key
28
+ * that was written ever left. `0` keeps the old behaviour for a caller that
29
+ * genuinely wants no ceiling.
30
+ */
31
+ readonly #maxItems: number;
22
32
 
23
- constructor(sweepIntervalMs = 60_000) {
33
+ constructor(sweepIntervalMs = 60_000, maxItems = 0) {
34
+ this.#maxItems = maxItems > 0 ? maxItems : 0;
24
35
  this.#sweepInterval = setInterval(() => this.#sweep(), sweepIntervalMs);
25
36
  if (
26
37
  typeof this.#sweepInterval === "object" &&
@@ -30,6 +41,28 @@ export class MemoryDriver implements TaggableDriver {
30
41
  }
31
42
  }
32
43
 
44
+ /**
45
+ * Drop the oldest entries until the store fits under its ceiling.
46
+ *
47
+ * Expired ones go first — evicting a live entry while a dead one sits in
48
+ * the map would throw away a value someone still wants. Only if that is not
49
+ * enough does it take the least recently written.
50
+ */
51
+ #enforceCeiling(): void {
52
+ if (this.#maxItems === 0 || this.#store.size <= this.#maxItems) return;
53
+ const now = Date.now();
54
+ for (const [key, entry] of this.#store) {
55
+ if (this.#store.size <= this.#maxItems) return;
56
+ if (entry.staleUntil > 0 && entry.staleUntil < now) {
57
+ this.#evict(key, entry);
58
+ }
59
+ }
60
+ for (const [key, entry] of this.#store) {
61
+ if (this.#store.size <= this.#maxItems) return;
62
+ this.#evict(key, entry);
63
+ }
64
+ }
65
+
33
66
  /** Evict every entry past its grace window. */
34
67
  #sweep(): void {
35
68
  const now = Date.now();
@@ -130,7 +163,12 @@ export class MemoryDriver implements TaggableDriver {
130
163
  if (prev !== undefined) {
131
164
  for (const t of prev.tags) this.#tagIndex.get(t)?.delete(key);
132
165
  }
166
+ // Re-inserting moves the key to the end of the Map's iteration order, so
167
+ // the first entry is always the least recently WRITTEN — which is the
168
+ // order this evicts in.
169
+ this.#store.delete(key);
133
170
  this.#store.set(key, { value, expiresAt, staleUntil, tags });
171
+ this.#enforceCeiling();
134
172
  for (const tag of tags) {
135
173
  let set = this.#tagIndex.get(tag);
136
174
  if (!set) {
package/src/quasar.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { RedisClient } from "./drivers/RedisDriver.js";
14
+ import type { BusMessage, CacheBus } from "./drivers/TieredDriver.js";
14
15
 
15
16
  /** The slice of quasar's manager this needs: a connection, by name. */
16
17
  interface ConnectionSource {
@@ -87,3 +88,103 @@ export function quasarConnection(name?: string): () => Promise<RedisClient> {
87
88
  return connection;
88
89
  };
89
90
  }
91
+
92
+ /** The slice of quasar's manager a pub/sub bus needs. */
93
+ interface PubSubSource {
94
+ publish(channel: string, message: string): unknown;
95
+ subscribe(channel: string, handler: (message: string) => void): unknown;
96
+ }
97
+
98
+ function isPubSubSource(value: unknown): value is PubSubSource {
99
+ return (
100
+ typeof value === "object" &&
101
+ value !== null &&
102
+ typeof Reflect.get(value, "publish") === "function" &&
103
+ typeof Reflect.get(value, "subscribe") === "function"
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Resolve quasar's manager, loading it on first use.
109
+ *
110
+ * Shared by the connection resolver and the bus: echo declares quasar an
111
+ * optional peer and never imports it statically, so an application that caches
112
+ * in memory neither installs it nor pays for it.
113
+ */
114
+ async function loadQuasar(context: string): Promise<unknown> {
115
+ const specifier = "@c9up/quasar/services/main";
116
+ try {
117
+ const loaded = await import(/* @vite-ignore */ specifier);
118
+ return isConnectionSource(loaded) || isPubSubSource(loaded)
119
+ ? loaded
120
+ : Reflect.get(Object(loaded), "default");
121
+ } catch (cause) {
122
+ throw new Error(
123
+ `Echo: ${context}, but @c9up/quasar is not installed.\n pnpm add @c9up/quasar`,
124
+ { cause },
125
+ );
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Redis pub/sub as a cache bus, for keeping each instance's L1 in step.
131
+ *
132
+ * A two-layer store without one is wrong the moment a second instance exists:
133
+ * each process keeps serving its own L1 copy of a key another process has
134
+ * already deleted, and nothing ever tells it. That is what the bus is for, and
135
+ * it is why `useBus` sits next to `useL2Layer` in the generated config.
136
+ *
137
+ * Quasar opens the subscriber socket lazily and on its own connection — Redis
138
+ * puts a subscribed client into a mode where it accepts nothing else, so a
139
+ * connection that both publishes and listens needs two.
140
+ */
141
+ export function quasarBus(options?: {
142
+ connection?: string;
143
+ channel?: string;
144
+ }): CacheBus {
145
+ const channel = options?.channel ?? "echo::invalidate";
146
+ let ready: Promise<PubSubSource> | undefined;
147
+
148
+ const manager = (): Promise<PubSubSource> => {
149
+ ready ??= loadQuasar(
150
+ `the cache bus asks for the "${options?.connection ?? "default"}" quasar connection`,
151
+ ).then((loaded) => {
152
+ const source = isConnectionSource(loaded)
153
+ ? loaded.connection(options?.connection)
154
+ : loaded;
155
+ if (!isPubSubSource(source)) {
156
+ throw new Error(
157
+ "Echo: the quasar connection does not expose publish()/subscribe()",
158
+ );
159
+ }
160
+ return source;
161
+ });
162
+ return ready;
163
+ };
164
+
165
+ return {
166
+ async publish(message: BusMessage): Promise<void> {
167
+ const source = await manager();
168
+ await source.publish(channel, JSON.stringify(message));
169
+ },
170
+ subscribe(handler: (message: BusMessage) => void): void {
171
+ // Not awaited: `subscribe` is synchronous in the bus contract, and the
172
+ // socket opens on quasar's own schedule. A failure to reach Redis must
173
+ // not take down the store — a bus that is down costs staleness, and
174
+ // throwing here would cost every cache read.
175
+ void manager()
176
+ .then((source) =>
177
+ source.subscribe(channel, (raw: string) => {
178
+ try {
179
+ handler(JSON.parse(raw) as BusMessage);
180
+ } catch {
181
+ /* a malformed frame is not a reason to stop listening */
182
+ }
183
+ }),
184
+ )
185
+ .catch(() => {
186
+ /* reported by the first publish, which does surface its error */
187
+ });
188
+ },
189
+ };
190
+ }