@gobing-ai/ts-infra 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -20
- package/dist/event-bus/event-bus.d.ts.map +1 -1
- package/dist/event-bus/event-bus.js +4 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/job-queue/db-job-queue.d.ts +40 -0
- package/dist/job-queue/db-job-queue.d.ts.map +1 -0
- package/dist/job-queue/db-job-queue.js +154 -0
- package/dist/job-queue/index.d.ts +1 -0
- package/dist/job-queue/index.d.ts.map +1 -1
- package/dist/job-queue/index.js +1 -0
- package/dist/job-queue/types.d.ts +3 -5
- package/dist/job-queue/types.d.ts.map +1 -1
- package/dist/job-queue/types.js +2 -4
- package/dist/logger.js +5 -6
- package/dist/scheduler/cloudflare.d.ts +0 -4
- package/dist/scheduler/cloudflare.d.ts.map +1 -1
- package/dist/scheduler/cloudflare.js +10 -1
- package/dist/scheduler/factory.d.ts +0 -3
- package/dist/scheduler/factory.d.ts.map +1 -1
- package/dist/scheduler/factory.js +5 -14
- package/dist/scheduler/node.d.ts +0 -4
- package/dist/scheduler/node.d.ts.map +1 -1
- package/dist/scheduler/node.js +11 -0
- package/dist/telemetry/config.d.ts +0 -5
- package/dist/telemetry/config.d.ts.map +1 -1
- package/dist/telemetry/config.js +0 -3
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/telemetry/metrics.d.ts +1 -10
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +9 -26
- package/dist/telemetry/otel-node.d.ts +37 -0
- package/dist/telemetry/otel-node.d.ts.map +1 -0
- package/dist/telemetry/otel-node.js +85 -0
- package/dist/telemetry/sdk.d.ts +12 -1
- package/dist/telemetry/sdk.d.ts.map +1 -1
- package/dist/telemetry/sdk.js +17 -27
- package/package.json +36 -7
- package/src/event-bus/event-bus.ts +4 -0
- package/src/index.ts +2 -11
- package/src/job-queue/db-job-queue.ts +178 -0
- package/src/job-queue/index.ts +1 -0
- package/src/job-queue/types.ts +3 -5
- package/src/logger.ts +4 -4
- package/src/scheduler/cloudflare.ts +8 -1
- package/src/scheduler/factory.ts +2 -15
- package/src/scheduler/node.ts +10 -0
- package/src/telemetry/config.ts +0 -8
- package/src/telemetry/index.ts +0 -6
- package/src/telemetry/metrics.ts +9 -38
- package/src/telemetry/otel-node.ts +116 -0
- package/src/telemetry/sdk.ts +18 -30
- package/dist/events/app-events.d.ts +0 -7
- package/dist/events/app-events.d.ts.map +0 -1
- package/dist/events/app-events.js +0 -4
- package/dist/events/create-system-bus.d.ts +0 -6
- package/dist/events/create-system-bus.d.ts.map +0 -1
- package/dist/events/create-system-bus.js +0 -7
- package/dist/events/index.d.ts +0 -3
- package/dist/events/index.d.ts.map +0 -1
- package/dist/events/index.js +0 -1
- package/src/events/app-events.ts +0 -8
- package/src/events/create-system-bus.ts +0 -8
- package/src/events/index.ts +0 -2
package/README.md
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
# @gobing-ai/ts-infra
|
|
2
2
|
|
|
3
|
-
Infrastructure backbone — typed event bus, job queue
|
|
3
|
+
Infrastructure backbone — typed event bus, DB-backed job queue, cron scheduler, OpenTelemetry telemetry, HTTP API client, and structured logging.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
`ts-infra` provides
|
|
7
|
+
`ts-infra` provides six subsystems that form the application backbone:
|
|
8
8
|
|
|
9
9
|
| Subsystem | Module | Purpose |
|
|
10
10
|
|-----------|--------|---------|
|
|
11
11
|
| **Event Bus** | `event-bus/` | Typed pub/sub with sync + async dispatch, lifecycle self-observability |
|
|
12
|
-
| **
|
|
13
|
-
| **Job Queue** | `job-queue/` | Types for DB-backed job processing (`JobQueue`, `QueueConsumer`, `Job`) |
|
|
12
|
+
| **Job Queue** | `job-queue/` | DB-backed enqueue and consume flow (`DBJobQueue`, `DBQueueConsumer`) plus queue interfaces |
|
|
14
13
|
| **Scheduler** | `scheduler/` | Cron-like scheduled actions — Node (interval), Cloudflare (Cron Triggers), Noop (test) |
|
|
15
|
-
| **Telemetry** | `telemetry/` |
|
|
16
|
-
| **API Client** | `api-client.ts` | Typed HTTP client with OTel tracing,
|
|
14
|
+
| **Telemetry** | `telemetry/` | OTel instrumentation — tracing (`traceAsync`), metrics (12 instruments), SQL sanitizer; opt-in OTLP export via the `/otel-node` subpath |
|
|
15
|
+
| **API Client** | `api-client.ts` | Typed HTTP client with OTel tracing, timeout, and error handling |
|
|
17
16
|
| **Logger** | `logger.ts` | Structured JSON logger with levels, child loggers, and mute toggle |
|
|
18
17
|
|
|
19
18
|
## Architecture
|
|
@@ -32,12 +31,6 @@ classDiagram
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
namespace events {
|
|
36
|
-
class EventFactory {
|
|
37
|
-
+createSystemBus() EventBus
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
34
|
namespace job-queue {
|
|
42
35
|
class JobQueue~T~ {
|
|
43
36
|
<<interface>>
|
|
@@ -51,6 +44,18 @@ classDiagram
|
|
|
51
44
|
+stop() Promise~void~
|
|
52
45
|
+stats() Promise~QueueStats~
|
|
53
46
|
}
|
|
47
|
+
class DBJobQueue~T~ {
|
|
48
|
+
+enqueue(type, payload, opts?) Promise~string~
|
|
49
|
+
+enqueueBatch(jobs) Promise~string[]~
|
|
50
|
+
+stats() Promise~QueueStats~
|
|
51
|
+
}
|
|
52
|
+
class DBQueueConsumer~T~ {
|
|
53
|
+
+register(type, handler) void
|
|
54
|
+
+start() Promise~void~
|
|
55
|
+
+stop() Promise~void~
|
|
56
|
+
+processOnce() Promise~number~
|
|
57
|
+
+stats() Promise~QueueStats~
|
|
58
|
+
}
|
|
54
59
|
class Job~T~ {
|
|
55
60
|
<<interface>>
|
|
56
61
|
}
|
|
@@ -92,12 +97,16 @@ classDiagram
|
|
|
92
97
|
+getActiveSpan() Span
|
|
93
98
|
}
|
|
94
99
|
class Metrics {
|
|
95
|
-
+
|
|
96
|
-
+
|
|
97
|
-
+
|
|
100
|
+
+getHttpClientRequestTotal() Counter
|
|
101
|
+
+getHttpClientRequestDuration() Histogram
|
|
102
|
+
+getEventbusEmitsTotal() Counter
|
|
98
103
|
+getQueueJobEnqueuedTotal() Counter
|
|
99
104
|
+getSchedulerJobExecutedTotal() Counter
|
|
100
105
|
}
|
|
106
|
+
class OtelNode {
|
|
107
|
+
+initNodeTelemetry(opts) void
|
|
108
|
+
+shutdownNodeTelemetry() Promise~void~
|
|
109
|
+
}
|
|
101
110
|
class DbSanitize {
|
|
102
111
|
+sanitizeSql(sql) string
|
|
103
112
|
+extractSqlOperation(sql) string
|
|
@@ -141,6 +150,8 @@ classDiagram
|
|
|
141
150
|
SchedulerFactory --> CloudflareSchedulerAdapter
|
|
142
151
|
SchedulerFactory --> NoopSchedulerAdapter
|
|
143
152
|
EventBus --> JobQueue : "async dispatch"
|
|
153
|
+
DBJobQueue --> JobQueue : "implements"
|
|
154
|
+
DBQueueConsumer --> QueueConsumer : "implements"
|
|
144
155
|
EventBus --> Logger : "self-observability"
|
|
145
156
|
APIClient --> Tracing : "traceAsync"
|
|
146
157
|
APIClient --> Metrics : "counters + histograms"
|
|
@@ -182,7 +193,11 @@ bus.once('user.signed_up', () => console.log('one-time'));
|
|
|
182
193
|
**Lifecycle events** — inject a second `EventBus` to observe bus internals:
|
|
183
194
|
|
|
184
195
|
```ts
|
|
185
|
-
|
|
196
|
+
type LifecycleEvents = {
|
|
197
|
+
'bus.emit.done': (detail: { event: string; syncCount: number; asyncCount: number; emitDurationMs: number }) => void;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const lifecycleBus = new EventBus<LifecycleEvents>();
|
|
186
201
|
lifecycleBus.on('bus.emit.done', (detail) => {
|
|
187
202
|
// { event, syncCount, asyncCount, emitDurationMs, errors }
|
|
188
203
|
metrics.recordEmit(detail);
|
|
@@ -191,6 +206,45 @@ lifecycleBus.on('bus.emit.done', (detail) => {
|
|
|
191
206
|
const bus = new EventBus<AppEvents>({ lifecycleBus });
|
|
192
207
|
```
|
|
193
208
|
|
|
209
|
+
### Job Queue — DB-backed async work
|
|
210
|
+
|
|
211
|
+
`DBJobQueue` and `DBQueueConsumer` run over `@gobing-ai/ts-db`'s `QueueJobDao`. Use this when event handlers, schedulers, or API handlers need durable background work with retries.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import { createDbAdapter, QueueJobDao } from '@gobing-ai/ts-db';
|
|
215
|
+
import { DBJobQueue, DBQueueConsumer } from '@gobing-ai/ts-infra';
|
|
216
|
+
|
|
217
|
+
const db = await createDbAdapter({ driver: 'bun-sqlite', url: './jobs.db' });
|
|
218
|
+
const dao = new QueueJobDao(db);
|
|
219
|
+
|
|
220
|
+
const queue = new DBJobQueue<{ to: string; subject: string }>(dao);
|
|
221
|
+
await queue.enqueue(
|
|
222
|
+
'send-email',
|
|
223
|
+
{ to: 'alice@example.com', subject: 'Welcome' },
|
|
224
|
+
{ maxRetries: 5, delay: 1_000, ttlMs: 86_400_000 },
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const consumer = new DBQueueConsumer<{ to: string; subject: string }>(dao, {
|
|
228
|
+
batchSize: 10,
|
|
229
|
+
maxConcurrency: 4,
|
|
230
|
+
visibilityTimeout: 30_000,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
consumer.register('send-email', async (job) => {
|
|
234
|
+
await sendEmail(job.payload.to, job.payload.subject);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
await consumer.start();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
For scheduled drains and tests, call `processOnce()` instead of starting the polling loop:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const processed = await consumer.processOnce();
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The consumer claims ready jobs, resets stuck processing jobs after the visibility timeout, retries failed jobs with exponential backoff, and marks expired jobs failed through `QueueJobDao`.
|
|
247
|
+
|
|
194
248
|
### Scheduler — cron-like actions
|
|
195
249
|
|
|
196
250
|
```ts
|
|
@@ -269,9 +323,7 @@ const reqLog = log.child({ requestId: 'req-123' });
|
|
|
269
323
|
reqLog.error('Validation failed', { field: 'email' });
|
|
270
324
|
// → {...,"category":"auth","requestId":"req-123","field":"email"}
|
|
271
325
|
|
|
272
|
-
//
|
|
273
|
-
import { setLoggerMuted } from './logger.js'; // internal import
|
|
274
|
-
setLoggerMuted(true);
|
|
326
|
+
// In tests, prefer injecting a logger boundary or initializing at a quiet level.
|
|
275
327
|
```
|
|
276
328
|
|
|
277
329
|
### Telemetry — OpenTelemetry
|
|
@@ -288,7 +340,6 @@ initTelemetry({
|
|
|
288
340
|
enabled: true,
|
|
289
341
|
serviceName: 'my-api',
|
|
290
342
|
environment: 'production',
|
|
291
|
-
exporterEndpoint: 'http://otel-collector:4318/v1/traces',
|
|
292
343
|
});
|
|
293
344
|
|
|
294
345
|
// Trace an operation
|
|
@@ -316,6 +367,46 @@ getQueueJobEnqueuedTotal().add(1, { 'queue.job_type': 'send-email' });
|
|
|
316
367
|
getQueueJobProcessingDuration().record(42, { 'queue.job_type': 'send-email' });
|
|
317
368
|
```
|
|
318
369
|
|
|
370
|
+
#### Instrumentation vs. export
|
|
371
|
+
|
|
372
|
+
The main barrel only **instruments** — it records spans and metrics against the
|
|
373
|
+
globally-registered OpenTelemetry provider, and degrades to no-ops when none is
|
|
374
|
+
registered. It does **not** ship an exporter, so it never forces an OTel SDK or a
|
|
375
|
+
collector opinion on consumers. Two ways to actually export:
|
|
376
|
+
|
|
377
|
+
1. **BYO** — register your own OTel SDK (tracer + meter providers, exporters) at
|
|
378
|
+
process startup. ts-infra's spans/metrics flow into it automatically.
|
|
379
|
+
2. **Turnkey OTLP** — opt into the `@gobing-ai/ts-infra/otel-node` subpath, which
|
|
380
|
+
wires Node OTLP/HTTP export for both signals and registers the providers
|
|
381
|
+
globally.
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
// Node-only convenience: pulls the optional OTLP exporter peers.
|
|
385
|
+
import { initNodeTelemetry, shutdownNodeTelemetry } from '@gobing-ai/ts-infra/otel-node';
|
|
386
|
+
|
|
387
|
+
initNodeTelemetry({
|
|
388
|
+
serviceName: 'my-api',
|
|
389
|
+
serviceVersion: '1.4.0',
|
|
390
|
+
endpoint: 'http://otel-collector:4318', // signal paths (/v1/traces, /v1/metrics) appended
|
|
391
|
+
headers: { authorization: `Bearer ${process.env.OTEL_TOKEN}` },
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
process.on('SIGTERM', async () => {
|
|
395
|
+
await shutdownNodeTelemetry(); // flush + drain buffered spans/metrics
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The exporter packages are **optional peers** — only consumers of `/otel-node`
|
|
400
|
+
need them installed. The main `@gobing-ai/ts-infra` import never pulls them, so
|
|
401
|
+
BYO and browser/edge consumers stay lean. To use the subpath:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
bun add @opentelemetry/sdk-trace-node @opentelemetry/sdk-metrics \
|
|
405
|
+
@opentelemetry/resources \
|
|
406
|
+
@opentelemetry/exporter-trace-otlp-http \
|
|
407
|
+
@opentelemetry/exporter-metrics-otlp-http
|
|
408
|
+
```
|
|
409
|
+
|
|
319
410
|
## Usage
|
|
320
411
|
|
|
321
412
|
### Install
|
|
@@ -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;
|
|
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;AAGnD,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,kBAAkB,CAAK;gBAEnB,IAAI,CAAC,EAAE;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,YAAY,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;KAC/C;IAKD,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;IA2E7F,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,4 +1,5 @@
|
|
|
1
1
|
import { getLogger } from '../logger.js';
|
|
2
|
+
import { getEventbusEmitsTotal, getEventbusErrorsTotal } from '../telemetry/metrics.js';
|
|
2
3
|
let _busLogger;
|
|
3
4
|
function busLogger() {
|
|
4
5
|
if (!_busLogger)
|
|
@@ -123,6 +124,9 @@ export class EventBus {
|
|
|
123
124
|
}
|
|
124
125
|
const durationMs = performance.now() - startMs;
|
|
125
126
|
const detail = args.length === 1 ? args[0] : args.length > 1 ? args : undefined;
|
|
127
|
+
getEventbusEmitsTotal().add(1, { event: eventName });
|
|
128
|
+
if (errors > 0)
|
|
129
|
+
getEventbusErrorsTotal().add(errors, { event: eventName });
|
|
126
130
|
this.publishEmitDone({ event: eventName, syncCount, asyncCount, emitDurationMs: durationMs, errors, detail });
|
|
127
131
|
if (syncCount === 0 && asyncCount === 0) {
|
|
128
132
|
busLogger().debug('emit with zero handlers', { event: eventName });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
export { APIClient, type APIClientConfig, APIError, type RequestOptions } from './api-client';
|
|
2
2
|
export { EventBus, type EventMap, type SubscribeOptions } from './event-bus/index';
|
|
3
|
-
export type { AppEvents, AppInternalEvents } from './events/index';
|
|
4
|
-
export { createSystemBus } from './events/index';
|
|
5
3
|
export type { EnqueueOptions, Job, JobHandler, JobQueue, QueueConsumer, QueueConsumerConfig, QueueStats, } from './job-queue/index';
|
|
4
|
+
export { DBJobQueue, DBQueueConsumer } from './job-queue/index';
|
|
6
5
|
export { getLogger, initializeLogger, type Logger, type LogLevel } from './logger';
|
|
7
6
|
export { CloudflareSchedulerAdapter, getSchedulerAdapter, initScheduler, NodeSchedulerAdapter, NoopSchedulerAdapter, type ScheduledAction, type SchedulerAdapter, setSchedulerAdapter, } from './scheduler/index';
|
|
8
|
-
export { addSpanAttributes, addSpanEvent, extractSqlOperation, getActiveSpan,
|
|
7
|
+
export { addSpanAttributes, addSpanEvent, extractSqlOperation, getActiveSpan, getEventbusEmitsTotal, getEventbusErrorsTotal, getHttpClientRequestDuration, getHttpClientRequestErrors, getHttpClientRequestTotal, getQueueJobCompletedTotal, getQueueJobEnqueuedTotal, getQueueJobFailedTotal, getQueueJobProcessingDuration, getSchedulerJobDuration, getSchedulerJobExecutedTotal, getSchedulerJobFailedTotal, getTelemetryConfig, getTracer, initMetrics, initTelemetry, isTelemetryEnabled, sanitizeSql, shutdownMetrics, shutdownTelemetry, type TelemetryConfig, type TelemetryConfigPartial, traceAsync, traceSync, withSpan, } from './telemetry/index';
|
|
9
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
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,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEnF,YAAY,EACR,cAAc,EACd,GAAG,EACH,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,GACb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGhE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGnF,OAAO,EACH,0BAA0B,EAC1B,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,mBAAmB,GACtB,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
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
export { APIClient, APIError } from './api-client.js';
|
|
3
3
|
// Event Bus
|
|
4
4
|
export { EventBus } from './event-bus/index.js';
|
|
5
|
-
|
|
5
|
+
// Job Queue
|
|
6
|
+
export { DBJobQueue, DBQueueConsumer } from './job-queue/index.js';
|
|
6
7
|
// Logger
|
|
7
8
|
export { getLogger, initializeLogger } from './logger.js';
|
|
8
9
|
// Scheduler
|
|
9
10
|
export { CloudflareSchedulerAdapter, getSchedulerAdapter, initScheduler, NodeSchedulerAdapter, NoopSchedulerAdapter, setSchedulerAdapter, } from './scheduler/index.js';
|
|
10
11
|
// Telemetry
|
|
11
|
-
export { addSpanAttributes, addSpanEvent, extractSqlOperation, getActiveSpan,
|
|
12
|
+
export { addSpanAttributes, addSpanEvent, extractSqlOperation, getActiveSpan, getEventbusEmitsTotal, getEventbusErrorsTotal, getHttpClientRequestDuration, getHttpClientRequestErrors, getHttpClientRequestTotal, getQueueJobCompletedTotal, getQueueJobEnqueuedTotal, getQueueJobFailedTotal, getQueueJobProcessingDuration, getSchedulerJobDuration, getSchedulerJobExecutedTotal, getSchedulerJobFailedTotal, getTelemetryConfig, getTracer, initMetrics, initTelemetry, isTelemetryEnabled, sanitizeSql, shutdownMetrics, shutdownTelemetry, traceAsync, traceSync, withSpan, } from './telemetry/index.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { QueueJobDao, QueueStats } from '@gobing-ai/ts-db';
|
|
2
|
+
import type { EnqueueOptions, JobHandler, JobQueue, QueueConsumer, QueueConsumerConfig } from './types';
|
|
3
|
+
/** DB-backed job queue implementation over `@gobing-ai/ts-db`'s `QueueJobDao`. */
|
|
4
|
+
export declare class DBJobQueue<T = unknown> implements JobQueue<T> {
|
|
5
|
+
readonly dao: QueueJobDao;
|
|
6
|
+
constructor(dao: QueueJobDao);
|
|
7
|
+
enqueue(type: string, payload: T, options?: EnqueueOptions): Promise<string>;
|
|
8
|
+
enqueueBatch(jobs: Array<{
|
|
9
|
+
type: string;
|
|
10
|
+
payload: T;
|
|
11
|
+
} & EnqueueOptions>): Promise<string[]>;
|
|
12
|
+
stats(): Promise<QueueStats>;
|
|
13
|
+
}
|
|
14
|
+
/** DB-backed queue consumer with polling, retry, and visibility-timeout handling. */
|
|
15
|
+
export declare class DBQueueConsumer<T = unknown> implements QueueConsumer<T> {
|
|
16
|
+
private readonly dao;
|
|
17
|
+
private readonly handlers;
|
|
18
|
+
private readonly pollInterval;
|
|
19
|
+
private readonly batchSize;
|
|
20
|
+
private readonly maxConcurrency;
|
|
21
|
+
private readonly visibilityTimeout;
|
|
22
|
+
private readonly baseDelay;
|
|
23
|
+
private readonly maxDelay;
|
|
24
|
+
private readonly drainTimeoutMs;
|
|
25
|
+
private timer;
|
|
26
|
+
private running;
|
|
27
|
+
private inFlight;
|
|
28
|
+
constructor(dao: QueueJobDao, config?: QueueConsumerConfig);
|
|
29
|
+
register(type: string, handler: JobHandler<T>): void;
|
|
30
|
+
start(): Promise<void>;
|
|
31
|
+
stop(): Promise<void>;
|
|
32
|
+
stats(): Promise<QueueStats>;
|
|
33
|
+
/** Process one batch immediately. Useful for tests, schedulers, and manual drains. */
|
|
34
|
+
processOnce(): Promise<number>;
|
|
35
|
+
private schedule;
|
|
36
|
+
private poll;
|
|
37
|
+
private processJob;
|
|
38
|
+
private failOrRetry;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=db-job-queue.d.ts.map
|
|
@@ -0,0 +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;AAOhF,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;IAyBpC,OAAO,CAAC,QAAQ;YAMF,IAAI;YASJ,UAAU;YAoBV,WAAW;CAY5B"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { getQueueJobCompletedTotal, getQueueJobEnqueuedTotal, getQueueJobFailedTotal, getQueueJobProcessingDuration, } from '../telemetry/metrics.js';
|
|
2
|
+
/** DB-backed job queue implementation over `@gobing-ai/ts-db`'s `QueueJobDao`. */
|
|
3
|
+
export class DBJobQueue {
|
|
4
|
+
dao;
|
|
5
|
+
constructor(dao) {
|
|
6
|
+
this.dao = dao;
|
|
7
|
+
}
|
|
8
|
+
async enqueue(type, payload, options) {
|
|
9
|
+
const id = await this.dao.enqueue(type, payload, options);
|
|
10
|
+
getQueueJobEnqueuedTotal().add(1, { type });
|
|
11
|
+
return id;
|
|
12
|
+
}
|
|
13
|
+
async enqueueBatch(jobs) {
|
|
14
|
+
const ids = await this.dao.enqueueBatch(jobs);
|
|
15
|
+
getQueueJobEnqueuedTotal().add(jobs.length);
|
|
16
|
+
return ids;
|
|
17
|
+
}
|
|
18
|
+
async stats() {
|
|
19
|
+
return this.dao.getStats();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** DB-backed queue consumer with polling, retry, and visibility-timeout handling. */
|
|
23
|
+
export class DBQueueConsumer {
|
|
24
|
+
dao;
|
|
25
|
+
handlers = new Map();
|
|
26
|
+
pollInterval;
|
|
27
|
+
batchSize;
|
|
28
|
+
maxConcurrency;
|
|
29
|
+
visibilityTimeout;
|
|
30
|
+
baseDelay;
|
|
31
|
+
maxDelay;
|
|
32
|
+
drainTimeoutMs;
|
|
33
|
+
timer = null;
|
|
34
|
+
running = false;
|
|
35
|
+
inFlight = 0;
|
|
36
|
+
constructor(dao, config = {}) {
|
|
37
|
+
this.dao = dao;
|
|
38
|
+
this.pollInterval = config.pollInterval ?? 1_000;
|
|
39
|
+
this.batchSize = config.batchSize ?? 10;
|
|
40
|
+
this.maxConcurrency = config.maxConcurrency ?? this.batchSize;
|
|
41
|
+
this.visibilityTimeout = config.visibilityTimeout ?? 30_000;
|
|
42
|
+
this.baseDelay = config.baseDelay ?? 1_000;
|
|
43
|
+
this.maxDelay = config.maxDelay ?? 60_000;
|
|
44
|
+
this.drainTimeoutMs = config.drainTimeoutMs ?? 30_000;
|
|
45
|
+
}
|
|
46
|
+
register(type, handler) {
|
|
47
|
+
this.handlers.set(type, handler);
|
|
48
|
+
}
|
|
49
|
+
async start() {
|
|
50
|
+
if (this.running)
|
|
51
|
+
return;
|
|
52
|
+
this.running = true;
|
|
53
|
+
this.schedule(0);
|
|
54
|
+
}
|
|
55
|
+
async stop() {
|
|
56
|
+
this.running = false;
|
|
57
|
+
if (this.timer !== null) {
|
|
58
|
+
clearTimeout(this.timer);
|
|
59
|
+
this.timer = null;
|
|
60
|
+
}
|
|
61
|
+
const deadline = Date.now() + this.drainTimeoutMs;
|
|
62
|
+
while (this.inFlight > 0 && Date.now() < deadline) {
|
|
63
|
+
await sleep(10);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async stats() {
|
|
67
|
+
return this.dao.getStats();
|
|
68
|
+
}
|
|
69
|
+
/** Process one batch immediately. Useful for tests, schedulers, and manual drains. */
|
|
70
|
+
async processOnce() {
|
|
71
|
+
await this.dao.resetStuckJobs(this.visibilityTimeout);
|
|
72
|
+
await this.dao.failExpiredJobs();
|
|
73
|
+
const jobs = await this.dao.claimReady(this.batchSize);
|
|
74
|
+
let processed = 0;
|
|
75
|
+
for (let index = 0; index < jobs.length; index += this.maxConcurrency) {
|
|
76
|
+
const batch = jobs.slice(index, index + this.maxConcurrency);
|
|
77
|
+
await Promise.all(batch.map(async (job) => {
|
|
78
|
+
this.inFlight += 1;
|
|
79
|
+
try {
|
|
80
|
+
await this.processJob(job);
|
|
81
|
+
processed += 1;
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
this.inFlight -= 1;
|
|
85
|
+
}
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
return processed;
|
|
89
|
+
}
|
|
90
|
+
schedule(delay) {
|
|
91
|
+
this.timer = setTimeout(() => {
|
|
92
|
+
void this.poll();
|
|
93
|
+
}, delay);
|
|
94
|
+
}
|
|
95
|
+
async poll() {
|
|
96
|
+
if (!this.running)
|
|
97
|
+
return;
|
|
98
|
+
try {
|
|
99
|
+
await this.processOnce();
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
if (this.running)
|
|
103
|
+
this.schedule(this.pollInterval);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async processJob(record) {
|
|
107
|
+
const job = toJob(record);
|
|
108
|
+
const handler = this.handlers.get(job.type);
|
|
109
|
+
if (handler === undefined) {
|
|
110
|
+
await this.failOrRetry(job, new Error(`No handler registered for job type "${job.type}"`));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const startMs = performance.now();
|
|
114
|
+
try {
|
|
115
|
+
await handler(job);
|
|
116
|
+
await this.dao.markCompleted(job.id);
|
|
117
|
+
getQueueJobCompletedTotal().add(1, { type: job.type });
|
|
118
|
+
getQueueJobProcessingDuration().record(performance.now() - startMs, { type: job.type });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
getQueueJobProcessingDuration().record(performance.now() - startMs, { type: job.type });
|
|
122
|
+
await this.failOrRetry(job, error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async failOrRetry(job, error) {
|
|
126
|
+
const attempts = job.attempts + 1;
|
|
127
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
128
|
+
if (attempts >= job.maxRetries) {
|
|
129
|
+
await this.dao.markFailed(job.id, attempts, message);
|
|
130
|
+
getQueueJobFailedTotal().add(1, { type: job.type });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const delay = Math.min(this.maxDelay, this.baseDelay * 2 ** Math.max(0, attempts - 1));
|
|
134
|
+
await this.dao.markForRetry(job.id, attempts, message, Date.now() + delay);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function toJob(record) {
|
|
138
|
+
return {
|
|
139
|
+
id: record.id,
|
|
140
|
+
type: record.type,
|
|
141
|
+
payload: JSON.parse(record.payload),
|
|
142
|
+
status: record.status,
|
|
143
|
+
attempts: record.attempts,
|
|
144
|
+
maxRetries: record.maxRetries,
|
|
145
|
+
createdAt: record.createdAt,
|
|
146
|
+
updatedAt: record.updatedAt,
|
|
147
|
+
nextRetryAt: record.nextRetryAt,
|
|
148
|
+
lastError: record.lastError,
|
|
149
|
+
processingAt: record.processingAt,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function sleep(ms) {
|
|
153
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
154
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/job-queue/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,cAAc,EACd,GAAG,EACH,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,GACb,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/job-queue/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7D,YAAY,EACR,cAAc,EACd,GAAG,EACH,UAAU,EACV,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,GACb,MAAM,SAAS,CAAC"}
|
package/dist/job-queue/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DBJobQueue, DBQueueConsumer } from './db-job-queue.js';
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Job queue types for async work processing with retry.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* are provided here so `EventBus` and other infra components can reference
|
|
7
|
-
* the queue contract without a circular dependency on the DB layer.
|
|
4
|
+
* The concrete DB-backed implementations live beside these interfaces in
|
|
5
|
+
* `DBJobQueue` and `DBQueueConsumer`.
|
|
8
6
|
*/
|
|
9
7
|
export interface Job<T = unknown> {
|
|
10
8
|
id: string;
|
|
@@ -30,6 +28,7 @@ export interface JobQueue<T = unknown> {
|
|
|
30
28
|
type: string;
|
|
31
29
|
payload: T;
|
|
32
30
|
} & EnqueueOptions>): Promise<string[]>;
|
|
31
|
+
stats(): Promise<QueueStats>;
|
|
33
32
|
}
|
|
34
33
|
export type JobHandler<T = unknown> = (job: Job<T>) => Promise<void>;
|
|
35
34
|
export interface QueueStats {
|
|
@@ -46,7 +45,6 @@ export interface QueueConsumerConfig {
|
|
|
46
45
|
baseDelay?: number;
|
|
47
46
|
maxDelay?: number;
|
|
48
47
|
drainTimeoutMs?: number;
|
|
49
|
-
maxPollBackoff?: number;
|
|
50
48
|
}
|
|
51
49
|
export interface QueueConsumer<T = unknown> {
|
|
52
50
|
register(type: string, handler: JobHandler<T>): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/job-queue/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/job-queue/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,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,MAAM,WAAW,cAAc;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,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,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,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,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,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"}
|
package/dist/job-queue/types.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Job queue types for async work processing with retry.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* are provided here so `EventBus` and other infra components can reference
|
|
7
|
-
* the queue contract without a circular dependency on the DB layer.
|
|
4
|
+
* The concrete DB-backed implementations live beside these interfaces in
|
|
5
|
+
* `DBJobQueue` and `DBQueueConsumer`.
|
|
8
6
|
*/
|
package/dist/logger.js
CHANGED
|
@@ -13,15 +13,14 @@ const LEVEL_ORDER = {
|
|
|
13
13
|
};
|
|
14
14
|
class ConsoleLogger {
|
|
15
15
|
category;
|
|
16
|
-
minLevel;
|
|
17
16
|
context;
|
|
18
|
-
constructor(category,
|
|
17
|
+
constructor(category, context = {}) {
|
|
19
18
|
this.category = category;
|
|
20
|
-
this.minLevel = minLevel;
|
|
21
19
|
this.context = { category, ...context };
|
|
22
20
|
}
|
|
23
21
|
log(level, msg, data) {
|
|
24
|
-
|
|
22
|
+
// Read the level dynamically so re-initialization affects cached loggers too.
|
|
23
|
+
if (LEVEL_ORDER[level] < LEVEL_ORDER[globalLevel])
|
|
25
24
|
return;
|
|
26
25
|
if (globalMuted)
|
|
27
26
|
return;
|
|
@@ -68,7 +67,7 @@ class ConsoleLogger {
|
|
|
68
67
|
this.log('fatal', msg, data);
|
|
69
68
|
}
|
|
70
69
|
child(context) {
|
|
71
|
-
return new ConsoleLogger(this.category,
|
|
70
|
+
return new ConsoleLogger(this.category, { ...this.context, ...context });
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
const loggers = new Map();
|
|
@@ -87,7 +86,7 @@ export function getLogger(category) {
|
|
|
87
86
|
const existing = loggers.get(category);
|
|
88
87
|
if (existing)
|
|
89
88
|
return existing;
|
|
90
|
-
const logger = new ConsoleLogger(category
|
|
89
|
+
const logger = new ConsoleLogger(category);
|
|
91
90
|
loggers.set(category, logger);
|
|
92
91
|
return logger;
|
|
93
92
|
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cloudflare Workers scheduler adapter using Cron Triggers.
|
|
3
|
-
* Uses minimal local type declarations — no @cloudflare/workers-types dependency.
|
|
4
|
-
*/
|
|
5
1
|
import type { ScheduledAction, SchedulerAdapter } from './types';
|
|
6
2
|
interface CfScheduledEvent {
|
|
7
3
|
cron: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/scheduler/cloudflare.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/scheduler/cloudflare.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEjE,UAAU,gBAAgB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C;AAED,UAAU,cAAc;IACpB,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C;AAED,qBAAa,0BAA2B,YAAW,gBAAgB;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;;IAI9D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAI/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;OAGG;IACH,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI;CAY3E"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workers scheduler adapter using Cron Triggers.
|
|
3
|
+
* Uses minimal local type declarations — no @cloudflare/workers-types dependency.
|
|
4
|
+
*/
|
|
5
|
+
import { getSchedulerJobExecutedTotal, getSchedulerJobFailedTotal } from '../telemetry/metrics.js';
|
|
1
6
|
export class CloudflareSchedulerAdapter {
|
|
2
7
|
entries = new Map();
|
|
3
8
|
constructor() { }
|
|
@@ -18,7 +23,11 @@ export class CloudflareSchedulerAdapter {
|
|
|
18
23
|
handleScheduledEvent(event, ctx) {
|
|
19
24
|
const action = this.entries.get(event.cron);
|
|
20
25
|
if (action) {
|
|
21
|
-
|
|
26
|
+
getSchedulerJobExecutedTotal().add(1, { cron: event.cron });
|
|
27
|
+
ctx.waitUntil(action().catch((error) => {
|
|
28
|
+
getSchedulerJobFailedTotal().add(1, { cron: event.cron });
|
|
29
|
+
throw error;
|
|
30
|
+
}));
|
|
22
31
|
}
|
|
23
32
|
}
|
|
24
33
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scheduler factory — selects adapter based on runtime.
|
|
3
|
-
*/
|
|
4
1
|
import type { ScheduledAction, SchedulerAdapter } from './types';
|
|
5
2
|
export declare function setSchedulerAdapter(adapter: SchedulerAdapter): void;
|
|
6
3
|
/** Reset the scheduler adapter singleton. For testing. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/scheduler/factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/scheduler/factory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAIjE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAEnE;AAED,0DAA0D;AAC1D,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,GAAG,gBAAgB,CAa9F"}
|