alepha 0.20.4 → 0.20.6
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/audits/index.d.ts +391 -359
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +23 -1
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +18 -0
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +51 -0
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +33 -14
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +452 -155
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +474 -159
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +32 -4
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js +53 -0
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +29 -1
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +55 -13
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/organizations/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +15 -0
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +37 -0
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/users/index.d.ts +150 -9
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +237 -28
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +3 -3
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bin/index.js +0 -0
- package/dist/bucket/index.d.ts +18 -0
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +47 -0
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +24 -0
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +20 -3
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/database/index.d.ts +155 -0
- package/dist/cache/database/index.d.ts.map +1 -0
- package/dist/cache/database/index.js +266 -0
- package/dist/cache/database/index.js.map +1 -0
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +35 -5
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +85 -6
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/platform/index.js +1 -1
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.browser.js.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/brevo/index.js.map +1 -1
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/core/index.workerd.js.map +1 -1
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.js +2 -0
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.browser.js.map +1 -1
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/react/websocket/index.js.map +1 -1
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +22 -0
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +12 -0
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +12 -0
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.js.map +1 -1
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/topic/redis/index.js.map +1 -1
- package/dist/websocket/index.browser.js +4 -0
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.js +10 -0
- package/dist/websocket/index.js.map +1 -1
- package/package.json +282 -272
- package/src/api/audits/controllers/AdminAuditController.ts +29 -0
- package/src/api/files/controllers/FileController.ts +24 -0
- package/src/api/files/services/FileService.ts +41 -0
- package/src/api/jobs/__tests__/$job.spec.ts +427 -2
- package/src/api/jobs/entities/jobExecutionEntity.ts +3 -3
- package/src/api/jobs/index.ts +47 -10
- package/src/api/jobs/primitives/$job.ts +22 -9
- package/src/api/jobs/providers/DirectJobDispatcher.ts +71 -0
- package/src/api/jobs/providers/JobDispatcher.ts +49 -0
- package/src/api/jobs/providers/JobProvider.ts +365 -142
- package/src/api/jobs/providers/JobQueueProvider.ts +43 -18
- package/src/api/jobs/schemas/jobConfigAtom.ts +4 -3
- package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +11 -0
- package/src/api/jobs/schemas/jobRegistrationSchema.ts +4 -2
- package/src/api/jobs/services/JobService.ts +21 -11
- package/src/api/keys/controllers/AdminApiKeyController.ts +23 -0
- package/src/api/keys/services/ApiKeyService.ts +42 -0
- package/src/api/notifications/__tests__/AlephaApiNotifications.spec.ts +63 -0
- package/src/api/notifications/controllers/AdminNotificationController.ts +48 -1
- package/src/api/notifications/index.ts +13 -3
- package/src/api/notifications/jobs/NotificationJobs.ts +0 -6
- package/src/api/parameters/controllers/AdminParameterController.ts +26 -0
- package/src/api/parameters/services/ParameterProvider.ts +18 -0
- package/src/api/users/__tests__/Registration-emailMode.spec.ts +203 -0
- package/src/api/users/__tests__/UsernameSlugger.spec.ts +138 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +41 -3
- package/src/api/users/controllers/AdminSessionController.ts +29 -0
- package/src/api/users/controllers/AdminUserController.ts +32 -0
- package/src/api/users/index.ts +3 -0
- package/src/api/users/services/CredentialService.ts +5 -0
- package/src/api/users/services/RegistrationService.ts +49 -1
- package/src/api/users/services/SessionCrudService.ts +16 -0
- package/src/api/users/services/SessionService.ts +17 -59
- package/src/api/users/services/UsernameSlugger.ts +195 -0
- package/src/bucket/primitives/$bucket.ts +21 -0
- package/src/bucket/providers/CloudflareR2Provider.ts +15 -0
- package/src/bucket/providers/FileStorageProvider.ts +9 -0
- package/src/bucket/providers/LocalFileStorageProvider.ts +14 -0
- package/src/bucket/providers/MemoryFileStorageProvider.ts +9 -0
- package/src/bucket/providers/NodeS3BucketProvider.ts +35 -0
- package/src/cache/core/primitives/$cache.ts +20 -3
- package/src/cache/database/__tests__/DatabaseCacheProvider.behavior.spec.ts +203 -0
- package/src/cache/database/__tests__/DatabaseCacheProvider.spec.ts +110 -0
- package/src/cache/database/entities/cacheEntries.ts +55 -0
- package/src/cache/database/index.ts +36 -0
- package/src/cache/database/providers/DatabaseCacheProvider.ts +348 -0
- package/src/cli/core/services/ProjectScaffolder.ts +0 -2
- package/src/cli/core/tasks/BuildCloudflareTask.ts +17 -3
- package/src/cli/core/tasks/BuildSitemapTask.ts +7 -0
- package/src/cli/core/tasks/BuildVercelTask.ts +82 -3
- package/src/cli/platform/__tests__/detectResources.spec.ts +96 -0
- package/src/cli/platform/commands/platform.ts +7 -1
- package/src/scheduler/index.ts +14 -0
- package/src/scheduler/providers/CronProvider.ts +13 -0
package/src/api/jobs/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { AlephaLock } from "alepha/lock";
|
|
|
4
4
|
import { AlephaQueue } from "alepha/queue";
|
|
5
5
|
import { AlephaScheduler } from "alepha/scheduler";
|
|
6
6
|
import { AdminJobController } from "./controllers/AdminJobController.ts";
|
|
7
|
+
import { $job } from "./primitives/$job.ts";
|
|
8
|
+
import { DirectJobDispatcher } from "./providers/DirectJobDispatcher.ts";
|
|
7
9
|
import { JobProvider } from "./providers/JobProvider.ts";
|
|
8
10
|
import { JobQueueProvider } from "./providers/JobQueueProvider.ts";
|
|
9
11
|
import { JobService } from "./services/JobService.ts";
|
|
@@ -13,6 +15,8 @@ import { JobService } from "./services/JobService.ts";
|
|
|
13
15
|
export * from "./controllers/AdminJobController.ts";
|
|
14
16
|
export * from "./entities/jobExecutionEntity.ts";
|
|
15
17
|
export * from "./primitives/$job.ts";
|
|
18
|
+
export * from "./providers/DirectJobDispatcher.ts";
|
|
19
|
+
export * from "./providers/JobDispatcher.ts";
|
|
16
20
|
export * from "./providers/JobProvider.ts";
|
|
17
21
|
export * from "./providers/JobQueueProvider.ts";
|
|
18
22
|
export * from "./schemas/jobConfigAtom.ts";
|
|
@@ -39,28 +43,61 @@ declare module "alepha" {
|
|
|
39
43
|
/**
|
|
40
44
|
* Job execution framework — cron and durable queue work with a single primitive.
|
|
41
45
|
*
|
|
42
|
-
* A `$job` is either **cron-only** (declares `cron`) or **
|
|
43
|
-
* Cron jobs run inline on their schedule and only record errors by default.
|
|
44
|
-
* Queue jobs use the outbox pattern: push commits to DB first, then notifies via queue.
|
|
46
|
+
* A `$job` is either **cron-only** (declares `cron`) or **payload-only** (declares `schema`).
|
|
45
47
|
*
|
|
46
|
-
* **
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* **Three runtime modes:**
|
|
49
|
+
*
|
|
50
|
+
* - **cron** — fires on a schedule. Cron-mode jobs are protected by a
|
|
51
|
+
* distributed lock by default (`lock: true`), so multi-replica Docker
|
|
52
|
+
* deployments only run the handler once per tick. Override with
|
|
53
|
+
* `lock: false` if you genuinely want every replica to fire.
|
|
54
|
+
* - **queue** — push-driven, dispatched through the queue infrastructure
|
|
55
|
+
* (`AlephaQueue`, e.g. Cloudflare Queues, Redis). Real-time delivery,
|
|
56
|
+
* ideal for high-volume systems. Requires `AlephaApiJobsQueue`.
|
|
57
|
+
* - **direct** — push-driven, processed in-process right after the caller
|
|
58
|
+
* awaits the push. The DB outbox row is the durability guarantee — if
|
|
59
|
+
* the process dies, the reconciliation sweep re-dispatches. Default
|
|
60
|
+
* when `AlephaApiJobsQueue` is *not* loaded. Best for cheap deployments
|
|
61
|
+
* (Cloudflare Workers, single-instance Node) where standing up a queue
|
|
62
|
+
* is overkill.
|
|
63
|
+
*
|
|
64
|
+
* **Retries** are sweep-driven across all modes (no exponential backoff).
|
|
65
|
+
* Granularity is bounded by `sweepCron` (default 5 min). The first retry
|
|
66
|
+
* may land anywhere from a few seconds to ~5 min later depending on when
|
|
67
|
+
* the next sweep tick fires. Cron jobs that declare `retry` go through
|
|
68
|
+
* the same sweep path — a transient failure no longer means waiting for
|
|
69
|
+
* the next cron tick (useful for once-daily jobs).
|
|
70
|
+
*
|
|
71
|
+
* **Runtime support for cron triggers**
|
|
72
|
+
*
|
|
73
|
+
* - **Long-running Node / Docker** — `CronProvider` runs an in-process
|
|
74
|
+
* timer loop. Multi-replica deployments serialize ticks via the cron
|
|
75
|
+
* lock (see `$job.lock`).
|
|
76
|
+
* - **Cloudflare Workers** — the build emits cron expressions into
|
|
77
|
+
* `wrangler.jsonc`; Cloudflare invokes the worker on schedule and the
|
|
78
|
+
* `cloudflare:scheduled` hook routes the event to the matching jobs.
|
|
79
|
+
* - **Vercel** — the build emits cron entries into
|
|
80
|
+
* `.vercel/output/config.json` mapped to `/_alepha/cron/:name`; the
|
|
81
|
+
* serverless handler emits `serverless:cron` and `CronProvider` runs
|
|
82
|
+
* the matching job. Set `CRON_SECRET` to require authenticated calls.
|
|
50
83
|
*
|
|
51
84
|
* @module alepha.api.jobs
|
|
52
85
|
*/
|
|
53
86
|
export const AlephaApiJobs = $module({
|
|
54
87
|
name: "alepha.api.jobs",
|
|
88
|
+
primitives: [$job],
|
|
55
89
|
imports: [AlephaScheduler, AlephaLock],
|
|
56
|
-
services: [JobProvider, JobService, AdminJobController],
|
|
90
|
+
services: [JobProvider, JobService, AdminJobController, DirectJobDispatcher],
|
|
57
91
|
});
|
|
58
92
|
|
|
59
93
|
/**
|
|
60
94
|
* Queue support for `$job`. Import alongside {@link AlephaApiJobs} when your
|
|
61
|
-
* app declares queue-mode jobs (any `$job` with a `schema`)
|
|
95
|
+
* app declares queue-mode jobs (any `$job` with a `schema`) and you want a
|
|
96
|
+
* real queue (e.g. Cloudflare Queues, Redis) instead of in-process direct
|
|
97
|
+
* execution.
|
|
62
98
|
*
|
|
63
|
-
* Adds `JobQueueProvider`
|
|
99
|
+
* Adds `JobQueueProvider` to the container. `JobProvider` detects its
|
|
100
|
+
* presence at start-up and routes dispatches through it.
|
|
64
101
|
*
|
|
65
102
|
* @module alepha.api.jobs.queue
|
|
66
103
|
*/
|
|
@@ -40,16 +40,8 @@ export interface JobHandlerArgs<T extends TSchema = TSchema> {
|
|
|
40
40
|
executionId: string;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export interface JobRetryBackoff {
|
|
44
|
-
initial: DurationLike;
|
|
45
|
-
factor?: number;
|
|
46
|
-
max?: DurationLike;
|
|
47
|
-
jitter?: boolean;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
43
|
export interface JobRetryOptions {
|
|
51
44
|
retries: number;
|
|
52
|
-
backoff?: DurationLike | JobRetryBackoff;
|
|
53
45
|
when?: (error: Error) => boolean;
|
|
54
46
|
}
|
|
55
47
|
|
|
@@ -81,11 +73,32 @@ export interface JobPrimitiveOptions<T extends TSchema = TSchema>
|
|
|
81
73
|
cron?: string;
|
|
82
74
|
|
|
83
75
|
/**
|
|
84
|
-
* Retry policy for queue-mode jobs.
|
|
76
|
+
* Retry policy for queue-mode and direct-mode jobs.
|
|
85
77
|
* Cron-mode jobs do not retry — the next tick re-runs.
|
|
78
|
+
*
|
|
79
|
+
* Retries are picked up by the reconciliation sweep, so retry granularity
|
|
80
|
+
* is bounded by `sweepCron` (default 5 minutes). The first retry may run
|
|
81
|
+
* earlier than 5 minutes if the sweep tick happens sooner.
|
|
86
82
|
*/
|
|
87
83
|
retry?: JobRetryOptions;
|
|
88
84
|
|
|
85
|
+
/**
|
|
86
|
+
* **Cron-mode only.** Whether to acquire a distributed lock around the
|
|
87
|
+
* cron tick so that only one instance of a multi-replica deployment runs
|
|
88
|
+
* the handler per tick.
|
|
89
|
+
*
|
|
90
|
+
* Has **no effect** on queue-mode and direct-mode jobs — those rely on
|
|
91
|
+
* the outbox `claim()` UPDATE-guard to serialize work instead, which is
|
|
92
|
+
* always on.
|
|
93
|
+
*
|
|
94
|
+
* To get cross-instance coordination on Docker / Node deployments,
|
|
95
|
+
* register a real `LockProvider` (e.g. `alepha/lock/redis`). The default
|
|
96
|
+
* `MemoryLockProvider` is per-process only.
|
|
97
|
+
*
|
|
98
|
+
* @default true
|
|
99
|
+
*/
|
|
100
|
+
lock?: boolean;
|
|
101
|
+
|
|
89
102
|
/**
|
|
90
103
|
* Max execution time per attempt. Handler receives an `AbortSignal`.
|
|
91
104
|
*/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { $inject, Alepha } from "alepha";
|
|
2
|
+
import { $logger } from "alepha/logger";
|
|
3
|
+
import { JobDispatcher } from "./JobDispatcher.ts";
|
|
4
|
+
import { JobProvider } from "./JobProvider.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default `JobDispatcher` for environments without `AlephaApiJobsQueue`.
|
|
8
|
+
*
|
|
9
|
+
* Runs `JobProvider.processExecution` in the background — the caller's
|
|
10
|
+
* `push()` returns immediately while the handler continues to completion
|
|
11
|
+
* in the same process. The DB outbox row is the durability guarantee:
|
|
12
|
+
* if the process dies before the handler finishes, the next sweep tick
|
|
13
|
+
* picks the row up and re-dispatches.
|
|
14
|
+
*
|
|
15
|
+
* **Cloudflare Workers** — when an `executionCtx.waitUntil` is available
|
|
16
|
+
* in the alepha store at `cloudflare.waitUntil`, the dispatch wraps the
|
|
17
|
+
* background promise with `waitUntil` so the runtime keeps the isolate
|
|
18
|
+
* alive past the HTTP response. Without this, the handler would be
|
|
19
|
+
* terminated when the response is returned and only the next sweep
|
|
20
|
+
* (every 5 min by default) would re-dispatch.
|
|
21
|
+
*
|
|
22
|
+
* **Vercel / single-Node** — on long-running runtimes the event loop
|
|
23
|
+
* keeps the promise alive naturally; no special wiring is required.
|
|
24
|
+
*/
|
|
25
|
+
export class DirectJobDispatcher extends JobDispatcher {
|
|
26
|
+
public readonly kind = "direct" as const;
|
|
27
|
+
|
|
28
|
+
protected readonly alepha = $inject(Alepha);
|
|
29
|
+
protected readonly log = $logger();
|
|
30
|
+
|
|
31
|
+
// Lazy: resolved on first dispatch to break the JobProvider ↔ Dispatcher
|
|
32
|
+
// injection cycle (JobProvider injects JobDispatcher to dispatch; we need
|
|
33
|
+
// JobProvider to actually run executions).
|
|
34
|
+
protected jobProviderRef?: JobProvider;
|
|
35
|
+
protected getJobProvider(): JobProvider {
|
|
36
|
+
if (!this.jobProviderRef) {
|
|
37
|
+
this.jobProviderRef = this.alepha.inject(JobProvider);
|
|
38
|
+
}
|
|
39
|
+
return this.jobProviderRef;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async dispatch(jobName: string, executionId: string): Promise<void> {
|
|
43
|
+
const promise = this.getJobProvider()
|
|
44
|
+
.processExecution(jobName, executionId)
|
|
45
|
+
.catch((err) => {
|
|
46
|
+
this.log.warn(
|
|
47
|
+
`Direct execution failed for '${jobName}' (sweep will retry)`,
|
|
48
|
+
err,
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Cloudflare Workers: keep the isolate alive past the HTTP response so
|
|
53
|
+
// the handler actually finishes. Outside CF this read returns undefined
|
|
54
|
+
// and we fall through to plain fire-and-track.
|
|
55
|
+
const waitUntil = this.alepha.store.get("cloudflare.waitUntil") as
|
|
56
|
+
| ((p: Promise<unknown>) => void)
|
|
57
|
+
| undefined;
|
|
58
|
+
if (typeof waitUntil === "function") {
|
|
59
|
+
try {
|
|
60
|
+
waitUntil(promise);
|
|
61
|
+
} catch (e) {
|
|
62
|
+
// The runtime may reject waitUntil if called outside a request scope.
|
|
63
|
+
// Promise still runs; just log.
|
|
64
|
+
this.log.debug(
|
|
65
|
+
"waitUntil rejected — falling back to fire-and-track",
|
|
66
|
+
e,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract dispatcher for queued/direct job executions.
|
|
3
|
+
*
|
|
4
|
+
* The default implementation, {@link DirectJobDispatcher}, runs the handler
|
|
5
|
+
* in-process after the caller's `push()` returns — fast and dependency-free.
|
|
6
|
+
*
|
|
7
|
+
* `AlephaApiJobsQueue` substitutes this with `JobQueueProvider`, which
|
|
8
|
+
* publishes the executionId to `AlephaQueue` so a worker pool can consume
|
|
9
|
+
* the work asynchronously.
|
|
10
|
+
*
|
|
11
|
+
* Substitute via DI:
|
|
12
|
+
* ```ts
|
|
13
|
+
* Alepha.create()
|
|
14
|
+
* .with({ provide: JobDispatcher, use: MyCustomDispatcher })
|
|
15
|
+
* .with(AlephaApiJobs);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* The `kind` getter is read by the `JobProvider.effectiveMode` accessor
|
|
19
|
+
* and by the admin UI so users can see which dispatcher is currently active.
|
|
20
|
+
*/
|
|
21
|
+
export abstract class JobDispatcher {
|
|
22
|
+
/**
|
|
23
|
+
* Identifier for this dispatcher's effective mode. Reported to the admin
|
|
24
|
+
* UI so operators can see whether `$job` is running in `queue` or
|
|
25
|
+
* `direct` mode.
|
|
26
|
+
*/
|
|
27
|
+
public abstract readonly kind: "queue" | "direct";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Hand off a single execution. The caller's `push()` awaits this so the
|
|
31
|
+
* caller can be sure the dispatch has at least been initiated. Long-running
|
|
32
|
+
* work must NOT be awaited here (use background scheduling instead) — this
|
|
33
|
+
* call should return as quickly as possible.
|
|
34
|
+
*/
|
|
35
|
+
public abstract dispatch(jobName: string, executionId: string): Promise<void>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Optional batch dispatch. The default implementation loops, but
|
|
39
|
+
* dispatchers backed by a real queue should override this to use the
|
|
40
|
+
* provider's batch send (e.g. Cloudflare Queues `sendBatch`).
|
|
41
|
+
*/
|
|
42
|
+
public async dispatchMany(
|
|
43
|
+
items: Array<{ jobName: string; executionId: string }>,
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
for (const item of items) {
|
|
46
|
+
await this.dispatch(item.jobName, item.executionId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|