alepha 0.20.1 → 0.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/files/index.js +2 -1
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +64 -148
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +371 -573
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +605 -1012
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +78 -17
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +90 -23
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/payments/index.d.ts +2 -1
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/payments/index.js +4 -2
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/users/index.d.ts +34 -31
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +13 -7
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.js +2 -1
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +8 -34
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +43 -232
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +36 -11
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +93 -27
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/command/index.d.ts +1 -1
- package/dist/core/index.browser.js +6 -0
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +6 -0
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +6 -0
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/react/form/index.d.ts +60 -1
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +86 -1
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.browser.js +16 -1
- package/dist/react/head/index.browser.js.map +1 -1
- package/dist/react/head/index.d.ts +6 -0
- package/dist/react/head/index.d.ts.map +1 -1
- package/dist/react/head/index.js +16 -1
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/router/index.browser.js +0 -10
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +35 -12
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +0 -10
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +124 -0
- package/dist/react/ui/index.d.ts.map +1 -0
- package/dist/react/ui/index.js +206 -0
- package/dist/react/ui/index.js.map +1 -0
- package/dist/router/index.d.ts +13 -13
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +45 -32
- package/dist/router/index.js.map +1 -1
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +1 -0
- package/dist/system/index.js.map +1 -1
- package/dist/topic/core/index.js +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/package.json +6 -23
- package/src/api/files/jobs/FileJobs.ts +2 -1
- package/src/api/jobs/__tests__/$job.spec.ts +316 -2867
- package/src/api/jobs/controllers/AdminJobController.ts +29 -138
- package/src/api/jobs/entities/jobExecutionEntity.ts +27 -19
- package/src/api/jobs/index.browser.ts +5 -7
- package/src/api/jobs/index.ts +23 -51
- package/src/api/jobs/primitives/$job.ts +66 -58
- package/src/api/jobs/providers/JobProvider.ts +561 -566
- package/src/api/jobs/providers/JobQueueProvider.ts +18 -19
- package/src/api/jobs/schemas/jobConfigAtom.ts +20 -23
- package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +3 -27
- package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +5 -7
- package/src/api/jobs/schemas/jobRegistrationSchema.ts +7 -4
- package/src/api/jobs/schemas/triggerJobSchema.ts +0 -1
- package/src/api/jobs/services/JobService.ts +90 -483
- package/src/api/notifications/controllers/AdminNotificationController.ts +19 -12
- package/src/api/notifications/index.ts +7 -4
- package/src/api/notifications/jobs/NotificationJobs.ts +83 -12
- package/src/api/payments/services/PaymentService.ts +4 -2
- package/src/api/users/__tests__/UserJobs.spec.ts +10 -49
- package/src/api/users/audits/UserAudits.ts +3 -1
- package/src/api/users/buckets/UserBuckets.ts +2 -1
- package/src/api/users/index.ts +1 -4
- package/src/api/users/jobs/UserJobs.ts +5 -4
- package/src/api/verifications/jobs/VerificationJobs.ts +2 -1
- package/src/cli/core/__tests__/init.spec.ts +1 -1
- package/src/cli/core/commands/init.ts +0 -12
- package/src/cli/core/services/PackageManagerUtils.ts +2 -9
- package/src/cli/core/services/ProjectScaffolder.ts +17 -65
- package/src/cli/core/templates/agentMd.ts +2 -8
- package/src/cli/core/templates/apiIndexTs.ts +4 -18
- package/src/cli/core/templates/mainCss.ts +1 -36
- package/src/cli/core/templates/vitestConfigTs.ts +17 -0
- package/src/cli/core/templates/webAppRouterTs.ts +2 -85
- package/src/cli/platform/__tests__/CloudflareAdapter.spec.ts +22 -71
- package/src/cli/platform/adapters/CloudflareAdapter.ts +12 -11
- package/src/cli/platform/atoms/platformOptions.ts +9 -0
- package/src/cli/platform/schemas/cloudflare.ts +3 -2
- package/src/cli/platform/services/CloudflareApi.ts +164 -25
- package/src/cli/platform/services/WranglerApi.ts +0 -17
- package/src/core/Alepha.ts +9 -0
- package/src/react/form/index.ts +2 -0
- package/src/react/form/services/parseField.ts +163 -0
- package/src/react/form/services/prettyName.ts +19 -0
- package/src/react/head/providers/BrowserHeadProvider.ts +31 -10
- package/src/react/router/primitives/$page.ts +35 -12
- package/src/react/ui/atoms/uiAtom.ts +28 -0
- package/src/react/ui/components/ColorScheme.tsx +36 -0
- package/src/react/ui/hooks/useColorMode.ts +49 -0
- package/src/react/ui/hooks/useSidebarState.ts +26 -0
- package/src/react/ui/hooks/useTheme.ts +22 -0
- package/src/react/ui/index.ts +35 -0
- package/src/react/ui/services/UiPersistence.ts +41 -0
- package/src/router/TemplatedPathParser.ts +50 -51
- package/src/router/__tests__/RouterProvider.spec.ts +62 -0
- package/src/router/__tests__/TemplatedPathParser.spec.ts +18 -0
- package/src/router/providers/RouterProvider.ts +10 -5
- package/src/system/providers/NodeShellProvider.ts +1 -0
- package/src/topic/core/providers/TopicProvider.ts +1 -1
- package/dist/api/invitations/index.d.ts +0 -790
- package/dist/api/invitations/index.d.ts.map +0 -1
- package/dist/api/invitations/index.js +0 -662
- package/dist/api/invitations/index.js.map +0 -1
- package/dist/api/issues/index.d.ts +0 -810
- package/dist/api/issues/index.d.ts.map +0 -1
- package/dist/api/issues/index.js +0 -444
- package/dist/api/issues/index.js.map +0 -1
- package/dist/api/subscriptions/index.d.ts +0 -1692
- package/dist/api/subscriptions/index.d.ts.map +0 -1
- package/dist/api/subscriptions/index.js +0 -1867
- package/dist/api/subscriptions/index.js.map +0 -1
- package/dist/api/workflows/index.browser.js +0 -246
- package/dist/api/workflows/index.browser.js.map +0 -1
- package/dist/api/workflows/index.d.ts +0 -1618
- package/dist/api/workflows/index.d.ts.map +0 -1
- package/dist/api/workflows/index.js +0 -1495
- package/dist/api/workflows/index.js.map +0 -1
- package/src/api/invitations/__tests__/InvitationService.spec.ts +0 -439
- package/src/api/invitations/controllers/AdminInvitationController.ts +0 -86
- package/src/api/invitations/controllers/InvitationController.ts +0 -84
- package/src/api/invitations/entities/invitations.ts +0 -33
- package/src/api/invitations/index.ts +0 -58
- package/src/api/invitations/jobs/InvitationJobs.ts +0 -37
- package/src/api/invitations/providers/InvitationProvider.ts +0 -45
- package/src/api/invitations/schemas/createInvitationSchema.ts +0 -12
- package/src/api/invitations/schemas/invitationConfigAtom.ts +0 -20
- package/src/api/invitations/schemas/invitationQuerySchema.ts +0 -15
- package/src/api/invitations/schemas/invitationResourceSchema.ts +0 -6
- package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +0 -22
- package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +0 -10
- package/src/api/invitations/services/InvitationService.ts +0 -556
- package/src/api/issues/__tests__/IssueService.spec.ts +0 -263
- package/src/api/issues/controllers/AdminIssueController.ts +0 -149
- package/src/api/issues/controllers/IssueController.ts +0 -44
- package/src/api/issues/entities/issues.ts +0 -49
- package/src/api/issues/index.ts +0 -50
- package/src/api/issues/schemas/createIssueSchema.ts +0 -13
- package/src/api/issues/schemas/issueConfigAtom.ts +0 -13
- package/src/api/issues/schemas/issueQuerySchema.ts +0 -18
- package/src/api/issues/schemas/issueResourceSchema.ts +0 -6
- package/src/api/issues/schemas/myIssueQuerySchema.ts +0 -10
- package/src/api/issues/schemas/updateIssueSchema.ts +0 -13
- package/src/api/issues/services/IssueService.ts +0 -264
- package/src/api/jobs/__tests__/$job-middleware.spec.ts +0 -126
- package/src/api/jobs/__tests__/JobService.spec.ts +0 -31
- package/src/api/jobs/entities/jobExecutionLogEntity.ts +0 -13
- package/src/api/jobs/schemas/jobActivitySchema.ts +0 -15
- package/src/api/jobs/schemas/jobCronInfoSchema.ts +0 -22
- package/src/api/jobs/schemas/jobExecutionDetailResourceSchema.ts +0 -20
- package/src/api/jobs/schemas/jobFailureSchema.ts +0 -9
- package/src/api/jobs/schemas/jobQueueDepthSchema.ts +0 -14
- package/src/api/jobs/schemas/jobStatsSchema.ts +0 -14
- package/src/api/jobs/services/JobService-tests.ts +0 -157
- package/src/api/subscriptions/__tests__/BillingService.spec.ts +0 -218
- package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +0 -278
- package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +0 -212
- package/src/api/subscriptions/controllers/SubscriptionController.ts +0 -189
- package/src/api/subscriptions/entities/subscriptionEvents.ts +0 -54
- package/src/api/subscriptions/entities/subscriptions.ts +0 -68
- package/src/api/subscriptions/index.ts +0 -133
- package/src/api/subscriptions/jobs/SubscriptionJobs.ts +0 -382
- package/src/api/subscriptions/middleware/$requireLimit.ts +0 -50
- package/src/api/subscriptions/middleware/$requirePlan.ts +0 -49
- package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +0 -110
- package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +0 -8
- package/src/api/subscriptions/schemas/changePlanSchema.ts +0 -9
- package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +0 -11
- package/src/api/subscriptions/schemas/entitlementsSchema.ts +0 -21
- package/src/api/subscriptions/schemas/mrrSchema.ts +0 -13
- package/src/api/subscriptions/schemas/planDefinitionSchema.ts +0 -71
- package/src/api/subscriptions/schemas/planResourceSchema.ts +0 -25
- package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +0 -8
- package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +0 -19
- package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +0 -6
- package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +0 -32
- package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +0 -23
- package/src/api/subscriptions/services/BillingService.ts +0 -437
- package/src/api/subscriptions/services/SubscriptionConfig.ts +0 -56
- package/src/api/subscriptions/services/SubscriptionService.ts +0 -867
- package/src/api/subscriptions/services/UsageService.ts +0 -118
- package/src/api/workflows/__tests__/$workflow.spec.ts +0 -616
- package/src/api/workflows/controllers/AdminWorkflowController.ts +0 -191
- package/src/api/workflows/entities/workflowExecutions.ts +0 -74
- package/src/api/workflows/entities/workflowStepExecutions.ts +0 -74
- package/src/api/workflows/entities/workflowStepLogs.ts +0 -13
- package/src/api/workflows/index.browser.ts +0 -22
- package/src/api/workflows/index.ts +0 -115
- package/src/api/workflows/jobs/WorkflowJobs.ts +0 -77
- package/src/api/workflows/primitives/$workflow.ts +0 -202
- package/src/api/workflows/providers/WorkflowProvider.ts +0 -1284
- package/src/api/workflows/schemas/workflowActivitySchema.ts +0 -15
- package/src/api/workflows/schemas/workflowConfigAtom.ts +0 -51
- package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +0 -18
- package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +0 -26
- package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +0 -30
- package/src/api/workflows/schemas/workflowRegistrationSchema.ts +0 -26
- package/src/api/workflows/schemas/workflowStatsSchema.ts +0 -16
- package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +0 -15
- package/src/api/workflows/services/WorkflowService.ts +0 -382
- package/src/cli/core/templates/apiAppSecurityTs.ts +0 -43
- package/src/cli/core/templates/webAdminDashboardTsx.ts +0 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/api/jobs/schemas/jobActivitySchema.ts","../../../src/api/jobs/schemas/jobCronInfoSchema.ts","../../../src/api/jobs/entities/jobExecutionEntity.ts","../../../src/api/jobs/schemas/jobExecutionResourceSchema.ts","../../../src/api/jobs/schemas/jobExecutionDetailResourceSchema.ts","../../../src/api/jobs/schemas/jobExecutionQuerySchema.ts","../../../src/api/jobs/schemas/jobFailureSchema.ts","../../../src/api/jobs/schemas/jobQueueDepthSchema.ts","../../../src/api/jobs/schemas/jobRegistrationSchema.ts","../../../src/api/jobs/schemas/jobStatsSchema.ts","../../../src/api/jobs/schemas/triggerJobSchema.ts","../../../src/api/jobs/entities/jobExecutionLogEntity.ts","../../../src/api/jobs/schemas/jobConfigAtom.ts","../../../src/api/jobs/providers/JobProvider.ts","../../../src/api/jobs/primitives/$job.ts","../../../src/api/jobs/services/JobService.ts","../../../src/api/jobs/controllers/AdminJobController.ts","../../../src/api/jobs/providers/JobQueueProvider.ts","../../../src/api/jobs/index.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\n\nexport const jobActivityPointSchema = t.object({\n date: t.text(),\n completed: t.integer(),\n failed: t.integer(),\n});\n\nexport type JobActivityPoint = Static<typeof jobActivityPointSchema>;\n\nexport const jobActivityQuerySchema = t.object({\n days: t.optional(t.integer({ minimum: 1, maximum: 90 })),\n});\n\nexport type JobActivityQuery = Static<typeof jobActivityQuerySchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const jobCronInfoSchema = t.object({\n name: t.text(),\n cron: t.text(),\n lock: t.boolean(),\n priority: t.enum([\"critical\", \"high\", \"normal\", \"low\"]),\n concurrency: t.integer(),\n hasSchema: t.boolean(),\n paused: t.boolean(),\n lastExecution: t.optional(\n t.object({\n id: t.uuid(),\n status: t.text(),\n startedAt: t.optional(t.datetime()),\n completedAt: t.optional(t.datetime()),\n error: t.optional(t.text()),\n }),\n ),\n});\n\nexport type JobCronInfo = Static<typeof jobCronInfoSchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const jobExecutionEntity = $entity({\n name: \"job_executions\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n\n jobName: t.text(),\n key: t.optional(t.nullable(t.text())),\n\n payload: t.optional(t.record(t.text(), t.any())),\n status: db.default(\n t.enum([\n \"pending\",\n \"scheduled\",\n \"retrying\",\n \"running\",\n \"completed\",\n \"dead\",\n \"cancelled\",\n ]),\n \"pending\",\n ),\n priority: db.default(t.integer({ minimum: 0, maximum: 3 }), 2),\n\n attempt: db.default(t.integer(), 0),\n maxAttempts: db.default(t.integer(), 1),\n\n scheduledAt: t.optional(t.datetime()),\n startedAt: t.optional(t.datetime()),\n completedAt: t.optional(t.datetime()),\n\n result: t.optional(t.record(t.text(), t.any())),\n error: t.optional(t.text()),\n workerId: t.optional(t.text()),\n\n triggeredBy: t.optional(t.text()),\n triggeredByName: t.optional(t.text()),\n cancelledBy: t.optional(t.text()),\n cancelledByName: t.optional(t.text()),\n }),\n indexes: [\n { columns: [\"jobName\", \"status\", \"priority\", \"scheduledAt\"] },\n { columns: [\"jobName\", \"status\", \"startedAt\"] },\n { columns: [\"jobName\", \"completedAt\"] },\n { columns: [\"jobName\", \"key\"], unique: true },\n ],\n});\n\nexport type JobExecutionEntity = Static<typeof jobExecutionEntity.schema>;\n\nexport type JobStatus =\n | \"pending\"\n | \"scheduled\"\n | \"retrying\"\n | \"running\"\n | \"completed\"\n | \"dead\"\n | \"cancelled\";\n","import { type Static, t } from \"alepha\";\nimport { jobExecutionEntity } from \"../entities/jobExecutionEntity.ts\";\n\nexport const jobExecutionCanSchema = t.object({\n retry: t.boolean(),\n cancel: t.boolean(),\n});\n\nexport const jobExecutionResourceSchema = t.extend(\n jobExecutionEntity.schema,\n {\n can: jobExecutionCanSchema,\n },\n {\n title: \"JobExecutionResource\",\n description: \"A job execution resource.\",\n },\n);\n\nexport type JobExecutionResource = Static<typeof jobExecutionResourceSchema>;\n","import { type Static, t } from \"alepha\";\nimport { logEntrySchema } from \"alepha/logger\";\nimport { jobExecutionEntity } from \"../entities/jobExecutionEntity.ts\";\nimport { jobExecutionCanSchema } from \"./jobExecutionResourceSchema.ts\";\n\nexport const jobExecutionDetailResourceSchema = t.extend(\n jobExecutionEntity.schema,\n {\n can: jobExecutionCanSchema,\n logs: t.optional(t.array(logEntrySchema)),\n },\n {\n title: \"JobExecutionDetailResource\",\n description: \"A job execution resource with logs.\",\n },\n);\n\nexport type JobExecutionDetailResource = Static<\n typeof jobExecutionDetailResourceSchema\n>;\n","import { type Static, t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const jobExecutionQuerySchema = t.extend(pageQuerySchema, {\n job: t.optional(\n t.text({\n description: \"Filter by job name\",\n }),\n ),\n status: t.optional(\n t.enum([\n \"pending\",\n \"scheduled\",\n \"retrying\",\n \"running\",\n \"completed\",\n \"dead\",\n \"cancelled\",\n ]),\n ),\n priority: t.optional(t.enum([\"critical\", \"high\", \"normal\", \"low\"])),\n from: t.optional(\n t.datetime({\n description: \"From date (ISO)\",\n }),\n ),\n to: t.optional(\n t.datetime({\n description: \"To date (ISO)\",\n }),\n ),\n});\n\nexport type JobExecutionQuery = Static<typeof jobExecutionQuerySchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const jobFailureSchema = t.object({\n jobName: t.text(),\n failures: t.integer(),\n lastError: t.optional(t.text()),\n});\n\nexport type JobFailure = Static<typeof jobFailureSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const jobQueueDepthSchema = t.object({\n jobName: t.text(),\n pending: t.integer(),\n running: t.integer(),\n scheduled: t.integer(),\n retrying: t.integer(),\n dead: t.integer(),\n concurrency: t.integer(),\n paused: t.boolean(),\n});\n\nexport type JobQueueDepth = Static<typeof jobQueueDepthSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const jobRegistrationSchema = t.object({\n name: t.text(),\n type: t.enum([\"cron\", \"push\", \"both\"]),\n priority: t.enum([\"critical\", \"high\", \"normal\", \"low\"]),\n concurrency: t.integer(),\n hasSchema: t.boolean(),\n cron: t.optional(t.text()),\n timeout: t.optional(t.text()),\n retry: t.optional(\n t.object({\n retries: t.integer(),\n hasBackoff: t.boolean(),\n }),\n ),\n paused: t.boolean(),\n});\n\nexport type JobRegistration = Static<typeof jobRegistrationSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const jobStatsSchema = t.object({\n registered: t.integer(),\n running: t.integer(),\n pending: t.integer(),\n scheduled: t.integer(),\n retrying: t.integer(),\n dead: t.integer(),\n completed: t.integer(),\n failed: t.integer(),\n});\n\nexport type JobStats = Static<typeof jobStatsSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const triggerJobSchema = t.object({\n name: t.text(),\n payload: t.optional(t.record(t.text(), t.any())),\n});\n\nexport type TriggerJob = Static<typeof triggerJobSchema>;\n","import { type Static, t } from \"alepha\";\nimport { logEntrySchema } from \"alepha/logger\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const jobExecutionLogEntity = $entity({\n name: \"job_execution_logs\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n logs: t.array(logEntrySchema),\n }),\n});\n\nexport type JobExecutionLogEntity = Static<typeof jobExecutionLogEntity.schema>;\n","import { $atom, type Static, t } from \"alepha\";\n\nexport const jobConfig = $atom({\n name: \"alepha.jobs\",\n description: \"Configuration for the $job v2 primitive.\",\n schema: t.object({\n recovery: t.object({\n interval: t.integer({ description: \"Sweep interval (ms).\" }),\n staleThreshold: t.integer({\n description: \"Pending age (ms) before re-dispatch.\",\n }),\n runTimeout: t.integer({\n description:\n \"Running age (ms) before assumed crash. Used as fallback when no per-job timeout is set.\",\n }),\n }),\n delayed: t.object({\n interval: t.integer({ description: \"Sweep interval (ms).\" }),\n }),\n logRetentionDays: t.integer({\n description: \"Days to keep completed/dead executions.\",\n }),\n logMaxEntries: t.integer({\n description: \"Max log entries captured per execution.\",\n }),\n drainTimeout: t.integer({\n description: \"Max time (ms) to wait for in-flight jobs during shutdown.\",\n }),\n }),\n default: {\n recovery: {\n interval: 300_000,\n staleThreshold: 300_000,\n runTimeout: 1_800_000,\n },\n delayed: {\n interval: 300_000,\n },\n logRetentionDays: 30,\n logMaxEntries: 100,\n drainTimeout: 30_000,\n },\n});\n\nexport type JobConfig = Static<typeof jobConfig.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [jobConfig.key]: JobConfig;\n }\n}\n","import {\n $hook,\n $inject,\n $state,\n Alepha,\n AlephaError,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { LockProvider } from \"alepha/lock\";\nimport type { LogEntry } from \"alepha/logger\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { CronProvider } from \"alepha/scheduler\";\nimport {\n type JobStatus,\n jobExecutionEntity,\n} from \"../entities/jobExecutionEntity.ts\";\nimport { jobExecutionLogEntity } from \"../entities/jobExecutionLogEntity.ts\";\nimport type {\n JobItem,\n JobPrimitiveOptions,\n JobPriority,\n JobRetryBackoff,\n JobRetryOptions,\n} from \"../primitives/$job.ts\";\nimport { jobConfig } from \"../schemas/jobConfigAtom.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\nconst PRIORITY_MAP: Record<string, number> = {\n critical: 0,\n high: 1,\n normal: 2,\n low: 3,\n};\n\nconst PRIORITY_REVERSE: Record<number, JobPriority> = {\n 0: \"critical\",\n 1: \"high\",\n 2: \"normal\",\n 3: \"low\",\n};\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport interface PushOptions {\n delay?: DurationLike;\n key?: string;\n priority?: JobPriority;\n scheduledAt?: Date;\n}\n\nexport interface PushManyItem<T extends TSchema = TSchema> {\n payload: Static<T>;\n key?: string;\n delay?: DurationLike;\n priority?: JobPriority;\n scheduledAt?: Date;\n}\n\nexport interface JobTriggerContext {\n payload?: Record<string, unknown>;\n triggeredBy?: string;\n triggeredByName?: string;\n}\n\nexport interface CancelContext {\n cancelledBy?: string;\n cancelledByName?: string;\n}\n\ninterface JobRegistration {\n name: string;\n options: JobPrimitiveOptions;\n}\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport class JobProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dt = $inject(DateTimeProvider);\n protected readonly cronProvider = $inject(CronProvider);\n protected readonly lockProvider = $inject(LockProvider);\n protected readonly config = $state(jobConfig);\n protected readonly log = $logger();\n protected readonly executions = $repository(jobExecutionEntity);\n protected readonly executionLogs = $repository(jobExecutionLogEntity);\n\n protected readonly jobs = new Map<string, JobRegistration>();\n protected readonly pausedJobs = new Set<string>();\n protected readonly inFlight = new Set<Promise<void>>();\n\n /**\n * When set, job executions are dispatched through a queue (e.g. `JobQueueProvider`).\n * When null, jobs execute inline (fire-and-forget). Useful for serverless environments.\n */\n public queueDispatch:\n | ((jobName: string, executionId: string) => Promise<void>)\n | null = null;\n protected readonly logs = new Map<string, LogEntry[]>();\n protected readonly abortControllers = new Map<string, AbortController>();\n protected static readonly SWEEP_CRON = \"*/5 * * * *\";\n protected stopping = false;\n protected workerId = \"\";\n\n // --- Registration ---\n\n public registerJob(name: string, options: JobPrimitiveOptions): void {\n if (this.jobs.has(name)) {\n throw new AlephaError(`Job already registered: ${name}`);\n }\n\n this.jobs.set(name, { name, options });\n this.log.debug(`Registered job '${name}'`, {\n cron: options.cron,\n priority: options.priority ?? \"normal\",\n retries: options.retry?.retries ?? 0,\n });\n\n if (options.cron) {\n this.cronProvider.createCronJob(name, options.cron, async () => {\n try {\n await this.trigger(name, {\n triggeredBy: \"system\",\n triggeredByName: \"system (cron)\",\n });\n } catch (error) {\n this.log.error(`Cron trigger failed for job '${name}'`, error);\n }\n });\n }\n }\n\n /**\n * Get all registered job definitions.\n */\n public getRegisteredJobs(): Map<string, JobRegistration> {\n return this.jobs;\n }\n\n // --- Push ---\n\n public async push(\n name: string,\n payload: unknown,\n options?: PushOptions,\n ): Promise<string> {\n const registration = this.getRegistration(name);\n const opts = registration.options;\n\n if (!opts.schema) {\n throw new AlephaError(\n `Cannot push to job '${name}': no schema defined. Use trigger() for cron-only jobs.`,\n );\n }\n\n const validated = this.alepha.codec.validate(opts.schema, payload);\n\n const priority =\n PRIORITY_MAP[options?.priority ?? opts.priority ?? \"normal\"];\n const maxAttempts = (opts.retry?.retries ?? 0) + 1;\n\n const isDelayed = options?.delay || options?.scheduledAt;\n const status: JobStatus = isDelayed ? \"scheduled\" : \"pending\";\n\n let scheduledAt: string | undefined;\n if (options?.scheduledAt) {\n scheduledAt = options.scheduledAt.toISOString();\n } else if (options?.delay) {\n const now = this.dt.now();\n scheduledAt = now.add(this.dt.duration(options.delay)).toISOString();\n }\n\n // Keyed path: atomic upsert to avoid race between concurrent pushes\n if (options?.key) {\n const now = this.dt.nowISOString();\n const execution = await this.executions.upsert(\n {\n jobName: name,\n key: options.key,\n payload: validated as Record<string, unknown>,\n status,\n priority,\n maxAttempts,\n scheduledAt,\n createdAt: now,\n updatedAt: now,\n },\n { target: [\"jobName\", \"key\"], set: {}, now },\n );\n\n // Fresh insert: both timestamps equal the explicit `now` value.\n // Conflict: updatedAt was bumped by the ON CONFLICT SET clause, so they differ.\n if (\n execution.createdAt === execution.updatedAt &&\n status === \"pending\" &&\n !this.stopping\n ) {\n await this.scheduleProcessing(name, execution.id);\n }\n\n return execution.id;\n }\n\n const execution = await this.executions.create({\n jobName: name,\n payload: validated as Record<string, unknown>,\n status,\n priority,\n maxAttempts,\n scheduledAt,\n });\n\n this.log.debug(`Pushed job '${name}'`, {\n executionId: execution.id,\n status,\n priority: PRIORITY_REVERSE[priority],\n });\n\n // Dispatch to processing if immediate\n if (status === \"pending\" && !this.stopping) {\n await this.scheduleProcessing(name, execution.id);\n }\n\n return execution.id;\n }\n\n public async pushMany(\n name: string,\n items: Array<PushManyItem>,\n ): Promise<string[]> {\n if (items.length === 0) return [];\n\n const registration = this.getRegistration(name);\n const opts = registration.options;\n\n if (!opts.schema) {\n throw new AlephaError(\n `Cannot push to job '${name}': no schema defined. Use trigger() for cron-only jobs.`,\n );\n }\n\n const maxAttempts = (opts.retry?.retries ?? 0) + 1;\n\n // Keyed items need upsert logic — fall back to individual push\n const keyed: PushManyItem[] = [];\n const bulkRows: Array<{\n jobName: string;\n payload: Record<string, unknown>;\n status: JobStatus;\n priority: number;\n maxAttempts: number;\n scheduledAt?: string;\n }> = [];\n\n for (const item of items) {\n const validated = this.alepha.codec.validate(opts.schema, item.payload);\n if (item.key) {\n keyed.push({ ...item, payload: validated as Static<TSchema> });\n } else {\n const isDelayed = item.delay || item.scheduledAt;\n const status: JobStatus = isDelayed ? \"scheduled\" : \"pending\";\n let scheduledAt: string | undefined;\n if (item.scheduledAt) {\n scheduledAt = item.scheduledAt.toISOString();\n } else if (item.delay) {\n scheduledAt = this.dt\n .now()\n .add(this.dt.duration(item.delay))\n .toISOString();\n }\n bulkRows.push({\n jobName: name,\n payload: validated as Record<string, unknown>,\n status,\n priority: PRIORITY_MAP[item.priority ?? opts.priority ?? \"normal\"],\n maxAttempts,\n scheduledAt,\n });\n }\n }\n\n const ids: string[] = [];\n\n // Keyed: sequential upserts\n for (const item of keyed) {\n const id = await this.push(name, item.payload, {\n key: item.key,\n delay: item.delay,\n priority: item.priority,\n scheduledAt: item.scheduledAt,\n });\n ids.push(id);\n }\n\n // Non-keyed: single bulk insert\n if (bulkRows.length > 0) {\n const created = await this.executions.createMany(bulkRows);\n for (const exec of created) {\n ids.push(exec.id);\n if (exec.status === \"pending\" && !this.stopping) {\n await this.scheduleProcessing(name, exec.id);\n }\n }\n }\n\n this.log.debug(`pushMany '${name}': ${ids.length} jobs created`, {\n bulk: bulkRows.length,\n keyed: keyed.length,\n });\n\n return ids;\n }\n\n // --- Trigger (manual / cron) ---\n\n public async trigger(\n name: string,\n context?: JobTriggerContext,\n ): Promise<void> {\n const registration = this.getRegistration(name);\n const opts = registration.options;\n\n if (context?.payload && opts.schema) {\n // Push-based trigger with payload\n const id = await this.push(name, context.payload, {});\n // Update trigger info\n await this.executions.updateById(id, {\n triggeredBy: context?.triggeredBy,\n triggeredByName: context?.triggeredByName,\n });\n return;\n }\n\n // Cron-style or manual trigger without payload\n const maxAttempts = (opts.retry?.retries ?? 0) + 1;\n const priority = PRIORITY_MAP[opts.priority ?? \"normal\"];\n\n const execution = await this.executions.create({\n jobName: name,\n status: \"pending\",\n priority,\n maxAttempts,\n triggeredBy: context?.triggeredBy,\n triggeredByName: context?.triggeredByName,\n });\n\n this.log.debug(`Triggered job '${name}'`, {\n executionId: execution.id,\n triggeredBy: context?.triggeredByName ?? context?.triggeredBy,\n });\n\n if (!this.stopping) {\n await this.scheduleProcessing(name, execution.id);\n }\n }\n\n // --- Cancel ---\n\n public async cancel(\n executionId: string,\n context?: CancelContext,\n ): Promise<void> {\n const execution = await this.executions.findById(executionId);\n if (!execution) {\n throw new AlephaError(`Execution not found: ${executionId}`);\n }\n\n if (\n execution.status === \"completed\" ||\n execution.status === \"dead\" ||\n execution.status === \"cancelled\"\n ) {\n throw new AlephaError(\n `Cannot cancel execution in '${execution.status}' status`,\n );\n }\n\n // If running, trigger the AbortSignal\n const controller = this.abortControllers.get(executionId);\n if (controller) {\n controller.abort();\n }\n\n await this.executions.updateById(executionId, {\n status: \"cancelled\",\n key: null,\n cancelledBy: context?.cancelledBy,\n cancelledByName: context?.cancelledByName,\n completedAt: this.dt.nowISOString(),\n });\n\n this.log.info(`Cancelled execution ${executionId}`, {\n jobName: execution.jobName,\n cancelledBy: context?.cancelledByName ?? context?.cancelledBy,\n });\n }\n\n // --- Execution ---\n\n protected async scheduleProcessing(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n if (this.pausedJobs.has(jobName)) {\n this.log.debug(`Job '${jobName}' is paused, deferring`, { executionId });\n return;\n }\n\n const registration = this.getRegistration(jobName);\n const maxConcurrency = registration.options.concurrency ?? 1;\n const runningCount = await this.executions.count({\n jobName: { eq: jobName },\n status: { eq: \"running\" },\n });\n if (runningCount >= maxConcurrency) {\n this.log.debug(\n `Job '${jobName}' at concurrency limit (${runningCount}/${maxConcurrency}), deferring`,\n { executionId },\n );\n return;\n }\n\n if (this.queueDispatch) {\n this.log.debug(`Dispatching job '${jobName}' via queue`, { executionId });\n await this.queueDispatch(jobName, executionId);\n } else {\n this.log.debug(`Executing job '${jobName}' inline`, { executionId });\n await this.processExecution(jobName, executionId);\n }\n }\n\n public async processExecution(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n const promise = this.processExecutionInner(jobName, executionId);\n this.inFlight.add(promise);\n try {\n await promise;\n } finally {\n this.inFlight.delete(promise);\n }\n }\n\n protected async processExecutionInner(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n const registration = this.getRegistration(jobName);\n\n // Claim the execution atomically\n const claimed = await this.claim(executionId);\n if (!claimed) {\n this.log.debug(`Execution ${executionId} already claimed, skipping`);\n return;\n }\n\n const context = this.alepha.context.createContextId();\n this.logs.set(context, []);\n\n this.log.debug(`Started processing job '${jobName}'`, { executionId });\n\n try {\n await this.alepha.context.run(\n async () => {\n // Create AbortController for timeout + cancellation\n const abortController = new AbortController();\n this.abortControllers.set(executionId, abortController);\n\n // Set up timeout if configured\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const opts = registration.options;\n if (opts.timeout) {\n const ms = this.dt.duration(opts.timeout).as(\"milliseconds\");\n timeoutId = setTimeout(() => abortController.abort(), ms);\n }\n\n const now = this.dt.now();\n\n await this.alepha.events.emit(\"job:begin\", {\n name: jobName,\n now,\n executionId,\n });\n\n try {\n // Build items array\n const execution = await this.executions.findById(executionId);\n const items: Array<JobItem> = [];\n if (execution?.payload) {\n items.push({\n id: executionId,\n payload: execution.payload,\n attempt: execution.attempt,\n });\n }\n\n // Execute handler\n this.log.debug(`Running job '${jobName}'`, {\n executionId,\n attempt: execution?.attempt,\n items: items.length,\n });\n\n await opts.handler({\n items,\n now,\n signal: abortController.signal,\n });\n\n // Success\n await this.executions.updateById(executionId, {\n status: \"completed\",\n completedAt: this.dt.nowISOString(),\n key: null,\n });\n\n this.log.info(`Job '${jobName}' completed`, { executionId });\n\n // Write logs to cold table\n await this.writeLogs(executionId, context);\n\n await this.alepha.events.emit(\n \"job:success\",\n { name: jobName, executionId },\n { catch: true },\n );\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(String(error));\n\n // Check if this was a cancellation\n if (abortController.signal.aborted) {\n // Already marked as cancelled by cancel() or it's a timeout\n const currentExecution =\n await this.executions.findById(executionId);\n if (currentExecution?.status !== \"cancelled\") {\n // Timeout — treat as failure\n await this.handleFailure(executionId, jobName, err, context);\n } else {\n // Was cancelled explicitly — just write logs\n await this.writeLogs(executionId, context);\n await this.alepha.events.emit(\n \"job:cancel\",\n { name: jobName, executionId },\n { catch: true },\n );\n }\n } else {\n await this.handleFailure(executionId, jobName, err, context);\n }\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n this.abortControllers.delete(executionId);\n\n await this.alepha.events.emit(\n \"job:end\",\n { name: jobName, executionId },\n { catch: true },\n );\n\n // A slot just opened — dispatch next pending job if any\n await this.dispatchNextPending(jobName);\n }\n },\n { context },\n );\n } finally {\n this.logs.delete(context);\n }\n }\n\n /**\n * After a job finishes (success, failure, or cancel), dispatch any pending\n * jobs that were deferred due to the concurrency limit.\n */\n protected async dispatchNextPending(jobName: string): Promise<void> {\n if (this.stopping || this.pausedJobs.has(jobName)) return;\n\n const registration = this.jobs.get(jobName);\n if (!registration) return;\n\n const maxConcurrency = registration.options.concurrency ?? 1;\n const runningCount = await this.executions.count({\n jobName: { eq: jobName },\n status: { eq: \"running\" },\n });\n\n const available = maxConcurrency - runningCount;\n if (available <= 0) return;\n\n const pending = await this.executions.findMany({\n where: { jobName: { eq: jobName }, status: { eq: \"pending\" } },\n orderBy: { column: \"priority\", direction: \"asc\" },\n limit: available,\n });\n\n for (const exec of pending) {\n await this.scheduleProcessing(jobName, exec.id);\n }\n }\n\n protected async claim(executionId: string): Promise<boolean> {\n const execution = await this.executions.findById(executionId);\n if (!execution) return false;\n\n try {\n await this.executions.updateOne(\n { id: { eq: executionId }, status: { eq: \"pending\" } },\n {\n status: \"running\",\n attempt: execution.attempt + 1,\n startedAt: this.dt.nowISOString(),\n workerId: this.workerId,\n },\n );\n return true;\n } catch {\n return false;\n }\n }\n\n protected async handleFailure(\n executionId: string,\n jobName: string,\n error: Error,\n context: string,\n ): Promise<void> {\n const execution = await this.executions.findById(executionId);\n if (!execution) return;\n\n const registration = this.getRegistration(jobName);\n const opts = registration.options;\n const retryOpts = opts.retry;\n\n const canRetry =\n retryOpts &&\n execution.attempt < execution.maxAttempts &&\n (retryOpts.when ? retryOpts.when(error) : true);\n\n if (canRetry) {\n // Compute next scheduledAt from backoff\n const nextScheduledAt = this.computeBackoff(retryOpts, execution.attempt);\n\n this.log.info(\n `Job '${jobName}' failed, scheduling retry ${execution.attempt}/${execution.maxAttempts}`,\n { executionId, error: error.message, nextScheduledAt },\n );\n\n await this.executions.updateById(executionId, {\n status: \"retrying\",\n error: error.message,\n scheduledAt: nextScheduledAt,\n });\n\n await this.writeLogs(executionId, context);\n\n // Optimistic dispatch: schedule a timeout for the exact backoff delay.\n // The delayed dispatch sweep is the safety net in case of crash.\n const delayMs = Math.max(\n 0,\n new Date(nextScheduledAt).getTime() - this.dt.nowMillis(),\n );\n this.dt.createTimeout(\n () => void this.dispatchRetrying(jobName, executionId),\n delayMs,\n );\n } else {\n // Dead — all retries exhausted or predicate returned false\n this.log.info(\n `Job '${jobName}' is dead after ${execution.attempt} attempt(s)`,\n { executionId, error: error.message },\n );\n\n await this.executions.updateById(executionId, {\n status: \"dead\",\n error: error.message,\n completedAt: this.dt.nowISOString(),\n key: null,\n });\n\n await this.writeLogs(executionId, context);\n }\n\n await this.alepha.events.emit(\n \"job:error\",\n { name: jobName, error, executionId },\n { catch: true },\n );\n }\n\n protected computeBackoff(\n retryOpts: JobRetryOptions,\n attempt: number,\n ): string {\n const now = this.dt.now();\n\n if (!retryOpts.backoff) {\n // Default: 1 second fixed\n return now.add(1, \"second\").toISOString();\n }\n\n // Fixed backoff shorthand: [5, \"second\"]\n if (Array.isArray(retryOpts.backoff)) {\n const delay = this.dt.duration(retryOpts.backoff);\n return now.add(delay).toISOString();\n }\n\n // Exponential backoff\n const backoff = retryOpts.backoff as JobRetryBackoff;\n const initial = this.dt.duration(backoff.initial).as(\"milliseconds\");\n const factor = backoff.factor ?? 2;\n let delayMs = initial * factor ** (attempt - 1);\n\n if (backoff.max) {\n const maxMs = this.dt.duration(backoff.max).as(\"milliseconds\");\n delayMs = Math.min(delayMs, maxMs);\n }\n\n if (backoff.jitter) {\n // Add up to 25% random jitter\n delayMs = delayMs * (0.75 + Math.random() * 0.5);\n }\n\n return now.add(delayMs, \"millisecond\").toISOString();\n }\n\n protected async writeLogs(\n executionId: string,\n context: string,\n ): Promise<void> {\n const entries = this.logs.get(context);\n if (!entries || entries.length === 0) return;\n\n const maxEntries = this.config.logMaxEntries;\n if (maxEntries === 0) return;\n\n let logs = entries;\n if (logs.length > maxEntries) {\n logs = logs.slice(0, maxEntries);\n logs.push({\n level: \"WARN\",\n message: `Log entries truncated at ${maxEntries}`,\n timestamp: this.dt.nowMillis(),\n service: \"alepha.jobs\",\n module: \"JobProvider\",\n } as LogEntry);\n }\n\n try {\n await this.executionLogs.create({\n id: executionId,\n logs,\n });\n } catch {\n // Log write failure is not critical\n this.log.warn(`Failed to write logs for execution ${executionId}`);\n }\n }\n\n protected async dispatchRetrying(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n if (this.stopping) return;\n try {\n await this.executions.updateOne(\n { id: { eq: executionId }, status: { eq: \"retrying\" } },\n { status: \"pending\" },\n );\n await this.scheduleProcessing(jobName, executionId);\n } catch {\n // Already transitioned by another worker or sweep\n }\n }\n\n // --- Internal system sweeps (Section 5 of spec) ---\n\n /**\n * Recovery Sweep (Section 5.1)\n *\n * Runs every `recovery.interval` (default: 1 minute).\n * - Stale `pending` jobs older than `staleThreshold` → re-dispatch.\n * - Crashed `running` jobs older than `max(job.timeout * 2, recovery.runTimeout)` → mark failed, apply retry policy.\n */\n protected async recoverySweep(): Promise<void> {\n this.log.trace(\"Starting recovery sweep\");\n if (this.stopping) return;\n\n const acquired = await this.tryLock(\"_alepha:jobs:recovery-lock\", 300_000);\n if (!acquired) return;\n\n try {\n const now = this.dt.now();\n\n // 1. Stale pending jobs (priority-ordered)\n const staleThreshold = now\n .subtract(this.config.recovery.staleThreshold, \"millisecond\")\n .toISOString();\n\n const pendingWhere = this.executions.createQueryWhere();\n pendingWhere.status = { eq: \"pending\" };\n pendingWhere.createdAt = { lte: staleThreshold };\n\n const stalePending = await this.executions.findMany({\n where: pendingWhere,\n orderBy: { column: \"priority\", direction: \"asc\" },\n });\n\n for (const exec of stalePending) {\n if (!this.jobs.has(exec.jobName)) continue;\n this.log.debug(\n `Recovery sweep: re-dispatching stale pending job ${exec.jobName} (${exec.id})`,\n );\n await this.scheduleProcessing(exec.jobName, exec.id);\n }\n\n // 2. Crashed running jobs\n const runningWhere = this.executions.createQueryWhere();\n runningWhere.status = { eq: \"running\" };\n\n const running = await this.executions.findMany({ where: runningWhere });\n const nowMs = now.valueOf();\n\n for (const exec of running) {\n const registration = this.jobs.get(exec.jobName);\n if (!registration) continue;\n\n // If this worker owns it and has an active AbortController, skip (still alive)\n if (this.abortControllers.has(exec.id)) continue;\n\n const opts = registration.options;\n let crashThresholdMs: number;\n if (opts.timeout) {\n crashThresholdMs =\n this.dt.duration(opts.timeout).as(\"milliseconds\") * 2;\n } else {\n crashThresholdMs = this.config.recovery.runTimeout;\n }\n\n const startedAt = exec.startedAt\n ? new Date(exec.startedAt).getTime()\n : 0;\n if (startedAt > 0 && nowMs - startedAt > crashThresholdMs) {\n this.log.warn(\n `Recovery sweep: marking crashed job ${exec.jobName} (${exec.id}) as failed`,\n );\n const error = new Error(\n \"Execution assumed crashed (recovered by sweep)\",\n );\n await this.handleFailure(exec.id, exec.jobName, error, \"\");\n }\n }\n } catch (e) {\n this.log.error(\"Recovery sweep failed\", { error: e });\n } finally {\n await this.releaseLock(\"_alepha:jobs:recovery-lock\");\n }\n }\n\n /**\n * Delayed Dispatch Sweep (Section 5.2)\n *\n * Runs every `delayed.interval` (default: 30 seconds).\n * Scans for `scheduled` and `retrying` jobs where `scheduledAt <= now`,\n * moves them to `pending`, and dispatches to the queue layer.\n */\n protected async delayedDispatchSweep(): Promise<void> {\n this.log.trace(\"Starting delayed dispatch sweep\");\n if (this.stopping) return;\n\n const acquired = await this.tryLock(\"_alepha:jobs:dispatch-lock\", 60_000);\n if (!acquired) return;\n\n try {\n const now = this.dt.nowISOString();\n\n const where = this.executions.createQueryWhere();\n where.status = { inArray: [\"scheduled\", \"retrying\"] };\n where.scheduledAt = { lte: now };\n\n const ready = await this.executions.findMany({\n where,\n orderBy: { column: \"priority\", direction: \"asc\" },\n });\n\n for (const exec of ready) {\n if (!this.jobs.has(exec.jobName)) continue;\n await this.executions.updateById(exec.id, { status: \"pending\" });\n await this.scheduleProcessing(exec.jobName, exec.id);\n }\n } catch (e) {\n this.log.error(\"Delayed dispatch sweep failed\", { error: e });\n } finally {\n await this.releaseLock(\"_alepha:jobs:dispatch-lock\");\n }\n }\n\n /**\n * Log Purge (Section 5.3)\n *\n * Runs daily at 03:00 via cron.\n * Deletes completed/dead/cancelled execution records older than `logRetentionDays`.\n */\n protected async logPurge(): Promise<void> {\n if (this.stopping) return;\n try {\n const cutoff = this.dt\n .now()\n .subtract(this.config.logRetentionDays, \"day\")\n .toISOString();\n\n const where = this.executions.createQueryWhere();\n where.status = { inArray: [\"completed\", \"dead\", \"cancelled\"] };\n where.completedAt = { lte: cutoff };\n\n // Bulk-delete logs first (FK-safe), then executions\n const expiredIds = await this.executions.findMany({\n where,\n columns: [\"id\"] as any,\n });\n if (expiredIds.length > 0) {\n const ids = expiredIds.map((e) => e.id);\n await this.executionLogs.deleteMany({ id: { inArray: ids } });\n await this.executions.deleteMany({ id: { inArray: ids } });\n this.log.info(`Log purge: deleted ${ids.length} old execution records`);\n }\n } catch (e) {\n this.log.error(\"Log purge failed\", { error: e });\n }\n }\n\n // --- Pause / Resume ---\n\n public pauseJob(name: string): void {\n this.getRegistration(name);\n this.pausedJobs.add(name);\n this.log.info(`Paused job '${name}'`);\n }\n\n public async resumeJob(name: string): Promise<void> {\n this.getRegistration(name);\n this.pausedJobs.delete(name);\n this.log.info(`Resumed job '${name}'`);\n\n // Dispatch any pending items for this job\n const pending = await this.executions.findMany({\n where: { jobName: { eq: name }, status: { eq: \"pending\" } },\n orderBy: { column: \"priority\", direction: \"asc\" },\n });\n for (const exec of pending) {\n await this.scheduleProcessing(name, exec.id);\n }\n }\n\n public isJobPaused(name: string): boolean {\n return this.pausedJobs.has(name);\n }\n\n public getPausedJobs(): string[] {\n return [...this.pausedJobs];\n }\n\n // --- Lock helpers ---\n\n protected async tryLock(key: string, ttlMs: number): Promise<boolean> {\n const lockValue = `${this.workerId},${this.dt.nowISOString()}`;\n const result = await this.lockProvider.set(key, lockValue, true, ttlMs);\n const [lockId] = result.split(\",\");\n return lockId === this.workerId;\n }\n\n protected async releaseLock(key: string): Promise<void> {\n await this.lockProvider.del(key);\n }\n\n // --- Lifecycle hooks ---\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n this.workerId = crypto.randomUUID().slice(0, 12);\n this.log.info(`Job system OK`, {\n workerId: this.workerId,\n dispatch: this.queueDispatch ? \"queue\" : \"inline\",\n });\n\n // Set up log capture listener (once)\n this.alepha.events.on(\"log\", ({ entry }) => {\n const ctx = entry.context;\n if (!ctx) return;\n const entries = this.logs.get(ctx);\n if (!entries) return;\n entries.push(entry);\n });\n\n // Run initial sweeps to recover from previous crashes.\n // Skipped on serverless — cron triggers handle periodic sweeps instead.\n if (!this.alepha.isServerless()) {\n await this.delayedDispatchSweep();\n await this.recoverySweep();\n }\n\n // Periodic sweeps via cron (works in serverless environments like Cloudflare Workers)\n this.cronProvider.createCronJob(\n \"_alepha:jobs:recovery\",\n JobProvider.SWEEP_CRON,\n async () => {\n await this.recoverySweep();\n },\n true,\n );\n this.cronProvider.createCronJob(\n \"_alepha:jobs:dispatch\",\n JobProvider.SWEEP_CRON,\n async () => {\n await this.delayedDispatchSweep();\n },\n true,\n );\n\n // Daily log purge\n this.cronProvider.createCronJob(\n \"_alepha:jobs:log-purge\",\n \"0 0 * * *\",\n async () => {\n await this.logPurge();\n },\n true,\n );\n },\n });\n\n protected readonly onStop = $hook({\n on: \"stop\",\n handler: async () => {\n this.stopping = true;\n\n // Drain: wait for in-flight jobs to finish before aborting\n if (this.inFlight.size > 0) {\n this.log.info(`Draining ${this.inFlight.size} in-flight job(s)...`);\n await Promise.race([\n Promise.allSettled([...this.inFlight]),\n this.dt.wait([this.config.drainTimeout, \"millisecond\"]),\n ]);\n }\n\n // Abort any still-running executions after drain timeout\n if (this.abortControllers.size > 0) {\n this.log.warn(\n `Aborting ${this.abortControllers.size} remaining job(s) after drain timeout`,\n );\n for (const controller of this.abortControllers.values()) {\n controller.abort();\n }\n }\n },\n });\n\n // --- Helpers ---\n\n protected getRegistration(name: string): JobRegistration {\n const registration = this.jobs.get(name);\n if (!registration) {\n throw new AlephaError(`Job not registered: ${name}`);\n }\n return registration;\n }\n}\n","import {\n $inject,\n type Async,\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DateTime, DurationLike } from \"alepha/datetime\";\nimport {\n JobProvider,\n type JobTriggerContext,\n type PushManyItem,\n type PushOptions,\n} from \"../providers/JobProvider.ts\";\n\n/**\n * Job primitive for defining scheduled and on-demand tasks with payload validation and retry policies.\n */\nexport const $job = <T extends TSchema = TSchema>(\n options: JobPrimitiveOptions<T>,\n): JobPrimitive<T> => {\n return createPrimitive(JobPrimitive<T>, options);\n};\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport interface JobItem<T extends TSchema = TSchema> {\n id: string;\n payload: Static<T>;\n attempt: number;\n}\n\nexport interface JobHandlerArgs<T extends TSchema = TSchema> {\n items: Array<JobItem<T>>;\n now: DateTime;\n signal: AbortSignal;\n}\n\nexport interface JobRetryBackoff {\n initial: DurationLike;\n factor?: number;\n max?: DurationLike;\n jitter?: boolean;\n}\n\nexport interface JobRetryOptions {\n retries: number;\n backoff?: DurationLike | JobRetryBackoff;\n when?: (error: Error) => boolean;\n}\n\nexport type JobPriority = \"critical\" | \"high\" | \"normal\" | \"low\";\n\nexport interface JobPrimitiveOptions<T extends TSchema = TSchema>\n extends PipelinePrimitiveOptions {\n /**\n * Payload schema (TypeBox). Optional for cron-only jobs.\n */\n schema?: T;\n\n /**\n * Cron expression for automatic scheduling.\n */\n cron?: string;\n\n /**\n * Whether to use a distributed lock for cron execution.\n * @default true\n */\n lock?: boolean;\n\n /**\n * Retry policy for failed executions.\n */\n retry?: JobRetryOptions;\n\n /**\n * Max execution time per attempt.\n */\n timeout?: DurationLike;\n\n /**\n * Max parallel executions.\n * @default 1\n */\n concurrency?: number;\n\n /**\n * Default priority for pushed jobs.\n * @default \"normal\"\n */\n priority?: JobPriority;\n\n /**\n * Handler function for job execution.\n */\n handler: (args: JobHandlerArgs<T>) => Async<void>;\n}\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport class JobPrimitive<\n T extends TSchema = TSchema,\n> extends PipelinePrimitive<JobPrimitiveOptions<T>> {\n protected readonly jobProvider = $inject(JobProvider);\n\n public get name(): string {\n return `${this.config.service.name}.${this.config.propertyKey}`;\n }\n\n protected onInit() {\n const handler = this.handler.run.bind(this.handler);\n this.jobProvider.registerJob(this.name, { ...this.options, handler });\n }\n\n /**\n * Push a single payload or an array of payloads.\n */\n public async push(\n payload: Static<T> | Array<Static<T>>,\n options?: PushOptions,\n ): Promise<string | string[]> {\n if (Array.isArray(payload)) {\n const ids = await Promise.all(\n payload.map((p) => this.jobProvider.push(this.name, p, options)),\n );\n return ids;\n }\n return this.jobProvider.push(this.name, payload, options);\n }\n\n /**\n * Push multiple payloads with per-item options.\n */\n public async pushMany(items: Array<PushManyItem<T>>): Promise<string[]> {\n return this.jobProvider.pushMany(this.name, items);\n }\n\n /**\n * Cancel a running or pending execution.\n */\n public async cancel(executionId: string): Promise<void> {\n return this.jobProvider.cancel(executionId);\n }\n\n /**\n * Manually trigger the job (admin / CLI).\n */\n public async trigger(context?: JobTriggerContext): Promise<void> {\n return this.jobProvider.trigger(this.name, context);\n }\n\n /**\n * Pause this job. Pushed items are still accepted but processing is held.\n */\n public pause(): void {\n this.jobProvider.pauseJob(this.name);\n }\n\n /**\n * Resume a paused job and dispatch any pending items.\n */\n public async resume(): Promise<void> {\n return this.jobProvider.resumeJob(this.name);\n }\n\n /**\n * Whether this job is currently paused.\n */\n public get paused(): boolean {\n return this.jobProvider.isJobPaused(this.name);\n }\n}\n\n$job[KIND] = JobPrimitive;\n","import { $inject, Alepha, AlephaError, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, DatabaseProvider, sql } from \"alepha/orm\";\nimport { NotFoundError } from \"alepha/server\";\nimport type { JobExecutionEntity } from \"../entities/jobExecutionEntity.ts\";\nimport { jobExecutionEntity } from \"../entities/jobExecutionEntity.ts\";\nimport { jobExecutionLogEntity } from \"../entities/jobExecutionLogEntity.ts\";\nimport { $job } from \"../primitives/$job.ts\";\nimport type { JobTriggerContext } from \"../providers/JobProvider.ts\";\nimport { JobProvider } from \"../providers/JobProvider.ts\";\nimport type { JobActivityPoint } from \"../schemas/jobActivitySchema.ts\";\nimport type { JobCronInfo } from \"../schemas/jobCronInfoSchema.ts\";\nimport type { JobExecutionQuery } from \"../schemas/jobExecutionQuerySchema.ts\";\nimport type { JobFailure } from \"../schemas/jobFailureSchema.ts\";\nimport type { JobQueueDepth } from \"../schemas/jobQueueDepthSchema.ts\";\nimport type { JobRegistration } from \"../schemas/jobRegistrationSchema.ts\";\nimport type { JobStats } from \"../schemas/jobStatsSchema.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport class JobService {\n protected readonly alepha = $inject(Alepha);\n protected readonly dt = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly jobProvider = $inject(JobProvider);\n protected readonly database = $inject(DatabaseProvider);\n protected readonly executions = $repository(jobExecutionEntity);\n protected readonly executionLogs = $repository(jobExecutionLogEntity);\n\n protected computeCan(status: string) {\n return {\n retry: status === \"dead\" || status === \"cancelled\",\n cancel:\n status === \"pending\" ||\n status === \"running\" ||\n status === \"scheduled\" ||\n status === \"retrying\",\n };\n }\n\n /**\n * Convert an ISO date string to the raw SQL parameter format\n * expected by the current database dialect.\n *\n * - PostgreSQL: ISO string (timestamp comparison)\n * - SQLite: epoch milliseconds (integer comparison)\n */\n protected toRawDate(iso: string): string | number {\n return this.database.dialect === \"sqlite\" ? new Date(iso).getTime() : iso;\n }\n\n public async getStats(days?: number): Promise<JobStats> {\n const jobs = this.jobProvider.getRegisteredJobs();\n const periodAgo = this.toRawDate(\n this.dt\n .now()\n .subtract(days ?? 1, \"day\")\n .toISOString(),\n );\n\n const rows = await this.executions.query(\n (e) => sql`\n SELECT\n COUNT(*) FILTER (WHERE ${e.status} = 'running') AS running,\n COUNT(*) FILTER (WHERE ${e.status} = 'pending') AS pending,\n COUNT(*) FILTER (WHERE ${e.status} = 'scheduled') AS scheduled,\n COUNT(*) FILTER (WHERE ${e.status} = 'retrying') AS retrying,\n COUNT(*) FILTER (WHERE ${e.status} = 'dead') AS dead,\n COUNT(*) FILTER (WHERE ${e.status} = 'completed' AND ${e.completedAt} >= ${periodAgo}) AS completed_24h,\n COUNT(*) FILTER (WHERE ${e.status} = 'dead' AND ${e.completedAt} >= ${periodAgo}) AS failed_24h\n FROM ${e}\n `,\n t.object({\n running: t.string(),\n pending: t.string(),\n scheduled: t.string(),\n retrying: t.string(),\n dead: t.string(),\n completed_24h: t.string(),\n failed_24h: t.string(),\n }),\n );\n\n const row = rows[0];\n return {\n registered: jobs.size,\n running: Number(row.running),\n pending: Number(row.pending),\n scheduled: Number(row.scheduled),\n retrying: Number(row.retrying),\n dead: Number(row.dead),\n completed: Number(row.completed_24h),\n failed: Number(row.failed_24h),\n };\n }\n\n public getRegistry(): JobRegistration[] {\n const jobs = this.jobProvider.getRegisteredJobs();\n const result: JobRegistration[] = [];\n\n for (const [name, reg] of jobs) {\n const opts = reg.options;\n const hasCron = Boolean(opts.cron);\n const hasSchema = Boolean(opts.schema);\n\n let type: \"cron\" | \"push\" | \"both\";\n if (hasCron && hasSchema) {\n type = \"both\";\n } else if (hasCron) {\n type = \"cron\";\n } else {\n type = \"push\";\n }\n\n const registration: JobRegistration = {\n name,\n type,\n priority: (opts.priority ?? \"normal\") as JobRegistration[\"priority\"],\n concurrency: opts.concurrency ?? 1,\n hasSchema,\n cron: opts.cron,\n timeout: opts.timeout ? String(opts.timeout) : undefined,\n retry: opts.retry\n ? {\n retries: opts.retry.retries,\n hasBackoff: Boolean(opts.retry.backoff),\n }\n : undefined,\n paused: this.jobProvider.isJobPaused(name),\n };\n\n result.push(registration);\n }\n\n return result;\n }\n\n public async findExecutions(query: JobExecutionQuery = {}) {\n query.sort ??= \"-createdAt\";\n\n const where = this.executions.createQueryWhere();\n\n if (query.job) {\n where.jobName = { eq: query.job };\n }\n\n if (query.status) {\n where.status = { eq: query.status };\n }\n\n if (query.priority) {\n const priorityMap: Record<string, number> = {\n critical: 0,\n high: 1,\n normal: 2,\n low: 3,\n };\n where.priority = { eq: priorityMap[query.priority] };\n }\n\n if (query.from) {\n where.createdAt = { gte: query.from };\n }\n\n if (query.to) {\n where.createdAt = {\n ...(where.createdAt as object),\n lte: query.to,\n };\n }\n\n const page = await this.executions.paginate(\n query,\n { where },\n { count: true },\n );\n return {\n ...page,\n content: page.content.map((exec: JobExecutionEntity) => ({\n ...exec,\n can: this.computeCan(exec.status),\n })),\n };\n }\n\n public async getExecution(id: string) {\n const execution = await this.executions.findById(id);\n if (!execution) {\n throw new NotFoundError(`Execution not found: ${id}`);\n }\n\n const logRecord = await this.executionLogs.findById(id);\n\n return {\n ...execution,\n can: this.computeCan(execution.status),\n logs: logRecord?.logs,\n };\n }\n\n public async triggerJob(\n name: string,\n context?: JobTriggerContext,\n ): Promise<{ ok: boolean }> {\n const jobPrimitives = this.alepha.primitives($job);\n const job = jobPrimitives.find((j) => j.name === name);\n\n if (!job) {\n throw new NotFoundError(`Job not found: ${name}`);\n }\n\n this.log.info(`Triggering job '${name}'`, {\n triggeredBy: context?.triggeredByName ?? context?.triggeredBy,\n });\n\n await job.trigger(context);\n return { ok: true };\n }\n\n public async retryExecution(\n id: string,\n context?: { triggeredBy?: string; triggeredByName?: string },\n ): Promise<{ ok: boolean }> {\n const execution = await this.executions.findById(id);\n if (!execution) {\n throw new NotFoundError(`Execution not found: ${id}`);\n }\n\n if (execution.status !== \"dead\" && execution.status !== \"cancelled\") {\n throw new AlephaError(\n `Cannot retry execution in '${execution.status}' status`,\n );\n }\n\n this.log.info(`Retrying execution ${id}`, {\n jobName: execution.jobName,\n previousStatus: execution.status,\n triggeredBy: context?.triggeredByName ?? context?.triggeredBy,\n });\n\n const jobPrimitives = this.alepha.primitives($job);\n const job = jobPrimitives.find((j) => j.name === execution.jobName);\n\n if (!job) {\n throw new NotFoundError(`Job not found: ${execution.jobName}`);\n }\n\n if (execution.payload) {\n await job.push(execution.payload, {});\n } else {\n await job.trigger({\n triggeredBy: context?.triggeredBy,\n triggeredByName: context?.triggeredByName,\n });\n }\n\n return { ok: true };\n }\n\n public async cancelExecution(\n id: string,\n context?: { cancelledBy?: string; cancelledByName?: string },\n ): Promise<{ ok: boolean }> {\n this.log.info(`Cancelling execution ${id}`, {\n cancelledBy: context?.cancelledByName ?? context?.cancelledBy,\n });\n\n await this.jobProvider.cancel(id, {\n cancelledBy: context?.cancelledBy,\n cancelledByName: context?.cancelledByName,\n });\n return { ok: true };\n }\n\n public pauseJob(\n name: string,\n context?: { pausedBy?: string; pausedByName?: string },\n ): { ok: boolean } {\n const jobPrimitives = this.alepha.primitives($job);\n const job = jobPrimitives.find((j) => j.name === name);\n\n if (!job) {\n throw new NotFoundError(`Job not found: ${name}`);\n }\n\n this.log.info(`Pausing job '${name}'`, {\n pausedBy: context?.pausedByName ?? context?.pausedBy,\n });\n\n job.pause();\n return { ok: true };\n }\n\n public async resumeJob(\n name: string,\n context?: { resumedBy?: string; resumedByName?: string },\n ): Promise<{ ok: boolean }> {\n const jobPrimitives = this.alepha.primitives($job);\n const job = jobPrimitives.find((j) => j.name === name);\n\n if (!job) {\n throw new NotFoundError(`Job not found: ${name}`);\n }\n\n this.log.info(`Resuming job '${name}'`, {\n resumedBy: context?.resumedByName ?? context?.resumedBy,\n });\n\n await job.resume();\n return { ok: true };\n }\n\n public getPausedJobs(): string[] {\n return this.jobProvider.getPausedJobs();\n }\n\n public async getCronJobs(): Promise<JobCronInfo[]> {\n const jobs = this.jobProvider.getRegisteredJobs();\n const cronJobNames: string[] = [];\n\n for (const [name, reg] of jobs) {\n if (reg.options.cron) cronJobNames.push(name);\n }\n\n const lastByJob = await this.getLastExecutionPerJob(cronJobNames);\n\n const result: JobCronInfo[] = [];\n for (const name of cronJobNames) {\n const reg = jobs.get(name)!;\n const opts = reg.options;\n const last = lastByJob.get(name);\n\n result.push({\n name,\n cron: opts.cron!,\n lock: opts.lock !== false,\n priority: (opts.priority ?? \"normal\") as JobCronInfo[\"priority\"],\n concurrency: opts.concurrency ?? 1,\n hasSchema: Boolean(opts.schema),\n paused: this.jobProvider.isJobPaused(name),\n lastExecution: last\n ? {\n id: last.id,\n status: last.status,\n startedAt: last.started_at ?? undefined,\n completedAt: last.completed_at ?? undefined,\n error: last.error ?? undefined,\n }\n : undefined,\n });\n }\n\n return result;\n }\n\n public async getQueueDepth(): Promise<JobQueueDepth[]> {\n const jobs = this.jobProvider.getRegisteredJobs();\n\n const rows = await this.executions.query(\n (e) => sql`\n SELECT\n ${e.jobName} AS job_name,\n COUNT(*) FILTER (WHERE ${e.status} = 'pending') AS pending,\n COUNT(*) FILTER (WHERE ${e.status} = 'running') AS running,\n COUNT(*) FILTER (WHERE ${e.status} = 'scheduled') AS scheduled,\n COUNT(*) FILTER (WHERE ${e.status} = 'retrying') AS retrying,\n COUNT(*) FILTER (WHERE ${e.status} = 'dead') AS dead\n FROM ${e}\n WHERE ${e.status} IN ('pending', 'running', 'scheduled', 'retrying', 'dead')\n GROUP BY ${e.jobName}\n `,\n t.object({\n job_name: t.string(),\n pending: t.string(),\n running: t.string(),\n scheduled: t.string(),\n retrying: t.string(),\n dead: t.string(),\n }),\n );\n\n const counts = new Map(rows.map((r) => [r.job_name, r]));\n\n const result: JobQueueDepth[] = [];\n for (const [name, reg] of jobs) {\n const row = counts.get(name);\n result.push({\n jobName: name,\n pending: Number(row?.pending ?? 0),\n running: Number(row?.running ?? 0),\n scheduled: Number(row?.scheduled ?? 0),\n retrying: Number(row?.retrying ?? 0),\n dead: Number(row?.dead ?? 0),\n concurrency: reg.options.concurrency ?? 1,\n paused: this.jobProvider.isJobPaused(name),\n });\n }\n\n return result;\n }\n\n public async getActivity(days = 14): Promise<JobActivityPoint[]> {\n if (this.database.dialect === \"sqlite\") {\n return this.getActivitySqlite(days);\n }\n\n const rows = await this.executions.query(\n (e) => sql`\n WITH date_series AS (\n SELECT generate_series(\n CURRENT_DATE - ${days - 1}::int,\n CURRENT_DATE,\n '1 day'::interval\n )::date AS date\n )\n SELECT\n ds.date::text AS date,\n COALESCE(COUNT(*) FILTER (WHERE ${e.status} = 'completed'), 0) AS completed,\n COALESCE(COUNT(*) FILTER (WHERE ${e.status} = 'dead'), 0) AS failed\n FROM date_series ds\n LEFT JOIN ${e} ON DATE(${e.completedAt}) = ds.date\n AND ${e.status} IN ('completed', 'dead')\n GROUP BY ds.date\n ORDER BY ds.date ASC\n `,\n t.object({\n date: t.string(),\n completed: t.string(),\n failed: t.string(),\n }),\n );\n\n return rows.map((row) => ({\n date: row.date,\n completed: Number(row.completed),\n failed: Number(row.failed),\n }));\n }\n\n protected async getActivitySqlite(days = 14): Promise<JobActivityPoint[]> {\n const now = this.dt.now();\n const startDate = now.subtract(days - 1, \"day\");\n\n const where = this.executions.createQueryWhere();\n where.status = { inArray: [\"completed\", \"dead\"] };\n where.completedAt = { gte: startDate.startOf(\"day\").toISOString() };\n\n const executions = await this.executions.findMany({ where });\n\n // Build date → counts map\n const byDate = new Map<string, { completed: number; failed: number }>();\n for (let i = 0; i < days; i++) {\n const date = startDate.add(i, \"day\").format(\"YYYY-MM-DD\");\n byDate.set(date, { completed: 0, failed: 0 });\n }\n\n for (const exec of executions) {\n if (!exec.completedAt) continue;\n const date = this.dt.of(exec.completedAt).format(\"YYYY-MM-DD\");\n const entry = byDate.get(date);\n if (!entry) continue;\n if (exec.status === \"completed\") entry.completed++;\n else entry.failed++;\n }\n\n return [...byDate.entries()].map(([date, counts]) => ({\n date,\n ...counts,\n }));\n }\n\n public async getTopFailures(days?: number): Promise<JobFailure[]> {\n const periodAgoIso = this.dt\n .now()\n .subtract(days ?? 7, \"day\")\n .toISOString();\n\n if (this.database.dialect === \"sqlite\") {\n return this.getTopFailuresSqlite(periodAgoIso);\n }\n\n const rows = await this.executions.query(\n (e) => sql`\n SELECT\n ${e.jobName} AS job_name,\n COUNT(*) AS failures,\n (ARRAY_AGG(${e.error} ORDER BY ${e.completedAt} DESC))[1] AS last_error\n FROM ${e}\n WHERE ${e.status} = 'dead'\n AND ${e.completedAt} >= ${periodAgoIso}\n GROUP BY ${e.jobName}\n ORDER BY failures DESC\n `,\n t.object({\n job_name: t.string(),\n failures: t.string(),\n last_error: t.optional(t.nullable(t.string())),\n }),\n );\n\n return rows.map((row) => ({\n jobName: row.job_name,\n failures: Number(row.failures),\n lastError: row.last_error ?? undefined,\n }));\n }\n\n protected async getTopFailuresSqlite(\n periodAgoIso: string,\n ): Promise<JobFailure[]> {\n const where = this.executions.createQueryWhere();\n where.status = { eq: \"dead\" };\n where.completedAt = { gte: periodAgoIso };\n\n const failures = await this.executions.findMany({\n where,\n orderBy: { column: \"completedAt\", direction: \"desc\" },\n });\n\n const byJob = new Map<string, { failures: number; lastError?: string }>();\n for (const exec of failures) {\n const entry = byJob.get(exec.jobName) ?? { failures: 0 };\n entry.failures++;\n if (!entry.lastError) entry.lastError = exec.error ?? undefined;\n byJob.set(exec.jobName, entry);\n }\n\n return [...byJob.entries()]\n .map(([jobName, data]) => ({\n jobName,\n failures: data.failures,\n lastError: data.lastError,\n }))\n .sort((a, b) => b.failures - a.failures);\n }\n\n /**\n * Fetch the most recent execution per job name.\n *\n * - PostgreSQL: uses `DISTINCT ON` for a single-pass query\n * - SQLite: uses ORM queries (one per job name) since `DISTINCT ON` is not supported\n */\n protected async getLastExecutionPerJob(jobNames: string[]): Promise<\n Map<\n string,\n {\n id: string;\n job_name: string;\n status: string;\n started_at?: string | null;\n completed_at?: string | null;\n error?: string | null;\n }\n >\n > {\n if (jobNames.length === 0) {\n return new Map();\n }\n\n if (this.database.dialect === \"sqlite\") {\n const result = new Map<string, any>();\n for (const name of jobNames) {\n const rows = await this.executions.findMany({\n where: { jobName: { eq: name } },\n orderBy: { column: \"createdAt\", direction: \"desc\" },\n limit: 1,\n });\n if (rows[0]) {\n result.set(name, {\n id: rows[0].id,\n job_name: rows[0].jobName,\n status: rows[0].status,\n started_at: rows[0].startedAt,\n completed_at: rows[0].completedAt,\n error: rows[0].error,\n });\n }\n }\n return result;\n }\n\n const schema = t.object({\n id: t.string(),\n job_name: t.string(),\n status: t.string(),\n started_at: t.optional(t.nullable(t.string())),\n completed_at: t.optional(t.nullable(t.string())),\n error: t.optional(t.nullable(t.string())),\n });\n\n const rows = await this.executions.query(\n (e) => sql`\n SELECT DISTINCT ON (${e.jobName})\n ${e.id}, ${e.jobName} AS job_name, ${e.status},\n ${e.startedAt} AS started_at, ${e.completedAt} AS completed_at, ${e.error}\n FROM ${e}\n WHERE ${e.jobName} IN (${sql.join(\n jobNames.map((n) => sql`${n}`),\n sql`, `,\n )})\n ORDER BY ${e.jobName}, ${e.createdAt} DESC\n `,\n schema,\n );\n\n return new Map(rows.map((r) => [r.job_name, r]));\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action, okSchema } from \"alepha/server\";\nimport {\n jobActivityPointSchema,\n jobActivityQuerySchema,\n} from \"../schemas/jobActivitySchema.ts\";\nimport { jobCronInfoSchema } from \"../schemas/jobCronInfoSchema.ts\";\nimport { jobExecutionDetailResourceSchema } from \"../schemas/jobExecutionDetailResourceSchema.ts\";\nimport { jobExecutionQuerySchema } from \"../schemas/jobExecutionQuerySchema.ts\";\nimport { jobExecutionResourceSchema } from \"../schemas/jobExecutionResourceSchema.ts\";\nimport { jobFailureSchema } from \"../schemas/jobFailureSchema.ts\";\nimport { jobQueueDepthSchema } from \"../schemas/jobQueueDepthSchema.ts\";\nimport { jobRegistrationSchema } from \"../schemas/jobRegistrationSchema.ts\";\nimport { jobStatsSchema } from \"../schemas/jobStatsSchema.ts\";\nimport { triggerJobSchema } from \"../schemas/triggerJobSchema.ts\";\nimport { JobService } from \"../services/JobService.ts\";\n\nexport class AdminJobController {\n protected readonly url: string = \"/jobs\";\n protected readonly group: string = \"admin:jobs\";\n protected readonly jobService = $inject(JobService);\n\n public readonly getJobStats = $action({\n path: `${this.url}/stats`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n query: jobActivityQuerySchema,\n response: jobStatsSchema,\n },\n handler: ({ query }) => this.jobService.getStats(query.days),\n });\n\n public readonly getJobRegistry = $action({\n path: this.url,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n response: t.array(jobRegistrationSchema),\n },\n handler: () => this.jobService.getRegistry(),\n });\n\n public readonly findJobExecutions = $action({\n path: `${this.url}/executions`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n query: jobExecutionQuerySchema,\n response: t.page(jobExecutionResourceSchema),\n },\n handler: ({ query }) => this.jobService.findExecutions(query),\n });\n\n public readonly getJobExecution = $action({\n path: `${this.url}/executions/:id`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: jobExecutionDetailResourceSchema,\n },\n handler: ({ params }) => this.jobService.getExecution(params.id),\n });\n\n public readonly triggerJob = $action({\n method: \"POST\",\n path: `${this.url}/trigger`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:trigger\"] })],\n schema: {\n body: triggerJobSchema,\n response: okSchema,\n },\n handler: async ({ body, user }) => {\n return this.jobService.triggerJob(body.name, {\n payload: body.payload,\n triggeredBy: user?.id,\n triggeredByName: user?.name,\n });\n },\n });\n\n public readonly retryJobExecution = $action({\n method: \"POST\",\n path: `${this.url}/executions/:id/retry`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:trigger\"] })],\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: okSchema,\n },\n handler: async ({ params, user }) => {\n return this.jobService.retryExecution(params.id, {\n triggeredBy: user?.id,\n triggeredByName: user?.name,\n });\n },\n });\n\n public readonly cancelJobExecution = $action({\n method: \"POST\",\n path: `${this.url}/executions/:id/cancel`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:cancel\"] })],\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: okSchema,\n },\n handler: async ({ params, user }) => {\n return this.jobService.cancelExecution(params.id, {\n cancelledBy: user?.id,\n cancelledByName: user?.name,\n });\n },\n });\n\n public readonly getJobActivity = $action({\n path: `${this.url}/activity`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n query: jobActivityQuerySchema,\n response: t.array(jobActivityPointSchema),\n },\n handler: ({ query }) => this.jobService.getActivity(query.days),\n });\n\n public readonly getCronJobs = $action({\n path: `${this.url}/cron`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n response: t.array(jobCronInfoSchema),\n },\n handler: () => this.jobService.getCronJobs(),\n });\n\n public readonly getJobQueueDepth = $action({\n path: `${this.url}/queue`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n response: t.array(jobQueueDepthSchema),\n },\n handler: () => this.jobService.getQueueDepth(),\n });\n\n public readonly getJobTopFailures = $action({\n path: `${this.url}/failures`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n query: jobActivityQuerySchema,\n response: t.array(jobFailureSchema),\n },\n handler: ({ query }) => this.jobService.getTopFailures(query.days),\n });\n\n public readonly pauseJob = $action({\n method: \"POST\",\n path: `${this.url}/pause`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:trigger\"] })],\n schema: {\n body: t.object({ name: t.text() }),\n response: okSchema,\n },\n handler: ({ body, user }) => {\n return this.jobService.pauseJob(body.name, {\n pausedBy: user?.id,\n pausedByName: user?.name,\n });\n },\n });\n\n public readonly resumeJob = $action({\n method: \"POST\",\n path: `${this.url}/resume`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:trigger\"] })],\n schema: {\n body: t.object({ name: t.text() }),\n response: okSchema,\n },\n handler: async ({ body, user }) => {\n return this.jobService.resumeJob(body.name, {\n resumedBy: user?.id,\n resumedByName: user?.name,\n });\n },\n });\n\n public readonly getPausedJobs = $action({\n path: `${this.url}/paused`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n response: t.array(t.text()),\n },\n handler: () => this.jobService.getPausedJobs(),\n });\n}\n","import { $hook, $inject, t } from \"alepha\";\nimport { $queue } from \"alepha/queue\";\nimport { JobProvider } from \"./JobProvider.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\n/**\n * Optional queue-backed dispatch for the job system.\n *\n * When registered, `JobProvider` will push work through this queue instead of\n * executing inline. This is the default for long-running (non-serverless) environments.\n * In serverless environments (Cloudflare Workers, Vercel), this provider is typically\n * omitted so jobs execute inline without requiring an external queue resource.\n */\nexport class JobQueueProvider {\n protected readonly jobProvider = $inject(JobProvider);\n\n protected readonly queue = $queue({\n name: \"_alepha:jobs:dispatch\",\n schema: t.object({ jobName: t.text(), executionId: t.text() }),\n handler: async (msg) => {\n await this.jobProvider.processExecution(\n msg.payload.jobName,\n msg.payload.executionId,\n );\n },\n });\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n this.jobProvider.queueDispatch = (jobName, executionId) =>\n this.push(jobName, executionId);\n },\n });\n\n /**\n * Push a job execution onto the queue for async processing.\n */\n public async push(jobName: string, executionId: string): Promise<void> {\n await this.queue.push({ jobName, executionId });\n }\n}\n","import { $module, type Alepha, type Static, t } from \"alepha\";\nimport type { DateTime } from \"alepha/datetime\";\nimport { AlephaLock } from \"alepha/lock\";\nimport { AlephaQueue } from \"alepha/queue\";\nimport { AlephaScheduler } from \"alepha/scheduler\";\nimport { AdminJobController } from \"./controllers/AdminJobController.ts\";\nimport { JobProvider } from \"./providers/JobProvider.ts\";\nimport { JobQueueProvider } from \"./providers/JobQueueProvider.ts\";\nimport { JobService } from \"./services/JobService.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/AdminJobController.ts\";\nexport * from \"./entities/jobExecutionEntity.ts\";\nexport * from \"./entities/jobExecutionLogEntity.ts\";\nexport * from \"./primitives/$job.ts\";\nexport * from \"./providers/JobProvider.ts\";\nexport * from \"./providers/JobQueueProvider.ts\";\nexport * from \"./schemas/jobActivitySchema.ts\";\nexport * from \"./schemas/jobConfigAtom.ts\";\nexport * from \"./schemas/jobCronInfoSchema.ts\";\nexport * from \"./schemas/jobExecutionDetailResourceSchema.ts\";\nexport * from \"./schemas/jobExecutionQuerySchema.ts\";\nexport * from \"./schemas/jobExecutionResourceSchema.ts\";\nexport * from \"./schemas/jobFailureSchema.ts\";\nexport * from \"./schemas/jobQueueDepthSchema.ts\";\nexport * from \"./schemas/jobRegistrationSchema.ts\";\nexport * from \"./schemas/jobStatsSchema.ts\";\nexport * from \"./schemas/triggerJobSchema.ts\";\nexport * from \"./services/JobService.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof jobEnvSchema>> {}\n\n interface Hooks {\n \"job:begin\": { name: string; now: DateTime; executionId: string };\n \"job:success\": { name: string; executionId: string };\n \"job:error\": { name: string; error: Error; executionId: string };\n \"job:cancel\": { name: string; executionId: string };\n \"job:end\": { name: string; executionId: string };\n }\n}\n\n// -----------------------------------------------------------------------------------------------------------------\n\nconst jobEnvSchema = t.object({\n /**\n * Controls whether the job system dispatches work through a queue or executes inline.\n *\n * - `1` — always use queue (force queue even in serverless)\n * - `0` — never use queue (force inline, useful for testing)\n * - not set — auto: use queue when NOT serverless (default)\n */\n ALEPHA_JOBS_QUEUE: t.optional(\n t.integer({\n description:\n \"Set to 1 to always use queue, 0 to disable queue (default: auto-detect based on environment)\",\n }),\n ),\n});\n\n// -----------------------------------------------------------------------------------------------------------------\n\n/**\n * Job execution framework — unified primitive for deferred, scheduled, and queued work.\n *\n * **Features:**\n * - Push-based jobs with typed payloads\n * - Cron scheduling with execution tracking\n * - Retry with exponential backoff\n * - Priority, delay, cancellation\n * - Deduplication via unique keys\n * - Per-execution log capture\n *\n * @module alepha.api.jobs\n */\nexport const AlephaApiJobs = $module({\n name: \"alepha.api.jobs\",\n imports: [AlephaScheduler, AlephaLock],\n services: [JobProvider, JobService, AdminJobController],\n // AlephaQueue + JobQueueProvider are conditional — injected only when queue is enabled.\n variants: [JobQueueProvider],\n register: (alepha: Alepha) => {\n const env = alepha.parseEnv(jobEnvSchema);\n const useQueue =\n env.ALEPHA_JOBS_QUEUE === 1\n ? true\n : env.ALEPHA_JOBS_QUEUE === 0\n ? false\n : !alepha.isServerless();\n\n if (useQueue) {\n alepha.with(AlephaQueue);\n alepha.with(JobQueueProvider);\n }\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/api/jobs/schemas/jobExecutionQuerySchema.ts","../../../src/api/jobs/entities/jobExecutionEntity.ts","../../../src/api/jobs/schemas/jobExecutionResourceSchema.ts","../../../src/api/jobs/schemas/jobRegistrationSchema.ts","../../../src/api/jobs/schemas/triggerJobSchema.ts","../../../src/api/jobs/schemas/jobConfigAtom.ts","../../../src/api/jobs/providers/JobProvider.ts","../../../src/api/jobs/primitives/$job.ts","../../../src/api/jobs/services/JobService.ts","../../../src/api/jobs/controllers/AdminJobController.ts","../../../src/api/jobs/providers/JobQueueProvider.ts","../../../src/api/jobs/index.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\n\nexport const jobExecutionQuerySchema = t.object({\n status: t.optional(\n t.enum([\"pending\", \"running\", \"scheduled\", \"ok\", \"error\", \"cancelled\"]),\n ),\n limit: t.optional(t.integer({ minimum: 1, maximum: 200, default: 20 })),\n});\n\nexport type JobExecutionQuery = Static<typeof jobExecutionQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { logEntrySchema } from \"alepha/logger\";\nimport { $entity, db } from \"alepha/orm\";\n\n/**\n * Job execution record.\n *\n * Stores durable state for queue-mode jobs (outbox pattern) and error records\n * for cron-mode jobs. Successful executions are trimmed by the sweep to keep\n * the last N rows per job (configurable via `jobConfig.keepLastSuccess`).\n *\n * Status transitions:\n * - queue push → pending\n * - worker claim → running\n * - success → ok\n * - terminal failure → error\n * - retry → scheduled (with scheduledAt = now + backoff)\n * - delay → scheduled (with scheduledAt = now + delay)\n * - sweep picks due ones → pending\n * - cancel → cancelled\n */\nexport const jobExecutionEntity = $entity({\n name: \"job_executions\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n\n jobName: t.text(),\n key: t.optional(t.nullable(t.text())),\n\n status: db.default(\n t.enum([\"pending\", \"running\", \"scheduled\", \"ok\", \"error\", \"cancelled\"]),\n \"pending\",\n ),\n priority: db.default(t.integer({ minimum: 0, maximum: 3 }), 2),\n\n attempt: db.default(t.integer(), 0),\n maxAttempts: db.default(t.integer(), 1),\n\n payload: t.optional(t.record(t.text(), t.any())),\n\n scheduledAt: t.optional(t.datetime()),\n startedAt: t.optional(t.datetime()),\n completedAt: t.optional(t.datetime()),\n\n error: t.optional(t.text()),\n logs: t.optional(t.array(logEntrySchema)),\n\n triggeredBy: t.optional(t.text()),\n triggeredByName: t.optional(t.text()),\n cancelledBy: t.optional(t.text()),\n cancelledByName: t.optional(t.text()),\n }),\n indexes: [\n { columns: [\"jobName\", \"status\", \"scheduledAt\"] },\n { columns: [\"jobName\", \"startedAt\"] },\n { columns: [\"jobName\", \"key\"], unique: true },\n ],\n});\n\nexport type JobExecutionEntity = Static<typeof jobExecutionEntity.schema>;\n\nexport type JobStatus =\n | \"pending\"\n | \"running\"\n | \"scheduled\"\n | \"ok\"\n | \"error\"\n | \"cancelled\";\n","import { type Static, t } from \"alepha\";\nimport { jobExecutionEntity } from \"../entities/jobExecutionEntity.ts\";\n\nexport const jobExecutionResourceSchema = t.extend(\n jobExecutionEntity.schema,\n {\n can: t.object({\n retry: t.boolean(),\n cancel: t.boolean(),\n }),\n },\n {\n title: \"JobExecutionResource\",\n description: \"A job execution row with derived actions.\",\n },\n);\n\nexport type JobExecutionResource = Static<typeof jobExecutionResourceSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const jobRegistrationSchema = t.object({\n name: t.text(),\n description: t.optional(t.text()),\n type: t.enum([\"cron\", \"queue\"]),\n priority: t.enum([\"critical\", \"high\", \"normal\", \"low\"]),\n cron: t.optional(t.text()),\n timeout: t.optional(t.text()),\n retry: t.optional(\n t.object({\n retries: t.integer(),\n hasBackoff: t.boolean(),\n }),\n ),\n recent: t.object({\n ok: t.integer(),\n error: t.integer(),\n lastRun: t.optional(t.datetime()),\n }),\n});\n\nexport type JobRegistration = Static<typeof jobRegistrationSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const triggerJobSchema = t.object({\n payload: t.optional(t.record(t.text(), t.any())),\n});\n\nexport type TriggerJob = Static<typeof triggerJobSchema>;\n","import { $atom, type Static, t } from \"alepha\";\n\nexport const jobConfig = $atom({\n name: \"alepha.jobs\",\n description: \"Configuration for the $job primitive.\",\n schema: t.object({\n sweepInterval: t.integer({\n description: \"Sweep cron interval in milliseconds.\",\n }),\n staleThreshold: t.integer({\n description: \"Pending age (ms) before the sweep re-dispatches it.\",\n }),\n runTimeout: t.integer({\n description:\n \"Running age (ms) before assumed crash (fallback when no per-job timeout).\",\n }),\n keepLastSuccess: t.integer({\n description:\n \"Max successful rows to keep per job. Set 0 to disable and delete on success.\",\n }),\n keepLastError: t.integer({\n description: \"Max error rows to keep per job.\",\n }),\n logMaxEntries: t.integer({\n description: \"Max log entries captured per execution.\",\n }),\n drainTimeout: t.integer({\n description: \"Max time (ms) to wait for in-flight jobs during shutdown.\",\n }),\n }),\n default: {\n sweepInterval: 300_000,\n staleThreshold: 300_000,\n runTimeout: 1_800_000,\n keepLastSuccess: 10,\n keepLastError: 10,\n logMaxEntries: 100,\n drainTimeout: 30_000,\n },\n});\n\nexport type JobConfig = Static<typeof jobConfig.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [jobConfig.key]: JobConfig;\n }\n}\n","import {\n $hook,\n $inject,\n $state,\n Alepha,\n AlephaError,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport type { LogEntry } from \"alepha/logger\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { CronProvider } from \"alepha/scheduler\";\nimport {\n type JobStatus,\n jobExecutionEntity,\n} from \"../entities/jobExecutionEntity.ts\";\nimport type {\n JobPrimitiveOptions,\n JobPriority,\n JobRetryBackoff,\n JobRetryOptions,\n} from \"../primitives/$job.ts\";\nimport { jobConfig } from \"../schemas/jobConfigAtom.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\nconst PRIORITY_MAP: Record<JobPriority, number> = {\n critical: 0,\n high: 1,\n normal: 2,\n low: 3,\n};\n\nconst PRIORITY_REVERSE: Record<number, JobPriority> = {\n 0: \"critical\",\n 1: \"high\",\n 2: \"normal\",\n 3: \"low\",\n};\n\nconst SWEEP_CRON = \"*/5 * * * *\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport interface PushOptions {\n delay?: DurationLike;\n key?: string;\n priority?: JobPriority;\n scheduledAt?: Date;\n triggeredBy?: string;\n triggeredByName?: string;\n}\n\nexport interface PushManyItem<T extends TSchema = TSchema> {\n payload: Static<T>;\n key?: string;\n delay?: DurationLike;\n priority?: JobPriority;\n scheduledAt?: Date;\n}\n\nexport interface JobTriggerContext<T extends TSchema = TSchema> {\n payload?: Static<T>;\n triggeredBy?: string;\n triggeredByName?: string;\n}\n\nexport interface CancelContext {\n cancelledBy?: string;\n cancelledByName?: string;\n}\n\ninterface JobRuntimeRegistration {\n name: string;\n options: JobPrimitiveOptions;\n type: \"cron\" | \"queue\";\n}\n\n// -----------------------------------------------------------------------------------------------------------------\n\n/**\n * Coordinates cron (scheduler) and queue (push) jobs with a durable outbox\n * table and a single reconciliation sweep.\n *\n * Queue-mode flow:\n * push() → INSERT row (pending) + queue.send({ executionId })\n * worker → SELECT row → UPDATE running → handler → DELETE (ok) / UPDATE (error)\n *\n * Cron-mode flow:\n * scheduler tick → handler runs inline → INSERT row only on error\n *\n * Sweep responsibilities (every `sweepInterval`):\n * - re-enqueue pending rows older than `staleThreshold`\n * - fail running rows older than `max(timeout*2, runTimeout)`\n * - move `scheduled` rows with `scheduledAt <= now` to pending + enqueue\n * - trim per-job history beyond `keepLastSuccess` / `keepLastError`\n */\nexport class JobProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dt = $inject(DateTimeProvider);\n protected readonly cronProvider = $inject(CronProvider);\n protected readonly config = $state(jobConfig);\n protected readonly log = $logger();\n protected readonly executions = $repository(jobExecutionEntity);\n\n protected readonly jobs = new Map<string, JobRuntimeRegistration>();\n protected readonly inFlight = new Set<Promise<void>>();\n protected readonly abortControllers = new Map<string, AbortController>();\n protected readonly perExecutionLogs = new Map<string, LogEntry[]>();\n protected stopping = false;\n\n /**\n * Set by `JobQueueProvider` when `AlephaApiJobsQueue` is loaded.\n * When null, queue-mode jobs cannot be pushed.\n */\n public queueDispatch:\n | ((jobName: string, executionId: string) => Promise<void>)\n | null = null;\n\n // --- Registration -----------------------------------------------------------------------------------------------\n\n public registerJob(name: string, options: JobPrimitiveOptions): void {\n if (this.jobs.has(name)) {\n throw new AlephaError(`Job already registered: ${name}`);\n }\n if (options.cron && options.schema) {\n throw new AlephaError(\n `Job '${name}' declares both 'cron' and 'schema'. A job must be either cron-only (recurring) or queue-only (push-based). Split into two jobs.`,\n );\n }\n if (!options.cron && !options.schema) {\n throw new AlephaError(\n `Job '${name}' must declare either 'cron' (for recurring tasks) or 'schema' (for queue-mode tasks).`,\n );\n }\n\n const type: \"cron\" | \"queue\" = options.cron ? \"cron\" : \"queue\";\n this.jobs.set(name, { name, options, type });\n this.log.debug(`Registered ${type} job '${name}'`, {\n cron: options.cron,\n priority: options.priority ?? \"normal\",\n retries: options.retry?.retries ?? 0,\n });\n\n if (options.cron) {\n this.cronProvider.createCronJob(name, options.cron, async () => {\n try {\n await this.runCron(name);\n } catch (error) {\n this.log.error(`Cron tick failed for job '${name}'`, error);\n }\n });\n }\n }\n\n public getRegisteredJobs(): Map<string, JobRuntimeRegistration> {\n return this.jobs;\n }\n\n // --- Cron execution (inline, no queue) --------------------------------------------------------------------------\n\n protected async runCron(name: string): Promise<void> {\n const registration = this.getRegistration(name);\n if (registration.type !== \"cron\") {\n throw new AlephaError(`Job '${name}' is not cron-mode`);\n }\n if (this.stopping) return;\n\n const executionId = crypto.randomUUID();\n const promise = this.executeInline(registration, executionId, {\n payload: undefined,\n attempt: 1,\n triggeredBy: \"system\",\n triggeredByName: \"system (cron)\",\n });\n this.inFlight.add(promise);\n try {\n await promise;\n } finally {\n this.inFlight.delete(promise);\n }\n }\n\n /**\n * Execute a cron handler inline. Records a row only on error (or always,\n * when `record: 'all'`). No DB writes on the happy path by default.\n */\n protected async executeInline(\n registration: JobRuntimeRegistration,\n executionId: string,\n ctx: {\n payload: unknown;\n attempt: number;\n triggeredBy?: string;\n triggeredByName?: string;\n },\n ): Promise<void> {\n const opts = registration.options;\n const name = registration.name;\n const record = opts.record ?? \"error\";\n const contextId = this.alepha.context.createContextId();\n this.perExecutionLogs.set(contextId, []);\n\n const abortController = new AbortController();\n this.abortControllers.set(executionId, abortController);\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (opts.timeout) {\n const ms = this.dt.duration(opts.timeout).as(\"milliseconds\");\n timeoutId = setTimeout(() => abortController.abort(), ms);\n }\n\n const startedAt = this.dt.now();\n\n try {\n await this.alepha.context.run(\n async () => {\n await this.alepha.events.emit(\"job:begin\", {\n name,\n now: startedAt,\n executionId,\n });\n\n try {\n await opts.handler({\n payload: ctx.payload,\n attempt: ctx.attempt,\n now: startedAt,\n signal: abortController.signal,\n executionId,\n });\n\n if (record === \"all\") {\n await this.writeTerminalRow(executionId, name, \"ok\", {\n payload: ctx.payload,\n attempt: ctx.attempt,\n startedAt,\n error: undefined,\n context: contextId,\n triggeredBy: ctx.triggeredBy,\n triggeredByName: ctx.triggeredByName,\n });\n }\n\n await this.alepha.events.emit(\n \"job:success\",\n { name, executionId },\n { catch: true },\n );\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(String(error));\n if (record !== \"none\") {\n await this.writeTerminalRow(executionId, name, \"error\", {\n payload: ctx.payload,\n attempt: ctx.attempt,\n startedAt,\n error: err,\n context: contextId,\n triggeredBy: ctx.triggeredBy,\n triggeredByName: ctx.triggeredByName,\n });\n }\n await this.alepha.events.emit(\n \"job:error\",\n { name, error: err, executionId },\n { catch: true },\n );\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n this.abortControllers.delete(executionId);\n await this.alepha.events.emit(\n \"job:end\",\n { name, executionId },\n { catch: true },\n );\n }\n },\n { context: contextId },\n );\n } finally {\n this.perExecutionLogs.delete(contextId);\n }\n }\n\n protected async writeTerminalRow(\n executionId: string,\n jobName: string,\n status: \"ok\" | \"error\",\n fields: {\n payload: unknown;\n attempt: number;\n startedAt: ReturnType<DateTimeProvider[\"now\"]>;\n error?: Error;\n context: string;\n triggeredBy?: string;\n triggeredByName?: string;\n },\n ): Promise<void> {\n try {\n const logs =\n status === \"error\" ? this.snapshotLogs(fields.context) : undefined;\n await this.executions.create({\n id: executionId,\n jobName,\n status,\n payload: fields.payload as Record<string, unknown> | undefined,\n attempt: fields.attempt,\n maxAttempts: fields.attempt,\n startedAt: fields.startedAt.toISOString(),\n completedAt: this.dt.nowISOString(),\n error: fields.error?.message,\n logs,\n triggeredBy: fields.triggeredBy,\n triggeredByName: fields.triggeredByName,\n });\n } catch (e) {\n this.log.warn(`Failed to write terminal row for ${executionId}`, e);\n }\n }\n\n // --- Queue push -------------------------------------------------------------------------------------------------\n\n public async push(\n name: string,\n payload: unknown,\n options?: PushOptions,\n ): Promise<string> {\n const registration = this.getRegistration(name);\n if (registration.type !== \"queue\") {\n throw new AlephaError(\n `Job '${name}' is not queue-mode (no schema declared). Use trigger() instead.`,\n );\n }\n const opts = registration.options;\n const validated = this.alepha.codec.validate(opts.schema!, payload);\n\n const priority =\n PRIORITY_MAP[options?.priority ?? opts.priority ?? \"normal\"];\n const maxAttempts = (opts.retry?.retries ?? 0) + 1;\n\n const isDelayed = options?.delay || options?.scheduledAt;\n const status: JobStatus = isDelayed ? \"scheduled\" : \"pending\";\n\n let scheduledAt: string | undefined;\n if (options?.scheduledAt) {\n scheduledAt = options.scheduledAt.toISOString();\n } else if (options?.delay) {\n scheduledAt = this.dt\n .now()\n .add(this.dt.duration(options.delay))\n .toISOString();\n }\n\n if (options?.key) {\n // Key-based dedup: check for existing row first, then insert.\n // Two queries in the no-conflict path, but deterministic across dialects.\n const existing = await this.executions.findMany({\n where: { jobName: { eq: name }, key: { eq: options.key } },\n limit: 1,\n });\n if (existing.length > 0) {\n return existing[0].id;\n }\n const execution = await this.executions.create({\n jobName: name,\n key: options.key,\n payload: validated as Record<string, unknown>,\n status,\n priority,\n maxAttempts,\n scheduledAt,\n triggeredBy: options.triggeredBy,\n triggeredByName: options.triggeredByName,\n });\n if (status === \"pending\") {\n await this.dispatchToQueue(name, execution.id);\n } else if (status === \"scheduled\" && scheduledAt) {\n this.scheduleOptimisticDispatch(name, execution.id, scheduledAt);\n }\n return execution.id;\n }\n\n const execution = await this.executions.create({\n jobName: name,\n payload: validated as Record<string, unknown>,\n status,\n priority,\n maxAttempts,\n scheduledAt,\n triggeredBy: options?.triggeredBy,\n triggeredByName: options?.triggeredByName,\n });\n\n if (status === \"pending\") {\n await this.dispatchToQueue(name, execution.id);\n } else if (status === \"scheduled\" && scheduledAt) {\n this.scheduleOptimisticDispatch(name, execution.id, scheduledAt);\n }\n return execution.id;\n }\n\n /**\n * Fire a local setTimeout so delayed/retrying rows dispatch as close to\n * `scheduledAt` as possible, rather than waiting for the next sweep tick.\n * No-op on stateless runtimes where timers won't survive (the sweep\n * handles those).\n */\n protected scheduleOptimisticDispatch(\n jobName: string,\n executionId: string,\n scheduledAt: string,\n ): void {\n const delayMs = Math.max(\n 0,\n new Date(scheduledAt).getTime() - this.dt.nowMillis(),\n );\n this.dt.createTimeout(() => {\n void this.dispatchScheduled(jobName, executionId);\n }, delayMs);\n }\n\n public async pushMany(\n name: string,\n items: Array<PushManyItem>,\n ): Promise<string[]> {\n if (items.length === 0) return [];\n\n const registration = this.getRegistration(name);\n if (registration.type !== \"queue\") {\n throw new AlephaError(\n `Job '${name}' is not queue-mode (no schema declared).`,\n );\n }\n const opts = registration.options;\n const maxAttempts = (opts.retry?.retries ?? 0) + 1;\n\n const keyed: PushManyItem[] = [];\n const bulk: Array<{\n jobName: string;\n payload: Record<string, unknown>;\n status: JobStatus;\n priority: number;\n maxAttempts: number;\n scheduledAt?: string;\n }> = [];\n\n for (const item of items) {\n const validated = this.alepha.codec.validate(opts.schema!, item.payload);\n if (item.key) {\n keyed.push({ ...item, payload: validated as Static<TSchema> });\n continue;\n }\n const isDelayed = item.delay || item.scheduledAt;\n const status: JobStatus = isDelayed ? \"scheduled\" : \"pending\";\n let scheduledAt: string | undefined;\n if (item.scheduledAt) {\n scheduledAt = item.scheduledAt.toISOString();\n } else if (item.delay) {\n scheduledAt = this.dt\n .now()\n .add(this.dt.duration(item.delay))\n .toISOString();\n }\n bulk.push({\n jobName: name,\n payload: validated as Record<string, unknown>,\n status,\n priority: PRIORITY_MAP[item.priority ?? opts.priority ?? \"normal\"],\n maxAttempts,\n scheduledAt,\n });\n }\n\n const ids: string[] = [];\n\n for (const item of keyed) {\n const id = await this.push(name, item.payload, {\n key: item.key,\n delay: item.delay,\n priority: item.priority,\n scheduledAt: item.scheduledAt,\n });\n ids.push(id);\n }\n\n if (bulk.length > 0) {\n const created = await this.executions.createMany(bulk);\n for (const exec of created) {\n ids.push(exec.id);\n if (exec.status === \"pending\" && !this.stopping) {\n await this.dispatchToQueue(name, exec.id);\n } else if (\n exec.status === \"scheduled\" &&\n exec.scheduledAt &&\n !this.stopping\n ) {\n this.scheduleOptimisticDispatch(name, exec.id, exec.scheduledAt);\n }\n }\n }\n\n this.log.debug(`pushMany '${name}': ${ids.length} jobs created`, {\n bulk: bulk.length,\n keyed: keyed.length,\n });\n\n return ids;\n }\n\n protected async dispatchToQueue(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n if (this.stopping) return;\n if (!this.queueDispatch) {\n throw new AlephaError(\n `Queue-mode job '${jobName}' cannot be pushed: AlephaApiJobsQueue is not loaded. Add '.with(AlephaApiJobsQueue)' to your app.`,\n );\n }\n await this.queueDispatch(jobName, executionId);\n }\n\n // --- Manual trigger (admin / CLI) ------------------------------------------------------------------------------\n\n public async trigger(\n name: string,\n context?: JobTriggerContext,\n ): Promise<void> {\n const registration = this.getRegistration(name);\n\n if (registration.type === \"cron\") {\n const executionId = crypto.randomUUID();\n await this.executeInline(registration, executionId, {\n payload: undefined,\n attempt: 1,\n triggeredBy: context?.triggeredBy,\n triggeredByName: context?.triggeredByName,\n });\n return;\n }\n\n // queue-mode: treat as a normal push with the given payload\n if (!context?.payload) {\n throw new AlephaError(\n `Queue-mode job '${name}' requires a payload for manual trigger.`,\n );\n }\n await this.push(name, context.payload, {\n triggeredBy: context.triggeredBy,\n triggeredByName: context.triggeredByName,\n });\n }\n\n // --- Cancel ----------------------------------------------------------------------------------------------------\n\n public async cancel(\n executionId: string,\n context?: CancelContext,\n ): Promise<void> {\n const execution = await this.executions.findById(executionId);\n if (!execution) {\n throw new AlephaError(`Execution not found: ${executionId}`);\n }\n if (\n execution.status === \"ok\" ||\n execution.status === \"error\" ||\n execution.status === \"cancelled\"\n ) {\n throw new AlephaError(\n `Cannot cancel execution in '${execution.status}' status`,\n );\n }\n\n const controller = this.abortControllers.get(executionId);\n if (controller) controller.abort();\n\n await this.executions.updateById(executionId, {\n status: \"cancelled\",\n key: null,\n cancelledBy: context?.cancelledBy,\n cancelledByName: context?.cancelledByName,\n completedAt: this.dt.nowISOString(),\n });\n\n this.log.info(`Cancelled execution ${executionId}`, {\n jobName: execution.jobName,\n cancelledBy: context?.cancelledByName ?? context?.cancelledBy,\n });\n }\n\n // --- Queue consumer (called by JobQueueProvider) --------------------------------------------------------------\n\n public async processExecution(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n const registration = this.jobs.get(jobName);\n if (!registration) {\n this.log.warn(`Unknown job '${jobName}' — skipping execution`, {\n executionId,\n });\n return;\n }\n if (registration.type !== \"queue\") {\n this.log.warn(`Job '${jobName}' is not queue-mode — skipping`, {\n executionId,\n });\n return;\n }\n\n const promise = this.processQueueExecution(registration, executionId);\n this.inFlight.add(promise);\n try {\n await promise;\n } finally {\n this.inFlight.delete(promise);\n }\n }\n\n protected async processQueueExecution(\n registration: JobRuntimeRegistration,\n executionId: string,\n ): Promise<void> {\n const jobName = registration.name;\n const opts = registration.options;\n const record = opts.record ?? \"error\";\n\n const claimed = await this.claim(executionId);\n if (!claimed) {\n this.log.debug(`Execution ${executionId} already claimed, skipping`);\n return;\n }\n\n const execution = await this.executions.findById(executionId);\n if (!execution) return;\n\n const contextId = this.alepha.context.createContextId();\n this.perExecutionLogs.set(contextId, []);\n\n const abortController = new AbortController();\n this.abortControllers.set(executionId, abortController);\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n if (opts.timeout) {\n const ms = this.dt.duration(opts.timeout).as(\"milliseconds\");\n timeoutId = setTimeout(() => abortController.abort(), ms);\n }\n\n const now = this.dt.now();\n\n try {\n await this.alepha.context.run(\n async () => {\n await this.alepha.events.emit(\"job:begin\", {\n name: jobName,\n now,\n executionId,\n });\n\n try {\n await opts.handler({\n payload: execution.payload,\n attempt: execution.attempt,\n now,\n signal: abortController.signal,\n executionId,\n });\n\n // Success: either DELETE (keepLastSuccess=0 or record=error)\n // or UPDATE to 'ok' (record=all and keepLastSuccess>0).\n const keepSuccess =\n record === \"all\" && this.config.keepLastSuccess > 0;\n if (keepSuccess) {\n await this.executions.updateById(executionId, {\n status: \"ok\",\n completedAt: this.dt.nowISOString(),\n key: null,\n });\n } else {\n await this.executions.deleteById(executionId);\n }\n\n await this.alepha.events.emit(\n \"job:success\",\n { name: jobName, executionId },\n { catch: true },\n );\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(String(error));\n\n if (abortController.signal.aborted) {\n const current = await this.executions.findById(executionId);\n if (current?.status === \"cancelled\") {\n await this.alepha.events.emit(\n \"job:cancel\",\n { name: jobName, executionId },\n { catch: true },\n );\n return;\n }\n }\n\n await this.handleFailure(\n executionId,\n registration,\n execution.attempt,\n err,\n contextId,\n );\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n this.abortControllers.delete(executionId);\n await this.alepha.events.emit(\n \"job:end\",\n { name: jobName, executionId },\n { catch: true },\n );\n }\n },\n { context: contextId },\n );\n } finally {\n this.perExecutionLogs.delete(contextId);\n }\n }\n\n protected async claim(executionId: string): Promise<boolean> {\n const execution = await this.executions.findById(executionId);\n if (!execution) return false;\n try {\n await this.executions.updateOne(\n { id: { eq: executionId }, status: { eq: \"pending\" } },\n {\n status: \"running\",\n attempt: execution.attempt + 1,\n startedAt: this.dt.nowISOString(),\n },\n );\n return true;\n } catch {\n return false;\n }\n }\n\n protected async handleFailure(\n executionId: string,\n registration: JobRuntimeRegistration,\n currentAttempt: number,\n error: Error,\n contextId: string,\n ): Promise<void> {\n const jobName = registration.name;\n const opts = registration.options;\n const retry = opts.retry;\n const maxAttempts = (retry?.retries ?? 0) + 1;\n\n const canRetry =\n retry &&\n currentAttempt + 1 < maxAttempts &&\n (retry.when ? retry.when(error) : true);\n\n if (canRetry) {\n const nextScheduledAt = this.computeBackoff(retry, currentAttempt + 1);\n this.log.info(\n `Job '${jobName}' failed, scheduling retry ${currentAttempt + 1}/${maxAttempts}`,\n { executionId, error: error.message, nextScheduledAt },\n );\n await this.executions.updateById(executionId, {\n status: \"scheduled\",\n error: error.message,\n scheduledAt: nextScheduledAt,\n logs: this.snapshotLogs(contextId),\n });\n // Optimistic dispatch: fire a local timer so the retry runs as close to\n // `scheduledAt` as possible. The sweep is the safety net for worker\n // crashes and stateless runtimes (CF Workers, where setTimeout won't\n // survive across invocations anyway).\n const delayMs = Math.max(\n 0,\n new Date(nextScheduledAt).getTime() - this.dt.nowMillis(),\n );\n this.dt.createTimeout(() => {\n void this.dispatchScheduled(jobName, executionId);\n }, delayMs);\n } else {\n this.log.info(\n `Job '${jobName}' dead after ${currentAttempt} attempt(s)`,\n { executionId, error: error.message },\n );\n await this.executions.updateById(executionId, {\n status: \"error\",\n error: error.message,\n completedAt: this.dt.nowISOString(),\n key: null,\n logs: this.snapshotLogs(contextId),\n });\n }\n\n await this.alepha.events.emit(\n \"job:error\",\n { name: jobName, error, executionId },\n { catch: true },\n );\n }\n\n protected computeBackoff(retry: JobRetryOptions, attempt: number): string {\n const now = this.dt.now();\n if (!retry.backoff) {\n return now.add(1, \"second\").toISOString();\n }\n if (Array.isArray(retry.backoff)) {\n return now.add(this.dt.duration(retry.backoff)).toISOString();\n }\n const backoff = retry.backoff as JobRetryBackoff;\n const initial = this.dt.duration(backoff.initial).as(\"milliseconds\");\n const factor = backoff.factor ?? 2;\n let delayMs = initial * factor ** (attempt - 1);\n if (backoff.max) {\n delayMs = Math.min(\n delayMs,\n this.dt.duration(backoff.max).as(\"milliseconds\"),\n );\n }\n if (backoff.jitter) {\n delayMs = delayMs * (0.75 + Math.random() * 0.5);\n }\n return now.add(delayMs, \"millisecond\").toISOString();\n }\n\n protected snapshotLogs(contextId: string): LogEntry[] | undefined {\n const entries = this.perExecutionLogs.get(contextId);\n if (!entries || entries.length === 0) return undefined;\n const max = this.config.logMaxEntries;\n if (max === 0) return undefined;\n if (entries.length <= max) return [...entries];\n const truncated = entries.slice(0, max);\n truncated.push({\n level: \"WARN\",\n message: `Log entries truncated at ${max}`,\n timestamp: this.dt.nowMillis(),\n service: \"alepha.jobs\",\n module: \"JobProvider\",\n } as LogEntry);\n return truncated;\n }\n\n // --- Sweep ----------------------------------------------------------------------------------------------------\n\n protected async sweep(): Promise<void> {\n if (this.stopping) return;\n this.log.trace(\"Starting job sweep\");\n const now = this.dt.now();\n const nowIso = now.toISOString();\n\n try {\n // 1. Due scheduled rows → pending + dispatch\n const dueWhere = this.executions.createQueryWhere();\n dueWhere.status = { eq: \"scheduled\" };\n dueWhere.scheduledAt = { lte: nowIso };\n const due = await this.executions.findMany({\n where: dueWhere,\n orderBy: { column: \"priority\", direction: \"asc\" },\n });\n for (const exec of due) {\n if (!this.jobs.has(exec.jobName)) continue;\n await this.executions.updateById(exec.id, { status: \"pending\" });\n await this.dispatchToQueueSafe(exec.jobName, exec.id);\n }\n\n // 2. Stale pending rows → re-dispatch\n const staleIso = now\n .subtract(this.config.staleThreshold, \"millisecond\")\n .toISOString();\n const staleWhere = this.executions.createQueryWhere();\n staleWhere.status = { eq: \"pending\" };\n staleWhere.createdAt = { lte: staleIso };\n const stale = await this.executions.findMany({\n where: staleWhere,\n orderBy: { column: \"priority\", direction: \"asc\" },\n });\n for (const exec of stale) {\n if (!this.jobs.has(exec.jobName)) continue;\n await this.dispatchToQueueSafe(exec.jobName, exec.id);\n }\n\n // 3. Crashed running rows → mark as failed + apply retry\n const runningWhere = this.executions.createQueryWhere();\n runningWhere.status = { eq: \"running\" };\n const running = await this.executions.findMany({ where: runningWhere });\n const nowMs = now.valueOf();\n for (const exec of running) {\n const reg = this.jobs.get(exec.jobName);\n if (!reg) continue;\n if (this.abortControllers.has(exec.id)) continue; // still alive locally\n const crashThresholdMs = reg.options.timeout\n ? this.dt.duration(reg.options.timeout).as(\"milliseconds\") * 2\n : this.config.runTimeout;\n const startedAtMs = exec.startedAt\n ? new Date(exec.startedAt).getTime()\n : 0;\n if (startedAtMs > 0 && nowMs - startedAtMs > crashThresholdMs) {\n this.log.warn(\n `Sweep: marking crashed ${exec.jobName} (${exec.id}) as failed`,\n );\n const err = new Error(\n \"Execution assumed crashed (recovered by sweep)\",\n );\n await this.handleFailure(exec.id, reg, exec.attempt, err, \"\");\n }\n }\n\n // 4. Trim ring buffer per job\n await this.trimRingBuffers();\n } catch (e) {\n this.log.error(\"Sweep failed\", { error: e });\n }\n }\n\n protected async dispatchToQueueSafe(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n try {\n await this.dispatchToQueue(jobName, executionId);\n } catch (e) {\n this.log.warn(`Sweep failed to dispatch ${jobName} (${executionId})`, e);\n }\n }\n\n /**\n * Move a row from `scheduled` → `pending` and dispatch it.\n * Used by the optimistic retry/delay timer. If the sweep has already moved\n * the row, or another worker has claimed it, the UPDATE guard fails silently.\n */\n protected async dispatchScheduled(\n jobName: string,\n executionId: string,\n ): Promise<void> {\n if (this.stopping) return;\n try {\n await this.executions.updateOne(\n { id: { eq: executionId }, status: { eq: \"scheduled\" } },\n { status: \"pending\" },\n );\n await this.dispatchToQueueSafe(jobName, executionId);\n } catch {\n // Row already transitioned (sweep ran, another worker claimed, etc.)\n }\n }\n\n protected async trimRingBuffers(): Promise<void> {\n for (const [jobName, reg] of this.jobs) {\n const okLimit = reg.options.keep?.ok ?? this.config.keepLastSuccess;\n const errLimit = reg.options.keep?.error ?? this.config.keepLastError;\n if (okLimit > 0) {\n await this.trimByStatus(jobName, \"ok\", okLimit);\n }\n if (errLimit > 0) {\n await this.trimByStatus(jobName, \"error\", errLimit);\n }\n }\n }\n\n protected async trimByStatus(\n jobName: string,\n status: \"ok\" | \"error\",\n keep: number,\n ): Promise<void> {\n try {\n const rows = await this.executions.findMany({\n where: { jobName: { eq: jobName }, status: { eq: status } },\n orderBy: { column: \"startedAt\", direction: \"desc\" },\n limit: keep + 50,\n });\n if (rows.length <= keep) return;\n const toDelete = rows.slice(keep).map((r) => r.id);\n if (toDelete.length > 0) {\n await this.executions.deleteMany({ id: { inArray: toDelete } });\n this.log.debug(\n `Trimmed ${toDelete.length} ${status} rows for '${jobName}'`,\n );\n }\n } catch (e) {\n this.log.warn(`Failed to trim ${status} rows for '${jobName}'`, e);\n }\n }\n\n // --- Lifecycle -----------------------------------------------------------------------------------------------\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n // Validate that queue-mode jobs have a dispatcher registered.\n const needsQueue = [...this.jobs.values()].some(\n (j) => j.type === \"queue\",\n );\n if (needsQueue && !this.queueDispatch) {\n throw new AlephaError(\n `Queue-mode jobs are registered but no queue dispatcher is available. Add '.with(AlephaApiJobsQueue)' to your app.`,\n );\n }\n\n this.log.info(`Job system OK`, {\n dispatch: this.queueDispatch ? \"queue\" : \"inline-only\",\n jobs: this.jobs.size,\n });\n\n // Capture logs per execution context.\n this.alepha.events.on(\"log\", ({ entry }) => {\n const ctx = entry.context;\n if (!ctx) return;\n const entries = this.perExecutionLogs.get(ctx);\n if (!entries) return;\n entries.push(entry);\n });\n\n if (!this.alepha.isServerless()) {\n await this.sweep();\n }\n\n this.cronProvider.createCronJob(\n \"api:jobs:sweep\",\n SWEEP_CRON,\n async () => {\n await this.sweep();\n },\n true,\n );\n },\n });\n\n protected readonly onStop = $hook({\n on: \"stop\",\n handler: async () => {\n this.stopping = true;\n if (this.inFlight.size > 0) {\n this.log.info(`Draining ${this.inFlight.size} in-flight job(s)...`);\n await Promise.race([\n Promise.allSettled([...this.inFlight]),\n this.dt.wait([this.config.drainTimeout, \"millisecond\"]),\n ]);\n }\n if (this.abortControllers.size > 0) {\n this.log.warn(\n `Aborting ${this.abortControllers.size} remaining job(s) after drain timeout`,\n );\n for (const controller of this.abortControllers.values()) {\n controller.abort();\n }\n }\n },\n });\n\n // --- Helpers -------------------------------------------------------------------------------------------------\n\n protected getRegistration(name: string): JobRuntimeRegistration {\n const registration = this.jobs.get(name);\n if (!registration) {\n throw new AlephaError(`Job not registered: ${name}`);\n }\n return registration;\n }\n}\n\nexport { PRIORITY_MAP, PRIORITY_REVERSE };\n","import {\n $inject,\n type Async,\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DateTime, DurationLike } from \"alepha/datetime\";\nimport {\n JobProvider,\n type JobTriggerContext,\n type PushManyItem,\n type PushOptions,\n} from \"../providers/JobProvider.ts\";\n\n/**\n * Job primitive for defining scheduled (cron) or queued (push) tasks.\n *\n * A job must be either **cron-only** (pass `cron`) or **queue-only**\n * (pass `schema`), never both. To run scheduled work that processes\n * payloads, compose two jobs: a cron that pushes payloads, and a\n * queue job that handles them.\n */\nexport const $job = <T extends TSchema = TSchema>(\n options: JobPrimitiveOptions<T>,\n): JobPrimitive<T> => {\n return createPrimitive(JobPrimitive<T>, options);\n};\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport interface JobHandlerArgs<T extends TSchema = TSchema> {\n payload: Static<T>;\n attempt: number;\n now: DateTime;\n signal: AbortSignal;\n executionId: string;\n}\n\nexport interface JobRetryBackoff {\n initial: DurationLike;\n factor?: number;\n max?: DurationLike;\n jitter?: boolean;\n}\n\nexport interface JobRetryOptions {\n retries: number;\n backoff?: DurationLike | JobRetryBackoff;\n when?: (error: Error) => boolean;\n}\n\nexport type JobPriority = \"critical\" | \"high\" | \"normal\" | \"low\";\n\nexport interface JobPrimitiveOptions<T extends TSchema = TSchema>\n extends PipelinePrimitiveOptions {\n /**\n * Optional explicit job name. Defaults to `ClassName.propertyKey`.\n * Recommended convention for framework-internal jobs: `api:module:jobName`.\n */\n name?: string;\n\n /**\n * Human-readable description (shown in the admin UI).\n */\n description?: string;\n\n /**\n * Payload schema (TypeBox). When set, the job is queue-mode.\n * Must not be combined with `cron`.\n */\n schema?: T;\n\n /**\n * Cron expression for recurring execution. When set, the job is cron-mode.\n * Must not be combined with `schema`.\n */\n cron?: string;\n\n /**\n * Retry policy for queue-mode jobs.\n * Cron-mode jobs do not retry — the next tick re-runs.\n */\n retry?: JobRetryOptions;\n\n /**\n * Max execution time per attempt. Handler receives an `AbortSignal`.\n */\n timeout?: DurationLike;\n\n /**\n * Default priority for pushed jobs. Used by the sweep to order\n * dispatch when there is a backlog. Real-time queue consumption\n * is FIFO.\n * @default \"normal\"\n */\n priority?: JobPriority;\n\n /**\n * Whether to record successful executions.\n *\n * - `\"error\"` (default for cron, default for queue): only error/cancelled rows kept\n * - `\"all\"`: keep success rows too (bounded by `keepLastSuccess`)\n * - `\"none\"`: fire-and-forget, no row even on error\n *\n * Note: queue-mode jobs always write a `pending` row at push time (outbox).\n * This setting controls whether that row is kept on success.\n */\n record?: \"error\" | \"all\" | \"none\";\n\n /**\n * Override the global ring-buffer trim for this job.\n *\n * - `{ ok: 0, error: 0 }` — **keep forever** (no sweep trim). Useful for\n * audit-heavy jobs where retention is time-based (handled by a separate\n * cron) rather than count-based.\n * - `{ ok: 50 }` — keep last 50 successes; fall back to global default for errors.\n * - omitted — use global `keepLastSuccess` / `keepLastError` from `jobConfig`.\n */\n keep?: {\n ok?: number;\n error?: number;\n };\n\n /**\n * Handler function. For cron-mode, `payload` is `undefined`.\n */\n handler: (args: JobHandlerArgs<T>) => Async<void>;\n}\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport class JobPrimitive<\n T extends TSchema = TSchema,\n> extends PipelinePrimitive<JobPrimitiveOptions<T>> {\n protected readonly jobProvider = $inject(JobProvider);\n\n public get name(): string {\n return (\n this.options.name ??\n `${this.config.service.name}.${this.config.propertyKey}`\n );\n }\n\n protected onInit() {\n const handler = this.handler.run.bind(this.handler);\n this.jobProvider.registerJob(this.name, { ...this.options, handler });\n }\n\n /**\n * Push a single payload to the queue (queue-mode only).\n */\n public async push(\n payload: Static<T>,\n options?: PushOptions,\n ): Promise<string> {\n return this.jobProvider.push(this.name, payload, options);\n }\n\n /**\n * Push multiple payloads at once (queue-mode only).\n * Batched INSERT + batched queue send when supported.\n */\n public async pushMany(items: Array<PushManyItem<T>>): Promise<string[]> {\n return this.jobProvider.pushMany(this.name, items);\n }\n\n /**\n * Cancel a pending or running execution.\n */\n public async cancel(executionId: string): Promise<void> {\n return this.jobProvider.cancel(executionId);\n }\n\n /**\n * Manually fire a cron-mode job, or trigger a queue-mode job with an explicit payload.\n */\n public async trigger(context?: JobTriggerContext<T>): Promise<void> {\n return this.jobProvider.trigger(this.name, context);\n }\n}\n\n$job[KIND] = JobPrimitive;\n","import { $inject, Alepha, AlephaError, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, sql } from \"alepha/orm\";\nimport { NotFoundError } from \"alepha/server\";\nimport { jobExecutionEntity } from \"../entities/jobExecutionEntity.ts\";\nimport { $job } from \"../primitives/$job.ts\";\nimport type { JobTriggerContext } from \"../providers/JobProvider.ts\";\nimport { JobProvider } from \"../providers/JobProvider.ts\";\nimport type { JobExecutionQuery } from \"../schemas/jobExecutionQuerySchema.ts\";\nimport type { JobRegistration } from \"../schemas/jobRegistrationSchema.ts\";\n\n/**\n * Admin surface for the job system.\n *\n * Six methods: list jobs, list executions, get execution,\n * trigger, retry, cancel. Everything else lives in events — any\n * analytics/observability is an external concern that subscribes\n * to `job:begin` / `job:success` / `job:error`.\n */\nexport class JobService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly jobProvider = $inject(JobProvider);\n protected readonly executions = $repository(jobExecutionEntity);\n\n protected computeCan(status: string) {\n return {\n retry: status === \"error\" || status === \"cancelled\",\n cancel:\n status === \"pending\" || status === \"running\" || status === \"scheduled\",\n };\n }\n\n /**\n * List every registered job with recent ok/error counts and lastRun.\n * One aggregate query covers all jobs.\n */\n public async listJobs(): Promise<JobRegistration[]> {\n const registry = this.jobProvider.getRegisteredJobs();\n\n const aggRows = await this.executions.query(\n (e) => sql`\n SELECT\n ${e.jobName} AS job_name,\n ${e.status} AS status,\n COUNT(*) AS count,\n MAX(${e.completedAt}) AS last_run\n FROM ${e}\n WHERE ${e.status} IN ('ok', 'error')\n GROUP BY ${e.jobName}, ${e.status}\n `,\n t.object({\n job_name: t.string(),\n status: t.string(),\n count: t.string(),\n last_run: t.optional(t.nullable(t.union([t.string(), t.number()]))),\n }),\n );\n\n const toIso = (\n v: string | number | null | undefined,\n ): string | undefined => {\n if (v === null || v === undefined) return undefined;\n if (typeof v === \"number\") return new Date(v).toISOString();\n return v;\n };\n\n const byJob = new Map<\n string,\n { ok: number; error: number; lastRun?: string }\n >();\n for (const row of aggRows) {\n const entry = byJob.get(row.job_name) ?? { ok: 0, error: 0 };\n if (row.status === \"ok\") entry.ok = Number(row.count);\n if (row.status === \"error\") entry.error = Number(row.count);\n const iso = toIso(row.last_run);\n if (iso && (!entry.lastRun || iso > entry.lastRun)) {\n entry.lastRun = iso;\n }\n byJob.set(row.job_name, entry);\n }\n\n const result: JobRegistration[] = [];\n for (const [name, reg] of registry) {\n const opts = reg.options;\n const counts = byJob.get(name) ?? { ok: 0, error: 0 };\n result.push({\n name,\n description: opts.description,\n type: reg.type,\n cron: opts.cron,\n priority: (opts.priority ?? \"normal\") as JobRegistration[\"priority\"],\n timeout: opts.timeout ? String(opts.timeout) : undefined,\n retry: opts.retry\n ? {\n retries: opts.retry.retries,\n hasBackoff: Boolean(opts.retry.backoff),\n }\n : undefined,\n recent: counts,\n });\n }\n return result;\n }\n\n /**\n * Recent executions for a single job, ORDER BY startedAt DESC.\n */\n public async getExecutions(jobName: string, query: JobExecutionQuery = {}) {\n const registry = this.jobProvider.getRegisteredJobs();\n if (!registry.has(jobName)) {\n throw new NotFoundError(`Job not found: ${jobName}`);\n }\n const where = this.executions.createQueryWhere();\n where.jobName = { eq: jobName };\n if (query.status) {\n where.status = { eq: query.status };\n }\n const rows = await this.executions.findMany({\n where,\n orderBy: { column: \"startedAt\", direction: \"desc\" },\n limit: query.limit ?? 20,\n });\n return rows.map((row) => ({\n ...row,\n can: this.computeCan(row.status),\n }));\n }\n\n /**\n * Full execution detail (includes captured logs).\n */\n public async getExecution(id: string) {\n const execution = await this.executions.findById(id);\n if (!execution) {\n throw new NotFoundError(`Execution not found: ${id}`);\n }\n return {\n ...execution,\n can: this.computeCan(execution.status),\n };\n }\n\n /**\n * Manual trigger (cron jobs) or push-with-payload (queue jobs).\n */\n public async triggerJob(\n name: string,\n context?: JobTriggerContext,\n ): Promise<{ ok: boolean }> {\n const jobPrimitives = this.alepha.primitives($job);\n const job = jobPrimitives.find((j) => j.name === name);\n if (!job) {\n throw new NotFoundError(`Job not found: ${name}`);\n }\n this.log.info(`Triggering job '${name}'`, {\n triggeredBy: context?.triggeredByName ?? context?.triggeredBy,\n });\n await job.trigger(context);\n return { ok: true };\n }\n\n /**\n * Retry a dead or cancelled execution by re-pushing with the original payload.\n */\n public async retryExecution(\n id: string,\n context?: { triggeredBy?: string; triggeredByName?: string },\n ): Promise<{ ok: boolean }> {\n const execution = await this.executions.findById(id);\n if (!execution) {\n throw new NotFoundError(`Execution not found: ${id}`);\n }\n if (execution.status !== \"error\" && execution.status !== \"cancelled\") {\n throw new AlephaError(\n `Cannot retry execution in '${execution.status}' status`,\n );\n }\n\n const jobPrimitives = this.alepha.primitives($job);\n const job = jobPrimitives.find((j) => j.name === execution.jobName);\n if (!job) {\n throw new NotFoundError(`Job not found: ${execution.jobName}`);\n }\n\n this.log.info(`Retrying execution ${id}`, {\n jobName: execution.jobName,\n previousStatus: execution.status,\n triggeredBy: context?.triggeredByName ?? context?.triggeredBy,\n });\n\n if (execution.payload) {\n await job.push(execution.payload as any);\n } else {\n await job.trigger({\n triggeredBy: context?.triggeredBy,\n triggeredByName: context?.triggeredByName,\n });\n }\n return { ok: true };\n }\n\n public async cancelExecution(\n id: string,\n context?: { cancelledBy?: string; cancelledByName?: string },\n ): Promise<{ ok: boolean }> {\n this.log.info(`Cancelling execution ${id}`, {\n cancelledBy: context?.cancelledByName ?? context?.cancelledBy,\n });\n await this.jobProvider.cancel(id, {\n cancelledBy: context?.cancelledBy,\n cancelledByName: context?.cancelledByName,\n });\n return { ok: true };\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { jobExecutionQuerySchema } from \"../schemas/jobExecutionQuerySchema.ts\";\nimport { jobExecutionResourceSchema } from \"../schemas/jobExecutionResourceSchema.ts\";\nimport { jobRegistrationSchema } from \"../schemas/jobRegistrationSchema.ts\";\nimport { triggerJobSchema } from \"../schemas/triggerJobSchema.ts\";\nimport { JobService } from \"../services/JobService.ts\";\n\n/**\n * Minimal admin surface for the job system. Six endpoints.\n */\nexport class AdminJobController {\n protected readonly url: string = \"/jobs\";\n protected readonly group: string = \"admin:jobs\";\n protected readonly jobService = $inject(JobService);\n\n public readonly listJobs = $action({\n path: this.url,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n response: t.array(jobRegistrationSchema),\n },\n handler: () => this.jobService.listJobs(),\n });\n\n public readonly listExecutions = $action({\n path: `${this.url}/:name/executions`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n params: t.object({ name: t.text() }),\n query: jobExecutionQuerySchema,\n response: t.array(jobExecutionResourceSchema),\n },\n handler: ({ params, query }) =>\n this.jobService.getExecutions(params.name, query),\n });\n\n public readonly getExecution = $action({\n path: `${this.url}/executions/:id`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:read\"] })],\n schema: {\n params: t.object({ id: t.uuid() }),\n response: jobExecutionResourceSchema,\n },\n handler: ({ params }) => this.jobService.getExecution(params.id),\n });\n\n public readonly triggerJob = $action({\n method: \"POST\",\n path: `${this.url}/:name/trigger`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:trigger\"] })],\n schema: {\n params: t.object({ name: t.text() }),\n body: triggerJobSchema,\n response: okSchema,\n },\n handler: ({ params, body, user }) =>\n this.jobService.triggerJob(params.name, {\n payload: body.payload,\n triggeredBy: user?.id,\n triggeredByName: user?.name,\n }),\n });\n\n public readonly retryExecution = $action({\n method: \"POST\",\n path: `${this.url}/executions/:id/retry`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:trigger\"] })],\n schema: {\n params: t.object({ id: t.uuid() }),\n response: okSchema,\n },\n handler: ({ params, user }) =>\n this.jobService.retryExecution(params.id, {\n triggeredBy: user?.id,\n triggeredByName: user?.name,\n }),\n });\n\n public readonly cancelExecution = $action({\n method: \"POST\",\n path: `${this.url}/executions/:id/cancel`,\n group: this.group,\n use: [$secure({ permissions: [\"admin:job:cancel\"] })],\n schema: {\n params: t.object({ id: t.uuid() }),\n response: okSchema,\n },\n handler: ({ params, user }) =>\n this.jobService.cancelExecution(params.id, {\n cancelledBy: user?.id,\n cancelledByName: user?.name,\n }),\n });\n}\n","import { $inject, t } from \"alepha\";\nimport { $queue } from \"alepha/queue\";\nimport { JobProvider } from \"./JobProvider.ts\";\n\n/**\n * Plumbs outbox-style dispatch through `AlephaQueue`.\n *\n * Registered only when the app imports `AlephaApiJobsQueue`. Sets\n * `JobProvider.queueDispatch` eagerly at instantiation so queue-mode jobs\n * can dispatch regardless of start-hook ordering.\n */\nexport class JobQueueProvider {\n protected readonly jobProvider = $inject(JobProvider);\n\n protected readonly queue = $queue({\n name: \"api:jobs:dispatch\",\n schema: t.object({ jobName: t.text(), executionId: t.text() }),\n handler: async (msg) => {\n await this.jobProvider.processExecution(\n msg.payload.jobName,\n msg.payload.executionId,\n );\n },\n });\n\n constructor() {\n // Install the dispatcher immediately — before any start hook runs,\n // so JobProvider can validate presence and queue-mode push works\n // from any lifecycle point.\n this.wireDispatcher();\n }\n\n protected wireDispatcher(): void {\n // `$inject` is resolved by the time constructor runs; assignment is safe.\n this.jobProvider.queueDispatch = (jobName, executionId) =>\n this.push(jobName, executionId);\n }\n\n public async push(jobName: string, executionId: string): Promise<void> {\n await this.queue.push({ jobName, executionId });\n }\n}\n","import { $module } from \"alepha\";\nimport type { DateTime } from \"alepha/datetime\";\nimport { AlephaLock } from \"alepha/lock\";\nimport { AlephaQueue } from \"alepha/queue\";\nimport { AlephaScheduler } from \"alepha/scheduler\";\nimport { AdminJobController } from \"./controllers/AdminJobController.ts\";\nimport { JobProvider } from \"./providers/JobProvider.ts\";\nimport { JobQueueProvider } from \"./providers/JobQueueProvider.ts\";\nimport { JobService } from \"./services/JobService.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/AdminJobController.ts\";\nexport * from \"./entities/jobExecutionEntity.ts\";\nexport * from \"./primitives/$job.ts\";\nexport * from \"./providers/JobProvider.ts\";\nexport * from \"./providers/JobQueueProvider.ts\";\nexport * from \"./schemas/jobConfigAtom.ts\";\nexport * from \"./schemas/jobExecutionQuerySchema.ts\";\nexport * from \"./schemas/jobExecutionResourceSchema.ts\";\nexport * from \"./schemas/jobRegistrationSchema.ts\";\nexport * from \"./schemas/triggerJobSchema.ts\";\nexport * from \"./services/JobService.ts\";\n\n// -----------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"job:begin\": { name: string; now: DateTime; executionId: string };\n \"job:success\": { name: string; executionId: string };\n \"job:error\": { name: string; error: Error; executionId: string };\n \"job:cancel\": { name: string; executionId: string };\n \"job:end\": { name: string; executionId: string };\n }\n}\n\n// -----------------------------------------------------------------------------------------------------------------\n\n/**\n * Job execution framework — cron and durable queue work with a single primitive.\n *\n * A `$job` is either **cron-only** (declares `cron`) or **queue-only** (declares `schema`).\n * Cron jobs run inline on their schedule and only record errors by default.\n * Queue jobs use the outbox pattern: push commits to DB first, then notifies via queue.\n *\n * **This module provides cron support only.** To enable queue-mode jobs, also\n * import {@link AlephaApiJobsQueue} — it brings in the queue layer and infrastructure\n * binding (e.g. Cloudflare Queues). Cron-only deployments (Vercel, CF-without-Queues)\n * do not need `AlephaApiJobsQueue`.\n *\n * @module alepha.api.jobs\n */\nexport const AlephaApiJobs = $module({\n name: \"alepha.api.jobs\",\n imports: [AlephaScheduler, AlephaLock],\n services: [JobProvider, JobService, AdminJobController],\n});\n\n/**\n * Queue support for `$job`. Import alongside {@link AlephaApiJobs} when your\n * app declares queue-mode jobs (any `$job` with a `schema`).\n *\n * Adds `JobQueueProvider` which plumbs the outbox dispatch through `AlephaQueue`.\n *\n * @module alepha.api.jobs.queue\n */\nexport const AlephaApiJobsQueue = $module({\n name: \"alepha.api.jobs.queue\",\n imports: [AlephaApiJobs, AlephaQueue],\n services: [JobQueueProvider],\n});\n"],"mappings":"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"}
|