@gobing-ai/ts-infra 0.3.10 → 0.3.12

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 (59) hide show
  1. package/README.md +96 -22
  2. package/dist/api-client.d.ts +44 -1
  3. package/dist/api-client.d.ts.map +1 -1
  4. package/dist/api-client.js +144 -22
  5. package/dist/application/index.d.ts.map +1 -1
  6. package/dist/application/index.js +3 -3
  7. package/dist/application/plugins/builtins.d.ts.map +1 -1
  8. package/dist/application/plugins/builtins.js +9 -7
  9. package/dist/application/types.d.ts +1 -1
  10. package/dist/application-node.d.ts +18 -2
  11. package/dist/application-node.d.ts.map +1 -1
  12. package/dist/application-node.js +12 -7
  13. package/dist/event-bus/event-bus.d.ts +19 -2
  14. package/dist/event-bus/event-bus.d.ts.map +1 -1
  15. package/dist/event-bus/event-bus.js +56 -3
  16. package/dist/event-bus/index.d.ts +1 -1
  17. package/dist/event-bus/index.d.ts.map +1 -1
  18. package/dist/event-bus/types.d.ts +18 -0
  19. package/dist/event-bus/types.d.ts.map +1 -1
  20. package/dist/events.d.ts +5 -2
  21. package/dist/events.d.ts.map +1 -1
  22. package/dist/events.js +5 -2
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/job-queue/db-job-queue.d.ts +5 -1
  27. package/dist/job-queue/db-job-queue.d.ts.map +1 -1
  28. package/dist/job-queue/db-job-queue.js +55 -3
  29. package/dist/job-queue/types.d.ts +8 -0
  30. package/dist/job-queue/types.d.ts.map +1 -1
  31. package/dist/scheduler/action.js +1 -1
  32. package/dist/scheduler/node.d.ts.map +1 -1
  33. package/dist/scheduler/node.js +21 -11
  34. package/dist/scheduler/wrap-handler.js +1 -1
  35. package/dist/telemetry/metrics.d.ts.map +1 -1
  36. package/dist/telemetry/metrics.js +21 -1
  37. package/dist/telemetry/sdk.d.ts +7 -2
  38. package/dist/telemetry/sdk.d.ts.map +1 -1
  39. package/dist/telemetry/tracing.d.ts.map +1 -1
  40. package/dist/telemetry/tracing.js +18 -2
  41. package/package.json +5 -5
  42. package/src/api-client.ts +212 -24
  43. package/src/application/index.ts +4 -3
  44. package/src/application/plugins/builtins.ts +9 -7
  45. package/src/application/types.ts +1 -1
  46. package/src/application-node.ts +33 -10
  47. package/src/event-bus/event-bus.ts +63 -4
  48. package/src/event-bus/index.ts +1 -0
  49. package/src/event-bus/types.ts +19 -0
  50. package/src/events.ts +5 -2
  51. package/src/index.ts +9 -1
  52. package/src/job-queue/db-job-queue.ts +59 -4
  53. package/src/job-queue/types.ts +9 -0
  54. package/src/scheduler/action.ts +1 -1
  55. package/src/scheduler/node.ts +20 -12
  56. package/src/scheduler/wrap-handler.ts +1 -1
  57. package/src/telemetry/metrics.ts +20 -1
  58. package/src/telemetry/sdk.ts +7 -2
  59. package/src/telemetry/tracing.ts +20 -2
