@keystrokehq/scheduler 0.0.156 → 0.0.158
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/contract-C5JvbyQ-.cjs.map +1 -1
- package/dist/{contract-B3JBTWO3.d.cts → contract-DAqQua3D.d.cts} +5 -2
- package/dist/contract-DAqQua3D.d.cts.map +1 -0
- package/dist/{contract-B3JBTWO3.d.mts → contract-DAqQua3D.d.mts} +5 -2
- package/dist/contract-DAqQua3D.d.mts.map +1 -0
- package/dist/contract-E1QJBH6_.mjs.map +1 -1
- package/dist/contract.d.cts +2 -2
- package/dist/contract.d.mts +2 -2
- package/dist/index.cjs +61 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +9 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +62 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/dist/contract-B3JBTWO3.d.cts.map +0 -1
- package/dist/contract-B3JBTWO3.d.mts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-C5JvbyQ-.cjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind = \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n\nexport type JobTrigger = \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n\nexport type JobPayload = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload: unknown;\n attempt: number;\n maxAttempts: number;\n /** True when this delivery is the final retry attempt. */\n exhaustedRetries?: boolean;\n scheduledAt: Date;\n jobId: string;\n};\n\nexport type EnqueueInput = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload?: unknown;\n scheduledAt?: Date;\n attempt?: number;\n maxAttempts?: number;\n /** Dedupe key — pg-boss singletonKey / BullMQ jobId. */\n dedupeKey?: string;\n};\n\nexport type JobHandler = (job: JobPayload) => Promise<void>;\n\nexport type StopFn = () => Promise<void> | void;\n\nexport type WorkerOptions = {\n workerId?: string;\n pollIntervalMs?: number;\n leaseSweepIntervalMs?: number;\n};\n\nexport type JobQueue = {\n enqueue(input: EnqueueInput): Promise<string>;\n startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;\n};\n\nimport type { SchedulerPlugin, SchedulerScope } from \"./contract\";\n\nexport type CreateJobQueueOptions = {\n url?: string;\n dialect?: \"postgres\" | \"sqlite\";\n adapter?: JobQueue;\n plugin?: SchedulerPlugin;\n scope?: SchedulerScope;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n attachmentKey: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n};\n\nexport type ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byAttachment?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport type Scheduler = JobQueue & {\n syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void>;\n startScheduleTicker(options?: ScheduleTickerOptions): Promise<StopFn>;\n fireDueSchedules(asOf?: Date): Promise<number>;\n};\n\nexport type CreateSchedulerOptions = CreateJobQueueOptions;\n\nexport const DEFAULT_RETRY_DELAY_MS = 5_000;\n\nexport function retryDelayMs(attempt: number): number {\n return DEFAULT_RETRY_DELAY_MS * 2 ** Math.max(0, attempt - 1);\n}\n","export type {\n JobKind,\n JobTrigger,\n JobPayload,\n EnqueueInput,\n JobHandler,\n StopFn,\n WorkerOptions,\n JobQueue,\n} from \"./types\";\nexport { retryDelayMs, DEFAULT_RETRY_DELAY_MS } from \"./types\";\n\nimport type { JobQueue } from \"./types\";\n\n/**\n * Inlined to keep the contract database-free — structurally identical to\n * `@keystrokehq/database`'s `DatabaseDialect`. Importing it from the database\n * package would re-introduce the dependency `./contract` exists to avoid.\n */\nexport type DatabaseDialect = \"postgres\" | \"sqlite\";\n\nexport type SchedulerScope = \"platform\" | \"project\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"contract-C5JvbyQ-.cjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind = \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n\nexport type JobTrigger = \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n\nexport type JobPayload = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload: unknown;\n attempt: number;\n maxAttempts: number;\n /** True when this delivery is the final retry attempt. */\n exhaustedRetries?: boolean;\n scheduledAt: Date;\n jobId: string;\n};\n\nexport type EnqueueInput = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload?: unknown;\n scheduledAt?: Date;\n attempt?: number;\n maxAttempts?: number;\n /** Dedupe key — pg-boss singletonKey / BullMQ jobId. */\n dedupeKey?: string;\n};\n\nexport type JobHandler = (job: JobPayload) => Promise<void>;\n\nexport type CancelHandler = (runId: string) => void | Promise<void>;\n\nexport type StopFn = () => Promise<void> | void;\n\nexport type WorkerOptions = {\n workerId?: string;\n pollIntervalMs?: number;\n leaseSweepIntervalMs?: number;\n};\n\nexport type JobQueue = {\n enqueue(input: EnqueueInput): Promise<string>;\n startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;\n publishCancel(runId: string): Promise<void>;\n subscribeCancel(handler: CancelHandler): Promise<StopFn>;\n};\n\nimport type { SchedulerPlugin, SchedulerScope } from \"./contract\";\n\nexport type CreateJobQueueOptions = {\n url?: string;\n dialect?: \"postgres\" | \"sqlite\";\n adapter?: JobQueue;\n plugin?: SchedulerPlugin;\n scope?: SchedulerScope;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n attachmentKey: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n};\n\nexport type ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byAttachment?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport type Scheduler = JobQueue & {\n syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void>;\n startScheduleTicker(options?: ScheduleTickerOptions): Promise<StopFn>;\n fireDueSchedules(asOf?: Date): Promise<number>;\n};\n\nexport type CreateSchedulerOptions = CreateJobQueueOptions;\n\nexport const DEFAULT_RETRY_DELAY_MS = 5_000;\n\nexport function retryDelayMs(attempt: number): number {\n return DEFAULT_RETRY_DELAY_MS * 2 ** Math.max(0, attempt - 1);\n}\n","export type {\n JobKind,\n JobTrigger,\n JobPayload,\n EnqueueInput,\n JobHandler,\n CancelHandler,\n StopFn,\n WorkerOptions,\n JobQueue,\n} from \"./types\";\nexport { retryDelayMs, DEFAULT_RETRY_DELAY_MS } from \"./types\";\n\nimport type { JobQueue } from \"./types\";\n\n/**\n * Inlined to keep the contract database-free — structurally identical to\n * `@keystrokehq/database`'s `DatabaseDialect`. Importing it from the database\n * package would re-introduce the dependency `./contract` exists to avoid.\n */\nexport type DatabaseDialect = \"postgres\" | \"sqlite\";\n\nexport type SchedulerScope = \"platform\" | \"project\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";AAyFA,MAAa,yBAAyB;AAEtC,SAAgB,aAAa,SAAyB;CACpD,OAAO,yBAAyB,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC;AAC9D;;;ACxDA,SAAgB,sBAAsB,QAA0C;CAC9E,OAAO;AACT"}
|
|
@@ -25,6 +25,7 @@ type EnqueueInput = {
|
|
|
25
25
|
dedupeKey?: string;
|
|
26
26
|
};
|
|
27
27
|
type JobHandler = (job: JobPayload) => Promise<void>;
|
|
28
|
+
type CancelHandler = (runId: string) => void | Promise<void>;
|
|
28
29
|
type StopFn = () => Promise<void> | void;
|
|
29
30
|
type WorkerOptions = {
|
|
30
31
|
workerId?: string;
|
|
@@ -34,6 +35,8 @@ type WorkerOptions = {
|
|
|
34
35
|
type JobQueue = {
|
|
35
36
|
enqueue(input: EnqueueInput): Promise<string>;
|
|
36
37
|
startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;
|
|
38
|
+
publishCancel(runId: string): Promise<void>;
|
|
39
|
+
subscribeCancel(handler: CancelHandler): Promise<StopFn>;
|
|
37
40
|
};
|
|
38
41
|
type CreateJobQueueOptions = {
|
|
39
42
|
url?: string;
|
|
@@ -90,5 +93,5 @@ type SchedulerPlugin = {
|
|
|
90
93
|
};
|
|
91
94
|
declare function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin;
|
|
92
95
|
//#endregion
|
|
93
|
-
export {
|
|
94
|
-
//# sourceMappingURL=contract-
|
|
96
|
+
export { ScheduleTickerOptions as _, defineSchedulerPlugin as a, WorkerOptions as b, CreateSchedulerOptions as c, JobHandler as d, JobKind as f, ScheduleSyncOptions as g, JobTrigger as h, SchedulerScope as i, DEFAULT_RETRY_DELAY_MS as l, JobQueue as m, SchedulerPlugin as n, CancelHandler as o, JobPayload as p, SchedulerPluginContext as r, CreateJobQueueOptions as s, DatabaseDialect as t, EnqueueInput as u, Scheduler as v, retryDelayMs as x, StopFn as y };
|
|
97
|
+
//# sourceMappingURL=contract-DAqQua3D.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-DAqQua3D.d.cts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":";KAAY,OAAA;AAAA,KAEA,UAAA;AAAA,KAEA,UAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,OAAA;EACA,WAAA,UAToB;EAWpB,gBAAA;EACA,WAAA,EAAa,IAAA;EACb,KAAA;AAAA;AAAA,KAGU,YAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,WAAA,GAAc,IAAA;EACd,OAAA;EACA,WAAA,WAlBS;EAoBT,SAAA;AAAA;AAAA,KAGU,UAAA,IAAc,GAAA,EAAK,UAAA,KAAe,OAAO;AAAA,KAEzC,aAAA,IAAiB,KAAA,oBAAyB,OAAO;AAAA,KAEjD,MAAA,SAAe,OAAO;AAAA,KAEtB,aAAA;EACV,QAAA;EACA,cAAA;EACA,oBAAA;AAAA;AAAA,KAGU,QAAA;EACV,OAAA,CAAQ,KAAA,EAAO,YAAA,GAAe,OAAA;EAC9B,WAAA,CAAY,OAAA,EAAS,UAAA,EAAY,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,MAAA;EACnE,aAAA,CAAc,KAAA,WAAgB,OAAA;EAC9B,eAAA,CAAgB,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA;AAAA,KAKvC,qBAAA;EACV,GAAA;EACA,OAAA;EACA,OAAA,GAAU,QAAA;EACV,MAAA,GAAS,eAAA;EACT,KAAA,GAAQ,cAAA;EACR,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,mBAAA;EACV,aAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,KAGU,mBAAA;EACV,SAAA,EAAW,mBAAA;EACX,iBAAA;IACE,MAAA;IACA,YAAA,GAAe,MAAM;EAAA;AAAA;AAAA,KAIb,qBAAA;EACV,cAAA;EACA,SAAS;AAAA;AAAA,KAGC,SAAA,GAAY,QAAA;EACtB,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAA;EACpD,mBAAA,CAAoB,OAAA,GAAU,qBAAA,GAAwB,OAAA,CAAQ,MAAA;EAC9D,gBAAA,CAAiB,IAAA,GAAO,IAAA,GAAO,OAAA;AAAA;AAAA,KAGrB,sBAAA,GAAyB,qBAAqB;AAAA,cAE7C,sBAAA;AAAA,iBAEG,YAAA,CAAa,OAAe;;;;;AA3FzB;AAEnB;;KCkBY,eAAA;AAAA,KAEA,cAAA;AAAA,KAEA,sBAAA;EACV,KAAA,EAAO,cAAA;EACP,GAAA;EACA,OAAA,GAAU,eAAe;EACzB,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,eAAA;EACV,IAAA;EACA,cAAA,CAAe,GAAA,EAAK,sBAAA,GAAyB,OAAA,CAAQ,QAAA;AAAA;AAAA,iBAGvC,qBAAA,CAAsB,MAAA,EAAQ,eAAA,GAAkB,eAAe"}
|
|
@@ -25,6 +25,7 @@ type EnqueueInput = {
|
|
|
25
25
|
dedupeKey?: string;
|
|
26
26
|
};
|
|
27
27
|
type JobHandler = (job: JobPayload) => Promise<void>;
|
|
28
|
+
type CancelHandler = (runId: string) => void | Promise<void>;
|
|
28
29
|
type StopFn = () => Promise<void> | void;
|
|
29
30
|
type WorkerOptions = {
|
|
30
31
|
workerId?: string;
|
|
@@ -34,6 +35,8 @@ type WorkerOptions = {
|
|
|
34
35
|
type JobQueue = {
|
|
35
36
|
enqueue(input: EnqueueInput): Promise<string>;
|
|
36
37
|
startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;
|
|
38
|
+
publishCancel(runId: string): Promise<void>;
|
|
39
|
+
subscribeCancel(handler: CancelHandler): Promise<StopFn>;
|
|
37
40
|
};
|
|
38
41
|
type CreateJobQueueOptions = {
|
|
39
42
|
url?: string;
|
|
@@ -90,5 +93,5 @@ type SchedulerPlugin = {
|
|
|
90
93
|
};
|
|
91
94
|
declare function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin;
|
|
92
95
|
//#endregion
|
|
93
|
-
export {
|
|
94
|
-
//# sourceMappingURL=contract-
|
|
96
|
+
export { ScheduleTickerOptions as _, defineSchedulerPlugin as a, WorkerOptions as b, CreateSchedulerOptions as c, JobHandler as d, JobKind as f, ScheduleSyncOptions as g, JobTrigger as h, SchedulerScope as i, DEFAULT_RETRY_DELAY_MS as l, JobQueue as m, SchedulerPlugin as n, CancelHandler as o, JobPayload as p, SchedulerPluginContext as r, CreateJobQueueOptions as s, DatabaseDialect as t, EnqueueInput as u, Scheduler as v, retryDelayMs as x, StopFn as y };
|
|
97
|
+
//# sourceMappingURL=contract-DAqQua3D.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-DAqQua3D.d.mts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":";KAAY,OAAA;AAAA,KAEA,UAAA;AAAA,KAEA,UAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,OAAA;EACA,WAAA,UAToB;EAWpB,gBAAA;EACA,WAAA,EAAa,IAAA;EACb,KAAA;AAAA;AAAA,KAGU,YAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,WAAA,GAAc,IAAA;EACd,OAAA;EACA,WAAA,WAlBS;EAoBT,SAAA;AAAA;AAAA,KAGU,UAAA,IAAc,GAAA,EAAK,UAAA,KAAe,OAAO;AAAA,KAEzC,aAAA,IAAiB,KAAA,oBAAyB,OAAO;AAAA,KAEjD,MAAA,SAAe,OAAO;AAAA,KAEtB,aAAA;EACV,QAAA;EACA,cAAA;EACA,oBAAA;AAAA;AAAA,KAGU,QAAA;EACV,OAAA,CAAQ,KAAA,EAAO,YAAA,GAAe,OAAA;EAC9B,WAAA,CAAY,OAAA,EAAS,UAAA,EAAY,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,MAAA;EACnE,aAAA,CAAc,KAAA,WAAgB,OAAA;EAC9B,eAAA,CAAgB,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA;AAAA,KAKvC,qBAAA;EACV,GAAA;EACA,OAAA;EACA,OAAA,GAAU,QAAA;EACV,MAAA,GAAS,eAAA;EACT,KAAA,GAAQ,cAAA;EACR,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,mBAAA;EACV,aAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,KAGU,mBAAA;EACV,SAAA,EAAW,mBAAA;EACX,iBAAA;IACE,MAAA;IACA,YAAA,GAAe,MAAM;EAAA;AAAA;AAAA,KAIb,qBAAA;EACV,cAAA;EACA,SAAS;AAAA;AAAA,KAGC,SAAA,GAAY,QAAA;EACtB,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAA;EACpD,mBAAA,CAAoB,OAAA,GAAU,qBAAA,GAAwB,OAAA,CAAQ,MAAA;EAC9D,gBAAA,CAAiB,IAAA,GAAO,IAAA,GAAO,OAAA;AAAA;AAAA,KAGrB,sBAAA,GAAyB,qBAAqB;AAAA,cAE7C,sBAAA;AAAA,iBAEG,YAAA,CAAa,OAAe;;;;;AA3FzB;AAEnB;;KCkBY,eAAA;AAAA,KAEA,cAAA;AAAA,KAEA,sBAAA;EACV,KAAA,EAAO,cAAA;EACP,GAAA;EACA,OAAA,GAAU,eAAe;EACzB,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,eAAA;EACV,IAAA;EACA,cAAA,CAAe,GAAA,EAAK,sBAAA,GAAyB,OAAA,CAAQ,QAAA;AAAA;AAAA,iBAGvC,qBAAA,CAAsB,MAAA,EAAQ,eAAA,GAAkB,eAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-E1QJBH6_.mjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind = \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n\nexport type JobTrigger = \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n\nexport type JobPayload = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload: unknown;\n attempt: number;\n maxAttempts: number;\n /** True when this delivery is the final retry attempt. */\n exhaustedRetries?: boolean;\n scheduledAt: Date;\n jobId: string;\n};\n\nexport type EnqueueInput = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload?: unknown;\n scheduledAt?: Date;\n attempt?: number;\n maxAttempts?: number;\n /** Dedupe key — pg-boss singletonKey / BullMQ jobId. */\n dedupeKey?: string;\n};\n\nexport type JobHandler = (job: JobPayload) => Promise<void>;\n\nexport type StopFn = () => Promise<void> | void;\n\nexport type WorkerOptions = {\n workerId?: string;\n pollIntervalMs?: number;\n leaseSweepIntervalMs?: number;\n};\n\nexport type JobQueue = {\n enqueue(input: EnqueueInput): Promise<string>;\n startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;\n};\n\nimport type { SchedulerPlugin, SchedulerScope } from \"./contract\";\n\nexport type CreateJobQueueOptions = {\n url?: string;\n dialect?: \"postgres\" | \"sqlite\";\n adapter?: JobQueue;\n plugin?: SchedulerPlugin;\n scope?: SchedulerScope;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n attachmentKey: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n};\n\nexport type ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byAttachment?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport type Scheduler = JobQueue & {\n syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void>;\n startScheduleTicker(options?: ScheduleTickerOptions): Promise<StopFn>;\n fireDueSchedules(asOf?: Date): Promise<number>;\n};\n\nexport type CreateSchedulerOptions = CreateJobQueueOptions;\n\nexport const DEFAULT_RETRY_DELAY_MS = 5_000;\n\nexport function retryDelayMs(attempt: number): number {\n return DEFAULT_RETRY_DELAY_MS * 2 ** Math.max(0, attempt - 1);\n}\n","export type {\n JobKind,\n JobTrigger,\n JobPayload,\n EnqueueInput,\n JobHandler,\n StopFn,\n WorkerOptions,\n JobQueue,\n} from \"./types\";\nexport { retryDelayMs, DEFAULT_RETRY_DELAY_MS } from \"./types\";\n\nimport type { JobQueue } from \"./types\";\n\n/**\n * Inlined to keep the contract database-free — structurally identical to\n * `@keystrokehq/database`'s `DatabaseDialect`. Importing it from the database\n * package would re-introduce the dependency `./contract` exists to avoid.\n */\nexport type DatabaseDialect = \"postgres\" | \"sqlite\";\n\nexport type SchedulerScope = \"platform\" | \"project\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"contract-E1QJBH6_.mjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind = \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n\nexport type JobTrigger = \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n\nexport type JobPayload = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload: unknown;\n attempt: number;\n maxAttempts: number;\n /** True when this delivery is the final retry attempt. */\n exhaustedRetries?: boolean;\n scheduledAt: Date;\n jobId: string;\n};\n\nexport type EnqueueInput = {\n kind: JobKind;\n targetId: string;\n runId: string;\n trigger: JobTrigger;\n payload?: unknown;\n scheduledAt?: Date;\n attempt?: number;\n maxAttempts?: number;\n /** Dedupe key — pg-boss singletonKey / BullMQ jobId. */\n dedupeKey?: string;\n};\n\nexport type JobHandler = (job: JobPayload) => Promise<void>;\n\nexport type CancelHandler = (runId: string) => void | Promise<void>;\n\nexport type StopFn = () => Promise<void> | void;\n\nexport type WorkerOptions = {\n workerId?: string;\n pollIntervalMs?: number;\n leaseSweepIntervalMs?: number;\n};\n\nexport type JobQueue = {\n enqueue(input: EnqueueInput): Promise<string>;\n startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;\n publishCancel(runId: string): Promise<void>;\n subscribeCancel(handler: CancelHandler): Promise<StopFn>;\n};\n\nimport type { SchedulerPlugin, SchedulerScope } from \"./contract\";\n\nexport type CreateJobQueueOptions = {\n url?: string;\n dialect?: \"postgres\" | \"sqlite\";\n adapter?: JobQueue;\n plugin?: SchedulerPlugin;\n scope?: SchedulerScope;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n attachmentKey: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n};\n\nexport type ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byAttachment?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport type Scheduler = JobQueue & {\n syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void>;\n startScheduleTicker(options?: ScheduleTickerOptions): Promise<StopFn>;\n fireDueSchedules(asOf?: Date): Promise<number>;\n};\n\nexport type CreateSchedulerOptions = CreateJobQueueOptions;\n\nexport const DEFAULT_RETRY_DELAY_MS = 5_000;\n\nexport function retryDelayMs(attempt: number): number {\n return DEFAULT_RETRY_DELAY_MS * 2 ** Math.max(0, attempt - 1);\n}\n","export type {\n JobKind,\n JobTrigger,\n JobPayload,\n EnqueueInput,\n JobHandler,\n CancelHandler,\n StopFn,\n WorkerOptions,\n JobQueue,\n} from \"./types\";\nexport { retryDelayMs, DEFAULT_RETRY_DELAY_MS } from \"./types\";\n\nimport type { JobQueue } from \"./types\";\n\n/**\n * Inlined to keep the contract database-free — structurally identical to\n * `@keystrokehq/database`'s `DatabaseDialect`. Importing it from the database\n * package would re-introduce the dependency `./contract` exists to avoid.\n */\nexport type DatabaseDialect = \"postgres\" | \"sqlite\";\n\nexport type SchedulerScope = \"platform\" | \"project\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";AAyFA,MAAa,yBAAyB;AAEtC,SAAgB,aAAa,SAAyB;CACpD,OAAO,yBAAyB,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC;AAC9D;;;ACxDA,SAAgB,sBAAsB,QAA0C;CAC9E,OAAO;AACT"}
|
package/dist/contract.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as defineSchedulerPlugin, b as
|
|
2
|
-
export { DEFAULT_RETRY_DELAY_MS, DatabaseDialect, type EnqueueInput, type JobHandler, type JobKind, type JobPayload, type JobQueue, type JobTrigger, SchedulerPlugin, SchedulerPluginContext, SchedulerScope, type StopFn, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
|
|
1
|
+
import { a as defineSchedulerPlugin, b as WorkerOptions, d as JobHandler, f as JobKind, h as JobTrigger, i as SchedulerScope, l as DEFAULT_RETRY_DELAY_MS, m as JobQueue, n as SchedulerPlugin, o as CancelHandler, p as JobPayload, r as SchedulerPluginContext, t as DatabaseDialect, u as EnqueueInput, x as retryDelayMs, y as StopFn } from "./contract-DAqQua3D.cjs";
|
|
2
|
+
export { type CancelHandler, DEFAULT_RETRY_DELAY_MS, DatabaseDialect, type EnqueueInput, type JobHandler, type JobKind, type JobPayload, type JobQueue, type JobTrigger, SchedulerPlugin, SchedulerPluginContext, SchedulerScope, type StopFn, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
|
package/dist/contract.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as defineSchedulerPlugin, b as
|
|
2
|
-
export { DEFAULT_RETRY_DELAY_MS, DatabaseDialect, type EnqueueInput, type JobHandler, type JobKind, type JobPayload, type JobQueue, type JobTrigger, SchedulerPlugin, SchedulerPluginContext, SchedulerScope, type StopFn, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
|
|
1
|
+
import { a as defineSchedulerPlugin, b as WorkerOptions, d as JobHandler, f as JobKind, h as JobTrigger, i as SchedulerScope, l as DEFAULT_RETRY_DELAY_MS, m as JobQueue, n as SchedulerPlugin, o as CancelHandler, p as JobPayload, r as SchedulerPluginContext, t as DatabaseDialect, u as EnqueueInput, x as retryDelayMs, y as StopFn } from "./contract-DAqQua3D.mjs";
|
|
2
|
+
export { type CancelHandler, DEFAULT_RETRY_DELAY_MS, DatabaseDialect, type EnqueueInput, type JobHandler, type JobKind, type JobPayload, type JobQueue, type JobTrigger, SchedulerPlugin, SchedulerPluginContext, SchedulerScope, type StopFn, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
|
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_contract = require("./contract-C5JvbyQ-.cjs");
|
|
3
3
|
let _keystrokehq_database = require("@keystrokehq/database");
|
|
4
4
|
let _keystrokehq_trigger = require("@keystrokehq/trigger");
|
|
5
|
+
let node_events = require("node:events");
|
|
5
6
|
let pg_boss = require("pg-boss");
|
|
6
7
|
let node_crypto = require("node:crypto");
|
|
7
8
|
//#region src/schedule-ticker.ts
|
|
@@ -54,6 +55,26 @@ function sleep$1(ms) {
|
|
|
54
55
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
55
56
|
}
|
|
56
57
|
//#endregion
|
|
58
|
+
//#region src/cancel-channel.ts
|
|
59
|
+
/** In-process cancel pub/sub for single-process queues (memory, db polling). */
|
|
60
|
+
function createInProcessCancelChannel() {
|
|
61
|
+
const emitter = new node_events.EventEmitter();
|
|
62
|
+
return {
|
|
63
|
+
async publishCancel(runId) {
|
|
64
|
+
emitter.emit("cancel", runId);
|
|
65
|
+
},
|
|
66
|
+
async subscribeCancel(handler) {
|
|
67
|
+
const listener = (runId) => {
|
|
68
|
+
handler(runId);
|
|
69
|
+
};
|
|
70
|
+
emitter.on("cancel", listener);
|
|
71
|
+
return async () => {
|
|
72
|
+
emitter.off("cancel", listener);
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
57
78
|
//#region src/database-queue.ts
|
|
58
79
|
function toJobPayload(job) {
|
|
59
80
|
return {
|
|
@@ -69,6 +90,7 @@ function toJobPayload(job) {
|
|
|
69
90
|
};
|
|
70
91
|
}
|
|
71
92
|
function createDatabaseJobQueue() {
|
|
93
|
+
const cancelChannel = createInProcessCancelChannel();
|
|
72
94
|
return {
|
|
73
95
|
async enqueue(input) {
|
|
74
96
|
return (0, _keystrokehq_database.enqueueJob)(input);
|
|
@@ -107,7 +129,9 @@ function createDatabaseJobQueue() {
|
|
|
107
129
|
running = false;
|
|
108
130
|
clearInterval(leaseTimer);
|
|
109
131
|
};
|
|
110
|
-
}
|
|
132
|
+
},
|
|
133
|
+
publishCancel: (runId) => cancelChannel.publishCancel(runId),
|
|
134
|
+
subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
|
|
111
135
|
};
|
|
112
136
|
}
|
|
113
137
|
function sleep(ms) {
|
|
@@ -144,6 +168,13 @@ async function stopPgBoss() {
|
|
|
144
168
|
boss = void 0;
|
|
145
169
|
}
|
|
146
170
|
//#endregion
|
|
171
|
+
//#region src/pg-cancel-channel.ts
|
|
172
|
+
/** Postgres NOTIFY channel — lowercase identifier (project ids are UUIDs, well under 63 chars). */
|
|
173
|
+
function pgCancelChannelName(scope, projectId) {
|
|
174
|
+
if (scope === "platform") return "keystroke_cancel_platform";
|
|
175
|
+
return `keystroke_cancel_${(projectId ?? "default").replace(/[^a-z0-9_]/gi, "_").toLowerCase()}`;
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
147
178
|
//#region src/pg-boss-queue.ts
|
|
148
179
|
/** Shared queue name for the platform control-plane scope. */
|
|
149
180
|
const DEFAULT_QUEUE_NAME = "keystroke";
|
|
@@ -159,8 +190,9 @@ function pgBossProjectQueueName(projectId) {
|
|
|
159
190
|
if (name.length <= 50) return name;
|
|
160
191
|
return `${DEFAULT_QUEUE_NAME}_${(0, node_crypto.createHash)("sha1").update(projectId).digest("hex").slice(0, 40)}`;
|
|
161
192
|
}
|
|
162
|
-
function buildPgBossJobQueue(boss, options
|
|
193
|
+
function buildPgBossJobQueue(boss, options) {
|
|
163
194
|
const queueName = options.queueName ?? "keystroke";
|
|
195
|
+
const cancelChannel = (0, _keystrokehq_database.createPostgresCancelChannel)(options.connectionString, pgCancelChannelName(options.cancelScope ?? "platform", options.projectId));
|
|
164
196
|
return {
|
|
165
197
|
async enqueue(input) {
|
|
166
198
|
const jobId = await boss.send(queueName, input, {
|
|
@@ -208,7 +240,9 @@ function buildPgBossJobQueue(boss, options = {}) {
|
|
|
208
240
|
timeout: 5e3
|
|
209
241
|
});
|
|
210
242
|
};
|
|
211
|
-
}
|
|
243
|
+
},
|
|
244
|
+
publishCancel: (runId) => cancelChannel.publishCancel(runId),
|
|
245
|
+
subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
|
|
212
246
|
};
|
|
213
247
|
}
|
|
214
248
|
async function createPgBossQueue(options) {
|
|
@@ -221,12 +255,20 @@ async function createPgBossQueue(options) {
|
|
|
221
255
|
await boss.createQueue(queueName);
|
|
222
256
|
return buildPgBossJobQueue(boss, {
|
|
223
257
|
stopBossOnWorkerStop: true,
|
|
224
|
-
queueName
|
|
258
|
+
queueName,
|
|
259
|
+
connectionString: options.connectionString,
|
|
260
|
+
cancelScope: "project",
|
|
261
|
+
projectId: options.projectId
|
|
225
262
|
});
|
|
226
263
|
}
|
|
227
|
-
async function createSharedPgBossJobQueue(boss,
|
|
264
|
+
async function createSharedPgBossJobQueue(boss, options) {
|
|
265
|
+
const queueName = options.queueName ?? "keystroke";
|
|
228
266
|
await boss.createQueue(queueName);
|
|
229
|
-
return buildPgBossJobQueue(boss, {
|
|
267
|
+
return buildPgBossJobQueue(boss, {
|
|
268
|
+
queueName,
|
|
269
|
+
connectionString: options.connectionString,
|
|
270
|
+
cancelScope: "platform"
|
|
271
|
+
});
|
|
230
272
|
}
|
|
231
273
|
//#endregion
|
|
232
274
|
//#region src/plugin.ts
|
|
@@ -241,11 +283,13 @@ function pgBossSchedulerPlugin() {
|
|
|
241
283
|
const url = resolveUrl(ctx);
|
|
242
284
|
if (ctx.scope === "platform") {
|
|
243
285
|
await startPgBoss(url);
|
|
244
|
-
return createSharedPgBossJobQueue(getPgBoss());
|
|
286
|
+
return createSharedPgBossJobQueue(getPgBoss(), { connectionString: url });
|
|
245
287
|
}
|
|
288
|
+
const projectId = ctx.projectId ?? (0, _keystrokehq_database.getProjectScopeId)();
|
|
246
289
|
return createPgBossQueue({
|
|
247
290
|
connectionString: url,
|
|
248
|
-
queueName: pgBossProjectQueueName(
|
|
291
|
+
queueName: pgBossProjectQueueName(projectId),
|
|
292
|
+
projectId
|
|
249
293
|
});
|
|
250
294
|
}
|
|
251
295
|
});
|
|
@@ -321,6 +365,8 @@ function wrapScheduler(jobQueue) {
|
|
|
321
365
|
return {
|
|
322
366
|
enqueue: (input) => jobQueue.enqueue(input),
|
|
323
367
|
startWorker: (handler, options) => jobQueue.startWorker(handler, options),
|
|
368
|
+
publishCancel: (runId) => jobQueue.publishCancel(runId),
|
|
369
|
+
subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),
|
|
324
370
|
syncTriggerSchedules: (options) => syncTriggerSchedules(options),
|
|
325
371
|
startScheduleTicker: (options) => ticker.startScheduleTicker(options),
|
|
326
372
|
fireDueSchedules: (asOf) => ticker.fireDueSchedules(asOf)
|
|
@@ -338,6 +384,7 @@ function wrapJobQueueAsScheduler(jobQueue) {
|
|
|
338
384
|
//#endregion
|
|
339
385
|
//#region src/memory.ts
|
|
340
386
|
function createMemoryJobQueue(options = {}) {
|
|
387
|
+
const cancelChannel = createInProcessCancelChannel();
|
|
341
388
|
const pending = [];
|
|
342
389
|
let handler;
|
|
343
390
|
let draining = false;
|
|
@@ -382,7 +429,9 @@ function createMemoryJobQueue(options = {}) {
|
|
|
382
429
|
return async () => {
|
|
383
430
|
handler = void 0;
|
|
384
431
|
};
|
|
385
|
-
}
|
|
432
|
+
},
|
|
433
|
+
publishCancel: (runId) => cancelChannel.publishCancel(runId),
|
|
434
|
+
subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
|
|
386
435
|
};
|
|
387
436
|
}
|
|
388
437
|
//#endregion
|
|
@@ -393,7 +442,9 @@ async function createSharedPgBossScheduler() {
|
|
|
393
442
|
}
|
|
394
443
|
async function getSharedPgBossJobQueue() {
|
|
395
444
|
if (sharedJobQueue$1) return sharedJobQueue$1;
|
|
396
|
-
|
|
445
|
+
const connectionString = (0, _keystrokehq_database.resolvePostgresUrlFromEnv)(process.env);
|
|
446
|
+
if (!connectionString) throw new Error("Postgres connection string is required for shared pg-boss queue");
|
|
447
|
+
sharedJobQueue$1 = await createSharedPgBossJobQueue(getPgBoss(), { connectionString });
|
|
397
448
|
return sharedJobQueue$1;
|
|
398
449
|
}
|
|
399
450
|
function resetSharedPgBossJobQueueForTests() {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["sleep","retryDelayMs","PgBoss","retryDelayMs","PgBoss","DEFAULT_DATABASE_URL","defineSchedulerPlugin","syncTriggerScheduleRows","sharedJobQueue"],"sources":["../src/schedule-ticker.ts","../src/database-queue.ts","../src/pg-boss-client.ts","../src/pg-boss-queue.ts","../src/plugin.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts","../src/resolve-plugin-from-env.ts"],"sourcesContent":["import { claimDueTriggerSchedules } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport function createScheduleTicker(ctx: ScheduleTickerContext) {\n const pollIntervalMs = ctx.pollIntervalMs ?? 1_000;\n const batchSize = ctx.batchSize ?? 10;\n\n async function fireDueSchedules(asOf = new Date()): Promise<number> {\n const claimed = await claimDueTriggerSchedules(\n asOf,\n (schedule) => nextTriggerRunAt(schedule, asOf),\n batchSize,\n );\n\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n scheduledAt: asOf,\n });\n }\n\n return claimed.length;\n }\n\n async function startScheduleTicker(options: ScheduleTickerOptions = {}): Promise<StopFn> {\n const intervalMs = options.pollIntervalMs ?? pollIntervalMs;\n const limit = options.batchSize ?? batchSize;\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n await claimDueTriggerSchedules(\n new Date(),\n (schedule) => nextTriggerRunAt(schedule, new Date()),\n limit,\n ).then(async (claimed) => {\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n });\n }\n });\n } catch {\n // keep ticking\n }\n\n await sleep(intervalMs);\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n };\n }\n\n return { fireDueSchedules, startScheduleTicker };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import {\n claimNextJob,\n enqueueJob,\n failWorkflowRun,\n markJobComplete,\n markJobFailed,\n requeueExpiredLeases,\n scheduleJobRetry,\n} from \"@keystrokehq/database\";\nimport type { ClaimedJob } from \"@keystrokehq/database\";\nimport type { JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nfunction toJobPayload(job: ClaimedJob): Parameters<JobHandler>[0] {\n return {\n jobId: job.id,\n kind: job.kind,\n targetId: job.targetId,\n runId: job.runId,\n trigger: job.trigger,\n payload: job.payload,\n attempt: job.attempt,\n maxAttempts: job.maxAttempts,\n scheduledAt: job.scheduledAt,\n };\n}\n\nexport function createDatabaseJobQueue(): JobQueue {\n return {\n async enqueue(input) {\n return enqueueJob(input);\n },\n\n async startWorker(handler: JobHandler, options: WorkerOptions = {}): Promise<StopFn> {\n const workerId = options.workerId ?? crypto.randomUUID();\n const pollIntervalMs = options.pollIntervalMs ?? 250;\n const leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 30_000;\n let running = true;\n\n const leaseTimer = setInterval(() => {\n void requeueExpiredLeases();\n }, leaseSweepIntervalMs);\n\n const loop = async () => {\n while (running) {\n try {\n const job = await claimNextJob(workerId);\n if (!job) {\n await sleep(pollIntervalMs);\n continue;\n }\n\n try {\n await handler(toJobPayload(job));\n await markJobComplete(job.id);\n } catch (error) {\n if (job.attempt < job.maxAttempts) {\n await scheduleJobRetry(job.id, job.attempt + 1, retryDelayMs(job.attempt));\n } else {\n await markJobFailed(job.id, error);\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n }\n }\n } catch {\n await sleep(pollIntervalMs);\n }\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n clearInterval(leaseTimer);\n };\n },\n };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { resolvePostgresUrlFromEnv } from \"@keystrokehq/database\";\nimport { PgBoss } from \"pg-boss\";\n\nlet boss: PgBoss | undefined;\n\nfunction resolveDatabaseUrl(url?: string): string {\n const resolved = url ?? resolvePostgresUrlFromEnv(process.env);\n if (!resolved) {\n throw new Error(\n \"DATABASE_URL or POSTGRES_HOST/POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB is required\",\n );\n }\n return resolved;\n}\n\nexport async function startPgBoss(url?: string): Promise<PgBoss> {\n if (boss) {\n return boss;\n }\n\n const next = new PgBoss({\n connectionString: resolveDatabaseUrl(url),\n schema: \"pgboss\",\n });\n next.on(\"error\", (error) => {\n console.error(\"[pg-boss]\", error);\n });\n\n await next.start();\n boss = next;\n return next;\n}\n\nexport function getPgBoss(): PgBoss {\n if (!boss) {\n throw new Error(\"PgBoss not started. Call startPgBoss() first.\");\n }\n return boss;\n}\n\nexport async function stopPgBoss(): Promise<void> {\n if (!boss) {\n return;\n }\n\n await boss.stop();\n boss = undefined;\n}\n","import { createHash } from \"node:crypto\";\nimport { PgBoss, type JobWithMetadata } from \"pg-boss\";\nimport type { JobHandler, JobQueue, StopFn } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\n/** Shared queue name for the platform control-plane scope. */\nexport const DEFAULT_QUEUE_NAME = \"keystroke\";\nconst PGBOSS_SCHEMA = \"pgboss\";\n\n/**\n * Derive a per-project pg-boss queue name. pg-boss queue names must be <= 50\n * chars, contain only [A-Za-z0-9_], and not start with a digit. Project ids are\n * usually UUIDs (hyphens, may start with a digit), so we sanitize, and fall back\n * to a hash when the sanitized name would exceed the length limit.\n */\nexport function pgBossProjectQueueName(projectId: string): string {\n const sanitized = projectId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_${createHash(\"sha1\").update(projectId).digest(\"hex\").slice(0, 40)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n};\n\ntype PgBossJobData = {\n kind: \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n targetId: string;\n runId: string;\n trigger: \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n payload?: unknown;\n attempt?: number;\n maxAttempts?: number;\n scheduledAt?: string;\n};\n\ntype BuildPgBossJobQueueOptions = {\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n};\n\nexport function buildPgBossJobQueue(\n boss: PgBoss,\n options: BuildPgBossJobQueueOptions = {},\n): JobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n\n return {\n async enqueue(input) {\n const jobId = await boss.send(queueName, input, {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n });\n\n if (!jobId) {\n if (input.dedupeKey) {\n return input.dedupeKey;\n }\n throw new Error(\"Failed to enqueue job\");\n }\n\n return jobId;\n },\n\n async startWorker(handler: JobHandler): Promise<StopFn> {\n let stopped = false;\n\n const workerId = await boss.work<PgBossJobData>(\n queueName,\n { batchSize: 1, includeMetadata: true },\n async (jobs: JobWithMetadata<PgBossJobData>[]) => {\n if (stopped) {\n return;\n }\n\n const job = jobs[0];\n if (!job) {\n return;\n }\n\n const data = job.data;\n const maxAttempts = data.maxAttempts ?? (job.retryLimit ?? 0) + 1;\n const attempt = (job.retryCount ?? 0) + 1;\n\n await handler({\n jobId: job.id,\n kind: data.kind,\n targetId: data.targetId,\n runId: data.runId,\n trigger: data.trigger,\n payload: data.payload ?? {},\n attempt,\n maxAttempts,\n exhaustedRetries: attempt >= maxAttempts,\n scheduledAt: data.scheduledAt ? new Date(data.scheduledAt) : new Date(),\n });\n },\n );\n\n return async () => {\n stopped = true;\n await boss.offWork(queueName, { id: workerId });\n if (options.stopBossOnWorkerStop) {\n await boss.stop({ graceful: true, timeout: 5_000 });\n }\n };\n },\n };\n}\n\nexport async function createPgBossQueue(options: PgBossQueueOptions): Promise<JobQueue> {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const boss = new PgBoss({\n connectionString: options.connectionString,\n schema: PGBOSS_SCHEMA,\n });\n await boss.start();\n await boss.createQueue(queueName);\n\n return buildPgBossJobQueue(boss, { stopBossOnWorkerStop: true, queueName });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n queueName: string = DEFAULT_QUEUE_NAME,\n): Promise<JobQueue> {\n await boss.createQueue(queueName);\n return buildPgBossJobQueue(boss, { queueName });\n}\n","import {\n DEFAULT_DATABASE_URL,\n getProjectScopeId,\n inferDialect,\n resolveProjectDatabaseUrlFromEnv,\n} from \"@keystrokehq/database\";\n\nimport {\n defineSchedulerPlugin,\n type SchedulerPlugin,\n type SchedulerPluginContext,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\n\nexport type { SchedulerPlugin, SchedulerPluginContext, SchedulerScope } from \"./contract\";\nexport { defineSchedulerPlugin } from \"./contract\";\n\nfunction resolveUrl(ctx: SchedulerPluginContext): string {\n return ctx.url ?? resolveProjectDatabaseUrlFromEnv(process.env) ?? DEFAULT_DATABASE_URL;\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; project scope gets a per-project queue. */\nexport function pgBossSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"pg-boss\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n\n if (ctx.scope === \"platform\") {\n await startPgBoss(url);\n return createSharedPgBossJobQueue(getPgBoss());\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n });\n },\n });\n}\n\n/** DB-table queue (Drizzle `jobs`), for SQLite / self-host. */\nexport function pollingSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"polling\",\n async createJobQueue() {\n return createDatabaseJobQueue();\n },\n });\n}\n\n/** Auto-selects pg-boss (postgres) or polling (sqlite) by dialect. */\nexport function defaultSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"default\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createJobQueue(ctx);\n }\n\n return pollingSchedulerPlugin().createJobQueue(ctx);\n },\n });\n}\n","import { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byAttachment?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n attachmentKey: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(attachmentKey, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byAttachment,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n selectActiveEphemeralScheduledAttachmentSlugs,\n selectTriggerScheduleBySlug,\n upsertTriggerSchedule,\n} from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { ScheduleSyncOptions } from \"./types\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\n\nexport async function syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void> {\n const now = new Date();\n const projectSlugs = options.schedules.map((schedule) => schedule.attachmentKey);\n // Ephemeral cron/poll schedules are managed by set_trigger, not project discovery — keep them out\n // of the disable sweep so a deploy/restart does not wipe agent-created triggers.\n const ephemeralSlugs = await selectActiveEphemeralScheduledAttachmentSlugs();\n const slugs = [...projectSlugs, ...ephemeralSlugs];\n\n if (slugs.length === 0) {\n await disableAllTriggerSchedules(now);\n return;\n }\n\n await disableTriggerSchedulesNotInSlugs(slugs, now);\n\n for (const spec of options.schedules) {\n const schedule = resolveTriggerSchedule(\n spec.attachmentKey,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerScheduleBySlug(spec.attachmentKey);\n const scheduleChanged = existing?.schedule !== schedule;\n const nextRunAt =\n existing && !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now);\n\n await upsertTriggerSchedule({\n attachmentSlug: spec.attachmentKey,\n kind: spec.kind,\n schedule,\n nextRunAt,\n enabled: true,\n updatedAt: now,\n });\n }\n}\n","import { createScheduleTicker } from \"./schedule-ticker\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport { syncTriggerSchedules as syncTriggerScheduleRows } from \"./sync-trigger-schedules\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n} from \"./types\";\n\nasync function createUnderlyingJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n if (options.adapter) {\n return options.adapter;\n }\n\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n return plugin.createJobQueue({\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue): Scheduler {\n const ticker = createScheduleTicker({ jobQueue });\n\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, options) => jobQueue.startWorker(handler, options),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => syncTriggerScheduleRows(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => ticker.startScheduleTicker(options),\n fireDueSchedules: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n return wrapScheduler(jobQueue);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport function wrapJobQueueAsScheduler(jobQueue: JobQueue): Scheduler {\n return wrapScheduler(jobQueue);\n}\n\nexport type { StopFn };\n","import type { EnqueueInput, JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nexport type MemoryJobQueueOptions = {\n sync?: boolean;\n};\n\nexport function createMemoryJobQueue(options: MemoryJobQueueOptions = {}): JobQueue {\n const pending: Array<{ id: string; input: EnqueueInput; enqueuedAt: Date }> = [];\n let handler: JobHandler | undefined;\n let draining = false;\n\n async function drain(): Promise<void> {\n if (!handler || draining) {\n return;\n }\n\n draining = true;\n try {\n while (pending.length > 0) {\n const next = pending.shift();\n if (!next) {\n break;\n }\n\n const input = next.input;\n await handler({\n jobId: next.id,\n kind: input.kind,\n targetId: input.targetId,\n runId: input.runId,\n trigger: input.trigger,\n payload: input.payload ?? {},\n attempt: input.attempt ?? 1,\n maxAttempts: input.maxAttempts ?? 3,\n scheduledAt: input.scheduledAt ?? next.enqueuedAt,\n });\n }\n } finally {\n draining = false;\n }\n }\n\n return {\n async enqueue(input) {\n const id = crypto.randomUUID();\n pending.push({ id, input, enqueuedAt: new Date() });\n\n if (options.sync) {\n await drain();\n }\n\n return id;\n },\n\n async startWorker(nextHandler: JobHandler, _opts: WorkerOptions = {}): Promise<StopFn> {\n handler = nextHandler;\n\n if (!options.sync) {\n void drain();\n }\n\n return async () => {\n handler = undefined;\n };\n },\n };\n}\n\nexport { retryDelayMs };\n","import { getPgBoss } from \"./pg-boss-client\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { createSharedPgBossJobQueue } from \"./pg-boss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet sharedJobQueue: JobQueue | undefined;\n\nexport async function createSharedPgBossScheduler(): Promise<Scheduler> {\n const jobQueue = await getSharedPgBossJobQueue();\n return wrapJobQueueAsScheduler(jobQueue);\n}\n\nexport async function getSharedPgBossJobQueue(): Promise<JobQueue> {\n if (sharedJobQueue) {\n return sharedJobQueue;\n }\n\n sharedJobQueue = await createSharedPgBossJobQueue(getPgBoss());\n return sharedJobQueue;\n}\n\nexport function resetSharedPgBossJobQueueForTests(): void {\n sharedJobQueue = undefined;\n}\n","import type { SchedulerPlugin } from \"./contract\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport {\n createSharedPgBossScheduler,\n resetSharedPgBossJobQueueForTests,\n} from \"./shared-pgboss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet configuredPlugin: SchedulerPlugin | undefined;\nlet sharedJobQueue: JobQueue | undefined;\n\nexport function configureSharedScheduler(plugin: SchedulerPlugin): void {\n configuredPlugin = plugin;\n sharedJobQueue = undefined;\n}\n\nexport async function createSharedScheduler(): Promise<Scheduler> {\n const plugin = configuredPlugin ?? defaultSchedulerPlugin();\n\n if (plugin.name === \"default\") {\n return createSharedPgBossScheduler();\n }\n\n if (!sharedJobQueue) {\n sharedJobQueue = await plugin.createJobQueue({ scope: \"platform\" });\n }\n\n return wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\n}\n","import type { SchedulerPlugin } from \"./contract\";\n\n/** Env var naming a module that exports `createSchedulerPlugin(env): SchedulerPlugin`. */\nexport const SCHEDULER_MODULE_ENV = \"KEYSTROKE_SCHEDULER_MODULE\";\n\ntype SchedulerEnv = Record<string, string | undefined>;\n\nfunction isSchedulerPlugin(value: unknown): value is SchedulerPlugin {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Record<string, unknown>;\n return typeof candidate.name === \"string\" && typeof candidate.createJobQueue === \"function\";\n}\n\nfunction getCreateSchedulerPlugin(mod: unknown): ((env: SchedulerEnv) => unknown) | undefined {\n if (typeof mod !== \"object\" || mod === null) {\n return undefined;\n }\n const factory = (mod as Record<string, unknown>).createSchedulerPlugin;\n return typeof factory === \"function\" ? (factory as (env: SchedulerEnv) => unknown) : undefined;\n}\n\n/**\n * Resolve a scheduler plugin selected via env so a producer (platform) and a\n * consumer (container worker) can never diverge on backend. When\n * `KEYSTROKE_SCHEDULER_MODULE` is set, dynamically import that module and call\n * its `createSchedulerPlugin(env)`\n */\nexport async function resolveSchedulerPluginFromEnv(\n env: SchedulerEnv = process.env,\n): Promise<SchedulerPlugin | undefined> {\n const moduleSpecifier = env[SCHEDULER_MODULE_ENV]?.trim();\n if (!moduleSpecifier) {\n return undefined;\n }\n\n const mod: unknown = await import(moduleSpecifier);\n const createSchedulerPlugin = getCreateSchedulerPlugin(mod);\n if (!createSchedulerPlugin) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" must export createSchedulerPlugin(env): SchedulerPlugin`,\n );\n }\n\n const plugin = createSchedulerPlugin(env);\n if (!isSchedulerPlugin(plugin)) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" createSchedulerPlugin(env) did not return a valid SchedulerPlugin (expected { name: string, createJobQueue: function })`,\n );\n }\n\n return plugin;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["sleep","EventEmitter","retryDelayMs","PgBoss","retryDelayMs","PgBoss","DEFAULT_DATABASE_URL","defineSchedulerPlugin","syncTriggerScheduleRows","sharedJobQueue"],"sources":["../src/schedule-ticker.ts","../src/cancel-channel.ts","../src/database-queue.ts","../src/pg-boss-client.ts","../src/pg-cancel-channel.ts","../src/pg-boss-queue.ts","../src/plugin.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts","../src/resolve-plugin-from-env.ts"],"sourcesContent":["import { claimDueTriggerSchedules } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport function createScheduleTicker(ctx: ScheduleTickerContext) {\n const pollIntervalMs = ctx.pollIntervalMs ?? 1_000;\n const batchSize = ctx.batchSize ?? 10;\n\n async function fireDueSchedules(asOf = new Date()): Promise<number> {\n const claimed = await claimDueTriggerSchedules(\n asOf,\n (schedule) => nextTriggerRunAt(schedule, asOf),\n batchSize,\n );\n\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n scheduledAt: asOf,\n });\n }\n\n return claimed.length;\n }\n\n async function startScheduleTicker(options: ScheduleTickerOptions = {}): Promise<StopFn> {\n const intervalMs = options.pollIntervalMs ?? pollIntervalMs;\n const limit = options.batchSize ?? batchSize;\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n await claimDueTriggerSchedules(\n new Date(),\n (schedule) => nextTriggerRunAt(schedule, new Date()),\n limit,\n ).then(async (claimed) => {\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n });\n }\n });\n } catch {\n // keep ticking\n }\n\n await sleep(intervalMs);\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n };\n }\n\n return { fireDueSchedules, startScheduleTicker };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { EventEmitter } from \"node:events\";\n\nimport type { CancelHandler, StopFn } from \"./types\";\n\n/** In-process cancel pub/sub for single-process queues (memory, db polling). */\nexport function createInProcessCancelChannel(): {\n publishCancel(runId: string): Promise<void>;\n subscribeCancel(handler: CancelHandler): Promise<StopFn>;\n} {\n const emitter = new EventEmitter();\n return {\n async publishCancel(runId) {\n emitter.emit(\"cancel\", runId);\n },\n\n async subscribeCancel(handler) {\n const listener = (runId: string) => {\n void handler(runId);\n };\n emitter.on(\"cancel\", listener);\n return async () => {\n emitter.off(\"cancel\", listener);\n };\n },\n };\n}\n","import {\n claimNextJob,\n enqueueJob,\n failWorkflowRun,\n markJobComplete,\n markJobFailed,\n requeueExpiredLeases,\n scheduleJobRetry,\n} from \"@keystrokehq/database\";\nimport type { ClaimedJob } from \"@keystrokehq/database\";\nimport { createInProcessCancelChannel } from \"./cancel-channel\";\nimport type { JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nfunction toJobPayload(job: ClaimedJob): Parameters<JobHandler>[0] {\n return {\n jobId: job.id,\n kind: job.kind,\n targetId: job.targetId,\n runId: job.runId,\n trigger: job.trigger,\n payload: job.payload,\n attempt: job.attempt,\n maxAttempts: job.maxAttempts,\n scheduledAt: job.scheduledAt,\n };\n}\n\nexport function createDatabaseJobQueue(): JobQueue {\n const cancelChannel = createInProcessCancelChannel();\n\n return {\n async enqueue(input) {\n return enqueueJob(input);\n },\n\n async startWorker(handler: JobHandler, options: WorkerOptions = {}): Promise<StopFn> {\n const workerId = options.workerId ?? crypto.randomUUID();\n const pollIntervalMs = options.pollIntervalMs ?? 250;\n const leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 30_000;\n let running = true;\n\n const leaseTimer = setInterval(() => {\n void requeueExpiredLeases();\n }, leaseSweepIntervalMs);\n\n const loop = async () => {\n while (running) {\n try {\n const job = await claimNextJob(workerId);\n if (!job) {\n await sleep(pollIntervalMs);\n continue;\n }\n\n try {\n await handler(toJobPayload(job));\n await markJobComplete(job.id);\n } catch (error) {\n if (job.attempt < job.maxAttempts) {\n await scheduleJobRetry(job.id, job.attempt + 1, retryDelayMs(job.attempt));\n } else {\n await markJobFailed(job.id, error);\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n }\n }\n } catch {\n await sleep(pollIntervalMs);\n }\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n clearInterval(leaseTimer);\n };\n },\n\n publishCancel: (runId) => cancelChannel.publishCancel(runId),\n subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler),\n };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { resolvePostgresUrlFromEnv } from \"@keystrokehq/database\";\nimport { PgBoss } from \"pg-boss\";\n\nlet boss: PgBoss | undefined;\n\nfunction resolveDatabaseUrl(url?: string): string {\n const resolved = url ?? resolvePostgresUrlFromEnv(process.env);\n if (!resolved) {\n throw new Error(\n \"DATABASE_URL or POSTGRES_HOST/POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB is required\",\n );\n }\n return resolved;\n}\n\nexport async function startPgBoss(url?: string): Promise<PgBoss> {\n if (boss) {\n return boss;\n }\n\n const next = new PgBoss({\n connectionString: resolveDatabaseUrl(url),\n schema: \"pgboss\",\n });\n next.on(\"error\", (error) => {\n console.error(\"[pg-boss]\", error);\n });\n\n await next.start();\n boss = next;\n return next;\n}\n\nexport function getPgBoss(): PgBoss {\n if (!boss) {\n throw new Error(\"PgBoss not started. Call startPgBoss() first.\");\n }\n return boss;\n}\n\nexport async function stopPgBoss(): Promise<void> {\n if (!boss) {\n return;\n }\n\n await boss.stop();\n boss = undefined;\n}\n","export { createPostgresCancelChannel as createPgCancelChannel } from \"@keystrokehq/database\";\n\n/** Postgres NOTIFY channel — lowercase identifier (project ids are UUIDs, well under 63 chars). */\nexport function pgCancelChannelName(scope: \"platform\" | \"project\", projectId?: string): string {\n if (scope === \"platform\") {\n return \"keystroke_cancel_platform\";\n }\n\n const sanitized = (projectId ?? \"default\").replace(/[^a-z0-9_]/gi, \"_\").toLowerCase();\n return `keystroke_cancel_${sanitized}`;\n}\n","import { createHash } from \"node:crypto\";\nimport { PgBoss, type JobWithMetadata } from \"pg-boss\";\nimport { createPgCancelChannel, pgCancelChannelName } from \"./pg-cancel-channel\";\nimport type { JobHandler, JobQueue, StopFn } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\n/** Shared queue name for the platform control-plane scope. */\nexport const DEFAULT_QUEUE_NAME = \"keystroke\";\nconst PGBOSS_SCHEMA = \"pgboss\";\n\n/**\n * Derive a per-project pg-boss queue name. pg-boss queue names must be <= 50\n * chars, contain only [A-Za-z0-9_], and not start with a digit. Project ids are\n * usually UUIDs (hyphens, may start with a digit), so we sanitize, and fall back\n * to a hash when the sanitized name would exceed the length limit.\n */\nexport function pgBossProjectQueueName(projectId: string): string {\n const sanitized = projectId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_${createHash(\"sha1\").update(projectId).digest(\"hex\").slice(0, 40)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n projectId?: string;\n};\n\ntype PgBossJobData = {\n kind: \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n targetId: string;\n runId: string;\n trigger: \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n payload?: unknown;\n attempt?: number;\n maxAttempts?: number;\n scheduledAt?: string;\n};\n\ntype BuildPgBossJobQueueOptions = {\n connectionString: string;\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n cancelScope?: \"platform\" | \"project\";\n projectId?: string;\n};\n\nexport function buildPgBossJobQueue(boss: PgBoss, options: BuildPgBossJobQueueOptions): JobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const cancelChannel = createPgCancelChannel(\n options.connectionString,\n pgCancelChannelName(options.cancelScope ?? \"platform\", options.projectId),\n );\n\n return {\n async enqueue(input) {\n const jobId = await boss.send(queueName, input, {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n });\n\n if (!jobId) {\n if (input.dedupeKey) {\n return input.dedupeKey;\n }\n throw new Error(\"Failed to enqueue job\");\n }\n\n return jobId;\n },\n\n async startWorker(handler: JobHandler): Promise<StopFn> {\n let stopped = false;\n\n const workerId = await boss.work<PgBossJobData>(\n queueName,\n { batchSize: 1, includeMetadata: true },\n async (jobs: JobWithMetadata<PgBossJobData>[]) => {\n if (stopped) {\n return;\n }\n\n const job = jobs[0];\n if (!job) {\n return;\n }\n\n const data = job.data;\n const maxAttempts = data.maxAttempts ?? (job.retryLimit ?? 0) + 1;\n const attempt = (job.retryCount ?? 0) + 1;\n\n await handler({\n jobId: job.id,\n kind: data.kind,\n targetId: data.targetId,\n runId: data.runId,\n trigger: data.trigger,\n payload: data.payload ?? {},\n attempt,\n maxAttempts,\n exhaustedRetries: attempt >= maxAttempts,\n scheduledAt: data.scheduledAt ? new Date(data.scheduledAt) : new Date(),\n });\n },\n );\n\n return async () => {\n stopped = true;\n await boss.offWork(queueName, { id: workerId });\n if (options.stopBossOnWorkerStop) {\n await boss.stop({ graceful: true, timeout: 5_000 });\n }\n };\n },\n\n publishCancel: (runId) => cancelChannel.publishCancel(runId),\n subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler),\n };\n}\n\nexport async function createPgBossQueue(options: PgBossQueueOptions): Promise<JobQueue> {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const boss = new PgBoss({\n connectionString: options.connectionString,\n schema: PGBOSS_SCHEMA,\n });\n await boss.start();\n await boss.createQueue(queueName);\n\n return buildPgBossJobQueue(boss, {\n stopBossOnWorkerStop: true,\n queueName,\n connectionString: options.connectionString,\n cancelScope: \"project\",\n projectId: options.projectId,\n });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n options: { connectionString: string; queueName?: string },\n): Promise<JobQueue> {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n await boss.createQueue(queueName);\n return buildPgBossJobQueue(boss, {\n queueName,\n connectionString: options.connectionString,\n cancelScope: \"platform\",\n });\n}\n","import {\n DEFAULT_DATABASE_URL,\n getProjectScopeId,\n inferDialect,\n resolveProjectDatabaseUrlFromEnv,\n} from \"@keystrokehq/database\";\n\nimport {\n defineSchedulerPlugin,\n type SchedulerPlugin,\n type SchedulerPluginContext,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\n\nexport type { SchedulerPlugin, SchedulerPluginContext, SchedulerScope } from \"./contract\";\nexport { defineSchedulerPlugin } from \"./contract\";\n\nfunction resolveUrl(ctx: SchedulerPluginContext): string {\n return ctx.url ?? resolveProjectDatabaseUrlFromEnv(process.env) ?? DEFAULT_DATABASE_URL;\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; project scope gets a per-project queue. */\nexport function pgBossSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"pg-boss\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n\n if (ctx.scope === \"platform\") {\n await startPgBoss(url);\n return createSharedPgBossJobQueue(getPgBoss(), { connectionString: url });\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n projectId,\n });\n },\n });\n}\n\n/** DB-table queue (Drizzle `jobs`), for SQLite / self-host. */\nexport function pollingSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"polling\",\n async createJobQueue() {\n return createDatabaseJobQueue();\n },\n });\n}\n\n/** Auto-selects pg-boss (postgres) or polling (sqlite) by dialect. */\nexport function defaultSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"default\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createJobQueue(ctx);\n }\n\n return pollingSchedulerPlugin().createJobQueue(ctx);\n },\n });\n}\n","import { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byAttachment?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n attachmentKey: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(attachmentKey, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byAttachment,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n selectActiveEphemeralScheduledAttachmentSlugs,\n selectTriggerScheduleBySlug,\n upsertTriggerSchedule,\n} from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { ScheduleSyncOptions } from \"./types\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\n\nexport async function syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void> {\n const now = new Date();\n const projectSlugs = options.schedules.map((schedule) => schedule.attachmentKey);\n // Ephemeral cron/poll schedules are managed by set_trigger, not project discovery — keep them out\n // of the disable sweep so a deploy/restart does not wipe agent-created triggers.\n const ephemeralSlugs = await selectActiveEphemeralScheduledAttachmentSlugs();\n const slugs = [...projectSlugs, ...ephemeralSlugs];\n\n if (slugs.length === 0) {\n await disableAllTriggerSchedules(now);\n return;\n }\n\n await disableTriggerSchedulesNotInSlugs(slugs, now);\n\n for (const spec of options.schedules) {\n const schedule = resolveTriggerSchedule(\n spec.attachmentKey,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerScheduleBySlug(spec.attachmentKey);\n const scheduleChanged = existing?.schedule !== schedule;\n const nextRunAt =\n existing && !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now);\n\n await upsertTriggerSchedule({\n attachmentSlug: spec.attachmentKey,\n kind: spec.kind,\n schedule,\n nextRunAt,\n enabled: true,\n updatedAt: now,\n });\n }\n}\n","import { createScheduleTicker } from \"./schedule-ticker\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport { syncTriggerSchedules as syncTriggerScheduleRows } from \"./sync-trigger-schedules\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n} from \"./types\";\n\nasync function createUnderlyingJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n if (options.adapter) {\n return options.adapter;\n }\n\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n return plugin.createJobQueue({\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue): Scheduler {\n const ticker = createScheduleTicker({ jobQueue });\n\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, options) => jobQueue.startWorker(handler, options),\n publishCancel: (runId) => jobQueue.publishCancel(runId),\n subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => syncTriggerScheduleRows(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => ticker.startScheduleTicker(options),\n fireDueSchedules: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n return wrapScheduler(jobQueue);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport function wrapJobQueueAsScheduler(jobQueue: JobQueue): Scheduler {\n return wrapScheduler(jobQueue);\n}\n\nexport type { StopFn };\n","import { createInProcessCancelChannel } from \"./cancel-channel\";\nimport type { EnqueueInput, JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nexport type MemoryJobQueueOptions = {\n sync?: boolean;\n};\n\nexport function createMemoryJobQueue(options: MemoryJobQueueOptions = {}): JobQueue {\n const cancelChannel = createInProcessCancelChannel();\n const pending: Array<{ id: string; input: EnqueueInput; enqueuedAt: Date }> = [];\n let handler: JobHandler | undefined;\n let draining = false;\n\n async function drain(): Promise<void> {\n if (!handler || draining) {\n return;\n }\n\n draining = true;\n try {\n while (pending.length > 0) {\n const next = pending.shift();\n if (!next) {\n break;\n }\n\n const input = next.input;\n await handler({\n jobId: next.id,\n kind: input.kind,\n targetId: input.targetId,\n runId: input.runId,\n trigger: input.trigger,\n payload: input.payload ?? {},\n attempt: input.attempt ?? 1,\n maxAttempts: input.maxAttempts ?? 3,\n scheduledAt: input.scheduledAt ?? next.enqueuedAt,\n });\n }\n } finally {\n draining = false;\n }\n }\n\n return {\n async enqueue(input) {\n const id = crypto.randomUUID();\n pending.push({ id, input, enqueuedAt: new Date() });\n\n if (options.sync) {\n await drain();\n }\n\n return id;\n },\n\n async startWorker(nextHandler: JobHandler, _opts: WorkerOptions = {}): Promise<StopFn> {\n handler = nextHandler;\n\n if (!options.sync) {\n void drain();\n }\n\n return async () => {\n handler = undefined;\n };\n },\n\n publishCancel: (runId) => cancelChannel.publishCancel(runId),\n subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler),\n };\n}\n\nexport { retryDelayMs };\n","import { resolvePostgresUrlFromEnv } from \"@keystrokehq/database\";\nimport { getPgBoss } from \"./pg-boss-client\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { createSharedPgBossJobQueue } from \"./pg-boss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet sharedJobQueue: JobQueue | undefined;\n\nexport async function createSharedPgBossScheduler(): Promise<Scheduler> {\n const jobQueue = await getSharedPgBossJobQueue();\n return wrapJobQueueAsScheduler(jobQueue);\n}\n\nexport async function getSharedPgBossJobQueue(): Promise<JobQueue> {\n if (sharedJobQueue) {\n return sharedJobQueue;\n }\n\n const connectionString = resolvePostgresUrlFromEnv(process.env);\n if (!connectionString) {\n throw new Error(\"Postgres connection string is required for shared pg-boss queue\");\n }\n\n sharedJobQueue = await createSharedPgBossJobQueue(getPgBoss(), { connectionString });\n return sharedJobQueue;\n}\n\nexport function resetSharedPgBossJobQueueForTests(): void {\n sharedJobQueue = undefined;\n}\n","import type { SchedulerPlugin } from \"./contract\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport {\n createSharedPgBossScheduler,\n resetSharedPgBossJobQueueForTests,\n} from \"./shared-pgboss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet configuredPlugin: SchedulerPlugin | undefined;\nlet sharedJobQueue: JobQueue | undefined;\n\nexport function configureSharedScheduler(plugin: SchedulerPlugin): void {\n configuredPlugin = plugin;\n sharedJobQueue = undefined;\n}\n\nexport async function createSharedScheduler(): Promise<Scheduler> {\n const plugin = configuredPlugin ?? defaultSchedulerPlugin();\n\n if (plugin.name === \"default\") {\n return createSharedPgBossScheduler();\n }\n\n if (!sharedJobQueue) {\n sharedJobQueue = await plugin.createJobQueue({ scope: \"platform\" });\n }\n\n return wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\n}\n","import type { SchedulerPlugin } from \"./contract\";\n\n/** Env var naming a module that exports `createSchedulerPlugin(env): SchedulerPlugin`. */\nexport const SCHEDULER_MODULE_ENV = \"KEYSTROKE_SCHEDULER_MODULE\";\n\ntype SchedulerEnv = Record<string, string | undefined>;\n\nfunction isSchedulerPlugin(value: unknown): value is SchedulerPlugin {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Record<string, unknown>;\n return typeof candidate.name === \"string\" && typeof candidate.createJobQueue === \"function\";\n}\n\nfunction getCreateSchedulerPlugin(mod: unknown): ((env: SchedulerEnv) => unknown) | undefined {\n if (typeof mod !== \"object\" || mod === null) {\n return undefined;\n }\n const factory = (mod as Record<string, unknown>).createSchedulerPlugin;\n return typeof factory === \"function\" ? (factory as (env: SchedulerEnv) => unknown) : undefined;\n}\n\n/**\n * Resolve a scheduler plugin selected via env so a producer (platform) and a\n * consumer (container worker) can never diverge on backend. When\n * `KEYSTROKE_SCHEDULER_MODULE` is set, dynamically import that module and call\n * its `createSchedulerPlugin(env)`\n */\nexport async function resolveSchedulerPluginFromEnv(\n env: SchedulerEnv = process.env,\n): Promise<SchedulerPlugin | undefined> {\n const moduleSpecifier = env[SCHEDULER_MODULE_ENV]?.trim();\n if (!moduleSpecifier) {\n return undefined;\n }\n\n const mod: unknown = await import(moduleSpecifier);\n const createSchedulerPlugin = getCreateSchedulerPlugin(mod);\n if (!createSchedulerPlugin) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" must export createSchedulerPlugin(env): SchedulerPlugin`,\n );\n }\n\n const plugin = createSchedulerPlugin(env);\n if (!isSchedulerPlugin(plugin)) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" createSchedulerPlugin(env) did not return a valid SchedulerPlugin (expected { name: string, createJobQueue: function })`,\n );\n }\n\n return plugin;\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as ScheduleTickerOptions, a as defineSchedulerPlugin, b as WorkerOptions, c as CreateSchedulerOptions, d as JobHandler, g as ScheduleSyncOptions, h as JobTrigger, i as SchedulerScope, l as DEFAULT_RETRY_DELAY_MS, m as JobQueue, n as SchedulerPlugin, p as JobPayload, r as SchedulerPluginContext, s as CreateJobQueueOptions, u as EnqueueInput, v as Scheduler, x as retryDelayMs, y as StopFn } from "./contract-DAqQua3D.cjs";
|
|
2
2
|
import { PgBoss } from "pg-boss";
|
|
3
3
|
|
|
4
4
|
//#region src/create-scheduler.d.ts
|
|
@@ -58,11 +58,17 @@ declare const DEFAULT_QUEUE_NAME = "keystroke";
|
|
|
58
58
|
*/
|
|
59
59
|
declare function pgBossProjectQueueName(projectId: string): string;
|
|
60
60
|
type BuildPgBossJobQueueOptions = {
|
|
61
|
+
connectionString: string;
|
|
61
62
|
stopBossOnWorkerStop?: boolean;
|
|
62
63
|
queueName?: string;
|
|
64
|
+
cancelScope?: "platform" | "project";
|
|
65
|
+
projectId?: string;
|
|
63
66
|
};
|
|
64
|
-
declare function buildPgBossJobQueue(boss: PgBoss, options
|
|
65
|
-
declare function createSharedPgBossJobQueue(boss: PgBoss,
|
|
67
|
+
declare function buildPgBossJobQueue(boss: PgBoss, options: BuildPgBossJobQueueOptions): JobQueue;
|
|
68
|
+
declare function createSharedPgBossJobQueue(boss: PgBoss, options: {
|
|
69
|
+
connectionString: string;
|
|
70
|
+
queueName?: string;
|
|
71
|
+
}): Promise<JobQueue>;
|
|
66
72
|
//#endregion
|
|
67
73
|
export { type CreateJobQueueOptions, type CreateSchedulerOptions, DEFAULT_QUEUE_NAME, DEFAULT_RETRY_DELAY_MS, type EnqueueInput, type JobHandler, type JobPayload, type JobQueue, type JobTrigger, type MemoryJobQueueOptions, SCHEDULER_MODULE_ENV, type ScheduleSyncOptions, type ScheduleTickerOptions, type Scheduler, type SchedulerPlugin, type SchedulerPluginContext, type SchedulerScope, type StopFn, type WorkerOptions, buildPgBossJobQueue, configureSharedScheduler, createJobQueue, createMemoryJobQueue, createScheduler, createSharedPgBossJobQueue, createSharedPgBossScheduler, createSharedScheduler, defaultSchedulerPlugin, defineSchedulerPlugin, getPgBoss, getSharedPgBossJobQueue, pgBossProjectQueueName, pgBossSchedulerPlugin, pollingSchedulerPlugin, resetSharedPgBossJobQueueForTests, resetSharedSchedulerForTests, resolveSchedulerPluginFromEnv, retryDelayMs, startPgBoss, stopPgBoss, wrapJobQueueAsScheduler };
|
|
68
74
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/create-scheduler.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/resolve-plugin-from-env.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/create-scheduler.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/resolve-plugin-from-env.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts"],"mappings":";;;;iBA0CsB,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,OAAA,CAAQ,SAAA;AAAA,iBAK/D,cAAA,CAAe,OAAA,GAAS,qBAAA,GAA6B,OAAA,CAAQ,QAAA;AAAA,iBAInE,uBAAA,CAAwB,QAAA,EAAU,QAAA,GAAW,SAAS;;;KC/C1D,qBAAA;EACV,IAAI;AAAA;AAAA,iBAGU,oBAAA,CAAqB,OAAA,GAAS,qBAAA,GAA6B,QAAQ;;;iBCInE,wBAAA,CAAyB,MAAuB,EAAf,eAAe;AAAA,iBAK1C,qBAAA,CAAA,GAAyB,OAAO,CAAC,SAAA;AAAA,iBAcvC,4BAAA,CAAA;;;;iBCHA,qBAAA,CAAA,GAAyB,eAAe;;iBAsBxC,sBAAA,CAAA,GAA0B,eAAe;;iBAUzC,sBAAA,CAAA,GAA0B,eAAe;;;;cCzD5C,oBAAA;AAAA,KAER,YAAA,GAAe,MAAM;AJqC1B;;;;;;AAAA,iBIbsB,6BAAA,CACpB,GAAA,GAAK,YAAA,GACJ,OAAA,CAAQ,eAAA;;;iBChBW,WAAA,CAAY,GAAA,YAAe,OAAO,CAAC,MAAA;AAAA,iBAkBzC,SAAA,CAAA,GAAa,MAAM;AAAA,iBAOb,UAAA,CAAA,GAAc,OAAO;;;iBChCrB,2BAAA,CAAA,GAA+B,OAAO,CAAC,SAAA;AAAA,iBAKvC,uBAAA,CAAA,GAA2B,OAAO,CAAC,QAAA;AAAA,iBAczC,iCAAA,CAAA;;;;cCpBH,kBAAA;APmCb;;;;;;AAAA,iBO1BgB,sBAAA,CAAuB,SAAiB;AAAA,KA0BnD,0BAAA;EACH,gBAAA;EACA,oBAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;AAAA;AAAA,iBAGc,mBAAA,CAAoB,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,0BAAA,GAA6B,QAAA;AAAA,iBA6FlE,0BAAA,CACpB,IAAA,EAAM,MAAA,EACN,OAAA;EAAW,gBAAA;EAA0B,SAAA;AAAA,IACpC,OAAA,CAAQ,QAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as ScheduleTickerOptions, a as defineSchedulerPlugin, b as WorkerOptions, c as CreateSchedulerOptions, d as JobHandler, g as ScheduleSyncOptions, h as JobTrigger, i as SchedulerScope, l as DEFAULT_RETRY_DELAY_MS, m as JobQueue, n as SchedulerPlugin, p as JobPayload, r as SchedulerPluginContext, s as CreateJobQueueOptions, u as EnqueueInput, v as Scheduler, x as retryDelayMs, y as StopFn } from "./contract-DAqQua3D.mjs";
|
|
2
2
|
import { PgBoss } from "pg-boss";
|
|
3
3
|
|
|
4
4
|
//#region src/create-scheduler.d.ts
|
|
@@ -58,11 +58,17 @@ declare const DEFAULT_QUEUE_NAME = "keystroke";
|
|
|
58
58
|
*/
|
|
59
59
|
declare function pgBossProjectQueueName(projectId: string): string;
|
|
60
60
|
type BuildPgBossJobQueueOptions = {
|
|
61
|
+
connectionString: string;
|
|
61
62
|
stopBossOnWorkerStop?: boolean;
|
|
62
63
|
queueName?: string;
|
|
64
|
+
cancelScope?: "platform" | "project";
|
|
65
|
+
projectId?: string;
|
|
63
66
|
};
|
|
64
|
-
declare function buildPgBossJobQueue(boss: PgBoss, options
|
|
65
|
-
declare function createSharedPgBossJobQueue(boss: PgBoss,
|
|
67
|
+
declare function buildPgBossJobQueue(boss: PgBoss, options: BuildPgBossJobQueueOptions): JobQueue;
|
|
68
|
+
declare function createSharedPgBossJobQueue(boss: PgBoss, options: {
|
|
69
|
+
connectionString: string;
|
|
70
|
+
queueName?: string;
|
|
71
|
+
}): Promise<JobQueue>;
|
|
66
72
|
//#endregion
|
|
67
73
|
export { type CreateJobQueueOptions, type CreateSchedulerOptions, DEFAULT_QUEUE_NAME, DEFAULT_RETRY_DELAY_MS, type EnqueueInput, type JobHandler, type JobPayload, type JobQueue, type JobTrigger, type MemoryJobQueueOptions, SCHEDULER_MODULE_ENV, type ScheduleSyncOptions, type ScheduleTickerOptions, type Scheduler, type SchedulerPlugin, type SchedulerPluginContext, type SchedulerScope, type StopFn, type WorkerOptions, buildPgBossJobQueue, configureSharedScheduler, createJobQueue, createMemoryJobQueue, createScheduler, createSharedPgBossJobQueue, createSharedPgBossScheduler, createSharedScheduler, defaultSchedulerPlugin, defineSchedulerPlugin, getPgBoss, getSharedPgBossJobQueue, pgBossProjectQueueName, pgBossSchedulerPlugin, pollingSchedulerPlugin, resetSharedPgBossJobQueueForTests, resetSharedSchedulerForTests, resolveSchedulerPluginFromEnv, retryDelayMs, startPgBoss, stopPgBoss, wrapJobQueueAsScheduler };
|
|
68
74
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/create-scheduler.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/resolve-plugin-from-env.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/create-scheduler.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/resolve-plugin-from-env.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts"],"mappings":";;;;iBA0CsB,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,OAAA,CAAQ,SAAA;AAAA,iBAK/D,cAAA,CAAe,OAAA,GAAS,qBAAA,GAA6B,OAAA,CAAQ,QAAA;AAAA,iBAInE,uBAAA,CAAwB,QAAA,EAAU,QAAA,GAAW,SAAS;;;KC/C1D,qBAAA;EACV,IAAI;AAAA;AAAA,iBAGU,oBAAA,CAAqB,OAAA,GAAS,qBAAA,GAA6B,QAAQ;;;iBCInE,wBAAA,CAAyB,MAAuB,EAAf,eAAe;AAAA,iBAK1C,qBAAA,CAAA,GAAyB,OAAO,CAAC,SAAA;AAAA,iBAcvC,4BAAA,CAAA;;;;iBCHA,qBAAA,CAAA,GAAyB,eAAe;;iBAsBxC,sBAAA,CAAA,GAA0B,eAAe;;iBAUzC,sBAAA,CAAA,GAA0B,eAAe;;;;cCzD5C,oBAAA;AAAA,KAER,YAAA,GAAe,MAAM;AJqC1B;;;;;;AAAA,iBIbsB,6BAAA,CACpB,GAAA,GAAK,YAAA,GACJ,OAAA,CAAQ,eAAA;;;iBChBW,WAAA,CAAY,GAAA,YAAe,OAAO,CAAC,MAAA;AAAA,iBAkBzC,SAAA,CAAA,GAAa,MAAM;AAAA,iBAOb,UAAA,CAAA,GAAc,OAAO;;;iBChCrB,2BAAA,CAAA,GAA+B,OAAO,CAAC,SAAA;AAAA,iBAKvC,uBAAA,CAAA,GAA2B,OAAO,CAAC,QAAA;AAAA,iBAczC,iCAAA,CAAA;;;;cCpBH,kBAAA;APmCb;;;;;;AAAA,iBO1BgB,sBAAA,CAAuB,SAAiB;AAAA,KA0BnD,0BAAA;EACH,gBAAA;EACA,oBAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;AAAA;AAAA,iBAGc,mBAAA,CAAoB,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,0BAAA,GAA6B,QAAA;AAAA,iBA6FlE,0BAAA,CACpB,IAAA,EAAM,MAAA,EACN,OAAA;EAAW,gBAAA;EAA0B,SAAA;AAAA,IACpC,OAAA,CAAQ,QAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { n as DEFAULT_RETRY_DELAY_MS, r as retryDelayMs, t as defineSchedulerPlugin } from "./contract-E1QJBH6_.mjs";
|
|
2
|
-
import { DEFAULT_DATABASE_URL, claimDueTriggerSchedules, claimNextJob, disableAllTriggerSchedules, disableTriggerSchedulesNotInSlugs, enqueueJob, failWorkflowRun, getProjectScopeId, inferDialect, markJobComplete, markJobFailed, requeueExpiredLeases, resolvePostgresUrlFromEnv, resolveProjectDatabaseUrlFromEnv, scheduleJobRetry, selectActiveEphemeralScheduledAttachmentSlugs, selectTriggerScheduleBySlug, upsertTriggerSchedule } from "@keystrokehq/database";
|
|
2
|
+
import { DEFAULT_DATABASE_URL, claimDueTriggerSchedules, claimNextJob, createPostgresCancelChannel as createPgCancelChannel, disableAllTriggerSchedules, disableTriggerSchedulesNotInSlugs, enqueueJob, failWorkflowRun, getProjectScopeId, inferDialect, markJobComplete, markJobFailed, requeueExpiredLeases, resolvePostgresUrlFromEnv, resolveProjectDatabaseUrlFromEnv, scheduleJobRetry, selectActiveEphemeralScheduledAttachmentSlugs, selectTriggerScheduleBySlug, upsertTriggerSchedule } from "@keystrokehq/database";
|
|
3
3
|
import { nextTriggerRunAt, resolveCronSchedule } from "@keystrokehq/trigger";
|
|
4
|
+
import { EventEmitter } from "node:events";
|
|
4
5
|
import { PgBoss } from "pg-boss";
|
|
5
6
|
import { createHash } from "node:crypto";
|
|
6
7
|
//#region src/schedule-ticker.ts
|
|
@@ -53,6 +54,26 @@ function sleep$1(ms) {
|
|
|
53
54
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
54
55
|
}
|
|
55
56
|
//#endregion
|
|
57
|
+
//#region src/cancel-channel.ts
|
|
58
|
+
/** In-process cancel pub/sub for single-process queues (memory, db polling). */
|
|
59
|
+
function createInProcessCancelChannel() {
|
|
60
|
+
const emitter = new EventEmitter();
|
|
61
|
+
return {
|
|
62
|
+
async publishCancel(runId) {
|
|
63
|
+
emitter.emit("cancel", runId);
|
|
64
|
+
},
|
|
65
|
+
async subscribeCancel(handler) {
|
|
66
|
+
const listener = (runId) => {
|
|
67
|
+
handler(runId);
|
|
68
|
+
};
|
|
69
|
+
emitter.on("cancel", listener);
|
|
70
|
+
return async () => {
|
|
71
|
+
emitter.off("cancel", listener);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
56
77
|
//#region src/database-queue.ts
|
|
57
78
|
function toJobPayload(job) {
|
|
58
79
|
return {
|
|
@@ -68,6 +89,7 @@ function toJobPayload(job) {
|
|
|
68
89
|
};
|
|
69
90
|
}
|
|
70
91
|
function createDatabaseJobQueue() {
|
|
92
|
+
const cancelChannel = createInProcessCancelChannel();
|
|
71
93
|
return {
|
|
72
94
|
async enqueue(input) {
|
|
73
95
|
return enqueueJob(input);
|
|
@@ -106,7 +128,9 @@ function createDatabaseJobQueue() {
|
|
|
106
128
|
running = false;
|
|
107
129
|
clearInterval(leaseTimer);
|
|
108
130
|
};
|
|
109
|
-
}
|
|
131
|
+
},
|
|
132
|
+
publishCancel: (runId) => cancelChannel.publishCancel(runId),
|
|
133
|
+
subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
|
|
110
134
|
};
|
|
111
135
|
}
|
|
112
136
|
function sleep(ms) {
|
|
@@ -143,6 +167,13 @@ async function stopPgBoss() {
|
|
|
143
167
|
boss = void 0;
|
|
144
168
|
}
|
|
145
169
|
//#endregion
|
|
170
|
+
//#region src/pg-cancel-channel.ts
|
|
171
|
+
/** Postgres NOTIFY channel — lowercase identifier (project ids are UUIDs, well under 63 chars). */
|
|
172
|
+
function pgCancelChannelName(scope, projectId) {
|
|
173
|
+
if (scope === "platform") return "keystroke_cancel_platform";
|
|
174
|
+
return `keystroke_cancel_${(projectId ?? "default").replace(/[^a-z0-9_]/gi, "_").toLowerCase()}`;
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
146
177
|
//#region src/pg-boss-queue.ts
|
|
147
178
|
/** Shared queue name for the platform control-plane scope. */
|
|
148
179
|
const DEFAULT_QUEUE_NAME = "keystroke";
|
|
@@ -158,8 +189,9 @@ function pgBossProjectQueueName(projectId) {
|
|
|
158
189
|
if (name.length <= 50) return name;
|
|
159
190
|
return `${DEFAULT_QUEUE_NAME}_${createHash("sha1").update(projectId).digest("hex").slice(0, 40)}`;
|
|
160
191
|
}
|
|
161
|
-
function buildPgBossJobQueue(boss, options
|
|
192
|
+
function buildPgBossJobQueue(boss, options) {
|
|
162
193
|
const queueName = options.queueName ?? "keystroke";
|
|
194
|
+
const cancelChannel = createPgCancelChannel(options.connectionString, pgCancelChannelName(options.cancelScope ?? "platform", options.projectId));
|
|
163
195
|
return {
|
|
164
196
|
async enqueue(input) {
|
|
165
197
|
const jobId = await boss.send(queueName, input, {
|
|
@@ -207,7 +239,9 @@ function buildPgBossJobQueue(boss, options = {}) {
|
|
|
207
239
|
timeout: 5e3
|
|
208
240
|
});
|
|
209
241
|
};
|
|
210
|
-
}
|
|
242
|
+
},
|
|
243
|
+
publishCancel: (runId) => cancelChannel.publishCancel(runId),
|
|
244
|
+
subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
|
|
211
245
|
};
|
|
212
246
|
}
|
|
213
247
|
async function createPgBossQueue(options) {
|
|
@@ -220,12 +254,20 @@ async function createPgBossQueue(options) {
|
|
|
220
254
|
await boss.createQueue(queueName);
|
|
221
255
|
return buildPgBossJobQueue(boss, {
|
|
222
256
|
stopBossOnWorkerStop: true,
|
|
223
|
-
queueName
|
|
257
|
+
queueName,
|
|
258
|
+
connectionString: options.connectionString,
|
|
259
|
+
cancelScope: "project",
|
|
260
|
+
projectId: options.projectId
|
|
224
261
|
});
|
|
225
262
|
}
|
|
226
|
-
async function createSharedPgBossJobQueue(boss,
|
|
263
|
+
async function createSharedPgBossJobQueue(boss, options) {
|
|
264
|
+
const queueName = options.queueName ?? "keystroke";
|
|
227
265
|
await boss.createQueue(queueName);
|
|
228
|
-
return buildPgBossJobQueue(boss, {
|
|
266
|
+
return buildPgBossJobQueue(boss, {
|
|
267
|
+
queueName,
|
|
268
|
+
connectionString: options.connectionString,
|
|
269
|
+
cancelScope: "platform"
|
|
270
|
+
});
|
|
229
271
|
}
|
|
230
272
|
//#endregion
|
|
231
273
|
//#region src/plugin.ts
|
|
@@ -240,11 +282,13 @@ function pgBossSchedulerPlugin() {
|
|
|
240
282
|
const url = resolveUrl(ctx);
|
|
241
283
|
if (ctx.scope === "platform") {
|
|
242
284
|
await startPgBoss(url);
|
|
243
|
-
return createSharedPgBossJobQueue(getPgBoss());
|
|
285
|
+
return createSharedPgBossJobQueue(getPgBoss(), { connectionString: url });
|
|
244
286
|
}
|
|
287
|
+
const projectId = ctx.projectId ?? getProjectScopeId();
|
|
245
288
|
return createPgBossQueue({
|
|
246
289
|
connectionString: url,
|
|
247
|
-
queueName: pgBossProjectQueueName(
|
|
290
|
+
queueName: pgBossProjectQueueName(projectId),
|
|
291
|
+
projectId
|
|
248
292
|
});
|
|
249
293
|
}
|
|
250
294
|
});
|
|
@@ -320,6 +364,8 @@ function wrapScheduler(jobQueue) {
|
|
|
320
364
|
return {
|
|
321
365
|
enqueue: (input) => jobQueue.enqueue(input),
|
|
322
366
|
startWorker: (handler, options) => jobQueue.startWorker(handler, options),
|
|
367
|
+
publishCancel: (runId) => jobQueue.publishCancel(runId),
|
|
368
|
+
subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),
|
|
323
369
|
syncTriggerSchedules: (options) => syncTriggerSchedules(options),
|
|
324
370
|
startScheduleTicker: (options) => ticker.startScheduleTicker(options),
|
|
325
371
|
fireDueSchedules: (asOf) => ticker.fireDueSchedules(asOf)
|
|
@@ -337,6 +383,7 @@ function wrapJobQueueAsScheduler(jobQueue) {
|
|
|
337
383
|
//#endregion
|
|
338
384
|
//#region src/memory.ts
|
|
339
385
|
function createMemoryJobQueue(options = {}) {
|
|
386
|
+
const cancelChannel = createInProcessCancelChannel();
|
|
340
387
|
const pending = [];
|
|
341
388
|
let handler;
|
|
342
389
|
let draining = false;
|
|
@@ -381,7 +428,9 @@ function createMemoryJobQueue(options = {}) {
|
|
|
381
428
|
return async () => {
|
|
382
429
|
handler = void 0;
|
|
383
430
|
};
|
|
384
|
-
}
|
|
431
|
+
},
|
|
432
|
+
publishCancel: (runId) => cancelChannel.publishCancel(runId),
|
|
433
|
+
subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
|
|
385
434
|
};
|
|
386
435
|
}
|
|
387
436
|
//#endregion
|
|
@@ -392,7 +441,9 @@ async function createSharedPgBossScheduler() {
|
|
|
392
441
|
}
|
|
393
442
|
async function getSharedPgBossJobQueue() {
|
|
394
443
|
if (sharedJobQueue$1) return sharedJobQueue$1;
|
|
395
|
-
|
|
444
|
+
const connectionString = resolvePostgresUrlFromEnv(process.env);
|
|
445
|
+
if (!connectionString) throw new Error("Postgres connection string is required for shared pg-boss queue");
|
|
446
|
+
sharedJobQueue$1 = await createSharedPgBossJobQueue(getPgBoss(), { connectionString });
|
|
396
447
|
return sharedJobQueue$1;
|
|
397
448
|
}
|
|
398
449
|
function resetSharedPgBossJobQueueForTests() {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["sleep","syncTriggerScheduleRows","sharedJobQueue"],"sources":["../src/schedule-ticker.ts","../src/database-queue.ts","../src/pg-boss-client.ts","../src/pg-boss-queue.ts","../src/plugin.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts","../src/resolve-plugin-from-env.ts"],"sourcesContent":["import { claimDueTriggerSchedules } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport function createScheduleTicker(ctx: ScheduleTickerContext) {\n const pollIntervalMs = ctx.pollIntervalMs ?? 1_000;\n const batchSize = ctx.batchSize ?? 10;\n\n async function fireDueSchedules(asOf = new Date()): Promise<number> {\n const claimed = await claimDueTriggerSchedules(\n asOf,\n (schedule) => nextTriggerRunAt(schedule, asOf),\n batchSize,\n );\n\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n scheduledAt: asOf,\n });\n }\n\n return claimed.length;\n }\n\n async function startScheduleTicker(options: ScheduleTickerOptions = {}): Promise<StopFn> {\n const intervalMs = options.pollIntervalMs ?? pollIntervalMs;\n const limit = options.batchSize ?? batchSize;\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n await claimDueTriggerSchedules(\n new Date(),\n (schedule) => nextTriggerRunAt(schedule, new Date()),\n limit,\n ).then(async (claimed) => {\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n });\n }\n });\n } catch {\n // keep ticking\n }\n\n await sleep(intervalMs);\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n };\n }\n\n return { fireDueSchedules, startScheduleTicker };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import {\n claimNextJob,\n enqueueJob,\n failWorkflowRun,\n markJobComplete,\n markJobFailed,\n requeueExpiredLeases,\n scheduleJobRetry,\n} from \"@keystrokehq/database\";\nimport type { ClaimedJob } from \"@keystrokehq/database\";\nimport type { JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nfunction toJobPayload(job: ClaimedJob): Parameters<JobHandler>[0] {\n return {\n jobId: job.id,\n kind: job.kind,\n targetId: job.targetId,\n runId: job.runId,\n trigger: job.trigger,\n payload: job.payload,\n attempt: job.attempt,\n maxAttempts: job.maxAttempts,\n scheduledAt: job.scheduledAt,\n };\n}\n\nexport function createDatabaseJobQueue(): JobQueue {\n return {\n async enqueue(input) {\n return enqueueJob(input);\n },\n\n async startWorker(handler: JobHandler, options: WorkerOptions = {}): Promise<StopFn> {\n const workerId = options.workerId ?? crypto.randomUUID();\n const pollIntervalMs = options.pollIntervalMs ?? 250;\n const leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 30_000;\n let running = true;\n\n const leaseTimer = setInterval(() => {\n void requeueExpiredLeases();\n }, leaseSweepIntervalMs);\n\n const loop = async () => {\n while (running) {\n try {\n const job = await claimNextJob(workerId);\n if (!job) {\n await sleep(pollIntervalMs);\n continue;\n }\n\n try {\n await handler(toJobPayload(job));\n await markJobComplete(job.id);\n } catch (error) {\n if (job.attempt < job.maxAttempts) {\n await scheduleJobRetry(job.id, job.attempt + 1, retryDelayMs(job.attempt));\n } else {\n await markJobFailed(job.id, error);\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n }\n }\n } catch {\n await sleep(pollIntervalMs);\n }\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n clearInterval(leaseTimer);\n };\n },\n };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { resolvePostgresUrlFromEnv } from \"@keystrokehq/database\";\nimport { PgBoss } from \"pg-boss\";\n\nlet boss: PgBoss | undefined;\n\nfunction resolveDatabaseUrl(url?: string): string {\n const resolved = url ?? resolvePostgresUrlFromEnv(process.env);\n if (!resolved) {\n throw new Error(\n \"DATABASE_URL or POSTGRES_HOST/POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB is required\",\n );\n }\n return resolved;\n}\n\nexport async function startPgBoss(url?: string): Promise<PgBoss> {\n if (boss) {\n return boss;\n }\n\n const next = new PgBoss({\n connectionString: resolveDatabaseUrl(url),\n schema: \"pgboss\",\n });\n next.on(\"error\", (error) => {\n console.error(\"[pg-boss]\", error);\n });\n\n await next.start();\n boss = next;\n return next;\n}\n\nexport function getPgBoss(): PgBoss {\n if (!boss) {\n throw new Error(\"PgBoss not started. Call startPgBoss() first.\");\n }\n return boss;\n}\n\nexport async function stopPgBoss(): Promise<void> {\n if (!boss) {\n return;\n }\n\n await boss.stop();\n boss = undefined;\n}\n","import { createHash } from \"node:crypto\";\nimport { PgBoss, type JobWithMetadata } from \"pg-boss\";\nimport type { JobHandler, JobQueue, StopFn } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\n/** Shared queue name for the platform control-plane scope. */\nexport const DEFAULT_QUEUE_NAME = \"keystroke\";\nconst PGBOSS_SCHEMA = \"pgboss\";\n\n/**\n * Derive a per-project pg-boss queue name. pg-boss queue names must be <= 50\n * chars, contain only [A-Za-z0-9_], and not start with a digit. Project ids are\n * usually UUIDs (hyphens, may start with a digit), so we sanitize, and fall back\n * to a hash when the sanitized name would exceed the length limit.\n */\nexport function pgBossProjectQueueName(projectId: string): string {\n const sanitized = projectId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_${createHash(\"sha1\").update(projectId).digest(\"hex\").slice(0, 40)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n};\n\ntype PgBossJobData = {\n kind: \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n targetId: string;\n runId: string;\n trigger: \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n payload?: unknown;\n attempt?: number;\n maxAttempts?: number;\n scheduledAt?: string;\n};\n\ntype BuildPgBossJobQueueOptions = {\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n};\n\nexport function buildPgBossJobQueue(\n boss: PgBoss,\n options: BuildPgBossJobQueueOptions = {},\n): JobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n\n return {\n async enqueue(input) {\n const jobId = await boss.send(queueName, input, {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n });\n\n if (!jobId) {\n if (input.dedupeKey) {\n return input.dedupeKey;\n }\n throw new Error(\"Failed to enqueue job\");\n }\n\n return jobId;\n },\n\n async startWorker(handler: JobHandler): Promise<StopFn> {\n let stopped = false;\n\n const workerId = await boss.work<PgBossJobData>(\n queueName,\n { batchSize: 1, includeMetadata: true },\n async (jobs: JobWithMetadata<PgBossJobData>[]) => {\n if (stopped) {\n return;\n }\n\n const job = jobs[0];\n if (!job) {\n return;\n }\n\n const data = job.data;\n const maxAttempts = data.maxAttempts ?? (job.retryLimit ?? 0) + 1;\n const attempt = (job.retryCount ?? 0) + 1;\n\n await handler({\n jobId: job.id,\n kind: data.kind,\n targetId: data.targetId,\n runId: data.runId,\n trigger: data.trigger,\n payload: data.payload ?? {},\n attempt,\n maxAttempts,\n exhaustedRetries: attempt >= maxAttempts,\n scheduledAt: data.scheduledAt ? new Date(data.scheduledAt) : new Date(),\n });\n },\n );\n\n return async () => {\n stopped = true;\n await boss.offWork(queueName, { id: workerId });\n if (options.stopBossOnWorkerStop) {\n await boss.stop({ graceful: true, timeout: 5_000 });\n }\n };\n },\n };\n}\n\nexport async function createPgBossQueue(options: PgBossQueueOptions): Promise<JobQueue> {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const boss = new PgBoss({\n connectionString: options.connectionString,\n schema: PGBOSS_SCHEMA,\n });\n await boss.start();\n await boss.createQueue(queueName);\n\n return buildPgBossJobQueue(boss, { stopBossOnWorkerStop: true, queueName });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n queueName: string = DEFAULT_QUEUE_NAME,\n): Promise<JobQueue> {\n await boss.createQueue(queueName);\n return buildPgBossJobQueue(boss, { queueName });\n}\n","import {\n DEFAULT_DATABASE_URL,\n getProjectScopeId,\n inferDialect,\n resolveProjectDatabaseUrlFromEnv,\n} from \"@keystrokehq/database\";\n\nimport {\n defineSchedulerPlugin,\n type SchedulerPlugin,\n type SchedulerPluginContext,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\n\nexport type { SchedulerPlugin, SchedulerPluginContext, SchedulerScope } from \"./contract\";\nexport { defineSchedulerPlugin } from \"./contract\";\n\nfunction resolveUrl(ctx: SchedulerPluginContext): string {\n return ctx.url ?? resolveProjectDatabaseUrlFromEnv(process.env) ?? DEFAULT_DATABASE_URL;\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; project scope gets a per-project queue. */\nexport function pgBossSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"pg-boss\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n\n if (ctx.scope === \"platform\") {\n await startPgBoss(url);\n return createSharedPgBossJobQueue(getPgBoss());\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n });\n },\n });\n}\n\n/** DB-table queue (Drizzle `jobs`), for SQLite / self-host. */\nexport function pollingSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"polling\",\n async createJobQueue() {\n return createDatabaseJobQueue();\n },\n });\n}\n\n/** Auto-selects pg-boss (postgres) or polling (sqlite) by dialect. */\nexport function defaultSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"default\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createJobQueue(ctx);\n }\n\n return pollingSchedulerPlugin().createJobQueue(ctx);\n },\n });\n}\n","import { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byAttachment?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n attachmentKey: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(attachmentKey, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byAttachment,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n selectActiveEphemeralScheduledAttachmentSlugs,\n selectTriggerScheduleBySlug,\n upsertTriggerSchedule,\n} from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { ScheduleSyncOptions } from \"./types\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\n\nexport async function syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void> {\n const now = new Date();\n const projectSlugs = options.schedules.map((schedule) => schedule.attachmentKey);\n // Ephemeral cron/poll schedules are managed by set_trigger, not project discovery — keep them out\n // of the disable sweep so a deploy/restart does not wipe agent-created triggers.\n const ephemeralSlugs = await selectActiveEphemeralScheduledAttachmentSlugs();\n const slugs = [...projectSlugs, ...ephemeralSlugs];\n\n if (slugs.length === 0) {\n await disableAllTriggerSchedules(now);\n return;\n }\n\n await disableTriggerSchedulesNotInSlugs(slugs, now);\n\n for (const spec of options.schedules) {\n const schedule = resolveTriggerSchedule(\n spec.attachmentKey,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerScheduleBySlug(spec.attachmentKey);\n const scheduleChanged = existing?.schedule !== schedule;\n const nextRunAt =\n existing && !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now);\n\n await upsertTriggerSchedule({\n attachmentSlug: spec.attachmentKey,\n kind: spec.kind,\n schedule,\n nextRunAt,\n enabled: true,\n updatedAt: now,\n });\n }\n}\n","import { createScheduleTicker } from \"./schedule-ticker\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport { syncTriggerSchedules as syncTriggerScheduleRows } from \"./sync-trigger-schedules\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n} from \"./types\";\n\nasync function createUnderlyingJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n if (options.adapter) {\n return options.adapter;\n }\n\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n return plugin.createJobQueue({\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue): Scheduler {\n const ticker = createScheduleTicker({ jobQueue });\n\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, options) => jobQueue.startWorker(handler, options),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => syncTriggerScheduleRows(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => ticker.startScheduleTicker(options),\n fireDueSchedules: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n return wrapScheduler(jobQueue);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport function wrapJobQueueAsScheduler(jobQueue: JobQueue): Scheduler {\n return wrapScheduler(jobQueue);\n}\n\nexport type { StopFn };\n","import type { EnqueueInput, JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nexport type MemoryJobQueueOptions = {\n sync?: boolean;\n};\n\nexport function createMemoryJobQueue(options: MemoryJobQueueOptions = {}): JobQueue {\n const pending: Array<{ id: string; input: EnqueueInput; enqueuedAt: Date }> = [];\n let handler: JobHandler | undefined;\n let draining = false;\n\n async function drain(): Promise<void> {\n if (!handler || draining) {\n return;\n }\n\n draining = true;\n try {\n while (pending.length > 0) {\n const next = pending.shift();\n if (!next) {\n break;\n }\n\n const input = next.input;\n await handler({\n jobId: next.id,\n kind: input.kind,\n targetId: input.targetId,\n runId: input.runId,\n trigger: input.trigger,\n payload: input.payload ?? {},\n attempt: input.attempt ?? 1,\n maxAttempts: input.maxAttempts ?? 3,\n scheduledAt: input.scheduledAt ?? next.enqueuedAt,\n });\n }\n } finally {\n draining = false;\n }\n }\n\n return {\n async enqueue(input) {\n const id = crypto.randomUUID();\n pending.push({ id, input, enqueuedAt: new Date() });\n\n if (options.sync) {\n await drain();\n }\n\n return id;\n },\n\n async startWorker(nextHandler: JobHandler, _opts: WorkerOptions = {}): Promise<StopFn> {\n handler = nextHandler;\n\n if (!options.sync) {\n void drain();\n }\n\n return async () => {\n handler = undefined;\n };\n },\n };\n}\n\nexport { retryDelayMs };\n","import { getPgBoss } from \"./pg-boss-client\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { createSharedPgBossJobQueue } from \"./pg-boss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet sharedJobQueue: JobQueue | undefined;\n\nexport async function createSharedPgBossScheduler(): Promise<Scheduler> {\n const jobQueue = await getSharedPgBossJobQueue();\n return wrapJobQueueAsScheduler(jobQueue);\n}\n\nexport async function getSharedPgBossJobQueue(): Promise<JobQueue> {\n if (sharedJobQueue) {\n return sharedJobQueue;\n }\n\n sharedJobQueue = await createSharedPgBossJobQueue(getPgBoss());\n return sharedJobQueue;\n}\n\nexport function resetSharedPgBossJobQueueForTests(): void {\n sharedJobQueue = undefined;\n}\n","import type { SchedulerPlugin } from \"./contract\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport {\n createSharedPgBossScheduler,\n resetSharedPgBossJobQueueForTests,\n} from \"./shared-pgboss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet configuredPlugin: SchedulerPlugin | undefined;\nlet sharedJobQueue: JobQueue | undefined;\n\nexport function configureSharedScheduler(plugin: SchedulerPlugin): void {\n configuredPlugin = plugin;\n sharedJobQueue = undefined;\n}\n\nexport async function createSharedScheduler(): Promise<Scheduler> {\n const plugin = configuredPlugin ?? defaultSchedulerPlugin();\n\n if (plugin.name === \"default\") {\n return createSharedPgBossScheduler();\n }\n\n if (!sharedJobQueue) {\n sharedJobQueue = await plugin.createJobQueue({ scope: \"platform\" });\n }\n\n return wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\n}\n","import type { SchedulerPlugin } from \"./contract\";\n\n/** Env var naming a module that exports `createSchedulerPlugin(env): SchedulerPlugin`. */\nexport const SCHEDULER_MODULE_ENV = \"KEYSTROKE_SCHEDULER_MODULE\";\n\ntype SchedulerEnv = Record<string, string | undefined>;\n\nfunction isSchedulerPlugin(value: unknown): value is SchedulerPlugin {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Record<string, unknown>;\n return typeof candidate.name === \"string\" && typeof candidate.createJobQueue === \"function\";\n}\n\nfunction getCreateSchedulerPlugin(mod: unknown): ((env: SchedulerEnv) => unknown) | undefined {\n if (typeof mod !== \"object\" || mod === null) {\n return undefined;\n }\n const factory = (mod as Record<string, unknown>).createSchedulerPlugin;\n return typeof factory === \"function\" ? (factory as (env: SchedulerEnv) => unknown) : undefined;\n}\n\n/**\n * Resolve a scheduler plugin selected via env so a producer (platform) and a\n * consumer (container worker) can never diverge on backend. When\n * `KEYSTROKE_SCHEDULER_MODULE` is set, dynamically import that module and call\n * its `createSchedulerPlugin(env)`\n */\nexport async function resolveSchedulerPluginFromEnv(\n env: SchedulerEnv = process.env,\n): Promise<SchedulerPlugin | undefined> {\n const moduleSpecifier = env[SCHEDULER_MODULE_ENV]?.trim();\n if (!moduleSpecifier) {\n return undefined;\n }\n\n const mod: unknown = await import(moduleSpecifier);\n const createSchedulerPlugin = getCreateSchedulerPlugin(mod);\n if (!createSchedulerPlugin) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" must export createSchedulerPlugin(env): SchedulerPlugin`,\n );\n }\n\n const plugin = createSchedulerPlugin(env);\n if (!isSchedulerPlugin(plugin)) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" createSchedulerPlugin(env) did not return a valid SchedulerPlugin (expected { name: string, createJobQueue: function })`,\n );\n }\n\n return plugin;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["sleep","syncTriggerScheduleRows","sharedJobQueue"],"sources":["../src/schedule-ticker.ts","../src/cancel-channel.ts","../src/database-queue.ts","../src/pg-boss-client.ts","../src/pg-cancel-channel.ts","../src/pg-boss-queue.ts","../src/plugin.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts","../src/resolve-plugin-from-env.ts"],"sourcesContent":["import { claimDueTriggerSchedules } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\nexport function createScheduleTicker(ctx: ScheduleTickerContext) {\n const pollIntervalMs = ctx.pollIntervalMs ?? 1_000;\n const batchSize = ctx.batchSize ?? 10;\n\n async function fireDueSchedules(asOf = new Date()): Promise<number> {\n const claimed = await claimDueTriggerSchedules(\n asOf,\n (schedule) => nextTriggerRunAt(schedule, asOf),\n batchSize,\n );\n\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n scheduledAt: asOf,\n });\n }\n\n return claimed.length;\n }\n\n async function startScheduleTicker(options: ScheduleTickerOptions = {}): Promise<StopFn> {\n const intervalMs = options.pollIntervalMs ?? pollIntervalMs;\n const limit = options.batchSize ?? batchSize;\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n await claimDueTriggerSchedules(\n new Date(),\n (schedule) => nextTriggerRunAt(schedule, new Date()),\n limit,\n ).then(async (claimed) => {\n for (const row of claimed) {\n await ctx.jobQueue.enqueue({\n kind: \"trigger\",\n targetId: row.attachmentSlug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n });\n }\n });\n } catch {\n // keep ticking\n }\n\n await sleep(intervalMs);\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n };\n }\n\n return { fireDueSchedules, startScheduleTicker };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { EventEmitter } from \"node:events\";\n\nimport type { CancelHandler, StopFn } from \"./types\";\n\n/** In-process cancel pub/sub for single-process queues (memory, db polling). */\nexport function createInProcessCancelChannel(): {\n publishCancel(runId: string): Promise<void>;\n subscribeCancel(handler: CancelHandler): Promise<StopFn>;\n} {\n const emitter = new EventEmitter();\n return {\n async publishCancel(runId) {\n emitter.emit(\"cancel\", runId);\n },\n\n async subscribeCancel(handler) {\n const listener = (runId: string) => {\n void handler(runId);\n };\n emitter.on(\"cancel\", listener);\n return async () => {\n emitter.off(\"cancel\", listener);\n };\n },\n };\n}\n","import {\n claimNextJob,\n enqueueJob,\n failWorkflowRun,\n markJobComplete,\n markJobFailed,\n requeueExpiredLeases,\n scheduleJobRetry,\n} from \"@keystrokehq/database\";\nimport type { ClaimedJob } from \"@keystrokehq/database\";\nimport { createInProcessCancelChannel } from \"./cancel-channel\";\nimport type { JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nfunction toJobPayload(job: ClaimedJob): Parameters<JobHandler>[0] {\n return {\n jobId: job.id,\n kind: job.kind,\n targetId: job.targetId,\n runId: job.runId,\n trigger: job.trigger,\n payload: job.payload,\n attempt: job.attempt,\n maxAttempts: job.maxAttempts,\n scheduledAt: job.scheduledAt,\n };\n}\n\nexport function createDatabaseJobQueue(): JobQueue {\n const cancelChannel = createInProcessCancelChannel();\n\n return {\n async enqueue(input) {\n return enqueueJob(input);\n },\n\n async startWorker(handler: JobHandler, options: WorkerOptions = {}): Promise<StopFn> {\n const workerId = options.workerId ?? crypto.randomUUID();\n const pollIntervalMs = options.pollIntervalMs ?? 250;\n const leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 30_000;\n let running = true;\n\n const leaseTimer = setInterval(() => {\n void requeueExpiredLeases();\n }, leaseSweepIntervalMs);\n\n const loop = async () => {\n while (running) {\n try {\n const job = await claimNextJob(workerId);\n if (!job) {\n await sleep(pollIntervalMs);\n continue;\n }\n\n try {\n await handler(toJobPayload(job));\n await markJobComplete(job.id);\n } catch (error) {\n if (job.attempt < job.maxAttempts) {\n await scheduleJobRetry(job.id, job.attempt + 1, retryDelayMs(job.attempt));\n } else {\n await markJobFailed(job.id, error);\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n }\n }\n } catch {\n await sleep(pollIntervalMs);\n }\n }\n };\n\n void loop();\n\n return async () => {\n running = false;\n clearInterval(leaseTimer);\n };\n },\n\n publishCancel: (runId) => cancelChannel.publishCancel(runId),\n subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler),\n };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { resolvePostgresUrlFromEnv } from \"@keystrokehq/database\";\nimport { PgBoss } from \"pg-boss\";\n\nlet boss: PgBoss | undefined;\n\nfunction resolveDatabaseUrl(url?: string): string {\n const resolved = url ?? resolvePostgresUrlFromEnv(process.env);\n if (!resolved) {\n throw new Error(\n \"DATABASE_URL or POSTGRES_HOST/POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB is required\",\n );\n }\n return resolved;\n}\n\nexport async function startPgBoss(url?: string): Promise<PgBoss> {\n if (boss) {\n return boss;\n }\n\n const next = new PgBoss({\n connectionString: resolveDatabaseUrl(url),\n schema: \"pgboss\",\n });\n next.on(\"error\", (error) => {\n console.error(\"[pg-boss]\", error);\n });\n\n await next.start();\n boss = next;\n return next;\n}\n\nexport function getPgBoss(): PgBoss {\n if (!boss) {\n throw new Error(\"PgBoss not started. Call startPgBoss() first.\");\n }\n return boss;\n}\n\nexport async function stopPgBoss(): Promise<void> {\n if (!boss) {\n return;\n }\n\n await boss.stop();\n boss = undefined;\n}\n","export { createPostgresCancelChannel as createPgCancelChannel } from \"@keystrokehq/database\";\n\n/** Postgres NOTIFY channel — lowercase identifier (project ids are UUIDs, well under 63 chars). */\nexport function pgCancelChannelName(scope: \"platform\" | \"project\", projectId?: string): string {\n if (scope === \"platform\") {\n return \"keystroke_cancel_platform\";\n }\n\n const sanitized = (projectId ?? \"default\").replace(/[^a-z0-9_]/gi, \"_\").toLowerCase();\n return `keystroke_cancel_${sanitized}`;\n}\n","import { createHash } from \"node:crypto\";\nimport { PgBoss, type JobWithMetadata } from \"pg-boss\";\nimport { createPgCancelChannel, pgCancelChannelName } from \"./pg-cancel-channel\";\nimport type { JobHandler, JobQueue, StopFn } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\n/** Shared queue name for the platform control-plane scope. */\nexport const DEFAULT_QUEUE_NAME = \"keystroke\";\nconst PGBOSS_SCHEMA = \"pgboss\";\n\n/**\n * Derive a per-project pg-boss queue name. pg-boss queue names must be <= 50\n * chars, contain only [A-Za-z0-9_], and not start with a digit. Project ids are\n * usually UUIDs (hyphens, may start with a digit), so we sanitize, and fall back\n * to a hash when the sanitized name would exceed the length limit.\n */\nexport function pgBossProjectQueueName(projectId: string): string {\n const sanitized = projectId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_${createHash(\"sha1\").update(projectId).digest(\"hex\").slice(0, 40)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n projectId?: string;\n};\n\ntype PgBossJobData = {\n kind: \"workflow\" | \"agent\" | \"trigger\" | \"runtime\";\n targetId: string;\n runId: string;\n trigger: \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n payload?: unknown;\n attempt?: number;\n maxAttempts?: number;\n scheduledAt?: string;\n};\n\ntype BuildPgBossJobQueueOptions = {\n connectionString: string;\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n cancelScope?: \"platform\" | \"project\";\n projectId?: string;\n};\n\nexport function buildPgBossJobQueue(boss: PgBoss, options: BuildPgBossJobQueueOptions): JobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const cancelChannel = createPgCancelChannel(\n options.connectionString,\n pgCancelChannelName(options.cancelScope ?? \"platform\", options.projectId),\n );\n\n return {\n async enqueue(input) {\n const jobId = await boss.send(queueName, input, {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n });\n\n if (!jobId) {\n if (input.dedupeKey) {\n return input.dedupeKey;\n }\n throw new Error(\"Failed to enqueue job\");\n }\n\n return jobId;\n },\n\n async startWorker(handler: JobHandler): Promise<StopFn> {\n let stopped = false;\n\n const workerId = await boss.work<PgBossJobData>(\n queueName,\n { batchSize: 1, includeMetadata: true },\n async (jobs: JobWithMetadata<PgBossJobData>[]) => {\n if (stopped) {\n return;\n }\n\n const job = jobs[0];\n if (!job) {\n return;\n }\n\n const data = job.data;\n const maxAttempts = data.maxAttempts ?? (job.retryLimit ?? 0) + 1;\n const attempt = (job.retryCount ?? 0) + 1;\n\n await handler({\n jobId: job.id,\n kind: data.kind,\n targetId: data.targetId,\n runId: data.runId,\n trigger: data.trigger,\n payload: data.payload ?? {},\n attempt,\n maxAttempts,\n exhaustedRetries: attempt >= maxAttempts,\n scheduledAt: data.scheduledAt ? new Date(data.scheduledAt) : new Date(),\n });\n },\n );\n\n return async () => {\n stopped = true;\n await boss.offWork(queueName, { id: workerId });\n if (options.stopBossOnWorkerStop) {\n await boss.stop({ graceful: true, timeout: 5_000 });\n }\n };\n },\n\n publishCancel: (runId) => cancelChannel.publishCancel(runId),\n subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler),\n };\n}\n\nexport async function createPgBossQueue(options: PgBossQueueOptions): Promise<JobQueue> {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const boss = new PgBoss({\n connectionString: options.connectionString,\n schema: PGBOSS_SCHEMA,\n });\n await boss.start();\n await boss.createQueue(queueName);\n\n return buildPgBossJobQueue(boss, {\n stopBossOnWorkerStop: true,\n queueName,\n connectionString: options.connectionString,\n cancelScope: \"project\",\n projectId: options.projectId,\n });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n options: { connectionString: string; queueName?: string },\n): Promise<JobQueue> {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n await boss.createQueue(queueName);\n return buildPgBossJobQueue(boss, {\n queueName,\n connectionString: options.connectionString,\n cancelScope: \"platform\",\n });\n}\n","import {\n DEFAULT_DATABASE_URL,\n getProjectScopeId,\n inferDialect,\n resolveProjectDatabaseUrlFromEnv,\n} from \"@keystrokehq/database\";\n\nimport {\n defineSchedulerPlugin,\n type SchedulerPlugin,\n type SchedulerPluginContext,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\n\nexport type { SchedulerPlugin, SchedulerPluginContext, SchedulerScope } from \"./contract\";\nexport { defineSchedulerPlugin } from \"./contract\";\n\nfunction resolveUrl(ctx: SchedulerPluginContext): string {\n return ctx.url ?? resolveProjectDatabaseUrlFromEnv(process.env) ?? DEFAULT_DATABASE_URL;\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; project scope gets a per-project queue. */\nexport function pgBossSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"pg-boss\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n\n if (ctx.scope === \"platform\") {\n await startPgBoss(url);\n return createSharedPgBossJobQueue(getPgBoss(), { connectionString: url });\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n projectId,\n });\n },\n });\n}\n\n/** DB-table queue (Drizzle `jobs`), for SQLite / self-host. */\nexport function pollingSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"polling\",\n async createJobQueue() {\n return createDatabaseJobQueue();\n },\n });\n}\n\n/** Auto-selects pg-boss (postgres) or polling (sqlite) by dialect. */\nexport function defaultSchedulerPlugin(): SchedulerPlugin {\n return defineSchedulerPlugin({\n name: \"default\",\n async createJobQueue(ctx) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createJobQueue(ctx);\n }\n\n return pollingSchedulerPlugin().createJobQueue(ctx);\n },\n });\n}\n","import { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byAttachment?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n attachmentKey: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(attachmentKey, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byAttachment,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n selectActiveEphemeralScheduledAttachmentSlugs,\n selectTriggerScheduleBySlug,\n upsertTriggerSchedule,\n} from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { ScheduleSyncOptions } from \"./types\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\n\nexport async function syncTriggerSchedules(options: ScheduleSyncOptions): Promise<void> {\n const now = new Date();\n const projectSlugs = options.schedules.map((schedule) => schedule.attachmentKey);\n // Ephemeral cron/poll schedules are managed by set_trigger, not project discovery — keep them out\n // of the disable sweep so a deploy/restart does not wipe agent-created triggers.\n const ephemeralSlugs = await selectActiveEphemeralScheduledAttachmentSlugs();\n const slugs = [...projectSlugs, ...ephemeralSlugs];\n\n if (slugs.length === 0) {\n await disableAllTriggerSchedules(now);\n return;\n }\n\n await disableTriggerSchedulesNotInSlugs(slugs, now);\n\n for (const spec of options.schedules) {\n const schedule = resolveTriggerSchedule(\n spec.attachmentKey,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerScheduleBySlug(spec.attachmentKey);\n const scheduleChanged = existing?.schedule !== schedule;\n const nextRunAt =\n existing && !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now);\n\n await upsertTriggerSchedule({\n attachmentSlug: spec.attachmentKey,\n kind: spec.kind,\n schedule,\n nextRunAt,\n enabled: true,\n updatedAt: now,\n });\n }\n}\n","import { createScheduleTicker } from \"./schedule-ticker\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport { syncTriggerSchedules as syncTriggerScheduleRows } from \"./sync-trigger-schedules\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n} from \"./types\";\n\nasync function createUnderlyingJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n if (options.adapter) {\n return options.adapter;\n }\n\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n return plugin.createJobQueue({\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue): Scheduler {\n const ticker = createScheduleTicker({ jobQueue });\n\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, options) => jobQueue.startWorker(handler, options),\n publishCancel: (runId) => jobQueue.publishCancel(runId),\n subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => syncTriggerScheduleRows(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => ticker.startScheduleTicker(options),\n fireDueSchedules: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n return wrapScheduler(jobQueue);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport function wrapJobQueueAsScheduler(jobQueue: JobQueue): Scheduler {\n return wrapScheduler(jobQueue);\n}\n\nexport type { StopFn };\n","import { createInProcessCancelChannel } from \"./cancel-channel\";\nimport type { EnqueueInput, JobHandler, JobQueue, StopFn, WorkerOptions } from \"./types\";\nimport { retryDelayMs } from \"./types\";\n\nexport type MemoryJobQueueOptions = {\n sync?: boolean;\n};\n\nexport function createMemoryJobQueue(options: MemoryJobQueueOptions = {}): JobQueue {\n const cancelChannel = createInProcessCancelChannel();\n const pending: Array<{ id: string; input: EnqueueInput; enqueuedAt: Date }> = [];\n let handler: JobHandler | undefined;\n let draining = false;\n\n async function drain(): Promise<void> {\n if (!handler || draining) {\n return;\n }\n\n draining = true;\n try {\n while (pending.length > 0) {\n const next = pending.shift();\n if (!next) {\n break;\n }\n\n const input = next.input;\n await handler({\n jobId: next.id,\n kind: input.kind,\n targetId: input.targetId,\n runId: input.runId,\n trigger: input.trigger,\n payload: input.payload ?? {},\n attempt: input.attempt ?? 1,\n maxAttempts: input.maxAttempts ?? 3,\n scheduledAt: input.scheduledAt ?? next.enqueuedAt,\n });\n }\n } finally {\n draining = false;\n }\n }\n\n return {\n async enqueue(input) {\n const id = crypto.randomUUID();\n pending.push({ id, input, enqueuedAt: new Date() });\n\n if (options.sync) {\n await drain();\n }\n\n return id;\n },\n\n async startWorker(nextHandler: JobHandler, _opts: WorkerOptions = {}): Promise<StopFn> {\n handler = nextHandler;\n\n if (!options.sync) {\n void drain();\n }\n\n return async () => {\n handler = undefined;\n };\n },\n\n publishCancel: (runId) => cancelChannel.publishCancel(runId),\n subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler),\n };\n}\n\nexport { retryDelayMs };\n","import { resolvePostgresUrlFromEnv } from \"@keystrokehq/database\";\nimport { getPgBoss } from \"./pg-boss-client\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { createSharedPgBossJobQueue } from \"./pg-boss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet sharedJobQueue: JobQueue | undefined;\n\nexport async function createSharedPgBossScheduler(): Promise<Scheduler> {\n const jobQueue = await getSharedPgBossJobQueue();\n return wrapJobQueueAsScheduler(jobQueue);\n}\n\nexport async function getSharedPgBossJobQueue(): Promise<JobQueue> {\n if (sharedJobQueue) {\n return sharedJobQueue;\n }\n\n const connectionString = resolvePostgresUrlFromEnv(process.env);\n if (!connectionString) {\n throw new Error(\"Postgres connection string is required for shared pg-boss queue\");\n }\n\n sharedJobQueue = await createSharedPgBossJobQueue(getPgBoss(), { connectionString });\n return sharedJobQueue;\n}\n\nexport function resetSharedPgBossJobQueueForTests(): void {\n sharedJobQueue = undefined;\n}\n","import type { SchedulerPlugin } from \"./contract\";\nimport { wrapJobQueueAsScheduler } from \"./create-scheduler\";\nimport { defaultSchedulerPlugin } from \"./plugin\";\nimport {\n createSharedPgBossScheduler,\n resetSharedPgBossJobQueueForTests,\n} from \"./shared-pgboss-queue\";\nimport type { JobQueue, Scheduler } from \"./types\";\n\nlet configuredPlugin: SchedulerPlugin | undefined;\nlet sharedJobQueue: JobQueue | undefined;\n\nexport function configureSharedScheduler(plugin: SchedulerPlugin): void {\n configuredPlugin = plugin;\n sharedJobQueue = undefined;\n}\n\nexport async function createSharedScheduler(): Promise<Scheduler> {\n const plugin = configuredPlugin ?? defaultSchedulerPlugin();\n\n if (plugin.name === \"default\") {\n return createSharedPgBossScheduler();\n }\n\n if (!sharedJobQueue) {\n sharedJobQueue = await plugin.createJobQueue({ scope: \"platform\" });\n }\n\n return wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\n}\n","import type { SchedulerPlugin } from \"./contract\";\n\n/** Env var naming a module that exports `createSchedulerPlugin(env): SchedulerPlugin`. */\nexport const SCHEDULER_MODULE_ENV = \"KEYSTROKE_SCHEDULER_MODULE\";\n\ntype SchedulerEnv = Record<string, string | undefined>;\n\nfunction isSchedulerPlugin(value: unknown): value is SchedulerPlugin {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Record<string, unknown>;\n return typeof candidate.name === \"string\" && typeof candidate.createJobQueue === \"function\";\n}\n\nfunction getCreateSchedulerPlugin(mod: unknown): ((env: SchedulerEnv) => unknown) | undefined {\n if (typeof mod !== \"object\" || mod === null) {\n return undefined;\n }\n const factory = (mod as Record<string, unknown>).createSchedulerPlugin;\n return typeof factory === \"function\" ? (factory as (env: SchedulerEnv) => unknown) : undefined;\n}\n\n/**\n * Resolve a scheduler plugin selected via env so a producer (platform) and a\n * consumer (container worker) can never diverge on backend. When\n * `KEYSTROKE_SCHEDULER_MODULE` is set, dynamically import that module and call\n * its `createSchedulerPlugin(env)`\n */\nexport async function resolveSchedulerPluginFromEnv(\n env: SchedulerEnv = process.env,\n): Promise<SchedulerPlugin | undefined> {\n const moduleSpecifier = env[SCHEDULER_MODULE_ENV]?.trim();\n if (!moduleSpecifier) {\n return undefined;\n }\n\n const mod: unknown = await import(moduleSpecifier);\n const createSchedulerPlugin = getCreateSchedulerPlugin(mod);\n if (!createSchedulerPlugin) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" must export createSchedulerPlugin(env): SchedulerPlugin`,\n );\n }\n\n const plugin = createSchedulerPlugin(env);\n if (!isSchedulerPlugin(plugin)) {\n throw new Error(\n `Scheduler module \"${moduleSpecifier}\" createSchedulerPlugin(env) did not return a valid SchedulerPlugin (expected { name: string, createJobQueue: function })`,\n );\n }\n\n return plugin;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keystrokehq/scheduler",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.158",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,16 +43,16 @@
|
|
|
43
43
|
"tsdown": "^0.22.0",
|
|
44
44
|
"typescript": "^6.0.3",
|
|
45
45
|
"vitest": "^4.1.7",
|
|
46
|
-
"@keystrokehq/database": "0.0.
|
|
46
|
+
"@keystrokehq/database": "0.0.100",
|
|
47
47
|
"@keystrokehq/oxlint-config": "0.0.3",
|
|
48
|
-
"@keystrokehq/trigger": "0.0.
|
|
48
|
+
"@keystrokehq/trigger": "0.0.104",
|
|
49
49
|
"@keystrokehq/tsconfig": "0.0.3",
|
|
50
50
|
"@keystrokehq/tsdown-config": "0.0.3",
|
|
51
51
|
"@keystrokehq/vitest-config": "0.0.4"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@keystrokehq/database": "0.0.
|
|
55
|
-
"@keystrokehq/trigger": "0.0.
|
|
54
|
+
"@keystrokehq/database": "0.0.100",
|
|
55
|
+
"@keystrokehq/trigger": "0.0.104"
|
|
56
56
|
},
|
|
57
57
|
"peerDependenciesMeta": {
|
|
58
58
|
"@keystrokehq/database": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contract-B3JBTWO3.d.cts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":";KAAY,OAAA;AAAA,KAEA,UAAA;AAAA,KAEA,UAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,OAAA;EACA,WAAA,UAToB;EAWpB,gBAAA;EACA,WAAA,EAAa,IAAA;EACb,KAAA;AAAA;AAAA,KAGU,YAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,WAAA,GAAc,IAAA;EACd,OAAA;EACA,WAAA,WAlBS;EAoBT,SAAA;AAAA;AAAA,KAGU,UAAA,IAAc,GAAA,EAAK,UAAA,KAAe,OAAO;AAAA,KAEzC,MAAA,SAAe,OAAO;AAAA,KAEtB,aAAA;EACV,QAAA;EACA,cAAA;EACA,oBAAA;AAAA;AAAA,KAGU,QAAA;EACV,OAAA,CAAQ,KAAA,EAAO,YAAA,GAAe,OAAA;EAC9B,WAAA,CAAY,OAAA,EAAS,UAAA,EAAY,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA;AAAA,KAKzD,qBAAA;EACV,GAAA;EACA,OAAA;EACA,OAAA,GAAU,QAAA;EACV,MAAA,GAAS,eAAA;EACT,KAAA,GAAQ,cAAA;EACR,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,mBAAA;EACV,aAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,KAGU,mBAAA;EACV,SAAA,EAAW,mBAAA;EACX,iBAAA;IACE,MAAA;IACA,YAAA,GAAe,MAAM;EAAA;AAAA;AAAA,KAIb,qBAAA;EACV,cAAA;EACA,SAAS;AAAA;AAAA,KAGC,SAAA,GAAY,QAAA;EACtB,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAA;EACpD,mBAAA,CAAoB,OAAA,GAAU,qBAAA,GAAwB,OAAA,CAAQ,MAAA;EAC9D,gBAAA,CAAiB,IAAA,GAAO,IAAA,GAAO,OAAA;AAAA;AAAA,KAGrB,sBAAA,GAAyB,qBAAqB;AAAA,cAE7C,sBAAA;AAAA,iBAEG,YAAA,CAAa,OAAe;;;;;AAvFzB;AAEnB;;KCiBY,eAAA;AAAA,KAEA,cAAA;AAAA,KAEA,sBAAA;EACV,KAAA,EAAO,cAAA;EACP,GAAA;EACA,OAAA,GAAU,eAAe;EACzB,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,eAAA;EACV,IAAA;EACA,cAAA,CAAe,GAAA,EAAK,sBAAA,GAAyB,OAAA,CAAQ,QAAA;AAAA;AAAA,iBAGvC,qBAAA,CAAsB,MAAA,EAAQ,eAAA,GAAkB,eAAe"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"contract-B3JBTWO3.d.mts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":";KAAY,OAAA;AAAA,KAEA,UAAA;AAAA,KAEA,UAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,OAAA;EACA,WAAA,UAToB;EAWpB,gBAAA;EACA,WAAA,EAAa,IAAA;EACb,KAAA;AAAA;AAAA,KAGU,YAAA;EACV,IAAA,EAAM,OAAA;EACN,QAAA;EACA,KAAA;EACA,OAAA,EAAS,UAAA;EACT,OAAA;EACA,WAAA,GAAc,IAAA;EACd,OAAA;EACA,WAAA,WAlBS;EAoBT,SAAA;AAAA;AAAA,KAGU,UAAA,IAAc,GAAA,EAAK,UAAA,KAAe,OAAO;AAAA,KAEzC,MAAA,SAAe,OAAO;AAAA,KAEtB,aAAA;EACV,QAAA;EACA,cAAA;EACA,oBAAA;AAAA;AAAA,KAGU,QAAA;EACV,OAAA,CAAQ,KAAA,EAAO,YAAA,GAAe,OAAA;EAC9B,WAAA,CAAY,OAAA,EAAS,UAAA,EAAY,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA;AAAA,KAKzD,qBAAA;EACV,GAAA;EACA,OAAA;EACA,OAAA,GAAU,QAAA;EACV,MAAA,GAAS,eAAA;EACT,KAAA,GAAQ,cAAA;EACR,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,mBAAA;EACV,aAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,KAGU,mBAAA;EACV,SAAA,EAAW,mBAAA;EACX,iBAAA;IACE,MAAA;IACA,YAAA,GAAe,MAAM;EAAA;AAAA;AAAA,KAIb,qBAAA;EACV,cAAA;EACA,SAAS;AAAA;AAAA,KAGC,SAAA,GAAY,QAAA;EACtB,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAA;EACpD,mBAAA,CAAoB,OAAA,GAAU,qBAAA,GAAwB,OAAA,CAAQ,MAAA;EAC9D,gBAAA,CAAiB,IAAA,GAAO,IAAA,GAAO,OAAA;AAAA;AAAA,KAGrB,sBAAA,GAAyB,qBAAqB;AAAA,cAE7C,sBAAA;AAAA,iBAEG,YAAA,CAAa,OAAe;;;;;AAvFzB;AAEnB;;KCiBY,eAAA;AAAA,KAEA,cAAA;AAAA,KAEA,sBAAA;EACV,KAAA,EAAO,cAAA;EACP,GAAA;EACA,OAAA,GAAU,eAAe;EACzB,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,eAAA;EACV,IAAA;EACA,cAAA,CAAe,GAAA,EAAK,sBAAA,GAAyB,OAAA,CAAQ,QAAA;AAAA;AAAA,iBAGvC,qBAAA,CAAsB,MAAA,EAAQ,eAAA,GAAkB,eAAe"}
|