@queuebase/core 0.0.1

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.
@@ -0,0 +1,15 @@
1
+ import type { AnyJobDefinition } from './router.js';
2
+ import { WEBHOOK_HEADERS } from './webhook.js';
3
+ export { WEBHOOK_HEADERS };
4
+ export interface HandlerRequest {
5
+ body: string;
6
+ signatureHeader: string | null;
7
+ }
8
+ export interface HandlerResponse {
9
+ status: number;
10
+ body: unknown;
11
+ }
12
+ export declare function processJobCallback(router: Record<string, AnyJobDefinition>, request: HandlerRequest, options?: {
13
+ webhookSecret?: string;
14
+ }): Promise<HandlerResponse>;
15
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EAAmB,eAAe,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,EAAE,eAAe,EAAE,CAAC;AAU3B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACxC,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC,OAAO,CAAC,eAAe,CAAC,CAuD1B"}
@@ -0,0 +1,53 @@
1
+ import { verifySignature, WEBHOOK_HEADERS } from './webhook.js';
2
+ export { WEBHOOK_HEADERS };
3
+ export async function processJobCallback(router, request, options) {
4
+ const webhookSecret = options?.webhookSecret ?? process.env.QUEUEBASE_WEBHOOK_SECRET;
5
+ if (webhookSecret) {
6
+ if (!request.signatureHeader) {
7
+ return {
8
+ status: 401,
9
+ body: { success: false, error: 'Missing webhook signature' },
10
+ };
11
+ }
12
+ if (!verifySignature(request.body, request.signatureHeader, webhookSecret)) {
13
+ return {
14
+ status: 401,
15
+ body: { success: false, error: 'Invalid webhook signature' },
16
+ };
17
+ }
18
+ }
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(request.body);
22
+ }
23
+ catch {
24
+ return { status: 400, body: { success: false, error: 'Invalid JSON body' } };
25
+ }
26
+ const { jobId, name, payload, attempt, maxAttempts } = parsed;
27
+ const jobDef = router[name];
28
+ if (!jobDef) {
29
+ return { status: 404, body: { success: false, error: `Unknown job: ${name}` } };
30
+ }
31
+ const parseResult = jobDef.input.safeParse(payload);
32
+ if (!parseResult.success) {
33
+ return {
34
+ status: 400,
35
+ body: { success: false, error: `Invalid payload: ${parseResult.error.message}` },
36
+ };
37
+ }
38
+ const context = {
39
+ input: parseResult.data,
40
+ jobId,
41
+ attempt,
42
+ maxAttempts,
43
+ };
44
+ try {
45
+ const output = await jobDef.handler(context);
46
+ return { status: 200, body: { success: true, output } };
47
+ }
48
+ catch (error) {
49
+ const message = error instanceof Error ? error.message : 'Unknown error';
50
+ return { status: 500, body: { success: false, error: message } };
51
+ }
52
+ }
53
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,EAAE,eAAe,EAAE,CAAC;AAoB3B,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAwC,EACxC,OAAuB,EACvB,OAAoC;IAEpC,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IAErF,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7B,OAAO;gBACL,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,EAAE;aAC7D,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,eAAe,EAAE,aAAa,CAAC,EAAE,CAAC;YAC3E,OAAO;gBACL,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,EAAE;aAC7D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,MAA0B,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAuB,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC;IAC/E,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAE9D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,IAAI,EAAE,EAAE,EAAE,CAAC;IAClF,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QACzB,OAAO;YACL,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE;SACjF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAwB;QACnC,KAAK,EAAE,WAAW,CAAC,IAAI;QACvB,KAAK;QACL,OAAO;QACP,WAAW;KACZ,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;IACnE,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type { JobStatus, BackoffStrategy, EnqueueOptions, JobDefinition, JobContext, QueuedJob, JobResult, QueuebaseConfig, } from './types.js';
2
+ export { job, type InferJobInput, type InferJobOutput } from './job.js';
3
+ export { createJobRouter, type AnyJobDefinition, type JobRouter, type CallableJob, type CallableJobRouter, } from './router.js';
4
+ export { parseDelay, calculateBackoff, generateJobId, generatePublicId } from './utils.js';
5
+ export { signPayload, verifySignature, WEBHOOK_HEADERS } from './webhook.js';
6
+ export { processJobCallback, type HandlerRequest, type HandlerResponse, } from './handler.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,eAAe,EACf,cAAc,EACd,aAAa,EACb,UAAU,EACV,SAAS,EACT,SAAS,EACT,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,GAAG,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGxE,OAAO,EACL,eAAe,EACf,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG3F,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG7E,OAAO,EACL,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // Job creation
2
+ export { job } from './job.js';
3
+ // Router
4
+ export { createJobRouter, } from './router.js';
5
+ // Utilities
6
+ export { parseDelay, calculateBackoff, generateJobId, generatePublicId } from './utils.js';
7
+ // Webhook signing
8
+ export { signPayload, verifySignature, WEBHOOK_HEADERS } from './webhook.js';
9
+ // Handler
10
+ export { processJobCallback, } from './handler.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,eAAe;AACf,OAAO,EAAE,GAAG,EAA2C,MAAM,UAAU,CAAC;AAExE,SAAS;AACT,OAAO,EACL,eAAe,GAKhB,MAAM,aAAa,CAAC;AAErB,YAAY;AACZ,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE3F,kBAAkB;AAClB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE7E,UAAU;AACV,OAAO,EACL,kBAAkB,GAGnB,MAAM,cAAc,CAAC"}
package/dist/job.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { z } from 'zod';
2
+ import type { EnqueueOptions, JobContext } from './types.js';
3
+ import type { AnyJobDefinition } from './router.js';
4
+ /**
5
+ * Creates a job definition with typed input and handler
6
+ */
7
+ export declare function job<TInput, TOutput>(definition: {
8
+ input: z.ZodType<TInput>;
9
+ handler: (ctx: JobContext<TInput>) => Promise<TOutput>;
10
+ defaults?: EnqueueOptions;
11
+ }): AnyJobDefinition;
12
+ /**
13
+ * Infer the input type from a job definition's Zod schema
14
+ */
15
+ export type InferJobInput<T extends AnyJobDefinition> = T['input'] extends z.ZodType<infer I> ? I : never;
16
+ /**
17
+ * Infer the output type from a job definition's handler
18
+ */
19
+ export type InferJobOutput<T extends AnyJobDefinition> = T['handler'] extends (ctx: JobContext<unknown>) => Promise<infer O> ? O : never;
20
+ //# sourceMappingURL=job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;GAEG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE;IAC/C,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,GAAG,gBAAgB,CAInB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,gBAAgB,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GACzF,CAAC,GACD,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,gBAAgB,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAC5E,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,KACrB,OAAO,CAAC,MAAM,CAAC,CAAC,GACjB,CAAC,GACD,KAAK,CAAC"}
package/dist/job.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Creates a job definition with typed input and handler
3
+ */
4
+ export function job(definition) {
5
+ // Cast to AnyJobDefinition for storage in the router
6
+ // The actual types are preserved through the generic inference
7
+ return definition;
8
+ }
9
+ //# sourceMappingURL=job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.js","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,UAAU,GAAG,CAAkB,UAIpC;IACC,qDAAqD;IACrD,+DAA+D;IAC/D,OAAO,UAAyC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type { z } from 'zod';
2
+ import type { EnqueueOptions, JobContext } from './types.js';
3
+ /**
4
+ * A job definition - the basic building block
5
+ */
6
+ export interface AnyJobDefinition {
7
+ input: z.ZodTypeAny;
8
+ handler: (ctx: JobContext<unknown>) => Promise<unknown>;
9
+ defaults?: EnqueueOptions;
10
+ }
11
+ /**
12
+ * A collection of job definitions
13
+ */
14
+ export type JobRouter = Record<string, AnyJobDefinition>;
15
+ /**
16
+ * Creates a typed job router from job definitions
17
+ */
18
+ export declare function createJobRouter<T extends Record<string, AnyJobDefinition>>(jobs: T): T;
19
+ /**
20
+ * Callable job that can be enqueued
21
+ */
22
+ export interface CallableJob<TInput, TOutput> {
23
+ /** Enqueue this job for execution */
24
+ enqueue: (input: TInput, options?: EnqueueOptions) => Promise<{
25
+ jobId: string;
26
+ }>;
27
+ /** The underlying job definition */
28
+ _def: AnyJobDefinition;
29
+ }
30
+ /**
31
+ * Extract input type from a job definition
32
+ */
33
+ type ExtractJobInput<T extends AnyJobDefinition> = T['input'] extends z.ZodType<infer I> ? I : never;
34
+ /**
35
+ * Extract output type from a job definition (from handler return type)
36
+ */
37
+ type ExtractJobOutput<T extends AnyJobDefinition> = T['handler'] extends (ctx: JobContext<unknown>) => Promise<infer O> ? O : never;
38
+ /**
39
+ * Transform a job router into callable jobs
40
+ */
41
+ export type CallableJobRouter<T extends Record<string, AnyJobDefinition>> = {
42
+ [K in keyof T]: CallableJob<ExtractJobInput<T[K]>, ExtractJobOutput<T[K]>>;
43
+ };
44
+ export {};
45
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC;IACpB,OAAO,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEzD;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAEtF;AAED;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,MAAM,EAAE,OAAO;IAC1C,qCAAqC;IACrC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjF,oCAAoC;IACpC,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED;;GAEG;AACH,KAAK,eAAe,CAAC,CAAC,SAAS,gBAAgB,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GACpF,CAAC,GACD,KAAK,CAAC;AAEV;;GAEG;AACH,KAAK,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CACvE,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,KACrB,OAAO,CAAC,MAAM,CAAC,CAAC,GACjB,CAAC,GACD,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI;KACzE,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3E,CAAC"}
package/dist/router.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Creates a typed job router from job definitions
3
+ */
4
+ export function createJobRouter(jobs) {
5
+ return jobs;
6
+ }
7
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,UAAU,eAAe,CAA6C,IAAO;IACjF,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,91 @@
1
+ import type { z } from 'zod';
2
+ /**
3
+ * Status of a job in the queue
4
+ */
5
+ export type JobStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
6
+ /**
7
+ * Backoff strategy for retries
8
+ */
9
+ export type BackoffStrategy = 'linear' | 'exponential';
10
+ /**
11
+ * Options for enqueueing a job
12
+ */
13
+ export interface EnqueueOptions {
14
+ /** Delay before running the job (e.g., '5m', '1h', or milliseconds) */
15
+ delay?: string | number;
16
+ /** Number of retry attempts on failure */
17
+ retries?: number;
18
+ /** Backoff strategy for retries */
19
+ backoff?: BackoffStrategy;
20
+ /** Initial delay between retries in ms (default: 1000) */
21
+ backoffDelay?: number;
22
+ /** Maximum concurrent jobs of this type (per worker) */
23
+ concurrency?: number;
24
+ }
25
+ /**
26
+ * A job definition with typed input/output
27
+ */
28
+ export interface JobDefinition<TInput = unknown, TOutput = unknown> {
29
+ /** Zod schema for validating job input */
30
+ input: z.ZodType<TInput>;
31
+ /** The handler function that executes the job */
32
+ handler: (ctx: JobContext<TInput>) => Promise<TOutput>;
33
+ /** Default options for this job */
34
+ defaults?: EnqueueOptions;
35
+ }
36
+ /**
37
+ * Context passed to job handlers
38
+ */
39
+ export interface JobContext<TInput = unknown> {
40
+ /** The validated input data */
41
+ input: TInput;
42
+ /** Unique ID for this job execution */
43
+ jobId: string;
44
+ /** Current attempt number (1-indexed) */
45
+ attempt: number;
46
+ /** Maximum attempts allowed */
47
+ maxAttempts: number;
48
+ }
49
+ /**
50
+ * Internal representation of a queued job
51
+ */
52
+ export interface QueuedJob {
53
+ id: number;
54
+ publicId: string;
55
+ name: string;
56
+ payload: unknown;
57
+ status: JobStatus;
58
+ attempt: number;
59
+ maxAttempts: number;
60
+ runAt: Date;
61
+ createdAt: Date;
62
+ startedAt: Date | null;
63
+ completedAt: Date | null;
64
+ result: unknown | null;
65
+ error: string | null;
66
+ backoffStrategy: BackoffStrategy;
67
+ backoffDelay: number;
68
+ }
69
+ /**
70
+ * Result of a job execution
71
+ */
72
+ export interface JobResult<TOutput = unknown> {
73
+ success: boolean;
74
+ output?: TOutput;
75
+ error?: string;
76
+ duration: number;
77
+ }
78
+ /**
79
+ * Configuration for queuebase
80
+ */
81
+ export interface QueuebaseConfig {
82
+ /** Directory containing job definitions */
83
+ jobsDir: string;
84
+ /** URL where the queuebase API can callback to execute jobs */
85
+ callbackUrl?: string;
86
+ /** API key for production (from queuebase.io) */
87
+ apiKey?: string;
88
+ /** Override the API endpoint (auto-detected by NODE_ENV) */
89
+ apiUrl?: string;
90
+ }
91
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAErF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO;IAChE,0CAA0C;IAC1C,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,iDAAiD;IACjD,OAAO,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,mCAAmC;IACnC,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,MAAM,GAAG,OAAO;IAC1C,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS,CAAC,OAAO,GAAG,OAAO;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Parse a delay string into milliseconds
3
+ * Supports: '5s', '5m', '5h', '5d' or raw milliseconds
4
+ */
5
+ export declare function parseDelay(delay: string | number): number;
6
+ /**
7
+ * Calculate the next retry delay based on backoff strategy
8
+ */
9
+ export declare function calculateBackoff(attempt: number, strategy: 'linear' | 'exponential', baseDelay: number): number;
10
+ /**
11
+ * Generate a unique job ID
12
+ * @deprecated Use `generatePublicId()` instead
13
+ */
14
+ export declare function generateJobId(): string;
15
+ /**
16
+ * Generate a Stripe-style public ID for external use
17
+ * @param prefix - Prefix for the ID (default: 'job')
18
+ * @returns A string like `job_V1StGXR8kZ5jx...`
19
+ */
20
+ export declare function generatePublicId(prefix?: string): string;
21
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CA6BzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,GAAG,aAAa,EAClC,SAAS,EAAE,MAAM,GAChB,MAAM,CASR;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,SAAQ,GAAG,MAAM,CAEvD"}
package/dist/utils.js ADDED
@@ -0,0 +1,63 @@
1
+ import { nanoid } from 'nanoid';
2
+ /**
3
+ * Parse a delay string into milliseconds
4
+ * Supports: '5s', '5m', '5h', '5d' or raw milliseconds
5
+ */
6
+ export function parseDelay(delay) {
7
+ if (typeof delay === 'number') {
8
+ return delay;
9
+ }
10
+ const match = delay.match(/^(\d+)(s|m|h|d)$/);
11
+ if (!match) {
12
+ const num = Number.parseInt(delay, 10);
13
+ if (Number.isNaN(num)) {
14
+ throw new Error(`Invalid delay format: ${delay}`);
15
+ }
16
+ return num;
17
+ }
18
+ const [, value, unit] = match;
19
+ const num = Number.parseInt(value, 10);
20
+ switch (unit) {
21
+ case 's':
22
+ return num * 1000;
23
+ case 'm':
24
+ return num * 60 * 1000;
25
+ case 'h':
26
+ return num * 60 * 60 * 1000;
27
+ case 'd':
28
+ return num * 24 * 60 * 60 * 1000;
29
+ default:
30
+ throw new Error(`Invalid delay unit: ${unit}`);
31
+ }
32
+ }
33
+ /**
34
+ * Calculate the next retry delay based on backoff strategy
35
+ */
36
+ export function calculateBackoff(attempt, strategy, baseDelay) {
37
+ switch (strategy) {
38
+ case 'linear':
39
+ return baseDelay * attempt;
40
+ case 'exponential':
41
+ return baseDelay * 2 ** (attempt - 1);
42
+ default:
43
+ return baseDelay;
44
+ }
45
+ }
46
+ /**
47
+ * Generate a unique job ID
48
+ * @deprecated Use `generatePublicId()` instead
49
+ */
50
+ export function generateJobId() {
51
+ const timestamp = Date.now().toString(36);
52
+ const random = Math.random().toString(36).substring(2, 10);
53
+ return `job_${timestamp}_${random}`;
54
+ }
55
+ /**
56
+ * Generate a Stripe-style public ID for external use
57
+ * @param prefix - Prefix for the ID (default: 'job')
58
+ * @returns A string like `job_V1StGXR8kZ5jx...`
59
+ */
60
+ export function generatePublicId(prefix = 'job') {
61
+ return `${prefix}_${nanoid(21)}`;
62
+ }
63
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAM,EAAE,EAAE,CAAC,CAAC;IAExC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,IAAI,CAAC;QACpB,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;QACzB,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAC9B,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QACnC;YACE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,QAAkC,EAClC,SAAiB;IAEjB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,SAAS,GAAG,OAAO,CAAC;QAC7B,KAAK,aAAa;YAChB,OAAO,SAAS,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;QACxC;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3D,OAAO,OAAO,SAAS,IAAI,MAAM,EAAE,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAM,GAAG,KAAK;IAC7C,OAAO,GAAG,MAAM,IAAI,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../src/utils.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { calculateBackoff, generateJobId, generatePublicId, parseDelay } from './utils.js';
3
+ describe('parseDelay', () => {
4
+ it('should parse seconds', () => {
5
+ expect(parseDelay('5s')).toBe(5000);
6
+ expect(parseDelay('30s')).toBe(30000);
7
+ });
8
+ it('should parse minutes', () => {
9
+ expect(parseDelay('5m')).toBe(300000);
10
+ expect(parseDelay('1m')).toBe(60000);
11
+ });
12
+ it('should parse hours', () => {
13
+ expect(parseDelay('1h')).toBe(3600000);
14
+ expect(parseDelay('2h')).toBe(7200000);
15
+ });
16
+ it('should parse days', () => {
17
+ expect(parseDelay('1d')).toBe(86400000);
18
+ });
19
+ it('should handle raw milliseconds as number', () => {
20
+ expect(parseDelay(5000)).toBe(5000);
21
+ });
22
+ it('should handle raw milliseconds as string', () => {
23
+ expect(parseDelay('5000')).toBe(5000);
24
+ });
25
+ it('should throw on invalid format', () => {
26
+ expect(() => parseDelay('invalid')).toThrow('Invalid delay format');
27
+ });
28
+ });
29
+ describe('calculateBackoff', () => {
30
+ it('should calculate linear backoff', () => {
31
+ expect(calculateBackoff(1, 'linear', 1000)).toBe(1000);
32
+ expect(calculateBackoff(2, 'linear', 1000)).toBe(2000);
33
+ expect(calculateBackoff(3, 'linear', 1000)).toBe(3000);
34
+ });
35
+ it('should calculate exponential backoff', () => {
36
+ expect(calculateBackoff(1, 'exponential', 1000)).toBe(1000);
37
+ expect(calculateBackoff(2, 'exponential', 1000)).toBe(2000);
38
+ expect(calculateBackoff(3, 'exponential', 1000)).toBe(4000);
39
+ expect(calculateBackoff(4, 'exponential', 1000)).toBe(8000);
40
+ });
41
+ });
42
+ describe('generateJobId', () => {
43
+ it('should generate unique IDs', () => {
44
+ const id1 = generateJobId();
45
+ const id2 = generateJobId();
46
+ expect(id1).not.toBe(id2);
47
+ });
48
+ it('should start with job_ prefix', () => {
49
+ const id = generateJobId();
50
+ expect(id.startsWith('job_')).toBe(true);
51
+ });
52
+ });
53
+ describe('generatePublicId', () => {
54
+ it('should generate unique IDs', () => {
55
+ const id1 = generatePublicId();
56
+ const id2 = generatePublicId();
57
+ expect(id1).not.toBe(id2);
58
+ });
59
+ it('should use default job_ prefix', () => {
60
+ const id = generatePublicId();
61
+ expect(id.startsWith('job_')).toBe(true);
62
+ });
63
+ it('should use custom prefix', () => {
64
+ const id = generatePublicId('proj');
65
+ expect(id.startsWith('proj_')).toBe(true);
66
+ });
67
+ it('should have consistent length', () => {
68
+ const id1 = generatePublicId();
69
+ const id2 = generatePublicId();
70
+ expect(id1.length).toBe(id2.length);
71
+ // prefix (3) + underscore (1) + nanoid (21) = 25
72
+ expect(id1.length).toBe(25);
73
+ });
74
+ });
75
+ //# sourceMappingURL=utils.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../src/utils.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export declare const WEBHOOK_HEADERS: {
2
+ readonly SIGNATURE: "X-Queuebase-Signature";
3
+ };
4
+ /**
5
+ * Sign a webhook payload using HMAC-SHA256.
6
+ * Returns a signature string in the format `t=<timestamp>,v1=<hmac>`.
7
+ */
8
+ export declare function signPayload(payload: string, secret: string): {
9
+ signature: string;
10
+ timestamp: number;
11
+ };
12
+ /**
13
+ * Verify a webhook signature against a payload.
14
+ * Uses timing-safe comparison and enforces a timestamp tolerance for replay protection.
15
+ */
16
+ export declare function verifySignature(payload: string, signature: string, secret: string, toleranceSeconds?: number): boolean;
17
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe;;CAElB,CAAC;AAIX;;;GAGG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAQ1C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,gBAAgB,SAA4B,GAC3C,OAAO,CAmCT"}
@@ -0,0 +1,51 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ export const WEBHOOK_HEADERS = {
3
+ SIGNATURE: 'X-Queuebase-Signature',
4
+ };
5
+ const DEFAULT_TOLERANCE_SECONDS = 5 * 60; // 5 minutes
6
+ /**
7
+ * Sign a webhook payload using HMAC-SHA256.
8
+ * Returns a signature string in the format `t=<timestamp>,v1=<hmac>`.
9
+ */
10
+ export function signPayload(payload, secret) {
11
+ const timestamp = Math.floor(Date.now() / 1000);
12
+ const signedContent = `${timestamp}.${payload}`;
13
+ const hmac = createHmac('sha256', secret).update(signedContent).digest('hex');
14
+ return {
15
+ signature: `t=${timestamp},v1=${hmac}`,
16
+ timestamp,
17
+ };
18
+ }
19
+ /**
20
+ * Verify a webhook signature against a payload.
21
+ * Uses timing-safe comparison and enforces a timestamp tolerance for replay protection.
22
+ */
23
+ export function verifySignature(payload, signature, secret, toleranceSeconds = DEFAULT_TOLERANCE_SECONDS) {
24
+ const parts = signature.split(',');
25
+ const tPart = parts.find((p) => p.startsWith('t='));
26
+ const v1Part = parts.find((p) => p.startsWith('v1='));
27
+ if (!tPart || !v1Part) {
28
+ return false;
29
+ }
30
+ const timestamp = Number.parseInt(tPart.slice(2), 10);
31
+ const receivedHmac = v1Part.slice(3);
32
+ if (Number.isNaN(timestamp)) {
33
+ return false;
34
+ }
35
+ // Check timestamp tolerance for replay protection
36
+ const now = Math.floor(Date.now() / 1000);
37
+ if (Math.abs(now - timestamp) > toleranceSeconds) {
38
+ return false;
39
+ }
40
+ // Recompute the expected HMAC
41
+ const signedContent = `${timestamp}.${payload}`;
42
+ const expectedHmac = createHmac('sha256', secret).update(signedContent).digest('hex');
43
+ // Timing-safe comparison
44
+ const expected = Buffer.from(expectedHmac, 'hex');
45
+ const received = Buffer.from(receivedHmac, 'hex');
46
+ if (expected.length !== received.length) {
47
+ return false;
48
+ }
49
+ return timingSafeEqual(expected, received);
50
+ }
51
+ //# sourceMappingURL=webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,uBAAuB;CAC1B,CAAC;AAEX,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,YAAY;AAEtD;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,MAAc;IAEd,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,GAAG,SAAS,IAAI,OAAO,EAAE,CAAC;IAChD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9E,OAAO;QACL,SAAS,EAAE,KAAK,SAAS,OAAO,IAAI,EAAE;QACtC,SAAS;KACV,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,SAAiB,EACjB,MAAc,EACd,gBAAgB,GAAG,yBAAyB;IAE5C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAEtD,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAErC,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,kDAAkD;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,gBAAgB,EAAE,CAAC;QACjD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8BAA8B;IAC9B,MAAM,aAAa,GAAG,GAAG,SAAS,IAAI,OAAO,EAAE,CAAC;IAChD,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEtF,yBAAyB;IACzB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAElD,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=webhook.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.test.d.ts","sourceRoot":"","sources":["../src/webhook.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { signPayload, verifySignature } from './webhook.js';
3
+ const TEST_SECRET = 'qb_whsec_test_secret_key';
4
+ describe('webhook signing', () => {
5
+ it('should sign and verify a payload successfully', () => {
6
+ const payload = JSON.stringify({ jobId: 'job_123', name: 'sendEmail' });
7
+ const { signature } = signPayload(payload, TEST_SECRET);
8
+ expect(signature).toMatch(/^t=\d+,v1=[a-f0-9]{64}$/);
9
+ expect(verifySignature(payload, signature, TEST_SECRET)).toBe(true);
10
+ });
11
+ it('should reject a tampered payload', () => {
12
+ const payload = JSON.stringify({ jobId: 'job_123' });
13
+ const { signature } = signPayload(payload, TEST_SECRET);
14
+ const tampered = JSON.stringify({ jobId: 'job_456' });
15
+ expect(verifySignature(tampered, signature, TEST_SECRET)).toBe(false);
16
+ });
17
+ it('should reject a wrong secret', () => {
18
+ const payload = JSON.stringify({ jobId: 'job_123' });
19
+ const { signature } = signPayload(payload, TEST_SECRET);
20
+ expect(verifySignature(payload, signature, 'wrong_secret')).toBe(false);
21
+ });
22
+ it('should reject an invalid signature format', () => {
23
+ const payload = JSON.stringify({ jobId: 'job_123' });
24
+ expect(verifySignature(payload, 'invalid', TEST_SECRET)).toBe(false);
25
+ expect(verifySignature(payload, 't=abc,v1=def', TEST_SECRET)).toBe(false);
26
+ expect(verifySignature(payload, 'v1=abc', TEST_SECRET)).toBe(false);
27
+ });
28
+ it('should reject an expired timestamp', () => {
29
+ const payload = JSON.stringify({ jobId: 'job_123' });
30
+ const { signature } = signPayload(payload, TEST_SECRET);
31
+ // Move time forward 10 minutes
32
+ vi.useFakeTimers();
33
+ vi.setSystemTime(Date.now() + 10 * 60 * 1000);
34
+ expect(verifySignature(payload, signature, TEST_SECRET)).toBe(false);
35
+ vi.useRealTimers();
36
+ });
37
+ it('should accept within custom tolerance', () => {
38
+ const payload = JSON.stringify({ jobId: 'job_123' });
39
+ const { signature } = signPayload(payload, TEST_SECRET);
40
+ // Move time forward 2 minutes — within 10-minute tolerance
41
+ vi.useFakeTimers();
42
+ vi.setSystemTime(Date.now() + 2 * 60 * 1000);
43
+ expect(verifySignature(payload, signature, TEST_SECRET, 600)).toBe(true);
44
+ vi.useRealTimers();
45
+ });
46
+ });
47
+ //# sourceMappingURL=webhook.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.test.js","sourceRoot":"","sources":["../src/webhook.test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@queuebase/core",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ }
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "dependencies": {
18
+ "nanoid": "^5.0.9",
19
+ "zod": "^3.24.1"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.19.9",
23
+ "typescript": "^5.7.2",
24
+ "vitest": "^2.1.8",
25
+ "@queuebase/tsconfig": "0.0.0"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -b",
29
+ "dev": "tsc -b --watch",
30
+ "clean": "rm -rf dist .turbo *.tsbuildinfo",
31
+ "typecheck": "tsc --noEmit",
32
+ "lint": "biome lint ./src",
33
+ "lint:fix": "biome lint --write ./src",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest"
36
+ }
37
+ }