@nicnocquee/dataqueue 1.33.0 → 1.35.0-beta.20260224075710
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/ai/build-docs-content.ts +96 -0
- package/ai/build-llms-full.ts +42 -0
- package/ai/docs-content.json +290 -0
- package/ai/rules/advanced.md +170 -0
- package/ai/rules/basic.md +159 -0
- package/ai/rules/react-dashboard.md +87 -0
- package/ai/skills/dataqueue-advanced/SKILL.md +370 -0
- package/ai/skills/dataqueue-core/SKILL.md +235 -0
- package/ai/skills/dataqueue-react/SKILL.md +201 -0
- package/dist/cli.cjs +577 -32
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +52 -2
- package/dist/cli.d.ts +52 -2
- package/dist/cli.js +575 -32
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +937 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +358 -11
- package/dist/index.d.ts +358 -11
- package/dist/index.js +937 -108
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +186 -0
- package/dist/mcp-server.cjs.map +1 -0
- package/dist/mcp-server.d.cts +32 -0
- package/dist/mcp-server.d.ts +32 -0
- package/dist/mcp-server.js +175 -0
- package/dist/mcp-server.js.map +1 -0
- package/migrations/1781200000005_add_retry_config_to_job_queue.sql +17 -0
- package/migrations/1781200000006_add_output_to_job_queue.sql +3 -0
- package/package.json +10 -4
- package/src/backend.ts +36 -3
- package/src/backends/postgres.ts +344 -42
- package/src/backends/redis-scripts.ts +173 -8
- package/src/backends/redis.test.ts +668 -0
- package/src/backends/redis.ts +244 -15
- package/src/cli.test.ts +65 -0
- package/src/cli.ts +56 -19
- package/src/db-util.ts +1 -1
- package/src/index.test.ts +811 -12
- package/src/index.ts +106 -14
- package/src/install-mcp-command.test.ts +216 -0
- package/src/install-mcp-command.ts +185 -0
- package/src/install-rules-command.test.ts +218 -0
- package/src/install-rules-command.ts +233 -0
- package/src/install-skills-command.test.ts +176 -0
- package/src/install-skills-command.ts +124 -0
- package/src/mcp-server.test.ts +162 -0
- package/src/mcp-server.ts +231 -0
- package/src/processor.ts +133 -49
- package/src/queue.test.ts +477 -0
- package/src/queue.ts +20 -3
- package/src/supervisor.test.ts +340 -0
- package/src/supervisor.ts +177 -0
- package/src/types.ts +318 -3
|
@@ -31,7 +31,8 @@ const SCORE_RANGE = '1000000000000000'; // 1e15
|
|
|
31
31
|
* ADD JOB
|
|
32
32
|
* KEYS: [prefix]
|
|
33
33
|
* ARGV: [jobType, payloadJson, maxAttempts, priority, runAtMs, timeoutMs,
|
|
34
|
-
* forceKillOnTimeout, tagsJson, idempotencyKey, nowMs
|
|
34
|
+
* forceKillOnTimeout, tagsJson, idempotencyKey, nowMs,
|
|
35
|
+
* retryDelay, retryBackoff, retryDelayMax]
|
|
35
36
|
* Returns: job ID (number)
|
|
36
37
|
*/
|
|
37
38
|
export const ADD_JOB_SCRIPT = `
|
|
@@ -46,6 +47,9 @@ local forceKillOnTimeout = ARGV[7]
|
|
|
46
47
|
local tagsJson = ARGV[8] -- "null" or JSON array string
|
|
47
48
|
local idempotencyKey = ARGV[9] -- "null" string if not set
|
|
48
49
|
local nowMs = tonumber(ARGV[10])
|
|
50
|
+
local retryDelay = ARGV[11] -- "null" or seconds string
|
|
51
|
+
local retryBackoff = ARGV[12] -- "null" or "true"/"false"
|
|
52
|
+
local retryDelayMax = ARGV[13] -- "null" or seconds string
|
|
49
53
|
|
|
50
54
|
-- Idempotency check
|
|
51
55
|
if idempotencyKey ~= "null" then
|
|
@@ -89,7 +93,10 @@ redis.call('HMSET', jobKey,
|
|
|
89
93
|
'idempotencyKey', idempotencyKey,
|
|
90
94
|
'waitUntil', 'null',
|
|
91
95
|
'waitTokenId', 'null',
|
|
92
|
-
'stepData', 'null'
|
|
96
|
+
'stepData', 'null',
|
|
97
|
+
'retryDelay', retryDelay,
|
|
98
|
+
'retryBackoff', retryBackoff,
|
|
99
|
+
'retryDelayMax', retryDelayMax
|
|
93
100
|
)
|
|
94
101
|
|
|
95
102
|
-- Status index
|
|
@@ -131,6 +138,129 @@ end
|
|
|
131
138
|
return id
|
|
132
139
|
`;
|
|
133
140
|
|
|
141
|
+
/**
|
|
142
|
+
* ADD JOBS (batch)
|
|
143
|
+
* KEYS: [prefix]
|
|
144
|
+
* ARGV: [jobsJson, nowMs]
|
|
145
|
+
* jobsJson is a JSON array of objects, each with:
|
|
146
|
+
* jobType, payload (already JSON string), maxAttempts, priority,
|
|
147
|
+
* runAtMs, timeoutMs, forceKillOnTimeout, tags (JSON or "null"),
|
|
148
|
+
* idempotencyKey
|
|
149
|
+
* Returns: array of job IDs (one per input job, in order)
|
|
150
|
+
*/
|
|
151
|
+
export const ADD_JOBS_SCRIPT = `
|
|
152
|
+
local prefix = KEYS[1]
|
|
153
|
+
local jobsJson = ARGV[1]
|
|
154
|
+
local nowMs = tonumber(ARGV[2])
|
|
155
|
+
|
|
156
|
+
local jobs = cjson.decode(jobsJson)
|
|
157
|
+
local results = {}
|
|
158
|
+
|
|
159
|
+
for i, job in ipairs(jobs) do
|
|
160
|
+
local jobType = job.jobType
|
|
161
|
+
local payloadJson = job.payload
|
|
162
|
+
local maxAttempts = tonumber(job.maxAttempts)
|
|
163
|
+
local priority = tonumber(job.priority)
|
|
164
|
+
local runAtMs = tostring(job.runAtMs)
|
|
165
|
+
local timeoutMs = tostring(job.timeoutMs)
|
|
166
|
+
local forceKillOnTimeout = tostring(job.forceKillOnTimeout)
|
|
167
|
+
local tagsJson = tostring(job.tags)
|
|
168
|
+
local idempotencyKey = tostring(job.idempotencyKey)
|
|
169
|
+
local retryDelay = tostring(job.retryDelay)
|
|
170
|
+
local retryBackoff = tostring(job.retryBackoff)
|
|
171
|
+
local retryDelayMax = tostring(job.retryDelayMax)
|
|
172
|
+
|
|
173
|
+
-- Idempotency check
|
|
174
|
+
local skip = false
|
|
175
|
+
if idempotencyKey ~= "null" then
|
|
176
|
+
local existing = redis.call('GET', prefix .. 'idempotency:' .. idempotencyKey)
|
|
177
|
+
if existing then
|
|
178
|
+
results[i] = tonumber(existing)
|
|
179
|
+
skip = true
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
if not skip then
|
|
184
|
+
-- Generate ID
|
|
185
|
+
local id = redis.call('INCR', prefix .. 'id_seq')
|
|
186
|
+
local jobKey = prefix .. 'job:' .. id
|
|
187
|
+
local runAt = runAtMs ~= "0" and tonumber(runAtMs) or nowMs
|
|
188
|
+
|
|
189
|
+
-- Store the job hash
|
|
190
|
+
redis.call('HMSET', jobKey,
|
|
191
|
+
'id', id,
|
|
192
|
+
'jobType', jobType,
|
|
193
|
+
'payload', payloadJson,
|
|
194
|
+
'status', 'pending',
|
|
195
|
+
'maxAttempts', maxAttempts,
|
|
196
|
+
'attempts', 0,
|
|
197
|
+
'priority', priority,
|
|
198
|
+
'runAt', runAt,
|
|
199
|
+
'timeoutMs', timeoutMs,
|
|
200
|
+
'forceKillOnTimeout', forceKillOnTimeout,
|
|
201
|
+
'createdAt', nowMs,
|
|
202
|
+
'updatedAt', nowMs,
|
|
203
|
+
'lockedAt', 'null',
|
|
204
|
+
'lockedBy', 'null',
|
|
205
|
+
'nextAttemptAt', 'null',
|
|
206
|
+
'pendingReason', 'null',
|
|
207
|
+
'errorHistory', '[]',
|
|
208
|
+
'failureReason', 'null',
|
|
209
|
+
'completedAt', 'null',
|
|
210
|
+
'startedAt', 'null',
|
|
211
|
+
'lastRetriedAt', 'null',
|
|
212
|
+
'lastFailedAt', 'null',
|
|
213
|
+
'lastCancelledAt', 'null',
|
|
214
|
+
'tags', tagsJson,
|
|
215
|
+
'idempotencyKey', idempotencyKey,
|
|
216
|
+
'waitUntil', 'null',
|
|
217
|
+
'waitTokenId', 'null',
|
|
218
|
+
'stepData', 'null',
|
|
219
|
+
'retryDelay', retryDelay,
|
|
220
|
+
'retryBackoff', retryBackoff,
|
|
221
|
+
'retryDelayMax', retryDelayMax
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
-- Status index
|
|
225
|
+
redis.call('SADD', prefix .. 'status:pending', id)
|
|
226
|
+
|
|
227
|
+
-- Type index
|
|
228
|
+
redis.call('SADD', prefix .. 'type:' .. jobType, id)
|
|
229
|
+
|
|
230
|
+
-- Tag indexes
|
|
231
|
+
if tagsJson ~= "null" then
|
|
232
|
+
local tags = cjson.decode(tagsJson)
|
|
233
|
+
for _, tag in ipairs(tags) do
|
|
234
|
+
redis.call('SADD', prefix .. 'tag:' .. tag, id)
|
|
235
|
+
end
|
|
236
|
+
for _, tag in ipairs(tags) do
|
|
237
|
+
redis.call('SADD', prefix .. 'job:' .. id .. ':tags', tag)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
-- Idempotency mapping
|
|
242
|
+
if idempotencyKey ~= "null" then
|
|
243
|
+
redis.call('SET', prefix .. 'idempotency:' .. idempotencyKey, id)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
-- All-jobs sorted set
|
|
247
|
+
redis.call('ZADD', prefix .. 'all', nowMs, id)
|
|
248
|
+
|
|
249
|
+
-- Queue or delayed
|
|
250
|
+
if runAt <= nowMs then
|
|
251
|
+
local score = priority * ${SCORE_RANGE} + (${SCORE_RANGE} - nowMs)
|
|
252
|
+
redis.call('ZADD', prefix .. 'queue', score, id)
|
|
253
|
+
else
|
|
254
|
+
redis.call('ZADD', prefix .. 'delayed', runAt, id)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
results[i] = id
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
return results
|
|
262
|
+
`;
|
|
263
|
+
|
|
134
264
|
/**
|
|
135
265
|
* GET NEXT BATCH
|
|
136
266
|
* Atomically: move ready delayed/retry jobs into queue, then pop N jobs.
|
|
@@ -291,22 +421,30 @@ return results
|
|
|
291
421
|
/**
|
|
292
422
|
* COMPLETE JOB
|
|
293
423
|
* KEYS: [prefix]
|
|
294
|
-
* ARGV: [jobId, nowMs]
|
|
424
|
+
* ARGV: [jobId, nowMs, outputJson]
|
|
295
425
|
*/
|
|
296
426
|
export const COMPLETE_JOB_SCRIPT = `
|
|
297
427
|
local prefix = KEYS[1]
|
|
298
428
|
local jobId = ARGV[1]
|
|
299
429
|
local nowMs = ARGV[2]
|
|
430
|
+
local outputJson = ARGV[3]
|
|
300
431
|
local jk = prefix .. 'job:' .. jobId
|
|
301
432
|
|
|
302
|
-
|
|
433
|
+
local fields = {
|
|
303
434
|
'status', 'completed',
|
|
304
435
|
'updatedAt', nowMs,
|
|
305
436
|
'completedAt', nowMs,
|
|
306
437
|
'stepData', 'null',
|
|
307
438
|
'waitUntil', 'null',
|
|
308
439
|
'waitTokenId', 'null'
|
|
309
|
-
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if outputJson ~= '__NONE__' then
|
|
443
|
+
fields[#fields + 1] = 'output'
|
|
444
|
+
fields[#fields + 1] = outputJson
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
redis.call('HMSET', jk, unpack(fields))
|
|
310
448
|
redis.call('SREM', prefix .. 'status:processing', jobId)
|
|
311
449
|
redis.call('SADD', prefix .. 'status:completed', jobId)
|
|
312
450
|
|
|
@@ -330,11 +468,38 @@ local jk = prefix .. 'job:' .. jobId
|
|
|
330
468
|
local attempts = tonumber(redis.call('HGET', jk, 'attempts'))
|
|
331
469
|
local maxAttempts = tonumber(redis.call('HGET', jk, 'maxAttempts'))
|
|
332
470
|
|
|
333
|
-
--
|
|
471
|
+
-- Read per-job retry config (may be "null")
|
|
472
|
+
local rdRaw = redis.call('HGET', jk, 'retryDelay')
|
|
473
|
+
local rbRaw = redis.call('HGET', jk, 'retryBackoff')
|
|
474
|
+
local rmRaw = redis.call('HGET', jk, 'retryDelayMax')
|
|
475
|
+
|
|
334
476
|
local nextAttemptAt = 'null'
|
|
335
477
|
if attempts < maxAttempts then
|
|
336
|
-
local
|
|
337
|
-
|
|
478
|
+
local allNull = (rdRaw == 'null' or rdRaw == false)
|
|
479
|
+
and (rbRaw == 'null' or rbRaw == false)
|
|
480
|
+
and (rmRaw == 'null' or rmRaw == false)
|
|
481
|
+
if allNull then
|
|
482
|
+
-- Legacy formula: 2^attempts minutes
|
|
483
|
+
local delayMs = math.pow(2, attempts) * 60000
|
|
484
|
+
nextAttemptAt = nowMs + delayMs
|
|
485
|
+
else
|
|
486
|
+
local retryDelaySec = 60
|
|
487
|
+
if rdRaw and rdRaw ~= 'null' then retryDelaySec = tonumber(rdRaw) end
|
|
488
|
+
local useBackoff = true
|
|
489
|
+
if rbRaw and rbRaw ~= 'null' then useBackoff = (rbRaw == 'true') end
|
|
490
|
+
local maxDelaySec = nil
|
|
491
|
+
if rmRaw and rmRaw ~= 'null' then maxDelaySec = tonumber(rmRaw) end
|
|
492
|
+
|
|
493
|
+
local delaySec
|
|
494
|
+
if useBackoff then
|
|
495
|
+
delaySec = retryDelaySec * math.pow(2, attempts)
|
|
496
|
+
if maxDelaySec then delaySec = math.min(delaySec, maxDelaySec) end
|
|
497
|
+
delaySec = delaySec * (0.5 + 0.5 * math.random())
|
|
498
|
+
else
|
|
499
|
+
delaySec = retryDelaySec
|
|
500
|
+
end
|
|
501
|
+
nextAttemptAt = nowMs + math.floor(delaySec * 1000)
|
|
502
|
+
end
|
|
338
503
|
end
|
|
339
504
|
|
|
340
505
|
-- Append to error_history
|