@spfn/core 0.3.0-beta.6 → 0.3.0-beta.8

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,"sources":["../../src/job/boss.ts","../../src/job/job-builder.ts","../../src/job/job-router.ts","../../src/job/register-jobs.ts"],"names":["jobLogger","logger","job"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * pg-boss Wrapper\n *\n * Manages pg-boss instance lifecycle\n */\n\nimport PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Check if connection string uses SSL without certificate verification\n *\n * pg library verifies certificates by default even with sslmode=require.\n * For require/prefer modes (no explicit verification), we disable cert checking\n * to support self-signed certificates.\n */\nfunction requiresSSLWithoutVerification(connectionString: string): boolean\n{\n try\n {\n const url = new URL(connectionString);\n const sslmode = url.searchParams.get('sslmode');\n\n return sslmode === 'require' || sslmode === 'prefer';\n }\n catch\n {\n return false;\n }\n}\n\n/**\n * Remove sslmode parameter from connection string URL\n *\n * pg driver interprets sslmode=require as verify-full, which overrides\n * the ssl option object. Stripping it lets us control SSL via the ssl option only.\n */\nfunction stripSslModeFromUrl(connectionString: string): string\n{\n const url = new URL(connectionString);\n url.searchParams.delete('sslmode');\n\n return url.toString();\n}\n\n/**\n * globalThis keys for cross-module-cache singleton (ESM/CJS share same instance)\n */\nconst BOSS_KEY = Symbol.for('spfn:boss-instance');\nconst CONFIG_KEY = Symbol.for('spfn:boss-config');\n\nconst g = globalThis as any;\n\nfunction getBossInstance(): PgBoss | null\n{\n return g[BOSS_KEY] ?? null;\n}\n\nfunction setBossInstance(instance: PgBoss | null): void\n{\n g[BOSS_KEY] = instance;\n}\n\nfunction getBossConfig(): BossConfig | null\n{\n return g[CONFIG_KEY] ?? null;\n}\n\nfunction setBossConfig(config: BossConfig | null): void\n{\n g[CONFIG_KEY] = config;\n}\n\n/**\n * Options for pg-boss initialization\n *\n * @example\n * ```typescript\n * await initBoss({\n * connectionString: process.env.DATABASE_URL,\n * schema: 'spfn_queue',\n * clearOnStart: process.env.NODE_ENV === 'development',\n * });\n * ```\n */\nexport interface BossOptions\n{\n /**\n * PostgreSQL connection string\n *\n * @example 'postgresql://user:password@localhost:5432/mydb'\n */\n connectionString: string;\n\n /**\n * Schema name for pg-boss tables\n *\n * pg-boss creates its own tables in this schema.\n *\n * @default 'spfn_queue'\n */\n schema?: string;\n\n /**\n * Maintenance interval in seconds\n *\n * pg-boss runs maintenance tasks (cleanup, archiving) at this interval.\n *\n * @default 120\n */\n maintenanceIntervalSeconds?: number;\n\n /**\n * Monitor state changes interval in seconds\n *\n * When set, pg-boss emits state change events at this interval.\n *\n * @default undefined (disabled)\n */\n monitorIntervalSeconds?: number;\n\n /**\n * Clear all pending/scheduled jobs on startup\n *\n * Useful for development mode to start with a clean queue.\n * Should be false in production.\n *\n * @default false\n */\n clearOnStart?: boolean;\n\n /**\n * Unschedule cron schedules that are no longer declared on any registered\n * job router, once at startup after job registration. Queues and job rows\n * are never deleted — only the schedule row is removed.\n *\n * Leave disabled when multiple apps share the same pg-boss schema, when\n * schedules are created directly via getBoss(), or during rolling deploys\n * that mix router versions — the sweep only knows the routers registered\n * in this process and would unschedule everything else.\n *\n * @default false\n */\n sweepOrphanSchedules?: boolean;\n}\n\n/**\n * @deprecated Use BossOptions instead\n */\nexport type BossConfig = BossOptions;\n\n/**\n * Initialize pg-boss with the given configuration\n *\n * Must be called before registerJobs(). Typically handled by defineServerConfig().\n *\n * @param options - pg-boss configuration options\n * @returns The pg-boss instance\n *\n * @example\n * ```typescript\n * const boss = await initBoss({\n * connectionString: process.env.DATABASE_URL!,\n * schema: 'spfn_queue',\n * });\n * ```\n */\nexport async function initBoss(options: BossOptions): Promise<PgBoss>\n{\n const existing = getBossInstance();\n if (existing)\n {\n jobLogger.warn('pg-boss already initialized, returning existing instance');\n\n return existing;\n }\n\n jobLogger.info('Initializing pg-boss...');\n\n setBossConfig(options);\n\n const needsSSL = requiresSSLWithoutVerification(options.connectionString);\n\n const pgBossOptions: PgBoss.ConstructorOptions = {\n // pg 드라이버가 URL의 sslmode=require를 verify-full로 해석해서\n // ssl 옵션을 무시하므로, URL에서 sslmode를 빼고 ssl 객체만 전달\n connectionString: needsSSL\n ? stripSslModeFromUrl(options.connectionString)\n : options.connectionString,\n schema: options.schema ?? 'spfn_queue',\n maintenanceIntervalSeconds: options.maintenanceIntervalSeconds ?? 120,\n };\n\n if (needsSSL)\n {\n pgBossOptions.ssl = { rejectUnauthorized: false };\n }\n\n // Only set monitorIntervalSeconds if explicitly provided (must be >= 1)\n if (options.monitorIntervalSeconds !== undefined && options.monitorIntervalSeconds >= 1)\n {\n pgBossOptions.monitorIntervalSeconds = options.monitorIntervalSeconds;\n }\n\n const boss = new PgBoss(pgBossOptions);\n\n // Event handlers\n boss.on('error', (error) =>\n {\n jobLogger.error('pg-boss error:', error);\n });\n\n await boss.start();\n\n setBossInstance(boss);\n\n jobLogger.info('pg-boss started successfully');\n\n return boss;\n}\n\n/**\n * Get the current pg-boss instance\n */\nexport function getBoss(): PgBoss | null\n{\n return getBossInstance();\n}\n\n/**\n * Stop pg-boss gracefully\n */\nexport async function stopBoss(): Promise<void>\n{\n const boss = getBossInstance();\n if (!boss)\n {\n return;\n }\n\n jobLogger.info('Stopping pg-boss...');\n\n try\n {\n await boss.stop({ graceful: true, timeout: 30000 });\n jobLogger.info('pg-boss stopped gracefully');\n }\n catch (error)\n {\n jobLogger.error('Error stopping pg-boss:', error);\n throw error;\n }\n finally\n {\n setBossInstance(null);\n setBossConfig(null);\n }\n}\n\n/**\n * Check if pg-boss is initialized and running\n */\nexport function isBossRunning(): boolean\n{\n return getBossInstance() !== null;\n}\n\n/**\n * Check if jobs should be cleared on start\n */\nexport function shouldClearOnStart(): boolean\n{\n return getBossConfig()?.clearOnStart ?? false;\n}\n\n/**\n * Check if orphan cron schedules should be swept after registration\n */\nexport function shouldSweepOrphanSchedules(): boolean\n{\n return getBossConfig()?.sweepOrphanSchedules ?? false;\n}\n","/**\n * Job Builder\n *\n * Fluent API for defining jobs, similar to route builder pattern\n */\n\nimport type { Static, TSchema } from '@sinclair/typebox';\nimport type { CompensateHandler, JobDef, JobHandler, JobOptions, JobSendOptions } from './types';\nimport type { EventDef, InferEventPayload } from '@spfn/core/event';\nimport { getBoss } from './boss';\n\n/**\n * Build pg-boss options from job defaults and send options\n */\nfunction buildPgBossOptions(\n defaults?: JobOptions,\n sendOptions?: JobSendOptions,\n): Record<string, unknown>\n{\n const options: Record<string, unknown> = {};\n\n // Send options (per-job invocation)\n if (sendOptions?.startAfter)\n {\n options.startAfter = sendOptions.startAfter;\n }\n if (sendOptions?.singletonKey)\n {\n options.singletonKey = sendOptions.singletonKey;\n }\n if (sendOptions?.priority !== undefined)\n {\n options.priority = sendOptions.priority;\n }\n\n // Default options (from job definition)\n if (defaults?.retryLimit !== undefined)\n {\n options.retryLimit = defaults.retryLimit;\n }\n if (defaults?.retryDelay !== undefined)\n {\n options.retryDelay = defaults.retryDelay;\n }\n if (defaults?.expireInSeconds !== undefined)\n {\n options.expireInSeconds = defaults.expireInSeconds;\n }\n if (defaults?.priority !== undefined && sendOptions?.priority === undefined)\n {\n options.priority = defaults.priority;\n }\n if (defaults?.singletonKey && !sendOptions?.singletonKey)\n {\n options.singletonKey = defaults.singletonKey;\n }\n if (defaults?.retentionSeconds !== undefined)\n {\n options.retentionSeconds = defaults.retentionSeconds;\n }\n\n return options;\n}\n\n/**\n * Job builder class with fluent API\n */\nexport class JobBuilder<TInput = void, TOutput = void>\n{\n private readonly _name: string;\n private _inputSchema?: TSchema;\n private _outputSchema?: TSchema;\n private _cronExpression?: string;\n private _runOnce?: boolean;\n private _subscribedEvent?: string;\n private _subscribedEventDef?: EventDef<any>;\n private _options?: JobOptions;\n private _handler?: JobHandler<TInput, TOutput>;\n private _compensate?: CompensateHandler<TInput, TOutput>;\n\n constructor(name: string)\n {\n this._name = name;\n }\n\n /**\n * Define input schema with TypeBox\n */\n input<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<Static<TSchema>, TOutput>\n {\n const builder = new JobBuilder<Static<TSchema>, TOutput>(this._name);\n builder._inputSchema = schema;\n builder._outputSchema = this._outputSchema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Define output schema with TypeBox (for workflow integration)\n */\n output<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<TInput, Static<TSchema>>\n {\n const builder = new JobBuilder<TInput, Static<TSchema>>(this._name);\n builder._inputSchema = this._inputSchema;\n builder._outputSchema = schema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Subscribe to an event (decoupled triggering)\n *\n * @example\n * ```typescript\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * const sendWelcomeEmail = job('send-welcome-email')\n * .on(userCreated)\n * .handler(async (payload) => {\n * // payload is typed as { userId: string }\n * });\n * ```\n */\n on<TEvent extends EventDef<any>>(\n event: TEvent,\n ): JobBuilder<InferEventPayload<TEvent>, TOutput>\n {\n const builder = new JobBuilder<InferEventPayload<TEvent>, TOutput>(this._name);\n builder._inputSchema = event.schema;\n builder._outputSchema = this._outputSchema;\n builder._subscribedEvent = event.name;\n builder._subscribedEventDef = event;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Set cron expression for scheduled execution\n */\n cron(expression: string): this\n {\n this._cronExpression = expression;\n\n return this;\n }\n\n /**\n * Mark job to run once on server start\n */\n runOnce(): this\n {\n this._runOnce = true;\n\n return this;\n }\n\n /**\n * Set job options (retry, expiration, etc.)\n */\n options(options: JobOptions): this\n {\n this._options = options;\n\n return this;\n }\n\n /**\n * Set job timeout in milliseconds\n * (Converts to expireInSeconds for pg-boss)\n */\n timeout(ms: number): this\n {\n this._options = {\n ...this._options,\n expireInSeconds: Math.ceil(ms / 1000),\n };\n\n return this;\n }\n\n /**\n * Define compensate handler for rollback (workflow integration)\n */\n compensate(fn: CompensateHandler<TInput, TOutput>): this\n {\n this._compensate = fn;\n\n return this;\n }\n\n /**\n * Define the job handler and finalize the job definition\n */\n handler(fn: JobHandler<TInput, TOutput>): JobDef<TInput, TOutput>\n {\n this._handler = fn;\n\n const name = this._name;\n const inputSchema = this._inputSchema;\n const outputSchema = this._outputSchema;\n const cronExpression = this._cronExpression;\n const runOnce = this._runOnce;\n const subscribedEvent = this._subscribedEvent;\n const subscribedEventDef = this._subscribedEventDef;\n const options = this._options;\n const handler = this._handler;\n const compensate = this._compensate;\n\n // Create send function\n const send = async (\n inputOrOptions?: TInput | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<string | null> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n // Determine input and options based on whether job has input schema\n const [input, sendOptions] = inputSchema\n ? [inputOrOptions as TInput, maybeOptions]\n : [undefined, inputOrOptions as JobSendOptions | undefined];\n\n return await boss.send(\n name,\n input ?? {},\n buildPgBossOptions(options, sendOptions),\n );\n };\n\n // Create run function (synchronous execution)\n const run = async (input?: TInput): Promise<TOutput> =>\n {\n if (inputSchema)\n {\n return await (handler as (input: TInput) => Promise<TOutput>)(input as TInput);\n }\n else\n {\n return await (handler as () => Promise<TOutput>)();\n }\n };\n\n // Create sendBatch function (bulk insert via pg-boss)\n const sendBatch = async (\n inputsOrOptions?: TInput[] | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<void> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n const [inputs, sendOptions] = inputSchema\n ? [inputsOrOptions as TInput[], maybeOptions]\n : [undefined, inputsOrOptions as JobSendOptions | undefined];\n\n const pgBossOptions = buildPgBossOptions(options, sendOptions);\n\n const jobs = (inputs ?? [{}]).map((data) => ({\n name,\n ...pgBossOptions,\n data: data as object,\n }));\n\n await boss.insert(name, jobs);\n };\n\n return {\n name,\n inputSchema,\n outputSchema,\n cronExpression,\n runOnce,\n subscribedEvent,\n _subscribedEventDef: subscribedEventDef,\n options,\n handler,\n compensate,\n send: send as JobDef<TInput, TOutput>['send'],\n sendBatch: sendBatch as JobDef<TInput, TOutput>['sendBatch'],\n run: run as JobDef<TInput, TOutput>['run'],\n _input: undefined as unknown as TInput,\n _output: undefined as unknown as TOutput,\n };\n }\n}\n\n/**\n * Create a new job definition\n *\n * @example\n * ```typescript\n * // Simple job without input\n * export const cleanupJob = job('cleanup')\n * .handler(async () => {\n * await db.cleanup();\n * });\n *\n * // Job with typed input\n * export const sendEmailJob = job('send-email')\n * .input(Type.Object({\n * to: Type.String(),\n * subject: Type.String(),\n * body: Type.String(),\n * }))\n * .handler(async (input) => {\n * await emailService.send(input.to, input.subject, input.body);\n * });\n *\n * // Cron job\n * export const dailyReportJob = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => {\n * await reportService.generateDaily();\n * });\n *\n * // Run once on server start\n * export const initCacheJob = job('init-cache')\n * .runOnce()\n * .handler(async () => {\n * await cache.warmup();\n * });\n *\n * // With options\n * export const importantJob = job('important-task')\n * .input(Type.Object({ id: Type.String() }))\n * .options({\n * retryLimit: 5,\n * retryDelay: 5000,\n * priority: 10,\n * })\n * .handler(async (input) => {\n * await processImportant(input.id);\n * });\n * ```\n */\nexport function job(name: string): JobBuilder<void>\n{\n return new JobBuilder(name);\n}\n","/**\n * Job Router\n *\n * Groups job definitions for registration with the server\n */\n\nimport type { JobDef, JobRouter, JobRouterEntry } from './types';\n\n/**\n * Type guard to check if value is a JobDef\n */\nexport function isJobDef(value: unknown): value is JobDef<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n 'send' in value &&\n 'run' in value\n );\n}\n\n/**\n * Type guard to check if value is a JobRouter\n */\nexport function isJobRouter(value: unknown): value is JobRouter<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'jobs' in value &&\n '_jobs' in value\n );\n}\n\n/**\n * Define a job router to group jobs together\n *\n * @example\n * ```typescript\n * // Flat structure\n * export const jobRouter = defineJobRouter({\n * sendWelcomeEmail,\n * dailyReport,\n * initCache,\n * });\n *\n * // Nested structure\n * export const jobRouter = defineJobRouter({\n * email: defineJobRouter({\n * sendWelcome: sendWelcomeEmailJob,\n * sendReset: sendResetPasswordJob,\n * }),\n * reports: defineJobRouter({\n * daily: dailyReportJob,\n * weekly: weeklyReportJob,\n * }),\n * });\n *\n * // Mixed\n * export const jobRouter = defineJobRouter({\n * initCache, // flat\n * email: defineJobRouter({ ... }), // nested\n * });\n * ```\n */\nexport function defineJobRouter<\n TJobs extends Record<string, JobRouterEntry>,\n>(jobs: TJobs): JobRouter<TJobs>\n{\n return {\n jobs,\n _jobs: jobs,\n };\n}\n\n/**\n * Collect all JobDefs from a JobRouter (including nested)\n */\nexport function collectJobs(\n router: JobRouter<any>,\n prefix = '',\n): JobDef<any>[]\n{\n const jobs: JobDef<any>[] = [];\n\n for (const [key, value] of Object.entries(router.jobs))\n {\n const name = prefix ? `${prefix}.${key}` : key;\n\n if (isJobRouter(value))\n {\n // Nested router - recurse\n jobs.push(...collectJobs(value, name));\n }\n else if (isJobDef(value))\n {\n jobs.push(value);\n }\n }\n\n return jobs;\n}\n","/**\n * Job Registration\n *\n * Registers jobs with pg-boss\n */\n\nimport type PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\nimport { env } from '@spfn/core/config';\nimport type { JobDef, JobOptions, JobRouter } from './types';\nimport type { EventDef } from '@spfn/core/event';\nimport { collectJobs } from './job-router';\nimport { getBoss, shouldClearOnStart, shouldSweepOrphanSchedules } from './boss';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Get the pg-boss queue name for an event\n */\nexport function getEventQueueName(eventName: string): string\n{\n return `event:${eventName}`;\n}\n\n/**\n * Build default pg-boss options for a job\n */\nfunction getDefaultJobOptions(options?: JobOptions): PgBoss.SendOptions\n{\n return {\n retryLimit: options?.retryLimit ?? 3,\n retryDelay: options?.retryDelay ?? 1000,\n // Exponential backoff by default — a failed cohort (e.g. a provider 429)\n // would otherwise all retry at the same fixed offset (thundering herd).\n retryBackoff: options?.retryBackoff ?? true,\n expireInSeconds: options?.expireInSeconds ?? 300,\n };\n}\n\n/**\n * Register all jobs from a JobRouter with pg-boss\n *\n * This function:\n * 1. Collects all jobs from the router (including nested routers)\n * 2. Optionally clears existing jobs (if clearOnStart is enabled)\n * 3. Registers each job's worker handler with pg-boss\n * 4. Sets up cron schedules for scheduled jobs\n * 5. Queues runOnce jobs\n * 6. Connects event subscriptions to job queues\n *\n * @param router - JobRouter containing job definitions\n *\n * @example\n * ```typescript\n * // Define jobs\n * const sendEmail = job('send-email')\n * .input(Type.Object({ to: Type.String() }))\n * .handler(async (input) => { ... });\n *\n * const dailyReport = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => { ... });\n *\n * // Create router\n * const jobRouter = defineJobRouter({ sendEmail, dailyReport });\n *\n * // Initialize pg-boss first\n * await initBoss({ connectionString: process.env.DATABASE_URL! });\n *\n * // Register jobs\n * await registerJobs(jobRouter);\n * ```\n */\nexport async function registerJobs(router: JobRouter<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n 'pg-boss not initialized. Call initBoss() before registerJobs()',\n );\n }\n\n const jobs = collectJobs(router);\n const clearOnStart = shouldClearOnStart();\n\n jobLogger.info(`Registering ${jobs.length} job(s)...`);\n\n // Clear existing jobs if requested (useful for development)\n if (clearOnStart)\n {\n jobLogger.info('Clearing existing jobs before registration...');\n for (const job of jobs)\n {\n // Clear job queue\n await boss.deleteAllJobs(job.name);\n\n // Also clear event queue if subscribed\n if (job.subscribedEvent)\n {\n const eventQueue = getEventQueueName(job.subscribedEvent);\n await boss.deleteAllJobs(eventQueue);\n }\n }\n jobLogger.info('Existing jobs cleared');\n }\n\n // Each job registers an independent pg-boss queue/worker, so register them\n // concurrently — sequential awaits made startup scale linearly with job count.\n await Promise.all(jobs.map((job) => registerJob(job)));\n\n for (const job of jobs)\n {\n if (job.cronExpression)\n {\n registeredCronNames.add(job.name);\n }\n }\n\n if (shouldSweepOrphanSchedules())\n {\n await sweepOrphanSchedules(boss);\n }\n\n jobLogger.info('All jobs registered successfully');\n}\n\n// Cron names accumulate across registerJobs() calls so a sweep in one call\n// never treats another router's schedules (registered earlier in the same\n// process) as orphans.\nconst registeredCronNames = new Set<string>();\n\n/**\n * Reset sweep state accumulated by registerJobs()\n *\n * @internal test-only\n */\nexport function resetOrphanSweepState(): void\n{\n registeredCronNames.clear();\n}\n\n/**\n * Unschedule pg-boss schedules that are no longer declared on any router\n * registered in this process (opt-in via `sweepOrphanSchedules`)\n *\n * The router is the only sanctioned way to create cron schedules, so any\n * schedule whose name is not a declared cron job is an orphan: pg-boss keeps\n * creating jobs for it on every cron tick, and without a worker they\n * accumulate forever. Unscheduling stops the pile-up; the queue and its\n * existing job rows are left untouched so nothing is destroyed if the name\n * belongs to another app, an ad-hoc getBoss() schedule, or a newer deploy.\n *\n * Skipped entirely when no cron job has been declared — an empty router says\n * nothing about which schedules are orphans. Runs once after registration;\n * failures are logged per orphan and never block startup.\n */\nasync function sweepOrphanSchedules(boss: PgBoss): Promise<void>\n{\n if (registeredCronNames.size === 0)\n {\n jobLogger.debug('No cron jobs declared; skipping orphan schedule sweep');\n\n return;\n }\n\n try\n {\n const schedules = await boss.getSchedules();\n const orphans = schedules.filter((schedule) => !registeredCronNames.has(schedule.name));\n\n if (orphans.length === 0)\n {\n jobLogger.debug('No orphan schedules found');\n\n return;\n }\n\n await Promise.all(orphans.map(async (orphan) =>\n {\n try\n {\n await boss.unschedule(orphan.name, orphan.key);\n jobLogger.info(`Removed orphan schedule: ${orphan.name}`);\n }\n catch (error)\n {\n jobLogger.error(`Failed to remove orphan schedule: ${orphan.name}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }));\n }\n catch (error)\n {\n jobLogger.error('Orphan schedule sweep failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\n/**\n * Create queue if not exists (required for pg-boss v11+)\n */\nasync function ensureQueue(boss: PgBoss, queueName: string): Promise<void>\n{\n await boss.createQueue(queueName);\n}\n\n/**\n * Execute a single job handler with logging\n */\nasync function executeJobHandler(\n job: JobDef<any>,\n pgBossJob: PgBoss.Job<any>,\n): Promise<void>\n{\n jobLogger.debug(`[Job:${job.name}] Executing...`, { jobId: pgBossJob.id });\n\n const startTime = Date.now();\n\n try\n {\n if (job.inputSchema)\n {\n await (job.handler as (input: unknown) => Promise<void>)(pgBossJob.data);\n }\n else\n {\n await (job.handler as () => Promise<void>)();\n }\n\n const duration = Date.now() - startTime;\n jobLogger.info(`[Job:${job.name}] Completed in ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n });\n }\n catch (error)\n {\n const duration = Date.now() - startTime;\n jobLogger.error(`[Job:${job.name}] Failed after ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n}\n\n/**\n * Register worker handler for a job\n *\n * When batchSize > 1, jobs are processed in parallel.\n * Failed jobs are individually marked via boss.fail() so pg-boss can retry them.\n * Successful jobs are auto-completed when the handler callback resolves.\n */\nasync function registerWorker(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n): Promise<void>\n{\n // Ensure queue exists before registering worker\n await ensureQueue(boss, queueName);\n\n const batchSize = job.options?.batchSize ?? 1;\n const pollingIntervalSeconds = job.options?.pollingIntervalSeconds ?? env.JOB_POLLING_INTERVAL_SECONDS;\n\n await boss.work(\n queueName,\n { batchSize, pollingIntervalSeconds },\n async (pgBossJobs) =>\n {\n if (batchSize <= 1)\n {\n // Single job — throw on error for pg-boss retry\n await executeJobHandler(job, pgBossJobs[0]);\n\n return;\n }\n\n // Batch — parallel execution with individual failure handling\n const results = await Promise.allSettled(\n pgBossJobs.map((pgBossJob) => executeJobHandler(job, pgBossJob)),\n );\n\n // Collect failed job IDs and mark them individually.\n // boss.fail() sets state = 'failed'; the subsequent auto-complete\n // from work() only affects jobs still in 'active' state, so\n // already-failed jobs are not overwritten.\n const failedIds: string[] = [];\n\n for (let i = 0; i < results.length; i++)\n {\n if (results[i].status === 'rejected')\n {\n failedIds.push(pgBossJobs[i].id);\n }\n }\n\n if (failedIds.length > 0)\n {\n await boss.fail(queueName, failedIds);\n }\n\n // Callback resolves → pg-boss auto-completes remaining 'active' jobs\n },\n );\n}\n\n/**\n * Connect event to pg-boss queue\n */\nfunction connectEventToQueue(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n): void\n{\n if (!job._subscribedEventDef)\n {\n return;\n }\n\n const eventDef = job._subscribedEventDef as EventDef<any>;\n eventDef._registerJobQueue(queueName, async (queue, payload) =>\n {\n await boss.send(queue, payload as object, getDefaultJobOptions(job.options));\n });\n\n jobLogger.debug(`[Job:${job.name}] Connected to event: ${job.subscribedEvent}`);\n}\n\n/**\n * Register cron schedule for a job\n */\nasync function registerCronSchedule(boss: PgBoss, job: JobDef<any>): Promise<void>\n{\n if (!job.cronExpression)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Scheduling cron: ${job.cronExpression}`);\n\n // Ensure queue exists for cron jobs (uses job.name as queue)\n await ensureQueue(boss, job.name);\n\n await boss.schedule(\n job.name,\n job.cronExpression,\n {},\n getDefaultJobOptions(job.options),\n );\n\n jobLogger.info(`[Job:${job.name}] Cron scheduled: ${job.cronExpression}`);\n}\n\n/**\n * Queue a runOnce job\n */\nasync function queueRunOnceJob(boss: PgBoss, job: JobDef<any>): Promise<void>\n{\n if (!job.runOnce)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Queuing runOnce job`);\n\n // Ensure queue exists for runOnce jobs (uses job.name as queue)\n await ensureQueue(boss, job.name);\n\n await boss.send(\n job.name,\n {},\n {\n ...getDefaultJobOptions(job.options),\n singletonKey: `runOnce:${job.name}`,\n },\n );\n\n jobLogger.info(`[Job:${job.name}] runOnce job queued`);\n}\n\n/**\n * Register a single job with pg-boss\n */\nasync function registerJob(job: JobDef<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error('pg-boss not initialized');\n }\n\n const queueName = job.subscribedEvent\n ? getEventQueueName(job.subscribedEvent)\n : job.name;\n\n jobLogger.debug(`Registering job: ${job.name}`, {\n queueName,\n subscribedEvent: job.subscribedEvent,\n });\n\n await registerWorker(boss, job, queueName);\n connectEventToQueue(boss, job, queueName);\n await registerCronSchedule(boss, job);\n await queueRunOnceJob(boss, job);\n\n jobLogger.debug(`Job registered: ${job.name}`);\n}\n"]}
1
+ {"version":3,"sources":["../../src/job/boss.ts","../../src/job/queue-policy.ts","../../src/job/job-builder.ts","../../src/job/job-router.ts","../../src/job/register-jobs.ts"],"names":["job","jobLogger","logger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * pg-boss Wrapper\n *\n * Manages pg-boss instance lifecycle\n */\n\nimport PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Check if connection string uses SSL without certificate verification\n *\n * pg library verifies certificates by default even with sslmode=require.\n * For require/prefer modes (no explicit verification), we disable cert checking\n * to support self-signed certificates.\n */\nfunction requiresSSLWithoutVerification(connectionString: string): boolean\n{\n try\n {\n const url = new URL(connectionString);\n const sslmode = url.searchParams.get('sslmode');\n\n return sslmode === 'require' || sslmode === 'prefer';\n }\n catch\n {\n return false;\n }\n}\n\n/**\n * Remove sslmode parameter from connection string URL\n *\n * pg driver interprets sslmode=require as verify-full, which overrides\n * the ssl option object. Stripping it lets us control SSL via the ssl option only.\n */\nfunction stripSslModeFromUrl(connectionString: string): string\n{\n const url = new URL(connectionString);\n url.searchParams.delete('sslmode');\n\n return url.toString();\n}\n\n/**\n * globalThis keys for cross-module-cache singleton (ESM/CJS share same instance)\n */\nconst BOSS_KEY = Symbol.for('spfn:boss-instance');\nconst CONFIG_KEY = Symbol.for('spfn:boss-config');\n\nconst g = globalThis as any;\n\nfunction getBossInstance(): PgBoss | null\n{\n return g[BOSS_KEY] ?? null;\n}\n\nfunction setBossInstance(instance: PgBoss | null): void\n{\n g[BOSS_KEY] = instance;\n}\n\nfunction getBossConfig(): BossConfig | null\n{\n return g[CONFIG_KEY] ?? null;\n}\n\nfunction setBossConfig(config: BossConfig | null): void\n{\n g[CONFIG_KEY] = config;\n}\n\n/**\n * Options for pg-boss initialization\n *\n * @example\n * ```typescript\n * await initBoss({\n * connectionString: process.env.DATABASE_URL,\n * schema: 'spfn_queue',\n * clearOnStart: process.env.NODE_ENV === 'development',\n * });\n * ```\n */\nexport interface BossOptions\n{\n /**\n * PostgreSQL connection string\n *\n * @example 'postgresql://user:password@localhost:5432/mydb'\n */\n connectionString: string;\n\n /**\n * Schema name for pg-boss tables\n *\n * pg-boss creates its own tables in this schema.\n *\n * @default 'spfn_queue'\n */\n schema?: string;\n\n /**\n * Maintenance interval in seconds\n *\n * pg-boss runs maintenance tasks (cleanup, archiving) at this interval.\n *\n * @default 120\n */\n maintenanceIntervalSeconds?: number;\n\n /**\n * Monitor state changes interval in seconds\n *\n * When set, pg-boss emits state change events at this interval.\n *\n * @default undefined (disabled)\n */\n monitorIntervalSeconds?: number;\n\n /**\n * Clear all pending/scheduled jobs on startup\n *\n * Useful for development mode to start with a clean queue.\n * Should be false in production.\n *\n * @default false\n */\n clearOnStart?: boolean;\n\n /**\n * Unschedule cron schedules that are no longer declared on any registered\n * job router, once at startup after job registration. Queues and job rows\n * are never deleted — only the schedule row is removed.\n *\n * Leave disabled when multiple apps share the same pg-boss schema, when\n * schedules are created directly via getBoss(), or during rolling deploys\n * that mix router versions — the sweep only knows the routers registered\n * in this process and would unschedule everything else.\n *\n * @default false\n */\n sweepOrphanSchedules?: boolean;\n}\n\n/**\n * @deprecated Use BossOptions instead\n */\nexport type BossConfig = BossOptions;\n\n/**\n * Initialize pg-boss with the given configuration\n *\n * Must be called before registerJobs(). Typically handled by defineServerConfig().\n *\n * @param options - pg-boss configuration options\n * @returns The pg-boss instance\n *\n * @example\n * ```typescript\n * const boss = await initBoss({\n * connectionString: process.env.DATABASE_URL!,\n * schema: 'spfn_queue',\n * });\n * ```\n */\nexport async function initBoss(options: BossOptions): Promise<PgBoss>\n{\n const existing = getBossInstance();\n if (existing)\n {\n jobLogger.warn('pg-boss already initialized, returning existing instance');\n\n return existing;\n }\n\n jobLogger.info('Initializing pg-boss...');\n\n setBossConfig(options);\n\n const needsSSL = requiresSSLWithoutVerification(options.connectionString);\n\n const pgBossOptions: PgBoss.ConstructorOptions = {\n // pg 드라이버가 URL의 sslmode=require를 verify-full로 해석해서\n // ssl 옵션을 무시하므로, URL에서 sslmode를 빼고 ssl 객체만 전달\n connectionString: needsSSL\n ? stripSslModeFromUrl(options.connectionString)\n : options.connectionString,\n schema: options.schema ?? 'spfn_queue',\n maintenanceIntervalSeconds: options.maintenanceIntervalSeconds ?? 120,\n };\n\n if (needsSSL)\n {\n pgBossOptions.ssl = { rejectUnauthorized: false };\n }\n\n // Only set monitorIntervalSeconds if explicitly provided (must be >= 1)\n if (options.monitorIntervalSeconds !== undefined && options.monitorIntervalSeconds >= 1)\n {\n pgBossOptions.monitorIntervalSeconds = options.monitorIntervalSeconds;\n }\n\n const boss = new PgBoss(pgBossOptions);\n\n // Event handlers\n boss.on('error', (error) =>\n {\n jobLogger.error('pg-boss error:', error);\n });\n\n await boss.start();\n\n setBossInstance(boss);\n\n jobLogger.info('pg-boss started successfully');\n\n return boss;\n}\n\n/**\n * Get the current pg-boss instance\n */\nexport function getBoss(): PgBoss | null\n{\n return getBossInstance();\n}\n\n/**\n * Stop pg-boss gracefully\n */\nexport async function stopBoss(): Promise<void>\n{\n const boss = getBossInstance();\n if (!boss)\n {\n return;\n }\n\n jobLogger.info('Stopping pg-boss...');\n\n try\n {\n await boss.stop({ graceful: true, timeout: 30000 });\n jobLogger.info('pg-boss stopped gracefully');\n }\n catch (error)\n {\n jobLogger.error('Error stopping pg-boss:', error);\n throw error;\n }\n finally\n {\n setBossInstance(null);\n setBossConfig(null);\n }\n}\n\n/**\n * Check if pg-boss is initialized and running\n */\nexport function isBossRunning(): boolean\n{\n return getBossInstance() !== null;\n}\n\n/**\n * Check if jobs should be cleared on start\n */\nexport function shouldClearOnStart(): boolean\n{\n return getBossConfig()?.clearOnStart ?? false;\n}\n\n/**\n * Check if orphan cron schedules should be swept after registration\n */\nexport function shouldSweepOrphanSchedules(): boolean\n{\n return getBossConfig()?.sweepOrphanSchedules ?? false;\n}\n","/**\n * Queue Policy Resolution\n *\n * Lives apart from register-jobs.ts and job-builder.ts so both can read the\n * policy of a job definition without importing each other.\n */\n\nimport type { JobOptions, JobQueuePolicy } from './types';\n\n/**\n * The part of a job definition that decides its queue policy\n *\n * `JobDef` satisfies this; the builder can also pass its own fields before the\n * definition object exists.\n */\nexport interface QueuePolicySource\n{\n readonly runOnce?: boolean;\n readonly options?: JobOptions;\n}\n\n/**\n * Resolve the pg-boss queue policy for a job definition\n *\n * An explicit `.options({ policy })` always wins. Otherwise a job that asks\n * for deduplication — `.runOnce()` or an `options.singletonKey` — gets\n * `exclusive`, the only policy under which a second enqueue is dropped while\n * the first is still pending or running.\n *\n * Everything else stays `standard`: `exclusive` without a `singletonKey`\n * deduplicates on `COALESCE(singleton_key, '')` and would collapse a plain\n * queue to a single pending job.\n */\nexport function resolveQueuePolicy(job: QueuePolicySource): JobQueuePolicy\n{\n if (job.options?.policy)\n {\n return job.options.policy;\n }\n\n if (job.runOnce || job.options?.singletonKey)\n {\n return 'exclusive';\n }\n\n return 'standard';\n}\n","/**\n * Job Builder\n *\n * Fluent API for defining jobs, similar to route builder pattern\n */\n\nimport type { Static, TSchema } from '@sinclair/typebox';\nimport type { CompensateHandler, JobDef, JobHandler, JobOptions, JobSendOptions } from './types';\nimport type { EventDef, InferEventPayload } from '@spfn/core/event';\nimport { logger } from '@spfn/core/logger';\nimport { getBoss } from './boss';\nimport { resolveQueuePolicy, type QueuePolicySource } from './queue-policy';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n// Job names whose ignored singletonKey has already been reported. A send-time\n// key is usually inside a loop, so warning per call would flood the log.\nconst warnedIgnoredSingletonKeys = new Set<string>();\n\n/**\n * Reset the singletonKey warn-once state\n *\n * @internal test-only\n */\nexport function resetSingletonKeyWarnings(): void\n{\n warnedIgnoredSingletonKeys.clear();\n}\n\n/**\n * Warn once when a send-time singletonKey lands on a `standard` queue\n *\n * pg-boss stores the key and never conflicts on it there, so the send is\n * accepted and the deduplication the caller asked for silently does not\n * happen. The policy lives on the definition, not on the send, so the fix is\n * on the definition too.\n */\nfunction warnIfSingletonKeyIgnored(name: string, source: QueuePolicySource): void\n{\n if (resolveQueuePolicy(source) !== 'standard' || warnedIgnoredSingletonKeys.has(name))\n {\n return;\n }\n\n warnedIgnoredSingletonKeys.add(name);\n\n jobLogger.warn(\n `[Job:${name}] singletonKey ignored: queue policy is \"standard\"; ` +\n \"set .options({ policy: 'exclusive' }) (or another non-standard policy) to deduplicate\",\n );\n}\n\n/**\n * Build pg-boss options from job defaults and send options\n */\nfunction buildPgBossOptions(\n defaults?: JobOptions,\n sendOptions?: JobSendOptions,\n): Record<string, unknown>\n{\n const options: Record<string, unknown> = {};\n\n // Send options (per-job invocation)\n if (sendOptions?.startAfter)\n {\n options.startAfter = sendOptions.startAfter;\n }\n if (sendOptions?.singletonKey)\n {\n options.singletonKey = sendOptions.singletonKey;\n }\n if (sendOptions?.priority !== undefined)\n {\n options.priority = sendOptions.priority;\n }\n\n // Default options (from job definition)\n if (defaults?.retryLimit !== undefined)\n {\n options.retryLimit = defaults.retryLimit;\n }\n if (defaults?.retryDelay !== undefined)\n {\n options.retryDelay = defaults.retryDelay;\n }\n if (defaults?.expireInSeconds !== undefined)\n {\n options.expireInSeconds = defaults.expireInSeconds;\n }\n if (defaults?.priority !== undefined && sendOptions?.priority === undefined)\n {\n options.priority = defaults.priority;\n }\n if (defaults?.singletonKey && !sendOptions?.singletonKey)\n {\n options.singletonKey = defaults.singletonKey;\n }\n if (defaults?.retentionSeconds !== undefined)\n {\n options.retentionSeconds = defaults.retentionSeconds;\n }\n\n return options;\n}\n\n/**\n * Job builder class with fluent API\n */\nexport class JobBuilder<TInput = void, TOutput = void>\n{\n private readonly _name: string;\n private _inputSchema?: TSchema;\n private _outputSchema?: TSchema;\n private _cronExpression?: string;\n private _runOnce?: boolean;\n private _subscribedEvent?: string;\n private _subscribedEventDef?: EventDef<any>;\n private _options?: JobOptions;\n private _handler?: JobHandler<TInput, TOutput>;\n private _compensate?: CompensateHandler<TInput, TOutput>;\n\n constructor(name: string)\n {\n this._name = name;\n }\n\n /**\n * Define input schema with TypeBox\n */\n input<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<Static<TSchema>, TOutput>\n {\n const builder = new JobBuilder<Static<TSchema>, TOutput>(this._name);\n builder._inputSchema = schema;\n builder._outputSchema = this._outputSchema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Define output schema with TypeBox (for workflow integration)\n */\n output<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<TInput, Static<TSchema>>\n {\n const builder = new JobBuilder<TInput, Static<TSchema>>(this._name);\n builder._inputSchema = this._inputSchema;\n builder._outputSchema = schema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Subscribe to an event (decoupled triggering)\n *\n * @example\n * ```typescript\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * const sendWelcomeEmail = job('send-welcome-email')\n * .on(userCreated)\n * .handler(async (payload) => {\n * // payload is typed as { userId: string }\n * });\n * ```\n */\n on<TEvent extends EventDef<any>>(\n event: TEvent,\n ): JobBuilder<InferEventPayload<TEvent>, TOutput>\n {\n const builder = new JobBuilder<InferEventPayload<TEvent>, TOutput>(this._name);\n builder._inputSchema = event.schema;\n builder._outputSchema = this._outputSchema;\n builder._subscribedEvent = event.name;\n builder._subscribedEventDef = event;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Set cron expression for scheduled execution\n */\n cron(expression: string): this\n {\n this._cronExpression = expression;\n\n return this;\n }\n\n /**\n * Mark job to run once on server start\n */\n runOnce(): this\n {\n this._runOnce = true;\n\n return this;\n }\n\n /**\n * Set job options (retry, expiration, etc.)\n */\n options(options: JobOptions): this\n {\n this._options = options;\n\n return this;\n }\n\n /**\n * Set job timeout in milliseconds\n * (Converts to expireInSeconds for pg-boss)\n */\n timeout(ms: number): this\n {\n this._options = {\n ...this._options,\n expireInSeconds: Math.ceil(ms / 1000),\n };\n\n return this;\n }\n\n /**\n * Define compensate handler for rollback (workflow integration)\n */\n compensate(fn: CompensateHandler<TInput, TOutput>): this\n {\n this._compensate = fn;\n\n return this;\n }\n\n /**\n * Define the job handler and finalize the job definition\n */\n handler(fn: JobHandler<TInput, TOutput>): JobDef<TInput, TOutput>\n {\n this._handler = fn;\n\n const name = this._name;\n const inputSchema = this._inputSchema;\n const outputSchema = this._outputSchema;\n const cronExpression = this._cronExpression;\n const runOnce = this._runOnce;\n const subscribedEvent = this._subscribedEvent;\n const subscribedEventDef = this._subscribedEventDef;\n const options = this._options;\n const handler = this._handler;\n const compensate = this._compensate;\n\n // Create send function\n const send = async (\n inputOrOptions?: TInput | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<string | null> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n // Determine input and options based on whether job has input schema\n const [input, sendOptions] = inputSchema\n ? [inputOrOptions as TInput, maybeOptions]\n : [undefined, inputOrOptions as JobSendOptions | undefined];\n\n if (sendOptions?.singletonKey)\n {\n warnIfSingletonKeyIgnored(name, { runOnce, options });\n }\n\n return await boss.send(\n name,\n input ?? {},\n buildPgBossOptions(options, sendOptions),\n );\n };\n\n // Create run function (synchronous execution)\n const run = async (input?: TInput): Promise<TOutput> =>\n {\n if (inputSchema)\n {\n return await (handler as (input: TInput) => Promise<TOutput>)(input as TInput);\n }\n else\n {\n return await (handler as () => Promise<TOutput>)();\n }\n };\n\n // Create sendBatch function (bulk insert via pg-boss)\n const sendBatch = async (\n inputsOrOptions?: TInput[] | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<void> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n const [inputs, sendOptions] = inputSchema\n ? [inputsOrOptions as TInput[], maybeOptions]\n : [undefined, inputsOrOptions as JobSendOptions | undefined];\n\n if (sendOptions?.singletonKey)\n {\n warnIfSingletonKeyIgnored(name, { runOnce, options });\n }\n\n const pgBossOptions = buildPgBossOptions(options, sendOptions);\n\n const jobs = (inputs ?? [{}]).map((data) => ({\n name,\n ...pgBossOptions,\n data: data as object,\n }));\n\n await boss.insert(name, jobs);\n };\n\n return {\n name,\n inputSchema,\n outputSchema,\n cronExpression,\n runOnce,\n subscribedEvent,\n _subscribedEventDef: subscribedEventDef,\n options,\n handler,\n compensate,\n send: send as JobDef<TInput, TOutput>['send'],\n sendBatch: sendBatch as JobDef<TInput, TOutput>['sendBatch'],\n run: run as JobDef<TInput, TOutput>['run'],\n _input: undefined as unknown as TInput,\n _output: undefined as unknown as TOutput,\n };\n }\n}\n\n/**\n * Create a new job definition\n *\n * @example\n * ```typescript\n * // Simple job without input\n * export const cleanupJob = job('cleanup')\n * .handler(async () => {\n * await db.cleanup();\n * });\n *\n * // Job with typed input\n * export const sendEmailJob = job('send-email')\n * .input(Type.Object({\n * to: Type.String(),\n * subject: Type.String(),\n * body: Type.String(),\n * }))\n * .handler(async (input) => {\n * await emailService.send(input.to, input.subject, input.body);\n * });\n *\n * // Cron job\n * export const dailyReportJob = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => {\n * await reportService.generateDaily();\n * });\n *\n * // Run once on server start\n * export const initCacheJob = job('init-cache')\n * .runOnce()\n * .handler(async () => {\n * await cache.warmup();\n * });\n *\n * // With options\n * export const importantJob = job('important-task')\n * .input(Type.Object({ id: Type.String() }))\n * .options({\n * retryLimit: 5,\n * retryDelay: 5000,\n * priority: 10,\n * })\n * .handler(async (input) => {\n * await processImportant(input.id);\n * });\n * ```\n */\nexport function job(name: string): JobBuilder<void>\n{\n return new JobBuilder(name);\n}\n","/**\n * Job Router\n *\n * Groups job definitions for registration with the server\n */\n\nimport type { JobDef, JobRouter, JobRouterEntry } from './types';\n\n/**\n * Type guard to check if value is a JobDef\n */\nexport function isJobDef(value: unknown): value is JobDef<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n 'send' in value &&\n 'run' in value\n );\n}\n\n/**\n * Type guard to check if value is a JobRouter\n */\nexport function isJobRouter(value: unknown): value is JobRouter<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'jobs' in value &&\n '_jobs' in value\n );\n}\n\n/**\n * Define a job router to group jobs together\n *\n * @example\n * ```typescript\n * // Flat structure\n * export const jobRouter = defineJobRouter({\n * sendWelcomeEmail,\n * dailyReport,\n * initCache,\n * });\n *\n * // Nested structure\n * export const jobRouter = defineJobRouter({\n * email: defineJobRouter({\n * sendWelcome: sendWelcomeEmailJob,\n * sendReset: sendResetPasswordJob,\n * }),\n * reports: defineJobRouter({\n * daily: dailyReportJob,\n * weekly: weeklyReportJob,\n * }),\n * });\n *\n * // Mixed\n * export const jobRouter = defineJobRouter({\n * initCache, // flat\n * email: defineJobRouter({ ... }), // nested\n * });\n * ```\n */\nexport function defineJobRouter<\n TJobs extends Record<string, JobRouterEntry>,\n>(jobs: TJobs): JobRouter<TJobs>\n{\n return {\n jobs,\n _jobs: jobs,\n };\n}\n\n/**\n * Merge two job routers into a new one\n *\n * `defineServerConfig().jobs()` may be called once per domain router, the way\n * `.routes()` is; each call merges into what is already registered instead of\n * replacing it. Keys are the router's own namespace, so a key present in both\n * routers is a mistake the app cannot recover from silently — one of the two\n * would disappear.\n *\n * Neither input is mutated: a fresh router is returned.\n *\n * @throws if a top-level key exists in both routers\n */\nexport function mergeJobRouters(base: JobRouter<any>, extra: JobRouter<any>): JobRouter<any>\n{\n for (const key of Object.keys(extra.jobs))\n {\n if (key in base.jobs)\n {\n throw new Error(\n `jobs(): key \"${key}\" is already registered by an earlier .jobs() call; ` +\n 'rename it or nest it under a domain router',\n );\n }\n }\n\n return defineJobRouter({ ...base.jobs, ...extra.jobs });\n}\n\n/**\n * A collected job together with the router key path that reached it\n *\n * The key path (`email.sendWelcome`) is what an app author recognises, so it\n * is what error messages about a job name should quote.\n */\nexport interface JobEntry\n{\n readonly key: string;\n readonly job: JobDef<any>;\n}\n\n/**\n * Collect all JobDefs from a JobRouter (including nested) with their key paths\n */\nexport function collectJobEntries(\n router: JobRouter<any>,\n prefix = '',\n): JobEntry[]\n{\n const entries: JobEntry[] = [];\n\n for (const [key, value] of Object.entries(router.jobs))\n {\n const path = prefix ? `${prefix}.${key}` : key;\n\n if (isJobRouter(value))\n {\n // Nested router - recurse\n entries.push(...collectJobEntries(value, path));\n }\n else if (isJobDef(value))\n {\n entries.push({ key: path, job: value });\n }\n }\n\n return entries;\n}\n\n/**\n * Collect all JobDefs from a JobRouter (including nested)\n */\nexport function collectJobs(\n router: JobRouter<any>,\n prefix = '',\n): JobDef<any>[]\n{\n return collectJobEntries(router, prefix).map((entry) => entry.job);\n}\n","/**\n * Job Registration\n *\n * Registers jobs with pg-boss\n */\n\nimport type PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\nimport { env } from '@spfn/core/config';\nimport type { JobDef, JobOptions, JobQueuePolicy, JobRouter } from './types';\nimport type { EventDef } from '@spfn/core/event';\nimport { collectJobEntries, collectJobs } from './job-router';\nimport { resolveQueuePolicy } from './queue-policy';\nimport { getBoss, shouldClearOnStart, shouldSweepOrphanSchedules } from './boss';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Get the pg-boss queue name for an event\n */\nexport function getEventQueueName(eventName: string): string\n{\n return `event:${eventName}`;\n}\n\n/**\n * Build default pg-boss options for a job\n */\nfunction getDefaultJobOptions(options?: JobOptions): PgBoss.SendOptions\n{\n return {\n retryLimit: options?.retryLimit ?? 3,\n retryDelay: options?.retryDelay ?? 1000,\n // Exponential backoff by default — a failed cohort (e.g. a provider 429)\n // would otherwise all retry at the same fixed offset (thundering herd).\n retryBackoff: options?.retryBackoff ?? true,\n expireInSeconds: options?.expireInSeconds ?? 300,\n };\n}\n\n/**\n * Register all jobs from a JobRouter with pg-boss\n *\n * This function:\n * 1. Collects all jobs from the router (including nested routers)\n * 2. Optionally clears existing jobs (if clearOnStart is enabled)\n * 3. Registers each job's worker handler with pg-boss\n * 4. Sets up cron schedules for scheduled jobs\n * 5. Queues runOnce jobs\n * 6. Connects event subscriptions to job queues\n *\n * @param router - JobRouter containing job definitions\n *\n * @example\n * ```typescript\n * // Define jobs\n * const sendEmail = job('send-email')\n * .input(Type.Object({ to: Type.String() }))\n * .handler(async (input) => { ... });\n *\n * const dailyReport = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => { ... });\n *\n * // Create router\n * const jobRouter = defineJobRouter({ sendEmail, dailyReport });\n *\n * // Initialize pg-boss first\n * await initBoss({ connectionString: process.env.DATABASE_URL! });\n *\n * // Register jobs\n * await registerJobs(jobRouter);\n * ```\n */\nexport async function registerJobs(router: JobRouter<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n 'pg-boss not initialized. Call initBoss() before registerJobs()',\n );\n }\n\n assertUniqueJobNames(router);\n\n const jobs = collectJobs(router);\n const clearOnStart = shouldClearOnStart();\n\n jobLogger.info(`Registering ${jobs.length} job(s)...`);\n\n // Clear existing jobs if requested (useful for development)\n if (clearOnStart)\n {\n jobLogger.info('Clearing existing jobs before registration...');\n for (const job of jobs)\n {\n // Clear job queue\n await boss.deleteAllJobs(job.name);\n\n // Also clear event queue if subscribed\n if (job.subscribedEvent)\n {\n const eventQueue = getEventQueueName(job.subscribedEvent);\n await boss.deleteAllJobs(eventQueue);\n }\n }\n jobLogger.info('Existing jobs cleared');\n }\n\n // Each job registers an independent pg-boss queue/worker, so register them\n // concurrently — sequential awaits made startup scale linearly with job count.\n await Promise.all(jobs.map((job) => registerJob(job)));\n\n for (const job of jobs)\n {\n if (job.cronExpression)\n {\n registeredCronNames.add(job.name);\n }\n }\n\n if (shouldSweepOrphanSchedules())\n {\n await sweepOrphanSchedules(boss);\n }\n\n jobLogger.info('All jobs registered successfully');\n}\n\n/**\n * Reject two job definitions that share a `name`\n *\n * A job name *is* its pg-boss queue name, so two jobs with the same name share\n * one queue and one worker: whichever registered last silently handles both.\n * Merged routers make this easy to hit, and it must fail before any queue or\n * worker exists.\n *\n * @throws if two collected jobs share a name\n */\nfunction assertUniqueJobNames(router: JobRouter<any>): void\n{\n const keysByName = new Map<string, string>();\n\n for (const { key, job } of collectJobEntries(router))\n {\n const existingKey = keysByName.get(job.name);\n\n if (existingKey)\n {\n throw new Error(\n `registerJobs(): job name \"${job.name}\" is defined twice ` +\n `(keys \"${existingKey}\" and \"${key}\"); ` +\n 'job names are pg-boss queue names and must be unique',\n );\n }\n\n keysByName.set(job.name, key);\n }\n}\n\n// Cron names accumulate across registerJobs() calls so a sweep in one call\n// never treats another router's schedules (registered earlier in the same\n// process) as orphans.\nconst registeredCronNames = new Set<string>();\n\n/**\n * Reset sweep state accumulated by registerJobs()\n *\n * @internal test-only\n */\nexport function resetOrphanSweepState(): void\n{\n registeredCronNames.clear();\n}\n\n/**\n * Unschedule pg-boss schedules that are no longer declared on any router\n * registered in this process (opt-in via `sweepOrphanSchedules`)\n *\n * The router is the only sanctioned way to create cron schedules, so any\n * schedule whose name is not a declared cron job is an orphan: pg-boss keeps\n * creating jobs for it on every cron tick, and without a worker they\n * accumulate forever. Unscheduling stops the pile-up; the queue and its\n * existing job rows are left untouched so nothing is destroyed if the name\n * belongs to another app, an ad-hoc getBoss() schedule, or a newer deploy.\n *\n * Skipped entirely when no cron job has been declared — an empty router says\n * nothing about which schedules are orphans. Runs once after registration;\n * failures are logged per orphan and never block startup.\n */\nasync function sweepOrphanSchedules(boss: PgBoss): Promise<void>\n{\n if (registeredCronNames.size === 0)\n {\n jobLogger.debug('No cron jobs declared; skipping orphan schedule sweep');\n\n return;\n }\n\n try\n {\n const schedules = await boss.getSchedules();\n const orphans = schedules.filter((schedule) => !registeredCronNames.has(schedule.name));\n\n if (orphans.length === 0)\n {\n jobLogger.debug('No orphan schedules found');\n\n return;\n }\n\n await Promise.all(orphans.map(async (orphan) =>\n {\n try\n {\n await boss.unschedule(orphan.name, orphan.key);\n jobLogger.info(`Removed orphan schedule: ${orphan.name}`);\n }\n catch (error)\n {\n jobLogger.error(`Failed to remove orphan schedule: ${orphan.name}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }));\n }\n catch (error)\n {\n jobLogger.error('Orphan schedule sweep failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\n/**\n * Create the queue with the wanted policy, or report that it already differs\n *\n * pg-boss `create_queue` is `INSERT … ON CONFLICT DO NOTHING`, so a queue\n * created by an earlier version keeps its old policy and `createQueue` is a\n * silent no-op. `updateQueue` cannot change a policy either. Comparing against\n * `getQueue` is the only way an already-deployed app ever gets the fix.\n *\n * Recreating the queue drops its job rows, so that only happens under\n * `clearOnStart` (development). Otherwise the mismatch is logged and boot\n * continues — a queue that does not deduplicate is a degraded queue, not a\n * broken one.\n */\nasync function ensureQueue(boss: PgBoss, queueName: string, policy: JobQueuePolicy): Promise<void>\n{\n await boss.createQueue(queueName, { policy });\n\n // null = queue missing (nothing to compare); pg-boss defaults it to 'standard'\n const existing = await boss.getQueue(queueName);\n if (!existing || existing.policy === policy)\n {\n return;\n }\n\n if (shouldClearOnStart())\n {\n await boss.deleteQueue(queueName);\n await boss.createQueue(queueName, { policy });\n jobLogger.info(`[Queue:${queueName}] policy ${existing.policy} → ${policy} (recreated: clearOnStart)`);\n\n return;\n }\n\n jobLogger.error(\n `[Queue:${queueName}] policy mismatch: the queue exists as \"${existing.policy}\" but its jobs ` +\n `require \"${policy}\". Delete the queue in a quiet window ` +\n `(getBoss().deleteQueue('${queueName}') drops its job rows) or set clearOnStart in development; ` +\n 'until then singletonKey/runOnce on this queue do not deduplicate',\n );\n}\n\n/**\n * Build the once-per-queue ensure used by a single registerJob() call\n *\n * registerJob touches its queue up to three times (worker, cron, runOnce) and\n * all three want the same policy, so the queue is ensured on first use only —\n * otherwise the same mismatch would be created and logged three times over.\n */\nfunction createQueueEnsurer(boss: PgBoss, policy: JobQueuePolicy): (queueName: string) => Promise<void>\n{\n const ensured = new Set<string>();\n\n return async (queueName: string) =>\n {\n if (ensured.has(queueName))\n {\n return;\n }\n\n ensured.add(queueName);\n\n await ensureQueue(boss, queueName, policy);\n };\n}\n\n/**\n * Execute a single job handler with logging\n */\nasync function executeJobHandler(\n job: JobDef<any>,\n pgBossJob: PgBoss.Job<any>,\n): Promise<void>\n{\n jobLogger.debug(`[Job:${job.name}] Executing...`, { jobId: pgBossJob.id });\n\n const startTime = Date.now();\n\n try\n {\n if (job.inputSchema)\n {\n await (job.handler as (input: unknown) => Promise<void>)(pgBossJob.data);\n }\n else\n {\n await (job.handler as () => Promise<void>)();\n }\n\n const duration = Date.now() - startTime;\n jobLogger.info(`[Job:${job.name}] Completed in ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n });\n }\n catch (error)\n {\n const duration = Date.now() - startTime;\n jobLogger.error(`[Job:${job.name}] Failed after ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n}\n\n/**\n * Register worker handler for a job\n *\n * When batchSize > 1, jobs are processed in parallel.\n * Failed jobs are individually marked via boss.fail() so pg-boss can retry them.\n * Successful jobs are auto-completed when the handler callback resolves.\n */\nasync function registerWorker(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n ensureQueueOnce: (queueName: string) => Promise<void>,\n): Promise<void>\n{\n // Ensure queue exists before registering worker\n await ensureQueueOnce(queueName);\n\n const batchSize = job.options?.batchSize ?? 1;\n const pollingIntervalSeconds = job.options?.pollingIntervalSeconds ?? env.JOB_POLLING_INTERVAL_SECONDS;\n\n await boss.work(\n queueName,\n { batchSize, pollingIntervalSeconds },\n async (pgBossJobs) =>\n {\n if (batchSize <= 1)\n {\n // Single job — throw on error for pg-boss retry\n await executeJobHandler(job, pgBossJobs[0]);\n\n return;\n }\n\n // Batch — parallel execution with individual failure handling\n const results = await Promise.allSettled(\n pgBossJobs.map((pgBossJob) => executeJobHandler(job, pgBossJob)),\n );\n\n // Collect failed job IDs and mark them individually.\n // boss.fail() sets state = 'failed'; the subsequent auto-complete\n // from work() only affects jobs still in 'active' state, so\n // already-failed jobs are not overwritten.\n const failedIds: string[] = [];\n\n for (let i = 0; i < results.length; i++)\n {\n if (results[i].status === 'rejected')\n {\n failedIds.push(pgBossJobs[i].id);\n }\n }\n\n if (failedIds.length > 0)\n {\n await boss.fail(queueName, failedIds);\n }\n\n // Callback resolves → pg-boss auto-completes remaining 'active' jobs\n },\n );\n}\n\n/**\n * Connect event to pg-boss queue\n */\nfunction connectEventToQueue(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n): void\n{\n if (!job._subscribedEventDef)\n {\n return;\n }\n\n const eventDef = job._subscribedEventDef as EventDef<any>;\n eventDef._registerJobQueue(queueName, async (queue, payload) =>\n {\n await boss.send(queue, payload as object, getDefaultJobOptions(job.options));\n });\n\n jobLogger.debug(`[Job:${job.name}] Connected to event: ${job.subscribedEvent}`);\n}\n\n/**\n * Register cron schedule for a job\n */\nasync function registerCronSchedule(\n boss: PgBoss,\n job: JobDef<any>,\n ensureQueueOnce: (queueName: string) => Promise<void>,\n): Promise<void>\n{\n if (!job.cronExpression)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Scheduling cron: ${job.cronExpression}`);\n\n // Ensure queue exists for cron jobs (uses job.name as queue)\n await ensureQueueOnce(job.name);\n\n await boss.schedule(\n job.name,\n job.cronExpression,\n {},\n getDefaultJobOptions(job.options),\n );\n\n jobLogger.info(`[Job:${job.name}] Cron scheduled: ${job.cronExpression}`);\n}\n\n/**\n * Queue a runOnce job\n */\nasync function queueRunOnceJob(\n boss: PgBoss,\n job: JobDef<any>,\n ensureQueueOnce: (queueName: string) => Promise<void>,\n): Promise<void>\n{\n if (!job.runOnce)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Queuing runOnce job`);\n\n // Ensure queue exists for runOnce jobs (uses job.name as queue)\n await ensureQueueOnce(job.name);\n\n await boss.send(\n job.name,\n {},\n {\n ...getDefaultJobOptions(job.options),\n singletonKey: `runOnce:${job.name}`,\n },\n );\n\n jobLogger.info(`[Job:${job.name}] runOnce job queued`);\n}\n\n/**\n * Register a single job with pg-boss\n */\nasync function registerJob(job: JobDef<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error('pg-boss not initialized');\n }\n\n const queueName = job.subscribedEvent\n ? getEventQueueName(job.subscribedEvent)\n : job.name;\n\n jobLogger.debug(`Registering job: ${job.name}`, {\n queueName,\n subscribedEvent: job.subscribedEvent,\n });\n\n const ensureQueueOnce = createQueueEnsurer(boss, resolveQueuePolicy(job));\n\n await registerWorker(boss, job, queueName, ensureQueueOnce);\n connectEventToQueue(boss, job, queueName);\n await registerCronSchedule(boss, job, ensureQueueOnce);\n await queueRunOnceJob(boss, job, ensureQueueOnce);\n\n jobLogger.debug(`Job registered: ${job.name}`);\n}\n"]}
@@ -5,7 +5,7 @@ import { serve } from '@hono/node-server';
5
5
  import { NamedMiddleware, Router } from '@spfn/core/route';
6
6
  import { OnErrorContext, ProxyGuardConfig, RateLimitOptions } from '@spfn/core/middleware';
7
7
  import { SafeFetchPolicy } from '@spfn/core/security';
8
- import { c as JobRouter, e as BossOptions } from '../boss-D16fO2oG.js';
8
+ import { c as JobRouter, f as BossOptions } from '../boss-BiDCbbAQ.js';
9
9
  import { b as EventRouterDef, E as EventDef } from '../token-manager-BT5EnUAR.js';
10
10
  import { d as SSEHandlerConfig, e as SSEAuthConfig } from '../types-ZQODsBft.js';
11
11
  import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WSAuthConfig } from '../types-2AbaW4Ie.js';
@@ -234,6 +234,8 @@ interface ServerConfig {
234
234
  * Jobs defined with job()...handler() style
235
235
  * Uses pg-boss for PostgreSQL-based job queue
236
236
  *
237
+ * Merged across every `.jobs()` call, so this holds all of them.
238
+ *
237
239
  * @example
238
240
  * ```typescript
239
241
  * import { job, defineJobRouter } from '@spfn/core/job';
@@ -1145,6 +1147,20 @@ declare class ServerConfigBuilder {
1145
1147
  /**
1146
1148
  * Register background jobs router
1147
1149
  *
1150
+ * May be called once per domain router: repeated calls **merge** into one
1151
+ * router, the way `.routes()` composes. Calling it several times used to
1152
+ * keep only the last router and register nothing else, without warning.
1153
+ *
1154
+ * The pg-boss `config` is per server, so pass it on one call only.
1155
+ *
1156
+ * Two names can collide, and both fail loudly:
1157
+ * - a router **key** present in two calls throws here, at config time;
1158
+ * - two jobs sharing a **`job.name`** (the pg-boss queue name) throw in
1159
+ * `registerJobs()` at boot, before any queue or worker is created.
1160
+ *
1161
+ * @throws if a router key was already registered by an earlier call
1162
+ * @throws if `config` is given when an earlier call already gave one
1163
+ *
1148
1164
  * @example
1149
1165
  * ```typescript
1150
1166
  * import { job, defineJobRouter } from '@spfn/core/job';
@@ -1157,7 +1173,8 @@ declare class ServerConfigBuilder {
1157
1173
  *
1158
1174
  * export default defineServerConfig()
1159
1175
  * .routes(appRouter)
1160
- * .jobs(jobRouter)
1176
+ * .jobs(jobRouter, { clearOnStart: isDev }) // config on one call only
1177
+ * .jobs(billingJobRouter) // merged, not replaced
1161
1178
  * .build();
1162
1179
  * ```
1163
1180
  */
@@ -2275,6 +2275,20 @@ function shouldSweepOrphanSchedules() {
2275
2275
  return getBossConfig()?.sweepOrphanSchedules ?? false;
2276
2276
  }
2277
2277
 
2278
+ // src/job/queue-policy.ts
2279
+ function resolveQueuePolicy(job2) {
2280
+ if (job2.options?.policy) {
2281
+ return job2.options.policy;
2282
+ }
2283
+ if (job2.runOnce || job2.options?.singletonKey) {
2284
+ return "exclusive";
2285
+ }
2286
+ return "standard";
2287
+ }
2288
+
2289
+ // src/job/job-builder.ts
2290
+ logger.child("@spfn/core:job");
2291
+
2278
2292
  // src/job/job-router.ts
2279
2293
  function isJobDef(value) {
2280
2294
  return value !== null && typeof value === "object" && "name" in value && "handler" in value && "send" in value && "run" in value;
@@ -2282,19 +2296,38 @@ function isJobDef(value) {
2282
2296
  function isJobRouter(value) {
2283
2297
  return value !== null && typeof value === "object" && "jobs" in value && "_jobs" in value;
2284
2298
  }
2285
- function collectJobs(router, prefix = "") {
2286
- const jobs = [];
2299
+ function defineJobRouter(jobs) {
2300
+ return {
2301
+ jobs,
2302
+ _jobs: jobs
2303
+ };
2304
+ }
2305
+ function mergeJobRouters(base, extra) {
2306
+ for (const key of Object.keys(extra.jobs)) {
2307
+ if (key in base.jobs) {
2308
+ throw new Error(
2309
+ `jobs(): key "${key}" is already registered by an earlier .jobs() call; rename it or nest it under a domain router`
2310
+ );
2311
+ }
2312
+ }
2313
+ return defineJobRouter({ ...base.jobs, ...extra.jobs });
2314
+ }
2315
+ function collectJobEntries(router, prefix = "") {
2316
+ const entries = [];
2287
2317
  for (const [key, value] of Object.entries(router.jobs)) {
2288
- const name = prefix ? `${prefix}.${key}` : key;
2318
+ const path = prefix ? `${prefix}.${key}` : key;
2289
2319
  if (isJobRouter(value)) {
2290
- jobs.push(...collectJobs(value, name));
2320
+ entries.push(...collectJobEntries(value, path));
2291
2321
  } else if (isJobDef(value)) {
2292
- jobs.push(value);
2322
+ entries.push({ key: path, job: value });
2293
2323
  }
2294
2324
  }
2295
- return jobs;
2325
+ return entries;
2296
2326
  }
2297
- var jobLogger2 = logger.child("@spfn/core:job");
2327
+ function collectJobs(router, prefix = "") {
2328
+ return collectJobEntries(router, prefix).map((entry) => entry.job);
2329
+ }
2330
+ var jobLogger3 = logger.child("@spfn/core:job");
2298
2331
  function getEventQueueName(eventName) {
2299
2332
  return `event:${eventName}`;
2300
2333
  }
@@ -2315,11 +2348,12 @@ async function registerJobs(router) {
2315
2348
  "pg-boss not initialized. Call initBoss() before registerJobs()"
2316
2349
  );
2317
2350
  }
2351
+ assertUniqueJobNames(router);
2318
2352
  const jobs = collectJobs(router);
2319
2353
  const clearOnStart = shouldClearOnStart();
2320
- jobLogger2.info(`Registering ${jobs.length} job(s)...`);
2354
+ jobLogger3.info(`Registering ${jobs.length} job(s)...`);
2321
2355
  if (clearOnStart) {
2322
- jobLogger2.info("Clearing existing jobs before registration...");
2356
+ jobLogger3.info("Clearing existing jobs before registration...");
2323
2357
  for (const job2 of jobs) {
2324
2358
  await boss.deleteAllJobs(job2.name);
2325
2359
  if (job2.subscribedEvent) {
@@ -2327,7 +2361,7 @@ async function registerJobs(router) {
2327
2361
  await boss.deleteAllJobs(eventQueue);
2328
2362
  }
2329
2363
  }
2330
- jobLogger2.info("Existing jobs cleared");
2364
+ jobLogger3.info("Existing jobs cleared");
2331
2365
  }
2332
2366
  await Promise.all(jobs.map((job2) => registerJob(job2)));
2333
2367
  for (const job2 of jobs) {
@@ -2338,42 +2372,77 @@ async function registerJobs(router) {
2338
2372
  if (shouldSweepOrphanSchedules()) {
2339
2373
  await sweepOrphanSchedules(boss);
2340
2374
  }
2341
- jobLogger2.info("All jobs registered successfully");
2375
+ jobLogger3.info("All jobs registered successfully");
2376
+ }
2377
+ function assertUniqueJobNames(router) {
2378
+ const keysByName = /* @__PURE__ */ new Map();
2379
+ for (const { key, job: job2 } of collectJobEntries(router)) {
2380
+ const existingKey = keysByName.get(job2.name);
2381
+ if (existingKey) {
2382
+ throw new Error(
2383
+ `registerJobs(): job name "${job2.name}" is defined twice (keys "${existingKey}" and "${key}"); job names are pg-boss queue names and must be unique`
2384
+ );
2385
+ }
2386
+ keysByName.set(job2.name, key);
2387
+ }
2342
2388
  }
2343
2389
  var registeredCronNames = /* @__PURE__ */ new Set();
2344
2390
  async function sweepOrphanSchedules(boss) {
2345
2391
  if (registeredCronNames.size === 0) {
2346
- jobLogger2.debug("No cron jobs declared; skipping orphan schedule sweep");
2392
+ jobLogger3.debug("No cron jobs declared; skipping orphan schedule sweep");
2347
2393
  return;
2348
2394
  }
2349
2395
  try {
2350
2396
  const schedules = await boss.getSchedules();
2351
2397
  const orphans = schedules.filter((schedule) => !registeredCronNames.has(schedule.name));
2352
2398
  if (orphans.length === 0) {
2353
- jobLogger2.debug("No orphan schedules found");
2399
+ jobLogger3.debug("No orphan schedules found");
2354
2400
  return;
2355
2401
  }
2356
2402
  await Promise.all(orphans.map(async (orphan) => {
2357
2403
  try {
2358
2404
  await boss.unschedule(orphan.name, orphan.key);
2359
- jobLogger2.info(`Removed orphan schedule: ${orphan.name}`);
2405
+ jobLogger3.info(`Removed orphan schedule: ${orphan.name}`);
2360
2406
  } catch (error) {
2361
- jobLogger2.error(`Failed to remove orphan schedule: ${orphan.name}`, {
2407
+ jobLogger3.error(`Failed to remove orphan schedule: ${orphan.name}`, {
2362
2408
  error: error instanceof Error ? error.message : String(error)
2363
2409
  });
2364
2410
  }
2365
2411
  }));
2366
2412
  } catch (error) {
2367
- jobLogger2.error("Orphan schedule sweep failed", {
2413
+ jobLogger3.error("Orphan schedule sweep failed", {
2368
2414
  error: error instanceof Error ? error.message : String(error)
2369
2415
  });
2370
2416
  }
2371
2417
  }
2372
- async function ensureQueue(boss, queueName) {
2373
- await boss.createQueue(queueName);
2418
+ async function ensureQueue(boss, queueName, policy) {
2419
+ await boss.createQueue(queueName, { policy });
2420
+ const existing = await boss.getQueue(queueName);
2421
+ if (!existing || existing.policy === policy) {
2422
+ return;
2423
+ }
2424
+ if (shouldClearOnStart()) {
2425
+ await boss.deleteQueue(queueName);
2426
+ await boss.createQueue(queueName, { policy });
2427
+ jobLogger3.info(`[Queue:${queueName}] policy ${existing.policy} \u2192 ${policy} (recreated: clearOnStart)`);
2428
+ return;
2429
+ }
2430
+ jobLogger3.error(
2431
+ `[Queue:${queueName}] policy mismatch: the queue exists as "${existing.policy}" but its jobs require "${policy}". Delete the queue in a quiet window (getBoss().deleteQueue('${queueName}') drops its job rows) or set clearOnStart in development; until then singletonKey/runOnce on this queue do not deduplicate`
2432
+ );
2433
+ }
2434
+ function createQueueEnsurer(boss, policy) {
2435
+ const ensured = /* @__PURE__ */ new Set();
2436
+ return async (queueName) => {
2437
+ if (ensured.has(queueName)) {
2438
+ return;
2439
+ }
2440
+ ensured.add(queueName);
2441
+ await ensureQueue(boss, queueName, policy);
2442
+ };
2374
2443
  }
2375
2444
  async function executeJobHandler(job2, pgBossJob) {
2376
- jobLogger2.debug(`[Job:${job2.name}] Executing...`, { jobId: pgBossJob.id });
2445
+ jobLogger3.debug(`[Job:${job2.name}] Executing...`, { jobId: pgBossJob.id });
2377
2446
  const startTime = Date.now();
2378
2447
  try {
2379
2448
  if (job2.inputSchema) {
@@ -2382,13 +2451,13 @@ async function executeJobHandler(job2, pgBossJob) {
2382
2451
  await job2.handler();
2383
2452
  }
2384
2453
  const duration = Date.now() - startTime;
2385
- jobLogger2.info(`[Job:${job2.name}] Completed in ${duration}ms`, {
2454
+ jobLogger3.info(`[Job:${job2.name}] Completed in ${duration}ms`, {
2386
2455
  jobId: pgBossJob.id,
2387
2456
  duration
2388
2457
  });
2389
2458
  } catch (error) {
2390
2459
  const duration = Date.now() - startTime;
2391
- jobLogger2.error(`[Job:${job2.name}] Failed after ${duration}ms`, {
2460
+ jobLogger3.error(`[Job:${job2.name}] Failed after ${duration}ms`, {
2392
2461
  jobId: pgBossJob.id,
2393
2462
  duration,
2394
2463
  error: error instanceof Error ? error.message : String(error)
@@ -2396,8 +2465,8 @@ async function executeJobHandler(job2, pgBossJob) {
2396
2465
  throw error;
2397
2466
  }
2398
2467
  }
2399
- async function registerWorker(boss, job2, queueName) {
2400
- await ensureQueue(boss, queueName);
2468
+ async function registerWorker(boss, job2, queueName, ensureQueueOnce) {
2469
+ await ensureQueueOnce(queueName);
2401
2470
  const batchSize = job2.options?.batchSize ?? 1;
2402
2471
  const pollingIntervalSeconds = job2.options?.pollingIntervalSeconds ?? env.JOB_POLLING_INTERVAL_SECONDS;
2403
2472
  await boss.work(
@@ -2431,28 +2500,28 @@ function connectEventToQueue(boss, job2, queueName) {
2431
2500
  eventDef._registerJobQueue(queueName, async (queue, payload) => {
2432
2501
  await boss.send(queue, payload, getDefaultJobOptions(job2.options));
2433
2502
  });
2434
- jobLogger2.debug(`[Job:${job2.name}] Connected to event: ${job2.subscribedEvent}`);
2503
+ jobLogger3.debug(`[Job:${job2.name}] Connected to event: ${job2.subscribedEvent}`);
2435
2504
  }
2436
- async function registerCronSchedule(boss, job2) {
2505
+ async function registerCronSchedule(boss, job2, ensureQueueOnce) {
2437
2506
  if (!job2.cronExpression) {
2438
2507
  return;
2439
2508
  }
2440
- jobLogger2.debug(`[Job:${job2.name}] Scheduling cron: ${job2.cronExpression}`);
2441
- await ensureQueue(boss, job2.name);
2509
+ jobLogger3.debug(`[Job:${job2.name}] Scheduling cron: ${job2.cronExpression}`);
2510
+ await ensureQueueOnce(job2.name);
2442
2511
  await boss.schedule(
2443
2512
  job2.name,
2444
2513
  job2.cronExpression,
2445
2514
  {},
2446
2515
  getDefaultJobOptions(job2.options)
2447
2516
  );
2448
- jobLogger2.info(`[Job:${job2.name}] Cron scheduled: ${job2.cronExpression}`);
2517
+ jobLogger3.info(`[Job:${job2.name}] Cron scheduled: ${job2.cronExpression}`);
2449
2518
  }
2450
- async function queueRunOnceJob(boss, job2) {
2519
+ async function queueRunOnceJob(boss, job2, ensureQueueOnce) {
2451
2520
  if (!job2.runOnce) {
2452
2521
  return;
2453
2522
  }
2454
- jobLogger2.debug(`[Job:${job2.name}] Queuing runOnce job`);
2455
- await ensureQueue(boss, job2.name);
2523
+ jobLogger3.debug(`[Job:${job2.name}] Queuing runOnce job`);
2524
+ await ensureQueueOnce(job2.name);
2456
2525
  await boss.send(
2457
2526
  job2.name,
2458
2527
  {},
@@ -2461,7 +2530,7 @@ async function queueRunOnceJob(boss, job2) {
2461
2530
  singletonKey: `runOnce:${job2.name}`
2462
2531
  }
2463
2532
  );
2464
- jobLogger2.info(`[Job:${job2.name}] runOnce job queued`);
2533
+ jobLogger3.info(`[Job:${job2.name}] runOnce job queued`);
2465
2534
  }
2466
2535
  async function registerJob(job2) {
2467
2536
  const boss = getBoss();
@@ -2469,15 +2538,16 @@ async function registerJob(job2) {
2469
2538
  throw new Error("pg-boss not initialized");
2470
2539
  }
2471
2540
  const queueName = job2.subscribedEvent ? getEventQueueName(job2.subscribedEvent) : job2.name;
2472
- jobLogger2.debug(`Registering job: ${job2.name}`, {
2541
+ jobLogger3.debug(`Registering job: ${job2.name}`, {
2473
2542
  queueName,
2474
2543
  subscribedEvent: job2.subscribedEvent
2475
2544
  });
2476
- await registerWorker(boss, job2, queueName);
2545
+ const ensureQueueOnce = createQueueEnsurer(boss, resolveQueuePolicy(job2));
2546
+ await registerWorker(boss, job2, queueName, ensureQueueOnce);
2477
2547
  connectEventToQueue(boss, job2, queueName);
2478
- await registerCronSchedule(boss, job2);
2479
- await queueRunOnceJob(boss, job2);
2480
- jobLogger2.debug(`Job registered: ${job2.name}`);
2548
+ await registerCronSchedule(boss, job2, ensureQueueOnce);
2549
+ await queueRunOnceJob(boss, job2, ensureQueueOnce);
2550
+ jobLogger3.debug(`Job registered: ${job2.name}`);
2481
2551
  }
2482
2552
  var wsLogger = logger.child("@spfn/core:ws");
2483
2553
  function isOriginAllowed(origin, allowList) {
@@ -3506,6 +3576,20 @@ var ServerConfigBuilder = class {
3506
3576
  /**
3507
3577
  * Register background jobs router
3508
3578
  *
3579
+ * May be called once per domain router: repeated calls **merge** into one
3580
+ * router, the way `.routes()` composes. Calling it several times used to
3581
+ * keep only the last router and register nothing else, without warning.
3582
+ *
3583
+ * The pg-boss `config` is per server, so pass it on one call only.
3584
+ *
3585
+ * Two names can collide, and both fail loudly:
3586
+ * - a router **key** present in two calls throws here, at config time;
3587
+ * - two jobs sharing a **`job.name`** (the pg-boss queue name) throw in
3588
+ * `registerJobs()` at boot, before any queue or worker is created.
3589
+ *
3590
+ * @throws if a router key was already registered by an earlier call
3591
+ * @throws if `config` is given when an earlier call already gave one
3592
+ *
3509
3593
  * @example
3510
3594
  * ```typescript
3511
3595
  * import { job, defineJobRouter } from '@spfn/core/job';
@@ -3518,12 +3602,18 @@ var ServerConfigBuilder = class {
3518
3602
  *
3519
3603
  * export default defineServerConfig()
3520
3604
  * .routes(appRouter)
3521
- * .jobs(jobRouter)
3605
+ * .jobs(jobRouter, { clearOnStart: isDev }) // config on one call only
3606
+ * .jobs(billingJobRouter) // merged, not replaced
3522
3607
  * .build();
3523
3608
  * ```
3524
3609
  */
3525
3610
  jobs(router, config) {
3526
- this.config.jobs = router;
3611
+ if (config && this.config.jobsConfig) {
3612
+ throw new Error(
3613
+ "jobs(): pg-boss config was already given by an earlier .jobs() call; pass it once"
3614
+ );
3615
+ }
3616
+ this.config.jobs = this.config.jobs ? mergeJobRouters(this.config.jobs, router) : router;
3527
3617
  if (config) {
3528
3618
  this.config.jobsConfig = config;
3529
3619
  }