glasswork 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/types.d.ts +6 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/email/compiler/code-generator.d.ts +11 -0
- package/dist/email/compiler/code-generator.d.ts.map +1 -0
- package/dist/email/compiler/code-generator.js +319 -0
- package/dist/email/compiler/code-generator.js.map +1 -0
- package/dist/http/route-helpers.d.ts +4 -9
- package/dist/http/route-helpers.d.ts.map +1 -1
- package/dist/http/route-helpers.js +1 -0
- package/dist/http/route-helpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/jobs/define-job.d.ts +25 -0
- package/dist/jobs/define-job.d.ts.map +1 -0
- package/dist/jobs/define-job.js +12 -0
- package/dist/jobs/define-job.js.map +1 -0
- package/dist/jobs/drivers/mock-driver.d.ts +38 -0
- package/dist/jobs/drivers/mock-driver.d.ts.map +1 -0
- package/dist/jobs/drivers/mock-driver.js +76 -0
- package/dist/jobs/drivers/mock-driver.js.map +1 -0
- package/dist/jobs/drivers/sqs-driver.d.ts +52 -0
- package/dist/jobs/drivers/sqs-driver.d.ts.map +1 -0
- package/dist/jobs/drivers/sqs-driver.js +191 -0
- package/dist/jobs/drivers/sqs-driver.js.map +1 -0
- package/dist/jobs/errors.d.ts +40 -0
- package/dist/jobs/errors.d.ts.map +1 -0
- package/dist/jobs/errors.js +61 -0
- package/dist/jobs/errors.js.map +1 -0
- package/dist/jobs/index.d.ts +11 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +10 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/job-registry.d.ts +33 -0
- package/dist/jobs/job-registry.d.ts.map +1 -0
- package/dist/jobs/job-registry.js +56 -0
- package/dist/jobs/job-registry.js.map +1 -0
- package/dist/jobs/job-service.d.ts +44 -0
- package/dist/jobs/job-service.d.ts.map +1 -0
- package/dist/jobs/job-service.js +105 -0
- package/dist/jobs/job-service.js.map +1 -0
- package/dist/jobs/periodic-job.d.ts +6 -0
- package/dist/jobs/periodic-job.d.ts.map +1 -0
- package/dist/jobs/periodic-job.js +11 -0
- package/dist/jobs/periodic-job.js.map +1 -0
- package/dist/jobs/schedule-constants.d.ts +9 -0
- package/dist/jobs/schedule-constants.d.ts.map +1 -0
- package/dist/jobs/schedule-constants.js +9 -0
- package/dist/jobs/schedule-constants.js.map +1 -0
- package/dist/jobs/scheduler.d.ts +17 -0
- package/dist/jobs/scheduler.d.ts.map +1 -0
- package/dist/jobs/scheduler.js +47 -0
- package/dist/jobs/scheduler.js.map +1 -0
- package/dist/jobs/types.d.ts +156 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +2 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/jobs/utils.d.ts +24 -0
- package/dist/jobs/utils.d.ts.map +1 -0
- package/dist/jobs/utils.js +69 -0
- package/dist/jobs/utils.js.map +1 -0
- package/dist/jobs/worker.d.ts +36 -0
- package/dist/jobs/worker.d.ts.map +1 -0
- package/dist/jobs/worker.js +260 -0
- package/dist/jobs/worker.js.map +1 -0
- package/dist/list-query/schema-helpers.d.ts +32 -34
- package/dist/list-query/schema-helpers.d.ts.map +1 -1
- package/dist/list-query/schema-helpers.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Duration, EnqueueResult, JobDefinition, QueueDriver } from './types.js';
|
|
2
|
+
export interface JobServiceConfig {
|
|
3
|
+
/** Default queue name when a job does not specify one */
|
|
4
|
+
defaultQueue?: string;
|
|
5
|
+
/** Override the payload size limit (defaults to 256KB) */
|
|
6
|
+
maxPayloadBytes?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface JobServiceHooks {
|
|
9
|
+
/** Called after a job is successfully enqueued */
|
|
10
|
+
onEnqueued?: <TPayload>(job: JobDefinition<TPayload>, payload: TPayload, result: EnqueueResult) => Promise<void> | void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Service responsible for enqueuing jobs.
|
|
14
|
+
*/
|
|
15
|
+
export declare class JobService {
|
|
16
|
+
private readonly driver;
|
|
17
|
+
private readonly hooks?;
|
|
18
|
+
private readonly defaultQueue;
|
|
19
|
+
private readonly maxPayloadBytes;
|
|
20
|
+
constructor(driver: QueueDriver, config?: JobServiceConfig, hooks?: JobServiceHooks | undefined);
|
|
21
|
+
/**
|
|
22
|
+
* Enqueue a job for immediate processing.
|
|
23
|
+
*/
|
|
24
|
+
enqueue<TPayload>(job: JobDefinition<TPayload>, payload: TPayload): Promise<EnqueueResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Enqueue a job to run after a delay.
|
|
27
|
+
*/
|
|
28
|
+
enqueueIn<TPayload>(job: JobDefinition<TPayload>, payload: TPayload, delay: Duration): Promise<EnqueueResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Enqueue a job to run at a specific time.
|
|
31
|
+
*/
|
|
32
|
+
enqueueAt<TPayload>(job: JobDefinition<TPayload>, payload: TPayload, at: Date): Promise<EnqueueResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Enqueue multiple jobs sequentially.
|
|
35
|
+
*/
|
|
36
|
+
enqueueBatch<TPayload>(jobs: Array<{
|
|
37
|
+
job: JobDefinition<TPayload>;
|
|
38
|
+
payload: TPayload;
|
|
39
|
+
}>): Promise<EnqueueResult[]>;
|
|
40
|
+
private validatePayload;
|
|
41
|
+
private validatePayloadSize;
|
|
42
|
+
private getJobId;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=job-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-service.d.ts","sourceRoot":"","sources":["../../src/jobs/job-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAKtF,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,UAAU,CAAC,EAAE,CAAC,QAAQ,EACpB,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC5B,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,aAAa,KAClB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,UAAU;IAKnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IANzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAGtB,MAAM,EAAE,WAAW,EACpC,MAAM,GAAE,gBAAqB,EACZ,KAAK,CAAC,EAAE,eAAe,YAAA;IAM1C;;OAEG;IACG,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBhG;;OAEG;IACG,SAAS,CAAC,QAAQ,EACtB,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC5B,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,QAAQ,GACd,OAAO,CAAC,aAAa,CAAC;IA0BzB;;OAEG;IACG,SAAS,CAAC,QAAQ,EACtB,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC5B,OAAO,EAAE,QAAQ,EACjB,EAAE,EAAE,IAAI,GACP,OAAO,CAAC,aAAa,CAAC;IA0BzB;;OAEG;IACG,YAAY,CAAC,QAAQ,EACzB,IAAI,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;QAAC,OAAO,EAAE,QAAQ,CAAA;KAAE,CAAC,GAC/D,OAAO,CAAC,aAAa,EAAE,CAAC;IAI3B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,QAAQ;CAMjB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { safeParse } from 'valibot';
|
|
2
|
+
import { InvalidJobPayloadError, PayloadTooLargeError } from './errors.js';
|
|
3
|
+
import { calculatePayloadSizeBytes, durationToSeconds } from './utils.js';
|
|
4
|
+
const DEFAULT_MAX_PAYLOAD_BYTES = 256 * 1024; // 256KB
|
|
5
|
+
/**
|
|
6
|
+
* Service responsible for enqueuing jobs.
|
|
7
|
+
*/
|
|
8
|
+
export class JobService {
|
|
9
|
+
driver;
|
|
10
|
+
hooks;
|
|
11
|
+
defaultQueue;
|
|
12
|
+
maxPayloadBytes;
|
|
13
|
+
constructor(driver, config = {}, hooks) {
|
|
14
|
+
this.driver = driver;
|
|
15
|
+
this.hooks = hooks;
|
|
16
|
+
this.defaultQueue = config.defaultQueue ?? 'default';
|
|
17
|
+
this.maxPayloadBytes = config.maxPayloadBytes ?? DEFAULT_MAX_PAYLOAD_BYTES;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Enqueue a job for immediate processing.
|
|
21
|
+
*/
|
|
22
|
+
async enqueue(job, payload) {
|
|
23
|
+
this.validatePayload(job, payload);
|
|
24
|
+
this.validatePayloadSize(payload);
|
|
25
|
+
const queue = job.queue ?? this.defaultQueue;
|
|
26
|
+
const jobId = this.getJobId(job, payload);
|
|
27
|
+
const result = await this.driver.enqueue({
|
|
28
|
+
jobName: job.name,
|
|
29
|
+
payload,
|
|
30
|
+
queue,
|
|
31
|
+
jobId,
|
|
32
|
+
});
|
|
33
|
+
await this.hooks?.onEnqueued?.(job, payload, result);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Enqueue a job to run after a delay.
|
|
38
|
+
*/
|
|
39
|
+
async enqueueIn(job, payload, delay) {
|
|
40
|
+
this.validatePayload(job, payload);
|
|
41
|
+
this.validatePayloadSize(payload);
|
|
42
|
+
if (!this.driver.enqueueIn) {
|
|
43
|
+
const seconds = durationToSeconds(delay);
|
|
44
|
+
return this.enqueueAt(job, payload, new Date(Date.now() + seconds * 1000));
|
|
45
|
+
}
|
|
46
|
+
const queue = job.queue ?? this.defaultQueue;
|
|
47
|
+
const jobId = this.getJobId(job, payload);
|
|
48
|
+
const result = await this.driver.enqueueIn({
|
|
49
|
+
jobName: job.name,
|
|
50
|
+
payload,
|
|
51
|
+
queue,
|
|
52
|
+
jobId,
|
|
53
|
+
}, delay);
|
|
54
|
+
await this.hooks?.onEnqueued?.(job, payload, result);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Enqueue a job to run at a specific time.
|
|
59
|
+
*/
|
|
60
|
+
async enqueueAt(job, payload, at) {
|
|
61
|
+
this.validatePayload(job, payload);
|
|
62
|
+
this.validatePayloadSize(payload);
|
|
63
|
+
if (!this.driver.enqueueAt) {
|
|
64
|
+
const delaySeconds = Math.max(0, Math.floor((at.getTime() - Date.now()) / 1000));
|
|
65
|
+
return this.enqueueIn(job, payload, delaySeconds);
|
|
66
|
+
}
|
|
67
|
+
const queue = job.queue ?? this.defaultQueue;
|
|
68
|
+
const jobId = this.getJobId(job, payload);
|
|
69
|
+
const result = await this.driver.enqueueAt({
|
|
70
|
+
jobName: job.name,
|
|
71
|
+
payload,
|
|
72
|
+
queue,
|
|
73
|
+
jobId,
|
|
74
|
+
}, at);
|
|
75
|
+
await this.hooks?.onEnqueued?.(job, payload, result);
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Enqueue multiple jobs sequentially.
|
|
80
|
+
*/
|
|
81
|
+
async enqueueBatch(jobs) {
|
|
82
|
+
return Promise.all(jobs.map(({ job, payload }) => this.enqueue(job, payload)));
|
|
83
|
+
}
|
|
84
|
+
validatePayload(job, payload) {
|
|
85
|
+
if (job.schema) {
|
|
86
|
+
const result = safeParse(job.schema, payload);
|
|
87
|
+
if (!result.success) {
|
|
88
|
+
throw new InvalidJobPayloadError(job.name, result.issues);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
validatePayloadSize(payload) {
|
|
93
|
+
const sizeBytes = calculatePayloadSizeBytes(payload);
|
|
94
|
+
if (sizeBytes > this.maxPayloadBytes) {
|
|
95
|
+
throw new PayloadTooLargeError(sizeBytes, this.maxPayloadBytes);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
getJobId(job, payload) {
|
|
99
|
+
if (job.unique) {
|
|
100
|
+
return job.unique.key(payload);
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=job-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-service.js","sourceRoot":"","sources":["../../src/jobs/job-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { JobDefinition } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Define a periodic job (typically triggered by EventBridge).
|
|
4
|
+
*/
|
|
5
|
+
export declare function definePeriodicJob(config: Pick<JobDefinition<undefined>, 'name' | 'queue' | 'handler'>): JobDefinition<undefined>;
|
|
6
|
+
//# sourceMappingURL=periodic-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"periodic-job.d.ts","sourceRoot":"","sources":["../../src/jobs/periodic-job.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GACnE,aAAa,CAAC,SAAS,CAAC,CAM1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"periodic-job.js","sourceRoot":"","sources":["../../src/jobs/periodic-job.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAoE;IAEpE,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for job scheduling metadata.
|
|
3
|
+
*/
|
|
4
|
+
export declare const RUN_AT_METADATA_KEY = "__glassworkRunAt";
|
|
5
|
+
/**
|
|
6
|
+
* SQS supports a maximum delay of 15 minutes (900 seconds).
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_SQS_DELAY_SECONDS: number;
|
|
9
|
+
//# sourceMappingURL=schedule-constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-constants.d.ts","sourceRoot":"","sources":["../../src/jobs/schedule-constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAU,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for job scheduling metadata.
|
|
3
|
+
*/
|
|
4
|
+
export const RUN_AT_METADATA_KEY = '__glassworkRunAt';
|
|
5
|
+
/**
|
|
6
|
+
* SQS supports a maximum delay of 15 minutes (900 seconds).
|
|
7
|
+
*/
|
|
8
|
+
export const MAX_SQS_DELAY_SECONDS = 15 * 60;
|
|
9
|
+
//# sourceMappingURL=schedule-constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule-constants.js","sourceRoot":"","sources":["../../src/jobs/schedule-constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Logger } from '../utils/logger.js';
|
|
2
|
+
import type { QueueDriver } from './types.js';
|
|
3
|
+
export interface SchedulerConfig {
|
|
4
|
+
tableName: string;
|
|
5
|
+
driver: QueueDriver;
|
|
6
|
+
region: string;
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
logger?: Logger;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Create a scheduler handler that reads due jobs from DynamoDB and enqueues them.
|
|
12
|
+
* Intended to be triggered every minute (e.g., with EventBridge Scheduler).
|
|
13
|
+
*/
|
|
14
|
+
export declare function createSchedulerHandler(config: SchedulerConfig): () => Promise<{
|
|
15
|
+
dispatched: number;
|
|
16
|
+
}>;
|
|
17
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/jobs/scheduler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe;;GAkD7D"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
2
|
+
import { DeleteCommand, DynamoDBDocumentClient, QueryCommand } from '@aws-sdk/lib-dynamodb';
|
|
3
|
+
import { createLogger } from '../utils/logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Create a scheduler handler that reads due jobs from DynamoDB and enqueues them.
|
|
6
|
+
* Intended to be triggered every minute (e.g., with EventBridge Scheduler).
|
|
7
|
+
*/
|
|
8
|
+
export function createSchedulerHandler(config) {
|
|
9
|
+
const logger = config.logger ?? createLogger('JobScheduler');
|
|
10
|
+
const ddbClient = new DynamoDBClient({
|
|
11
|
+
region: config.region,
|
|
12
|
+
...(config.endpoint && { endpoint: config.endpoint }),
|
|
13
|
+
});
|
|
14
|
+
const docClient = DynamoDBDocumentClient.from(ddbClient);
|
|
15
|
+
return async () => {
|
|
16
|
+
const partition = `SCHEDULED#${new Date().toISOString().slice(0, 16)}`;
|
|
17
|
+
const result = await docClient.send(new QueryCommand({
|
|
18
|
+
TableName: config.tableName,
|
|
19
|
+
KeyConditionExpression: 'pk = :pk',
|
|
20
|
+
ExpressionAttributeValues: { ':pk': partition },
|
|
21
|
+
}));
|
|
22
|
+
if (!result.Items || result.Items.length === 0) {
|
|
23
|
+
return { dispatched: 0 };
|
|
24
|
+
}
|
|
25
|
+
let dispatched = 0;
|
|
26
|
+
for (const item of result.Items) {
|
|
27
|
+
const jobName = item.jobName;
|
|
28
|
+
const payload = JSON.parse(item.payload);
|
|
29
|
+
const queue = item.queue ?? 'default';
|
|
30
|
+
const jobId = item.sk;
|
|
31
|
+
await config.driver.enqueue({
|
|
32
|
+
jobName,
|
|
33
|
+
payload,
|
|
34
|
+
queue,
|
|
35
|
+
jobId,
|
|
36
|
+
});
|
|
37
|
+
await docClient.send(new DeleteCommand({
|
|
38
|
+
TableName: config.tableName,
|
|
39
|
+
Key: { pk: partition, sk: jobId },
|
|
40
|
+
}));
|
|
41
|
+
dispatched += 1;
|
|
42
|
+
}
|
|
43
|
+
logger.info('Dispatched scheduled jobs', { dispatched });
|
|
44
|
+
return { dispatched };
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/jobs/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAWlD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAuB;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,YAAY,CAAC,cAAc,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,IAAI,cAAc,CAAC;QACnC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;KACtD,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,sBAAsB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAEzD,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,SAAS,GAAG,aAAa,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAEvE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CACjC,IAAI,YAAY,CAAC;YACf,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,sBAAsB,EAAE,UAAU;YAClC,yBAAyB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;SAChD,CAAC,CACH,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAiB,CAAC;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAiB,CAAC,CAAC;YACnD,MAAM,KAAK,GAAI,IAAI,CAAC,KAAgB,IAAI,SAAS,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,CAAC,EAAY,CAAC;YAEhC,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;gBAC1B,OAAO;gBACP,OAAO;gBACP,KAAK;gBACL,KAAK;aACN,CAAC,CAAC;YAEH,MAAM,SAAS,CAAC,IAAI,CAClB,IAAI,aAAa,CAAC;gBAChB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,GAAG,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE;aAClC,CAAC,CACH,CAAC;YAEF,UAAU,IAAI,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACzD,OAAO,EAAE,UAAU,EAAE,CAAC;IACxB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { BaseIssue, BaseSchema } from 'valibot';
|
|
2
|
+
import type { Logger } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Duration expressed as human-readable string (e.g. "5m") or seconds as number.
|
|
5
|
+
*/
|
|
6
|
+
export type Duration = string | number;
|
|
7
|
+
/**
|
|
8
|
+
* Retry configuration for jobs.
|
|
9
|
+
* Phase 1 does not implement retries but the shape is defined for forward compatibility.
|
|
10
|
+
*/
|
|
11
|
+
export interface RetryConfig {
|
|
12
|
+
/** Maximum retry attempts (default: 3) */
|
|
13
|
+
maxAttempts?: number;
|
|
14
|
+
/** Backoff strategy (default: exponential) */
|
|
15
|
+
backoff?: 'exponential' | 'linear' | 'fixed';
|
|
16
|
+
/** Initial delay between retries (default: 30s) */
|
|
17
|
+
initialDelay?: Duration;
|
|
18
|
+
/** Maximum delay cap (default: 1h) */
|
|
19
|
+
maxDelay?: Duration;
|
|
20
|
+
/** Whether to apply jitter to backoff timing (default: true) */
|
|
21
|
+
jitter?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Execution context passed to job handlers.
|
|
25
|
+
*/
|
|
26
|
+
export interface JobContext {
|
|
27
|
+
/** DI container services (shaped by user module) */
|
|
28
|
+
services: Record<string, unknown>;
|
|
29
|
+
/** Job identifier */
|
|
30
|
+
jobId: string;
|
|
31
|
+
/** Current attempt number (starts at 1) */
|
|
32
|
+
attemptNumber: number;
|
|
33
|
+
/** Timestamp when the job was enqueued */
|
|
34
|
+
enqueuedAt: Date;
|
|
35
|
+
/** Optional logger scoped to the job */
|
|
36
|
+
logger?: Logger;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Job handler signature.
|
|
40
|
+
*/
|
|
41
|
+
export type JobHandler<TPayload> = (payload: TPayload, context: JobContext) => Promise<void> | void;
|
|
42
|
+
/**
|
|
43
|
+
* Job definition with optional schema validation and retry configuration.
|
|
44
|
+
*/
|
|
45
|
+
export interface JobDefinition<TPayload> {
|
|
46
|
+
/** Unique job name */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Target queue (default resolved by JobService) */
|
|
49
|
+
queue?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Dead-letter queue name for operational documentation.
|
|
52
|
+
* Configure the actual DLQ/redrive policy in your queue infrastructure
|
|
53
|
+
* (e.g., SQS redrive policy pointing the primary queue to this DLQ).
|
|
54
|
+
*/
|
|
55
|
+
deadLetterQueue?: string;
|
|
56
|
+
/** Optional payload schema for validation */
|
|
57
|
+
schema?: BaseSchema<TPayload, TPayload, BaseIssue<unknown>>;
|
|
58
|
+
/** Retry behavior (future phases) */
|
|
59
|
+
retry?: RetryConfig;
|
|
60
|
+
/**
|
|
61
|
+
* Optional uniqueness constraint. Intended for FIFO queues.
|
|
62
|
+
* The deduplication key should be stable for the same logical job.
|
|
63
|
+
*/
|
|
64
|
+
unique?: {
|
|
65
|
+
key: (payload: TPayload) => string;
|
|
66
|
+
/** Deduplication window (advisory; FIFO queues cap at 5 minutes) */
|
|
67
|
+
window?: Duration;
|
|
68
|
+
};
|
|
69
|
+
/** Handler invoked by the worker */
|
|
70
|
+
handler: JobHandler<TPayload>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Job message sent to a queue.
|
|
74
|
+
*/
|
|
75
|
+
export interface JobMessage {
|
|
76
|
+
/** Job type identifier */
|
|
77
|
+
jobName: string;
|
|
78
|
+
/** Serialized job payload */
|
|
79
|
+
payload: unknown;
|
|
80
|
+
/** Queue to use (default resolved by JobService/driver) */
|
|
81
|
+
queue?: string;
|
|
82
|
+
/** Optional client-provided job ID */
|
|
83
|
+
jobId?: string;
|
|
84
|
+
/** Additional metadata */
|
|
85
|
+
metadata?: Record<string, string>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Result returned after enqueuing a job.
|
|
89
|
+
*/
|
|
90
|
+
export interface EnqueueResult {
|
|
91
|
+
/** Queue-assigned message ID */
|
|
92
|
+
messageId: string;
|
|
93
|
+
/** Generated or provided job ID */
|
|
94
|
+
jobId: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Options for receiving jobs from a queue.
|
|
98
|
+
*/
|
|
99
|
+
export interface ReceiveOptions {
|
|
100
|
+
/** Queue name to poll */
|
|
101
|
+
queue?: string;
|
|
102
|
+
/** Visibility timeout in seconds */
|
|
103
|
+
visibilityTimeout?: number;
|
|
104
|
+
/** Maximum number of messages to receive */
|
|
105
|
+
maxMessages?: number;
|
|
106
|
+
/** Long polling wait time in seconds */
|
|
107
|
+
waitTimeSeconds?: number;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Job representation when received from a driver.
|
|
111
|
+
*/
|
|
112
|
+
export interface ReceivedJob {
|
|
113
|
+
/** Parsed message */
|
|
114
|
+
message: JobMessage & {
|
|
115
|
+
jobId: string;
|
|
116
|
+
};
|
|
117
|
+
/** Driver-specific receipt handle */
|
|
118
|
+
receiptHandle?: string;
|
|
119
|
+
/** Raw driver payload */
|
|
120
|
+
raw?: unknown;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Queue driver interface.
|
|
124
|
+
*
|
|
125
|
+
* Methods beyond `enqueue` are placeholders for later phases and may throw
|
|
126
|
+
* until implemented by a driver.
|
|
127
|
+
*/
|
|
128
|
+
export interface QueueDriver {
|
|
129
|
+
/** Driver name for logging/debugging */
|
|
130
|
+
readonly name: string;
|
|
131
|
+
/**
|
|
132
|
+
* Enqueue a job for immediate processing.
|
|
133
|
+
*/
|
|
134
|
+
enqueue(message: JobMessage): Promise<EnqueueResult>;
|
|
135
|
+
/**
|
|
136
|
+
* Enqueue a job for processing at a specific time.
|
|
137
|
+
*/
|
|
138
|
+
enqueueAt?(message: JobMessage, at: Date): Promise<EnqueueResult>;
|
|
139
|
+
/**
|
|
140
|
+
* Enqueue a job for processing after a delay.
|
|
141
|
+
*/
|
|
142
|
+
enqueueIn?(message: JobMessage, delay: Duration): Promise<EnqueueResult>;
|
|
143
|
+
/**
|
|
144
|
+
* Receive jobs from the queue (for worker processing).
|
|
145
|
+
*/
|
|
146
|
+
receive?(options?: ReceiveOptions): Promise<ReceivedJob[]>;
|
|
147
|
+
/**
|
|
148
|
+
* Acknowledge successful job completion.
|
|
149
|
+
*/
|
|
150
|
+
ack?(job: ReceivedJob): Promise<void>;
|
|
151
|
+
/**
|
|
152
|
+
* Reject a job (will be retried or sent to DLQ).
|
|
153
|
+
*/
|
|
154
|
+
nack?(job: ReceivedJob, error?: Error): Promise<void>;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Duration } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate a unique job identifier.
|
|
4
|
+
*/
|
|
5
|
+
export declare function generateJobId(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Calculate the size of a payload once serialized.
|
|
8
|
+
*/
|
|
9
|
+
export declare function calculatePayloadSizeBytes(payload: unknown): number;
|
|
10
|
+
/**
|
|
11
|
+
* Parse a duration value into seconds.
|
|
12
|
+
* Supports number (seconds) or string like "5m", "30s", "1h", "1d".
|
|
13
|
+
*/
|
|
14
|
+
export declare function durationToSeconds(duration: Duration): number;
|
|
15
|
+
/**
|
|
16
|
+
* Simple exponential backoff with optional jitter.
|
|
17
|
+
*/
|
|
18
|
+
export declare function computeBackoffSeconds(attemptNumber: number, config: {
|
|
19
|
+
baseDelaySeconds: number;
|
|
20
|
+
maxDelaySeconds?: number;
|
|
21
|
+
strategy?: 'exponential' | 'linear' | 'fixed';
|
|
22
|
+
jitter?: boolean;
|
|
23
|
+
}): number;
|
|
24
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/jobs/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAIlE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA2B5D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE;IACN,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GACA,MAAM,CAoBR"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Generate a unique job identifier.
|
|
4
|
+
*/
|
|
5
|
+
export function generateJobId() {
|
|
6
|
+
if (typeof randomUUID === 'function') {
|
|
7
|
+
return randomUUID();
|
|
8
|
+
}
|
|
9
|
+
return `job-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Calculate the size of a payload once serialized.
|
|
13
|
+
*/
|
|
14
|
+
export function calculatePayloadSizeBytes(payload) {
|
|
15
|
+
const serialized = JSON.stringify(payload);
|
|
16
|
+
const encoded = new TextEncoder().encode(serialized ?? 'null');
|
|
17
|
+
return encoded.length;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a duration value into seconds.
|
|
21
|
+
* Supports number (seconds) or string like "5m", "30s", "1h", "1d".
|
|
22
|
+
*/
|
|
23
|
+
export function durationToSeconds(duration) {
|
|
24
|
+
if (typeof duration === 'number') {
|
|
25
|
+
return duration;
|
|
26
|
+
}
|
|
27
|
+
const trimmed = duration.trim();
|
|
28
|
+
const match = /^(\d+(?:\.\d+)?)([smhd]?)$/i.exec(trimmed);
|
|
29
|
+
if (!match) {
|
|
30
|
+
throw new Error(`Invalid duration format: "${duration}"`);
|
|
31
|
+
}
|
|
32
|
+
const value = Number.parseFloat(match[1]);
|
|
33
|
+
const unit = match[2].toLowerCase();
|
|
34
|
+
switch (unit) {
|
|
35
|
+
case 's':
|
|
36
|
+
case '':
|
|
37
|
+
return value;
|
|
38
|
+
case 'm':
|
|
39
|
+
return value * 60;
|
|
40
|
+
case 'h':
|
|
41
|
+
return value * 60 * 60;
|
|
42
|
+
case 'd':
|
|
43
|
+
return value * 60 * 60 * 24;
|
|
44
|
+
default:
|
|
45
|
+
throw new Error(`Unsupported duration unit: "${unit}"`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Simple exponential backoff with optional jitter.
|
|
50
|
+
*/
|
|
51
|
+
export function computeBackoffSeconds(attemptNumber, config) {
|
|
52
|
+
const { baseDelaySeconds, maxDelaySeconds, strategy = 'exponential', jitter = true } = config;
|
|
53
|
+
let delay = baseDelaySeconds;
|
|
54
|
+
if (strategy === 'exponential') {
|
|
55
|
+
delay = baseDelaySeconds * 2 ** (attemptNumber - 1);
|
|
56
|
+
}
|
|
57
|
+
else if (strategy === 'linear') {
|
|
58
|
+
delay = baseDelaySeconds * attemptNumber;
|
|
59
|
+
}
|
|
60
|
+
if (maxDelaySeconds) {
|
|
61
|
+
delay = Math.min(delay, maxDelaySeconds);
|
|
62
|
+
}
|
|
63
|
+
if (jitter) {
|
|
64
|
+
const random = Math.random();
|
|
65
|
+
delay = delay * (0.5 + random / 2); // between 0.5x and 1x
|
|
66
|
+
}
|
|
67
|
+
return Math.max(0, Math.floor(delay));
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/jobs/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;QACrC,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;IACD,OAAO,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,CAAC;IAC/D,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAkB;IAClD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,6BAA6B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,GAAG,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAEpC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,GAAG,CAAC;QACT,KAAK,EAAE;YACL,OAAO,KAAK,CAAC;QACf,KAAK,GAAG;YACN,OAAO,KAAK,GAAG,EAAE,CAAC;QACpB,KAAK,GAAG;YACN,OAAO,KAAK,GAAG,EAAE,GAAG,EAAE,CAAC;QACzB,KAAK,GAAG;YACN,OAAO,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QAC9B;YACE,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,GAAG,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,aAAqB,EACrB,MAKC;IAED,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,GAAG,aAAa,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAE9F,IAAI,KAAK,GAAG,gBAAgB,CAAC;IAC7B,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;QAC/B,KAAK,GAAG,gBAAgB,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;SAAM,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,KAAK,GAAG,gBAAgB,GAAG,aAAa,CAAC;IAC3C,CAAC;IAED,IAAI,eAAe,EAAE,CAAC;QACpB,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7B,KAAK,GAAG,KAAK,GAAG,CAAC,GAAG,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB;IAC5D,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Context, EventBridgeEvent, SQSEvent } from 'aws-lambda';
|
|
2
|
+
import type { ModuleConfig } from '../core/types.js';
|
|
3
|
+
import { type Logger } from '../utils/logger.js';
|
|
4
|
+
import type { JobContext, QueueDriver } from './types.js';
|
|
5
|
+
interface WorkerHooks {
|
|
6
|
+
onJobStart?: (job: JobExecution, context: JobContext) => Promise<void> | void;
|
|
7
|
+
onJobComplete?: (job: JobExecution, context: JobContext) => Promise<void> | void;
|
|
8
|
+
onJobFailed?: (job: JobExecution, context: JobContext, error: Error) => Promise<void> | void;
|
|
9
|
+
onJobDeadLetter?: (job: JobExecution, context: JobContext, error: Error) => Promise<void> | void;
|
|
10
|
+
}
|
|
11
|
+
export interface WorkerConfig {
|
|
12
|
+
module: ModuleConfig;
|
|
13
|
+
driver?: QueueDriver;
|
|
14
|
+
hooks?: WorkerHooks;
|
|
15
|
+
logger?: Logger;
|
|
16
|
+
}
|
|
17
|
+
export interface JobExecution {
|
|
18
|
+
jobId: string;
|
|
19
|
+
jobName: string;
|
|
20
|
+
payload: unknown;
|
|
21
|
+
attemptNumber: number;
|
|
22
|
+
enqueuedAt: Date;
|
|
23
|
+
metadata?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Bootstrap a worker Lambda handler for processing jobs.
|
|
27
|
+
*/
|
|
28
|
+
export declare function bootstrapWorker(config: WorkerConfig): (event: SQSEvent | EventBridgeEvent<string, unknown> | Record<string, unknown>, _ctx?: Context) => Promise<{
|
|
29
|
+
batchItemFailures: import("aws-lambda").SQSBatchItemFailure[];
|
|
30
|
+
success?: undefined;
|
|
31
|
+
} | {
|
|
32
|
+
success: boolean;
|
|
33
|
+
batchItemFailures?: undefined;
|
|
34
|
+
}>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAoB,QAAQ,EAAa,MAAM,YAAY,CAAC;AAGnG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAI/D,OAAO,KAAK,EAAE,UAAU,EAAiB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,UAAU,WAAW;IACnB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9E,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7F,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAClG;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAUD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,IAKhD,OAAO,QAAQ,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7E,OAAO,OAAO;;;;;;GA4CjB"}
|