@c9up/bay 0.1.11 → 0.1.13

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.
Files changed (42) hide show
  1. package/README.md +66 -1
  2. package/dist/BayProvider.d.ts +24 -30
  3. package/dist/BayProvider.d.ts.map +1 -1
  4. package/dist/BayProvider.js +58 -9
  5. package/dist/BayProvider.js.map +1 -1
  6. package/dist/QueueManager.d.ts +7 -5
  7. package/dist/QueueManager.d.ts.map +1 -1
  8. package/dist/QueueManager.js +75 -31
  9. package/dist/QueueManager.js.map +1 -1
  10. package/dist/configure.d.ts +18 -0
  11. package/dist/configure.d.ts.map +1 -0
  12. package/dist/configure.js +31 -0
  13. package/dist/configure.js.map +1 -0
  14. package/dist/drivers/RedisDriver.d.ts +6 -0
  15. package/dist/drivers/RedisDriver.d.ts.map +1 -1
  16. package/dist/drivers/RedisDriver.js +49 -17
  17. package/dist/drivers/RedisDriver.js.map +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/nodeEnv.d.ts +16 -0
  23. package/dist/nodeEnv.d.ts.map +1 -0
  24. package/dist/nodeEnv.js +32 -0
  25. package/dist/nodeEnv.js.map +1 -0
  26. package/dist/services/main.d.ts +5 -0
  27. package/dist/services/main.d.ts.map +1 -1
  28. package/dist/services/main.js +7 -0
  29. package/dist/services/main.js.map +1 -1
  30. package/dist/stores.d.ts +41 -0
  31. package/dist/stores.d.ts.map +1 -0
  32. package/dist/stores.js +46 -0
  33. package/dist/stores.js.map +1 -0
  34. package/package.json +5 -1
  35. package/src/BayProvider.ts +84 -19
  36. package/src/QueueManager.ts +77 -32
  37. package/src/configure.ts +45 -0
  38. package/src/drivers/RedisDriver.ts +69 -22
  39. package/src/index.ts +1 -0
  40. package/src/nodeEnv.ts +30 -0
  41. package/src/services/main.ts +8 -0
  42. package/src/stores.ts +59 -0
package/README.md CHANGED
@@ -11,9 +11,14 @@ pnpm add @c9up/bay
11
11
  ream configure @c9up/bay
12
12
  ```
13
13
 
14
+ `ream add @c9up/bay` installs it, registers the provider and writes
15
+ `config/queue.ts`. The rest of this page assumes that has run.
16
+
14
17
  ## Usage
15
18
 
16
- Register the provider in your app, then configure it under `config/queue.ts` — the provider reads `config.get('queue')`, so a `config/bay.ts` would be loaded under the key `bay` and never seen:
19
+ Register the provider, then name the queue backend in `config/queue.ts` the
20
+ provider reads `config.get('queue')`, so a `config/bay.ts` would be loaded under
21
+ the key `bay` and never seen:
17
22
 
18
23
  ```ts
19
24
  // reamrc.ts
@@ -22,6 +27,66 @@ providers: [
22
27
  ]
