semola 0.6.0 → 0.6.1
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/README.md +17 -5
- package/dist/lib/api/index.cjs +1 -537
- package/dist/lib/api/index.d.cts +1 -271
- package/dist/lib/api/index.d.mts +80 -84
- package/dist/lib/api/index.mjs +1 -535
- package/dist/lib/cache/index.cjs +1 -99
- package/dist/lib/cache/index.d.cts +1 -27
- package/dist/lib/cache/index.mjs +1 -98
- package/dist/lib/cron/index.cjs +1 -735
- package/dist/lib/cron/index.d.cts +1 -146
- package/dist/lib/cron/index.d.mts +99 -36
- package/dist/lib/cron/index.mjs +1 -726
- package/dist/lib/errors/index.cjs +1 -30
- package/dist/lib/errors/index.d.cts +1 -13
- package/dist/lib/errors/index.mjs +1 -26
- package/dist/lib/i18n/index.cjs +1 -42
- package/dist/lib/i18n/index.d.cts +1 -28
- package/dist/lib/i18n/index.mjs +1 -41
- package/dist/lib/logging/index.cjs +1 -388
- package/dist/lib/logging/index.d.cts +1 -108
- package/dist/lib/logging/index.mjs +1 -374
- package/dist/lib/orm/index.cjs +1 -1642
- package/dist/lib/orm/index.d.cts +1 -402
- package/dist/lib/orm/index.d.mts +4 -2
- package/dist/lib/orm/index.mjs +1 -1630
- package/dist/lib/policy/index.cjs +1 -285
- package/dist/lib/policy/index.d.cts +1 -77
- package/dist/lib/policy/index.mjs +1 -265
- package/dist/lib/prompts/index.cjs +6 -769
- package/dist/lib/prompts/index.d.cts +1 -75
- package/dist/lib/prompts/index.mjs +6 -762
- package/dist/lib/pubsub/index.cjs +1 -141
- package/dist/lib/pubsub/index.d.cts +1 -26
- package/dist/lib/pubsub/index.mjs +1 -140
- package/dist/lib/queue/index.cjs +1 -212
- package/dist/lib/queue/index.d.cts +1 -81
- package/dist/lib/queue/index.mjs +1 -209
- package/dist/lib/workflow/index.cjs +1 -537
- package/dist/lib/workflow/index.d.cts +1 -150
- package/dist/lib/workflow/index.mjs +1 -527
- package/dist/rolldown-runtime-CMqjfN_6.cjs +1 -0
- package/package.json +7 -5
- package/dist/chunk-CKQMccvm.cjs +0 -28
package/dist/lib/queue/index.mjs
CHANGED
|
@@ -1,209 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import assert from "node:assert";
|
|
3
|
-
//#region src/lib/queue/errors.ts
|
|
4
|
-
var SerializationError = class extends Error {
|
|
5
|
-
constructor(message) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.name = "SerializationError";
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
var EnqueueError = class extends Error {
|
|
11
|
-
constructor(message) {
|
|
12
|
-
super(message);
|
|
13
|
-
this.name = "EnqueueError";
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region src/lib/queue/index.ts
|
|
18
|
-
const toMinimalJob = (jobState) => ({
|
|
19
|
-
id: jobState.id,
|
|
20
|
-
data: jobState.data,
|
|
21
|
-
attempts: jobState.attempts,
|
|
22
|
-
maxRetries: jobState.maxRetries,
|
|
23
|
-
createdAt: jobState.createdAt
|
|
24
|
-
});
|
|
25
|
-
const DEFAULT_RETRIES = 3;
|
|
26
|
-
const DEFAULT_TIMEOUT = 3e4;
|
|
27
|
-
const DEFAULT_CONCURRENCY = 1;
|
|
28
|
-
const DEFAULT_POLL_INTERVAL = 100;
|
|
29
|
-
const DEFAULT_RETRY_MAX_DELAY = 6e4;
|
|
30
|
-
const DEFAULT_RETRY_BASE_DELAY = 1e3;
|
|
31
|
-
const DEFAULT_RETRY_MULTIPLIER = 2;
|
|
32
|
-
const SHUTDOWN_POLL_INTERVAL = 10;
|
|
33
|
-
var Queue = class {
|
|
34
|
-
options;
|
|
35
|
-
running = true;
|
|
36
|
-
activeWorkers = 0;
|
|
37
|
-
retries;
|
|
38
|
-
timeout;
|
|
39
|
-
concurrency;
|
|
40
|
-
pollInterval;
|
|
41
|
-
retryBaseDelay;
|
|
42
|
-
retryMultiplier;
|
|
43
|
-
retryMaxDelay;
|
|
44
|
-
constructor(options) {
|
|
45
|
-
this.options = options;
|
|
46
|
-
this.retries = options.retries ?? DEFAULT_RETRIES;
|
|
47
|
-
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
48
|
-
this.concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
49
|
-
this.pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
50
|
-
this.retryBaseDelay = options.retryBackoff?.baseDelay ?? DEFAULT_RETRY_BASE_DELAY;
|
|
51
|
-
this.retryMultiplier = options.retryBackoff?.multiplier ?? DEFAULT_RETRY_MULTIPLIER;
|
|
52
|
-
this.retryMaxDelay = options.retryBackoff?.maxDelay ?? DEFAULT_RETRY_MAX_DELAY;
|
|
53
|
-
assert.ok(Number.isFinite(this.retryBaseDelay) && this.retryBaseDelay > 0, "Invalid retryBackoff.baseDelay: must be a positive finite number");
|
|
54
|
-
assert.ok(Number.isFinite(this.retryMultiplier) && this.retryMultiplier > 0, "Invalid retryBackoff.multiplier: must be a positive finite number");
|
|
55
|
-
assert.ok(Number.isFinite(this.retryMaxDelay) && this.retryMaxDelay > 0, "Invalid retryBackoff.maxDelay: must be a positive finite number");
|
|
56
|
-
this.startWorkers();
|
|
57
|
-
}
|
|
58
|
-
computeBackoffDelay(attempt) {
|
|
59
|
-
return Math.min(this.retryBaseDelay * this.retryMultiplier ** (attempt - 1), this.retryMaxDelay);
|
|
60
|
-
}
|
|
61
|
-
async enqueue(data) {
|
|
62
|
-
const job = {
|
|
63
|
-
id: crypto.randomUUID(),
|
|
64
|
-
data,
|
|
65
|
-
attempts: 0,
|
|
66
|
-
maxRetries: this.retries,
|
|
67
|
-
createdAt: Date.now()
|
|
68
|
-
};
|
|
69
|
-
const [serializeError, serialized] = this.serializeJob(job);
|
|
70
|
-
if (serializeError || !serialized) throw new SerializationError("Unable to serialize job data");
|
|
71
|
-
const [enqueueError] = await this.enqueueJobData(serialized);
|
|
72
|
-
if (enqueueError) throw new EnqueueError("Unable to enqueue job");
|
|
73
|
-
return job.id;
|
|
74
|
-
}
|
|
75
|
-
async stop() {
|
|
76
|
-
this.running = false;
|
|
77
|
-
while (this.activeWorkers > 0) await new Promise((resolve) => setTimeout(resolve, SHUTDOWN_POLL_INTERVAL));
|
|
78
|
-
}
|
|
79
|
-
waitForPollInterval() {
|
|
80
|
-
return new Promise((resolve) => setTimeout(resolve, this.pollInterval));
|
|
81
|
-
}
|
|
82
|
-
serializeJob(job) {
|
|
83
|
-
return mightThrowSync(() => JSON.stringify(job));
|
|
84
|
-
}
|
|
85
|
-
async enqueueJobData(serialized) {
|
|
86
|
-
const queueKey = `queue:${this.options.name}:jobs`;
|
|
87
|
-
return mightThrow(this.options.redis.lpush(queueKey, serialized));
|
|
88
|
-
}
|
|
89
|
-
async moveToDeadLetterQueue(jobData, parseError) {
|
|
90
|
-
const deadLetterKey = `queue:${this.options.name}:dead-letter`;
|
|
91
|
-
const deadLetterEntry = JSON.stringify({
|
|
92
|
-
jobData,
|
|
93
|
-
parseError: this.formatErrorMessage(parseError),
|
|
94
|
-
timestamp: Date.now()
|
|
95
|
-
});
|
|
96
|
-
return mightThrow(this.options.redis.lpush(deadLetterKey, deadLetterEntry));
|
|
97
|
-
}
|
|
98
|
-
formatErrorMessage(error) {
|
|
99
|
-
if (!error) return "Unknown error";
|
|
100
|
-
return error.message;
|
|
101
|
-
}
|
|
102
|
-
startWorkers() {
|
|
103
|
-
for (let i = 0; i < this.concurrency; i++) this.processJobs();
|
|
104
|
-
}
|
|
105
|
-
async processJobs() {
|
|
106
|
-
while (this.running) {
|
|
107
|
-
const queueKey = `queue:${this.options.name}:jobs`;
|
|
108
|
-
const [popError, jobData] = await mightThrow(this.options.redis.rpop(queueKey));
|
|
109
|
-
if (popError) {
|
|
110
|
-
await this.waitForPollInterval();
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (!jobData) {
|
|
114
|
-
await this.waitForPollInterval();
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const [parseError, job] = mightThrowSync(() => JSON.parse(jobData));
|
|
118
|
-
if (parseError || !job) {
|
|
119
|
-
await this.callOnErrorForParseFailure(jobData, parseError);
|
|
120
|
-
await this.moveToDeadLetterQueue(jobData, parseError);
|
|
121
|
-
await this.waitForPollInterval();
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (!this.running) {
|
|
125
|
-
const [stringifyError, serialized] = this.serializeJob(job);
|
|
126
|
-
if (!stringifyError && serialized) await mightThrow(this.options.redis.lpush(queueKey, serialized));
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
this.activeWorkers++;
|
|
130
|
-
await mightThrow(this.handleJob(job));
|
|
131
|
-
this.activeWorkers--;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async handleJob(job) {
|
|
135
|
-
const controller = new AbortController();
|
|
136
|
-
const handlerPromise = Promise.resolve().then(() => this.options.handler(job.data, controller.signal));
|
|
137
|
-
let timerId;
|
|
138
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
139
|
-
timerId = setTimeout(() => {
|
|
140
|
-
reject(/* @__PURE__ */ new Error(`Job timeout after ${this.timeout}ms`));
|
|
141
|
-
}, this.timeout);
|
|
142
|
-
});
|
|
143
|
-
const [handlerError] = await mightThrow(Promise.race([handlerPromise.then(() => void 0), timeoutPromise]));
|
|
144
|
-
if (timerId) clearTimeout(timerId);
|
|
145
|
-
if (handlerError && !controller.signal.aborted) controller.abort();
|
|
146
|
-
if (!handlerError) {
|
|
147
|
-
if (this.options.onSuccess) await mightThrow(Promise.resolve(this.options.onSuccess(toMinimalJob(job))));
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
job.attempts++;
|
|
151
|
-
const errorMsg = this.formatErrorMessage(handlerError);
|
|
152
|
-
job.error = errorMsg;
|
|
153
|
-
if (!job.errorHistory) job.errorHistory = [];
|
|
154
|
-
job.errorHistory.push({
|
|
155
|
-
attempt: job.attempts,
|
|
156
|
-
error: errorMsg,
|
|
157
|
-
timestamp: Date.now()
|
|
158
|
-
});
|
|
159
|
-
if (job.attempts <= job.maxRetries) {
|
|
160
|
-
if (this.options.onRetry) {
|
|
161
|
-
const delay = this.computeBackoffDelay(job.attempts);
|
|
162
|
-
await mightThrow(Promise.resolve(this.options.onRetry({
|
|
163
|
-
job: toMinimalJob(job),
|
|
164
|
-
error: errorMsg,
|
|
165
|
-
nextRetryDelayMs: delay,
|
|
166
|
-
retriesRemaining: job.maxRetries - job.attempts,
|
|
167
|
-
backoffMultiplier: this.retryMultiplier
|
|
168
|
-
})));
|
|
169
|
-
}
|
|
170
|
-
await this.retryJob(job);
|
|
171
|
-
} else await this.callOnError(job);
|
|
172
|
-
}
|
|
173
|
-
async callOnError(job) {
|
|
174
|
-
if (!this.options.onError) return;
|
|
175
|
-
await mightThrow(Promise.resolve(this.options.onError({
|
|
176
|
-
job: toMinimalJob(job),
|
|
177
|
-
lastError: job.error ?? "",
|
|
178
|
-
totalDurationMs: Date.now() - job.createdAt,
|
|
179
|
-
totalAttempts: job.attempts,
|
|
180
|
-
errorHistory: job.errorHistory ?? []
|
|
181
|
-
})));
|
|
182
|
-
}
|
|
183
|
-
async callOnErrorForParseFailure(jobData, parseError) {
|
|
184
|
-
if (!this.options.onParseError) return;
|
|
185
|
-
await mightThrow(Promise.resolve(this.options.onParseError({
|
|
186
|
-
rawJobData: jobData,
|
|
187
|
-
parseError: this.formatErrorMessage(parseError),
|
|
188
|
-
timestamp: Date.now()
|
|
189
|
-
})));
|
|
190
|
-
}
|
|
191
|
-
async retryJob(job) {
|
|
192
|
-
const delay = this.computeBackoffDelay(job.attempts);
|
|
193
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
194
|
-
const [stringifyError, serialized] = this.serializeJob(job);
|
|
195
|
-
if (stringifyError || !serialized) {
|
|
196
|
-
job.error = `Failed to serialize job for retry: ${this.formatErrorMessage(stringifyError)}`;
|
|
197
|
-
await this.callOnError(job);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const queueKey = `queue:${this.options.name}:jobs`;
|
|
201
|
-
const [pushError] = await mightThrow(this.options.redis.lpush(queueKey, serialized));
|
|
202
|
-
if (pushError) {
|
|
203
|
-
job.error = `Failed to re-enqueue job for retry: ${this.formatErrorMessage(pushError)}`;
|
|
204
|
-
await this.callOnError(job);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
//#endregion
|
|
209
|
-
export { Queue };
|
|
1
|
+
import{mightThrow as e,mightThrowSync as t}from"../errors/index.mjs";import n from"node:assert";var r=class extends Error{constructor(e){super(e),this.name=`SerializationError`}},i=class extends Error{constructor(e){super(e),this.name=`EnqueueError`}};const a=e=>({id:e.id,data:e.data,attempts:e.attempts,maxRetries:e.maxRetries,createdAt:e.createdAt});var o=class{options;running=!0;activeWorkers=0;retries;timeout;concurrency;pollInterval;retryBaseDelay;retryMultiplier;retryMaxDelay;constructor(e){this.options=e,this.retries=e.retries??3,this.timeout=e.timeout??3e4,this.concurrency=e.concurrency??1,this.pollInterval=e.pollInterval??100,this.retryBaseDelay=e.retryBackoff?.baseDelay??1e3,this.retryMultiplier=e.retryBackoff?.multiplier??2,this.retryMaxDelay=e.retryBackoff?.maxDelay??6e4,n.ok(Number.isFinite(this.retryBaseDelay)&&this.retryBaseDelay>0,`Invalid retryBackoff.baseDelay: must be a positive finite number`),n.ok(Number.isFinite(this.retryMultiplier)&&this.retryMultiplier>0,`Invalid retryBackoff.multiplier: must be a positive finite number`),n.ok(Number.isFinite(this.retryMaxDelay)&&this.retryMaxDelay>0,`Invalid retryBackoff.maxDelay: must be a positive finite number`),this.startWorkers()}computeBackoffDelay(e){return Math.min(this.retryBaseDelay*this.retryMultiplier**(e-1),this.retryMaxDelay)}async enqueue(e){let t={id:crypto.randomUUID(),data:e,attempts:0,maxRetries:this.retries,createdAt:Date.now()},[n,a]=this.serializeJob(t);if(n||!a)throw new r(`Unable to serialize job data`);let[o]=await this.enqueueJobData(a);if(o)throw new i(`Unable to enqueue job`);return t.id}async stop(){for(this.running=!1;this.activeWorkers>0;)await new Promise(e=>setTimeout(e,10))}waitForPollInterval(){return new Promise(e=>setTimeout(e,this.pollInterval))}serializeJob(e){return t(()=>JSON.stringify(e))}async enqueueJobData(t){let n=`queue:${this.options.name}:jobs`;return e(this.options.redis.lpush(n,t))}async moveToDeadLetterQueue(t,n){let r=`queue:${this.options.name}:dead-letter`,i=JSON.stringify({jobData:t,parseError:this.formatErrorMessage(n),timestamp:Date.now()});return e(this.options.redis.lpush(r,i))}formatErrorMessage(e){return e?e.message:`Unknown error`}startWorkers(){for(let e=0;e<this.concurrency;e++)this.processJobs()}async processJobs(){for(;this.running;){let n=`queue:${this.options.name}:jobs`,[r,i]=await e(this.options.redis.rpop(n));if(r){await this.waitForPollInterval();continue}if(!i){await this.waitForPollInterval();continue}let[a,o]=t(()=>JSON.parse(i));if(a||!o){await this.callOnErrorForParseFailure(i,a),await this.moveToDeadLetterQueue(i,a),await this.waitForPollInterval();continue}if(!this.running){let[t,r]=this.serializeJob(o);!t&&r&&await e(this.options.redis.lpush(n,r));break}this.activeWorkers++,await e(this.handleJob(o)),this.activeWorkers--}}async handleJob(t){let n=new AbortController,r=Promise.resolve().then(()=>this.options.handler(t.data,n.signal)),i,o=new Promise((e,t)=>{i=setTimeout(()=>{t(Error(`Job timeout after ${this.timeout}ms`))},this.timeout)}),[s]=await e(Promise.race([r.then(()=>void 0),o]));if(i&&clearTimeout(i),s&&!n.signal.aborted&&n.abort(),!s){this.options.onSuccess&&await e(Promise.resolve(this.options.onSuccess(a(t))));return}t.attempts++;let c=this.formatErrorMessage(s);if(t.error=c,t.errorHistory||=[],t.errorHistory.push({attempt:t.attempts,error:c,timestamp:Date.now()}),t.attempts<=t.maxRetries){if(this.options.onRetry){let n=this.computeBackoffDelay(t.attempts);await e(Promise.resolve(this.options.onRetry({job:a(t),error:c,nextRetryDelayMs:n,retriesRemaining:t.maxRetries-t.attempts,backoffMultiplier:this.retryMultiplier})))}await this.retryJob(t)}else await this.callOnError(t)}async callOnError(t){this.options.onError&&await e(Promise.resolve(this.options.onError({job:a(t),lastError:t.error??``,totalDurationMs:Date.now()-t.createdAt,totalAttempts:t.attempts,errorHistory:t.errorHistory??[]})))}async callOnErrorForParseFailure(t,n){this.options.onParseError&&await e(Promise.resolve(this.options.onParseError({rawJobData:t,parseError:this.formatErrorMessage(n),timestamp:Date.now()})))}async retryJob(t){let n=this.computeBackoffDelay(t.attempts);await new Promise(e=>setTimeout(e,n));let[r,i]=this.serializeJob(t);if(r||!i){t.error=`Failed to serialize job for retry: ${this.formatErrorMessage(r)}`,await this.callOnError(t);return}let a=`queue:${this.options.name}:jobs`,[o]=await e(this.options.redis.lpush(a,i));o&&(t.error=`Failed to re-enqueue job for retry: ${this.formatErrorMessage(o)}`,await this.callOnError(t))}};export{o as Queue};
|