tina4-nodejs 3.13.92 → 3.13.95
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/CLAUDE.md +170 -28
- package/README.md +2 -2
- package/package.json +13 -9
- package/packages/cli/dist/bin.js +33126 -30055
- package/packages/cli/src/commands/metrics.ts +17 -11
- package/packages/cli/src/commands/serve.ts +10 -9
- package/packages/core/dist/index.js +33062 -29908
- package/packages/core/src/ai.ts +7 -1
- package/packages/core/src/auth.ts +191 -39
- package/packages/core/src/background.ts +19 -19
- package/packages/core/src/cache.ts +492 -49
- package/packages/core/src/devAdmin.ts +79 -32
- package/packages/core/src/devMailbox.ts +20 -44
- package/packages/core/src/dispatchPipeline.ts +285 -0
- package/packages/core/src/dotenv.ts +185 -40
- package/packages/core/src/index.ts +7 -6
- package/packages/core/src/logger.ts +257 -36
- package/packages/core/src/mcp.ts +1 -1
- package/packages/core/src/messenger.ts +81 -13
- package/packages/core/src/metrics.ts +199 -961
- package/packages/core/src/middleware.ts +390 -123
- package/packages/core/src/queue.ts +188 -32
- package/packages/core/src/queueBackends/kafkaBackend.ts +109 -13
- package/packages/core/src/queueBackends/liteBackend.ts +13 -0
- package/packages/core/src/queueBackends/mongoBackend.ts +101 -9
- package/packages/core/src/queueBackends/rabbitmqBackend.ts +22 -4
- package/packages/core/src/rateLimiter.ts +10 -5
- package/packages/core/src/request.ts +6 -9
- package/packages/core/src/response.ts +46 -1
- package/packages/core/src/router.ts +29 -4
- package/packages/core/src/server.ts +751 -414
- package/packages/core/src/session.ts +244 -27
- package/packages/core/src/sessionHandlers/childError.ts +72 -0
- package/packages/core/src/sessionHandlers/databaseHandler.ts +338 -48
- package/packages/core/src/sessionHandlers/memcachedHandler.ts +181 -0
- package/packages/core/src/sessionHandlers/mongoClient.ts +293 -202
- package/packages/core/src/sessionHandlers/mongoHandler.ts +88 -8
- package/packages/core/src/sessionHandlers/respClient.ts +16 -143
- package/packages/core/src/sessionHandlers/sqlClient.ts +290 -0
- package/packages/core/src/sessionHandlers/syncBridge.ts +190 -0
- package/packages/core/src/sessionHandlers/syncSocket.ts +236 -0
- package/packages/core/src/testClient.ts +18 -5
- package/packages/core/src/trustedProxy.ts +249 -0
- package/packages/core/src/types.ts +29 -5
- package/packages/core/src/websocket.ts +66 -0
- package/packages/frond/dist/index.js +74 -31
- package/packages/frond/src/engine.ts +99 -33
- package/packages/orm/dist/index.js +26554 -23400
- package/packages/orm/src/adapters/firebird.ts +183 -56
- package/packages/orm/src/adapters/mongodb.ts +25 -4
- package/packages/orm/src/adapters/mssql.ts +114 -29
- package/packages/orm/src/adapters/mysql.ts +103 -40
- package/packages/orm/src/adapters/odbc.ts +44 -21
- package/packages/orm/src/adapters/postgres.ts +118 -26
- package/packages/orm/src/adapters/sqlDialect.ts +120 -0
- package/packages/orm/src/adapters/sqlite.ts +64 -25
- package/packages/orm/src/baseModel.ts +135 -40
- package/packages/orm/src/cachedDatabase.ts +43 -19
- package/packages/orm/src/connectTimeout.ts +265 -0
- package/packages/orm/src/database.ts +338 -198
- package/packages/orm/src/databaseResult.ts +65 -13
- package/packages/orm/src/databaseUrl.ts +484 -0
- package/packages/orm/src/docstore.ts +386 -145
- package/packages/orm/src/index.ts +13 -3
- package/packages/orm/src/migration.ts +18 -3
- package/packages/orm/src/queryBuilder.ts +38 -4
- package/packages/orm/src/sqlTranslator.ts +310 -4
- package/packages/orm/src/types.ts +15 -4
- package/types/cli/src/bin.d.ts +92 -0
- package/types/cli/src/commands/build.d.ts +2 -0
- package/types/cli/src/commands/generate.d.ts +47 -0
- package/types/cli/src/commands/init.d.ts +1 -0
- package/types/cli/src/commands/metrics.d.ts +6 -0
- package/types/cli/src/commands/migrate.d.ts +1 -0
- package/types/cli/src/commands/migrateCreate.d.ts +1 -0
- package/types/cli/src/commands/migrateRollback.d.ts +1 -0
- package/types/cli/src/commands/migrateStatus.d.ts +1 -0
- package/types/cli/src/commands/queue.d.ts +20 -0
- package/types/cli/src/commands/routes.d.ts +1 -0
- package/types/cli/src/commands/seed.d.ts +1 -0
- package/types/cli/src/commands/serve.d.ts +6 -0
- package/types/cli/src/commands/test.d.ts +1 -0
- package/types/core/src/ai.d.ts +64 -0
- package/types/core/src/api.d.ts +262 -0
- package/types/core/src/auth.d.ts +177 -0
- package/types/core/src/authGate.d.ts +20 -0
- package/types/core/src/background.d.ts +34 -0
- package/types/core/src/cache.d.ts +163 -0
- package/types/core/src/constants.d.ts +38 -0
- package/types/core/src/container.d.ts +44 -0
- package/types/core/src/context/chunker.d.ts +31 -0
- package/types/core/src/context/index.d.ts +93 -0
- package/types/core/src/devAdmin.d.ts +179 -0
- package/types/core/src/devMailbox.d.ts +54 -0
- package/types/core/src/dispatchPipeline.d.ts +117 -0
- package/types/core/src/docs.d.ts +141 -0
- package/types/core/src/docsAutoDiscovery.d.ts +6 -0
- package/types/core/src/dotenv.d.ts +87 -0
- package/types/core/src/env.d.ts +28 -0
- package/types/core/src/errorOverlay.d.ts +36 -0
- package/types/core/src/events.d.ts +75 -0
- package/types/core/src/fakeData.d.ts +55 -0
- package/types/core/src/feedback.d.ts +90 -0
- package/types/core/src/graphql.d.ts +207 -0
- package/types/core/src/health.d.ts +22 -0
- package/types/core/src/htmlElement.d.ts +75 -0
- package/types/core/src/i18n.d.ts +37 -0
- package/types/core/src/index.d.ts +92 -0
- package/types/core/src/job.d.ts +39 -0
- package/types/core/src/logger.d.ts +200 -0
- package/types/core/src/mcp.d.ts +248 -0
- package/types/core/src/messenger.d.ts +191 -0
- package/types/core/src/metrics.d.ts +41 -0
- package/types/core/src/middleware.d.ts +330 -0
- package/types/core/src/mqtt.d.ts +257 -0
- package/types/core/src/mqttMessage.d.ts +67 -0
- package/types/core/src/plan.d.ts +96 -0
- package/types/core/src/projectIndex.d.ts +56 -0
- package/types/core/src/queue.d.ts +268 -0
- package/types/core/src/queueBackends/kafkaBackend.d.ts +117 -0
- package/types/core/src/queueBackends/liteBackend.d.ts +128 -0
- package/types/core/src/queueBackends/mongoBackend.d.ts +119 -0
- package/types/core/src/queueBackends/rabbitmqBackend.d.ts +55 -0
- package/types/core/src/rateLimiter.d.ts +49 -0
- package/types/core/src/request.d.ts +25 -0
- package/types/core/src/response.d.ts +28 -0
- package/types/core/src/routeDiscovery.d.ts +12 -0
- package/types/core/src/router.d.ts +366 -0
- package/types/core/src/scss.d.ts +19 -0
- package/types/core/src/server.d.ts +146 -0
- package/types/core/src/service.d.ts +115 -0
- package/types/core/src/session.d.ts +341 -0
- package/types/core/src/sessionHandlers/childError.d.ts +34 -0
- package/types/core/src/sessionHandlers/databaseHandler.d.ts +97 -0
- package/types/core/src/sessionHandlers/memcachedHandler.d.ts +60 -0
- package/types/core/src/sessionHandlers/mongoClient.d.ts +35 -0
- package/types/core/src/sessionHandlers/mongoHandler.d.ts +109 -0
- package/types/core/src/sessionHandlers/respClient.d.ts +22 -0
- package/types/core/src/sessionHandlers/sqlClient.d.ts +39 -0
- package/types/core/src/sessionHandlers/syncBridge.d.ts +91 -0
- package/types/core/src/sessionHandlers/syncSocket.d.ts +49 -0
- package/types/core/src/sessionHandlers/valkeyHandler.d.ts +65 -0
- package/types/core/src/static.d.ts +2 -0
- package/types/core/src/test.d.ts +94 -0
- package/types/core/src/testClient.d.ts +36 -0
- package/types/core/src/testing.d.ts +58 -0
- package/types/core/src/trustedProxy.d.ts +44 -0
- package/types/core/src/types.d.ts +242 -0
- package/types/core/src/validator.d.ts +52 -0
- package/types/core/src/websocket.d.ts +402 -0
- package/types/core/src/websocketBackplane.d.ts +166 -0
- package/types/core/src/websocketConnection.d.ts +54 -0
- package/types/core/src/wsdl.d.ts +101 -0
- package/types/frond/src/engine.d.ts +263 -0
- package/types/frond/src/index.d.ts +2 -0
- package/types/orm/src/adapters/firebird.d.ts +183 -0
- package/types/orm/src/adapters/mongodb.d.ts +81 -0
- package/types/orm/src/adapters/mssql.d.ts +77 -0
- package/types/orm/src/adapters/mysql.d.ts +67 -0
- package/types/orm/src/adapters/odbc.d.ts +94 -0
- package/types/orm/src/adapters/postgres.d.ts +86 -0
- package/types/orm/src/adapters/sqlDialect.d.ts +71 -0
- package/types/orm/src/adapters/sqlite.d.ts +68 -0
- package/types/orm/src/autoCrud.d.ts +73 -0
- package/types/orm/src/baseModel.d.ts +427 -0
- package/types/orm/src/cachedDatabase.d.ts +190 -0
- package/types/orm/src/connectTimeout.d.ts +100 -0
- package/types/orm/src/database.d.ts +655 -0
- package/types/orm/src/databaseResult.d.ts +109 -0
- package/types/orm/src/databaseUrl.d.ts +125 -0
- package/types/orm/src/docstore.d.ts +241 -0
- package/types/orm/src/fakeData.d.ts +22 -0
- package/types/orm/src/index.d.ts +43 -0
- package/types/orm/src/migration.d.ts +275 -0
- package/types/orm/src/model.d.ts +7 -0
- package/types/orm/src/query.d.ts +14 -0
- package/types/orm/src/queryBuilder.d.ts +193 -0
- package/types/orm/src/realtime/index.d.ts +7 -0
- package/types/orm/src/realtime/models/attachment.d.ts +43 -0
- package/types/orm/src/realtime/models/channel.d.ts +32 -0
- package/types/orm/src/realtime/models/channelMember.d.ts +32 -0
- package/types/orm/src/realtime/models/message.d.ts +36 -0
- package/types/orm/src/realtime/models/workspace.d.ts +26 -0
- package/types/orm/src/realtime/realtime.d.ts +24 -0
- package/types/orm/src/realtime/storage.d.ts +61 -0
- package/types/orm/src/seeder.d.ts +118 -0
- package/types/orm/src/sqlTranslator.d.ts +258 -0
- package/types/orm/src/types.d.ts +148 -0
- package/types/orm/src/validation.d.ts +6 -0
- package/types/swagger/src/generator.d.ts +46 -0
- package/types/swagger/src/index.d.ts +2 -0
- package/types/swagger/src/ui.d.ts +11 -0
- package/packages/core/src/sessionHandlers/redisHandler.ts +0 -206
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project plan management — persistent, human-readable task state.
|
|
3
|
+
*
|
|
4
|
+
* Ported from tina4_python/dev_admin/plan.py (master reference).
|
|
5
|
+
* A plan is a markdown file under plan/ at the project root with sections
|
|
6
|
+
* for title, goal, steps (checkboxes), and notes. Exactly one plan is
|
|
7
|
+
* "current" at a time, recorded in plan/.current.
|
|
8
|
+
*
|
|
9
|
+
* Byte-for-byte compatible with Python's plan/*.md storage.
|
|
10
|
+
*/
|
|
11
|
+
export interface PlanStep {
|
|
12
|
+
text: string;
|
|
13
|
+
done: boolean;
|
|
14
|
+
index?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ParsedPlan {
|
|
17
|
+
title: string;
|
|
18
|
+
goal: string;
|
|
19
|
+
steps: PlanStep[];
|
|
20
|
+
notes: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PlanSummary {
|
|
23
|
+
name: string;
|
|
24
|
+
title: string;
|
|
25
|
+
steps_total: number;
|
|
26
|
+
steps_done: number;
|
|
27
|
+
is_current: boolean;
|
|
28
|
+
/** Path relative to project root — lets the SPA open the right file
|
|
29
|
+
* regardless of which dir the plan came from (plan/ vs .tina4/plans/). */
|
|
30
|
+
path: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ExecutionSummary {
|
|
33
|
+
created: string[];
|
|
34
|
+
patched: string[];
|
|
35
|
+
migrations: string[];
|
|
36
|
+
total: number;
|
|
37
|
+
}
|
|
38
|
+
export interface CurrentPlan {
|
|
39
|
+
current: string | null;
|
|
40
|
+
title?: string;
|
|
41
|
+
goal?: string;
|
|
42
|
+
steps?: PlanStep[];
|
|
43
|
+
next_step?: PlanStep | null;
|
|
44
|
+
notes?: string;
|
|
45
|
+
progress?: {
|
|
46
|
+
done: number;
|
|
47
|
+
total: number;
|
|
48
|
+
};
|
|
49
|
+
execution?: ExecutionSummary;
|
|
50
|
+
warning?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare const Plan: {
|
|
53
|
+
/**
|
|
54
|
+
* All plan files — merged from `plan/` (user-curated canonical) and
|
|
55
|
+
* `.tina4/plans/` (where the Rust supervisor's planner writes).
|
|
56
|
+
*
|
|
57
|
+
* Two directories exist because of a historic split: the framework
|
|
58
|
+
* treats `plan/` as the canonical project location, but the Rust agent's
|
|
59
|
+
* planner writes to `.tina4/plans/` (alongside other AI-state artefacts
|
|
60
|
+
* like chat history). Until those are unified we read both so plans
|
|
61
|
+
* created either way are discoverable. Dedup by filename when a plan
|
|
62
|
+
* exists in both — `plan/` wins on collision.
|
|
63
|
+
*
|
|
64
|
+
* Newest-first by filename (Rust planner uses unix-timestamp prefixes).
|
|
65
|
+
*/
|
|
66
|
+
listPlans(): PlanSummary[];
|
|
67
|
+
currentName(): string;
|
|
68
|
+
setCurrent(name: string): {
|
|
69
|
+
ok: boolean;
|
|
70
|
+
current?: string;
|
|
71
|
+
error?: string;
|
|
72
|
+
};
|
|
73
|
+
clearCurrent(): {
|
|
74
|
+
ok: boolean;
|
|
75
|
+
};
|
|
76
|
+
current(): CurrentPlan;
|
|
77
|
+
read(name: string): ParsedPlan & {
|
|
78
|
+
name?: string;
|
|
79
|
+
error?: string;
|
|
80
|
+
};
|
|
81
|
+
create(title: string, goal?: string, steps?: string[], makeCurrent?: boolean): {
|
|
82
|
+
ok: boolean;
|
|
83
|
+
name?: string;
|
|
84
|
+
title?: string;
|
|
85
|
+
is_current?: boolean;
|
|
86
|
+
error?: string;
|
|
87
|
+
};
|
|
88
|
+
completeStep(index: number, name?: string): Record<string, unknown>;
|
|
89
|
+
uncompleteStep(index: number, name?: string): Record<string, unknown>;
|
|
90
|
+
addStep(text: string, name?: string): Record<string, unknown>;
|
|
91
|
+
appendNote(text: string, name?: string): Record<string, unknown>;
|
|
92
|
+
recordAction(action: string, filePath: string, note?: string): void;
|
|
93
|
+
summariseExecution(name?: string): ExecutionSummary;
|
|
94
|
+
flesh(name?: string, prompt?: string): Promise<Record<string, unknown>>;
|
|
95
|
+
archive(name?: string): Record<string, unknown>;
|
|
96
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project index — lightweight, persistent "where is what" map.
|
|
3
|
+
*
|
|
4
|
+
* Ported from tina4_python/dev_admin/project_index.py (master reference).
|
|
5
|
+
*
|
|
6
|
+
* Storage: .tina4/project_index.json at the project root.
|
|
7
|
+
* Freshness: lazy-refreshes on read via mtime compare — no watchers.
|
|
8
|
+
* Extractors: per-language symbol extraction (TS/JS, Twig/HTML, SQL, Markdown,
|
|
9
|
+
* Python) using regex. No LLM involvement — pure static analysis.
|
|
10
|
+
*/
|
|
11
|
+
export interface FileRoute {
|
|
12
|
+
method: string;
|
|
13
|
+
path: string;
|
|
14
|
+
handler: string;
|
|
15
|
+
}
|
|
16
|
+
export interface FileEntry {
|
|
17
|
+
path?: string;
|
|
18
|
+
size?: number;
|
|
19
|
+
mtime?: number;
|
|
20
|
+
language?: string;
|
|
21
|
+
sha256?: string;
|
|
22
|
+
skipped?: string;
|
|
23
|
+
extraction_error?: string;
|
|
24
|
+
summary?: string;
|
|
25
|
+
symbols?: string[];
|
|
26
|
+
imports?: string[];
|
|
27
|
+
routes?: FileRoute[];
|
|
28
|
+
docstring?: string;
|
|
29
|
+
exports?: string[];
|
|
30
|
+
extends?: string[];
|
|
31
|
+
blocks?: string[];
|
|
32
|
+
includes?: string[];
|
|
33
|
+
creates?: string[];
|
|
34
|
+
alters?: string[];
|
|
35
|
+
title?: string;
|
|
36
|
+
sections?: string[];
|
|
37
|
+
first_line?: string;
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare const ProjectIndex: {
|
|
41
|
+
refresh(): {
|
|
42
|
+
added: number;
|
|
43
|
+
updated: number;
|
|
44
|
+
removed: number;
|
|
45
|
+
total: number;
|
|
46
|
+
path: string;
|
|
47
|
+
};
|
|
48
|
+
search(query: string, limit?: number): Array<{
|
|
49
|
+
path: string;
|
|
50
|
+
summary: string;
|
|
51
|
+
score: number;
|
|
52
|
+
language: string;
|
|
53
|
+
}>;
|
|
54
|
+
fileEntry(relPath: string): FileEntry;
|
|
55
|
+
overview(): Record<string, unknown>;
|
|
56
|
+
};
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { type QueueJob } from "./job.js";
|
|
2
|
+
export { LiteBackend } from "./queueBackends/liteBackend.js";
|
|
3
|
+
export { type QueueJob } from "./job.js";
|
|
4
|
+
export interface QueueConfig {
|
|
5
|
+
backend?: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
topic?: string;
|
|
8
|
+
maxRetries?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Seconds to delay a failed job's automatic re-enqueue. 0 (the default)
|
|
11
|
+
* means retry immediately — the next pop()/consume() iteration picks it up
|
|
12
|
+
* straight away. Parity with Python's retry_backoff.
|
|
13
|
+
*/
|
|
14
|
+
retryBackoff?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Reservation/visibility timeout (seconds). A popped job is reserved for this
|
|
17
|
+
* long; if the consumer dies before complete()/fail() (crash, OOM, k8s
|
|
18
|
+
* eviction) the next pop() reclaims it — incrementing attempts and
|
|
19
|
+
* re-enqueuing, or dead-lettering past maxRetries (at-least-once delivery).
|
|
20
|
+
* Falls back to TINA4_QUEUE_VISIBILITY_TIMEOUT, else 300 (5 min). <= 0
|
|
21
|
+
* disables the reclaim (a reservation then lasts until the consumer acks —
|
|
22
|
+
* the old at-most-once behaviour). File + MongoDB backends, which are the
|
|
23
|
+
* only backends Node offers (ADR-0022). Parity with Python's
|
|
24
|
+
* visibility_timeout.
|
|
25
|
+
*/
|
|
26
|
+
visibilityTimeout?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Where the file-backed queue stores its jobs — `TINA4_QUEUE_PATH`, else
|
|
30
|
+
* `data/queue` (relative to the working directory).
|
|
31
|
+
*
|
|
32
|
+
* Exported because it is the ONE answer to "where do the queue files live",
|
|
33
|
+
* and anything that reads the store directly (the dev-admin queue panel) must
|
|
34
|
+
* ask here rather than re-deriving it. The dev admin hardcoded
|
|
35
|
+
* `cwd/data/queue/<topic>` and so listed a DIFFERENT directory from the one
|
|
36
|
+
* `Queue.size()` counted the moment `TINA4_QUEUE_PATH` was set.
|
|
37
|
+
*/
|
|
38
|
+
export declare function queueBasePath(): string;
|
|
39
|
+
export interface ProcessOptions {
|
|
40
|
+
pollInterval?: number;
|
|
41
|
+
maxJobs?: number;
|
|
42
|
+
maxRetries?: number;
|
|
43
|
+
batchSize?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Override the queue's topic for this drain (parity with Python's
|
|
46
|
+
* process(handler, topic=...)). When set, process() retargets the queue so
|
|
47
|
+
* pop() reads the requested topic instead of the construction-time one.
|
|
48
|
+
*/
|
|
49
|
+
topic?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ConsumeOptions {
|
|
52
|
+
/** Topic to consume (defaults to the constructor topic). */
|
|
53
|
+
topic?: string;
|
|
54
|
+
batchSize?: number;
|
|
55
|
+
pollInterval?: number;
|
|
56
|
+
iterations?: number;
|
|
57
|
+
id?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface QueueBackendInterface {
|
|
60
|
+
push(queue: string, payload: unknown, delay?: number, priority?: number): string;
|
|
61
|
+
pop(queue: string): QueueJob | null;
|
|
62
|
+
size(queue: string): number;
|
|
63
|
+
clear(queue: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Release whatever connection the backend holds, and be safe to call twice.
|
|
66
|
+
*
|
|
67
|
+
* REQUIRED, not optional, and deliberately so: it mirrors PHP's
|
|
68
|
+
* Tina4\Queue\QueueBackend, where close() has always been part of the
|
|
69
|
+
* interface. Optional would reintroduce exactly the bug this closes — a
|
|
70
|
+
* caller feature-detecting `backend.close?.()` silently skips the backend
|
|
71
|
+
* that forgot to implement it, which is how tina4-ruby's lite backend went
|
|
72
|
+
* un-closed by every `respond_to?(:close)` guard in its tree.
|
|
73
|
+
*/
|
|
74
|
+
close(): void;
|
|
75
|
+
complete?(queue: string, id: string): void;
|
|
76
|
+
fail?(queue: string, id: string, error: string, maxRetries: number, retryBackoff: number): void;
|
|
77
|
+
retry?(queue: string, id: string, delaySeconds?: number): void;
|
|
78
|
+
deadLetters?(queue: string, maxRetries?: number): QueueJob[];
|
|
79
|
+
failed?(queue: string, maxRetries?: number): QueueJob[];
|
|
80
|
+
retryFailed?(queue: string, maxRetries?: number): number;
|
|
81
|
+
purge?(queue: string, status?: string): number;
|
|
82
|
+
}
|
|
83
|
+
export declare class Queue {
|
|
84
|
+
private backendName;
|
|
85
|
+
private basePath;
|
|
86
|
+
private topic;
|
|
87
|
+
private _maxRetries;
|
|
88
|
+
private _retryBackoff;
|
|
89
|
+
private _visibilityTimeout;
|
|
90
|
+
private externalBackend;
|
|
91
|
+
private liteBackend;
|
|
92
|
+
/**
|
|
93
|
+
* Unified Queue constructor.
|
|
94
|
+
*
|
|
95
|
+
* Accepts either:
|
|
96
|
+
* - new Queue({ topic: "tasks", backend: "mongodb" })
|
|
97
|
+
* - new Queue("mongodb", { path: "data/queue" }) // legacy
|
|
98
|
+
* - new Queue() // file backend, default topic
|
|
99
|
+
*
|
|
100
|
+
* Throws on backend "rabbitmq" or "kafka" (ADR-0022).
|
|
101
|
+
*/
|
|
102
|
+
constructor(backendOrConfig?: string | QueueConfig, config?: QueueConfig);
|
|
103
|
+
/**
|
|
104
|
+
* Point this queue at ``topic`` in place.
|
|
105
|
+
*
|
|
106
|
+
* produce()/consume()/process() call this so a topic argument actually
|
|
107
|
+
* changes which topic is read or written. Without it the argument was
|
|
108
|
+
* accepted but ignored on the read path — pop() always used the
|
|
109
|
+
* construction-time topic, so consume("other") silently drained the wrong
|
|
110
|
+
* queue. The lite + external backends are topic-per-call (every push/pop/size
|
|
111
|
+
* takes the queue name), so changing this.topic retargets all of them; the
|
|
112
|
+
* job lifecycle (complete()/fail()/retry()) routes by the job's own .topic, so
|
|
113
|
+
* it is unaffected. Mirrors Python's Queue._retarget().
|
|
114
|
+
*/
|
|
115
|
+
private retarget;
|
|
116
|
+
/**
|
|
117
|
+
* Add a job to the queue. Returns job ID.
|
|
118
|
+
*
|
|
119
|
+
* Can be called as:
|
|
120
|
+
* queue.push(payload) — uses constructor topic
|
|
121
|
+
* queue.push(payload, delay) — uses constructor topic with delay
|
|
122
|
+
* queue.push(payload, delay, priority) — with delay and priority
|
|
123
|
+
*
|
|
124
|
+
* @param priority — Higher value = higher priority. Default 0.
|
|
125
|
+
*/
|
|
126
|
+
push(payload: unknown, delay?: number, priority?: number): string;
|
|
127
|
+
/**
|
|
128
|
+
* Atomically claim the next available job from this queue's topic. Returns null if empty.
|
|
129
|
+
*/
|
|
130
|
+
pop(): QueueJob | null;
|
|
131
|
+
/**
|
|
132
|
+
* Pop up to count jobs at once. Returns a partial batch if fewer available.
|
|
133
|
+
*/
|
|
134
|
+
popBatch(count: number): QueueJob[];
|
|
135
|
+
/**
|
|
136
|
+
* Process jobs from a queue with a handler function.
|
|
137
|
+
*/
|
|
138
|
+
process(handler: (job: QueueJob | QueueJob[]) => Promise<void> | void, options?: ProcessOptions): void;
|
|
139
|
+
/**
|
|
140
|
+
* Count jobs filtered by status. Defaults to "pending".
|
|
141
|
+
*/
|
|
142
|
+
size(status?: string): number;
|
|
143
|
+
/**
|
|
144
|
+
* Remove all jobs from this queue's topic. Returns the number cleared.
|
|
145
|
+
*/
|
|
146
|
+
clear(): number;
|
|
147
|
+
/**
|
|
148
|
+
* Release the backend's connection and free its resources.
|
|
149
|
+
*
|
|
150
|
+
* MEASURED 2026-08-04: close() was absent on the top-level Queue in ALL FOUR
|
|
151
|
+
* frameworks, and in Node it was absent on every backend class too — so an
|
|
152
|
+
* application had no way at all to hand a queue's client back. Same class of
|
|
153
|
+
* leak as ADR-0025 corollary 4 (client-lifecycle-is-bounded).
|
|
154
|
+
*
|
|
155
|
+
* Safe on EVERY backend: the file backend holds no connection and closes as a
|
|
156
|
+
* documented no-op, so a TINA4_QUEUE_BACKEND change never turns a working
|
|
157
|
+
* shutdown path into an error. Idempotent — each backend drops its handles on
|
|
158
|
+
* the first call, so a second call finds nothing to close and returns.
|
|
159
|
+
*
|
|
160
|
+
* HONEST CAVEAT specific to Node: neither backend it can reach holds a
|
|
161
|
+
* connection between calls today. The Mongo backend runs each operation in
|
|
162
|
+
* its own child process (ADR-0022), which closes its own client before it
|
|
163
|
+
* exits, and rabbitmq/kafka are refused outright at construction. So this
|
|
164
|
+
* releases nothing YET — it is here for the contract, and because the day the
|
|
165
|
+
* persistent-connection rewrite lands the client is released here with no
|
|
166
|
+
* change at any call site. Python, PHP and Ruby release a REAL client through
|
|
167
|
+
* the identically-named method.
|
|
168
|
+
*
|
|
169
|
+
* Treat the queue as spent afterwards and build a new one to keep working.
|
|
170
|
+
*/
|
|
171
|
+
close(): void;
|
|
172
|
+
/**
|
|
173
|
+
* Get jobs that failed at least once but are still being retried
|
|
174
|
+
* (0 < attempts < maxRetries). These live in the pending queue under the
|
|
175
|
+
* auto-retry lifecycle; dead-lettered jobs are returned by deadLetters().
|
|
176
|
+
*/
|
|
177
|
+
failed(): QueueJob[];
|
|
178
|
+
/**
|
|
179
|
+
* Retry all dead letter jobs for this queue's topic.
|
|
180
|
+
* Moves failed jobs that exceeded max retries back to pending.
|
|
181
|
+
*
|
|
182
|
+
* @param delaySeconds - Optional delay before jobs become available
|
|
183
|
+
* @returns true if at least one job was re-queued, false if none found
|
|
184
|
+
*/
|
|
185
|
+
retry(jobId?: string, delaySeconds?: number): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Get dead letter jobs — failed jobs that exceeded max retries.
|
|
188
|
+
*/
|
|
189
|
+
deadLetters(maxRetries?: number): QueueJob[];
|
|
190
|
+
/**
|
|
191
|
+
* Delete messages by status (e.g. "completed", "failed", "dead").
|
|
192
|
+
*/
|
|
193
|
+
purge(status: string, maxRetries?: number): number;
|
|
194
|
+
/**
|
|
195
|
+
* Re-queue failed jobs that haven't exceeded max retries back to pending.
|
|
196
|
+
*/
|
|
197
|
+
retryFailed(maxRetries?: number): number;
|
|
198
|
+
/**
|
|
199
|
+
* Produce a message onto a topic. Convenience wrapper around push().
|
|
200
|
+
*
|
|
201
|
+
* Retargets to the requested topic (restoring the prior one afterwards) so it
|
|
202
|
+
* shares the same retarget path consume()/process() use — keeping produce and
|
|
203
|
+
* consume symmetric on the same topic argument.
|
|
204
|
+
*/
|
|
205
|
+
produce(topic: string, payload: unknown, priority?: number, delay?: number): string;
|
|
206
|
+
/**
|
|
207
|
+
* Consume jobs from a topic using a generator (yield pattern).
|
|
208
|
+
*
|
|
209
|
+
* Usage:
|
|
210
|
+
* for (const job of queue.consume("emails")) {
|
|
211
|
+
* processEmail(job);
|
|
212
|
+
* }
|
|
213
|
+
*
|
|
214
|
+
* // Consume a specific job by ID:
|
|
215
|
+
* for (const job of queue.consume("emails", "job-id-123")) {
|
|
216
|
+
* processEmail(job);
|
|
217
|
+
* }
|
|
218
|
+
*/
|
|
219
|
+
/**
|
|
220
|
+
* Long-running async generator that polls the queue continuously.
|
|
221
|
+
* When empty, sleeps for pollInterval ms before polling again.
|
|
222
|
+
* No external while-loop or sleep needed.
|
|
223
|
+
*
|
|
224
|
+
* @param topic Queue topic (defaults to constructor topic)
|
|
225
|
+
* @param id Optional job ID — single yield, no polling
|
|
226
|
+
* @param pollInterval Milliseconds to sleep when queue is empty (default 1000)
|
|
227
|
+
*
|
|
228
|
+
* Usage:
|
|
229
|
+
* for await (const job of queue.consume("emails")) { ... }
|
|
230
|
+
* for await (const job of queue.consume("emails", undefined, 5000)) { ... }
|
|
231
|
+
*/
|
|
232
|
+
consume(topicOrOptions?: string | ConsumeOptions, id?: string, pollInterval?: number, iterations?: number, batchSize?: number): AsyncGenerator<QueueJob | QueueJob[]>;
|
|
233
|
+
/**
|
|
234
|
+
* Pop a specific job by ID from this queue's topic.
|
|
235
|
+
*/
|
|
236
|
+
popById(id: string): QueueJob | null;
|
|
237
|
+
/**
|
|
238
|
+
* Get the configured topic name.
|
|
239
|
+
*/
|
|
240
|
+
getTopic(): string;
|
|
241
|
+
getMaxRetries(): number;
|
|
242
|
+
getRetryBackoff(): number;
|
|
243
|
+
/**
|
|
244
|
+
* Resolved reservation/visibility timeout (seconds). <= 0 means the reclaim
|
|
245
|
+
* is disabled. File + MongoDB backends honour it; RabbitMQ/Kafka delegate to
|
|
246
|
+
* the broker.
|
|
247
|
+
*/
|
|
248
|
+
getVisibilityTimeout(): number;
|
|
249
|
+
/**
|
|
250
|
+
* Record a failed attempt for a job. The backend increments `attempts`
|
|
251
|
+
* exactly once and decides whether to re-enqueue (attempts < maxRetries,
|
|
252
|
+
* after retryBackoff seconds) or dead-letter (attempts >= maxRetries).
|
|
253
|
+
*/
|
|
254
|
+
_failJob(queue: string, job: QueueJob, error: string, maxRetries: number): void;
|
|
255
|
+
/**
|
|
256
|
+
* Re-queue a job back to the main queue directory with incremented attempts.
|
|
257
|
+
*/
|
|
258
|
+
_retryJob(queue: string, job: QueueJob, delaySeconds?: number): void;
|
|
259
|
+
/**
|
|
260
|
+
* Acknowledge a completed job — drop its reservation so the visibility reclaim
|
|
261
|
+
* never re-delivers it. Routes to the active backend: a reservation-based
|
|
262
|
+
* external backend (MongoDB) acks there (without this its reserved doc would
|
|
263
|
+
* be re-delivered after the visibility window); RabbitMQ (no-ack on get) and
|
|
264
|
+
* Kafka (offset-based) expose no complete(), so the lite path is used and is a
|
|
265
|
+
* harmless no-op for them since they already acked/own redelivery.
|
|
266
|
+
*/
|
|
267
|
+
_completeJob(queue: string, job: QueueJob): void;
|
|
268
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { QueueJob } from "../queue.js";
|
|
2
|
+
export interface KafkaConfig {
|
|
3
|
+
brokers?: string;
|
|
4
|
+
groupId?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Accepted for API parity with the file/MongoDB backends and IGNORED —
|
|
7
|
+
* consumer-group offsets own redelivery, so the framework-level visibility
|
|
8
|
+
* timeout does not apply here.
|
|
9
|
+
*/
|
|
10
|
+
visibilityTimeout?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* librdkafka-style SSL/SASL client config (a TLS broker/proxy). Mirrors the
|
|
14
|
+
* keys produced by Python's `KafkaConnector._security_config`. Every key is
|
|
15
|
+
* optional — an unset env var leaves the key OUT (librdkafka defaults to the
|
|
16
|
+
* PLAINTEXT protocol with no SASL).
|
|
17
|
+
*/
|
|
18
|
+
export interface KafkaSecurityConfig {
|
|
19
|
+
"security.protocol"?: string;
|
|
20
|
+
"ssl.ca.location"?: string;
|
|
21
|
+
"sasl.mechanism"?: string;
|
|
22
|
+
"sasl.username"?: string;
|
|
23
|
+
"sasl.password"?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Resolved producer/consumer config — brokers, client id, and security keys. */
|
|
26
|
+
export interface KafkaClientConfig extends KafkaSecurityConfig {
|
|
27
|
+
"bootstrap.servers": string;
|
|
28
|
+
"client.id": string;
|
|
29
|
+
"group.id"?: string;
|
|
30
|
+
"auto.offset.reset"?: string;
|
|
31
|
+
"enable.auto.commit"?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build the SSL/SASL client config from the environment (for a TLS broker or
|
|
35
|
+
* proxy in front of Kafka). Each setting is read from the Tina4-namespaced env
|
|
36
|
+
* var FIRST (`TINA4_KAFKA_SECURITY_PROTOCOL` …) and falls back to the bare
|
|
37
|
+
* librdkafka-convention name (`KAFKA_SECURITY_PROTOCOL` …) that many Kafka
|
|
38
|
+
* deployments already set. Honours security.protocol (e.g. SSL, SASL_SSL),
|
|
39
|
+
* ssl.ca.location, and optional SASL (mechanism / username / password). Unset
|
|
40
|
+
* keys are omitted so librdkafka keeps its PLAINTEXT defaults.
|
|
41
|
+
*
|
|
42
|
+
* Exported for testing/introspection — and exact parity with Python's
|
|
43
|
+
* `_security_config` (same key set, same precedence, same omit-when-unset).
|
|
44
|
+
*/
|
|
45
|
+
export declare function kafkaSecurityConfig(env?: NodeJS.ProcessEnv): KafkaSecurityConfig;
|
|
46
|
+
export interface QueueBackend {
|
|
47
|
+
push(queue: string, payload: unknown, delay?: number, priority?: number): string;
|
|
48
|
+
pop(queue: string): QueueJob | null;
|
|
49
|
+
size(queue: string): number;
|
|
50
|
+
clear(queue: string): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Kafka queue backend using raw Kafka protocol over TCP.
|
|
54
|
+
*
|
|
55
|
+
* Uses synchronous-style communication by spawning a child process
|
|
56
|
+
* for each operation, similar to the Redis session handler pattern.
|
|
57
|
+
*/
|
|
58
|
+
export declare class KafkaBackend implements QueueBackend {
|
|
59
|
+
private brokers;
|
|
60
|
+
private groupId;
|
|
61
|
+
constructor(config?: KafkaConfig);
|
|
62
|
+
/**
|
|
63
|
+
* Resolved connection config — exposed for testing/introspection.
|
|
64
|
+
*/
|
|
65
|
+
getConfig(): Required<Omit<KafkaConfig, "visibilityTimeout">>;
|
|
66
|
+
/**
|
|
67
|
+
* Resolved SSL/SASL client config from the environment (PLAINTEXT default).
|
|
68
|
+
* Mirrors Python's `KafkaConnector._security_config`.
|
|
69
|
+
*/
|
|
70
|
+
securityConfig(): KafkaSecurityConfig;
|
|
71
|
+
/**
|
|
72
|
+
* Full producer config — brokers + client id + the resolved security block.
|
|
73
|
+
* The security keys are applied to BOTH producer and consumer (matching
|
|
74
|
+
* Python's `_connect_confluent`).
|
|
75
|
+
*/
|
|
76
|
+
producerConfig(): KafkaClientConfig;
|
|
77
|
+
/**
|
|
78
|
+
* Full consumer config — brokers + client id + group id + the SAME resolved
|
|
79
|
+
* security block applied to the producer.
|
|
80
|
+
*/
|
|
81
|
+
consumerConfig(): KafkaClientConfig;
|
|
82
|
+
/**
|
|
83
|
+
* Parse broker string into host:port.
|
|
84
|
+
*/
|
|
85
|
+
private parseBroker;
|
|
86
|
+
/**
|
|
87
|
+
* Execute a Kafka operation synchronously via a child process.
|
|
88
|
+
*
|
|
89
|
+
* The wire protocol is hand-rolled (Tina4 is zero-dependency — no npm Kafka
|
|
90
|
+
* library). Produce uses Produce **v3** carrying a Kafka **v2 RecordBatch**
|
|
91
|
+
* (magic byte 2) with a **CRC-32C** (Castagnoli) checksum; Fetch uses Fetch
|
|
92
|
+
* **v4** and parses the v2 RecordBatch out of the response. Both formats are
|
|
93
|
+
* what a modern KRaft broker (apache/kafka 3.7.0) requires — the old
|
|
94
|
+
* Produce-v0 / message-format-v0 / CRC=0 batch is rejected by such brokers.
|
|
95
|
+
*/
|
|
96
|
+
private execSync;
|
|
97
|
+
/**
|
|
98
|
+
* Sleep synchronously between produce retries.
|
|
99
|
+
*
|
|
100
|
+
* `push()` is synchronous (the whole backend drives its socket through a child
|
|
101
|
+
* process), so there is no event loop to await on. `Atomics.wait` on a
|
|
102
|
+
* SharedArrayBuffer is the stdlib way to block a thread for a fixed time --
|
|
103
|
+
* no dependency, no busy-wait burning CPU.
|
|
104
|
+
*/
|
|
105
|
+
private static sleepSync;
|
|
106
|
+
/**
|
|
107
|
+
* Turn a sentinel from the protocol child into a thrown error, or return.
|
|
108
|
+
*
|
|
109
|
+
* The wording matches the Python and PHP backends exactly -- the parity rule
|
|
110
|
+
* covers user-visible error messages, not just behaviour.
|
|
111
|
+
*/
|
|
112
|
+
private static assertNoError;
|
|
113
|
+
push(queue: string, payload: unknown, _delay?: number): string;
|
|
114
|
+
pop(queue: string): QueueJob | null;
|
|
115
|
+
size(_queue: string): number;
|
|
116
|
+
clear(_queue: string): void;
|
|
117
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { type QueueJob } from "../job.js";
|
|
2
|
+
import { type JobQueueBridge } from "../job.js";
|
|
3
|
+
export declare class LiteBackend {
|
|
4
|
+
private basePath;
|
|
5
|
+
private seq;
|
|
6
|
+
/**
|
|
7
|
+
* Reservation/visibility timeout (seconds). A popped job is held in reserved/
|
|
8
|
+
* with availableAt = now + visibilityTimeout. If the consumer dies before
|
|
9
|
+
* complete()/fail() (crash, OOM, k8s eviction) the next pop() reclaims it once
|
|
10
|
+
* the window expires — incrementing attempts and re-enqueuing, or
|
|
11
|
+
* dead-lettering past maxRetries. <= 0 disables the reclaim (a reservation
|
|
12
|
+
* then lasts until the consumer acks — the old at-most-once behaviour).
|
|
13
|
+
*/
|
|
14
|
+
private visibilityTimeout;
|
|
15
|
+
constructor(basePath?: string, visibilityTimeout?: number);
|
|
16
|
+
private ensureDir;
|
|
17
|
+
private ensureFailedDir;
|
|
18
|
+
private ensureReservedDir;
|
|
19
|
+
private reservedPath;
|
|
20
|
+
private nowIso;
|
|
21
|
+
private futureIso;
|
|
22
|
+
private nextPrefix;
|
|
23
|
+
/**
|
|
24
|
+
* No-op: the file backend holds no connection to release.
|
|
25
|
+
*
|
|
26
|
+
* It exists so `Queue.close()` can call ONE method on every backend instead
|
|
27
|
+
* of testing for it, and so switching TINA4_QUEUE_BACKEND to "file" never
|
|
28
|
+
* turns a working close() into "backend.close is not a function". Idempotent
|
|
29
|
+
* by construction — there is nothing to drop.
|
|
30
|
+
*/
|
|
31
|
+
close(): void;
|
|
32
|
+
push(queue: string, payload: unknown, delay?: number, priority?: number): string;
|
|
33
|
+
/**
|
|
34
|
+
* Return [filename, jobData] for every pending, non-delayed job, ordered by
|
|
35
|
+
* the dequeue policy: highest priority first, ties broken oldest-first by
|
|
36
|
+
* createdAt. createdAt is an ISO-8601 string, so lexicographic comparison ==
|
|
37
|
+
* chronological order.
|
|
38
|
+
*/
|
|
39
|
+
private availableCandidates;
|
|
40
|
+
/**
|
|
41
|
+
* Persist a reservation record so a dead consumer's job is reclaimable.
|
|
42
|
+
*
|
|
43
|
+
* Stores reservedAt + availableAt = now + visibilityTimeout. The next pop()
|
|
44
|
+
* reclaims this job once availableAt has passed (see reclaimExpired).
|
|
45
|
+
* complete()/fail()/retry() delete the record.
|
|
46
|
+
*/
|
|
47
|
+
private writeReserved;
|
|
48
|
+
/**
|
|
49
|
+
* Return expired reservations to the queue (at-least-once delivery).
|
|
50
|
+
*
|
|
51
|
+
* A reserved job whose availableAt <= now means its consumer never
|
|
52
|
+
* acknowledged in time (crash / OOM / pod eviction). Atomically claim it
|
|
53
|
+
* (delete the reservation file), increment attempts, and either re-enqueue it
|
|
54
|
+
* (so the next pop picks it up) or dead-letter it once it has hit maxRetries.
|
|
55
|
+
* Disabled when visibilityTimeout <= 0.
|
|
56
|
+
*/
|
|
57
|
+
private reclaimExpired;
|
|
58
|
+
pop(queue: string, bridge: JobQueueBridge): QueueJob | null;
|
|
59
|
+
popBatch(queue: string, bridge: JobQueueBridge, count: number): QueueJob[];
|
|
60
|
+
/**
|
|
61
|
+
* Delete a job's reservation record (best-effort).
|
|
62
|
+
*/
|
|
63
|
+
private clearReservation;
|
|
64
|
+
/**
|
|
65
|
+
* Acknowledge a completed job — drop its reservation record so the visibility
|
|
66
|
+
* reclaim never re-delivers an already-acked job.
|
|
67
|
+
*/
|
|
68
|
+
completeJob(queue: string, job: QueueJob): void;
|
|
69
|
+
private static readonly DEAD_STATES;
|
|
70
|
+
size(queue: string, status?: string): number;
|
|
71
|
+
clear(queue: string): number;
|
|
72
|
+
/**
|
|
73
|
+
* Jobs that have failed at least once but are still being retried.
|
|
74
|
+
*
|
|
75
|
+
* Under the auto-retry lifecycle a failed-but-retryable job lives in the
|
|
76
|
+
* pending queue (not the dead-letter dir), so this scans the queue dir for
|
|
77
|
+
* pending jobs with attempts > 0 that have not yet exhausted their retries.
|
|
78
|
+
* Dead-lettered jobs are returned by deadLetters().
|
|
79
|
+
*/
|
|
80
|
+
failed(queue: string, maxRetries?: number): QueueJob[];
|
|
81
|
+
/**
|
|
82
|
+
* Revive a specific dead-letter job by id back to the pending queue.
|
|
83
|
+
*
|
|
84
|
+
* Manual override (Queue.retry(jobId) / job.retry()) — always revives a
|
|
85
|
+
* dead-letter regardless of attempt count. Returns false only if no
|
|
86
|
+
* dead-letter with that id exists.
|
|
87
|
+
*/
|
|
88
|
+
retry(queue: string, jobId: string, delaySeconds?: number): boolean;
|
|
89
|
+
deadLetters(queue: string, maxRetries?: number): QueueJob[];
|
|
90
|
+
purge(queue: string, status: string, maxRetries?: number): number;
|
|
91
|
+
/**
|
|
92
|
+
* Re-queue dead-letter jobs that are under the (possibly raised) limit back
|
|
93
|
+
* to pending. Mirrors Python retry_failed(): a job dead-lettered at the
|
|
94
|
+
* original maxRetries needs a raised limit to qualify again.
|
|
95
|
+
*/
|
|
96
|
+
retryFailed(queue: string, maxRetries?: number): number;
|
|
97
|
+
popById(queue: string, id: string): QueueJob | null;
|
|
98
|
+
/**
|
|
99
|
+
* Write the job back to the pending queue (queue dir).
|
|
100
|
+
*
|
|
101
|
+
* Re-enqueued jobs get a fresh createdAt so that within a priority tier they
|
|
102
|
+
* sort behind jobs that have not yet been attempted. `attempts` already
|
|
103
|
+
* reflects the latest failure count. The job carries its prior error.
|
|
104
|
+
*/
|
|
105
|
+
private requeue;
|
|
106
|
+
/**
|
|
107
|
+
* Move the job to the dead-letter (failed/) directory. Terminal until a
|
|
108
|
+
* manual retryFailed()/retry() revives it.
|
|
109
|
+
*/
|
|
110
|
+
private deadLetter;
|
|
111
|
+
/**
|
|
112
|
+
* Record a failed attempt.
|
|
113
|
+
*
|
|
114
|
+
* Increments `attempts` exactly once (the increment lives here, NOT in
|
|
115
|
+
* job.ts — see the double-increment fix). If the job still has retries left
|
|
116
|
+
* (attempts < maxRetries) it is automatically re-enqueued to pending, after
|
|
117
|
+
* an optional retryBackoff delay. Once it has been attempted maxRetries times
|
|
118
|
+
* (attempts >= maxRetries) it is moved to the dead-letter store.
|
|
119
|
+
*/
|
|
120
|
+
failJob(queue: string, job: QueueJob, error: string, maxRetries: number, retryBackoff?: number): void;
|
|
121
|
+
/**
|
|
122
|
+
* Explicit re-queue requested by the caller (job.retry()).
|
|
123
|
+
*
|
|
124
|
+
* Always re-enqueues regardless of the retry limit — manual override,
|
|
125
|
+
* distinct from the automatic failJob() path.
|
|
126
|
+
*/
|
|
127
|
+
retryJob(queue: string, job: QueueJob, delaySeconds?: number): void;
|
|
128
|
+
}
|