nvent 0.5.15 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.d.mts +181 -8
- package/dist/module.json +1 -1
- package/dist/module.mjs +1257 -1196
- package/dist/runtime/app/composables/useFunctionCall.d.ts +17 -0
- package/dist/runtime/app/composables/useFunctionCall.js +25 -0
- package/dist/runtime/app/composables/useNventStream.d.ts +23 -0
- package/dist/runtime/app/composables/useNventStream.js +58 -0
- package/dist/runtime/nitro/plugins/00.iii-lifecycle.d.ts +19 -0
- package/dist/runtime/nitro/plugins/00.iii-lifecycle.js +62 -0
- package/dist/runtime/nitro/plugins/01.iii-worker.d.ts +15 -0
- package/dist/runtime/nitro/plugins/01.iii-worker.js +91 -0
- package/dist/runtime/nitro/routes/stream-proxy.d.ts +17 -0
- package/dist/runtime/nitro/routes/stream-proxy.js +45 -0
- package/dist/runtime/nitro/utils/console.d.ts +32 -0
- package/dist/runtime/nitro/utils/console.js +95 -0
- package/dist/runtime/nitro/utils/defineFunction.d.ts +378 -3
- package/dist/runtime/nitro/utils/defineFunction.js +224 -4
- package/dist/runtime/nitro/utils/engine.d.ts +25 -0
- package/dist/runtime/nitro/utils/engine.js +130 -0
- package/dist/runtime/nitro/utils/nventDir.d.ts +1 -0
- package/dist/runtime/nitro/utils/nventDir.js +20 -0
- package/dist/runtime/nitro/utils/useIii.d.ts +23 -0
- package/dist/runtime/nitro/utils/useIii.js +14 -0
- package/dist/runtime/nitro/utils/workers/node.d.ts +24 -0
- package/dist/runtime/nitro/utils/workers/node.js +57 -0
- package/dist/runtime/nitro/utils/workers/python.d.ts +67 -0
- package/dist/runtime/nitro/utils/workers/python.js +220 -0
- package/dist/runtime/python/nvent.py +208 -0
- package/dist/runtime/python/worker_runtime.py +659 -0
- package/dist/runtime/virtual.d.ts +17 -0
- package/dist/types.d.mts +5 -1
- package/package.json +11 -14
- package/dist/runtime/adapters/base/index.d.ts +0 -6
- package/dist/runtime/adapters/base/index.js +0 -1
- package/dist/runtime/adapters/base/store-validator.d.ts +0 -48
- package/dist/runtime/adapters/base/store-validator.js +0 -147
- package/dist/runtime/adapters/builtin/file-queue.d.ts +0 -67
- package/dist/runtime/adapters/builtin/file-queue.js +0 -499
- package/dist/runtime/adapters/builtin/file-store.d.ts +0 -32
- package/dist/runtime/adapters/builtin/file-store.js +0 -206
- package/dist/runtime/adapters/builtin/file-stream.d.ts +0 -39
- package/dist/runtime/adapters/builtin/file-stream.js +0 -56
- package/dist/runtime/adapters/builtin/index.d.ts +0 -10
- package/dist/runtime/adapters/builtin/index.js +0 -5
- package/dist/runtime/adapters/builtin/memory-queue.d.ts +0 -52
- package/dist/runtime/adapters/builtin/memory-queue.js +0 -243
- package/dist/runtime/adapters/builtin/memory-store.d.ts +0 -69
- package/dist/runtime/adapters/builtin/memory-store.js +0 -357
- package/dist/runtime/adapters/builtin/memory-stream.d.ts +0 -21
- package/dist/runtime/adapters/builtin/memory-stream.js +0 -56
- package/dist/runtime/adapters/factory.d.ts +0 -31
- package/dist/runtime/adapters/factory.js +0 -135
- package/dist/runtime/adapters/index.d.ts +0 -8
- package/dist/runtime/adapters/index.js +0 -3
- package/dist/runtime/adapters/interfaces/index.d.ts +0 -11
- package/dist/runtime/adapters/interfaces/index.js +0 -3
- package/dist/runtime/adapters/interfaces/queue.d.ts +0 -155
- package/dist/runtime/adapters/interfaces/queue.js +0 -0
- package/dist/runtime/adapters/interfaces/store.d.ts +0 -299
- package/dist/runtime/adapters/interfaces/store.js +0 -0
- package/dist/runtime/adapters/interfaces/stream.d.ts +0 -62
- package/dist/runtime/adapters/interfaces/stream.js +0 -0
- package/dist/runtime/adapters/registry.d.ts +0 -85
- package/dist/runtime/adapters/registry.js +0 -161
- package/dist/runtime/config/index.d.ts +0 -23
- package/dist/runtime/config/index.js +0 -200
- package/dist/runtime/config/types.d.ts +0 -439
- package/dist/runtime/config/types.js +0 -0
- package/dist/runtime/events/eventBus.d.ts +0 -20
- package/dist/runtime/events/eventBus.js +0 -35
- package/dist/runtime/events/types.d.ts +0 -144
- package/dist/runtime/events/types.js +0 -0
- package/dist/runtime/events/utils/scheduleTrigger.d.ts +0 -8
- package/dist/runtime/events/utils/scheduleTrigger.js +0 -71
- package/dist/runtime/events/utils/stallDetector.d.ts +0 -83
- package/dist/runtime/events/utils/stallDetector.js +0 -318
- package/dist/runtime/events/utils/triggerRuntime.d.ts +0 -58
- package/dist/runtime/events/utils/triggerRuntime.js +0 -212
- package/dist/runtime/events/wiring/flowWiring.d.ts +0 -34
- package/dist/runtime/events/wiring/flowWiring.js +0 -1258
- package/dist/runtime/events/wiring/registry.d.ts +0 -19
- package/dist/runtime/events/wiring/registry.js +0 -43
- package/dist/runtime/events/wiring/stateWiring.d.ts +0 -37
- package/dist/runtime/events/wiring/stateWiring.js +0 -92
- package/dist/runtime/events/wiring/streamWiring.d.ts +0 -36
- package/dist/runtime/events/wiring/streamWiring.js +0 -156
- package/dist/runtime/events/wiring/triggerWiring.d.ts +0 -21
- package/dist/runtime/events/wiring/triggerWiring.js +0 -422
- package/dist/runtime/nitro/plugins/00.adapters.d.ts +0 -14
- package/dist/runtime/nitro/plugins/00.adapters.js +0 -73
- package/dist/runtime/nitro/plugins/01.ws-lifecycle.d.ts +0 -5
- package/dist/runtime/nitro/plugins/01.ws-lifecycle.js +0 -69
- package/dist/runtime/nitro/plugins/02.workers.d.ts +0 -2
- package/dist/runtime/nitro/plugins/02.workers.js +0 -92
- package/dist/runtime/nitro/plugins/03.triggers.d.ts +0 -12
- package/dist/runtime/nitro/plugins/03.triggers.js +0 -55
- package/dist/runtime/nitro/routes/webhook.await.d.ts +0 -23
- package/dist/runtime/nitro/routes/webhook.await.js +0 -112
- package/dist/runtime/nitro/routes/webhook.trigger.d.ts +0 -86
- package/dist/runtime/nitro/routes/webhook.trigger.js +0 -73
- package/dist/runtime/nitro/utils/adapters.d.ts +0 -66
- package/dist/runtime/nitro/utils/adapters.js +0 -51
- package/dist/runtime/nitro/utils/awaitPatterns/event.d.ts +0 -15
- package/dist/runtime/nitro/utils/awaitPatterns/event.js +0 -128
- package/dist/runtime/nitro/utils/awaitPatterns/index.d.ts +0 -28
- package/dist/runtime/nitro/utils/awaitPatterns/index.js +0 -55
- package/dist/runtime/nitro/utils/awaitPatterns/schedule.d.ts +0 -16
- package/dist/runtime/nitro/utils/awaitPatterns/schedule.js +0 -83
- package/dist/runtime/nitro/utils/awaitPatterns/time.d.ts +0 -15
- package/dist/runtime/nitro/utils/awaitPatterns/time.js +0 -71
- package/dist/runtime/nitro/utils/awaitPatterns/webhook.d.ts +0 -15
- package/dist/runtime/nitro/utils/awaitPatterns/webhook.js +0 -128
- package/dist/runtime/nitro/utils/defineFunctionConfig.d.ts +0 -378
- package/dist/runtime/nitro/utils/defineFunctionConfig.js +0 -3
- package/dist/runtime/nitro/utils/defineHooks.d.ts +0 -95
- package/dist/runtime/nitro/utils/defineHooks.js +0 -9
- package/dist/runtime/nitro/utils/registerAdapter.d.ts +0 -59
- package/dist/runtime/nitro/utils/registerAdapter.js +0 -13
- package/dist/runtime/nitro/utils/useAwait.d.ts +0 -83
- package/dist/runtime/nitro/utils/useAwait.js +0 -169
- package/dist/runtime/nitro/utils/useEventManager.d.ts +0 -15
- package/dist/runtime/nitro/utils/useEventManager.js +0 -26
- package/dist/runtime/nitro/utils/useFlow.d.ts +0 -66
- package/dist/runtime/nitro/utils/useFlow.js +0 -310
- package/dist/runtime/nitro/utils/useHookRegistry.d.ts +0 -40
- package/dist/runtime/nitro/utils/useHookRegistry.js +0 -25
- package/dist/runtime/nitro/utils/useNventLogger.d.ts +0 -42
- package/dist/runtime/nitro/utils/useNventLogger.js +0 -54
- package/dist/runtime/nitro/utils/useRunContext.d.ts +0 -6
- package/dist/runtime/nitro/utils/useRunContext.js +0 -102
- package/dist/runtime/nitro/utils/useStreamTopics.d.ts +0 -83
- package/dist/runtime/nitro/utils/useStreamTopics.js +0 -94
- package/dist/runtime/nitro/utils/useTrigger.d.ts +0 -150
- package/dist/runtime/nitro/utils/useTrigger.js +0 -311
- package/dist/runtime/nitro/utils/wsPeerManager.d.ts +0 -44
- package/dist/runtime/nitro/utils/wsPeerManager.js +0 -32
- package/dist/runtime/scheduler/index.d.ts +0 -33
- package/dist/runtime/scheduler/index.js +0 -42
- package/dist/runtime/scheduler/scheduler.d.ts +0 -132
- package/dist/runtime/scheduler/scheduler.js +0 -799
- package/dist/runtime/scheduler/types.d.ts +0 -122
- package/dist/runtime/scheduler/types.js +0 -0
- package/dist/runtime/worker/node/runner.d.ts +0 -95
- package/dist/runtime/worker/node/runner.js +0 -272
- package/dist/runtime/worker/python/get_config.py +0 -64
- package/dist/runtime/worker/system/awaitHandlers.d.ts +0 -27
- package/dist/runtime/worker/system/awaitHandlers.js +0 -230
- package/dist/runtime/worker/system/index.d.ts +0 -24
- package/dist/runtime/worker/system/index.js +0 -39
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
import * as fastq from "fastq";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import cronParser from "cron-parser";
|
|
5
|
-
export class FileQueueAdapter {
|
|
6
|
-
jobs = /* @__PURE__ */ new Map();
|
|
7
|
-
eventListeners = /* @__PURE__ */ new Map();
|
|
8
|
-
workers = /* @__PURE__ */ new Map();
|
|
9
|
-
scheduledJobs = /* @__PURE__ */ new Map();
|
|
10
|
-
options;
|
|
11
|
-
initialized = false;
|
|
12
|
-
constructor(options) {
|
|
13
|
-
this.options = {
|
|
14
|
-
dataDir: options.dataDir,
|
|
15
|
-
maxQueueSize: options.maxQueueSize || 1e4
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
async init() {
|
|
19
|
-
if (this.initialized) return;
|
|
20
|
-
await fs.mkdir(this.options.dataDir, { recursive: true });
|
|
21
|
-
await this.loadJobsFromDisk();
|
|
22
|
-
this.initialized = true;
|
|
23
|
-
}
|
|
24
|
-
async loadJobsFromDisk() {
|
|
25
|
-
const queuesDir = this.options.dataDir;
|
|
26
|
-
try {
|
|
27
|
-
const queueNames = await fs.readdir(queuesDir);
|
|
28
|
-
for (const queueName of queueNames) {
|
|
29
|
-
const jobsDir = join(queuesDir, queueName, "jobs");
|
|
30
|
-
try {
|
|
31
|
-
const jobFiles = await fs.readdir(jobsDir);
|
|
32
|
-
for (const jobFile of jobFiles) {
|
|
33
|
-
if (!jobFile.endsWith(".json")) continue;
|
|
34
|
-
const jobPath = join(jobsDir, jobFile);
|
|
35
|
-
const jobData = await fs.readFile(jobPath, "utf-8");
|
|
36
|
-
const job = JSON.parse(jobData);
|
|
37
|
-
this.jobs.set(job.id, job);
|
|
38
|
-
}
|
|
39
|
-
} catch {
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
} catch {
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async persistJob(queueName, job) {
|
|
46
|
-
const jobsDir = join(this.options.dataDir, queueName, "jobs");
|
|
47
|
-
await fs.mkdir(jobsDir, { recursive: true });
|
|
48
|
-
const jobPath = join(jobsDir, `${job.id}.json`);
|
|
49
|
-
await fs.writeFile(jobPath, JSON.stringify(job, null, 2));
|
|
50
|
-
}
|
|
51
|
-
async deleteJobFile(queueName, jobId) {
|
|
52
|
-
const jobPath = join(this.options.dataDir, queueName, "jobs", `${jobId}.json`);
|
|
53
|
-
try {
|
|
54
|
-
await fs.unlink(jobPath);
|
|
55
|
-
} catch {
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
async enqueue(queueName, job) {
|
|
59
|
-
const jobId = job.opts?.jobId || this.generateId();
|
|
60
|
-
if (this.jobs.has(jobId)) {
|
|
61
|
-
return jobId;
|
|
62
|
-
}
|
|
63
|
-
if (this.jobs.size >= this.options.maxQueueSize) {
|
|
64
|
-
throw new Error(`Queue ${queueName} is full (max: ${this.options.maxQueueSize})`);
|
|
65
|
-
}
|
|
66
|
-
const internalJob = {
|
|
67
|
-
id: jobId,
|
|
68
|
-
name: job.name,
|
|
69
|
-
data: { ...job.data, __queueName: queueName },
|
|
70
|
-
state: "waiting",
|
|
71
|
-
timestamp: Date.now(),
|
|
72
|
-
attemptsMade: 0,
|
|
73
|
-
opts: job.opts
|
|
74
|
-
// Store job options for retry logic
|
|
75
|
-
};
|
|
76
|
-
this.jobs.set(jobId, internalJob);
|
|
77
|
-
await this.persistJob(queueName, internalJob);
|
|
78
|
-
this.emitEvent(queueName, "waiting", { jobId, job: internalJob });
|
|
79
|
-
const workerInfo = this.workers.get(queueName);
|
|
80
|
-
if (workerInfo && !workerInfo.paused) {
|
|
81
|
-
workerInfo.queue.push({ jobId, jobName: job.name, data: internalJob.data }).catch(() => {
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return jobId;
|
|
85
|
-
}
|
|
86
|
-
async schedule(queueName, job, opts) {
|
|
87
|
-
if (opts?.delay) {
|
|
88
|
-
const jobId = this.generateId();
|
|
89
|
-
const internalJob = {
|
|
90
|
-
id: jobId,
|
|
91
|
-
name: job.name,
|
|
92
|
-
data: { ...job.data, __queueName: queueName },
|
|
93
|
-
state: "delayed",
|
|
94
|
-
timestamp: Date.now()
|
|
95
|
-
};
|
|
96
|
-
this.jobs.set(jobId, internalJob);
|
|
97
|
-
await this.persistJob(queueName, internalJob);
|
|
98
|
-
this.emitEvent(queueName, "delayed", { jobId, job: internalJob, delay: opts.delay });
|
|
99
|
-
setTimeout(() => {
|
|
100
|
-
this.enqueue(queueName, job);
|
|
101
|
-
}, opts.delay);
|
|
102
|
-
return jobId;
|
|
103
|
-
}
|
|
104
|
-
if (opts?.cron || opts?.repeat) {
|
|
105
|
-
const scheduleId = this.generateId();
|
|
106
|
-
const cronPattern = opts.cron || opts.repeat?.pattern;
|
|
107
|
-
if (!cronPattern) {
|
|
108
|
-
throw new Error("Cron pattern is required for scheduled jobs");
|
|
109
|
-
}
|
|
110
|
-
const scheduledJob = {
|
|
111
|
-
queueName,
|
|
112
|
-
job,
|
|
113
|
-
opts,
|
|
114
|
-
repeatCount: 0
|
|
115
|
-
};
|
|
116
|
-
this.scheduledJobs.set(scheduleId, scheduledJob);
|
|
117
|
-
this.scheduleCronJob(scheduleId, scheduledJob);
|
|
118
|
-
console.info(`[FileQueue] Scheduled job "${job.name}" with cron pattern "${cronPattern}" (id: ${scheduleId})`);
|
|
119
|
-
return scheduleId;
|
|
120
|
-
}
|
|
121
|
-
return this.enqueue(queueName, job);
|
|
122
|
-
}
|
|
123
|
-
async getJob(_queueName, id) {
|
|
124
|
-
return this.jobs.get(id) || null;
|
|
125
|
-
}
|
|
126
|
-
async getJobs(queueName, query) {
|
|
127
|
-
let jobs = Array.from(this.jobs.values()).filter((j) => j.data?.__queueName === queueName);
|
|
128
|
-
if (query?.state && query.state.length > 0) {
|
|
129
|
-
jobs = jobs.filter((j) => query.state.includes(j.state));
|
|
130
|
-
}
|
|
131
|
-
if (query?.offset) {
|
|
132
|
-
jobs = jobs.slice(query.offset);
|
|
133
|
-
}
|
|
134
|
-
if (query?.limit) {
|
|
135
|
-
jobs = jobs.slice(0, query.limit);
|
|
136
|
-
}
|
|
137
|
-
return jobs;
|
|
138
|
-
}
|
|
139
|
-
on(queueName, event, callback) {
|
|
140
|
-
const key = `${queueName}:${event}`;
|
|
141
|
-
if (!this.eventListeners.has(key)) {
|
|
142
|
-
this.eventListeners.set(key, []);
|
|
143
|
-
}
|
|
144
|
-
this.eventListeners.get(key).push(callback);
|
|
145
|
-
return () => {
|
|
146
|
-
const listeners = this.eventListeners.get(key);
|
|
147
|
-
if (listeners) {
|
|
148
|
-
const index = listeners.indexOf(callback);
|
|
149
|
-
if (index > -1) {
|
|
150
|
-
listeners.splice(index, 1);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
async isPaused(queueName) {
|
|
156
|
-
const workerInfo = this.workers.get(queueName);
|
|
157
|
-
return workerInfo?.paused || false;
|
|
158
|
-
}
|
|
159
|
-
async getJobCounts(queueName) {
|
|
160
|
-
const jobs = Array.from(this.jobs.values()).filter((j) => j.data?.__queueName === queueName);
|
|
161
|
-
return {
|
|
162
|
-
active: jobs.filter((j) => j.state === "active").length,
|
|
163
|
-
completed: jobs.filter((j) => j.state === "completed").length,
|
|
164
|
-
failed: jobs.filter((j) => j.state === "failed").length,
|
|
165
|
-
delayed: jobs.filter((j) => j.state === "delayed").length,
|
|
166
|
-
waiting: jobs.filter((j) => j.state === "waiting").length,
|
|
167
|
-
paused: jobs.filter((j) => j.state === "paused").length
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
async getScheduledJobs(queueName) {
|
|
171
|
-
const scheduled = [];
|
|
172
|
-
for (const [scheduleId, scheduledJob] of this.scheduledJobs.entries()) {
|
|
173
|
-
if (scheduledJob.queueName === queueName) {
|
|
174
|
-
const cronPattern = scheduledJob.opts.cron || scheduledJob.opts.repeat?.pattern;
|
|
175
|
-
let nextRun;
|
|
176
|
-
if (cronPattern) {
|
|
177
|
-
try {
|
|
178
|
-
const interval = cronParser.parseExpression(cronPattern);
|
|
179
|
-
nextRun = interval.next().toDate();
|
|
180
|
-
} catch {
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
scheduled.push({
|
|
184
|
-
id: scheduleId,
|
|
185
|
-
jobName: scheduledJob.job.name,
|
|
186
|
-
queueName: scheduledJob.queueName,
|
|
187
|
-
cron: scheduledJob.opts.cron,
|
|
188
|
-
pattern: scheduledJob.opts.repeat?.pattern,
|
|
189
|
-
nextRun,
|
|
190
|
-
repeatCount: scheduledJob.repeatCount,
|
|
191
|
-
limit: scheduledJob.opts.repeat?.limit
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return scheduled;
|
|
196
|
-
}
|
|
197
|
-
async removeScheduledJob(scheduleId) {
|
|
198
|
-
const scheduledJob = this.scheduledJobs.get(scheduleId);
|
|
199
|
-
if (!scheduledJob) {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
if (scheduledJob.timerId) {
|
|
203
|
-
clearTimeout(scheduledJob.timerId);
|
|
204
|
-
}
|
|
205
|
-
this.scheduledJobs.delete(scheduleId);
|
|
206
|
-
console.info(`[FileQueue] Removed scheduled job: ${scheduleId}`);
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
async pause(queueName) {
|
|
210
|
-
const workerInfo = this.workers.get(queueName);
|
|
211
|
-
if (workerInfo) {
|
|
212
|
-
workerInfo.paused = true;
|
|
213
|
-
workerInfo.queue.pause();
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
async resume(queueName) {
|
|
217
|
-
const workerInfo = this.workers.get(queueName);
|
|
218
|
-
if (workerInfo) {
|
|
219
|
-
workerInfo.paused = false;
|
|
220
|
-
workerInfo.queue.resume();
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
async close() {
|
|
224
|
-
for (const [scheduleId, scheduledJob] of this.scheduledJobs.entries()) {
|
|
225
|
-
if (scheduledJob.timerId) {
|
|
226
|
-
clearTimeout(scheduledJob.timerId);
|
|
227
|
-
}
|
|
228
|
-
console.info(`[FileQueue] Stopped scheduled job: ${scheduleId}`);
|
|
229
|
-
}
|
|
230
|
-
this.scheduledJobs.clear();
|
|
231
|
-
const drainPromises = Array.from(this.workers.values()).map((w) => w.queue.drained());
|
|
232
|
-
await Promise.all(drainPromises);
|
|
233
|
-
const persistPromises = [];
|
|
234
|
-
for (const [_jobId, job] of Array.from(this.jobs.entries())) {
|
|
235
|
-
const queueName = job.data?.__queueName || "default";
|
|
236
|
-
persistPromises.push(this.persistJob(queueName, job));
|
|
237
|
-
}
|
|
238
|
-
await Promise.all(persistPromises);
|
|
239
|
-
this.jobs.clear();
|
|
240
|
-
this.eventListeners.clear();
|
|
241
|
-
this.workers.clear();
|
|
242
|
-
}
|
|
243
|
-
// ============================================================
|
|
244
|
-
// Worker Management
|
|
245
|
-
// ============================================================
|
|
246
|
-
registerWorker(queueName, jobName, handler, opts) {
|
|
247
|
-
const requestedConcurrency = opts?.concurrency || 1;
|
|
248
|
-
let workerInfo = this.workers.get(queueName);
|
|
249
|
-
if (workerInfo) {
|
|
250
|
-
console.info(`[FileQueue] Adding handler for job "${jobName}" to queue "${queueName}"`);
|
|
251
|
-
workerInfo.handlers.set(jobName, handler);
|
|
252
|
-
if (requestedConcurrency > workerInfo.concurrency) {
|
|
253
|
-
workerInfo.concurrency = requestedConcurrency;
|
|
254
|
-
workerInfo.queue.concurrency = requestedConcurrency;
|
|
255
|
-
console.info(`[FileQueue] Updated queue "${queueName}" concurrency to ${requestedConcurrency}`);
|
|
256
|
-
}
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
console.info(`[FileQueue] Creating new worker for queue: ${queueName}`);
|
|
260
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
261
|
-
handlers.set(jobName, handler);
|
|
262
|
-
const dispatcher = async (task) => {
|
|
263
|
-
const handler2 = handlers.get(task.jobName);
|
|
264
|
-
if (!handler2) {
|
|
265
|
-
const error = `No handler for job "${task.jobName}" on queue "${queueName}". Available: ${Array.from(handlers.keys()).join(", ")}`;
|
|
266
|
-
console.error(error);
|
|
267
|
-
throw new Error(error);
|
|
268
|
-
}
|
|
269
|
-
const storedJob = this.jobs.get(task.jobId);
|
|
270
|
-
if (!storedJob) {
|
|
271
|
-
throw new Error(`Job ${task.jobId} not found`);
|
|
272
|
-
}
|
|
273
|
-
const currentAttempts = storedJob.attemptsMade || 0;
|
|
274
|
-
const maxAttempts = storedJob.opts?.attempts || 1;
|
|
275
|
-
await this.updateJobState(queueName, task.jobId, "active", {
|
|
276
|
-
processedOn: Date.now()
|
|
277
|
-
});
|
|
278
|
-
this.emitEvent(queueName, "active", { jobId: task.jobId });
|
|
279
|
-
try {
|
|
280
|
-
const jobLike = {
|
|
281
|
-
id: task.jobId,
|
|
282
|
-
name: task.jobName,
|
|
283
|
-
data: task.data,
|
|
284
|
-
attemptsMade: currentAttempts,
|
|
285
|
-
opts: { attempts: maxAttempts, ...storedJob.opts }
|
|
286
|
-
};
|
|
287
|
-
const result = await handler2(jobLike, {});
|
|
288
|
-
if (result && typeof result === "object" && result.awaiting === true) {
|
|
289
|
-
this.jobs.delete(task.jobId);
|
|
290
|
-
await this.deleteJobFile(queueName, task.jobId);
|
|
291
|
-
return result;
|
|
292
|
-
}
|
|
293
|
-
await this.updateJobState(queueName, task.jobId, "completed", {
|
|
294
|
-
returnvalue: result,
|
|
295
|
-
finishedOn: Date.now()
|
|
296
|
-
});
|
|
297
|
-
this.emitEvent(queueName, "completed", { jobId: task.jobId, returnvalue: result });
|
|
298
|
-
await this.cleanupCompletedJobs(queueName, storedJob.opts?.removeOnComplete);
|
|
299
|
-
return result;
|
|
300
|
-
} catch (err) {
|
|
301
|
-
const newAttemptCount = currentAttempts + 1;
|
|
302
|
-
if (newAttemptCount < maxAttempts) {
|
|
303
|
-
let retryDelay = 0;
|
|
304
|
-
if (storedJob.opts?.backoff) {
|
|
305
|
-
const { type, delay } = storedJob.opts.backoff;
|
|
306
|
-
if (type === "exponential") {
|
|
307
|
-
retryDelay = delay * Math.pow(2, newAttemptCount - 1);
|
|
308
|
-
} else {
|
|
309
|
-
retryDelay = delay;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
await this.updateJobState(queueName, task.jobId, "waiting", {
|
|
313
|
-
attemptsMade: newAttemptCount,
|
|
314
|
-
failedReason: `Attempt ${newAttemptCount}/${maxAttempts} failed: ${err.message}`
|
|
315
|
-
});
|
|
316
|
-
console.info(
|
|
317
|
-
`[FileQueue] Retrying job ${task.jobId} (attempt ${newAttemptCount + 1}/${maxAttempts}) after ${retryDelay}ms delay`
|
|
318
|
-
);
|
|
319
|
-
const currentWorkerInfo = this.workers.get(queueName);
|
|
320
|
-
if (currentWorkerInfo) {
|
|
321
|
-
setTimeout(() => {
|
|
322
|
-
currentWorkerInfo.queue.push(task).catch(() => {
|
|
323
|
-
});
|
|
324
|
-
}, retryDelay);
|
|
325
|
-
}
|
|
326
|
-
throw err;
|
|
327
|
-
}
|
|
328
|
-
await this.updateJobState(queueName, task.jobId, "failed", {
|
|
329
|
-
failedReason: `Failed after ${newAttemptCount} attempts: ${err.message}`,
|
|
330
|
-
finishedOn: Date.now(),
|
|
331
|
-
attemptsMade: newAttemptCount
|
|
332
|
-
});
|
|
333
|
-
this.emitEvent(queueName, "failed", {
|
|
334
|
-
jobId: task.jobId,
|
|
335
|
-
failedReason: err.message,
|
|
336
|
-
attemptsMade: newAttemptCount,
|
|
337
|
-
maxAttempts
|
|
338
|
-
});
|
|
339
|
-
await this.cleanupFailedJobs(queueName, storedJob.opts?.removeOnFail);
|
|
340
|
-
throw err;
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
const concurrency = opts?.concurrency || 1;
|
|
344
|
-
const queue = fastq.promise(dispatcher, concurrency);
|
|
345
|
-
const shouldPause = opts?.autorun === false;
|
|
346
|
-
workerInfo = {
|
|
347
|
-
queue,
|
|
348
|
-
handlers,
|
|
349
|
-
paused: shouldPause || false,
|
|
350
|
-
concurrency
|
|
351
|
-
};
|
|
352
|
-
if (shouldPause) {
|
|
353
|
-
queue.pause();
|
|
354
|
-
console.info(`[FileQueue] Worker for "${queueName}" created but paused`);
|
|
355
|
-
}
|
|
356
|
-
this.workers.set(queueName, workerInfo);
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Start processing waiting jobs for a queue
|
|
360
|
-
* Should be called after all handlers are registered
|
|
361
|
-
*/
|
|
362
|
-
startProcessingQueue(queueName) {
|
|
363
|
-
const workerInfo = this.workers.get(queueName);
|
|
364
|
-
if (!workerInfo || workerInfo.paused) return;
|
|
365
|
-
const waitingJobs = Array.from(this.jobs.values()).filter(
|
|
366
|
-
(j) => j.state === "waiting" && j.data?.__queueName === queueName
|
|
367
|
-
);
|
|
368
|
-
for (const job of waitingJobs) {
|
|
369
|
-
workerInfo.queue.push({ jobId: job.id, jobName: job.name, data: job.data }).catch(() => {
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
// ============================================================
|
|
374
|
-
// Helper Methods
|
|
375
|
-
// ============================================================
|
|
376
|
-
async updateJobState(queueName, jobId, state, extra) {
|
|
377
|
-
const job = this.jobs.get(jobId);
|
|
378
|
-
if (job) {
|
|
379
|
-
job.state = state;
|
|
380
|
-
if (extra) {
|
|
381
|
-
Object.assign(job, extra);
|
|
382
|
-
}
|
|
383
|
-
await this.persistJob(queueName, job);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
emitEvent(queueName, event, payload) {
|
|
387
|
-
const key = `${queueName}:${event}`;
|
|
388
|
-
const listeners = this.eventListeners.get(key) || [];
|
|
389
|
-
for (const callback of listeners) {
|
|
390
|
-
try {
|
|
391
|
-
callback(payload);
|
|
392
|
-
} catch (error) {
|
|
393
|
-
console.error(`[FileQueueAdapter] Error in event listener for ${key}:`, error);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Clean up completed jobs based on removeOnComplete option
|
|
399
|
-
* - true: delete immediately
|
|
400
|
-
* - false/undefined: keep forever
|
|
401
|
-
* - number: keep last N jobs, delete older ones
|
|
402
|
-
*/
|
|
403
|
-
async cleanupCompletedJobs(queueName, removeOnComplete) {
|
|
404
|
-
if (removeOnComplete === false || removeOnComplete === void 0) {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (removeOnComplete === true) {
|
|
408
|
-
const completedJobs = Array.from(this.jobs.values()).filter(
|
|
409
|
-
(j) => j.state === "completed" && j.data?.__queueName === queueName
|
|
410
|
-
);
|
|
411
|
-
for (const job of completedJobs) {
|
|
412
|
-
this.jobs.delete(job.id);
|
|
413
|
-
await this.deleteJobFile(queueName, job.id);
|
|
414
|
-
}
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
if (typeof removeOnComplete === "number") {
|
|
418
|
-
const completedJobs = Array.from(this.jobs.values()).filter((j) => j.state === "completed" && j.data?.__queueName === queueName).sort((a, b) => (b.finishedOn || 0) - (a.finishedOn || 0));
|
|
419
|
-
const jobsToDelete = completedJobs.slice(removeOnComplete);
|
|
420
|
-
for (const job of jobsToDelete) {
|
|
421
|
-
this.jobs.delete(job.id);
|
|
422
|
-
await this.deleteJobFile(queueName, job.id);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Clean up failed jobs based on removeOnFail option
|
|
428
|
-
* - true: delete immediately
|
|
429
|
-
* - false/undefined: keep forever
|
|
430
|
-
* - number: keep last N jobs, delete older ones
|
|
431
|
-
*/
|
|
432
|
-
async cleanupFailedJobs(queueName, removeOnFail) {
|
|
433
|
-
if (removeOnFail === false || removeOnFail === void 0) {
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
if (removeOnFail === true) {
|
|
437
|
-
const failedJobs = Array.from(this.jobs.values()).filter(
|
|
438
|
-
(j) => j.state === "failed" && j.data?.__queueName === queueName
|
|
439
|
-
);
|
|
440
|
-
for (const job of failedJobs) {
|
|
441
|
-
this.jobs.delete(job.id);
|
|
442
|
-
await this.deleteJobFile(queueName, job.id);
|
|
443
|
-
}
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
if (typeof removeOnFail === "number") {
|
|
447
|
-
const failedJobs = Array.from(this.jobs.values()).filter((j) => j.state === "failed" && j.data?.__queueName === queueName).sort((a, b) => (b.finishedOn || 0) - (a.finishedOn || 0));
|
|
448
|
-
const jobsToDelete = failedJobs.slice(removeOnFail);
|
|
449
|
-
for (const job of jobsToDelete) {
|
|
450
|
-
this.jobs.delete(job.id);
|
|
451
|
-
await this.deleteJobFile(queueName, job.id);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
generateId() {
|
|
456
|
-
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
457
|
-
}
|
|
458
|
-
scheduleCronJob(scheduleId, scheduledJob) {
|
|
459
|
-
const cronPattern = scheduledJob.opts.cron || scheduledJob.opts.repeat?.pattern;
|
|
460
|
-
if (!cronPattern) {
|
|
461
|
-
console.error(`[FileQueue] No cron pattern found for schedule ${scheduleId}`);
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
try {
|
|
465
|
-
const interval = cronParser.parseExpression(cronPattern);
|
|
466
|
-
const nextRun = interval.next().toDate();
|
|
467
|
-
const now = /* @__PURE__ */ new Date();
|
|
468
|
-
const delay = nextRun.getTime() - now.getTime();
|
|
469
|
-
console.info(
|
|
470
|
-
`[FileQueue] Next run for schedule ${scheduleId}: ${nextRun.toISOString()} (in ${Math.round(delay / 1e3)}s)`
|
|
471
|
-
);
|
|
472
|
-
scheduledJob.timerId = setTimeout(async () => {
|
|
473
|
-
const limit = scheduledJob.opts.repeat?.limit;
|
|
474
|
-
if (limit !== void 0 && scheduledJob.repeatCount >= limit) {
|
|
475
|
-
console.info(`[FileQueue] Schedule ${scheduleId} reached repeat limit (${limit})`);
|
|
476
|
-
this.scheduledJobs.delete(scheduleId);
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
try {
|
|
480
|
-
await this.enqueue(scheduledJob.queueName, scheduledJob.job);
|
|
481
|
-
scheduledJob.repeatCount++;
|
|
482
|
-
const shouldContinue = limit === void 0 || scheduledJob.repeatCount < limit;
|
|
483
|
-
if (shouldContinue) {
|
|
484
|
-
this.scheduleCronJob(scheduleId, scheduledJob);
|
|
485
|
-
} else {
|
|
486
|
-
console.info(`[FileQueue] Schedule ${scheduleId} completed all ${limit} runs`);
|
|
487
|
-
this.scheduledJobs.delete(scheduleId);
|
|
488
|
-
}
|
|
489
|
-
} catch (error) {
|
|
490
|
-
console.error(`[FileQueue] Error executing scheduled job ${scheduleId}:`, error);
|
|
491
|
-
this.scheduleCronJob(scheduleId, scheduledJob);
|
|
492
|
-
}
|
|
493
|
-
}, delay);
|
|
494
|
-
} catch (error) {
|
|
495
|
-
console.error(`[FileQueue] Error parsing cron expression "${cronPattern}":`, error);
|
|
496
|
-
throw new Error(`Invalid cron pattern: ${cronPattern}`);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File Store Adapter
|
|
3
|
-
*
|
|
4
|
-
* File-based storage for development/small deployments
|
|
5
|
-
* - Extends MemoryStoreAdapter with persistence
|
|
6
|
-
* - Fast in-memory access backed by file system
|
|
7
|
-
* - Survives restarts
|
|
8
|
-
* - Single instance only
|
|
9
|
-
*
|
|
10
|
-
* Storage format:
|
|
11
|
-
* - {dataDir}/streams/{subject}.ndjson - Event streams (append-only NDJSON)
|
|
12
|
-
* - {dataDir}/indices/{key}.json - Sorted indices (JSON arrays)
|
|
13
|
-
* - {dataDir}/kv/{key}.json - KV store (individual JSON files)
|
|
14
|
-
*/
|
|
15
|
-
import { MemoryStoreAdapter } from './memory-store.js';
|
|
16
|
-
export interface FileStoreAdapterOptions {
|
|
17
|
-
dataDir: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* File-backed store adapter
|
|
21
|
-
* Extends memory store and persists on every write using same format as existing file adapter
|
|
22
|
-
*/
|
|
23
|
-
export declare class FileStoreAdapter extends MemoryStoreAdapter {
|
|
24
|
-
private options;
|
|
25
|
-
constructor(options: FileStoreAdapterOptions);
|
|
26
|
-
private streamPath;
|
|
27
|
-
private indexPath;
|
|
28
|
-
private kvPath;
|
|
29
|
-
init(): Promise<void>;
|
|
30
|
-
private loadFromDisk;
|
|
31
|
-
close(): Promise<void>;
|
|
32
|
-
}
|