@keystrokehq/scheduler 0.1.59 → 0.1.61

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.
@@ -1 +1 @@
1
- {"version":3,"file":"contract-C5JvbyQ-.cjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind =\n | \"workflow\"\n | \"agent\"\n | \"trigger\"\n | \"trigger-overview\"\n | \"workflow-canvas-annotations\"\n | \"workflow-overview\"\n | \"runtime\"\n | \"maintenance\";\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 /** Project scope for org-wide worker queues. */\n projectId?: 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 /** Project scope for org-wide worker queues. */\n projectId?: 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, TriggerScheduleSpec } from \"./contract\";\n\nexport type { TriggerScheduleSpec };\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 ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\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 upsertTriggerSchedule?(\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n removeTriggerSchedule?(triggerSlug: string, projectId?: string): Promise<void>;\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, StopFn } 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\" | \"organization\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n triggerSlug: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n /** IANA timezone for schedule wall-clock fields. When omitted, UTC. */\n timezone?: string;\n /** Project scope for org-wide worker queues. */\n projectId?: string;\n};\n\nexport type TriggerScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type TriggerSchedulerStartOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\n};\n\nexport type TriggerScheduler = {\n sync(options: TriggerScheduleSyncOptions): Promise<void>;\n start(options?: TriggerSchedulerStartOptions): Promise<StopFn>;\n /** Register or refresh one trigger schedule in the firing backend. */\n upsert?(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n remove?(triggerSlug: string, projectId?: string): Promise<void>;\n fireDue?(asOf?: Date): Promise<number>;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n createTriggerScheduler?(ctx: SchedulerPluginContext, queue: JobQueue): Promise<TriggerScheduler>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";AAwGA,MAAa,yBAAyB;AAEtC,SAAgB,aAAa,SAAyB;CACpD,OAAO,yBAAyB,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC;AAC9D;;;ACjCA,SAAgB,sBAAsB,QAA0C;CAC9E,OAAO;AACT"}
1
+ {"version":3,"file":"contract-C5JvbyQ-.cjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind =\n | \"workflow\"\n | \"agent\"\n | \"trigger\"\n | \"trigger-overview\"\n | \"workflow-canvas-annotations\"\n | \"workflow-overview\"\n | \"runtime\"\n | \"maintenance\";\n\nexport type JobTrigger = \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n\n/**\n * Identifies a run that dispatched this job as a child. The parent metadata\n * survives the queue boundary so the child can resume its parent at a terminal\n * outcome without treating the child as a separately-billable dispatch.\n */\nexport type JobParent = {\n kind: \"workflow\" | \"agent\";\n runId: string;\n targetId: string;\n correlationId?: string;\n};\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 /** Project scope for org-wide worker queues. */\n projectId?: string;\n /** Parent run that dispatched this child job, when applicable. */\n parent?: JobParent;\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 /** Project scope for org-wide worker queues. */\n projectId?: string;\n /** Parent run that dispatched this child job, when applicable. */\n parent?: JobParent;\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 /**\n * Temporarily lends a worker slot to a child while an agent job waits on it.\n * Backends without a hard concurrency cap may omit it.\n */\n withLentSlot?<T>(fn: () => Promise<T>): Promise<T>;\n publishCancel(runId: string): Promise<void>;\n subscribeCancel(handler: CancelHandler): Promise<StopFn>;\n};\n\nimport type { SchedulerPlugin, SchedulerScope, TriggerScheduleSpec } from \"./contract\";\n\nexport type { TriggerScheduleSpec };\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 ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\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 upsertTriggerSchedule?(\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n removeTriggerSchedule?(triggerSlug: string, projectId?: string): Promise<void>;\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 JobParent,\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, StopFn } 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\" | \"organization\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n triggerSlug: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n /** IANA timezone for schedule wall-clock fields. When omitted, UTC. */\n timezone?: string;\n /** Project scope for org-wide worker queues. */\n projectId?: string;\n};\n\nexport type TriggerScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type TriggerSchedulerStartOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\n};\n\nexport type TriggerScheduler = {\n sync(options: TriggerScheduleSyncOptions): Promise<void>;\n start(options?: TriggerSchedulerStartOptions): Promise<StopFn>;\n /** Register or refresh one trigger schedule in the firing backend. */\n upsert?(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n remove?(triggerSlug: string, projectId?: string): Promise<void>;\n fireDue?(asOf?: Date): Promise<number>;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n createTriggerScheduler?(ctx: SchedulerPluginContext, queue: JobQueue): Promise<TriggerScheduler>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";AA6HA,MAAa,yBAAyB;AAEtC,SAAgB,aAAa,SAAyB;CACpD,OAAO,yBAAyB,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC;AAC9D;;;ACrDA,SAAgB,sBAAsB,QAA0C;CAC9E,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"contract-E1QJBH6_.mjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind =\n | \"workflow\"\n | \"agent\"\n | \"trigger\"\n | \"trigger-overview\"\n | \"workflow-canvas-annotations\"\n | \"workflow-overview\"\n | \"runtime\"\n | \"maintenance\";\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 /** Project scope for org-wide worker queues. */\n projectId?: 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 /** Project scope for org-wide worker queues. */\n projectId?: 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, TriggerScheduleSpec } from \"./contract\";\n\nexport type { TriggerScheduleSpec };\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 ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\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 upsertTriggerSchedule?(\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n removeTriggerSchedule?(triggerSlug: string, projectId?: string): Promise<void>;\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, StopFn } 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\" | \"organization\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n triggerSlug: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n /** IANA timezone for schedule wall-clock fields. When omitted, UTC. */\n timezone?: string;\n /** Project scope for org-wide worker queues. */\n projectId?: string;\n};\n\nexport type TriggerScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type TriggerSchedulerStartOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\n};\n\nexport type TriggerScheduler = {\n sync(options: TriggerScheduleSyncOptions): Promise<void>;\n start(options?: TriggerSchedulerStartOptions): Promise<StopFn>;\n /** Register or refresh one trigger schedule in the firing backend. */\n upsert?(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n remove?(triggerSlug: string, projectId?: string): Promise<void>;\n fireDue?(asOf?: Date): Promise<number>;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n createTriggerScheduler?(ctx: SchedulerPluginContext, queue: JobQueue): Promise<TriggerScheduler>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";AAwGA,MAAa,yBAAyB;AAEtC,SAAgB,aAAa,SAAyB;CACpD,OAAO,yBAAyB,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC;AAC9D;;;ACjCA,SAAgB,sBAAsB,QAA0C;CAC9E,OAAO;AACT"}
1
+ {"version":3,"file":"contract-E1QJBH6_.mjs","names":[],"sources":["../src/types.ts","../src/contract.ts"],"sourcesContent":["export type JobKind =\n | \"workflow\"\n | \"agent\"\n | \"trigger\"\n | \"trigger-overview\"\n | \"workflow-canvas-annotations\"\n | \"workflow-overview\"\n | \"runtime\"\n | \"maintenance\";\n\nexport type JobTrigger = \"api\" | \"cron\" | \"webhook\" | \"poll\" | \"retry\" | \"prompt\";\n\n/**\n * Identifies a run that dispatched this job as a child. The parent metadata\n * survives the queue boundary so the child can resume its parent at a terminal\n * outcome without treating the child as a separately-billable dispatch.\n */\nexport type JobParent = {\n kind: \"workflow\" | \"agent\";\n runId: string;\n targetId: string;\n correlationId?: string;\n};\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 /** Project scope for org-wide worker queues. */\n projectId?: string;\n /** Parent run that dispatched this child job, when applicable. */\n parent?: JobParent;\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 /** Project scope for org-wide worker queues. */\n projectId?: string;\n /** Parent run that dispatched this child job, when applicable. */\n parent?: JobParent;\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 /**\n * Temporarily lends a worker slot to a child while an agent job waits on it.\n * Backends without a hard concurrency cap may omit it.\n */\n withLentSlot?<T>(fn: () => Promise<T>): Promise<T>;\n publishCancel(runId: string): Promise<void>;\n subscribeCancel(handler: CancelHandler): Promise<StopFn>;\n};\n\nimport type { SchedulerPlugin, SchedulerScope, TriggerScheduleSpec } from \"./contract\";\n\nexport type { TriggerScheduleSpec };\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 ScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type ScheduleTickerOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\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 upsertTriggerSchedule?(\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n removeTriggerSchedule?(triggerSlug: string, projectId?: string): Promise<void>;\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 JobParent,\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, StopFn } 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\" | \"organization\";\n\nexport type SchedulerPluginContext = {\n scope: SchedulerScope;\n url?: string;\n dialect?: DatabaseDialect;\n projectId?: string;\n organizationId?: string;\n};\n\nexport type TriggerScheduleSpec = {\n triggerSlug: string;\n kind: \"cron\" | \"poll\";\n schedule: string;\n /** IANA timezone for schedule wall-clock fields. When omitted, UTC. */\n timezone?: string;\n /** Project scope for org-wide worker queues. */\n projectId?: string;\n};\n\nexport type TriggerScheduleSyncOptions = {\n schedules: TriggerScheduleSpec[];\n scheduleOverrides?: {\n global?: string;\n byTrigger?: Record<string, string>;\n };\n};\n\nexport type TriggerSchedulerStartOptions = {\n pollIntervalMs?: number;\n batchSize?: number;\n /** When `organization`, claims due schedules across all projects in the org schema. */\n scope?: \"project\" | \"organization\";\n};\n\nexport type TriggerScheduler = {\n sync(options: TriggerScheduleSyncOptions): Promise<void>;\n start(options?: TriggerSchedulerStartOptions): Promise<StopFn>;\n /** Register or refresh one trigger schedule in the firing backend. */\n upsert?(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void>;\n remove?(triggerSlug: string, projectId?: string): Promise<void>;\n fireDue?(asOf?: Date): Promise<number>;\n};\n\nexport type SchedulerPlugin = {\n name: string;\n createJobQueue(ctx: SchedulerPluginContext): Promise<JobQueue>;\n createTriggerScheduler?(ctx: SchedulerPluginContext, queue: JobQueue): Promise<TriggerScheduler>;\n};\n\nexport function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin {\n return plugin;\n}\n"],"mappings":";AA6HA,MAAa,yBAAyB;AAEtC,SAAgB,aAAa,SAAyB;CACpD,OAAO,yBAAyB,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC;AAC9D;;;ACrDA,SAAgB,sBAAsB,QAA0C;CAC9E,OAAO;AACT"}
@@ -1,6 +1,17 @@
1
1
  //#region src/types.d.ts
2
2
  type JobKind = "workflow" | "agent" | "trigger" | "trigger-overview" | "workflow-canvas-annotations" | "workflow-overview" | "runtime" | "maintenance";
3
3
  type JobTrigger = "api" | "cron" | "webhook" | "poll" | "retry" | "prompt";
4
+ /**
5
+ * Identifies a run that dispatched this job as a child. The parent metadata
6
+ * survives the queue boundary so the child can resume its parent at a terminal
7
+ * outcome without treating the child as a separately-billable dispatch.
8
+ */
9
+ type JobParent = {
10
+ kind: "workflow" | "agent";
11
+ runId: string;
12
+ targetId: string;
13
+ correlationId?: string;
14
+ };
4
15
  type JobPayload = {
5
16
  kind: JobKind;
6
17
  targetId: string;
@@ -12,7 +23,8 @@ type JobPayload = {
12
23
  exhaustedRetries?: boolean;
13
24
  scheduledAt: Date;
14
25
  jobId: string; /** Project scope for org-wide worker queues. */
15
- projectId?: string;
26
+ projectId?: string; /** Parent run that dispatched this child job, when applicable. */
27
+ parent?: JobParent;
16
28
  };
17
29
  type EnqueueInput = {
18
30
  kind: JobKind;
@@ -24,7 +36,8 @@ type EnqueueInput = {
24
36
  attempt?: number;
25
37
  maxAttempts?: number; /** Dedupe key — pg-boss singletonKey / BullMQ jobId. */
26
38
  dedupeKey?: string; /** Project scope for org-wide worker queues. */
27
- projectId?: string;
39
+ projectId?: string; /** Parent run that dispatched this child job, when applicable. */
40
+ parent?: JobParent;
28
41
  };
29
42
  type JobHandler = (job: JobPayload) => Promise<void>;
30
43
  type CancelHandler = (runId: string) => void | Promise<void>;
@@ -37,6 +50,11 @@ type WorkerOptions = {
37
50
  type JobQueue = {
38
51
  enqueue(input: EnqueueInput): Promise<string>;
39
52
  startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;
53
+ /**
54
+ * Temporarily lends a worker slot to a child while an agent job waits on it.
55
+ * Backends without a hard concurrency cap may omit it.
56
+ */
57
+ withLentSlot?<T>(fn: () => Promise<T>): Promise<T>;
40
58
  publishCancel(runId: string): Promise<void>;
41
59
  subscribeCancel(handler: CancelHandler): Promise<StopFn>;
42
60
  };
@@ -120,5 +138,5 @@ type SchedulerPlugin = {
120
138
  };
121
139
  declare function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin;
122
140
  //#endregion
123
- export { StopFn as C, Scheduler as S, retryDelayMs as T, JobPayload as _, TriggerScheduleSpec as a, ScheduleSyncOptions as b, TriggerSchedulerStartOptions as c, CreateJobQueueOptions as d, CreateSchedulerOptions as f, JobKind as g, JobHandler as h, SchedulerScope as i, defineSchedulerPlugin as l, EnqueueInput as m, SchedulerPlugin as n, TriggerScheduleSyncOptions as o, DEFAULT_RETRY_DELAY_MS as p, SchedulerPluginContext as r, TriggerScheduler as s, DatabaseDialect as t, CancelHandler as u, JobQueue as v, WorkerOptions as w, ScheduleTickerOptions as x, JobTrigger as y };
124
- //# sourceMappingURL=contract-GEHKV_QB.d.cts.map
141
+ export { Scheduler as C, retryDelayMs as E, ScheduleTickerOptions as S, WorkerOptions as T, JobParent as _, TriggerScheduleSpec as a, JobTrigger as b, TriggerSchedulerStartOptions as c, CreateJobQueueOptions as d, CreateSchedulerOptions as f, JobKind as g, JobHandler as h, SchedulerScope as i, defineSchedulerPlugin as l, EnqueueInput as m, SchedulerPlugin as n, TriggerScheduleSyncOptions as o, DEFAULT_RETRY_DELAY_MS as p, SchedulerPluginContext as r, TriggerScheduler as s, DatabaseDialect as t, CancelHandler as u, JobPayload as v, StopFn as w, ScheduleSyncOptions as x, JobQueue as y };
142
+ //# sourceMappingURL=contract-tgjsITaG.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-tgjsITaG.d.cts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":"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"}
@@ -1,6 +1,17 @@
1
1
  //#region src/types.d.ts
2
2
  type JobKind = "workflow" | "agent" | "trigger" | "trigger-overview" | "workflow-canvas-annotations" | "workflow-overview" | "runtime" | "maintenance";
3
3
  type JobTrigger = "api" | "cron" | "webhook" | "poll" | "retry" | "prompt";
4
+ /**
5
+ * Identifies a run that dispatched this job as a child. The parent metadata
6
+ * survives the queue boundary so the child can resume its parent at a terminal
7
+ * outcome without treating the child as a separately-billable dispatch.
8
+ */
9
+ type JobParent = {
10
+ kind: "workflow" | "agent";
11
+ runId: string;
12
+ targetId: string;
13
+ correlationId?: string;
14
+ };
4
15
  type JobPayload = {
5
16
  kind: JobKind;
6
17
  targetId: string;
@@ -12,7 +23,8 @@ type JobPayload = {
12
23
  exhaustedRetries?: boolean;
13
24
  scheduledAt: Date;
14
25
  jobId: string; /** Project scope for org-wide worker queues. */
15
- projectId?: string;
26
+ projectId?: string; /** Parent run that dispatched this child job, when applicable. */
27
+ parent?: JobParent;
16
28
  };
17
29
  type EnqueueInput = {
18
30
  kind: JobKind;
@@ -24,7 +36,8 @@ type EnqueueInput = {
24
36
  attempt?: number;
25
37
  maxAttempts?: number; /** Dedupe key — pg-boss singletonKey / BullMQ jobId. */
26
38
  dedupeKey?: string; /** Project scope for org-wide worker queues. */
27
- projectId?: string;
39
+ projectId?: string; /** Parent run that dispatched this child job, when applicable. */
40
+ parent?: JobParent;
28
41
  };
29
42
  type JobHandler = (job: JobPayload) => Promise<void>;
30
43
  type CancelHandler = (runId: string) => void | Promise<void>;
@@ -37,6 +50,11 @@ type WorkerOptions = {
37
50
  type JobQueue = {
38
51
  enqueue(input: EnqueueInput): Promise<string>;
39
52
  startWorker(handler: JobHandler, options?: WorkerOptions): Promise<StopFn>;
53
+ /**
54
+ * Temporarily lends a worker slot to a child while an agent job waits on it.
55
+ * Backends without a hard concurrency cap may omit it.
56
+ */
57
+ withLentSlot?<T>(fn: () => Promise<T>): Promise<T>;
40
58
  publishCancel(runId: string): Promise<void>;
41
59
  subscribeCancel(handler: CancelHandler): Promise<StopFn>;
42
60
  };
@@ -120,5 +138,5 @@ type SchedulerPlugin = {
120
138
  };
121
139
  declare function defineSchedulerPlugin(plugin: SchedulerPlugin): SchedulerPlugin;
122
140
  //#endregion
123
- export { StopFn as C, Scheduler as S, retryDelayMs as T, JobPayload as _, TriggerScheduleSpec as a, ScheduleSyncOptions as b, TriggerSchedulerStartOptions as c, CreateJobQueueOptions as d, CreateSchedulerOptions as f, JobKind as g, JobHandler as h, SchedulerScope as i, defineSchedulerPlugin as l, EnqueueInput as m, SchedulerPlugin as n, TriggerScheduleSyncOptions as o, DEFAULT_RETRY_DELAY_MS as p, SchedulerPluginContext as r, TriggerScheduler as s, DatabaseDialect as t, CancelHandler as u, JobQueue as v, WorkerOptions as w, ScheduleTickerOptions as x, JobTrigger as y };
124
- //# sourceMappingURL=contract-GEHKV_QB.d.mts.map
141
+ export { Scheduler as C, retryDelayMs as E, ScheduleTickerOptions as S, WorkerOptions as T, JobParent as _, TriggerScheduleSpec as a, JobTrigger as b, TriggerSchedulerStartOptions as c, CreateJobQueueOptions as d, CreateSchedulerOptions as f, JobKind as g, JobHandler as h, SchedulerScope as i, defineSchedulerPlugin as l, EnqueueInput as m, SchedulerPlugin as n, TriggerScheduleSyncOptions as o, DEFAULT_RETRY_DELAY_MS as p, SchedulerPluginContext as r, TriggerScheduler as s, DatabaseDialect as t, CancelHandler as u, JobPayload as v, StopFn as w, ScheduleSyncOptions as x, JobQueue as y };
142
+ //# sourceMappingURL=contract-tgjsITaG.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-tgjsITaG.d.mts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { C as StopFn, T as retryDelayMs, _ as JobPayload, a as TriggerScheduleSpec, c as TriggerSchedulerStartOptions, g as JobKind, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, t as DatabaseDialect, u as CancelHandler, v as JobQueue, w as WorkerOptions, y as JobTrigger } from "./contract-GEHKV_QB.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, TriggerScheduleSpec, TriggerScheduleSyncOptions, TriggerScheduler, TriggerSchedulerStartOptions, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
1
+ import { E as retryDelayMs, T as WorkerOptions, _ as JobParent, a as TriggerScheduleSpec, b as JobTrigger, c as TriggerSchedulerStartOptions, g as JobKind, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, t as DatabaseDialect, u as CancelHandler, v as JobPayload, w as StopFn, y as JobQueue } from "./contract-tgjsITaG.cjs";
2
+ export { type CancelHandler, DEFAULT_RETRY_DELAY_MS, DatabaseDialect, type EnqueueInput, type JobHandler, type JobKind, type JobParent, type JobPayload, type JobQueue, type JobTrigger, SchedulerPlugin, SchedulerPluginContext, SchedulerScope, type StopFn, TriggerScheduleSpec, TriggerScheduleSyncOptions, TriggerScheduler, TriggerSchedulerStartOptions, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
@@ -1,2 +1,2 @@
1
- import { C as StopFn, T as retryDelayMs, _ as JobPayload, a as TriggerScheduleSpec, c as TriggerSchedulerStartOptions, g as JobKind, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, t as DatabaseDialect, u as CancelHandler, v as JobQueue, w as WorkerOptions, y as JobTrigger } from "./contract-GEHKV_QB.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, TriggerScheduleSpec, TriggerScheduleSyncOptions, TriggerScheduler, TriggerSchedulerStartOptions, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
1
+ import { E as retryDelayMs, T as WorkerOptions, _ as JobParent, a as TriggerScheduleSpec, b as JobTrigger, c as TriggerSchedulerStartOptions, g as JobKind, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, t as DatabaseDialect, u as CancelHandler, v as JobPayload, w as StopFn, y as JobQueue } from "./contract-tgjsITaG.mjs";
2
+ export { type CancelHandler, DEFAULT_RETRY_DELAY_MS, DatabaseDialect, type EnqueueInput, type JobHandler, type JobKind, type JobParent, type JobPayload, type JobQueue, type JobTrigger, SchedulerPlugin, SchedulerPluginContext, SchedulerScope, type StopFn, TriggerScheduleSpec, TriggerScheduleSyncOptions, TriggerScheduler, TriggerSchedulerStartOptions, type WorkerOptions, defineSchedulerPlugin, retryDelayMs };
package/dist/index.cjs CHANGED
@@ -36,25 +36,26 @@ function toJobPayload(job) {
36
36
  payload: job.payload,
37
37
  attempt: job.attempt,
38
38
  maxAttempts: job.maxAttempts,
39
- scheduledAt: job.scheduledAt
39
+ scheduledAt: job.scheduledAt,
40
+ ...job.parent ? { parent: job.parent } : {}
40
41
  };
41
42
  }
42
43
  function createDatabaseJobQueue() {
43
44
  const cancelChannel = createInProcessCancelChannel();
44
- return {
45
- async enqueue(input) {
46
- return (0, _keystrokehq_database.enqueueJob)(input);
47
- },
48
- async startWorker(handler, options = {}) {
49
- const workerId = options.workerId ?? crypto.randomUUID();
50
- const pollIntervalMs = options.pollIntervalMs ?? 250;
51
- const leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 3e4;
52
- let running = true;
53
- const leaseTimer = setInterval(() => {
54
- (0, _keystrokehq_database.requeueExpiredLeases)();
55
- }, leaseSweepIntervalMs);
56
- const loop = async () => {
57
- while (running) try {
45
+ let handler;
46
+ let running = false;
47
+ let lentSlots = 0;
48
+ let activeLoops = 0;
49
+ let workerIdBase = "";
50
+ let pollIntervalMs = 250;
51
+ let leaseSweepIntervalMs = 3e4;
52
+ let leaseTimer;
53
+ const desiredConcurrency = () => 1 + lentSlots;
54
+ const loop = async (workerId) => {
55
+ try {
56
+ while (running && handler) {
57
+ if (activeLoops > desiredConcurrency()) return;
58
+ try {
58
59
  const job = await (0, _keystrokehq_database.claimNextJob)(workerId);
59
60
  if (!job) {
60
61
  await sleep$1(pollIntervalMs);
@@ -77,13 +78,47 @@ function createDatabaseJobQueue() {
77
78
  } catch {
78
79
  await sleep$1(pollIntervalMs);
79
80
  }
80
- };
81
- loop();
81
+ }
82
+ } finally {
83
+ activeLoops -= 1;
84
+ ensureWorkers();
85
+ }
86
+ };
87
+ const ensureWorkers = () => {
88
+ while (running && handler && activeLoops < desiredConcurrency()) {
89
+ activeLoops += 1;
90
+ loop(`${workerIdBase}:${activeLoops}`);
91
+ }
92
+ };
93
+ return {
94
+ async enqueue(input) {
95
+ return (0, _keystrokehq_database.enqueueJob)(input);
96
+ },
97
+ async startWorker(nextHandler, options = {}) {
98
+ handler = nextHandler;
99
+ running = true;
100
+ workerIdBase = options.workerId ?? crypto.randomUUID();
101
+ pollIntervalMs = options.pollIntervalMs ?? 250;
102
+ leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 3e4;
103
+ leaseTimer = setInterval(() => {
104
+ (0, _keystrokehq_database.requeueExpiredLeases)();
105
+ }, leaseSweepIntervalMs);
106
+ ensureWorkers();
82
107
  return async () => {
83
108
  running = false;
84
109
  clearInterval(leaseTimer);
110
+ leaseTimer = void 0;
85
111
  };
86
112
  },
113
+ async withLentSlot(fn) {
114
+ lentSlots += 1;
115
+ ensureWorkers();
116
+ try {
117
+ return await fn();
118
+ } finally {
119
+ lentSlots -= 1;
120
+ }
121
+ },
87
122
  publishCancel: (runId) => cancelChannel.publishCancel(runId),
88
123
  subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
89
124
  };
@@ -321,7 +356,8 @@ function buildPgBossJobQueue(boss, options) {
321
356
  maxAttempts,
322
357
  exhaustedRetries: attempt >= maxAttempts,
323
358
  scheduledAt: data.scheduledAt ? new Date(data.scheduledAt) : /* @__PURE__ */ new Date(),
324
- projectId: data.projectId
359
+ projectId: data.projectId,
360
+ parent: data.parent
325
361
  });
326
362
  });
327
363
  return async () => {
@@ -584,6 +620,7 @@ function wrapScheduler(jobQueue, triggerScheduler) {
584
620
  return {
585
621
  enqueue: (input) => jobQueue.enqueue(input),
586
622
  startWorker: (handler, workerOptions) => jobQueue.startWorker(handler, workerOptions),
623
+ ...jobQueue.withLentSlot ? { withLentSlot: (fn) => jobQueue.withLentSlot(fn) } : {},
587
624
  publishCancel: (runId) => jobQueue.publishCancel(runId),
588
625
  subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),
589
626
  syncTriggerSchedules: (options) => triggerScheduler.sync(options),
@@ -630,7 +667,8 @@ function createMemoryJobQueue(options = {}) {
630
667
  payload: input.payload ?? {},
631
668
  attempt: input.attempt ?? 1,
632
669
  maxAttempts: input.maxAttempts ?? 3,
633
- scheduledAt: input.scheduledAt ?? next.enqueuedAt
670
+ scheduledAt: input.scheduledAt ?? next.enqueuedAt,
671
+ ...input.parent ? { parent: input.parent } : {}
634
672
  });
635
673
  }
636
674
  } finally {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["EventEmitter","sleep","retryDelayMs","PgBoss","retryDelayMs","PgBoss","DEFAULT_DATABASE_URL","defineSchedulerPlugin","sharedJobQueue"],"sources":["../src/cancel-channel.ts","../src/database-queue.ts","../src/schedule-ticker.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/pg-cancel-channel.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts","../src/plugin.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts"],"sourcesContent":["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 failAgentSession,\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 // Last-resort run finalization: the dispatcher's own\n // finalization covers everything inside dispatch, but errors\n // before it (project resolution, scope setup) would otherwise\n // strand the run in a non-terminal status forever.\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n if (job.kind === \"agent\") {\n const sessionId = (job.payload as { sessionId?: string } | null)?.sessionId;\n if (sessionId) {\n await failAgentSession(sessionId, error);\n }\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 { claimDueTriggers, claimDueTriggersForOrg } from \"@keystrokehq/database\";\nimport { getProjectScopeId } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { EnqueueInput, JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n scope?: \"project\" | \"organization\";\n};\n\nfunction enqueueTriggerJob(\n jobQueue: JobQueue,\n row: { slug: string; kind: \"cron\" | \"poll\"; projectId: string },\n scheduledAt?: Date,\n): Promise<string> {\n const input: EnqueueInput = {\n kind: \"trigger\",\n targetId: row.slug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n projectId: row.projectId,\n };\n\n if (scheduledAt) {\n input.scheduledAt = scheduledAt;\n }\n\n return jobQueue.enqueue(input);\n}\n\nasync function claimDue(\n scope: \"project\" | \"organization\",\n asOf: Date,\n resolveNextRunAt: (schedule: string, timezone?: string) => Date,\n limit: number,\n) {\n if (scope === \"organization\") {\n return claimDueTriggersForOrg(asOf, resolveNextRunAt, limit);\n }\n\n return claimDueTriggers(asOf, resolveNextRunAt, limit).then((rows) =>\n rows.map((row) => ({ ...row, projectId: getProjectScopeId() })),\n );\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 scope = ctx.scope ?? \"project\";\n const claimed = await claimDue(\n scope,\n asOf,\n (schedule, timezone) => nextTriggerRunAt(schedule, asOf, timezone),\n batchSize,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n // Claim uses `asOf` to decide which schedules are due, but the job must be\n // processable immediately — stamping `scheduledAt: asOf` (e.g. a far-future\n // test clock) would leave the job pending forever under `scheduledAt <= now`.\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 const scope = options.scope ?? ctx.scope ?? \"project\";\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n const claimed = await claimDue(\n scope,\n new Date(),\n (schedule, timezone) => nextTriggerRunAt(schedule, new Date(), timezone),\n limit,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byTrigger?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n triggerSlug: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(triggerSlug, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byTrigger,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n listExecutableAttachmentsByTriggerIds,\n selectActiveEphemeralScheduledTriggerSlugs,\n selectTriggerBySlug,\n triggerTimezoneFromConfig,\n upsertTriggerScheduleFields,\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.triggerSlug);\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 selectActiveEphemeralScheduledTriggerSlugs();\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.triggerSlug,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerBySlug(spec.triggerSlug);\n if (!existing) {\n continue;\n }\n\n const existingTimezone = triggerTimezoneFromConfig(existing.config);\n const scheduleChanged = existing.schedule !== schedule || existingTimezone !== spec.timezone;\n const nextRunAt =\n !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now, spec.timezone);\n\n const executable = await listExecutableAttachmentsByTriggerIds([existing.id]);\n const enabled = executable.length > 0;\n\n await upsertTriggerScheduleFields({\n triggerSlug: spec.triggerSlug,\n schedule,\n nextRunAt: nextRunAt ?? now,\n enabled,\n updatedAt: now,\n });\n }\n}\n","import { setTriggerScheduleEnabled } from \"@keystrokehq/database\";\n\nimport { createScheduleTicker } from \"./schedule-ticker\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\nimport type {\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { JobQueue } from \"./types\";\n\nexport type DatabaseTriggerSchedulerOptions = {\n jobQueue: JobQueue;\n scope?: \"project\" | \"organization\";\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\n/** Postgres poll loop — default trigger scheduler for pg-boss, polling, and memory paths. */\nexport function createDatabaseTriggerScheduler(\n options: DatabaseTriggerSchedulerOptions,\n): TriggerScheduler {\n const ticker = createScheduleTicker({\n jobQueue: options.jobQueue,\n scope: options.scope,\n pollIntervalMs: options.pollIntervalMs,\n batchSize: options.batchSize,\n });\n\n return {\n sync: (syncOptions: TriggerScheduleSyncOptions) => syncTriggerSchedules(syncOptions),\n start: (startOptions?: TriggerSchedulerStartOptions) =>\n ticker.startScheduleTicker(startOptions),\n fireDue: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n async remove(triggerSlug: string) {\n await setTriggerScheduleEnabled(triggerSlug, false, new Date());\n },\n async upsert(_spec: TriggerScheduleSpec) {\n // DB rows are the firing source of truth; callers update triggers before upsert.\n },\n };\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(\n scope: \"platform\" | \"project\" | \"organization\",\n id?: string,\n): string {\n if (scope === \"platform\") {\n return \"keystroke_cancel_platform\";\n }\n\n if (scope === \"organization\") {\n const sanitized = (id ?? \"default\").replace(/[^a-z0-9_]/gi, \"_\").toLowerCase();\n return `keystroke_cancel_org_${sanitized}`;\n }\n\n const sanitized = (id ?? \"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 function pgBossOrgQueueName(organizationId: string): string {\n const sanitized = organizationId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_org_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_org_${createHash(\"sha1\").update(organizationId).digest(\"hex\").slice(0, 36)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n projectId?: string;\n organizationId?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\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 projectId?: string;\n};\n\ntype BuildPgBossJobQueueOptions = {\n connectionString: string;\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\n projectId?: string;\n organizationId?: string;\n};\n\n/**\n * A {@link JobQueue} that also exposes its underlying pg-boss instance and queue\n * name so the trigger scheduler can register native cron schedules\n * (`boss.schedule`) against the same queue the worker drains.\n */\nexport type PgBossJobQueue = JobQueue & {\n boss: PgBoss;\n queueName: string;\n};\n\nexport function buildPgBossJobQueue(\n boss: PgBoss,\n options: BuildPgBossJobQueueOptions,\n): PgBossJobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const cancelChannel = createPgCancelChannel(\n options.connectionString,\n pgCancelChannelName(\n options.cancelScope ?? \"platform\",\n options.cancelScope === \"organization\" ? options.organizationId : options.projectId,\n ),\n );\n\n return {\n boss,\n queueName,\n async enqueue(input) {\n const jobId = await boss.send(\n queueName,\n {\n ...input,\n payload: input.payload,\n },\n {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n },\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 projectId: data.projectId,\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<PgBossJobQueue> {\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: options.cancelScope ?? \"project\",\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n options: { connectionString: string; queueName?: string },\n): Promise<PgBossJobQueue> {\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 getProjectScopeId,\n listEnabledScheduledTriggers,\n triggerTimezoneFromConfig,\n type StoredTriggerConfig,\n} from \"@keystrokehq/database\";\n\nimport type {\n SchedulerPluginContext,\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { PgBossJobQueue } from \"./pg-boss-queue\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\n\n/**\n * pg-boss schedule key for one trigger. Mirrors {@link bullmqTriggerSchedulerId}\n * so the firing backend is interchangeable: `trigger-<projectId>-<slug>`. pg-boss\n * keys allow hyphens, so colons (legal in ids) are swapped for `-`.\n */\nexport function pgBossTriggerScheduleKey(\n spec: Pick<TriggerScheduleSpec, \"triggerSlug\" | \"projectId\">,\n): string {\n const projectId = (spec.projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n const slug = spec.triggerSlug.replaceAll(\":\", \"-\");\n return `trigger-${projectId}-${slug}`;\n}\n\n/** Key prefix scoping reconcile cleanup to the active project (org queues are shared). */\nexport function pgBossTriggerScheduleKeyPrefix(projectId?: string): string {\n const scopedProjectId = (projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n return `trigger-${scopedProjectId}-`;\n}\n\nfunction triggerJobData(spec: TriggerScheduleSpec): {\n kind: \"trigger\";\n targetId: string;\n runId: string;\n trigger: \"cron\" | \"poll\";\n payload: Record<string, never>;\n projectId: string;\n} {\n return {\n kind: \"trigger\",\n targetId: spec.triggerSlug,\n // Empty: the worker fans out to attachment runs, each with its own run id.\n runId: \"\",\n trigger: spec.kind,\n payload: {},\n projectId: spec.projectId ?? getProjectScopeId(),\n };\n}\n\nfunction rowToSpec(row: {\n slug: string;\n kind: string;\n schedule: string | null;\n projectId: string;\n config?: StoredTriggerConfig | null;\n}): TriggerScheduleSpec | undefined {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n return undefined;\n }\n\n if (!row.schedule) {\n return undefined;\n }\n\n const timezone = triggerTimezoneFromConfig(row.config ?? null);\n\n return {\n triggerSlug: row.slug,\n kind: row.kind,\n schedule: row.schedule,\n ...(timezone ? { timezone } : {}),\n projectId: row.projectId,\n };\n}\n\n/**\n * Trigger scheduler backed by pg-boss native cron scheduling. Mirrors\n * {@link createBullmqTriggerScheduler}: `boss.schedule` registers a recurring job\n * onto the project/org queue (no per-second DB poll), and reconcile keeps the\n * `pgboss.schedule` table in sync with the `triggers` table (source of truth).\n *\n * Granularity: pg-boss cron is minute-level (it checks schedules every ~30s and\n * dedupes per minute), so sub-minute schedules effectively fire at most once per\n * minute — unlike the DB ticker's 1s loop.\n */\nexport function createPgBossTriggerScheduler(\n _ctx: SchedulerPluginContext,\n queue: PgBossJobQueue,\n): TriggerScheduler {\n async function upsertScheduler(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const schedule = resolveTriggerSchedule(spec.triggerSlug, spec.schedule, overrides);\n const key = pgBossTriggerScheduleKey(spec);\n await queue.boss.schedule(queue.queueName, schedule, triggerJobData({ ...spec, schedule }), {\n key,\n ...(spec.timezone ? { tz: spec.timezone } : {}),\n });\n }\n\n async function reconcileFromDatabase(\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const projectPrefix = pgBossTriggerScheduleKeyPrefix();\n const rows = await listEnabledScheduledTriggers();\n const expectedKeys = new Set<string>();\n const failures: Array<{ triggerSlug: string; error: unknown }> = [];\n\n for (const row of rows) {\n const spec = rowToSpec(row);\n if (!spec) {\n continue;\n }\n\n expectedKeys.add(pgBossTriggerScheduleKey(spec));\n try {\n await upsertScheduler(spec, overrides);\n } catch (error) {\n failures.push({ triggerSlug: spec.triggerSlug, error });\n console.error(`[pg-boss-trigger-scheduler] failed to schedule ${spec.triggerSlug}`, error);\n }\n }\n\n const schedules = await queue.boss.getSchedules(queue.queueName);\n for (const schedule of schedules) {\n const key = schedule.key;\n if (!key || !key.startsWith(projectPrefix)) {\n continue;\n }\n\n if (!expectedKeys.has(key)) {\n try {\n await queue.boss.unschedule(queue.queueName, key);\n } catch (error) {\n failures.push({ triggerSlug: key, error });\n console.error(`[pg-boss-trigger-scheduler] failed to unschedule stale ${key}`, error);\n }\n }\n }\n\n if (failures.length > 0) {\n throw new Error(\n `pg-boss trigger scheduler reconcile failed for ${failures.length} trigger(s)`,\n );\n }\n }\n\n return {\n async sync(syncOptions: TriggerScheduleSyncOptions) {\n await syncTriggerSchedules(syncOptions);\n await reconcileFromDatabase(syncOptions.scheduleOverrides);\n },\n\n async start(_options?: TriggerSchedulerStartOptions) {\n // pg-boss fires schedules from its own timekeeper; no DB poll loop to run.\n return async () => undefined;\n },\n\n fireDue: async () => 0,\n\n upsert: upsertScheduler,\n\n async remove(triggerSlug: string, projectId?: string) {\n await queue.boss.unschedule(\n queue.queueName,\n pgBossTriggerScheduleKey({ triggerSlug, projectId }),\n );\n },\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 type TriggerScheduler,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n type PgBossJobQueue,\n pgBossOrgQueueName,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\nimport { createPgBossTriggerScheduler } from \"./pg-boss-trigger-scheduler\";\nimport type { JobQueue } from \"./types\";\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/** DB poll-loop trigger scheduler — used by the SQLite/self-host polling backend. */\nfunction createPollingTriggerScheduler(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = ctx.scope === \"organization\" ? \"organization\" : \"project\";\n return Promise.resolve(createDatabaseTriggerScheduler({ jobQueue: queue, scope }));\n}\n\nfunction isPgBossJobQueue(queue: JobQueue): queue is PgBossJobQueue {\n return \"boss\" in queue && \"queueName\" in queue;\n}\n\n/** Native pg-boss cron scheduler; falls back to the DB ticker if the queue isn't pg-boss. */\nfunction createPgBossTriggerSchedulerForPlugin(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n if (!isPgBossJobQueue(queue)) {\n return createPollingTriggerScheduler(ctx, queue);\n }\n return Promise.resolve(createPgBossTriggerScheduler(ctx, queue));\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; org scope gets one queue per org. */\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 if (ctx.scope === \"organization\") {\n const organizationId = ctx.organizationId;\n if (!organizationId) {\n throw new Error(\"organizationId is required for organization-scoped pg-boss queue\");\n }\n\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossOrgQueueName(organizationId),\n cancelScope: \"organization\",\n organizationId,\n });\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n projectId,\n });\n },\n createTriggerScheduler: createPgBossTriggerSchedulerForPlugin,\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 createTriggerScheduler: createPollingTriggerScheduler,\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 async createTriggerScheduler(ctx, queue) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n }\n\n return pollingSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n },\n });\n}\n","import { defaultSchedulerPlugin } from \"./plugin\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport type { SchedulerPluginContext, TriggerScheduler } from \"./contract\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n TriggerScheduleSpec,\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 pluginContext(options: CreateSchedulerOptions = {}): SchedulerPluginContext {\n return {\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n };\n}\n\nasync function createTriggerScheduler(\n options: CreateSchedulerOptions,\n jobQueue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = options.scope === \"organization\" ? \"organization\" : \"project\";\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n const ctx = pluginContext(options);\n\n if (plugin.createTriggerScheduler) {\n return plugin.createTriggerScheduler(ctx, jobQueue);\n }\n\n return createDatabaseTriggerScheduler({ jobQueue, scope });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue, triggerScheduler: TriggerScheduler): Scheduler {\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, workerOptions) => jobQueue.startWorker(handler, workerOptions),\n publishCancel: (runId) => jobQueue.publishCancel(runId),\n subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => triggerScheduler.sync(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => triggerScheduler.start(options),\n fireDueSchedules: (asOf?: Date) => triggerScheduler.fireDue?.(asOf) ?? Promise.resolve(0),\n ...(triggerScheduler.upsert\n ? {\n upsertTriggerSchedule: (\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ) => triggerScheduler.upsert!(spec, overrides),\n }\n : {}),\n ...(triggerScheduler.remove\n ? {\n removeTriggerSchedule: (triggerSlug: string, projectId?: string) =>\n triggerScheduler.remove!(triggerSlug, projectId),\n }\n : {}),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n const triggerScheduler = await createTriggerScheduler(options, jobQueue);\n return wrapScheduler(jobQueue, triggerScheduler);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport async function wrapJobQueueAsScheduler(\n jobQueue: JobQueue,\n scope?: \"project\" | \"organization\",\n): Promise<Scheduler> {\n const triggerScheduler = createDatabaseTriggerScheduler({\n jobQueue,\n scope,\n });\n return wrapScheduler(jobQueue, triggerScheduler);\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 await 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 await wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["EventEmitter","sleep","retryDelayMs","PgBoss","retryDelayMs","PgBoss","DEFAULT_DATABASE_URL","defineSchedulerPlugin","sharedJobQueue"],"sources":["../src/cancel-channel.ts","../src/database-queue.ts","../src/schedule-ticker.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/pg-cancel-channel.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts","../src/plugin.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts"],"sourcesContent":["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 failAgentSession,\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 ...(job.parent ? { parent: job.parent } : {}),\n };\n}\n\nexport function createDatabaseJobQueue(): JobQueue {\n const cancelChannel = createInProcessCancelChannel();\n let handler: JobHandler | undefined;\n let running = false;\n let lentSlots = 0;\n let activeLoops = 0;\n let workerIdBase = \"\";\n let pollIntervalMs = 250;\n let leaseSweepIntervalMs = 30_000;\n let leaseTimer: ReturnType<typeof setInterval> | undefined;\n\n const desiredConcurrency = () => 1 + lentSlots;\n\n const loop = async (workerId: string): Promise<void> => {\n try {\n while (running && handler) {\n // A loan may have been released while this loop was idle. Let excess\n // workers leave after their current job so the database queue returns\n // to its normal single-worker behavior.\n if (activeLoops > desiredConcurrency()) {\n return;\n }\n\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 // Last-resort run finalization: the dispatcher's own\n // finalization covers everything inside dispatch, but errors\n // before it (project resolution, scope setup) would otherwise\n // strand the run in a non-terminal status forever.\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n if (job.kind === \"agent\") {\n const sessionId = (job.payload as { sessionId?: string } | null)?.sessionId;\n if (sessionId) {\n await failAgentSession(sessionId, error);\n }\n }\n }\n }\n } catch {\n await sleep(pollIntervalMs);\n }\n }\n } finally {\n activeLoops -= 1;\n ensureWorkers();\n }\n };\n\n const ensureWorkers = (): void => {\n while (running && handler && activeLoops < desiredConcurrency()) {\n activeLoops += 1;\n const workerId = `${workerIdBase}:${activeLoops}`;\n void loop(workerId);\n }\n };\n\n return {\n async enqueue(input) {\n return enqueueJob(input);\n },\n\n async startWorker(nextHandler: JobHandler, options: WorkerOptions = {}): Promise<StopFn> {\n handler = nextHandler;\n running = true;\n workerIdBase = options.workerId ?? crypto.randomUUID();\n pollIntervalMs = options.pollIntervalMs ?? 250;\n leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 30_000;\n leaseTimer = setInterval(() => {\n void requeueExpiredLeases();\n }, leaseSweepIntervalMs);\n ensureWorkers();\n\n return async () => {\n running = false;\n clearInterval(leaseTimer);\n leaseTimer = undefined;\n };\n },\n\n async withLentSlot(fn) {\n lentSlots += 1;\n ensureWorkers();\n try {\n return await fn();\n } finally {\n lentSlots -= 1;\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 { claimDueTriggers, claimDueTriggersForOrg } from \"@keystrokehq/database\";\nimport { getProjectScopeId } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { EnqueueInput, JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n scope?: \"project\" | \"organization\";\n};\n\nfunction enqueueTriggerJob(\n jobQueue: JobQueue,\n row: { slug: string; kind: \"cron\" | \"poll\"; projectId: string },\n scheduledAt?: Date,\n): Promise<string> {\n const input: EnqueueInput = {\n kind: \"trigger\",\n targetId: row.slug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n projectId: row.projectId,\n };\n\n if (scheduledAt) {\n input.scheduledAt = scheduledAt;\n }\n\n return jobQueue.enqueue(input);\n}\n\nasync function claimDue(\n scope: \"project\" | \"organization\",\n asOf: Date,\n resolveNextRunAt: (schedule: string, timezone?: string) => Date,\n limit: number,\n) {\n if (scope === \"organization\") {\n return claimDueTriggersForOrg(asOf, resolveNextRunAt, limit);\n }\n\n return claimDueTriggers(asOf, resolveNextRunAt, limit).then((rows) =>\n rows.map((row) => ({ ...row, projectId: getProjectScopeId() })),\n );\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 scope = ctx.scope ?? \"project\";\n const claimed = await claimDue(\n scope,\n asOf,\n (schedule, timezone) => nextTriggerRunAt(schedule, asOf, timezone),\n batchSize,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n // Claim uses `asOf` to decide which schedules are due, but the job must be\n // processable immediately — stamping `scheduledAt: asOf` (e.g. a far-future\n // test clock) would leave the job pending forever under `scheduledAt <= now`.\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 const scope = options.scope ?? ctx.scope ?? \"project\";\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n const claimed = await claimDue(\n scope,\n new Date(),\n (schedule, timezone) => nextTriggerRunAt(schedule, new Date(), timezone),\n limit,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byTrigger?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n triggerSlug: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(triggerSlug, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byTrigger,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n listExecutableAttachmentsByTriggerIds,\n selectActiveEphemeralScheduledTriggerSlugs,\n selectTriggerBySlug,\n triggerTimezoneFromConfig,\n upsertTriggerScheduleFields,\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.triggerSlug);\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 selectActiveEphemeralScheduledTriggerSlugs();\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.triggerSlug,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerBySlug(spec.triggerSlug);\n if (!existing) {\n continue;\n }\n\n const existingTimezone = triggerTimezoneFromConfig(existing.config);\n const scheduleChanged = existing.schedule !== schedule || existingTimezone !== spec.timezone;\n const nextRunAt =\n !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now, spec.timezone);\n\n const executable = await listExecutableAttachmentsByTriggerIds([existing.id]);\n const enabled = executable.length > 0;\n\n await upsertTriggerScheduleFields({\n triggerSlug: spec.triggerSlug,\n schedule,\n nextRunAt: nextRunAt ?? now,\n enabled,\n updatedAt: now,\n });\n }\n}\n","import { setTriggerScheduleEnabled } from \"@keystrokehq/database\";\n\nimport { createScheduleTicker } from \"./schedule-ticker\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\nimport type {\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { JobQueue } from \"./types\";\n\nexport type DatabaseTriggerSchedulerOptions = {\n jobQueue: JobQueue;\n scope?: \"project\" | \"organization\";\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\n/** Postgres poll loop — default trigger scheduler for pg-boss, polling, and memory paths. */\nexport function createDatabaseTriggerScheduler(\n options: DatabaseTriggerSchedulerOptions,\n): TriggerScheduler {\n const ticker = createScheduleTicker({\n jobQueue: options.jobQueue,\n scope: options.scope,\n pollIntervalMs: options.pollIntervalMs,\n batchSize: options.batchSize,\n });\n\n return {\n sync: (syncOptions: TriggerScheduleSyncOptions) => syncTriggerSchedules(syncOptions),\n start: (startOptions?: TriggerSchedulerStartOptions) =>\n ticker.startScheduleTicker(startOptions),\n fireDue: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n async remove(triggerSlug: string) {\n await setTriggerScheduleEnabled(triggerSlug, false, new Date());\n },\n async upsert(_spec: TriggerScheduleSpec) {\n // DB rows are the firing source of truth; callers update triggers before upsert.\n },\n };\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(\n scope: \"platform\" | \"project\" | \"organization\",\n id?: string,\n): string {\n if (scope === \"platform\") {\n return \"keystroke_cancel_platform\";\n }\n\n if (scope === \"organization\") {\n const sanitized = (id ?? \"default\").replace(/[^a-z0-9_]/gi, \"_\").toLowerCase();\n return `keystroke_cancel_org_${sanitized}`;\n }\n\n const sanitized = (id ?? \"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, JobParent, 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 function pgBossOrgQueueName(organizationId: string): string {\n const sanitized = organizationId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_org_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_org_${createHash(\"sha1\").update(organizationId).digest(\"hex\").slice(0, 36)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n projectId?: string;\n organizationId?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\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 projectId?: string;\n parent?: JobParent;\n};\n\ntype BuildPgBossJobQueueOptions = {\n connectionString: string;\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\n projectId?: string;\n organizationId?: string;\n};\n\n/**\n * A {@link JobQueue} that also exposes its underlying pg-boss instance and queue\n * name so the trigger scheduler can register native cron schedules\n * (`boss.schedule`) against the same queue the worker drains.\n */\nexport type PgBossJobQueue = JobQueue & {\n boss: PgBoss;\n queueName: string;\n};\n\nexport function buildPgBossJobQueue(\n boss: PgBoss,\n options: BuildPgBossJobQueueOptions,\n): PgBossJobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const cancelChannel = createPgCancelChannel(\n options.connectionString,\n pgCancelChannelName(\n options.cancelScope ?? \"platform\",\n options.cancelScope === \"organization\" ? options.organizationId : options.projectId,\n ),\n );\n\n return {\n boss,\n queueName,\n async enqueue(input) {\n const jobId = await boss.send(\n queueName,\n {\n ...input,\n payload: input.payload,\n },\n {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n },\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 projectId: data.projectId,\n parent: data.parent,\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<PgBossJobQueue> {\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: options.cancelScope ?? \"project\",\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n options: { connectionString: string; queueName?: string },\n): Promise<PgBossJobQueue> {\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 getProjectScopeId,\n listEnabledScheduledTriggers,\n triggerTimezoneFromConfig,\n type StoredTriggerConfig,\n} from \"@keystrokehq/database\";\n\nimport type {\n SchedulerPluginContext,\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { PgBossJobQueue } from \"./pg-boss-queue\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\n\n/**\n * pg-boss schedule key for one trigger. Mirrors {@link bullmqTriggerSchedulerId}\n * so the firing backend is interchangeable: `trigger-<projectId>-<slug>`. pg-boss\n * keys allow hyphens, so colons (legal in ids) are swapped for `-`.\n */\nexport function pgBossTriggerScheduleKey(\n spec: Pick<TriggerScheduleSpec, \"triggerSlug\" | \"projectId\">,\n): string {\n const projectId = (spec.projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n const slug = spec.triggerSlug.replaceAll(\":\", \"-\");\n return `trigger-${projectId}-${slug}`;\n}\n\n/** Key prefix scoping reconcile cleanup to the active project (org queues are shared). */\nexport function pgBossTriggerScheduleKeyPrefix(projectId?: string): string {\n const scopedProjectId = (projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n return `trigger-${scopedProjectId}-`;\n}\n\nfunction triggerJobData(spec: TriggerScheduleSpec): {\n kind: \"trigger\";\n targetId: string;\n runId: string;\n trigger: \"cron\" | \"poll\";\n payload: Record<string, never>;\n projectId: string;\n} {\n return {\n kind: \"trigger\",\n targetId: spec.triggerSlug,\n // Empty: the worker fans out to attachment runs, each with its own run id.\n runId: \"\",\n trigger: spec.kind,\n payload: {},\n projectId: spec.projectId ?? getProjectScopeId(),\n };\n}\n\nfunction rowToSpec(row: {\n slug: string;\n kind: string;\n schedule: string | null;\n projectId: string;\n config?: StoredTriggerConfig | null;\n}): TriggerScheduleSpec | undefined {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n return undefined;\n }\n\n if (!row.schedule) {\n return undefined;\n }\n\n const timezone = triggerTimezoneFromConfig(row.config ?? null);\n\n return {\n triggerSlug: row.slug,\n kind: row.kind,\n schedule: row.schedule,\n ...(timezone ? { timezone } : {}),\n projectId: row.projectId,\n };\n}\n\n/**\n * Trigger scheduler backed by pg-boss native cron scheduling. Mirrors\n * {@link createBullmqTriggerScheduler}: `boss.schedule` registers a recurring job\n * onto the project/org queue (no per-second DB poll), and reconcile keeps the\n * `pgboss.schedule` table in sync with the `triggers` table (source of truth).\n *\n * Granularity: pg-boss cron is minute-level (it checks schedules every ~30s and\n * dedupes per minute), so sub-minute schedules effectively fire at most once per\n * minute — unlike the DB ticker's 1s loop.\n */\nexport function createPgBossTriggerScheduler(\n _ctx: SchedulerPluginContext,\n queue: PgBossJobQueue,\n): TriggerScheduler {\n async function upsertScheduler(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const schedule = resolveTriggerSchedule(spec.triggerSlug, spec.schedule, overrides);\n const key = pgBossTriggerScheduleKey(spec);\n await queue.boss.schedule(queue.queueName, schedule, triggerJobData({ ...spec, schedule }), {\n key,\n ...(spec.timezone ? { tz: spec.timezone } : {}),\n });\n }\n\n async function reconcileFromDatabase(\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const projectPrefix = pgBossTriggerScheduleKeyPrefix();\n const rows = await listEnabledScheduledTriggers();\n const expectedKeys = new Set<string>();\n const failures: Array<{ triggerSlug: string; error: unknown }> = [];\n\n for (const row of rows) {\n const spec = rowToSpec(row);\n if (!spec) {\n continue;\n }\n\n expectedKeys.add(pgBossTriggerScheduleKey(spec));\n try {\n await upsertScheduler(spec, overrides);\n } catch (error) {\n failures.push({ triggerSlug: spec.triggerSlug, error });\n console.error(`[pg-boss-trigger-scheduler] failed to schedule ${spec.triggerSlug}`, error);\n }\n }\n\n const schedules = await queue.boss.getSchedules(queue.queueName);\n for (const schedule of schedules) {\n const key = schedule.key;\n if (!key || !key.startsWith(projectPrefix)) {\n continue;\n }\n\n if (!expectedKeys.has(key)) {\n try {\n await queue.boss.unschedule(queue.queueName, key);\n } catch (error) {\n failures.push({ triggerSlug: key, error });\n console.error(`[pg-boss-trigger-scheduler] failed to unschedule stale ${key}`, error);\n }\n }\n }\n\n if (failures.length > 0) {\n throw new Error(\n `pg-boss trigger scheduler reconcile failed for ${failures.length} trigger(s)`,\n );\n }\n }\n\n return {\n async sync(syncOptions: TriggerScheduleSyncOptions) {\n await syncTriggerSchedules(syncOptions);\n await reconcileFromDatabase(syncOptions.scheduleOverrides);\n },\n\n async start(_options?: TriggerSchedulerStartOptions) {\n // pg-boss fires schedules from its own timekeeper; no DB poll loop to run.\n return async () => undefined;\n },\n\n fireDue: async () => 0,\n\n upsert: upsertScheduler,\n\n async remove(triggerSlug: string, projectId?: string) {\n await queue.boss.unschedule(\n queue.queueName,\n pgBossTriggerScheduleKey({ triggerSlug, projectId }),\n );\n },\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 type TriggerScheduler,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n type PgBossJobQueue,\n pgBossOrgQueueName,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\nimport { createPgBossTriggerScheduler } from \"./pg-boss-trigger-scheduler\";\nimport type { JobQueue } from \"./types\";\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/** DB poll-loop trigger scheduler — used by the SQLite/self-host polling backend. */\nfunction createPollingTriggerScheduler(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = ctx.scope === \"organization\" ? \"organization\" : \"project\";\n return Promise.resolve(createDatabaseTriggerScheduler({ jobQueue: queue, scope }));\n}\n\nfunction isPgBossJobQueue(queue: JobQueue): queue is PgBossJobQueue {\n return \"boss\" in queue && \"queueName\" in queue;\n}\n\n/** Native pg-boss cron scheduler; falls back to the DB ticker if the queue isn't pg-boss. */\nfunction createPgBossTriggerSchedulerForPlugin(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n if (!isPgBossJobQueue(queue)) {\n return createPollingTriggerScheduler(ctx, queue);\n }\n return Promise.resolve(createPgBossTriggerScheduler(ctx, queue));\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; org scope gets one queue per org. */\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 if (ctx.scope === \"organization\") {\n const organizationId = ctx.organizationId;\n if (!organizationId) {\n throw new Error(\"organizationId is required for organization-scoped pg-boss queue\");\n }\n\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossOrgQueueName(organizationId),\n cancelScope: \"organization\",\n organizationId,\n });\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n projectId,\n });\n },\n createTriggerScheduler: createPgBossTriggerSchedulerForPlugin,\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 createTriggerScheduler: createPollingTriggerScheduler,\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 async createTriggerScheduler(ctx, queue) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n }\n\n return pollingSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n },\n });\n}\n","import { defaultSchedulerPlugin } from \"./plugin\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport type { SchedulerPluginContext, TriggerScheduler } from \"./contract\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n TriggerScheduleSpec,\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 pluginContext(options: CreateSchedulerOptions = {}): SchedulerPluginContext {\n return {\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n };\n}\n\nasync function createTriggerScheduler(\n options: CreateSchedulerOptions,\n jobQueue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = options.scope === \"organization\" ? \"organization\" : \"project\";\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n const ctx = pluginContext(options);\n\n if (plugin.createTriggerScheduler) {\n return plugin.createTriggerScheduler(ctx, jobQueue);\n }\n\n return createDatabaseTriggerScheduler({ jobQueue, scope });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue, triggerScheduler: TriggerScheduler): Scheduler {\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, workerOptions) => jobQueue.startWorker(handler, workerOptions),\n ...(jobQueue.withLentSlot\n ? { withLentSlot: <T>(fn: () => Promise<T>) => jobQueue.withLentSlot!(fn) }\n : {}),\n publishCancel: (runId) => jobQueue.publishCancel(runId),\n subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => triggerScheduler.sync(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => triggerScheduler.start(options),\n fireDueSchedules: (asOf?: Date) => triggerScheduler.fireDue?.(asOf) ?? Promise.resolve(0),\n ...(triggerScheduler.upsert\n ? {\n upsertTriggerSchedule: (\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ) => triggerScheduler.upsert!(spec, overrides),\n }\n : {}),\n ...(triggerScheduler.remove\n ? {\n removeTriggerSchedule: (triggerSlug: string, projectId?: string) =>\n triggerScheduler.remove!(triggerSlug, projectId),\n }\n : {}),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n const triggerScheduler = await createTriggerScheduler(options, jobQueue);\n return wrapScheduler(jobQueue, triggerScheduler);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport async function wrapJobQueueAsScheduler(\n jobQueue: JobQueue,\n scope?: \"project\" | \"organization\",\n): Promise<Scheduler> {\n const triggerScheduler = createDatabaseTriggerScheduler({\n jobQueue,\n scope,\n });\n return wrapScheduler(jobQueue, triggerScheduler);\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 ...(input.parent ? { parent: input.parent } : {}),\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 await 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 await wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as StopFn, S as Scheduler, T as retryDelayMs, _ as JobPayload, a as TriggerScheduleSpec, b as ScheduleSyncOptions, d as CreateJobQueueOptions, f as CreateSchedulerOptions, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, v as JobQueue, w as WorkerOptions, x as ScheduleTickerOptions, y as JobTrigger } from "./contract-GEHKV_QB.cjs";
1
+ import { C as Scheduler, E as retryDelayMs, S as ScheduleTickerOptions, T as WorkerOptions, _ as JobParent, a as TriggerScheduleSpec, b as JobTrigger, d as CreateJobQueueOptions, f as CreateSchedulerOptions, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, v as JobPayload, w as StopFn, x as ScheduleSyncOptions, y as JobQueue } from "./contract-tgjsITaG.cjs";
2
2
  import { PgBoss } from "pg-boss";
3
3
 
4
4
  //#region src/create-scheduler.d.ts
@@ -109,5 +109,5 @@ declare function pgBossTriggerScheduleKeyPrefix(projectId?: string): string;
109
109
  */
110
110
  declare function createPgBossTriggerScheduler(_ctx: SchedulerPluginContext, queue: PgBossJobQueue): TriggerScheduler;
111
111
  //#endregion
112
- export { type CreateJobQueueOptions, type CreateSchedulerOptions, DEFAULT_QUEUE_NAME, DEFAULT_RETRY_DELAY_MS, type EnqueueInput, type JobHandler, type JobPayload, type JobQueue, type JobTrigger, type MemoryJobQueueOptions, type PgBossJobQueue, type ScheduleSyncOptions, type ScheduleTickerOptions, type Scheduler, type SchedulerPlugin, type SchedulerPluginContext, type SchedulerScope, type StopFn, type TriggerScheduleSpec, type TriggerScheduleSyncOptions, type TriggerScheduler, type WorkerOptions, buildPgBossJobQueue, configureSharedScheduler, createDatabaseTriggerScheduler, createJobQueue, createMemoryJobQueue, createPgBossTriggerScheduler, createScheduler, createSharedPgBossJobQueue, createSharedPgBossScheduler, createSharedScheduler, defaultSchedulerPlugin, defineSchedulerPlugin, getPgBoss, getSharedPgBossJobQueue, pgBossProjectQueueName, pgBossSchedulerPlugin, pgBossTriggerScheduleKey, pgBossTriggerScheduleKeyPrefix, pollingSchedulerPlugin, resetSharedPgBossJobQueueForTests, resetSharedSchedulerForTests, resolveTriggerSchedule, retryDelayMs, startPgBoss, stopPgBoss, syncTriggerSchedules, wrapJobQueueAsScheduler };
112
+ export { type CreateJobQueueOptions, type CreateSchedulerOptions, DEFAULT_QUEUE_NAME, DEFAULT_RETRY_DELAY_MS, type EnqueueInput, type JobHandler, type JobParent, type JobPayload, type JobQueue, type JobTrigger, type MemoryJobQueueOptions, type PgBossJobQueue, type ScheduleSyncOptions, type ScheduleTickerOptions, type Scheduler, type SchedulerPlugin, type SchedulerPluginContext, type SchedulerScope, type StopFn, type TriggerScheduleSpec, type TriggerScheduleSyncOptions, type TriggerScheduler, type WorkerOptions, buildPgBossJobQueue, configureSharedScheduler, createDatabaseTriggerScheduler, createJobQueue, createMemoryJobQueue, createPgBossTriggerScheduler, createScheduler, createSharedPgBossJobQueue, createSharedPgBossScheduler, createSharedScheduler, defaultSchedulerPlugin, defineSchedulerPlugin, getPgBoss, getSharedPgBossJobQueue, pgBossProjectQueueName, pgBossSchedulerPlugin, pgBossTriggerScheduleKey, pgBossTriggerScheduleKeyPrefix, pollingSchedulerPlugin, resetSharedPgBossJobQueueForTests, resetSharedSchedulerForTests, resolveTriggerSchedule, retryDelayMs, startPgBoss, stopPgBoss, syncTriggerSchedules, wrapJobQueueAsScheduler };
113
113
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/create-scheduler.ts","../src/sync-trigger-schedules.ts","../src/resolve-schedule.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts"],"mappings":";;;;iBAgFsB,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,OAAA,CAAQ,SAAA;AAAA,iBAM/D,cAAA,CAAe,OAAA,GAAS,qBAAA,GAA6B,OAAA,CAAQ,QAAA;AAAA,iBAI7D,uBAAA,CACpB,QAAA,EAAU,QAAA,EACV,KAAA,gCACC,OAAA,CAAQ,SAAA;;;iBChFW,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAO;;;KCXrE,uBAAA;EACV,MAAA;EACA,SAAA,GAAY,MAAM;AAAA;AAAA,iBAGJ,sBAAA,CACd,WAAA,UACA,QAAA,UACA,SAAA,GAAY,uBAAuB;;;KCNzB,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;;;;iBC2BA,qBAAA,CAAA,GAAyB,eAAe;;iBAqCxC,sBAAA,CAAA,GAA0B,eAAe;;iBAWzC,sBAAA,CAAA,GAA0B,eAAe;;;KC9F7C,+BAAA;EACV,QAAA,EAAU,QAAQ;EAClB,KAAA;EACA,cAAA;EACA,SAAA;AAAA;;iBAIc,8BAAA,CACd,OAAA,EAAS,+BAAA,GACR,gBAAgB;;;iBCPG,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;ATyEb;;;;;;AAAA,iBShEgB,sBAAA,CAAuB,SAAiB;AAAA,KAsCnD,0BAAA;EACH,gBAAA;EACA,oBAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;EACA,cAAA;AAAA;;;;;AT0ByF;KSlB/E,cAAA,GAAiB,QAAA;EAC3B,IAAA,EAAM,MAAM;EACZ,SAAA;AAAA;AAAA,iBAGc,mBAAA,CACd,IAAA,EAAM,MAAA,EACN,OAAA,EAAS,0BAAA,GACR,cAAA;AAAA,iBA2GmB,0BAAA,CACpB,IAAA,EAAM,MAAA,EACN,OAAA;EAAW,gBAAA;EAA0B,SAAA;AAAA,IACpC,OAAA,CAAQ,cAAA;;;;;AT1GX;;;iBUzDgB,wBAAA,CACd,IAAA,EAAM,IAAI,CAAC,mBAAA;;iBAQG,8BAAA,CAA+B,SAAkB;;;;;;;;AVgD6B;AAM9F;;iBUMgB,4BAAA,CACd,IAAA,EAAM,sBAAA,EACN,KAAA,EAAO,cAAA,GACN,gBAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/create-scheduler.ts","../src/sync-trigger-schedules.ts","../src/resolve-schedule.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts"],"mappings":";;;;iBAmFsB,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,OAAA,CAAQ,SAAA;AAAA,iBAM/D,cAAA,CAAe,OAAA,GAAS,qBAAA,GAA6B,OAAA,CAAQ,QAAA;AAAA,iBAI7D,uBAAA,CACpB,QAAA,EAAU,QAAA,EACV,KAAA,gCACC,OAAA,CAAQ,SAAA;;;iBCnFW,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAO;;;KCXrE,uBAAA;EACV,MAAA;EACA,SAAA,GAAY,MAAM;AAAA;AAAA,iBAGJ,sBAAA,CACd,WAAA,UACA,QAAA,UACA,SAAA,GAAY,uBAAuB;;;KCNzB,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;;;;iBC2BA,qBAAA,CAAA,GAAyB,eAAe;;iBAqCxC,sBAAA,CAAA,GAA0B,eAAe;;iBAWzC,sBAAA,CAAA,GAA0B,eAAe;;;KC9F7C,+BAAA;EACV,QAAA,EAAU,QAAQ;EAClB,KAAA;EACA,cAAA;EACA,SAAA;AAAA;;iBAIc,8BAAA,CACd,OAAA,EAAS,+BAAA,GACR,gBAAgB;;;iBCPG,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;AT4Eb;;;;;;AAAA,iBSnEgB,sBAAA,CAAuB,SAAiB;AAAA,KAuCnD,0BAAA;EACH,gBAAA;EACA,oBAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;EACA,cAAA;AAAA;;;;;AT4ByF;KSpB/E,cAAA,GAAiB,QAAA;EAC3B,IAAA,EAAM,MAAM;EACZ,SAAA;AAAA;AAAA,iBAGc,mBAAA,CACd,IAAA,EAAM,MAAA,EACN,OAAA,EAAS,0BAAA,GACR,cAAA;AAAA,iBA4GmB,0BAAA,CACpB,IAAA,EAAM,MAAA,EACN,OAAA;EAAW,gBAAA;EAA0B,SAAA;AAAA,IACpC,OAAA,CAAQ,cAAA;;;;;ATzGX;;;iBU5DgB,wBAAA,CACd,IAAA,EAAM,IAAI,CAAC,mBAAA;;iBAQG,8BAAA,CAA+B,SAAkB;;;;;;;;AVmD6B;AAM9F;;iBUGgB,4BAAA,CACd,IAAA,EAAM,sBAAA,EACN,KAAA,EAAO,cAAA,GACN,gBAAA"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as StopFn, S as Scheduler, T as retryDelayMs, _ as JobPayload, a as TriggerScheduleSpec, b as ScheduleSyncOptions, d as CreateJobQueueOptions, f as CreateSchedulerOptions, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, v as JobQueue, w as WorkerOptions, x as ScheduleTickerOptions, y as JobTrigger } from "./contract-GEHKV_QB.mjs";
1
+ import { C as Scheduler, E as retryDelayMs, S as ScheduleTickerOptions, T as WorkerOptions, _ as JobParent, a as TriggerScheduleSpec, b as JobTrigger, d as CreateJobQueueOptions, f as CreateSchedulerOptions, h as JobHandler, i as SchedulerScope, l as defineSchedulerPlugin, m as EnqueueInput, n as SchedulerPlugin, o as TriggerScheduleSyncOptions, p as DEFAULT_RETRY_DELAY_MS, r as SchedulerPluginContext, s as TriggerScheduler, v as JobPayload, w as StopFn, x as ScheduleSyncOptions, y as JobQueue } from "./contract-tgjsITaG.mjs";
2
2
  import { PgBoss } from "pg-boss";
3
3
 
4
4
  //#region src/create-scheduler.d.ts
@@ -109,5 +109,5 @@ declare function pgBossTriggerScheduleKeyPrefix(projectId?: string): string;
109
109
  */
110
110
  declare function createPgBossTriggerScheduler(_ctx: SchedulerPluginContext, queue: PgBossJobQueue): TriggerScheduler;
111
111
  //#endregion
112
- export { type CreateJobQueueOptions, type CreateSchedulerOptions, DEFAULT_QUEUE_NAME, DEFAULT_RETRY_DELAY_MS, type EnqueueInput, type JobHandler, type JobPayload, type JobQueue, type JobTrigger, type MemoryJobQueueOptions, type PgBossJobQueue, type ScheduleSyncOptions, type ScheduleTickerOptions, type Scheduler, type SchedulerPlugin, type SchedulerPluginContext, type SchedulerScope, type StopFn, type TriggerScheduleSpec, type TriggerScheduleSyncOptions, type TriggerScheduler, type WorkerOptions, buildPgBossJobQueue, configureSharedScheduler, createDatabaseTriggerScheduler, createJobQueue, createMemoryJobQueue, createPgBossTriggerScheduler, createScheduler, createSharedPgBossJobQueue, createSharedPgBossScheduler, createSharedScheduler, defaultSchedulerPlugin, defineSchedulerPlugin, getPgBoss, getSharedPgBossJobQueue, pgBossProjectQueueName, pgBossSchedulerPlugin, pgBossTriggerScheduleKey, pgBossTriggerScheduleKeyPrefix, pollingSchedulerPlugin, resetSharedPgBossJobQueueForTests, resetSharedSchedulerForTests, resolveTriggerSchedule, retryDelayMs, startPgBoss, stopPgBoss, syncTriggerSchedules, wrapJobQueueAsScheduler };
112
+ export { type CreateJobQueueOptions, type CreateSchedulerOptions, DEFAULT_QUEUE_NAME, DEFAULT_RETRY_DELAY_MS, type EnqueueInput, type JobHandler, type JobParent, type JobPayload, type JobQueue, type JobTrigger, type MemoryJobQueueOptions, type PgBossJobQueue, type ScheduleSyncOptions, type ScheduleTickerOptions, type Scheduler, type SchedulerPlugin, type SchedulerPluginContext, type SchedulerScope, type StopFn, type TriggerScheduleSpec, type TriggerScheduleSyncOptions, type TriggerScheduler, type WorkerOptions, buildPgBossJobQueue, configureSharedScheduler, createDatabaseTriggerScheduler, createJobQueue, createMemoryJobQueue, createPgBossTriggerScheduler, createScheduler, createSharedPgBossJobQueue, createSharedPgBossScheduler, createSharedScheduler, defaultSchedulerPlugin, defineSchedulerPlugin, getPgBoss, getSharedPgBossJobQueue, pgBossProjectQueueName, pgBossSchedulerPlugin, pgBossTriggerScheduleKey, pgBossTriggerScheduleKeyPrefix, pollingSchedulerPlugin, resetSharedPgBossJobQueueForTests, resetSharedSchedulerForTests, resolveTriggerSchedule, retryDelayMs, startPgBoss, stopPgBoss, syncTriggerSchedules, wrapJobQueueAsScheduler };
113
113
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/create-scheduler.ts","../src/sync-trigger-schedules.ts","../src/resolve-schedule.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts"],"mappings":";;;;iBAgFsB,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,OAAA,CAAQ,SAAA;AAAA,iBAM/D,cAAA,CAAe,OAAA,GAAS,qBAAA,GAA6B,OAAA,CAAQ,QAAA;AAAA,iBAI7D,uBAAA,CACpB,QAAA,EAAU,QAAA,EACV,KAAA,gCACC,OAAA,CAAQ,SAAA;;;iBChFW,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAO;;;KCXrE,uBAAA;EACV,MAAA;EACA,SAAA,GAAY,MAAM;AAAA;AAAA,iBAGJ,sBAAA,CACd,WAAA,UACA,QAAA,UACA,SAAA,GAAY,uBAAuB;;;KCNzB,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;;;;iBC2BA,qBAAA,CAAA,GAAyB,eAAe;;iBAqCxC,sBAAA,CAAA,GAA0B,eAAe;;iBAWzC,sBAAA,CAAA,GAA0B,eAAe;;;KC9F7C,+BAAA;EACV,QAAA,EAAU,QAAQ;EAClB,KAAA;EACA,cAAA;EACA,SAAA;AAAA;;iBAIc,8BAAA,CACd,OAAA,EAAS,+BAAA,GACR,gBAAgB;;;iBCPG,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;ATyEb;;;;;;AAAA,iBShEgB,sBAAA,CAAuB,SAAiB;AAAA,KAsCnD,0BAAA;EACH,gBAAA;EACA,oBAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;EACA,cAAA;AAAA;;;;;AT0ByF;KSlB/E,cAAA,GAAiB,QAAA;EAC3B,IAAA,EAAM,MAAM;EACZ,SAAA;AAAA;AAAA,iBAGc,mBAAA,CACd,IAAA,EAAM,MAAA,EACN,OAAA,EAAS,0BAAA,GACR,cAAA;AAAA,iBA2GmB,0BAAA,CACpB,IAAA,EAAM,MAAA,EACN,OAAA;EAAW,gBAAA;EAA0B,SAAA;AAAA,IACpC,OAAA,CAAQ,cAAA;;;;;AT1GX;;;iBUzDgB,wBAAA,CACd,IAAA,EAAM,IAAI,CAAC,mBAAA;;iBAQG,8BAAA,CAA+B,SAAkB;;;;;;;;AVgD6B;AAM9F;;iBUMgB,4BAAA,CACd,IAAA,EAAM,sBAAA,EACN,KAAA,EAAO,cAAA,GACN,gBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/create-scheduler.ts","../src/sync-trigger-schedules.ts","../src/resolve-schedule.ts","../src/memory.ts","../src/shared-scheduler.ts","../src/plugin.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/shared-pgboss-queue.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts"],"mappings":";;;;iBAmFsB,eAAA,CAAgB,OAAA,GAAS,sBAAA,GAA8B,OAAA,CAAQ,SAAA;AAAA,iBAM/D,cAAA,CAAe,OAAA,GAAS,qBAAA,GAA6B,OAAA,CAAQ,QAAA;AAAA,iBAI7D,uBAAA,CACpB,QAAA,EAAU,QAAA,EACV,KAAA,gCACC,OAAA,CAAQ,SAAA;;;iBCnFW,oBAAA,CAAqB,OAAA,EAAS,mBAAA,GAAsB,OAAO;;;KCXrE,uBAAA;EACV,MAAA;EACA,SAAA,GAAY,MAAM;AAAA;AAAA,iBAGJ,sBAAA,CACd,WAAA,UACA,QAAA,UACA,SAAA,GAAY,uBAAuB;;;KCNzB,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;;;;iBC2BA,qBAAA,CAAA,GAAyB,eAAe;;iBAqCxC,sBAAA,CAAA,GAA0B,eAAe;;iBAWzC,sBAAA,CAAA,GAA0B,eAAe;;;KC9F7C,+BAAA;EACV,QAAA,EAAU,QAAQ;EAClB,KAAA;EACA,cAAA;EACA,SAAA;AAAA;;iBAIc,8BAAA,CACd,OAAA,EAAS,+BAAA,GACR,gBAAgB;;;iBCPG,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;AT4Eb;;;;;;AAAA,iBSnEgB,sBAAA,CAAuB,SAAiB;AAAA,KAuCnD,0BAAA;EACH,gBAAA;EACA,oBAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;EACA,cAAA;AAAA;;;;;AT4ByF;KSpB/E,cAAA,GAAiB,QAAA;EAC3B,IAAA,EAAM,MAAM;EACZ,SAAA;AAAA;AAAA,iBAGc,mBAAA,CACd,IAAA,EAAM,MAAA,EACN,OAAA,EAAS,0BAAA,GACR,cAAA;AAAA,iBA4GmB,0BAAA,CACpB,IAAA,EAAM,MAAA,EACN,OAAA;EAAW,gBAAA;EAA0B,SAAA;AAAA,IACpC,OAAA,CAAQ,cAAA;;;;;ATzGX;;;iBU5DgB,wBAAA,CACd,IAAA,EAAM,IAAI,CAAC,mBAAA;;iBAQG,8BAAA,CAA+B,SAAkB;;;;;;;;AVmD6B;AAM9F;;iBUGgB,4BAAA,CACd,IAAA,EAAM,sBAAA,EACN,KAAA,EAAO,cAAA,GACN,gBAAA"}
package/dist/index.mjs CHANGED
@@ -35,25 +35,26 @@ function toJobPayload(job) {
35
35
  payload: job.payload,
36
36
  attempt: job.attempt,
37
37
  maxAttempts: job.maxAttempts,
38
- scheduledAt: job.scheduledAt
38
+ scheduledAt: job.scheduledAt,
39
+ ...job.parent ? { parent: job.parent } : {}
39
40
  };
40
41
  }
41
42
  function createDatabaseJobQueue() {
42
43
  const cancelChannel = createInProcessCancelChannel();
43
- return {
44
- async enqueue(input) {
45
- return enqueueJob(input);
46
- },
47
- async startWorker(handler, options = {}) {
48
- const workerId = options.workerId ?? crypto.randomUUID();
49
- const pollIntervalMs = options.pollIntervalMs ?? 250;
50
- const leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 3e4;
51
- let running = true;
52
- const leaseTimer = setInterval(() => {
53
- requeueExpiredLeases();
54
- }, leaseSweepIntervalMs);
55
- const loop = async () => {
56
- while (running) try {
44
+ let handler;
45
+ let running = false;
46
+ let lentSlots = 0;
47
+ let activeLoops = 0;
48
+ let workerIdBase = "";
49
+ let pollIntervalMs = 250;
50
+ let leaseSweepIntervalMs = 3e4;
51
+ let leaseTimer;
52
+ const desiredConcurrency = () => 1 + lentSlots;
53
+ const loop = async (workerId) => {
54
+ try {
55
+ while (running && handler) {
56
+ if (activeLoops > desiredConcurrency()) return;
57
+ try {
57
58
  const job = await claimNextJob(workerId);
58
59
  if (!job) {
59
60
  await sleep$1(pollIntervalMs);
@@ -76,13 +77,47 @@ function createDatabaseJobQueue() {
76
77
  } catch {
77
78
  await sleep$1(pollIntervalMs);
78
79
  }
79
- };
80
- loop();
80
+ }
81
+ } finally {
82
+ activeLoops -= 1;
83
+ ensureWorkers();
84
+ }
85
+ };
86
+ const ensureWorkers = () => {
87
+ while (running && handler && activeLoops < desiredConcurrency()) {
88
+ activeLoops += 1;
89
+ loop(`${workerIdBase}:${activeLoops}`);
90
+ }
91
+ };
92
+ return {
93
+ async enqueue(input) {
94
+ return enqueueJob(input);
95
+ },
96
+ async startWorker(nextHandler, options = {}) {
97
+ handler = nextHandler;
98
+ running = true;
99
+ workerIdBase = options.workerId ?? crypto.randomUUID();
100
+ pollIntervalMs = options.pollIntervalMs ?? 250;
101
+ leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 3e4;
102
+ leaseTimer = setInterval(() => {
103
+ requeueExpiredLeases();
104
+ }, leaseSweepIntervalMs);
105
+ ensureWorkers();
81
106
  return async () => {
82
107
  running = false;
83
108
  clearInterval(leaseTimer);
109
+ leaseTimer = void 0;
84
110
  };
85
111
  },
112
+ async withLentSlot(fn) {
113
+ lentSlots += 1;
114
+ ensureWorkers();
115
+ try {
116
+ return await fn();
117
+ } finally {
118
+ lentSlots -= 1;
119
+ }
120
+ },
86
121
  publishCancel: (runId) => cancelChannel.publishCancel(runId),
87
122
  subscribeCancel: (handler) => cancelChannel.subscribeCancel(handler)
88
123
  };
@@ -320,7 +355,8 @@ function buildPgBossJobQueue(boss, options) {
320
355
  maxAttempts,
321
356
  exhaustedRetries: attempt >= maxAttempts,
322
357
  scheduledAt: data.scheduledAt ? new Date(data.scheduledAt) : /* @__PURE__ */ new Date(),
323
- projectId: data.projectId
358
+ projectId: data.projectId,
359
+ parent: data.parent
324
360
  });
325
361
  });
326
362
  return async () => {
@@ -583,6 +619,7 @@ function wrapScheduler(jobQueue, triggerScheduler) {
583
619
  return {
584
620
  enqueue: (input) => jobQueue.enqueue(input),
585
621
  startWorker: (handler, workerOptions) => jobQueue.startWorker(handler, workerOptions),
622
+ ...jobQueue.withLentSlot ? { withLentSlot: (fn) => jobQueue.withLentSlot(fn) } : {},
586
623
  publishCancel: (runId) => jobQueue.publishCancel(runId),
587
624
  subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),
588
625
  syncTriggerSchedules: (options) => triggerScheduler.sync(options),
@@ -629,7 +666,8 @@ function createMemoryJobQueue(options = {}) {
629
666
  payload: input.payload ?? {},
630
667
  attempt: input.attempt ?? 1,
631
668
  maxAttempts: input.maxAttempts ?? 3,
632
- scheduledAt: input.scheduledAt ?? next.enqueuedAt
669
+ scheduledAt: input.scheduledAt ?? next.enqueuedAt,
670
+ ...input.parent ? { parent: input.parent } : {}
633
671
  });
634
672
  }
635
673
  } finally {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["sleep","sharedJobQueue"],"sources":["../src/cancel-channel.ts","../src/database-queue.ts","../src/schedule-ticker.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/pg-cancel-channel.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts","../src/plugin.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts"],"sourcesContent":["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 failAgentSession,\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 // Last-resort run finalization: the dispatcher's own\n // finalization covers everything inside dispatch, but errors\n // before it (project resolution, scope setup) would otherwise\n // strand the run in a non-terminal status forever.\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n if (job.kind === \"agent\") {\n const sessionId = (job.payload as { sessionId?: string } | null)?.sessionId;\n if (sessionId) {\n await failAgentSession(sessionId, error);\n }\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 { claimDueTriggers, claimDueTriggersForOrg } from \"@keystrokehq/database\";\nimport { getProjectScopeId } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { EnqueueInput, JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n scope?: \"project\" | \"organization\";\n};\n\nfunction enqueueTriggerJob(\n jobQueue: JobQueue,\n row: { slug: string; kind: \"cron\" | \"poll\"; projectId: string },\n scheduledAt?: Date,\n): Promise<string> {\n const input: EnqueueInput = {\n kind: \"trigger\",\n targetId: row.slug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n projectId: row.projectId,\n };\n\n if (scheduledAt) {\n input.scheduledAt = scheduledAt;\n }\n\n return jobQueue.enqueue(input);\n}\n\nasync function claimDue(\n scope: \"project\" | \"organization\",\n asOf: Date,\n resolveNextRunAt: (schedule: string, timezone?: string) => Date,\n limit: number,\n) {\n if (scope === \"organization\") {\n return claimDueTriggersForOrg(asOf, resolveNextRunAt, limit);\n }\n\n return claimDueTriggers(asOf, resolveNextRunAt, limit).then((rows) =>\n rows.map((row) => ({ ...row, projectId: getProjectScopeId() })),\n );\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 scope = ctx.scope ?? \"project\";\n const claimed = await claimDue(\n scope,\n asOf,\n (schedule, timezone) => nextTriggerRunAt(schedule, asOf, timezone),\n batchSize,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n // Claim uses `asOf` to decide which schedules are due, but the job must be\n // processable immediately — stamping `scheduledAt: asOf` (e.g. a far-future\n // test clock) would leave the job pending forever under `scheduledAt <= now`.\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 const scope = options.scope ?? ctx.scope ?? \"project\";\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n const claimed = await claimDue(\n scope,\n new Date(),\n (schedule, timezone) => nextTriggerRunAt(schedule, new Date(), timezone),\n limit,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byTrigger?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n triggerSlug: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(triggerSlug, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byTrigger,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n listExecutableAttachmentsByTriggerIds,\n selectActiveEphemeralScheduledTriggerSlugs,\n selectTriggerBySlug,\n triggerTimezoneFromConfig,\n upsertTriggerScheduleFields,\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.triggerSlug);\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 selectActiveEphemeralScheduledTriggerSlugs();\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.triggerSlug,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerBySlug(spec.triggerSlug);\n if (!existing) {\n continue;\n }\n\n const existingTimezone = triggerTimezoneFromConfig(existing.config);\n const scheduleChanged = existing.schedule !== schedule || existingTimezone !== spec.timezone;\n const nextRunAt =\n !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now, spec.timezone);\n\n const executable = await listExecutableAttachmentsByTriggerIds([existing.id]);\n const enabled = executable.length > 0;\n\n await upsertTriggerScheduleFields({\n triggerSlug: spec.triggerSlug,\n schedule,\n nextRunAt: nextRunAt ?? now,\n enabled,\n updatedAt: now,\n });\n }\n}\n","import { setTriggerScheduleEnabled } from \"@keystrokehq/database\";\n\nimport { createScheduleTicker } from \"./schedule-ticker\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\nimport type {\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { JobQueue } from \"./types\";\n\nexport type DatabaseTriggerSchedulerOptions = {\n jobQueue: JobQueue;\n scope?: \"project\" | \"organization\";\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\n/** Postgres poll loop — default trigger scheduler for pg-boss, polling, and memory paths. */\nexport function createDatabaseTriggerScheduler(\n options: DatabaseTriggerSchedulerOptions,\n): TriggerScheduler {\n const ticker = createScheduleTicker({\n jobQueue: options.jobQueue,\n scope: options.scope,\n pollIntervalMs: options.pollIntervalMs,\n batchSize: options.batchSize,\n });\n\n return {\n sync: (syncOptions: TriggerScheduleSyncOptions) => syncTriggerSchedules(syncOptions),\n start: (startOptions?: TriggerSchedulerStartOptions) =>\n ticker.startScheduleTicker(startOptions),\n fireDue: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n async remove(triggerSlug: string) {\n await setTriggerScheduleEnabled(triggerSlug, false, new Date());\n },\n async upsert(_spec: TriggerScheduleSpec) {\n // DB rows are the firing source of truth; callers update triggers before upsert.\n },\n };\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(\n scope: \"platform\" | \"project\" | \"organization\",\n id?: string,\n): string {\n if (scope === \"platform\") {\n return \"keystroke_cancel_platform\";\n }\n\n if (scope === \"organization\") {\n const sanitized = (id ?? \"default\").replace(/[^a-z0-9_]/gi, \"_\").toLowerCase();\n return `keystroke_cancel_org_${sanitized}`;\n }\n\n const sanitized = (id ?? \"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 function pgBossOrgQueueName(organizationId: string): string {\n const sanitized = organizationId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_org_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_org_${createHash(\"sha1\").update(organizationId).digest(\"hex\").slice(0, 36)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n projectId?: string;\n organizationId?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\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 projectId?: string;\n};\n\ntype BuildPgBossJobQueueOptions = {\n connectionString: string;\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\n projectId?: string;\n organizationId?: string;\n};\n\n/**\n * A {@link JobQueue} that also exposes its underlying pg-boss instance and queue\n * name so the trigger scheduler can register native cron schedules\n * (`boss.schedule`) against the same queue the worker drains.\n */\nexport type PgBossJobQueue = JobQueue & {\n boss: PgBoss;\n queueName: string;\n};\n\nexport function buildPgBossJobQueue(\n boss: PgBoss,\n options: BuildPgBossJobQueueOptions,\n): PgBossJobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const cancelChannel = createPgCancelChannel(\n options.connectionString,\n pgCancelChannelName(\n options.cancelScope ?? \"platform\",\n options.cancelScope === \"organization\" ? options.organizationId : options.projectId,\n ),\n );\n\n return {\n boss,\n queueName,\n async enqueue(input) {\n const jobId = await boss.send(\n queueName,\n {\n ...input,\n payload: input.payload,\n },\n {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n },\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 projectId: data.projectId,\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<PgBossJobQueue> {\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: options.cancelScope ?? \"project\",\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n options: { connectionString: string; queueName?: string },\n): Promise<PgBossJobQueue> {\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 getProjectScopeId,\n listEnabledScheduledTriggers,\n triggerTimezoneFromConfig,\n type StoredTriggerConfig,\n} from \"@keystrokehq/database\";\n\nimport type {\n SchedulerPluginContext,\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { PgBossJobQueue } from \"./pg-boss-queue\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\n\n/**\n * pg-boss schedule key for one trigger. Mirrors {@link bullmqTriggerSchedulerId}\n * so the firing backend is interchangeable: `trigger-<projectId>-<slug>`. pg-boss\n * keys allow hyphens, so colons (legal in ids) are swapped for `-`.\n */\nexport function pgBossTriggerScheduleKey(\n spec: Pick<TriggerScheduleSpec, \"triggerSlug\" | \"projectId\">,\n): string {\n const projectId = (spec.projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n const slug = spec.triggerSlug.replaceAll(\":\", \"-\");\n return `trigger-${projectId}-${slug}`;\n}\n\n/** Key prefix scoping reconcile cleanup to the active project (org queues are shared). */\nexport function pgBossTriggerScheduleKeyPrefix(projectId?: string): string {\n const scopedProjectId = (projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n return `trigger-${scopedProjectId}-`;\n}\n\nfunction triggerJobData(spec: TriggerScheduleSpec): {\n kind: \"trigger\";\n targetId: string;\n runId: string;\n trigger: \"cron\" | \"poll\";\n payload: Record<string, never>;\n projectId: string;\n} {\n return {\n kind: \"trigger\",\n targetId: spec.triggerSlug,\n // Empty: the worker fans out to attachment runs, each with its own run id.\n runId: \"\",\n trigger: spec.kind,\n payload: {},\n projectId: spec.projectId ?? getProjectScopeId(),\n };\n}\n\nfunction rowToSpec(row: {\n slug: string;\n kind: string;\n schedule: string | null;\n projectId: string;\n config?: StoredTriggerConfig | null;\n}): TriggerScheduleSpec | undefined {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n return undefined;\n }\n\n if (!row.schedule) {\n return undefined;\n }\n\n const timezone = triggerTimezoneFromConfig(row.config ?? null);\n\n return {\n triggerSlug: row.slug,\n kind: row.kind,\n schedule: row.schedule,\n ...(timezone ? { timezone } : {}),\n projectId: row.projectId,\n };\n}\n\n/**\n * Trigger scheduler backed by pg-boss native cron scheduling. Mirrors\n * {@link createBullmqTriggerScheduler}: `boss.schedule` registers a recurring job\n * onto the project/org queue (no per-second DB poll), and reconcile keeps the\n * `pgboss.schedule` table in sync with the `triggers` table (source of truth).\n *\n * Granularity: pg-boss cron is minute-level (it checks schedules every ~30s and\n * dedupes per minute), so sub-minute schedules effectively fire at most once per\n * minute — unlike the DB ticker's 1s loop.\n */\nexport function createPgBossTriggerScheduler(\n _ctx: SchedulerPluginContext,\n queue: PgBossJobQueue,\n): TriggerScheduler {\n async function upsertScheduler(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const schedule = resolveTriggerSchedule(spec.triggerSlug, spec.schedule, overrides);\n const key = pgBossTriggerScheduleKey(spec);\n await queue.boss.schedule(queue.queueName, schedule, triggerJobData({ ...spec, schedule }), {\n key,\n ...(spec.timezone ? { tz: spec.timezone } : {}),\n });\n }\n\n async function reconcileFromDatabase(\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const projectPrefix = pgBossTriggerScheduleKeyPrefix();\n const rows = await listEnabledScheduledTriggers();\n const expectedKeys = new Set<string>();\n const failures: Array<{ triggerSlug: string; error: unknown }> = [];\n\n for (const row of rows) {\n const spec = rowToSpec(row);\n if (!spec) {\n continue;\n }\n\n expectedKeys.add(pgBossTriggerScheduleKey(spec));\n try {\n await upsertScheduler(spec, overrides);\n } catch (error) {\n failures.push({ triggerSlug: spec.triggerSlug, error });\n console.error(`[pg-boss-trigger-scheduler] failed to schedule ${spec.triggerSlug}`, error);\n }\n }\n\n const schedules = await queue.boss.getSchedules(queue.queueName);\n for (const schedule of schedules) {\n const key = schedule.key;\n if (!key || !key.startsWith(projectPrefix)) {\n continue;\n }\n\n if (!expectedKeys.has(key)) {\n try {\n await queue.boss.unschedule(queue.queueName, key);\n } catch (error) {\n failures.push({ triggerSlug: key, error });\n console.error(`[pg-boss-trigger-scheduler] failed to unschedule stale ${key}`, error);\n }\n }\n }\n\n if (failures.length > 0) {\n throw new Error(\n `pg-boss trigger scheduler reconcile failed for ${failures.length} trigger(s)`,\n );\n }\n }\n\n return {\n async sync(syncOptions: TriggerScheduleSyncOptions) {\n await syncTriggerSchedules(syncOptions);\n await reconcileFromDatabase(syncOptions.scheduleOverrides);\n },\n\n async start(_options?: TriggerSchedulerStartOptions) {\n // pg-boss fires schedules from its own timekeeper; no DB poll loop to run.\n return async () => undefined;\n },\n\n fireDue: async () => 0,\n\n upsert: upsertScheduler,\n\n async remove(triggerSlug: string, projectId?: string) {\n await queue.boss.unschedule(\n queue.queueName,\n pgBossTriggerScheduleKey({ triggerSlug, projectId }),\n );\n },\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 type TriggerScheduler,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n type PgBossJobQueue,\n pgBossOrgQueueName,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\nimport { createPgBossTriggerScheduler } from \"./pg-boss-trigger-scheduler\";\nimport type { JobQueue } from \"./types\";\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/** DB poll-loop trigger scheduler — used by the SQLite/self-host polling backend. */\nfunction createPollingTriggerScheduler(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = ctx.scope === \"organization\" ? \"organization\" : \"project\";\n return Promise.resolve(createDatabaseTriggerScheduler({ jobQueue: queue, scope }));\n}\n\nfunction isPgBossJobQueue(queue: JobQueue): queue is PgBossJobQueue {\n return \"boss\" in queue && \"queueName\" in queue;\n}\n\n/** Native pg-boss cron scheduler; falls back to the DB ticker if the queue isn't pg-boss. */\nfunction createPgBossTriggerSchedulerForPlugin(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n if (!isPgBossJobQueue(queue)) {\n return createPollingTriggerScheduler(ctx, queue);\n }\n return Promise.resolve(createPgBossTriggerScheduler(ctx, queue));\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; org scope gets one queue per org. */\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 if (ctx.scope === \"organization\") {\n const organizationId = ctx.organizationId;\n if (!organizationId) {\n throw new Error(\"organizationId is required for organization-scoped pg-boss queue\");\n }\n\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossOrgQueueName(organizationId),\n cancelScope: \"organization\",\n organizationId,\n });\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n projectId,\n });\n },\n createTriggerScheduler: createPgBossTriggerSchedulerForPlugin,\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 createTriggerScheduler: createPollingTriggerScheduler,\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 async createTriggerScheduler(ctx, queue) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n }\n\n return pollingSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n },\n });\n}\n","import { defaultSchedulerPlugin } from \"./plugin\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport type { SchedulerPluginContext, TriggerScheduler } from \"./contract\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n TriggerScheduleSpec,\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 pluginContext(options: CreateSchedulerOptions = {}): SchedulerPluginContext {\n return {\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n };\n}\n\nasync function createTriggerScheduler(\n options: CreateSchedulerOptions,\n jobQueue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = options.scope === \"organization\" ? \"organization\" : \"project\";\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n const ctx = pluginContext(options);\n\n if (plugin.createTriggerScheduler) {\n return plugin.createTriggerScheduler(ctx, jobQueue);\n }\n\n return createDatabaseTriggerScheduler({ jobQueue, scope });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue, triggerScheduler: TriggerScheduler): Scheduler {\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, workerOptions) => jobQueue.startWorker(handler, workerOptions),\n publishCancel: (runId) => jobQueue.publishCancel(runId),\n subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => triggerScheduler.sync(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => triggerScheduler.start(options),\n fireDueSchedules: (asOf?: Date) => triggerScheduler.fireDue?.(asOf) ?? Promise.resolve(0),\n ...(triggerScheduler.upsert\n ? {\n upsertTriggerSchedule: (\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ) => triggerScheduler.upsert!(spec, overrides),\n }\n : {}),\n ...(triggerScheduler.remove\n ? {\n removeTriggerSchedule: (triggerSlug: string, projectId?: string) =>\n triggerScheduler.remove!(triggerSlug, projectId),\n }\n : {}),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n const triggerScheduler = await createTriggerScheduler(options, jobQueue);\n return wrapScheduler(jobQueue, triggerScheduler);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport async function wrapJobQueueAsScheduler(\n jobQueue: JobQueue,\n scope?: \"project\" | \"organization\",\n): Promise<Scheduler> {\n const triggerScheduler = createDatabaseTriggerScheduler({\n jobQueue,\n scope,\n });\n return wrapScheduler(jobQueue, triggerScheduler);\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 await 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 await wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["sleep","sharedJobQueue"],"sources":["../src/cancel-channel.ts","../src/database-queue.ts","../src/schedule-ticker.ts","../src/resolve-schedule.ts","../src/sync-trigger-schedules.ts","../src/database-trigger-scheduler.ts","../src/pg-boss-client.ts","../src/pg-cancel-channel.ts","../src/pg-boss-queue.ts","../src/pg-boss-trigger-scheduler.ts","../src/plugin.ts","../src/create-scheduler.ts","../src/memory.ts","../src/shared-pgboss-queue.ts","../src/shared-scheduler.ts"],"sourcesContent":["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 failAgentSession,\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 ...(job.parent ? { parent: job.parent } : {}),\n };\n}\n\nexport function createDatabaseJobQueue(): JobQueue {\n const cancelChannel = createInProcessCancelChannel();\n let handler: JobHandler | undefined;\n let running = false;\n let lentSlots = 0;\n let activeLoops = 0;\n let workerIdBase = \"\";\n let pollIntervalMs = 250;\n let leaseSweepIntervalMs = 30_000;\n let leaseTimer: ReturnType<typeof setInterval> | undefined;\n\n const desiredConcurrency = () => 1 + lentSlots;\n\n const loop = async (workerId: string): Promise<void> => {\n try {\n while (running && handler) {\n // A loan may have been released while this loop was idle. Let excess\n // workers leave after their current job so the database queue returns\n // to its normal single-worker behavior.\n if (activeLoops > desiredConcurrency()) {\n return;\n }\n\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 // Last-resort run finalization: the dispatcher's own\n // finalization covers everything inside dispatch, but errors\n // before it (project resolution, scope setup) would otherwise\n // strand the run in a non-terminal status forever.\n if (job.kind === \"workflow\") {\n await failWorkflowRun(job.runId, error);\n }\n if (job.kind === \"agent\") {\n const sessionId = (job.payload as { sessionId?: string } | null)?.sessionId;\n if (sessionId) {\n await failAgentSession(sessionId, error);\n }\n }\n }\n }\n } catch {\n await sleep(pollIntervalMs);\n }\n }\n } finally {\n activeLoops -= 1;\n ensureWorkers();\n }\n };\n\n const ensureWorkers = (): void => {\n while (running && handler && activeLoops < desiredConcurrency()) {\n activeLoops += 1;\n const workerId = `${workerIdBase}:${activeLoops}`;\n void loop(workerId);\n }\n };\n\n return {\n async enqueue(input) {\n return enqueueJob(input);\n },\n\n async startWorker(nextHandler: JobHandler, options: WorkerOptions = {}): Promise<StopFn> {\n handler = nextHandler;\n running = true;\n workerIdBase = options.workerId ?? crypto.randomUUID();\n pollIntervalMs = options.pollIntervalMs ?? 250;\n leaseSweepIntervalMs = options.leaseSweepIntervalMs ?? 30_000;\n leaseTimer = setInterval(() => {\n void requeueExpiredLeases();\n }, leaseSweepIntervalMs);\n ensureWorkers();\n\n return async () => {\n running = false;\n clearInterval(leaseTimer);\n leaseTimer = undefined;\n };\n },\n\n async withLentSlot(fn) {\n lentSlots += 1;\n ensureWorkers();\n try {\n return await fn();\n } finally {\n lentSlots -= 1;\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 { claimDueTriggers, claimDueTriggersForOrg } from \"@keystrokehq/database\";\nimport { getProjectScopeId } from \"@keystrokehq/database\";\nimport { nextTriggerRunAt } from \"@keystrokehq/trigger\";\nimport type { EnqueueInput, JobQueue, ScheduleTickerOptions, StopFn } from \"./types\";\n\nexport type ScheduleTickerContext = {\n jobQueue: JobQueue;\n pollIntervalMs?: number;\n batchSize?: number;\n scope?: \"project\" | \"organization\";\n};\n\nfunction enqueueTriggerJob(\n jobQueue: JobQueue,\n row: { slug: string; kind: \"cron\" | \"poll\"; projectId: string },\n scheduledAt?: Date,\n): Promise<string> {\n const input: EnqueueInput = {\n kind: \"trigger\",\n targetId: row.slug,\n runId: crypto.randomUUID(),\n trigger: row.kind,\n payload: {},\n projectId: row.projectId,\n };\n\n if (scheduledAt) {\n input.scheduledAt = scheduledAt;\n }\n\n return jobQueue.enqueue(input);\n}\n\nasync function claimDue(\n scope: \"project\" | \"organization\",\n asOf: Date,\n resolveNextRunAt: (schedule: string, timezone?: string) => Date,\n limit: number,\n) {\n if (scope === \"organization\") {\n return claimDueTriggersForOrg(asOf, resolveNextRunAt, limit);\n }\n\n return claimDueTriggers(asOf, resolveNextRunAt, limit).then((rows) =>\n rows.map((row) => ({ ...row, projectId: getProjectScopeId() })),\n );\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 scope = ctx.scope ?? \"project\";\n const claimed = await claimDue(\n scope,\n asOf,\n (schedule, timezone) => nextTriggerRunAt(schedule, asOf, timezone),\n batchSize,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n // Claim uses `asOf` to decide which schedules are due, but the job must be\n // processable immediately — stamping `scheduledAt: asOf` (e.g. a far-future\n // test clock) would leave the job pending forever under `scheduledAt <= now`.\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 const scope = options.scope ?? ctx.scope ?? \"project\";\n let running = true;\n\n const loop = async () => {\n while (running) {\n try {\n const claimed = await claimDue(\n scope,\n new Date(),\n (schedule, timezone) => nextTriggerRunAt(schedule, new Date(), timezone),\n limit,\n );\n\n for (const row of claimed) {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n continue;\n }\n await enqueueTriggerJob(ctx.jobQueue, {\n slug: row.slug,\n kind: row.kind,\n projectId: row.projectId,\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 { resolveCronSchedule } from \"@keystrokehq/trigger\";\n\nexport type ScheduleOverrideOptions = {\n global?: string;\n byTrigger?: Record<string, string>;\n};\n\nexport function resolveTriggerSchedule(\n triggerSlug: string,\n schedule: string,\n overrides?: ScheduleOverrideOptions,\n): string {\n return resolveCronSchedule(triggerSlug, schedule, {\n cronScheduleOverride: overrides?.global,\n attachmentScheduleOverrides: overrides?.byTrigger,\n });\n}\n","import {\n disableAllTriggerSchedules,\n disableTriggerSchedulesNotInSlugs,\n listExecutableAttachmentsByTriggerIds,\n selectActiveEphemeralScheduledTriggerSlugs,\n selectTriggerBySlug,\n triggerTimezoneFromConfig,\n upsertTriggerScheduleFields,\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.triggerSlug);\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 selectActiveEphemeralScheduledTriggerSlugs();\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.triggerSlug,\n spec.schedule,\n options.scheduleOverrides,\n );\n const existing = await selectTriggerBySlug(spec.triggerSlug);\n if (!existing) {\n continue;\n }\n\n const existingTimezone = triggerTimezoneFromConfig(existing.config);\n const scheduleChanged = existing.schedule !== schedule || existingTimezone !== spec.timezone;\n const nextRunAt =\n !scheduleChanged && existing.enabled === 1\n ? existing.nextRunAt\n : nextTriggerRunAt(schedule, now, spec.timezone);\n\n const executable = await listExecutableAttachmentsByTriggerIds([existing.id]);\n const enabled = executable.length > 0;\n\n await upsertTriggerScheduleFields({\n triggerSlug: spec.triggerSlug,\n schedule,\n nextRunAt: nextRunAt ?? now,\n enabled,\n updatedAt: now,\n });\n }\n}\n","import { setTriggerScheduleEnabled } from \"@keystrokehq/database\";\n\nimport { createScheduleTicker } from \"./schedule-ticker\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\nimport type {\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { JobQueue } from \"./types\";\n\nexport type DatabaseTriggerSchedulerOptions = {\n jobQueue: JobQueue;\n scope?: \"project\" | \"organization\";\n pollIntervalMs?: number;\n batchSize?: number;\n};\n\n/** Postgres poll loop — default trigger scheduler for pg-boss, polling, and memory paths. */\nexport function createDatabaseTriggerScheduler(\n options: DatabaseTriggerSchedulerOptions,\n): TriggerScheduler {\n const ticker = createScheduleTicker({\n jobQueue: options.jobQueue,\n scope: options.scope,\n pollIntervalMs: options.pollIntervalMs,\n batchSize: options.batchSize,\n });\n\n return {\n sync: (syncOptions: TriggerScheduleSyncOptions) => syncTriggerSchedules(syncOptions),\n start: (startOptions?: TriggerSchedulerStartOptions) =>\n ticker.startScheduleTicker(startOptions),\n fireDue: (asOf?: Date) => ticker.fireDueSchedules(asOf),\n async remove(triggerSlug: string) {\n await setTriggerScheduleEnabled(triggerSlug, false, new Date());\n },\n async upsert(_spec: TriggerScheduleSpec) {\n // DB rows are the firing source of truth; callers update triggers before upsert.\n },\n };\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(\n scope: \"platform\" | \"project\" | \"organization\",\n id?: string,\n): string {\n if (scope === \"platform\") {\n return \"keystroke_cancel_platform\";\n }\n\n if (scope === \"organization\") {\n const sanitized = (id ?? \"default\").replace(/[^a-z0-9_]/gi, \"_\").toLowerCase();\n return `keystroke_cancel_org_${sanitized}`;\n }\n\n const sanitized = (id ?? \"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, JobParent, 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 function pgBossOrgQueueName(organizationId: string): string {\n const sanitized = organizationId.replace(/[^a-zA-Z0-9_]/g, \"_\");\n const name = `${DEFAULT_QUEUE_NAME}_org_${sanitized}`;\n if (name.length <= 50) {\n return name;\n }\n return `${DEFAULT_QUEUE_NAME}_org_${createHash(\"sha1\").update(organizationId).digest(\"hex\").slice(0, 36)}`;\n}\n\nexport type PgBossQueueOptions = {\n connectionString: string;\n queueName?: string;\n projectId?: string;\n organizationId?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\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 projectId?: string;\n parent?: JobParent;\n};\n\ntype BuildPgBossJobQueueOptions = {\n connectionString: string;\n stopBossOnWorkerStop?: boolean;\n queueName?: string;\n cancelScope?: \"platform\" | \"project\" | \"organization\";\n projectId?: string;\n organizationId?: string;\n};\n\n/**\n * A {@link JobQueue} that also exposes its underlying pg-boss instance and queue\n * name so the trigger scheduler can register native cron schedules\n * (`boss.schedule`) against the same queue the worker drains.\n */\nexport type PgBossJobQueue = JobQueue & {\n boss: PgBoss;\n queueName: string;\n};\n\nexport function buildPgBossJobQueue(\n boss: PgBoss,\n options: BuildPgBossJobQueueOptions,\n): PgBossJobQueue {\n const queueName = options.queueName ?? DEFAULT_QUEUE_NAME;\n const cancelChannel = createPgCancelChannel(\n options.connectionString,\n pgCancelChannelName(\n options.cancelScope ?? \"platform\",\n options.cancelScope === \"organization\" ? options.organizationId : options.projectId,\n ),\n );\n\n return {\n boss,\n queueName,\n async enqueue(input) {\n const jobId = await boss.send(\n queueName,\n {\n ...input,\n payload: input.payload,\n },\n {\n retryLimit: (input.maxAttempts ?? 3) - 1,\n retryDelay: Math.ceil(retryDelayMs(1) / 1000),\n startAfter: input.scheduledAt,\n singletonKey: input.dedupeKey,\n },\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 projectId: data.projectId,\n parent: data.parent,\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<PgBossJobQueue> {\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: options.cancelScope ?? \"project\",\n projectId: options.projectId,\n organizationId: options.organizationId,\n });\n}\n\nexport async function createSharedPgBossJobQueue(\n boss: PgBoss,\n options: { connectionString: string; queueName?: string },\n): Promise<PgBossJobQueue> {\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 getProjectScopeId,\n listEnabledScheduledTriggers,\n triggerTimezoneFromConfig,\n type StoredTriggerConfig,\n} from \"@keystrokehq/database\";\n\nimport type {\n SchedulerPluginContext,\n TriggerScheduleSpec,\n TriggerScheduleSyncOptions,\n TriggerScheduler,\n TriggerSchedulerStartOptions,\n} from \"./contract\";\nimport type { PgBossJobQueue } from \"./pg-boss-queue\";\nimport { resolveTriggerSchedule } from \"./resolve-schedule\";\nimport { syncTriggerSchedules } from \"./sync-trigger-schedules\";\n\n/**\n * pg-boss schedule key for one trigger. Mirrors {@link bullmqTriggerSchedulerId}\n * so the firing backend is interchangeable: `trigger-<projectId>-<slug>`. pg-boss\n * keys allow hyphens, so colons (legal in ids) are swapped for `-`.\n */\nexport function pgBossTriggerScheduleKey(\n spec: Pick<TriggerScheduleSpec, \"triggerSlug\" | \"projectId\">,\n): string {\n const projectId = (spec.projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n const slug = spec.triggerSlug.replaceAll(\":\", \"-\");\n return `trigger-${projectId}-${slug}`;\n}\n\n/** Key prefix scoping reconcile cleanup to the active project (org queues are shared). */\nexport function pgBossTriggerScheduleKeyPrefix(projectId?: string): string {\n const scopedProjectId = (projectId ?? getProjectScopeId()).replaceAll(\":\", \"-\");\n return `trigger-${scopedProjectId}-`;\n}\n\nfunction triggerJobData(spec: TriggerScheduleSpec): {\n kind: \"trigger\";\n targetId: string;\n runId: string;\n trigger: \"cron\" | \"poll\";\n payload: Record<string, never>;\n projectId: string;\n} {\n return {\n kind: \"trigger\",\n targetId: spec.triggerSlug,\n // Empty: the worker fans out to attachment runs, each with its own run id.\n runId: \"\",\n trigger: spec.kind,\n payload: {},\n projectId: spec.projectId ?? getProjectScopeId(),\n };\n}\n\nfunction rowToSpec(row: {\n slug: string;\n kind: string;\n schedule: string | null;\n projectId: string;\n config?: StoredTriggerConfig | null;\n}): TriggerScheduleSpec | undefined {\n if (row.kind !== \"cron\" && row.kind !== \"poll\") {\n return undefined;\n }\n\n if (!row.schedule) {\n return undefined;\n }\n\n const timezone = triggerTimezoneFromConfig(row.config ?? null);\n\n return {\n triggerSlug: row.slug,\n kind: row.kind,\n schedule: row.schedule,\n ...(timezone ? { timezone } : {}),\n projectId: row.projectId,\n };\n}\n\n/**\n * Trigger scheduler backed by pg-boss native cron scheduling. Mirrors\n * {@link createBullmqTriggerScheduler}: `boss.schedule` registers a recurring job\n * onto the project/org queue (no per-second DB poll), and reconcile keeps the\n * `pgboss.schedule` table in sync with the `triggers` table (source of truth).\n *\n * Granularity: pg-boss cron is minute-level (it checks schedules every ~30s and\n * dedupes per minute), so sub-minute schedules effectively fire at most once per\n * minute — unlike the DB ticker's 1s loop.\n */\nexport function createPgBossTriggerScheduler(\n _ctx: SchedulerPluginContext,\n queue: PgBossJobQueue,\n): TriggerScheduler {\n async function upsertScheduler(\n spec: TriggerScheduleSpec,\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const schedule = resolveTriggerSchedule(spec.triggerSlug, spec.schedule, overrides);\n const key = pgBossTriggerScheduleKey(spec);\n await queue.boss.schedule(queue.queueName, schedule, triggerJobData({ ...spec, schedule }), {\n key,\n ...(spec.timezone ? { tz: spec.timezone } : {}),\n });\n }\n\n async function reconcileFromDatabase(\n overrides?: TriggerScheduleSyncOptions[\"scheduleOverrides\"],\n ): Promise<void> {\n const projectPrefix = pgBossTriggerScheduleKeyPrefix();\n const rows = await listEnabledScheduledTriggers();\n const expectedKeys = new Set<string>();\n const failures: Array<{ triggerSlug: string; error: unknown }> = [];\n\n for (const row of rows) {\n const spec = rowToSpec(row);\n if (!spec) {\n continue;\n }\n\n expectedKeys.add(pgBossTriggerScheduleKey(spec));\n try {\n await upsertScheduler(spec, overrides);\n } catch (error) {\n failures.push({ triggerSlug: spec.triggerSlug, error });\n console.error(`[pg-boss-trigger-scheduler] failed to schedule ${spec.triggerSlug}`, error);\n }\n }\n\n const schedules = await queue.boss.getSchedules(queue.queueName);\n for (const schedule of schedules) {\n const key = schedule.key;\n if (!key || !key.startsWith(projectPrefix)) {\n continue;\n }\n\n if (!expectedKeys.has(key)) {\n try {\n await queue.boss.unschedule(queue.queueName, key);\n } catch (error) {\n failures.push({ triggerSlug: key, error });\n console.error(`[pg-boss-trigger-scheduler] failed to unschedule stale ${key}`, error);\n }\n }\n }\n\n if (failures.length > 0) {\n throw new Error(\n `pg-boss trigger scheduler reconcile failed for ${failures.length} trigger(s)`,\n );\n }\n }\n\n return {\n async sync(syncOptions: TriggerScheduleSyncOptions) {\n await syncTriggerSchedules(syncOptions);\n await reconcileFromDatabase(syncOptions.scheduleOverrides);\n },\n\n async start(_options?: TriggerSchedulerStartOptions) {\n // pg-boss fires schedules from its own timekeeper; no DB poll loop to run.\n return async () => undefined;\n },\n\n fireDue: async () => 0,\n\n upsert: upsertScheduler,\n\n async remove(triggerSlug: string, projectId?: string) {\n await queue.boss.unschedule(\n queue.queueName,\n pgBossTriggerScheduleKey({ triggerSlug, projectId }),\n );\n },\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 type TriggerScheduler,\n} from \"./contract\";\nimport { createDatabaseJobQueue } from \"./database-queue\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport { getPgBoss, startPgBoss } from \"./pg-boss-client\";\nimport {\n createPgBossQueue,\n createSharedPgBossJobQueue,\n type PgBossJobQueue,\n pgBossOrgQueueName,\n pgBossProjectQueueName,\n} from \"./pg-boss-queue\";\nimport { createPgBossTriggerScheduler } from \"./pg-boss-trigger-scheduler\";\nimport type { JobQueue } from \"./types\";\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/** DB poll-loop trigger scheduler — used by the SQLite/self-host polling backend. */\nfunction createPollingTriggerScheduler(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = ctx.scope === \"organization\" ? \"organization\" : \"project\";\n return Promise.resolve(createDatabaseTriggerScheduler({ jobQueue: queue, scope }));\n}\n\nfunction isPgBossJobQueue(queue: JobQueue): queue is PgBossJobQueue {\n return \"boss\" in queue && \"queueName\" in queue;\n}\n\n/** Native pg-boss cron scheduler; falls back to the DB ticker if the queue isn't pg-boss. */\nfunction createPgBossTriggerSchedulerForPlugin(\n ctx: SchedulerPluginContext,\n queue: JobQueue,\n): Promise<TriggerScheduler> {\n if (!isPgBossJobQueue(queue)) {\n return createPollingTriggerScheduler(ctx, queue);\n }\n return Promise.resolve(createPgBossTriggerScheduler(ctx, queue));\n}\n\n/** Postgres pg-boss backend. Platform scope shares one queue; org scope gets one queue per org. */\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 if (ctx.scope === \"organization\") {\n const organizationId = ctx.organizationId;\n if (!organizationId) {\n throw new Error(\"organizationId is required for organization-scoped pg-boss queue\");\n }\n\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossOrgQueueName(organizationId),\n cancelScope: \"organization\",\n organizationId,\n });\n }\n\n const projectId = ctx.projectId ?? getProjectScopeId();\n return createPgBossQueue({\n connectionString: url,\n queueName: pgBossProjectQueueName(projectId),\n projectId,\n });\n },\n createTriggerScheduler: createPgBossTriggerSchedulerForPlugin,\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 createTriggerScheduler: createPollingTriggerScheduler,\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 async createTriggerScheduler(ctx, queue) {\n const url = resolveUrl(ctx);\n const dialect = inferDialect(url, ctx.dialect);\n\n if (dialect === \"postgres\") {\n return pgBossSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n }\n\n return pollingSchedulerPlugin().createTriggerScheduler!(ctx, queue);\n },\n });\n}\n","import { defaultSchedulerPlugin } from \"./plugin\";\nimport { createDatabaseTriggerScheduler } from \"./database-trigger-scheduler\";\nimport type { SchedulerPluginContext, TriggerScheduler } from \"./contract\";\nimport type {\n CreateJobQueueOptions,\n CreateSchedulerOptions,\n JobQueue,\n ScheduleSyncOptions,\n ScheduleTickerOptions,\n Scheduler,\n StopFn,\n TriggerScheduleSpec,\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 pluginContext(options: CreateSchedulerOptions = {}): SchedulerPluginContext {\n return {\n scope: options.scope ?? \"project\",\n url: options.url,\n dialect: options.dialect,\n projectId: options.projectId,\n organizationId: options.organizationId,\n };\n}\n\nasync function createTriggerScheduler(\n options: CreateSchedulerOptions,\n jobQueue: JobQueue,\n): Promise<TriggerScheduler> {\n const scope = options.scope === \"organization\" ? \"organization\" : \"project\";\n const plugin = options.plugin ?? defaultSchedulerPlugin();\n const ctx = pluginContext(options);\n\n if (plugin.createTriggerScheduler) {\n return plugin.createTriggerScheduler(ctx, jobQueue);\n }\n\n return createDatabaseTriggerScheduler({ jobQueue, scope });\n}\n\nfunction wrapScheduler(jobQueue: JobQueue, triggerScheduler: TriggerScheduler): Scheduler {\n return {\n enqueue: (input) => jobQueue.enqueue(input),\n startWorker: (handler, workerOptions) => jobQueue.startWorker(handler, workerOptions),\n ...(jobQueue.withLentSlot\n ? { withLentSlot: <T>(fn: () => Promise<T>) => jobQueue.withLentSlot!(fn) }\n : {}),\n publishCancel: (runId) => jobQueue.publishCancel(runId),\n subscribeCancel: (handler) => jobQueue.subscribeCancel(handler),\n syncTriggerSchedules: (options: ScheduleSyncOptions) => triggerScheduler.sync(options),\n startScheduleTicker: (options?: ScheduleTickerOptions) => triggerScheduler.start(options),\n fireDueSchedules: (asOf?: Date) => triggerScheduler.fireDue?.(asOf) ?? Promise.resolve(0),\n ...(triggerScheduler.upsert\n ? {\n upsertTriggerSchedule: (\n spec: TriggerScheduleSpec,\n overrides?: ScheduleSyncOptions[\"scheduleOverrides\"],\n ) => triggerScheduler.upsert!(spec, overrides),\n }\n : {}),\n ...(triggerScheduler.remove\n ? {\n removeTriggerSchedule: (triggerSlug: string, projectId?: string) =>\n triggerScheduler.remove!(triggerSlug, projectId),\n }\n : {}),\n };\n}\n\nexport async function createScheduler(options: CreateSchedulerOptions = {}): Promise<Scheduler> {\n const jobQueue = await createUnderlyingJobQueue(options);\n const triggerScheduler = await createTriggerScheduler(options, jobQueue);\n return wrapScheduler(jobQueue, triggerScheduler);\n}\n\nexport async function createJobQueue(options: CreateJobQueueOptions = {}): Promise<JobQueue> {\n return createUnderlyingJobQueue(options);\n}\n\nexport async function wrapJobQueueAsScheduler(\n jobQueue: JobQueue,\n scope?: \"project\" | \"organization\",\n): Promise<Scheduler> {\n const triggerScheduler = createDatabaseTriggerScheduler({\n jobQueue,\n scope,\n });\n return wrapScheduler(jobQueue, triggerScheduler);\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 ...(input.parent ? { parent: input.parent } : {}),\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 await 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 await wrapJobQueueAsScheduler(sharedJobQueue);\n}\n\nexport function resetSharedSchedulerForTests(): void {\n configuredPlugin = undefined;\n sharedJobQueue = undefined;\n resetSharedPgBossJobQueueForTests();\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.1.59",
3
+ "version": "0.1.61",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,16 +41,16 @@
41
41
  "tsdown": "^0.22.0",
42
42
  "typescript": "npm:@typescript/typescript6@^6.0.2",
43
43
  "vitest": "^4.1.7",
44
- "@keystrokehq/database": "0.0.169",
44
+ "@keystrokehq/database": "0.0.171",
45
45
  "@keystrokehq/oxlint-config": "0.0.4",
46
- "@keystrokehq/trigger": "0.0.209",
46
+ "@keystrokehq/trigger": "0.0.211",
47
47
  "@keystrokehq/tsconfig": "0.0.3",
48
48
  "@keystrokehq/tsdown-config": "0.0.5",
49
49
  "@keystrokehq/vitest-config": "0.0.7"
50
50
  },
51
51
  "peerDependencies": {
52
- "@keystrokehq/database": "0.0.169",
53
- "@keystrokehq/trigger": "0.0.209"
52
+ "@keystrokehq/database": "0.0.171",
53
+ "@keystrokehq/trigger": "0.0.211"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "@keystrokehq/database": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract-GEHKV_QB.d.cts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract-GEHKV_QB.d.mts","names":[],"sources":["../src/types.ts","../src/contract.ts"],"mappings":"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"}