@@ -177,19 +177,24 @@ export async function runNodeApplication(options) {
177
177
  // ── Node-owned plugins ──────────────────────────────────────────────
178
178
  const plugins = [];
179
179
  let dbAdapter = options.services?.db;
180
- const rawTel = { ...telemetryOpts };
181
180
  // Node OTel telemetry as a failFast plugin
182
- if (rawTel.enabled !== false && rawTel.endpoint) {
181
+ const otlpEndpoint = telemetryOpts.endpoint;
182
+ if (telemetryOpts.enabled !== false && otlpEndpoint) {
183
+ const otlpHeaders = telemetryOpts.headers;
184
+ const serviceName = telemetryOpts.serviceName ?? 'ts-libs';
183
185
  plugins.push({
184
186
  name: 'builtin:node-telemetry',
185
187
  version: '0.0.0',
186
188
  failFast: true,
187
- onLoad: async () => { },
188
- onStart: async () => {
189
+ // Providers must register during loadAll: the OTel metrics API has no
190
+ // proxy provider, so instruments created by telemetryPlugin's onStart
191
+ // (`initMetrics` pre-warm) bind permanently to whatever meter provider
192
+ // is global at that moment. loadAll runs before every onStart.
193
+ onLoad: async () => {
189
194
  initNodeTelemetry({
190
- serviceName: rawTel.serviceName ?? 'ts-libs',
191
- endpoint: rawTel.endpoint,
192
- headers: rawTel.headers,
195
+ serviceName,
196
+ endpoint: otlpEndpoint,
197
+ ...(otlpHeaders ? { headers: otlpHeaders } : {}),
193
198
  });
194
199
  },
195
200
  onStop: async () => {
@@ -1,6 +1,6 @@
1
- import type { JobQueue } from '../job-queue/types';
1
+ import type { JobHandler, JobQueue } from '../job-queue/types';
2
2
  import { type Logger } from '../logger';
3
- import type { BusLifecycleEvents, EventMap, SubscribeOptions } from './types';
3
+ import type { AsyncEventJobPayload, BusLifecycleEvents, EventMap, SubscribeOptions } from './types';
4
4
  /**
5
5
  * Type-safe event bus supporting both synchronous (in-process) and
6
6
  * asynchronous handlers with optional job-queue enqueue instrumentation.
@@ -9,6 +9,7 @@ export declare class EventBus<TEvents extends EventMap> {
9
9
  private readonly syncHandlers;
10
10
  private readonly asyncHandlers;
11
11
  private readonly asyncHandlerIds;
12
+ private readonly asyncHandlersById;
12
13
  private readonly jobQueue;
13
14
  private readonly lifecycleBus;
14
15
  private readonly logger;
@@ -28,6 +29,22 @@ export declare class EventBus<TEvents extends EventMap> {
28
29
  private registerSync;
29
30
  private registerAsync;
30
31
  private getAsyncHandlerId;
32
+ /** Drop a handler's id mappings once it is subscribed to no event at all. */
33
+ private releaseAsyncIdIfUnsubscribed;
34
+ /**
35
+ * Bridge for queue-backed async dispatch: returns a `JobHandler` to
36
+ * register on a queue consumer for each async event's job type. It
37
+ * dispatches the enqueued `{ event, args, handlerId }` payload back to the
38
+ * matching async handler on THIS bus instance.
39
+ *
40
+ * Use named async subscriptions (`SubscribeOptions.name`) when jobs may be
41
+ * consumed after a process restart — anonymous handler ids are
42
+ * process-local and not stable across restarts.
43
+ *
44
+ * @throws when the payload references an unknown handler id, so the job
45
+ * lands in the queue's retry/fail path instead of vanishing silently.
46
+ */
47
+ createJobHandler(): JobHandler<AsyncEventJobPayload>;
31
48
  private publishEmitDone;
32
49
  private publishEmitNoop;
33
50
  private publishHandlerError;
@@ -1 +1 @@
1
- {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/event-bus/event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAER,kBAAkB,EAElB,QAAQ,EAER,gBAAgB,EACnB,MAAM,SAAS,CAAC;AAQjB;;;GAGG;AACH,qBAAa,QAAQ,CAAC,OAAO,SAAS,QAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyD;IACtF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyD;IACvF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiD;IACjF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsC;IACnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,kBAAkB,CAAK;gBACnB,IAAI,CAAC,EAAE;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,YAAY,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB;IAMD,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAQzF,IAAI,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAS3F,GAAG,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAkBjE,kBAAkB,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI;IAUtD,IAAI,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkF7F,aAAa,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM;IAMjF,UAAU,IAAI,MAAM,EAAE;IAOtB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,oBAAoB;CAU/B"}
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/event-bus/event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAER,oBAAoB,EACpB,kBAAkB,EAElB,QAAQ,EAER,gBAAgB,EACnB,MAAM,SAAS,CAAC;AAQjB;;;GAGG;AACH,qBAAa,QAAQ,CAAC,OAAO,SAAS,QAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyD;IACtF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyD;IACvF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6C;IAC7E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6C;IAC/E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsC;IACnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,kBAAkB,CAAK;gBACnB,IAAI,CAAC,EAAE;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,YAAY,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;KACnB;IAMD,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAQzF,IAAI,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI;IAS3F,GAAG,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAmBjE,kBAAkB,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI;IAgBtD,IAAI,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkF7F,aAAa,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM;IAMjF,UAAU,IAAI,MAAM,EAAE;IAOtB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,iBAAiB;IAUzB,6EAA6E;IAC7E,OAAO,CAAC,4BAA4B;IAWpC;;;;;;;;;;;;OAYG;IACH,gBAAgB,IAAI,UAAU,CAAC,oBAAoB,CAAC;IAepD,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,oBAAoB;CAU/B"}
@@ -13,7 +13,8 @@ function busLogger() {
13
13
  export class EventBus {
14
14
  syncHandlers = new Map();
15
15
  asyncHandlers = new Map();
16
- asyncHandlerIds = new WeakMap();
16
+ asyncHandlerIds = new Map();
17
+ asyncHandlersById = new Map();
17
18
  jobQueue;
18
19
  lifecycleBus;
19
20
  logger;
@@ -25,7 +26,7 @@ export class EventBus {
25
26
  }
26
27
  on(event, handler, opts) {
27
28
  if (opts?.async) {
28
- this.registerAsync(event, handler);
29
+ this.registerAsync(event, handler, opts.name);
29
30
  }
30
31
  else {
31
32
  this.registerSync(event, handler);
@@ -52,16 +53,24 @@ export class EventBus {
52
53
  if (asyncSet.size === 0) {
53
54
  this.asyncHandlers.delete(event);
54
55
  }
56
+ this.releaseAsyncIdIfUnsubscribed(handler);
55
57
  }
56
58
  }
57
59
  removeAllListeners(event) {
58
60
  if (event !== undefined) {
59
61
  this.syncHandlers.delete(event);
62
+ const asyncSet = this.asyncHandlers.get(event);
60
63
  this.asyncHandlers.delete(event);
64
+ if (asyncSet) {
65
+ for (const handler of asyncSet)
66
+ this.releaseAsyncIdIfUnsubscribed(handler);
67
+ }
61
68
  }
62
69
  else {
63
70
  this.syncHandlers.clear();
64
71
  this.asyncHandlers.clear();
72
+ this.asyncHandlerIds.clear();
73
+ this.asyncHandlersById.clear();
65
74
  }
66
75
  }
67
76
  async emit(event, ...args) {
@@ -162,13 +171,21 @@ export class EventBus {
162
171
  }
163
172
  set.add(handler);
164
173
  }
165
- registerAsync(event, handler) {
174
+ registerAsync(event, handler, name) {
166
175
  let set = this.asyncHandlers.get(event);
167
176
  if (!set) {
168
177
  set = new Set();
169
178
  this.asyncHandlers.set(event, set);
170
179
  }
171
180
  set.add(handler);
181
+ if (!this.asyncHandlerIds.has(handler)) {
182
+ const id = name ?? `handler-${++this.nextAsyncHandlerId}`;
183
+ if (this.asyncHandlersById.has(id)) {
184
+ throw new Error(`Duplicate async handler name: "${id}"`);
185
+ }
186
+ this.asyncHandlerIds.set(handler, id);
187
+ this.asyncHandlersById.set(id, handler);
188
+ }
172
189
  }
173
190
  getAsyncHandlerId(handler) {
174
191
  const existing = this.asyncHandlerIds.get(handler);
@@ -176,8 +193,44 @@ export class EventBus {
176
193
  return existing;
177
194
  const id = `handler-${++this.nextAsyncHandlerId}`;
178
195
  this.asyncHandlerIds.set(handler, id);
196
+ this.asyncHandlersById.set(id, handler);
179
197
  return id;
180
198
  }
199
+ /** Drop a handler's id mappings once it is subscribed to no event at all. */
200
+ releaseAsyncIdIfUnsubscribed(handler) {
201
+ for (const set of this.asyncHandlers.values()) {
202
+ if (set.has(handler))
203
+ return;
204
+ }
205
+ const id = this.asyncHandlerIds.get(handler);
206
+ if (id !== undefined) {
207
+ this.asyncHandlerIds.delete(handler);
208
+ this.asyncHandlersById.delete(id);
209
+ }
210
+ }
211
+ /**
212
+ * Bridge for queue-backed async dispatch: returns a `JobHandler` to
213
+ * register on a queue consumer for each async event's job type. It
214
+ * dispatches the enqueued `{ event, args, handlerId }` payload back to the
215
+ * matching async handler on THIS bus instance.
216
+ *
217
+ * Use named async subscriptions (`SubscribeOptions.name`) when jobs may be
218
+ * consumed after a process restart — anonymous handler ids are
219
+ * process-local and not stable across restarts.
220
+ *
221
+ * @throws when the payload references an unknown handler id, so the job
222
+ * lands in the queue's retry/fail path instead of vanishing silently.
223
+ */
224
+ createJobHandler() {
225
+ return async (job) => {
226
+ const { event, args, handlerId } = job.payload;
227
+ const handler = this.asyncHandlersById.get(handlerId);
228
+ if (!handler) {
229
+ throw new Error(`No async handler registered for id "${handlerId}" (event "${event}")`);
230
+ }
231
+ await handler(...args);
232
+ };
233
+ }
181
234
  publishEmitDone(detail) {
182
235
  if (this.lifecycleBus) {
183
236
  try {
@@ -1,5 +1,5 @@
1
1
  export { attachDefaultObservers, attachLogObserver, attachTelemetryObserver, createLifecycleBus, } from './default-observers';
2
2
  export { EventBus } from './event-bus';
3
3
  export { attachFileObserver, type FileObserverWriter } from './file-observer';
4
- export type { AsyncEnqueuedDetail, BusLifecycleEvents, EmitDoneDetail, EventMap, HandlerErrorDetail, SubscribeOptions, } from './types';
4
+ export type { AsyncEnqueuedDetail, AsyncEventJobPayload, BusLifecycleEvents, EmitDoneDetail, EventMap, HandlerErrorDetail, SubscribeOptions, } from './types';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/event-bus/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,YAAY,EACR,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,GACnB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/event-bus/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,YAAY,EACR,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,GACnB,MAAM,SAAS,CAAC"}
@@ -7,6 +7,24 @@ export type EventMap = Record<string, (...args: never[]) => void>;
7
7
  export interface SubscribeOptions {
8
8
  /** When true, the handler is dispatched through the async handler path. */
9
9
  async?: boolean;
10
+ /**
11
+ * Stable identifier for an async handler. Used as the `handlerId` in jobs
12
+ * enqueued through an injected `JobQueue`, so a queue consumer (see
13
+ * `EventBus.createJobHandler`) can dispatch jobs back to this handler —
14
+ * including after a process restart. Anonymous async handlers get a
15
+ * process-local generated id that is NOT stable across restarts.
16
+ * Must be unique per bus instance.
17
+ */
18
+ name?: string;
19
+ }
20
+ /** Payload shape the bus enqueues for queue-backed async handlers. */
21
+ export interface AsyncEventJobPayload {
22
+ /** Event name the handler was subscribed to. */
23
+ event: string;
24
+ /** Arguments the event was emitted with. */
25
+ args: unknown[];
26
+ /** Async handler id (subscription `name` or a generated id). */
27
+ handlerId: string;
10
28
  }
11
29
  /** Payload emitted on `bus.emit.done` after synchronous handlers complete. */
12
30
  export interface EmitDoneDetail {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/event-bus/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,6FAA6F;AAC7F,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC;AAElE,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC7B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAID,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,oEAAoE;AACpE,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,yFAAyF;AACzF,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,gEAAgE;AAChE,MAAM,MAAM,kBAAkB,GAAG;IAC7B,eAAe,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACrD,mBAAmB,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC1D,4BAA4B,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACvE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/event-bus/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,6FAA6F;AAC7F,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC;AAElE,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC7B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,sEAAsE;AACtE,MAAM,WAAW,oBAAoB;IACjC,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;CACrB;AAID,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,oEAAoE;AACpE,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,yFAAyF;AACzF,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,gEAAgE;AAChE,MAAM,MAAM,kBAAkB,GAAG;IAC7B,eAAe,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACrD,mBAAmB,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC1D,4BAA4B,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACvE,CAAC"}
package/dist/events.d.ts CHANGED
@@ -2,8 +2,11 @@
2
2
  * Infrastructure-level event definitions for ts-infra observability.
3
3
  *
4
4
  * Mirrors the package-local pattern in `@gobing-ai/ts-ai-runner` (`events.ts`):
5
- * only the events ts-infra components actually emit live here. Application-domain
6
- * events (history import, HTTP server, …) belong to the consuming app, not this
5
+ * these maps define the infra-level event *contract*. ts-infra itself currently
6
+ * emits only `queue.stats` (`QueueStatsAction`) and `scheduler.job.executed`
7
+ * (`wrapScheduledHandler`); the remaining events are contracts for the consuming
8
+ * app or higher-level wiring to emit on the same bus. Application-domain events
9
+ * (history import, HTTP server, …) belong to the consuming app, not this
7
10
  * library. Process events belong to `@gobing-ai/ts-runtime` (the owner of
8
11
  * `ProcessExecutor`) and are intentionally not re-exported here.
9
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,yCAAyC;AACzC,MAAM,WAAW,uBAAuB;IACpC,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,gEAAgE;AAChE,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,4CAA4C;AAC5C,MAAM,WAAW,0BAA0B;IACvC,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,uCAAuC;AACvC,MAAM,WAAW,qBAAqB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACjB;AAID,mEAAmE;AACnE,MAAM,MAAM,QAAQ,GAAG;IACnB,uCAAuC;IACvC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,iCAAiC;IACjC,qBAAqB,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;CACpE,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG;IACtB,8BAA8B;IAC9B,oBAAoB,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,qCAAqC;IACrC,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC,wBAAwB;IACxB,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC,oCAAoC;IACpC,qBAAqB,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACzE,yDAAyD;IACzD,kBAAkB,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC3D,2CAA2C;IAC3C,oBAAoB,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;IAC/D,oDAAoD;IACpD,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,eAAe,GAAG;IAC1B,yDAAyD;IACzD,wBAAwB,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC;CAC1E,CAAC;AAEF,yBAAyB;AACzB,MAAM,MAAM,eAAe,GAAG;IAC1B,wDAAwD;IACxD,mBAAmB,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAChE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,yCAAyC;AACzC,MAAM,WAAW,uBAAuB;IACpC,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,gEAAgE;AAChE,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,4CAA4C;AAC5C,MAAM,WAAW,0BAA0B;IACvC,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,uCAAuC;AACvC,MAAM,WAAW,qBAAqB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACjB;AAID,mEAAmE;AACnE,MAAM,MAAM,QAAQ,GAAG;IACnB,uCAAuC;IACvC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,iCAAiC;IACjC,qBAAqB,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;CACpE,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG;IACtB,8BAA8B;IAC9B,oBAAoB,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,qCAAqC;IACrC,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC,wBAAwB;IACxB,wBAAwB,EAAE,MAAM,IAAI,CAAC;IACrC,oCAAoC;IACpC,qBAAqB,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACzE,yDAAyD;IACzD,kBAAkB,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC3D,2CAA2C;IAC3C,oBAAoB,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;IAC/D,oDAAoD;IACpD,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,eAAe,GAAG;IAC1B,yDAAyD;IACzD,wBAAwB,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC;CAC1E,CAAC;AAEF,yBAAyB;AACzB,MAAM,MAAM,eAAe,GAAG;IAC1B,wDAAwD;IACxD,mBAAmB,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAChE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,eAAe,CAAC"}
package/dist/events.js CHANGED
@@ -2,8 +2,11 @@
2
2
  * Infrastructure-level event definitions for ts-infra observability.
3
3
  *
4
4
  * Mirrors the package-local pattern in `@gobing-ai/ts-ai-runner` (`events.ts`):
5
- * only the events ts-infra components actually emit live here. Application-domain
6
- * events (history import, HTTP server, …) belong to the consuming app, not this
5
+ * these maps define the infra-level event *contract*. ts-infra itself currently
6
+ * emits only `queue.stats` (`QueueStatsAction`) and `scheduler.job.executed`
7
+ * (`wrapScheduledHandler`); the remaining events are contracts for the consuming
8
+ * app or higher-level wiring to emit on the same bus. Application-domain events
9
+ * (history import, HTTP server, …) belong to the consuming app, not this
7
10
  * library. Process events belong to `@gobing-ai/ts-runtime` (the owner of
8
11
  * `ProcessExecutor`) and are intentionally not re-exported here.
9
12
  *
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { APIClient, type APIClientConfig, APIError, type RequestOptions } from './api-client';
2
- export { attachDefaultObservers, attachFileObserver, attachLogObserver, attachTelemetryObserver, createLifecycleBus, EventBus, type EventMap, type FileObserverWriter, type SubscribeOptions, } from './event-bus/index';
1
+ export { APIClient, type APIClientConfig, APIError, type RawHttpResponse, type RawRequestOptions, type RequestOptions, } from './api-client';
2
+ export { type AsyncEventJobPayload, attachDefaultObservers, attachFileObserver, attachLogObserver, attachTelemetryObserver, createLifecycleBus, EventBus, type EventMap, type FileObserverWriter, type SubscribeOptions, } from './event-bus/index';
3
3
  export type { ApiClientEvents, ApiRequestErrorDetail, DbConnectionErrorDetail, DbEvents, InfraEvents, QueueEvents, QueueJobFailedDetail, QueueJobRetryingDetail, SchedulerEvents, SchedulerJobExecutedDetail, } from './events';
4
4
  export type { EnqueueOptions, Job, JobHandler, JobQueue, QueueConsumer, QueueConsumerConfig, QueueStats, } from './job-queue/index';
5
5
  export { getLogger, type InitLoggerOptions, initializeLogger, type Logger, type LogLevel, setLoggerMuted, } from './logger';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9F,OAAO,EACH,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACxB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACR,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,QAAQ,EACR,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,0BAA0B,GAC7B,MAAM,UAAU,CAAC;AAElB,YAAY,EACR,cAAc,EACd,GAAG,EACH,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,GACb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACH,SAAS,EACT,KAAK,iBAAiB,EACtB,gBAAgB,EAChB,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,cAAc,GACjB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACH,cAAc,EACd,KAAK,4BAA4B,EACjC,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,aAAa,EACb,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,oBAAoB,GACvB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACH,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,4BAA4B,EAC5B,0BAA0B,EAC1B,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,UAAU,EACV,SAAS,EACT,QAAQ,GACX,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACH,SAAS,EACT,KAAK,eAAe,EACpB,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACtB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACH,KAAK,oBAAoB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,EAClB,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACxB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACR,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,QAAQ,EACR,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,0BAA0B,GAC7B,MAAM,UAAU,CAAC;AAElB,YAAY,EACR,cAAc,EACd,GAAG,EACH,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,GACb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACH,SAAS,EACT,KAAK,iBAAiB,EACtB,gBAAgB,EAChB,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,cAAc,GACjB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACH,cAAc,EACd,KAAK,4BAA4B,EACjC,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,aAAa,EACb,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,iBAAiB,EACjB,oBAAoB,GACvB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACH,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,4BAA4B,EAC5B,0BAA0B,EAC1B,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,UAAU,EACV,SAAS,EACT,QAAQ,GACX,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // API Client
2
- export { APIClient, APIError } from './api-client.js';
2
+ export { APIClient, APIError, } from './api-client.js';
3
3
  // Event Bus
4
4
  export { attachDefaultObservers, attachFileObserver, attachLogObserver, attachTelemetryObserver, createLifecycleBus, EventBus, } from './event-bus/index.js';
5
5
  // Logger
@@ -1,9 +1,12 @@
1
1
  import type { QueueJobDao, QueueStats } from '@gobing-ai/ts-db';
2
+ import type { EventBus } from '../event-bus/event-bus';
3
+ import type { QueueEvents } from '../events';
2
4
  import type { EnqueueOptions, JobHandler, JobQueue, QueueConsumer, QueueConsumerConfig } from './types';
3
5
  /** DB-backed job queue implementation over `@gobing-ai/ts-db`'s `QueueJobDao`. */
4
6
  export declare class DBJobQueue<T = unknown> implements JobQueue<T> {
5
7
  readonly dao: QueueJobDao;
6
- constructor(dao: QueueJobDao);
8
+ private readonly events?;
9
+ constructor(dao: QueueJobDao, events?: EventBus<QueueEvents> | undefined);
7
10
  enqueue(type: string, payload: T, options?: EnqueueOptions): Promise<string>;
8
11
  enqueueBatch(jobs: Array<{
9
12
  type: string;
@@ -22,6 +25,7 @@ export declare class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
22
25
  private readonly baseDelay;
23
26
  private readonly maxDelay;
24
27
  private readonly drainTimeoutMs;
28
+ private readonly events;
25
29
  private timer;
26
30
  private running;
27
31
  private inFlight;
@@ -1 +1 @@
1
- {"version":3,"file":"db-job-queue.d.ts","sourceRoot":"","sources":["../../src/job-queue/db-job-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAkB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQhF,OAAO,KAAK,EAAE,cAAc,EAAO,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE7G,kFAAkF;AAClF,qBAAa,UAAU,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,QAAQ,CAAC,CAAC,CAAC;IAC3C,QAAQ,CAAC,GAAG,EAAE,WAAW;gBAAhB,GAAG,EAAE,WAAW;IAE/B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAM5E,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAM3F,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;CAGrC;AAED,qFAAqF;AACrF,qBAAa,eAAe,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAc7D,OAAO,CAAC,QAAQ,CAAC,GAAG;IAbxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;gBAGA,GAAG,EAAE,WAAW,EACjC,MAAM,GAAE,mBAAwB;IAWpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAI9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAarB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAIlC,sFAAsF;IAChF,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IA4BpC,OAAO,CAAC,QAAQ;YAMF,IAAI;YASJ,UAAU;YA4BV,WAAW;CAY5B"}
1
+ {"version":3,"file":"db-job-queue.d.ts","sourceRoot":"","sources":["../../src/job-queue/db-job-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAkB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAS7C,OAAO,KAAK,EAAE,cAAc,EAAO,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAQ7G,kFAAkF;AAClF,qBAAa,UAAU,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,QAAQ,CAAC,CAAC,CAAC;IAEnD,QAAQ,CAAC,GAAG,EAAE,WAAW;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBADf,GAAG,EAAE,WAAW,EACR,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,YAAA;IAG7C,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAO5E,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAW3F,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;CAGrC;AAED,qFAAqF;AACrF,qBAAa,eAAe,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAe7D,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;gBAGA,GAAG,EAAE,WAAW,EACjC,MAAM,GAAE,mBAAwB;IAYpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAI9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAerB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAIlC,sFAAsF;IAChF,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IA4BpC,OAAO,CAAC,QAAQ;YAMF,IAAI;YAgBJ,UAAU;YAqCV,WAAW;CA4B5B"}
@@ -1,19 +1,34 @@
1
+ import { getLogger } from '../logger.js';
1
2
  import { getQueueJobCompletedTotal, getQueueJobEnqueuedTotal, getQueueJobFailedTotal, getQueueJobProcessingDuration, } from '../telemetry/metrics.js';
2
3
  import { addSpanAttributes, traceAsync } from '../telemetry/tracing.js';
4
+ let _queueLogger;
5
+ function queueLogger() {
6
+ if (!_queueLogger)
7
+ _queueLogger = getLogger('job-queue');
8
+ return _queueLogger;
9
+ }
3
10
  /** DB-backed job queue implementation over `@gobing-ai/ts-db`'s `QueueJobDao`. */
4
11
  export class DBJobQueue {
5
12
  dao;
6
- constructor(dao) {
13
+ events;
14
+ constructor(dao, events) {
7
15
  this.dao = dao;
16
+ this.events = events;
8
17
  }
9
18
  async enqueue(type, payload, options) {
10
19
  const id = await this.dao.enqueue(type, payload, options);
11
20
  getQueueJobEnqueuedTotal().add(1, { type });
21
+ await this.events?.emit('queue.job.enqueued', { jobId: id, type });
12
22
  return id;
13
23
  }
14
24
  async enqueueBatch(jobs) {
15
25
  const ids = await this.dao.enqueueBatch(jobs);
16
26
  getQueueJobEnqueuedTotal().add(jobs.length);
27
+ if (this.events) {
28
+ for (const [index, jobId] of ids.entries()) {
29
+ await this.events.emit('queue.job.enqueued', { jobId, type: jobs[index]?.type ?? 'unknown' });
30
+ }
31
+ }
17
32
  return ids;
18
33
  }
19
34
  async stats() {
@@ -31,6 +46,7 @@ export class DBQueueConsumer {
31
46
  baseDelay;
32
47
  maxDelay;
33
48
  drainTimeoutMs;
49
+ events;
34
50
  timer = null;
35
51
  running = false;
36
52
  inFlight = 0;
@@ -43,6 +59,7 @@ export class DBQueueConsumer {
43
59
  this.baseDelay = config.baseDelay ?? 1_000;
44
60
  this.maxDelay = config.maxDelay ?? 60_000;
45
61
  this.drainTimeoutMs = config.drainTimeoutMs ?? 30_000;
62
+ this.events = config.events;
46
63
  }
47
64
  register(type, handler) {
48
65
  this.handlers.set(type, handler);
@@ -52,8 +69,10 @@ export class DBQueueConsumer {
52
69
  return;
53
70
  this.running = true;
54
71
  this.schedule(0);
72
+ await this.events?.emit('queue.consumer.started');
55
73
  }
56
74
  async stop() {
75
+ const wasRunning = this.running;
57
76
  this.running = false;
58
77
  if (this.timer !== null) {
59
78
  clearTimeout(this.timer);
@@ -63,6 +82,8 @@ export class DBQueueConsumer {
63
82
  while (this.inFlight > 0 && Date.now() < deadline) {
64
83
  await sleep(10);
65
84
  }
85
+ if (wasRunning)
86
+ await this.events?.emit('queue.consumer.stopped');
66
87
  }
67
88
  async stats() {
68
89
  return this.dao.getStats();
@@ -102,13 +123,30 @@ export class DBQueueConsumer {
102
123
  try {
103
124
  await this.processOnce();
104
125
  }
126
+ catch (error) {
127
+ // The timer fires poll() as a floating promise — a DAO/processing error
128
+ // must be contained here or it becomes an unhandled rejection that can
129
+ // kill the process. Log and let the next cycle retry.
130
+ queueLogger().error('queue poll cycle failed', {
131
+ error: error instanceof Error ? error.message : String(error),
132
+ });
133
+ }
105
134
  finally {
106
135
  if (this.running)
107
136
  this.schedule(this.pollInterval);
108
137
  }
109
138
  }
110
139
  async processJob(record) {
111
- const job = toJob(record);
140
+ let job;
141
+ try {
142
+ job = toJob(record);
143
+ }
144
+ catch (error) {
145
+ // Corrupt payload — the job can never parse; route it through the
146
+ // retry/fail path instead of rejecting the whole batch.
147
+ await this.failOrRetry(record, error);
148
+ return;
149
+ }
112
150
  return traceAsync('queue.job.process', async () => {
113
151
  addSpanAttributes({
114
152
  'queue.job_id': job.id,
@@ -126,6 +164,7 @@ export class DBQueueConsumer {
126
164
  await this.dao.markCompleted(job.id);
127
165
  getQueueJobCompletedTotal().add(1, { type: job.type });
128
166
  getQueueJobProcessingDuration().record(performance.now() - startMs, { type: job.type });
167
+ await this.events?.emit('queue.job.completed', { jobId: job.id, type: job.type });
129
168
  }
130
169
  catch (error) {
131
170
  getQueueJobProcessingDuration().record(performance.now() - startMs, { type: job.type });
@@ -139,10 +178,23 @@ export class DBQueueConsumer {
139
178
  if (attempts >= job.maxRetries) {
140
179
  await this.dao.markFailed(job.id, attempts, message);
141
180
  getQueueJobFailedTotal().add(1, { type: job.type });
181
+ await this.events?.emit('queue.job.failed', {
182
+ jobId: job.id,
183
+ type: job.type,
184
+ error: message,
185
+ attempt: attempts,
186
+ });
142
187
  return;
143
188
  }
144
189
  const delay = Math.min(this.maxDelay, this.baseDelay * 2 ** Math.max(0, attempts - 1));
145
- await this.dao.markForRetry(job.id, attempts, message, Date.now() + delay);
190
+ const nextRetryAt = Date.now() + delay;
191
+ await this.dao.markForRetry(job.id, attempts, message, nextRetryAt);
192
+ await this.events?.emit('queue.job.retrying', {
193
+ jobId: job.id,
194
+ type: job.type,
195
+ attempt: attempts,
196
+ nextRetryAt,
197
+ });
146
198
  }
147
199
  }
148
200
  function toJob(record) {
@@ -4,6 +4,8 @@
4
4
  * The concrete DB-backed implementations live beside these interfaces in
5
5
  * `DBJobQueue` and `DBQueueConsumer`.
6
6
  */
7
+ import type { EventBus } from '../event-bus/event-bus';
8
+ import type { QueueEvents } from '../events';
7
9
  /** A queued job with status tracking, retry metadata, and timestamps. */
8
10
  export interface Job<T = unknown> {
9
11
  id: string;
@@ -51,6 +53,12 @@ export interface QueueConsumerConfig {
51
53
  baseDelay?: number;
52
54
  maxDelay?: number;
53
55
  drainTimeoutMs?: number;
56
+ /**
57
+ * Optional bus for queue lifecycle events: `queue.consumer.started` /
58
+ * `queue.consumer.stopped` and `queue.job.completed` / `queue.job.failed` /
59
+ * `queue.job.retrying`. Omitting it leaves the consumer silent (default).
60
+ */
61
+ events?: EventBus<QueueEvents>;
54
62
  }
55
63
  /** Consumer interface for the job queue — register handlers and control the processing loop. */
56
64
  export interface QueueConsumer<T = unknown> {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/job-queue/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yEAAyE;AACzE,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,OAAO;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACjC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7E,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5F,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC;AAED,iDAAiD;AACjD,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,gGAAgG;AAChG,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/job-queue/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,yEAAyE;AACzE,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,OAAO;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACjC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7E,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5F,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC;AAED,iDAAiD;AACjD,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CAClC;AAED,gGAAgG;AAChG,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAChC"}
@@ -70,7 +70,7 @@ export class QueueStatsAction {
70
70
  const dao = await this.daoProvider();
71
71
  const stats = await dao.getStats();
72
72
  logger.info('Queue stats snapshot', { action: this.name, ...stats });
73
- this.systemBus?.emit('queue.stats', stats);
73
+ await this.systemBus?.emit('queue.stats', stats);
74
74
  }
75
75
  }
76
76
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/scheduler/node.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA6BjE;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,OAAO,CAAS;;IAIxB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAU/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,OAAO,CAAC,UAAU;YAOJ,gBAAgB;CAYjC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/scheduler/node.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAoCjE;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,OAAO,CAAS;;IAIxB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAU/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,OAAO,CAAC,UAAU;YAOJ,gBAAgB;CAYjC"}
@@ -2,24 +2,34 @@
2
2
  * Node.js scheduler adapter using a simple setInterval-based approach.
3
3
  * No external cron library dependency — cron expressions are parsed minimally.
4
4
  */
5
+ import { getLogger } from '../logger.js';
5
6
  import { getSchedulerJobDuration, getSchedulerJobExecutedTotal, getSchedulerJobFailedTotal, } from '../telemetry/metrics.js';
6
7
  /** Simple helper to parse cron-like interval strings into milliseconds. */
7
8
  function parseInterval(cron) {
8
9
  // Support simple patterns: "* * * * *" (every minute), "*/5 * * * *" (every 5 min)
9
10
  // Also support direct ms strings like "60000"
10
- const num = Number(cron);
11
- if (!Number.isNaN(num))
12
- return num;
13
- const parts = cron.trim().split(/\s+/);
14
- if (parts.length === 5 && parts[0] === '*') {
15
- return 60_000; // every minute default
11
+ const trimmed = cron.trim();
12
+ const num = Number(trimmed);
13
+ if (trimmed !== '' && !Number.isNaN(num)) {
14
+ // Guard against 0/negative intervals — setInterval would spin hot.
15
+ if (num > 0)
16
+ return num;
16
17
  }
17
- // */N pattern
18
- const match = parts[0]?.match(/^\*\/(\d+)$/);
19
- if (match) {
20
- return Number(match[1]) * 60_000;
18
+ else {
19
+ const parts = trimmed.split(/\s+/);
20
+ if (parts.length === 5 && parts[0] === '*') {
21
+ return 60_000; // every minute
22
+ }
23
+ // */N pattern
24
+ const match = parts[0]?.match(/^\*\/(\d+)$/);
25
+ if (match && Number(match[1]) > 0) {
26
+ return Number(match[1]) * 60_000;
27
+ }
21
28
  }
22
- return 60_000; // fallback: every minute
29
+ // Real cron field expressions ("0 3 * * *") are NOT supported — running
30
+ // them every minute silently would badly misfire, so warn on fallback.
31
+ getLogger('scheduler.node').warn('Unsupported cron expression — falling back to a 60s interval', { cron });
32
+ return 60_000;
23
33
  }
24
34
  /**
25
35
  * Scheduler adapter for Node.js using a setInterval-based approach.
@@ -34,7 +34,7 @@ export function wrapScheduledHandler(name, action, systemBus) {
34
34
  finally {
35
35
  const durationMs = Math.round(performance.now() - startTime);
36
36
  addSpanAttributes({ 'scheduler.duration_ms': durationMs });
37
- systemBus?.emit('scheduler.job.executed', {
37
+ void systemBus?.emit('scheduler.job.executed', {
38
38
  name,
39
39
  durationMs,
40
40
  ...(execError !== undefined ? { error: execError } : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/telemetry/metrics.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAW,MAAM,oBAAoB,CAAC;AAE3E,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI7D,kFAAkF;AAClF,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AA8BD,kFAAkF;AAClF,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,SAAS,CAExD;AAED,8DAA8D;AAC9D,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAID,yCAAyC;AACzC,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED,4CAA4C;AAC5C,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAID,uCAAuC;AACvC,wBAAgB,wBAAwB,IAAI,OAAO,CAElD;AAED,qDAAqD;AACrD,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED,8DAA8D;AAC9D,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,6DAA6D;AAC7D,wBAAgB,6BAA6B,IAAI,SAAS,CAEzD;AAID,kDAAkD;AAClD,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED,sEAAsE;AACtE,wBAAgB,uBAAuB,IAAI,SAAS,CAEnD;AAED,mDAAmD;AACnD,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAID;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAgBlC;AAED,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAS/C;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAMpC"}
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/telemetry/metrics.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,OAAO,EAAmB,KAAK,SAAS,EAAW,MAAM,oBAAoB,CAAC;AAG5F,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI7D,kFAAkF;AAClF,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAgDD,kFAAkF;AAClF,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,IAAI,SAAS,CAExD;AAED,8DAA8D;AAC9D,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAID,yCAAyC;AACzC,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED,4CAA4C;AAC5C,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAID,uCAAuC;AACvC,wBAAgB,wBAAwB,IAAI,OAAO,CAElD;AAED,qDAAqD;AACrD,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED,8DAA8D;AAC9D,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,6DAA6D;AAC7D,wBAAgB,6BAA6B,IAAI,SAAS,CAEzD;AAID,kDAAkD;AAClD,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED,sEAAsE;AACtE,wBAAgB,uBAAuB,IAAI,SAAS,CAEnD;AAED,mDAAmD;AACnD,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAID;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAgBlC;AAED,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAS/C;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAMpC"}