async-lube 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +1 -1
- package/docs/http.md +1 -1
- package/docs/reference.md +4 -2
- package/docs/tasks.md +19 -2
- package/package.json +4 -5
- package/public.d.ts +112 -0
- package/src/attempt.js +17 -7
- package/src/durable/context.js +107 -64
- package/src/durable/error.js +39 -15
- package/src/durable/index.js +18 -10
- package/src/durable/journal.js +29 -14
- package/src/durable/json.js +26 -9
- package/src/durable/lease.js +15 -5
- package/src/durable/persist.js +60 -22
- package/src/durable/run.js +103 -41
- package/src/durable/send.js +30 -13
- package/src/durable/serve.js +30 -24
- package/src/durable/state.js +28 -13
- package/src/durable/store.js +19 -10
- package/src/durable/watch.js +31 -11
- package/src/errors.js +9 -3
- package/src/flow/attempts.js +49 -29
- package/src/flow/builder.js +260 -161
- package/src/flow/compile.js +86 -66
- package/src/flow/graph.js +108 -42
- package/src/flow/index.js +13 -7
- package/src/flow/items.js +79 -46
- package/src/flow/nodes.js +267 -117
- package/src/flow/refs.js +27 -13
- package/src/flow/resources.js +9 -3
- package/src/flow/run.js +287 -114
- package/src/flow/streams.js +44 -19
- package/src/flow/subflows.js +93 -53
- package/src/http/config.js +11 -5
- package/src/http/index.js +25 -14
- package/src/http/refresh.js +29 -12
- package/src/http/request.js +246 -151
- package/src/http/response.js +105 -50
- package/src/http/retry.js +52 -27
- package/src/http/socket.js +220 -172
- package/src/http/sse.js +113 -74
- package/src/http/throttle.js +9 -4
- package/src/http/url.js +58 -35
- package/src/http/xhr.js +29 -24
- package/src/index.js +1 -0
- package/src/key.js +3 -5
- package/src/limiter.js +146 -65
- package/src/offload.js +116 -85
- package/src/offload_module.js +594 -0
- package/src/options.js +8 -4
- package/src/redial.js +129 -0
- package/src/signal.js +14 -4
- package/src/stream.js +353 -198
- package/types/attempt.d.ts.map +1 -1
- package/types/durable/index.d.ts.map +1 -1
- package/types/durable/state.d.ts.map +1 -1
- package/types/durable/store.d.ts.map +1 -1
- package/types/errors.d.ts.map +1 -1
- package/types/http/index.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/limiter.d.ts +5 -1
- package/types/limiter.d.ts.map +1 -1
- package/types/offload.d.ts +2 -16
- package/types/offload.d.ts.map +1 -1
- package/types/redial.d.ts +28 -0
- package/types/redial.d.ts.map +1 -0
- package/types/stream.d.ts +2 -0
- package/types/stream.d.ts.map +1 -1
package/src/durable/run.js
CHANGED
|
@@ -43,7 +43,9 @@ export async function cancel(d, key, reason) {
|
|
|
43
43
|
}
|
|
44
44
|
for (;;) {
|
|
45
45
|
const saved = await load(d.store, key)
|
|
46
|
-
if (!saved || saved.status == "cancelled" || saved.status == "done")
|
|
46
|
+
if (!saved || saved.status == "cancelled" || saved.status == "done") {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
47
49
|
const next = snapshot(saved)
|
|
48
50
|
delete next.inbox
|
|
49
51
|
next.error = saved_error(error)
|
|
@@ -65,18 +67,26 @@ export async function cancel(d, key, reason) {
|
|
|
65
67
|
export async function enter(d, key, input, mode) {
|
|
66
68
|
if (mode != "join") {
|
|
67
69
|
const problem = json_problem(input)
|
|
68
|
-
if (problem)
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
if (problem) {
|
|
71
|
+
throw TypeError(
|
|
72
|
+
`The input of run "${key}" is not a JSON value: ${problem}`
|
|
73
|
+
)
|
|
74
|
+
}
|
|
71
75
|
}
|
|
72
76
|
for (;;) {
|
|
73
77
|
check_halted(d)
|
|
74
78
|
const running = d.live.get(key)
|
|
75
|
-
if (running)
|
|
79
|
+
if (running) {
|
|
80
|
+
return mode == "start" ? false : follow(d, key, running.outcome)
|
|
81
|
+
}
|
|
76
82
|
const saved = await load(d.store, key)
|
|
77
|
-
if (d.live.has(key))
|
|
83
|
+
if (d.live.has(key)) {
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
78
86
|
if (!saved) {
|
|
79
|
-
if (mode == "join")
|
|
87
|
+
if (mode == "join") {
|
|
88
|
+
throw Error(`There is no run "${key}"`)
|
|
89
|
+
}
|
|
80
90
|
/** @type {SavedRun} */
|
|
81
91
|
const created = {
|
|
82
92
|
input: copy(input),
|
|
@@ -87,15 +97,25 @@ export async function enter(d, key, input, mode) {
|
|
|
87
97
|
status: "running",
|
|
88
98
|
version: 1
|
|
89
99
|
}
|
|
90
|
-
if (await d.store.put(key, created, void 0))
|
|
100
|
+
if (await d.store.put(key, created, void 0)) {
|
|
101
|
+
return launch(d, key, created, mode)
|
|
102
|
+
}
|
|
91
103
|
continue
|
|
92
104
|
}
|
|
93
|
-
if (mode == "start" && saved.status != "pending")
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
if (mode == "start" && saved.status != "pending") {
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
if (saved.status == "done") {
|
|
109
|
+
return saved.result
|
|
110
|
+
}
|
|
111
|
+
if (saved.status == "cancelled" || mode == "join" && saved.status == "failed") {
|
|
112
|
+
throw error_of(saved)
|
|
113
|
+
}
|
|
96
114
|
if (mode != "join" && (saved.status == "failed" || saved.status == "pending") || is_due(d, saved)) {
|
|
97
115
|
const claimed = await claim(d, key, saved, input)
|
|
98
|
-
if (!claimed)
|
|
116
|
+
if (!claimed) {
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
99
119
|
return launch(d, key, claimed, mode)
|
|
100
120
|
}
|
|
101
121
|
return outcome_of(d, key)
|
|
@@ -110,7 +130,8 @@ export async function enter(d, key, input, mode) {
|
|
|
110
130
|
function execute(d, key, saved) {
|
|
111
131
|
/** @type {(reason: typeof LOST | typeof SUSPENDED | { cancel: unknown }) => void} */
|
|
112
132
|
let interrupt = noop
|
|
113
|
-
|
|
133
|
+
/** @type {Promise<typeof LOST | typeof SUSPENDED | { cancel: unknown }>} */
|
|
134
|
+
const interrupted = new Promise(resolve => interrupt = resolve)
|
|
114
135
|
/** @type {() => void} */
|
|
115
136
|
let leave = noop
|
|
116
137
|
/** @type {Promise<void>} */
|
|
@@ -147,17 +168,22 @@ function execute(d, key, saved) {
|
|
|
147
168
|
d.live.set(key, execution)
|
|
148
169
|
const renew = setInterval(
|
|
149
170
|
() => {
|
|
150
|
-
if (!execution.writing)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
if (!execution.writing) {
|
|
172
|
+
void persist(d, execution)
|
|
173
|
+
} else if (expiring(d, execution)) {
|
|
174
|
+
lose(
|
|
175
|
+
execution,
|
|
176
|
+
store_error(execution.fault)
|
|
177
|
+
)
|
|
178
|
+
}
|
|
155
179
|
},
|
|
156
180
|
d.renew_ms
|
|
157
181
|
)
|
|
158
182
|
execution.outcome = (async () => {
|
|
159
183
|
try {
|
|
160
|
-
if (saved.status == "failed")
|
|
184
|
+
if (saved.status == "failed") {
|
|
185
|
+
throw error_of(saved)
|
|
186
|
+
}
|
|
161
187
|
const result = await Promise.race(
|
|
162
188
|
[
|
|
163
189
|
Promise.resolve()
|
|
@@ -171,42 +197,60 @@ function execute(d, key, saved) {
|
|
|
171
197
|
interrupted
|
|
172
198
|
]
|
|
173
199
|
)
|
|
174
|
-
if (result == LOST || result == SUSPENDED)
|
|
175
|
-
|
|
200
|
+
if (result == LOST || result == SUSPENDED) {
|
|
201
|
+
return DETACHED
|
|
202
|
+
}
|
|
203
|
+
if ("cancel" in result) {
|
|
204
|
+
throw result.cancel
|
|
205
|
+
}
|
|
176
206
|
const problem = json_problem(result.value)
|
|
177
|
-
if (problem)
|
|
178
|
-
|
|
179
|
-
|
|
207
|
+
if (problem) {
|
|
208
|
+
throw TypeError(
|
|
209
|
+
`The result of run "${key}" is not a JSON value: ${problem}`
|
|
210
|
+
)
|
|
211
|
+
}
|
|
180
212
|
execution.finished = true
|
|
181
213
|
saved.result = result.value
|
|
182
214
|
saved.status = "done"
|
|
183
215
|
unclaim(saved)
|
|
184
|
-
if (!await save(d, execution))
|
|
216
|
+
if (!await save(d, execution)) {
|
|
217
|
+
return DETACHED
|
|
218
|
+
}
|
|
185
219
|
return { value: result.value }
|
|
186
220
|
} catch (error) {
|
|
187
|
-
if (execution.lost)
|
|
221
|
+
if (execution.lost) {
|
|
222
|
+
return DETACHED
|
|
223
|
+
}
|
|
188
224
|
execution.finished = true
|
|
189
225
|
if (saved.status != "cancelled") {
|
|
190
226
|
rewind(execution, error)
|
|
191
227
|
saved.error = saved_error(error)
|
|
192
228
|
saved.status = "failed"
|
|
193
229
|
unclaim(saved)
|
|
194
|
-
if (!await save(d, execution))
|
|
230
|
+
if (!await save(d, execution)) {
|
|
231
|
+
return DETACHED
|
|
232
|
+
}
|
|
195
233
|
}
|
|
196
234
|
return { error }
|
|
197
235
|
} finally {
|
|
198
236
|
clearInterval(renew)
|
|
199
237
|
clearInterval(execution.inbox_timer)
|
|
200
238
|
for (const list of execution.waiters.values()) {
|
|
201
|
-
for (const waiter of list)
|
|
239
|
+
for (const waiter of list) {
|
|
240
|
+
waiter.clear()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (d.live.get(key) == execution) {
|
|
244
|
+
d.live.delete(key)
|
|
202
245
|
}
|
|
203
|
-
if (d.live.get(key) == execution) d.live.delete(key)
|
|
204
246
|
leave()
|
|
205
247
|
}
|
|
206
248
|
})()
|
|
207
249
|
.then(
|
|
208
250
|
outcome => {
|
|
209
|
-
if (!("detached" in outcome))
|
|
251
|
+
if (!("detached" in outcome)) {
|
|
252
|
+
notify(d, key, outcome)
|
|
253
|
+
}
|
|
210
254
|
return outcome
|
|
211
255
|
}
|
|
212
256
|
)
|
|
@@ -220,8 +264,12 @@ function execute(d, key, saved) {
|
|
|
220
264
|
*/
|
|
221
265
|
async function follow(d, key, outcome) {
|
|
222
266
|
const settled = await outcome
|
|
223
|
-
if ("detached" in settled)
|
|
224
|
-
|
|
267
|
+
if ("detached" in settled) {
|
|
268
|
+
return outcome_of(d, key)
|
|
269
|
+
}
|
|
270
|
+
if ("error" in settled) {
|
|
271
|
+
throw settled.error
|
|
272
|
+
}
|
|
225
273
|
return settled.value
|
|
226
274
|
}
|
|
227
275
|
/**
|
|
@@ -233,7 +281,9 @@ async function follow(d, key, outcome) {
|
|
|
233
281
|
*/
|
|
234
282
|
function launch(d, key, saved, mode) {
|
|
235
283
|
const outcome = execute(d, key, saved)
|
|
236
|
-
if (mode != "start")
|
|
284
|
+
if (mode != "start") {
|
|
285
|
+
return follow(d, key, outcome)
|
|
286
|
+
}
|
|
237
287
|
void outcome.then(
|
|
238
288
|
settled => report(d, key, saved, settled)
|
|
239
289
|
)
|
|
@@ -247,8 +297,12 @@ function launch(d, key, saved, mode) {
|
|
|
247
297
|
* @returns {void}
|
|
248
298
|
*/
|
|
249
299
|
function report(d, key, saved, outcome) {
|
|
250
|
-
if (!("error" in outcome) || saved.status != "failed")
|
|
251
|
-
|
|
300
|
+
if (!("error" in outcome) || saved.status != "failed") {
|
|
301
|
+
return
|
|
302
|
+
}
|
|
303
|
+
for (const reporter of d.reporters) {
|
|
304
|
+
reporter(outcome.error, key)
|
|
305
|
+
}
|
|
252
306
|
}
|
|
253
307
|
/**
|
|
254
308
|
* @param {DurableWorker} d
|
|
@@ -256,12 +310,20 @@ function report(d, key, saved, outcome) {
|
|
|
256
310
|
* @returns {Promise<void>}
|
|
257
311
|
*/
|
|
258
312
|
export async function resume(d, key) {
|
|
259
|
-
if (d.halted || d.live.has(key))
|
|
313
|
+
if (d.halted || d.live.has(key)) {
|
|
314
|
+
return
|
|
315
|
+
}
|
|
260
316
|
const saved = await load(d.store, key)
|
|
261
|
-
if (!saved || d.live.has(key) || !is_due(d, saved))
|
|
262
|
-
|
|
317
|
+
if (!saved || d.live.has(key) || !is_due(d, saved)) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
if (d.halted) {
|
|
321
|
+
return
|
|
322
|
+
}
|
|
263
323
|
const claimed = await claim(d, key, saved)
|
|
264
|
-
if (claimed && !d.live.has(key) && !d.halted)
|
|
265
|
-
|
|
266
|
-
|
|
324
|
+
if (claimed && !d.live.has(key) && !d.halted) {
|
|
325
|
+
void execute(d, key, claimed).then(
|
|
326
|
+
outcome => report(d, key, claimed, outcome)
|
|
327
|
+
)
|
|
328
|
+
}
|
|
267
329
|
}
|
package/src/durable/send.js
CHANGED
|
@@ -21,9 +21,11 @@ import { load } from "./store.js"
|
|
|
21
21
|
*/
|
|
22
22
|
export async function send(d, key, name, value) {
|
|
23
23
|
const problem = value === void 0 ? "undefined" : json_problem(value)
|
|
24
|
-
if (problem)
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if (problem) {
|
|
25
|
+
throw TypeError(
|
|
26
|
+
`The value sent to "${name}" of run "${key}" is not a JSON value: ${problem}`
|
|
27
|
+
)
|
|
28
|
+
}
|
|
27
29
|
const kept = copy(value)
|
|
28
30
|
for (;;) {
|
|
29
31
|
check_halted(d)
|
|
@@ -38,20 +40,30 @@ export async function send(d, key, name, value) {
|
|
|
38
40
|
running.unsaved.add(event)
|
|
39
41
|
const needed = running.taken + 1
|
|
40
42
|
await persist(d, running)
|
|
41
|
-
if (running.written >= needed)
|
|
43
|
+
if (running.written >= needed) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
42
46
|
const list = running.saved.events?.[name]
|
|
43
47
|
if (running.unsaved.delete(event) && list?.includes(event)) {
|
|
44
48
|
list.splice(list.indexOf(event), 1)
|
|
45
|
-
if (!list.length)
|
|
49
|
+
if (!list.length) {
|
|
50
|
+
delete running.saved.events?.[name]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (running.fault !== void 0) {
|
|
54
|
+
throw running.fault
|
|
46
55
|
}
|
|
47
|
-
if (running.fault !== void 0) throw running.fault
|
|
48
56
|
continue
|
|
49
57
|
}
|
|
50
58
|
const saved = await load(d.store, key)
|
|
51
|
-
if (d.live.has(key))
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)
|
|
59
|
+
if (d.live.has(key)) {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
if (saved && (saved.status == "cancelled" || saved.status == "done")) {
|
|
63
|
+
throw Error(
|
|
64
|
+
`The run "${key}" is ${saved.status}`
|
|
65
|
+
)
|
|
66
|
+
}
|
|
55
67
|
/** @type {SavedRun} */
|
|
56
68
|
const next = saved
|
|
57
69
|
? snapshot(saved)
|
|
@@ -62,9 +74,12 @@ export async function send(d, key, name, value) {
|
|
|
62
74
|
version: 1
|
|
63
75
|
}
|
|
64
76
|
if (next.status == "running" && next.owner && next.owner != d.owner) {
|
|
65
|
-
|
|
77
|
+
const inbox = next.inbox ??= []
|
|
78
|
+
inbox.push({ at, name, value: kept })
|
|
66
79
|
if (await d.store.put(key, next, saved?.version)) {
|
|
67
|
-
if (is_due(d, next))
|
|
80
|
+
if (is_due(d, next)) {
|
|
81
|
+
void resume(d, key).catch(noop)
|
|
82
|
+
}
|
|
68
83
|
return
|
|
69
84
|
}
|
|
70
85
|
continue
|
|
@@ -79,7 +94,9 @@ export async function send(d, key, name, value) {
|
|
|
79
94
|
unclaim(next)
|
|
80
95
|
}
|
|
81
96
|
if (await d.store.put(key, next, saved?.version)) {
|
|
82
|
-
if (revive)
|
|
97
|
+
if (revive) {
|
|
98
|
+
void resume(d, key).catch(noop)
|
|
99
|
+
}
|
|
83
100
|
return
|
|
84
101
|
}
|
|
85
102
|
}
|
package/src/durable/serve.js
CHANGED
|
@@ -25,38 +25,32 @@ export async function serve(d, serve_options = {}) {
|
|
|
25
25
|
interval = 1000,
|
|
26
26
|
onError = noop
|
|
27
27
|
} = serve_options
|
|
28
|
-
if (!(Number.isInteger(batch) && batch > 0))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
if (!(Number.isInteger(batch) && batch > 0)) {
|
|
29
|
+
throw TypeError(
|
|
30
|
+
"The batch of serve() must be a positive integer"
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
if (!(Number.isFinite(interval) && interval > 0)) {
|
|
34
|
+
throw TypeError(
|
|
35
|
+
"The interval of serve() must be a positive number of milliseconds"
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
if (typeof onError != "function") {
|
|
39
|
+
throw TypeError(
|
|
40
|
+
"The onError of serve() must be a function"
|
|
41
|
+
)
|
|
42
|
+
}
|
|
37
43
|
check_halted(d)
|
|
38
44
|
const controller = new AbortController()
|
|
39
45
|
const unlink = link_signal(controller, serve_options.signal)
|
|
40
|
-
/**
|
|
41
|
-
* @param {unknown} error
|
|
42
|
-
* @param {string=} key
|
|
43
|
-
* @returns {void}
|
|
44
|
-
*/
|
|
45
|
-
function reporter(error, key) {
|
|
46
|
-
onError(error, key)
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* @returns {void}
|
|
50
|
-
*/
|
|
51
|
-
function stop() {
|
|
52
|
-
controller.abort()
|
|
53
|
-
}
|
|
54
46
|
d.reporters.add(reporter)
|
|
55
47
|
d.stoppers.add(stop)
|
|
56
48
|
try {
|
|
57
49
|
while (!controller.signal.aborted) {
|
|
58
50
|
try {
|
|
59
|
-
for (const key of await d.store.due(Date.now(), batch))
|
|
51
|
+
for (const key of await d.store.due(Date.now(), batch)) {
|
|
52
|
+
void resume(d, key).catch(error => reporter(error, key))
|
|
53
|
+
}
|
|
60
54
|
} catch (error) {
|
|
61
55
|
reporter(error)
|
|
62
56
|
}
|
|
@@ -67,4 +61,16 @@ export async function serve(d, serve_options = {}) {
|
|
|
67
61
|
d.stoppers.delete(stop)
|
|
68
62
|
unlink()
|
|
69
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* @param {unknown} error
|
|
66
|
+
* @param {string=} key
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
69
|
+
function reporter(error, key) {
|
|
70
|
+
onError(error, key)
|
|
71
|
+
}
|
|
72
|
+
/** @returns {void} */
|
|
73
|
+
function stop() {
|
|
74
|
+
controller.abort()
|
|
75
|
+
}
|
|
70
76
|
}
|
package/src/durable/state.js
CHANGED
|
@@ -5,14 +5,18 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export function absorb(saved) {
|
|
7
7
|
const { inbox } = saved
|
|
8
|
-
if (!inbox)
|
|
8
|
+
if (!inbox) {
|
|
9
|
+
return
|
|
10
|
+
}
|
|
9
11
|
delete saved.inbox
|
|
10
|
-
for (const item of inbox)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
for (const item of inbox) {
|
|
13
|
+
push_event(
|
|
14
|
+
saved,
|
|
15
|
+
item.name,
|
|
16
|
+
item.at,
|
|
17
|
+
item.value
|
|
18
|
+
)
|
|
19
|
+
}
|
|
16
20
|
}
|
|
17
21
|
/**
|
|
18
22
|
* When a worker should pick the run up, in milliseconds since the epoch: the end of the lease of a running run, 0 for one
|
|
@@ -22,8 +26,12 @@ export function absorb(saved) {
|
|
|
22
26
|
* @returns {number | undefined}
|
|
23
27
|
*/
|
|
24
28
|
export function dueAt(saved) {
|
|
25
|
-
if (saved.status == "running")
|
|
26
|
-
|
|
29
|
+
if (saved.status == "running") {
|
|
30
|
+
return saved.owner ? saved.lease ?? 0 : 0
|
|
31
|
+
}
|
|
32
|
+
if (saved.status == "sleeping" || saved.status == "waiting") {
|
|
33
|
+
return saved.wake
|
|
34
|
+
}
|
|
27
35
|
return void 0
|
|
28
36
|
}
|
|
29
37
|
/**
|
|
@@ -36,7 +44,8 @@ export function dueAt(saved) {
|
|
|
36
44
|
export function push_event(saved, name, at, value) {
|
|
37
45
|
const event = { at, value }
|
|
38
46
|
const events = saved.events ??= {}
|
|
39
|
-
|
|
47
|
+
const queue = events[name] ??= []
|
|
48
|
+
queue.push(event)
|
|
40
49
|
return event
|
|
41
50
|
}
|
|
42
51
|
/**
|
|
@@ -63,10 +72,14 @@ export function snapshot(saved) {
|
|
|
63
72
|
if (saved.events) {
|
|
64
73
|
/** @type {NonNullable<SavedRun["events"]>} */
|
|
65
74
|
const events = {}
|
|
66
|
-
for (const [ name, list ] of Object.entries(saved.events))
|
|
75
|
+
for (const [ name, list ] of Object.entries(saved.events)) {
|
|
76
|
+
events[name] = [ ...list ]
|
|
77
|
+
}
|
|
67
78
|
next.events = events
|
|
68
79
|
}
|
|
69
|
-
if (saved.inbox)
|
|
80
|
+
if (saved.inbox) {
|
|
81
|
+
next.inbox = [ ...saved.inbox ]
|
|
82
|
+
}
|
|
70
83
|
return next
|
|
71
84
|
}
|
|
72
85
|
/**
|
|
@@ -80,7 +93,9 @@ export function take_event(saved, name) {
|
|
|
80
93
|
list.shift()
|
|
81
94
|
if (!list.length) {
|
|
82
95
|
delete events[name]
|
|
83
|
-
if (!Object.keys(events).length)
|
|
96
|
+
if (!Object.keys(events).length) {
|
|
97
|
+
delete saved.events
|
|
98
|
+
}
|
|
84
99
|
}
|
|
85
100
|
}
|
|
86
101
|
/**
|
package/src/durable/store.js
CHANGED
|
@@ -8,14 +8,15 @@ const encoded = new WeakMap()
|
|
|
8
8
|
*/
|
|
9
9
|
function encode(saved) {
|
|
10
10
|
const { journal, ...rest } = saved
|
|
11
|
-
let json = JSON.stringify(rest).slice(0, -1)
|
|
12
|
-
json += json.length > 1 ? ",\"journal\":[" : "\"journal\":["
|
|
11
|
+
let json = `${JSON.stringify(rest).slice(0, -1)},"journal":[`
|
|
13
12
|
for (let index = 0; index < journal.length; index++) {
|
|
14
13
|
const entry = /** @type {JournalEntry} */(journal[index])/**/
|
|
15
14
|
let text = entry.done ? encoded.get(entry) : void 0
|
|
16
15
|
if (text == null) {
|
|
17
16
|
text = JSON.stringify(entry)
|
|
18
|
-
if (entry.done)
|
|
17
|
+
if (entry.done) {
|
|
18
|
+
encoded.set(entry, text)
|
|
19
|
+
}
|
|
19
20
|
}
|
|
20
21
|
json += index ? `,${text}` : text
|
|
21
22
|
}
|
|
@@ -41,17 +42,23 @@ export function memory() {
|
|
|
41
42
|
/** @type {string[]} */
|
|
42
43
|
const keys = []
|
|
43
44
|
for (const [ key, row ] of rows) {
|
|
44
|
-
if (keys.length >= limit)
|
|
45
|
-
|
|
45
|
+
if (keys.length >= limit) {
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
if ((row.due ?? Infinity) <= now) {
|
|
49
|
+
keys.push(key)
|
|
50
|
+
}
|
|
46
51
|
}
|
|
47
52
|
return keys
|
|
48
53
|
},
|
|
49
54
|
get(key) {
|
|
50
55
|
const row = rows.get(key)
|
|
51
|
-
return row &&
|
|
56
|
+
return row && JSON.parse(row.data)
|
|
52
57
|
},
|
|
53
58
|
put(key, saved, expected) {
|
|
54
|
-
if (rows.get(key)?.version !== expected)
|
|
59
|
+
if (rows.get(key)?.version !== expected) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
55
62
|
rows.set(
|
|
56
63
|
key,
|
|
57
64
|
{
|
|
@@ -71,9 +78,11 @@ export function memory() {
|
|
|
71
78
|
* @returns {DurableStore}
|
|
72
79
|
*/
|
|
73
80
|
export function sqlite(db, table = "durable_runs") {
|
|
74
|
-
if (!/^[a-z_]\w*$/i.test(table))
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
if (!/^[a-z_]\w*$/i.test(table)) {
|
|
82
|
+
throw TypeError(
|
|
83
|
+
`The table name must be an identifier: ${table}`
|
|
84
|
+
)
|
|
85
|
+
}
|
|
77
86
|
db.exec(
|
|
78
87
|
`create table if not exists ${table} (key text primary key, version integer not null, due integer, data text not null)`
|
|
79
88
|
)
|
package/src/durable/watch.js
CHANGED
|
@@ -17,24 +17,35 @@ const finished_statuses = new Set(
|
|
|
17
17
|
*/
|
|
18
18
|
async function check(d, key, poller) {
|
|
19
19
|
poller.timer = void 0
|
|
20
|
-
if (d.live.has(key))
|
|
20
|
+
if (d.live.has(key)) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
21
23
|
poller.checking = true
|
|
22
24
|
const saved = await load(d.store, key).catch(noop)
|
|
23
|
-
if (saved && is_due(d, saved) && d.pollers.get(key) == poller)
|
|
25
|
+
if (saved && is_due(d, saved) && d.pollers.get(key) == poller) {
|
|
26
|
+
await resume(d, key).catch(noop)
|
|
27
|
+
}
|
|
24
28
|
poller.checking = false
|
|
25
|
-
if (d.pollers.get(key) != poller)
|
|
29
|
+
if (d.pollers.get(key) != poller) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
26
32
|
if (saved && finished_statuses.has(saved.status)) {
|
|
27
33
|
settle(
|
|
28
34
|
d,
|
|
29
35
|
key,
|
|
30
36
|
watcher => {
|
|
31
|
-
if (saved.status == "done")
|
|
32
|
-
|
|
37
|
+
if (saved.status == "done") {
|
|
38
|
+
watcher.resolve(saved.result)
|
|
39
|
+
} else {
|
|
40
|
+
watcher.reject(error_of(saved))
|
|
41
|
+
}
|
|
33
42
|
}
|
|
34
43
|
)
|
|
35
44
|
return
|
|
36
45
|
}
|
|
37
|
-
if (d.live.has(key))
|
|
46
|
+
if (d.live.has(key)) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
38
49
|
const { delay } = poller
|
|
39
50
|
poller.delay = Math.min(delay * 2, d.poll_cap)
|
|
40
51
|
const left = (saved && dueAt(saved) || 0) - Date.now()
|
|
@@ -56,8 +67,11 @@ export function notify(d, key, outcome) {
|
|
|
56
67
|
d,
|
|
57
68
|
key,
|
|
58
69
|
watcher => {
|
|
59
|
-
if ("error" in outcome)
|
|
60
|
-
|
|
70
|
+
if ("error" in outcome) {
|
|
71
|
+
watcher.reject(outcome.error)
|
|
72
|
+
} else {
|
|
73
|
+
watcher.resolve(outcome.value)
|
|
74
|
+
}
|
|
61
75
|
}
|
|
62
76
|
)
|
|
63
77
|
}
|
|
@@ -98,9 +112,13 @@ export function settle(d, key, each) {
|
|
|
98
112
|
d.pollers.delete(key)
|
|
99
113
|
}
|
|
100
114
|
const waiting = d.watchers.get(key)
|
|
101
|
-
if (!waiting)
|
|
115
|
+
if (!waiting) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
102
118
|
d.watchers.delete(key)
|
|
103
|
-
for (const watcher of waiting)
|
|
119
|
+
for (const watcher of waiting) {
|
|
120
|
+
each(watcher)
|
|
121
|
+
}
|
|
104
122
|
}
|
|
105
123
|
/**
|
|
106
124
|
* @param {DurableWorker} d
|
|
@@ -118,7 +136,9 @@ function watch(d, key) {
|
|
|
118
136
|
d.pollers.set(key, poller)
|
|
119
137
|
}
|
|
120
138
|
poller.delay = d.poll_ms
|
|
121
|
-
if (poller.checking)
|
|
139
|
+
if (poller.checking) {
|
|
140
|
+
return
|
|
141
|
+
}
|
|
122
142
|
clearTimeout(poller.timer)
|
|
123
143
|
void check(d, key, poller)
|
|
124
144
|
}
|
package/src/errors.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* @returns {string | undefined}
|
|
4
4
|
*/
|
|
5
5
|
function get_message(data) {
|
|
6
|
-
if (!data || typeof data != "object")
|
|
6
|
+
if (!data || typeof data != "object") {
|
|
7
|
+
return
|
|
8
|
+
}
|
|
7
9
|
for (const key of [
|
|
8
10
|
"message",
|
|
9
11
|
"error",
|
|
@@ -11,8 +13,12 @@ function get_message(data) {
|
|
|
11
13
|
"title"
|
|
12
14
|
]) {
|
|
13
15
|
const value = /** @type {Record<string, unknown>} */(data)/**/[key]
|
|
14
|
-
if (typeof value == "string" && value)
|
|
15
|
-
|
|
16
|
+
if (typeof value == "string" && value) {
|
|
17
|
+
return value
|
|
18
|
+
}
|
|
19
|
+
if (value && typeof value == "object" && typeof /** @type {{ message?: unknown }} */(value)/**/.message == "string") {
|
|
20
|
+
return /** @type {{ message: string }} */(value)/**/.message
|
|
21
|
+
}
|
|
16
22
|
}
|
|
17
23
|
return undefined
|
|
18
24
|
}
|