23
28
  ```
24
29
 
30
+ ```ts
31
+ // config/queue.ts
32
+ import { defineConfig, stores } from '@c9up/bay'
33
+ import env from '#start/env'
34
+
35
+ export default defineConfig({
36
+ default: env.get('QUEUE_STORE'),
37
+ stores: {
38
+ memory: stores.memory(),
39
+ redis: stores.redis({ connection: 'main' }),
40
+ },
41
+ })
42
+ ```
43
+
44
+ ```ts
45
+ // start/queue.ts
46
+ import queue from '@c9up/bay/services/main'
47
+
48
+ queue.register('send-email', new SendEmailJob())
49
+ await queue.dispatch('send-email', { to: 'user@example.com' })
50
+ ```
51
+
52
+ | Store | Keeps jobs | Use it when |
53
+ | --- | --- | --- |
54
+ | `stores.memory()` | until the process exits | tests, and local work |
55
+ | `stores.redis({ connection })` | in Redis | anything that must survive a restart |
56
+
57
+ Factories are lazy: only the store actually selected is built, so naming a Redis
58
+ queue in a config that runs in memory opens no connection. A `default` that
59
+ names nothing throws, listing what exists — falling back to memory would look
60
+ like it worked until a restart dropped every pending job.
61
+
62
+ `stores.redis` takes a `@c9up/quasar` connection name, resolved at first use so
63
+ bay never imports quasar, which stays an optional peer. Pass an ioredis-shaped
64
+ client (or a function answering one) to use any other.
65
+
66
+ ## Delivery guarantee, and the one way to lose it
67
+
68
+ A job the queue accepted gets run: `pop()` moves it from pending to processing
69
+ in a single `LMOVE`, so a worker that dies mid-job leaves the job recoverable
70
+ rather than gone.
71
+
72
+ `LMOVE` needs **Redis 6.2 or later**. Without it the move is `lpop` then
73
+ `rpush`, and a crash between the two deletes the job from pending before it
74
+ reaches processing — nothing recovers it, because nothing knows it existed.
75
+ That turns at-least-once delivery into at-most-once.
76
+
77
+ So on an older Redis the driver **refuses to start in production**, and says
78
+ which two ways out there are:
79
+
80
+ ```ts
81
+ // Either upgrade the server, or state that losing a job is acceptable here:
82
+ stores.redis({ connection: 'jobs', allowNonAtomicPop: true })
83
+ ```
84
+
85
+ The opt-in is honoured and still logs a warning on every process that starts
86
+ with it, naming production — agreeing once in a config file is not the same as
87
+ being reminded, in the logs of an incident, that this is how the process was
88
+ running. Outside production the fallback simply warns.
89
+
25
90
  ## Entry points
26
91
 
27
92
  - `@c9up/bay` — main API
@@ -1,3 +1,4 @@
1
+ import type { QueueStoreFactory } from "./stores.js";
1
2
  /**
2
3
  * Slim, duck-typed host context — bay stays publishable without
3
4
  * importing `@c9up/ream`. Any framework that exposes a Container with
@@ -17,44 +18,37 @@ export interface BayAppContext {
17
18
  }
18
19
  export interface BayProviderConfig {
19
20
  /**
20
- * Driver to bind by default. The provider only auto-wires `"memory"`
21
- * (the default) it has no Redis connection to build a `RedisDriver`
22
- * from. For Redis, a custom driver, or a pre-built instance, wire
23
- * `QueueManager` directly in your app's startup and skip the provider;
24
- * the `services/main` singleton resolves whatever is registered.
25
- * Passing anything other than `"memory"` throws at boot.
26
- *
27
- * Default `"memory"`.
21
+ * Which named store to use a key of {@link stores}. Read from the
22
+ * environment in the generated config, so a deployment picks its queue
23
+ * backend without editing a file.
24
+ */
25
+ default?: string;
26
+ /**
27
+ * The queue stores this application can use, by name. Each is a factory
28
+ * from `stores.*`, built only when it is the one selected.
29
+ */
30
+ stores?: Record<string, QueueStoreFactory>;
31
+ /**
32
+ * The single-store form, kept for configs written against it: only
33
+ * `"memory"` was ever accepted. Prefer `default` + `stores`, which is how a
34
+ * pluggable backend is configured everywhere else and what lets the
35
+ * environment choose.
28
36
  */
29
37
  driver?: "memory";
30
38
  }
31
- /**
32
- * BayProvider — registers a default in-memory `QueueManager` in the
33
- * host container so apps that don't need custom driver wiring can
34
- * `import queue from '@c9up/bay/services/main'` and dispatch
35
- * straight away. Job handlers are still registered manually via
36
- * `queue.register(name, handler)` — that's intrinsic to the queue
37
- * design (handlers are app-defined, not config-driven).
38
- *
39
- * Apps with non-trivial wiring (Redis driver, custom queue config)
40
- * can ignore this provider and bind their own `QueueManager` instance
41
- * in the container; the `services/main` proxy resolves whatever is
42
- * registered.
43
- *
44
- * // reamrc.ts
45
- * providers: [() => import('@c9up/bay/provider')]
46
- *
47
- * // start/queue.ts
48
- * import queue from '@c9up/bay/services/main'
49
- *
50
- * queue.register('send-email', new SendEmailJob())
51
- * await queue.dispatch('send-email', { to: 'user@example.com' })
52
- */
53
39
  export default class BayProvider {
40
+ #private;
54
41
  protected app: BayAppContext;
55
42
  constructor(app: BayAppContext);
56
43
  register(): void;
57
44
  boot(): Promise<void>;
45
+ /**
46
+ * Stop the worker the app started, and let the job in flight finish.
47
+ *
48
+ * A worker polls on a timer. Left running, it survives a dev reload, a test
49
+ * teardown and a SIGTERM — so the old process keeps pulling jobs the new
50
+ * one is also pulling, and the same job runs twice.
51
+ */
58
52
  shutdown(): Promise<void>;
59
53
  }
60
54
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"BayProvider.d.ts","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,UAAU,YAAY;IACrB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjD;AACD,UAAU,cAAc;IACvB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AACD,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;IACnB,SAAS,CAAC,GAAG,EAAE,aAAa;gBAAlB,GAAG,EAAE,aAAa;IAExC,QAAQ,IAAI,IAAI;IAiBV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B"}
1
+ {"version":3,"file":"BayProvider.d.ts","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;GAKG;AACH,UAAU,YAAY;IACrB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjD;AACD,UAAU,cAAc;IACvB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AACD,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC3C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CAClB;AAoED,MAAM,CAAC,OAAO,OAAO,WAAW;;IACnB,SAAS,CAAC,GAAG,EAAE,aAAa;gBAAlB,GAAG,EAAE,aAAa;IAExC,QAAQ,IAAI,IAAI;IAaV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B;;;;;;OAMG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAS/B"}
@@ -1,6 +1,6 @@
1
1
  import { MemoryDriver } from "./drivers/MemoryDriver.js";
2
2
  import { QueueManager } from "./QueueManager.js";
3
- import { setQueue } from "./services/main.js";
3
+ import { clearQueue, getQueue, setQueue } from "./services/main.js";
4
4
  /**
5
5
  * BayProvider — registers a default in-memory `QueueManager` in the
6
6
  * host container so apps that don't need custom driver wiring can
@@ -23,6 +23,40 @@ import { setQueue } from "./services/main.js";
23
23
  * queue.register('send-email', new SendEmailJob())
24
24
  * await queue.dispatch('send-email', { to: 'user@example.com' })
25
25
  */
26
+ /**
27
+ * The driver the config asks for.
28
+ *
29
+ * `default` + `stores` first — the form an environment variable can steer. The
30
+ * `driver` key is the single-store form kept for configs written against it.
31
+ * Naming a store that does not exist throws rather than falling back to memory:
32
+ * an application that meant to queue in Redis and silently got an in-process
33
+ * queue would only find out when a restart dropped every pending job.
34
+ */
35
+ function buildDriver(config) {
36
+ const stores = config?.stores;
37
+ const name = config?.default;
38
+ if (stores && name !== undefined) {
39
+ const selected = stores[name];
40
+ if (!selected) {
41
+ const known = Object.keys(stores);
42
+ throw new Error(`[bay] config.queue names the store '${name}', which is not in \`stores\`. ` +
43
+ (known.length > 0
44
+ ? `Declared: ${known.join(", ")}.`
45
+ : "`stores` is empty — declare one with stores.memory() or stores.redis()."));
46
+ }
47
+ return selected();
48
+ }
49
+ if (stores && name === undefined) {
50
+ throw new Error("[bay] config.queue declares `stores` but no `default` naming which one to use. " +
51
+ `Set default to one of: ${Object.keys(stores).join(", ")}.`);
52
+ }
53
+ const driverName = config?.driver ?? "memory";
54
+ if (driverName !== "memory") {
55
+ throw new Error(`[bay] Unsupported driver '${driverName}' — name it under \`stores\` instead: ` +
56
+ "stores: { redis: stores.redis({ connection: 'main' }) }.");
57
+ }
58
+ return new MemoryDriver();
59
+ }
26
60
  export default class BayProvider {
27
61
  app;
28
62
  constructor(app) {
@@ -31,20 +65,35 @@ export default class BayProvider {
31
65
  register() {
32
66
  this.app.container.singleton(QueueManager, () => {
33
67
  const config = this.app.config.get("queue");
34
- const driverName = config?.driver ?? "memory";
35
- if (driverName !== "memory") {
36
- throw new Error(`[bay] Unsupported driver '${driverName}' for default provider — ` +
37
- "wire QueueManager yourself in start/queue.ts for non-memory drivers.");
38
- }
39
- return new QueueManager(new MemoryDriver());
68
+ return new QueueManager(buildDriver(config));
40
69
  });
41
70
  this.app.container.singleton("queue", () => this.app.container.resolve(QueueManager));
42
71
  }
72
+ /** The queue THIS provider booted — not whatever the module singleton holds. */
73
+ #queue;
43
74
  async boot() {
44
75
  // Populate the `@c9up/bay/services/main` singleton so apps can
45
76
  // `import queue from '@c9up/bay/services/main'` from anywhere.
46
- setQueue(await this.app.container.resolve(QueueManager));
77
+ this.#queue = await this.app.container.resolve(QueueManager);
78
+ setQueue(this.#queue);
79
+ }
80
+ /**
81
+ * Stop the worker the app started, and let the job in flight finish.
82
+ *
83
+ * A worker polls on a timer. Left running, it survives a dev reload, a test
84
+ * teardown and a SIGTERM — so the old process keeps pulling jobs the new
85
+ * one is also pulling, and the same job runs twice.
86
+ */
87
+ async shutdown() {
88
+ if (!this.#queue)
89
+ return;
90
+ await this.#queue.stop();
91
+ // Two applications can share a process — parallel tests, a hot reload.
92
+ // The module singleton holds whichever booted last, so it is only ours
93
+ // to clear while it still points at the queue this provider booted.
94
+ if (getQueue() === this.#queue)
95
+ clearQueue();
96
+ this.#queue = undefined;
47
97
  }
48
- async shutdown() { }
49
98
  }
50
99
  //# sourceMappingURL=BayProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BayProvider.js","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAkC9C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;IACT;IAAtB,YAAsB,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAE5C,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,YAAY,EAAE,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAoB,OAAO,CAAC,CAAC;YAC/D,MAAM,UAAU,GAAG,MAAM,EAAE,MAAM,IAAI,QAAQ,CAAC;YAC9C,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CACd,6BAA6B,UAAU,2BAA2B;oBACjE,sEAAsE,CACvE,CAAC;YACH,CAAC;YACD,OAAO,IAAI,YAAY,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,CAC1C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAe,YAAY,CAAC,CACtD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACT,+DAA+D;QAC/D,+DAA+D;QAC/D,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAe,YAAY,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,QAAQ,KAAmB,CAAC;CAClC"}
1
+ {"version":3,"file":"BayProvider.js","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"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"}
@@ -37,10 +37,6 @@ export interface QueueDriver {
37
37
  }
38
38
  export declare class QueueManager {
39
39
  #private;
40
- private driver;
41
- private handlers;
42
- private running;
43
- private inflightPromise;
44
40
  constructor(driver: QueueDriver);
45
41
  /** Register a job handler. */
46
42
  register(name: string, handler: JobHandler | (new () => JobHandler)): void;
@@ -66,7 +62,13 @@ export declare class QueueManager {
66
62
  recoverStale(): Promise<number>;
67
63
  /** Await the currently in-flight processOne, if any. */
68
64
  drain(): Promise<void>;
69
- /** Stop the worker. */
65
+ /**
66
+ * Stop the worker and wait for it to actually be gone.
67
+ *
68
+ * Awaits the LOOP, not just the job in flight: a stop that returns while
69
+ * the loop is still sleeping leaves a worker running past the teardown that
70
+ * asked it to stop.
71
+ */
70
72
  stop(): Promise<void>;
71
73
  /** Get failed jobs. */
72
74
  failedJobs(): Promise<Job[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"QueueManager.d.ts","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACjC;AAED,qBAAa,YAAY;;IACxB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CACL;IACX,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,eAAe,CAAiC;gBAE5C,MAAM,EAAE,WAAW;IAI/B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,CAAC,UAAU,UAAU,CAAC,GAAG,IAAI;IAI1E,mCAAmC;IAC7B,QAAQ,CACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAChC,OAAO,CAAC,MAAM,CAAC;IAkBlB,yCAAyC;IACnC,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAwCpC;;;;;OAKG;IACG,IAAI,CAAC,cAAc,SAAO,EAAE,cAAc,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA8CzE;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAIrC,wDAAwD;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,uBAAuB;IACjB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,uBAAuB;IACjB,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAIlC,sBAAsB;IAChB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG7B"}
1
+ {"version":3,"file":"QueueManager.d.ts","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACjC;AAED,qBAAa,YAAY;;gBAUZ,MAAM,EAAE,WAAW;IAI/B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,CAAC,UAAU,UAAU,CAAC,GAAG,IAAI;IAI1E,mCAAmC;IAC7B,QAAQ,CACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAChC,OAAO,CAAC,MAAM,CAAC;IAkBlB,yCAAyC;IACnC,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IA2CpC;;;;;OAKG;IACG,IAAI,CAAC,cAAc,SAAO,EAAE,cAAc,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA6EzE;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAIrC,wDAAwD;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;;;;;OAMG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,uBAAuB;IACjB,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAIlC,sBAAsB;IAChB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG7B"}
@@ -7,16 +7,20 @@
7
7
  * queue.work()
8
8
  */
9
9
  export class QueueManager {
10
- driver;
11
- handlers = new Map();
12
- running = false;
13
- inflightPromise = null;
10
+ #driver;
11
+ #handlers = new Map();
12
+ #running = false;
13
+ /** The running loop, so `stop()` can wait for it to finish. */
14
+ #loopPromise;
15
+ /** Cuts the sleep between polls short. */
16
+ #wake;
17
+ #inflightPromise = null;
14
18
  constructor(driver) {
15
- this.driver = driver;
19
+ this.#driver = driver;
16
20
  }
17
21
  /** Register a job handler. */
18
22
  register(name, handler) {
19
- this.handlers.set(name, handler);
23
+ this.#handlers.set(name, handler);
20
24
  }
21
25
  /** Dispatch a job to the queue. */
22
26
  async dispatch(name, payload, options) {
@@ -33,18 +37,18 @@ export class QueueManager {
33
37
  status: "pending",
34
38
  createdAt: Date.now(),
35
39
  };
36
- await this.driver.push(job);
40
+ await this.#driver.push(job);
37
41
  return id;
38
42
  }
39
43
  /** Process the next job in the queue. */
40
44
  async processOne() {
41
- const job = await this.driver.pop();
45
+ const job = await this.#driver.pop();
42
46
  if (!job)
43
47
  return false;
44
- const handlerOrClass = this.handlers.get(job.name);
48
+ const handlerOrClass = this.#handlers.get(job.name);
45
49
  if (!handlerOrClass) {
46
50
  process.stderr.write(`QueueManager: no handler registered for job '${job.name}'\n`);
47
- await this.driver.fail(job, `No handler registered for job: ${job.name}`);
51
+ await this.#driver.fail(job, `No handler registered for job: ${job.name}`);
48
52
  return true;
49
53
  }
50
54
  const handler = typeof handlerOrClass === "function"
@@ -56,18 +60,18 @@ export class QueueManager {
56
60
  try {
57
61
  await handler.handle(job.payload);
58
62
  job.status = "completed";
59
- await this.driver.complete(job);
63
+ await this.#driver.complete(job);
60
64
  }
61
65
  catch (err) {
62
66
  const errorMsg = err instanceof Error ? err.message : String(err);
63
67
  if (job.attempts < job.maxAttempts) {
64
68
  job.status = "pending";
65
- await this.driver.retry(job);
69
+ await this.#driver.retry(job);
66
70
  }
67
71
  else {
68
72
  job.status = "failed";
69
73
  job.error = errorMsg;
70
- await this.driver.fail(job, errorMsg);
74
+ await this.#driver.fail(job, errorMsg);
71
75
  }
72
76
  }
73
77
  return true;
@@ -85,33 +89,64 @@ export class QueueManager {
85
89
  if (recoverStaleMs <= 0) {
86
90
  throw new Error("recoverStaleMs must be positive");
87
91
  }
88
- if (this.running) {
92
+ if (this.#running) {
89
93
  throw new Error("QueueManager is already running");
90
94
  }
91
- this.running = true;
95
+ this.#running = true;
96
+ const loop = this.#loop(pollIntervalMs, recoverStaleMs);
97
+ this.#loopPromise = loop;
98
+ try {
99
+ await loop;
100
+ }
101
+ finally {
102
+ this.#loopPromise = undefined;
103
+ }
104
+ }
105
+ /**
106
+ * The polling loop itself.
107
+ *
108
+ * Between jobs it sleeps, and that sleep is CANCELLABLE: `stop()` wakes it
109
+ * rather than waiting out the interval. Without that, stopping returned
110
+ * while the loop was still pending — up to a full poll interval of a worker
111
+ * that was supposed to be gone, and a timer holding the process open.
112
+ */
113
+ async #loop(pollIntervalMs, recoverStaleMs) {
92
114
  await this.#tryRecoverStale();
93
115
  let lastRecover = Date.now();
94
- while (this.running) {
116
+ while (this.#running) {
95
117
  try {
96
- this.inflightPromise = this.processOne();
97
- const processed = await this.inflightPromise;
98
- if (!processed) {
99
- await new Promise((r) => setTimeout(r, pollIntervalMs));
100
- }
118
+ this.#inflightPromise = this.processOne();
119
+ const processed = await this.#inflightPromise;
120
+ if (!processed)
121
+ await this.#sleep(pollIntervalMs);
101
122
  }
102
123
  catch (err) {
103
124
  process.stderr.write(`QueueManager processOne error: ${err instanceof Error ? err.message : String(err)}\n`);
104
- await new Promise((r) => setTimeout(r, pollIntervalMs));
125
+ await this.#sleep(pollIntervalMs);
105
126
  }
106
127
  finally {
107
- this.inflightPromise = null;
128
+ this.#inflightPromise = null;
108
129
  }
109
- if (this.running && Date.now() - lastRecover >= recoverStaleMs) {
130
+ if (this.#running && Date.now() - lastRecover >= recoverStaleMs) {
110
131
  await this.#tryRecoverStale();
111
132
  lastRecover = Date.now();
112
133
  }
113
134
  }
114
135
  }
136
+ /** Wait, unless `stop()` says otherwise first. */
137
+ #sleep(ms) {
138
+ return new Promise((resolve) => {
139
+ const timer = setTimeout(() => {
140
+ this.#wake = undefined;
141
+ resolve();
142
+ }, ms);
143
+ this.#wake = () => {
144
+ clearTimeout(timer);
145
+ this.#wake = undefined;
146
+ resolve();
147
+ };
148
+ });
149
+ }
115
150
  /** recoverStale() wrapper that swallows driver errors — used by the work loop. */
116
151
  async #tryRecoverStale() {
117
152
  try {
@@ -128,26 +163,35 @@ export class QueueManager {
128
163
  * Called automatically by work(); also safe to schedule manually.
129
164
  */
130
165
  async recoverStale() {
131
- return (await this.driver.recoverStale?.()) ?? 0;
166
+ return (await this.#driver.recoverStale?.()) ?? 0;
132
167
  }
133
168
  /** Await the currently in-flight processOne, if any. */
134
169
  async drain() {
135
- if (this.inflightPromise) {
136
- await this.inflightPromise.catch(() => { });
170
+ if (this.#inflightPromise) {
171
+ await this.#inflightPromise.catch(() => { });
137
172
  }
138
173
  }
139
- /** Stop the worker. */
174
+ /**
175
+ * Stop the worker and wait for it to actually be gone.
176
+ *
177
+ * Awaits the LOOP, not just the job in flight: a stop that returns while
178
+ * the loop is still sleeping leaves a worker running past the teardown that
179
+ * asked it to stop.
180
+ */
140
181
  async stop() {
141
- this.running = false;
182
+ this.#running = false;
183
+ this.#wake?.();
142
184
  await this.drain();
185
+ if (this.#loopPromise)
186
+ await this.#loopPromise.catch(() => { });
143
187
  }
144
188
  /** Get failed jobs. */
145
189
  async failedJobs() {
146
- return this.driver.failed();
190
+ return this.#driver.failed();
147
191
  }
148
192
  /** Get queue size. */
149
193
  async size() {
150
- return this.driver.size();
194
+ return this.#driver.size();
151
195
  }
152
196
  }
153
197
  //# sourceMappingURL=QueueManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"QueueManager.js","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"QueueManager.js","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `ream configure @c9up/bay` — wire the job queue in one command.
3
+ *
4
+ * The provider alone is not enough: it reads `config/queue.ts`, and a package
5
+ * registered without one falls back to a default that is rarely the one an
6
+ * application wants. Writing both together is what makes `ream add` mean
7
+ * installed AND working.
8
+ */
9
+ interface Codemods {
10
+ addProvider(importPath: string): Promise<void>;
11
+ addEnvVars(vars: Record<string, string>): Promise<void>;
12
+ writeFile(filePath: string, content: string, options?: {
13
+ force?: boolean;
14
+ }): Promise<void>;
15
+ }
16
+ export declare function configure(codemods: Codemods): Promise<void>;
17
+ export {};
18
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,UAAU,QAAQ;IACjB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,SAAS,CACR,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBjE"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `ream configure @c9up/bay` — wire the job queue in one command.
3
+ *
4
+ * The provider alone is not enough: it reads `config/queue.ts`, and a package
5
+ * registered without one falls back to a default that is rarely the one an
6
+ * application wants. Writing both together is what makes `ream add` mean
7
+ * installed AND working.
8
+ */
9
+ export async function configure(codemods) {
10
+ // The config below reads these, so they are declared here. Writing the file
11
+ // without them leaves an application whose config asks the environment for
12
+ // something nothing ever put there.
13
+ await codemods.addEnvVars({
14
+ QUEUE_STORE: "memory",
15
+ });
16
+ await codemods.addProvider("@c9up/bay/provider");
17
+ await codemods.writeFile("config/queue.ts", `import { defineConfig, stores } from '@c9up/bay'
18
+ import env from '#start/env'
19
+
20
+ export default defineConfig({
21
+ // Which store to run on. Memory forgets everything on restart, which is
22
+ // what a single process in development wants and nothing else does.
23
+ default: env.get('QUEUE_STORE', 'memory'),
24
+
25
+ stores: {
26
+ memory: stores.memory(),
27
+ redis: stores.redis({ connection: 'main' }),
28
+ },
29
+ })`);
30
+ }
31
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAkB;IACjD,4EAA4E;IAC5E,2EAA2E;IAC3E,oCAAoC;IACpC,MAAM,QAAQ,CAAC,UAAU,CAAC;QACzB,WAAW,EAAE,QAAQ;KACrB,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC;IACjD,MAAM,QAAQ,CAAC,SAAS,CACvB,iBAAiB,EACjB;;;;;;;;;;;;GAYC,CACD,CAAC;AACH,CAAC"}
@@ -41,6 +41,12 @@ export declare class RedisDriver implements QueueDriver {
41
41
  constructor(source: RedisClientSource, options?: {
42
42
  prefix?: string;
43
43
  visibilityTimeoutMs?: number;
44
+ /**
45
+ * Accept the non-atomic pop on a Redis older than 6.2, in
46
+ * production. Off by default: losing an accepted job is a choice a
47
+ * deployment makes, not one a version check makes for it.
48
+ */
49
+ allowNonAtomicPop?: boolean;
44
50
  });
45
51
  push(job: Job): Promise<void>;
46
52
  pop(): Promise<Job | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"RedisDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/RedisDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC3B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,CACL,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,OAAO,EACtB,EAAE,EAAE,MAAM,GAAG,OAAO,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACzC;AAcD;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAC1B,WAAW,GACX,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;AA6B9C,qBAAa,WAAY,YAAW,WAAW;;gBAsC7C,MAAM,EAAE,iBAAiB,EACzB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE;IA8BtD,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7B,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAuC1B,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5C,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IA8D/B,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAexB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAI7B"}
1
+ {"version":3,"file":"RedisDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/RedisDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC3B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,CACL,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,OAAO,EACtB,EAAE,EAAE,MAAM,GAAG,OAAO,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACzC;AAcD;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAC1B,WAAW,GACX,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;AAmD9C,qBAAa,WAAY,YAAW,WAAW;;gBAsC7C,MAAM,EAAE,iBAAiB,EACzB,OAAO,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B;;;;WAIG;QACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC5B;IAkCI,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7B,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAkD1B,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5C,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IA8D/B,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAexB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAI7B"}