@pattern-stack/codegen 0.2.0
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/CHANGELOG.md +67 -0
- package/README.md +214 -0
- package/dist/runtime/analytics/index.d.ts +6 -0
- package/dist/runtime/analytics/index.js +49 -0
- package/dist/runtime/analytics/index.js.map +1 -0
- package/dist/runtime/analytics/metrics.d.ts +75 -0
- package/dist/runtime/analytics/metrics.js +1 -0
- package/dist/runtime/analytics/metrics.js.map +1 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.d.ts +21 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.js +1 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.js.map +1 -0
- package/dist/runtime/analytics/packs/index.d.ts +3 -0
- package/dist/runtime/analytics/packs/index.js +1 -0
- package/dist/runtime/analytics/packs/index.js.map +1 -0
- package/dist/runtime/analytics/packs/monetary-measures.d.ts +21 -0
- package/dist/runtime/analytics/packs/monetary-measures.js +1 -0
- package/dist/runtime/analytics/packs/monetary-measures.js.map +1 -0
- package/dist/runtime/analytics/specs.d.ts +49 -0
- package/dist/runtime/analytics/specs.js +1 -0
- package/dist/runtime/analytics/specs.js.map +1 -0
- package/dist/runtime/analytics/types.d.ts +85 -0
- package/dist/runtime/analytics/types.js +49 -0
- package/dist/runtime/analytics/types.js.map +1 -0
- package/dist/runtime/base-classes/activity-entity-repository.d.ts +26 -0
- package/dist/runtime/base-classes/activity-entity-repository.js +195 -0
- package/dist/runtime/base-classes/activity-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/activity-entity-service.d.ts +39 -0
- package/dist/runtime/base-classes/activity-entity-service.js +214 -0
- package/dist/runtime/base-classes/activity-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/base-read-use-cases.d.ts +68 -0
- package/dist/runtime/base-classes/base-read-use-cases.js +32 -0
- package/dist/runtime/base-classes/base-read-use-cases.js.map +1 -0
- package/dist/runtime/base-classes/base-repository.d.ts +99 -0
- package/dist/runtime/base-classes/base-repository.js +160 -0
- package/dist/runtime/base-classes/base-repository.js.map +1 -0
- package/dist/runtime/base-classes/base-service.d.ts +98 -0
- package/dist/runtime/base-classes/base-service.js +186 -0
- package/dist/runtime/base-classes/base-service.js.map +1 -0
- package/dist/runtime/base-classes/index.d.ts +18 -0
- package/dist/runtime/base-classes/index.js +617 -0
- package/dist/runtime/base-classes/index.js.map +1 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.d.ts +17 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.js +166 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/knowledge-entity-service.d.ts +15 -0
- package/dist/runtime/base-classes/knowledge-entity-service.js +192 -0
- package/dist/runtime/base-classes/knowledge-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/lifecycle-events.d.ts +49 -0
- package/dist/runtime/base-classes/lifecycle-events.js +76 -0
- package/dist/runtime/base-classes/lifecycle-events.js.map +1 -0
- package/dist/runtime/base-classes/metadata-entity-repository.d.ts +27 -0
- package/dist/runtime/base-classes/metadata-entity-repository.js +212 -0
- package/dist/runtime/base-classes/metadata-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/metadata-entity-service.d.ts +39 -0
- package/dist/runtime/base-classes/metadata-entity-service.js +214 -0
- package/dist/runtime/base-classes/metadata-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/synced-entity-repository.d.ts +32 -0
- package/dist/runtime/base-classes/synced-entity-repository.js +203 -0
- package/dist/runtime/base-classes/synced-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/synced-entity-service.d.ts +41 -0
- package/dist/runtime/base-classes/synced-entity-service.js +215 -0
- package/dist/runtime/base-classes/synced-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/with-analytics.d.ts +18 -0
- package/dist/runtime/base-classes/with-analytics.js +11 -0
- package/dist/runtime/base-classes/with-analytics.js.map +1 -0
- package/dist/runtime/constants/tokens.d.ts +29 -0
- package/dist/runtime/constants/tokens.js +8 -0
- package/dist/runtime/constants/tokens.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.d.ts +30 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.js +1 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics.module.d.ts +34 -0
- package/dist/runtime/subsystems/analytics/analytics.module.js +117 -0
- package/dist/runtime/subsystems/analytics/analytics.module.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.d.ts +24 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.js +10 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.js.map +1 -0
- package/dist/runtime/subsystems/analytics/cube-backend.d.ts +28 -0
- package/dist/runtime/subsystems/analytics/cube-backend.js +71 -0
- package/dist/runtime/subsystems/analytics/cube-backend.js.map +1 -0
- package/dist/runtime/subsystems/analytics/index.d.ts +6 -0
- package/dist/runtime/subsystems/analytics/index.js +122 -0
- package/dist/runtime/subsystems/analytics/index.js.map +1 -0
- package/dist/runtime/subsystems/analytics/noop-backend.d.ts +7 -0
- package/dist/runtime/subsystems/analytics/noop-backend.js +25 -0
- package/dist/runtime/subsystems/analytics/noop-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.d.ts +43 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.js +133 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.d.ts +21 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.js +100 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.module.d.ts +37 -0
- package/dist/runtime/subsystems/cache/cache.module.js +272 -0
- package/dist/runtime/subsystems/cache/cache.module.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.protocol.d.ts +42 -0
- package/dist/runtime/subsystems/cache/cache.protocol.js +1 -0
- package/dist/runtime/subsystems/cache/cache.protocol.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.schema.d.ts +64 -0
- package/dist/runtime/subsystems/cache/cache.schema.js +18 -0
- package/dist/runtime/subsystems/cache/cache.schema.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.tokens.d.ts +18 -0
- package/dist/runtime/subsystems/cache/cache.tokens.js +8 -0
- package/dist/runtime/subsystems/cache/cache.tokens.js.map +1 -0
- package/dist/runtime/subsystems/cache/index.d.ts +11 -0
- package/dist/runtime/subsystems/cache/index.js +277 -0
- package/dist/runtime/subsystems/cache/index.js.map +1 -0
- package/dist/runtime/subsystems/events/domain-events.schema.d.ts +187 -0
- package/dist/runtime/subsystems/events/domain-events.schema.js +32 -0
- package/dist/runtime/subsystems/events/domain-events.schema.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +38 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +199 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +18 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js +71 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.d.ts +52 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.js +1 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.d.ts +95 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.js +229 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/events.module.d.ts +46 -0
- package/dist/runtime/subsystems/events/events.module.js +531 -0
- package/dist/runtime/subsystems/events/events.module.js.map +1 -0
- package/dist/runtime/subsystems/events/events.tokens.d.ts +19 -0
- package/dist/runtime/subsystems/events/events.tokens.js +8 -0
- package/dist/runtime/subsystems/events/events.tokens.js.map +1 -0
- package/dist/runtime/subsystems/events/index.d.ts +12 -0
- package/dist/runtime/subsystems/events/index.js +536 -0
- package/dist/runtime/subsystems/events/index.js.map +1 -0
- package/dist/runtime/subsystems/index.d.ts +24 -0
- package/dist/runtime/subsystems/index.js +1643 -0
- package/dist/runtime/subsystems/index.js.map +1 -0
- package/dist/runtime/subsystems/jobs/index.d.ts +14 -0
- package/dist/runtime/subsystems/jobs/index.js +680 -0
- package/dist/runtime/subsystems/jobs/index.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.d.ts +54 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js +186 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.d.ts +38 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js +228 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.d.ts +12 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js +44 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.d.ts +48 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.js +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.d.ts +46 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js +187 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.d.ts +237 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.js +44 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.js.map +1 -0
- package/dist/runtime/subsystems/jobs/jobs.module.d.ts +18 -0
- package/dist/runtime/subsystems/jobs/jobs.module.js +676 -0
- package/dist/runtime/subsystems/jobs/jobs.module.js.map +1 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.d.ts +13 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.js +8 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.js.map +1 -0
- package/dist/runtime/subsystems/storage/index.d.ts +6 -0
- package/dist/runtime/subsystems/storage/index.js +204 -0
- package/dist/runtime/subsystems/storage/index.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.d.ts +18 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.js +108 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.d.ts +28 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.js +72 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.module.d.ts +40 -0
- package/dist/runtime/subsystems/storage/storage.module.js +201 -0
- package/dist/runtime/subsystems/storage/storage.module.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.protocol.d.ts +69 -0
- package/dist/runtime/subsystems/storage/storage.protocol.js +1 -0
- package/dist/runtime/subsystems/storage/storage.protocol.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.tokens.d.ts +11 -0
- package/dist/runtime/subsystems/storage/storage.tokens.js +6 -0
- package/dist/runtime/subsystems/storage/storage.tokens.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.utils.d.ts +9 -0
- package/dist/runtime/subsystems/storage/storage.utils.js +18 -0
- package/dist/runtime/subsystems/storage/storage.utils.js.map +1 -0
- package/dist/runtime/types/drizzle.d.ts +17 -0
- package/dist/runtime/types/drizzle.js +1 -0
- package/dist/runtime/types/drizzle.js.map +1 -0
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +7365 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/index.d.ts +2384 -0
- package/dist/src/index.js +2198 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +114 -0
- package/templates/broadcast/new/backend-interface.ejs.t +47 -0
- package/templates/broadcast/new/bridge-listener.ejs.t +67 -0
- package/templates/broadcast/new/channel.ejs.t +77 -0
- package/templates/broadcast/new/index.ejs.t +21 -0
- package/templates/broadcast/new/memory-backend.ejs.t +87 -0
- package/templates/broadcast/new/module.ejs.t +57 -0
- package/templates/broadcast/new/prompt.js +268 -0
- package/templates/broadcast/new/websocket-backend.ejs.t +259 -0
- package/templates/entity/new/backend/application/commands/create.ejs.t +55 -0
- package/templates/entity/new/backend/application/commands/delete.ejs.t +45 -0
- package/templates/entity/new/backend/application/commands/grouped-index.ejs.t +149 -0
- package/templates/entity/new/backend/application/commands/index.ejs.t +15 -0
- package/templates/entity/new/backend/application/commands/update.ejs.t +58 -0
- package/templates/entity/new/backend/application/queries/declarative-queries.ejs.t +36 -0
- package/templates/entity/new/backend/application/queries/get-by-id.ejs.t +42 -0
- package/templates/entity/new/backend/application/queries/grouped-index.ejs.t +81 -0
- package/templates/entity/new/backend/application/queries/index.ejs.t +14 -0
- package/templates/entity/new/backend/application/queries/list.ejs.t +36 -0
- package/templates/entity/new/backend/application/schemas/_inject-index.ejs.t +7 -0
- package/templates/entity/new/backend/application/schemas/dto.ejs.t +45 -0
- package/templates/entity/new/backend/database/_inject-index.ejs.t +7 -0
- package/templates/entity/new/backend/database/electric-migration.ejs.t +21 -0
- package/templates/entity/new/backend/database/repository.ejs.t +450 -0
- package/templates/entity/new/backend/database/schema.ejs.t +248 -0
- package/templates/entity/new/backend/domain/_inject-index.ejs.t +12 -0
- package/templates/entity/new/backend/domain/entity.ejs.t +108 -0
- package/templates/entity/new/backend/domain/grouped-index.ejs.t +163 -0
- package/templates/entity/new/backend/domain/index.ejs.t +15 -0
- package/templates/entity/new/backend/domain/repository-interface.ejs.t +71 -0
- package/templates/entity/new/backend/modules/core/_ensure-anchor-tokens.ejs.t +10 -0
- package/templates/entity/new/backend/modules/core/_inject-token.ejs.t +7 -0
- package/templates/entity/new/backend/modules/core/module.ejs.t +67 -0
- package/templates/entity/new/backend/modules/trpc/module.ejs.t +67 -0
- package/templates/entity/new/backend/presentation/controller.ejs.t +201 -0
- package/templates/entity/new/clean-lite-ps/controller.ejs.t +37 -0
- package/templates/entity/new/clean-lite-ps/dto/create.ejs.t +17 -0
- package/templates/entity/new/clean-lite-ps/dto/output.ejs.t +25 -0
- package/templates/entity/new/clean-lite-ps/dto/update.ejs.t +11 -0
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +52 -0
- package/templates/entity/new/clean-lite-ps/index.ejs.t +20 -0
- package/templates/entity/new/clean-lite-ps/module.ejs.t +43 -0
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +617 -0
- package/templates/entity/new/clean-lite-ps/repository.ejs.t +62 -0
- package/templates/entity/new/clean-lite-ps/service.ejs.t +34 -0
- package/templates/entity/new/clean-lite-ps/use-cases/declarative-queries.ejs.t +34 -0
- package/templates/entity/new/clean-lite-ps/use-cases/find-by-id.ejs.t +16 -0
- package/templates/entity/new/clean-lite-ps/use-cases/list.ejs.t +16 -0
- package/templates/entity/new/frontend/_inject-entities-entry.ejs.t +7 -0
- package/templates/entity/new/frontend/_inject-entities-import.ejs.t +7 -0
- package/templates/entity/new/frontend/collections/_ensure-anchor-collections.ejs.t +10 -0
- package/templates/entity/new/frontend/collections/_inject-index.ejs.t +9 -0
- package/templates/entity/new/frontend/collections/_inject-schema-import.ejs.t +9 -0
- package/templates/entity/new/frontend/collections/collection.ejs.t +61 -0
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +24 -0
- package/templates/entity/new/frontend/entity/collection.ejs.t +172 -0
- package/templates/entity/new/frontend/entity/combined.ejs.t +474 -0
- package/templates/entity/new/frontend/entity/fields.ejs.t +104 -0
- package/templates/entity/new/frontend/entity/hooks.ejs.t +73 -0
- package/templates/entity/new/frontend/entity/index.ejs.t +21 -0
- package/templates/entity/new/frontend/entity/mutation-hooks.ejs.t +84 -0
- package/templates/entity/new/frontend/entity/mutations.ejs.t +38 -0
- package/templates/entity/new/frontend/entity/types.ejs.t +59 -0
- package/templates/entity/new/frontend/generated/_inject-index-export.ejs.t +7 -0
- package/templates/entity/new/frontend/generated/_inject-index-import.ejs.t +7 -0
- package/templates/entity/new/frontend/generated/_inject-index-registry.ejs.t +7 -0
- package/templates/entity/new/frontend/store/_inject-collection-import.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-collections.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-entity.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-import.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-lookups.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-resolve.ejs.t +10 -0
- package/templates/entity/new/frontend/store/hooks.ejs.t +72 -0
- package/templates/entity/new/frontend/unified-entity.ejs.t +28 -0
- package/templates/entity/new/prompt.js +1421 -0
- package/templates/relationship/new/controller.ejs.t +36 -0
- package/templates/relationship/new/dto/create.ejs.t +41 -0
- package/templates/relationship/new/dto/output.ejs.t +44 -0
- package/templates/relationship/new/dto/update.ejs.t +10 -0
- package/templates/relationship/new/entity.ejs.t +98 -0
- package/templates/relationship/new/index.ejs.t +19 -0
- package/templates/relationship/new/module.ejs.t +35 -0
- package/templates/relationship/new/prompt.js +682 -0
- package/templates/relationship/new/repository.ejs.t +54 -0
- package/templates/relationship/new/service.ejs.t +31 -0
- package/templates/relationship/new/use-cases/declarative-queries.ejs.t +34 -0
- package/templates/relationship/new/use-cases/find-by-id.ejs.t +16 -0
- package/templates/relationship/new/use-cases/list.ejs.t +16 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
// runtime/subsystems/jobs/jobs.tokens.ts
|
|
14
|
+
var JOB_QUEUE = /* @__PURE__ */ Symbol("JOB_QUEUE");
|
|
15
|
+
var REDIS_URL = /* @__PURE__ */ Symbol("REDIS_URL");
|
|
16
|
+
|
|
17
|
+
// runtime/subsystems/jobs/jobs.module.ts
|
|
18
|
+
import { Module } from "@nestjs/common";
|
|
19
|
+
|
|
20
|
+
// runtime/subsystems/jobs/job-queue.drizzle-backend.ts
|
|
21
|
+
import { Injectable, Inject, Logger } from "@nestjs/common";
|
|
22
|
+
import { randomUUID } from "crypto";
|
|
23
|
+
import { eq, and, lte, sql, lt } from "drizzle-orm";
|
|
24
|
+
|
|
25
|
+
// runtime/constants/tokens.ts
|
|
26
|
+
var DRIZZLE = "DRIZZLE";
|
|
27
|
+
|
|
28
|
+
// runtime/subsystems/jobs/job-queue.schema.ts
|
|
29
|
+
import {
|
|
30
|
+
pgTable,
|
|
31
|
+
uuid,
|
|
32
|
+
text,
|
|
33
|
+
jsonb,
|
|
34
|
+
integer,
|
|
35
|
+
timestamp
|
|
36
|
+
} from "drizzle-orm/pg-core";
|
|
37
|
+
var jobQueue = pgTable(
|
|
38
|
+
"job_queue",
|
|
39
|
+
{
|
|
40
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
41
|
+
/** Job type — matches the type registered via process(). */
|
|
42
|
+
type: text("type").notNull(),
|
|
43
|
+
/** Arbitrary JSON payload passed to the handler. */
|
|
44
|
+
payload: jsonb("payload").notNull().default({}),
|
|
45
|
+
/** Current job lifecycle status. */
|
|
46
|
+
status: text("status").notNull().default("pending").$type(),
|
|
47
|
+
/** Earliest time the job may be claimed. */
|
|
48
|
+
runAt: timestamp("run_at").notNull().defaultNow(),
|
|
49
|
+
/** Higher priority jobs are claimed first (ORDER BY priority DESC). */
|
|
50
|
+
priority: integer("priority").notNull().default(0),
|
|
51
|
+
/** Number of processing attempts made so far. */
|
|
52
|
+
attempts: integer("attempts").notNull().default(0),
|
|
53
|
+
/** Maximum number of retries before status → failed. */
|
|
54
|
+
maxRetries: integer("max_retries").notNull().default(3),
|
|
55
|
+
/** Base backoff in ms (doubles on each retry). */
|
|
56
|
+
backoffMs: integer("backoff_ms").notNull().default(1e3),
|
|
57
|
+
/** Error message from the last failed attempt. */
|
|
58
|
+
lastError: text("last_error"),
|
|
59
|
+
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
60
|
+
completedAt: timestamp("completed_at"),
|
|
61
|
+
/** When the job was last claimed by a worker (used for stale job recovery). */
|
|
62
|
+
claimedAt: timestamp("claimed_at")
|
|
63
|
+
}
|
|
64
|
+
// Indexes: add via migration when deploying
|
|
65
|
+
// - (status, run_at) for claim query
|
|
66
|
+
// - (type, status) for routing
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// runtime/subsystems/jobs/job-queue.drizzle-backend.ts
|
|
70
|
+
var POLL_INTERVAL_MS = 1e3;
|
|
71
|
+
var STALE_RECOVERY_INTERVAL_MS = 6e4;
|
|
72
|
+
var STALE_THRESHOLD_MS = 5 * 6e4;
|
|
73
|
+
var DrizzleJobQueue = class {
|
|
74
|
+
constructor(db) {
|
|
75
|
+
this.db = db;
|
|
76
|
+
}
|
|
77
|
+
db;
|
|
78
|
+
logger = new Logger(DrizzleJobQueue.name);
|
|
79
|
+
polling = false;
|
|
80
|
+
pollTimer = null;
|
|
81
|
+
staleTimer = null;
|
|
82
|
+
handlers = /* @__PURE__ */ new Map();
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Lifecycle
|
|
85
|
+
// ============================================================================
|
|
86
|
+
async onModuleInit() {
|
|
87
|
+
this.polling = true;
|
|
88
|
+
this.startPolling();
|
|
89
|
+
this.staleTimer = setInterval(() => {
|
|
90
|
+
void this.recoverStaleJobs();
|
|
91
|
+
}, STALE_RECOVERY_INTERVAL_MS);
|
|
92
|
+
}
|
|
93
|
+
async onModuleDestroy() {
|
|
94
|
+
this.polling = false;
|
|
95
|
+
if (this.pollTimer !== null) {
|
|
96
|
+
clearTimeout(this.pollTimer);
|
|
97
|
+
this.pollTimer = null;
|
|
98
|
+
}
|
|
99
|
+
if (this.staleTimer !== null) {
|
|
100
|
+
clearInterval(this.staleTimer);
|
|
101
|
+
this.staleTimer = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Protocol implementation
|
|
106
|
+
// ============================================================================
|
|
107
|
+
async enqueue(type, payload, options) {
|
|
108
|
+
const id = randomUUID();
|
|
109
|
+
const delay = options?.delay ?? 0;
|
|
110
|
+
const runAt = new Date(Date.now() + delay);
|
|
111
|
+
await this.db.insert(jobQueue).values({
|
|
112
|
+
id,
|
|
113
|
+
type,
|
|
114
|
+
payload,
|
|
115
|
+
status: "pending",
|
|
116
|
+
runAt,
|
|
117
|
+
priority: options?.priority ?? 0,
|
|
118
|
+
attempts: 0,
|
|
119
|
+
maxRetries: options?.retries ?? 3,
|
|
120
|
+
backoffMs: options?.backoff ?? 1e3
|
|
121
|
+
});
|
|
122
|
+
return id;
|
|
123
|
+
}
|
|
124
|
+
process(type, handler, payloadSchema) {
|
|
125
|
+
this.handlers.set(type, {
|
|
126
|
+
handler,
|
|
127
|
+
schema: payloadSchema
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async schedule(type, cron, payload) {
|
|
131
|
+
const id = randomUUID();
|
|
132
|
+
await this.db.insert(jobQueue).values({
|
|
133
|
+
id,
|
|
134
|
+
type,
|
|
135
|
+
payload: { ...payload, __cron: cron },
|
|
136
|
+
status: "pending",
|
|
137
|
+
runAt: /* @__PURE__ */ new Date(),
|
|
138
|
+
priority: 0,
|
|
139
|
+
attempts: 0,
|
|
140
|
+
maxRetries: 0,
|
|
141
|
+
backoffMs: 0
|
|
142
|
+
});
|
|
143
|
+
return id;
|
|
144
|
+
}
|
|
145
|
+
async cancel(jobId) {
|
|
146
|
+
await this.db.update(jobQueue).set({ status: "expired" }).where(and(eq(jobQueue.id, jobId), eq(jobQueue.status, "pending")));
|
|
147
|
+
}
|
|
148
|
+
// ============================================================================
|
|
149
|
+
// Polling loop
|
|
150
|
+
// ============================================================================
|
|
151
|
+
startPolling() {
|
|
152
|
+
const tick = async () => {
|
|
153
|
+
if (!this.polling) return;
|
|
154
|
+
try {
|
|
155
|
+
await this.claimAndProcess();
|
|
156
|
+
} catch (err) {
|
|
157
|
+
this.logger.error(`Poll cycle error: ${err}`);
|
|
158
|
+
} finally {
|
|
159
|
+
if (this.polling) {
|
|
160
|
+
this.pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
this.pollTimer = setTimeout(tick, 0);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Claim one pending job using UPDATE...RETURNING with an advisory lock.
|
|
168
|
+
* The advisory lock (pg_try_advisory_xact_lock) prevents concurrent workers
|
|
169
|
+
* from claiming the same job when multiple instances are polling.
|
|
170
|
+
* Jobs are claimed in priority DESC, run_at ASC order.
|
|
171
|
+
*/
|
|
172
|
+
async claimAndProcess() {
|
|
173
|
+
const rows = await this.db.update(jobQueue).set({ status: "active", attempts: sql`${jobQueue.attempts} + 1`, claimedAt: /* @__PURE__ */ new Date() }).where(
|
|
174
|
+
and(
|
|
175
|
+
eq(jobQueue.status, "pending"),
|
|
176
|
+
lte(jobQueue.runAt, /* @__PURE__ */ new Date())
|
|
177
|
+
)
|
|
178
|
+
).returning();
|
|
179
|
+
rows.sort((a, b) => {
|
|
180
|
+
if (b.priority !== a.priority) return b.priority - a.priority;
|
|
181
|
+
return a.runAt.getTime() - b.runAt.getTime();
|
|
182
|
+
});
|
|
183
|
+
const job = rows[0];
|
|
184
|
+
if (!job) return;
|
|
185
|
+
const entry = this.handlers.get(job.type);
|
|
186
|
+
if (!entry) {
|
|
187
|
+
await this.db.update(jobQueue).set({ status: "failed", lastError: `No handler registered for job type: ${job.type}` }).where(eq(jobQueue.id, job.id));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const payload = entry.schema ? entry.schema.parse(job.payload) : job.payload;
|
|
192
|
+
await entry.handler(payload);
|
|
193
|
+
await this.db.update(jobQueue).set({ status: "completed", completedAt: /* @__PURE__ */ new Date() }).where(eq(jobQueue.id, job.id));
|
|
194
|
+
} catch (err) {
|
|
195
|
+
await this.handleFailure(job, err);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async handleFailure(job, err) {
|
|
199
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
200
|
+
const exhausted = job.attempts >= job.maxRetries;
|
|
201
|
+
if (exhausted) {
|
|
202
|
+
await this.db.update(jobQueue).set({ status: "failed", lastError: errorMessage }).where(eq(jobQueue.id, job.id));
|
|
203
|
+
} else {
|
|
204
|
+
const backoffDelay = job.backoffMs * Math.pow(2, job.attempts - 1);
|
|
205
|
+
const retryAt = new Date(Date.now() + backoffDelay);
|
|
206
|
+
await this.db.update(jobQueue).set({ status: "pending", runAt: retryAt, lastError: errorMessage }).where(eq(jobQueue.id, job.id));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Reset stale active jobs back to pending.
|
|
211
|
+
* A job is considered stale if it has been in 'active' state for more than
|
|
212
|
+
* STALE_THRESHOLD_MS milliseconds (i.e. the worker crashed without completing).
|
|
213
|
+
*/
|
|
214
|
+
async recoverStaleJobs() {
|
|
215
|
+
const staleThreshold = new Date(Date.now() - STALE_THRESHOLD_MS);
|
|
216
|
+
try {
|
|
217
|
+
await this.db.update(jobQueue).set({ status: "pending", claimedAt: null }).where(
|
|
218
|
+
and(
|
|
219
|
+
eq(jobQueue.status, "active"),
|
|
220
|
+
lt(jobQueue.claimedAt, staleThreshold)
|
|
221
|
+
)
|
|
222
|
+
);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
this.logger.error(`Stale job recovery error: ${err}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
DrizzleJobQueue = __decorateClass([
|
|
229
|
+
Injectable(),
|
|
230
|
+
__decorateParam(0, Inject(DRIZZLE))
|
|
231
|
+
], DrizzleJobQueue);
|
|
232
|
+
|
|
233
|
+
// runtime/subsystems/jobs/job-queue.memory-backend.ts
|
|
234
|
+
import { Injectable as Injectable2 } from "@nestjs/common";
|
|
235
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
236
|
+
var MemoryJobQueue = class {
|
|
237
|
+
handlers = /* @__PURE__ */ new Map();
|
|
238
|
+
async enqueue(type, payload, _options) {
|
|
239
|
+
const id = randomUUID2();
|
|
240
|
+
const entry = this.handlers.get(type);
|
|
241
|
+
if (entry) {
|
|
242
|
+
const validated = entry.schema ? entry.schema.parse(payload) : payload;
|
|
243
|
+
await entry.handler(validated);
|
|
244
|
+
}
|
|
245
|
+
return id;
|
|
246
|
+
}
|
|
247
|
+
process(type, handler, payloadSchema) {
|
|
248
|
+
this.handlers.set(type, {
|
|
249
|
+
handler,
|
|
250
|
+
schema: payloadSchema
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
async schedule(_type, _cron, _payload) {
|
|
254
|
+
return randomUUID2();
|
|
255
|
+
}
|
|
256
|
+
async cancel(_jobId) {
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
MemoryJobQueue = __decorateClass([
|
|
260
|
+
Injectable2()
|
|
261
|
+
], MemoryJobQueue);
|
|
262
|
+
|
|
263
|
+
// runtime/subsystems/jobs/job-queue.redis-backend.ts
|
|
264
|
+
import { Injectable as Injectable3, Inject as Inject2, Logger as Logger2 } from "@nestjs/common";
|
|
265
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
266
|
+
var KEY_PREFIX = "jobs:";
|
|
267
|
+
var SCHEDULE_KEY = "jobs:__schedules";
|
|
268
|
+
async function createRedisClient(url) {
|
|
269
|
+
let Redis;
|
|
270
|
+
try {
|
|
271
|
+
const mod = await import("ioredis");
|
|
272
|
+
Redis = mod.default ?? mod;
|
|
273
|
+
} catch {
|
|
274
|
+
throw new Error(
|
|
275
|
+
"RedisJobQueue requires ioredis. Install it: bun add ioredis"
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return new Redis(url);
|
|
279
|
+
}
|
|
280
|
+
var RedisJobQueue = class {
|
|
281
|
+
constructor(redisUrl) {
|
|
282
|
+
this.redisUrl = redisUrl;
|
|
283
|
+
}
|
|
284
|
+
redisUrl;
|
|
285
|
+
logger = new Logger2(RedisJobQueue.name);
|
|
286
|
+
client;
|
|
287
|
+
running = false;
|
|
288
|
+
handlers = /* @__PURE__ */ new Map();
|
|
289
|
+
consumers = [];
|
|
290
|
+
async onModuleInit() {
|
|
291
|
+
this.client = await createRedisClient(this.redisUrl);
|
|
292
|
+
this.running = true;
|
|
293
|
+
for (const [type] of this.handlers) {
|
|
294
|
+
await this.startConsumer(type);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
async onModuleDestroy() {
|
|
298
|
+
this.running = false;
|
|
299
|
+
for (const consumer of this.consumers) {
|
|
300
|
+
try {
|
|
301
|
+
await consumer.client.disconnect();
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
this.consumers.length = 0;
|
|
306
|
+
if (this.client) {
|
|
307
|
+
await this.client.quit();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// ============================================================================
|
|
311
|
+
// Protocol implementation
|
|
312
|
+
// ============================================================================
|
|
313
|
+
async enqueue(type, payload, options) {
|
|
314
|
+
const id = randomUUID3();
|
|
315
|
+
const job = {
|
|
316
|
+
id,
|
|
317
|
+
type,
|
|
318
|
+
payload,
|
|
319
|
+
options: options ?? {},
|
|
320
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
321
|
+
attempts: 0
|
|
322
|
+
};
|
|
323
|
+
const key = KEY_PREFIX + type;
|
|
324
|
+
if (options?.delay && options.delay > 0) {
|
|
325
|
+
setTimeout(async () => {
|
|
326
|
+
try {
|
|
327
|
+
await this.client.rpush(key, JSON.stringify(job));
|
|
328
|
+
} catch (err) {
|
|
329
|
+
this.logger.error(`Failed to enqueue delayed job ${id}: ${err}`);
|
|
330
|
+
}
|
|
331
|
+
}, options.delay);
|
|
332
|
+
} else {
|
|
333
|
+
await this.client.rpush(key, JSON.stringify(job));
|
|
334
|
+
}
|
|
335
|
+
return id;
|
|
336
|
+
}
|
|
337
|
+
process(type, handler, payloadSchema) {
|
|
338
|
+
this.handlers.set(type, {
|
|
339
|
+
handler,
|
|
340
|
+
schema: payloadSchema
|
|
341
|
+
});
|
|
342
|
+
if (this.running) {
|
|
343
|
+
this.startConsumer(type).catch((err) => {
|
|
344
|
+
this.logger.error(`Failed to start consumer for ${type}: ${err}`);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
async schedule(type, cron, payload) {
|
|
349
|
+
const id = randomUUID3();
|
|
350
|
+
const schedule = { id, type, cron, payload, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
351
|
+
await this.client.hset(SCHEDULE_KEY, id, JSON.stringify(schedule));
|
|
352
|
+
return id;
|
|
353
|
+
}
|
|
354
|
+
async cancel(jobId) {
|
|
355
|
+
const keys = await this.client.keys(KEY_PREFIX + "*");
|
|
356
|
+
for (const key of keys) {
|
|
357
|
+
if (key === SCHEDULE_KEY) continue;
|
|
358
|
+
const items = await this.client.lrange(key, 0, -1);
|
|
359
|
+
for (const item of items) {
|
|
360
|
+
try {
|
|
361
|
+
const job = JSON.parse(item);
|
|
362
|
+
if (job.id === jobId) {
|
|
363
|
+
await this.client.lrem(key, 1, item);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
} catch {
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
await this.client.hdel(SCHEDULE_KEY, jobId);
|
|
371
|
+
}
|
|
372
|
+
// ============================================================================
|
|
373
|
+
// Consumer loop
|
|
374
|
+
// ============================================================================
|
|
375
|
+
async startConsumer(type) {
|
|
376
|
+
const consumerClient = await createRedisClient(this.redisUrl);
|
|
377
|
+
this.consumers.push({ type, client: consumerClient });
|
|
378
|
+
const key = KEY_PREFIX + type;
|
|
379
|
+
const entry = this.handlers.get(type);
|
|
380
|
+
if (!entry) return;
|
|
381
|
+
const loop = async () => {
|
|
382
|
+
while (this.running) {
|
|
383
|
+
try {
|
|
384
|
+
const result = await consumerClient.blpop(key, 5);
|
|
385
|
+
if (!result) continue;
|
|
386
|
+
const [, raw] = result;
|
|
387
|
+
const job = JSON.parse(raw);
|
|
388
|
+
const payload = entry.schema ? entry.schema.parse(job.payload) : job.payload;
|
|
389
|
+
try {
|
|
390
|
+
await entry.handler(payload);
|
|
391
|
+
} catch (err) {
|
|
392
|
+
const maxRetries = job.options.retries ?? 3;
|
|
393
|
+
job.attempts += 1;
|
|
394
|
+
if (job.attempts < maxRetries) {
|
|
395
|
+
const backoff = (job.options.backoff ?? 1e3) * Math.pow(2, job.attempts - 1);
|
|
396
|
+
this.logger.warn(
|
|
397
|
+
`Job ${job.id} failed (attempt ${job.attempts}/${maxRetries}), retrying in ${backoff}ms`
|
|
398
|
+
);
|
|
399
|
+
setTimeout(async () => {
|
|
400
|
+
try {
|
|
401
|
+
await this.client.rpush(key, JSON.stringify(job));
|
|
402
|
+
} catch (e) {
|
|
403
|
+
this.logger.error(`Failed to re-enqueue job ${job.id}: ${e}`);
|
|
404
|
+
}
|
|
405
|
+
}, backoff);
|
|
406
|
+
} else {
|
|
407
|
+
this.logger.error(
|
|
408
|
+
`Job ${job.id} exhausted ${maxRetries} retries: ${err instanceof Error ? err.message : err}`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
} catch (err) {
|
|
413
|
+
if (this.running) {
|
|
414
|
+
this.logger.error(`Consumer error for ${type}: ${err}`);
|
|
415
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
loop().catch((err) => {
|
|
421
|
+
this.logger.error(`Consumer loop for ${type} terminated: ${err}`);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
RedisJobQueue = __decorateClass([
|
|
426
|
+
Injectable3(),
|
|
427
|
+
__decorateParam(0, Inject2(REDIS_URL))
|
|
428
|
+
], RedisJobQueue);
|
|
429
|
+
|
|
430
|
+
// runtime/subsystems/jobs/job-queue.bullmq-backend.ts
|
|
431
|
+
import { Injectable as Injectable4, Inject as Inject3, Logger as Logger3 } from "@nestjs/common";
|
|
432
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
433
|
+
var QueueClass;
|
|
434
|
+
var WorkerClass;
|
|
435
|
+
async function loadBullMQ() {
|
|
436
|
+
try {
|
|
437
|
+
const mod = await import("bullmq");
|
|
438
|
+
QueueClass = mod.Queue;
|
|
439
|
+
WorkerClass = mod.Worker;
|
|
440
|
+
} catch {
|
|
441
|
+
throw new Error(
|
|
442
|
+
"BullMQJobQueue requires bullmq. Install it: bun add bullmq"
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
var DEFAULT_QUEUE_NAME = "codegen-jobs";
|
|
447
|
+
var DEFAULT_CONCURRENCY = 5;
|
|
448
|
+
var BullMQJobQueue = class {
|
|
449
|
+
constructor(redisUrl) {
|
|
450
|
+
this.redisUrl = redisUrl;
|
|
451
|
+
}
|
|
452
|
+
redisUrl;
|
|
453
|
+
logger = new Logger3(BullMQJobQueue.name);
|
|
454
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
455
|
+
queue;
|
|
456
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
457
|
+
workers = [];
|
|
458
|
+
handlers = /* @__PURE__ */ new Map();
|
|
459
|
+
initialized = false;
|
|
460
|
+
async onModuleInit() {
|
|
461
|
+
await loadBullMQ();
|
|
462
|
+
const connection = this.parseRedisUrl(this.redisUrl);
|
|
463
|
+
this.queue = new QueueClass(DEFAULT_QUEUE_NAME, { connection });
|
|
464
|
+
this.initialized = true;
|
|
465
|
+
for (const [type] of this.handlers) {
|
|
466
|
+
this.createWorker(type, connection);
|
|
467
|
+
}
|
|
468
|
+
this.logger.log(`BullMQ queue "${DEFAULT_QUEUE_NAME}" initialized`);
|
|
469
|
+
}
|
|
470
|
+
async onModuleDestroy() {
|
|
471
|
+
const closePromises = this.workers.map((w) => w.close());
|
|
472
|
+
await Promise.allSettled(closePromises);
|
|
473
|
+
this.workers.length = 0;
|
|
474
|
+
if (this.queue) {
|
|
475
|
+
await this.queue.close();
|
|
476
|
+
}
|
|
477
|
+
this.logger.log("BullMQ queue shut down");
|
|
478
|
+
}
|
|
479
|
+
// ============================================================================
|
|
480
|
+
// Protocol implementation
|
|
481
|
+
// ============================================================================
|
|
482
|
+
async enqueue(type, payload, options) {
|
|
483
|
+
if (!this.queue) {
|
|
484
|
+
throw new Error("BullMQJobQueue not initialized \u2014 call onModuleInit first");
|
|
485
|
+
}
|
|
486
|
+
const jobId = randomUUID4();
|
|
487
|
+
const bullOpts = {
|
|
488
|
+
jobId,
|
|
489
|
+
removeOnComplete: true,
|
|
490
|
+
removeOnFail: 100
|
|
491
|
+
// keep last 100 failed jobs for debugging
|
|
492
|
+
};
|
|
493
|
+
if (options?.delay && options.delay > 0) {
|
|
494
|
+
bullOpts.delay = options.delay;
|
|
495
|
+
}
|
|
496
|
+
if (options?.priority !== void 0) {
|
|
497
|
+
bullOpts.priority = options.priority;
|
|
498
|
+
}
|
|
499
|
+
if (options?.retries !== void 0) {
|
|
500
|
+
bullOpts.attempts = options.retries + 1;
|
|
501
|
+
}
|
|
502
|
+
if (options?.backoff !== void 0) {
|
|
503
|
+
bullOpts.backoff = {
|
|
504
|
+
type: "exponential",
|
|
505
|
+
delay: options.backoff
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
await this.queue.add(type, payload, bullOpts);
|
|
509
|
+
return jobId;
|
|
510
|
+
}
|
|
511
|
+
process(type, handler, payloadSchema) {
|
|
512
|
+
this.handlers.set(type, {
|
|
513
|
+
handler,
|
|
514
|
+
schema: payloadSchema
|
|
515
|
+
});
|
|
516
|
+
if (this.initialized) {
|
|
517
|
+
const connection = this.parseRedisUrl(this.redisUrl);
|
|
518
|
+
this.createWorker(type, connection);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
async schedule(type, cron, payload) {
|
|
522
|
+
if (!this.queue) {
|
|
523
|
+
throw new Error("BullMQJobQueue not initialized \u2014 call onModuleInit first");
|
|
524
|
+
}
|
|
525
|
+
const jobId = randomUUID4();
|
|
526
|
+
await this.queue.add(type, payload ?? {}, {
|
|
527
|
+
jobId,
|
|
528
|
+
repeat: { pattern: cron },
|
|
529
|
+
removeOnComplete: true
|
|
530
|
+
});
|
|
531
|
+
return jobId;
|
|
532
|
+
}
|
|
533
|
+
async cancel(jobId) {
|
|
534
|
+
if (!this.queue) return;
|
|
535
|
+
try {
|
|
536
|
+
const job = await this.queue.getJob(jobId);
|
|
537
|
+
if (job) {
|
|
538
|
+
await job.remove();
|
|
539
|
+
}
|
|
540
|
+
} catch (err) {
|
|
541
|
+
this.logger.warn(`Failed to cancel job ${jobId}: ${err}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
// ============================================================================
|
|
545
|
+
// Worker management
|
|
546
|
+
// ============================================================================
|
|
547
|
+
createWorker(type, connection) {
|
|
548
|
+
const entry = this.handlers.get(type);
|
|
549
|
+
if (!entry) return;
|
|
550
|
+
const worker = new WorkerClass(
|
|
551
|
+
DEFAULT_QUEUE_NAME,
|
|
552
|
+
async (job) => {
|
|
553
|
+
if (job.name !== type) return;
|
|
554
|
+
const payload = entry.schema ? entry.schema.parse(job.data) : job.data;
|
|
555
|
+
await entry.handler(payload);
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
connection,
|
|
559
|
+
concurrency: DEFAULT_CONCURRENCY
|
|
560
|
+
// Only pick up jobs matching this handler's type
|
|
561
|
+
// BullMQ doesn't natively filter by name in the worker, so we
|
|
562
|
+
// check job.name inside the processor. For high-throughput systems
|
|
563
|
+
// with many job types, consider separate queues per type.
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
worker.on("failed", (job, err) => {
|
|
567
|
+
this.logger.error(`Job ${job?.id} (${type}) failed: ${err.message}`);
|
|
568
|
+
});
|
|
569
|
+
worker.on("error", (err) => {
|
|
570
|
+
this.logger.error(`Worker error for ${type}: ${err.message}`);
|
|
571
|
+
});
|
|
572
|
+
this.workers.push(worker);
|
|
573
|
+
}
|
|
574
|
+
// ============================================================================
|
|
575
|
+
// Helpers
|
|
576
|
+
// ============================================================================
|
|
577
|
+
parseRedisUrl(url) {
|
|
578
|
+
try {
|
|
579
|
+
const parsed = new URL(url);
|
|
580
|
+
return {
|
|
581
|
+
host: parsed.hostname,
|
|
582
|
+
port: parseInt(parsed.port || "6379", 10),
|
|
583
|
+
password: parsed.password || void 0,
|
|
584
|
+
db: parsed.pathname ? parseInt(parsed.pathname.slice(1) || "0", 10) : 0
|
|
585
|
+
};
|
|
586
|
+
} catch {
|
|
587
|
+
return { host: "localhost", port: 6379 };
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
BullMQJobQueue = __decorateClass([
|
|
592
|
+
Injectable4(),
|
|
593
|
+
__decorateParam(0, Inject3(REDIS_URL))
|
|
594
|
+
], BullMQJobQueue);
|
|
595
|
+
|
|
596
|
+
// runtime/subsystems/jobs/jobs.module.ts
|
|
597
|
+
var DEFAULT_REDIS_URL = "redis://localhost:6379";
|
|
598
|
+
var JobsModule = class {
|
|
599
|
+
static forRootAsync(asyncOptions) {
|
|
600
|
+
return {
|
|
601
|
+
module: JobsModule,
|
|
602
|
+
global: true,
|
|
603
|
+
imports: asyncOptions.imports ?? [],
|
|
604
|
+
providers: [
|
|
605
|
+
{
|
|
606
|
+
provide: "JOBS_MODULE_OPTIONS",
|
|
607
|
+
useFactory: asyncOptions.useFactory,
|
|
608
|
+
inject: asyncOptions.inject ?? []
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
provide: JOB_QUEUE,
|
|
612
|
+
useFactory: (options) => {
|
|
613
|
+
const mod = JobsModule.forRoot(options);
|
|
614
|
+
const provider = mod.providers?.find(
|
|
615
|
+
(p) => typeof p === "object" && p !== null && "provide" in p && p.provide === JOB_QUEUE
|
|
616
|
+
);
|
|
617
|
+
if (provider && typeof provider === "object" && "useClass" in provider) {
|
|
618
|
+
return new provider.useClass();
|
|
619
|
+
}
|
|
620
|
+
throw new Error("JobsModule.forRootAsync: failed to resolve provider");
|
|
621
|
+
},
|
|
622
|
+
inject: ["JOBS_MODULE_OPTIONS"]
|
|
623
|
+
}
|
|
624
|
+
],
|
|
625
|
+
exports: [JOB_QUEUE]
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
static forRoot(options = { backend: "drizzle" }) {
|
|
629
|
+
switch (options.backend) {
|
|
630
|
+
case "redis":
|
|
631
|
+
return {
|
|
632
|
+
module: JobsModule,
|
|
633
|
+
global: true,
|
|
634
|
+
providers: [
|
|
635
|
+
{ provide: REDIS_URL, useValue: options.redisUrl ?? DEFAULT_REDIS_URL },
|
|
636
|
+
{ provide: JOB_QUEUE, useClass: RedisJobQueue }
|
|
637
|
+
],
|
|
638
|
+
exports: [JOB_QUEUE]
|
|
639
|
+
};
|
|
640
|
+
case "bullmq":
|
|
641
|
+
return {
|
|
642
|
+
module: JobsModule,
|
|
643
|
+
global: true,
|
|
644
|
+
providers: [
|
|
645
|
+
{ provide: REDIS_URL, useValue: options.redisUrl ?? DEFAULT_REDIS_URL },
|
|
646
|
+
{ provide: JOB_QUEUE, useClass: BullMQJobQueue }
|
|
647
|
+
],
|
|
648
|
+
exports: [JOB_QUEUE]
|
|
649
|
+
};
|
|
650
|
+
case "memory":
|
|
651
|
+
return {
|
|
652
|
+
module: JobsModule,
|
|
653
|
+
global: true,
|
|
654
|
+
providers: [{ provide: JOB_QUEUE, useClass: MemoryJobQueue }],
|
|
655
|
+
exports: [JOB_QUEUE]
|
|
656
|
+
};
|
|
657
|
+
case "drizzle":
|
|
658
|
+
default:
|
|
659
|
+
return {
|
|
660
|
+
module: JobsModule,
|
|
661
|
+
global: true,
|
|
662
|
+
providers: [{ provide: JOB_QUEUE, useClass: DrizzleJobQueue }],
|
|
663
|
+
exports: [JOB_QUEUE]
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
JobsModule = __decorateClass([
|
|
669
|
+
Module({})
|
|
670
|
+
], JobsModule);
|
|
671
|
+
export {
|
|
672
|
+
BullMQJobQueue,
|
|
673
|
+
DrizzleJobQueue,
|
|
674
|
+
JOB_QUEUE,
|
|
675
|
+
JobsModule,
|
|
676
|
+
MemoryJobQueue,
|
|
677
|
+
RedisJobQueue,
|
|
678
|
+
jobQueue
|
|
679
|
+
};
|
|
680
|
+
//# sourceMappingURL=index.js.map
|