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/flow/compile.js
CHANGED
|
@@ -15,7 +15,9 @@ const programs = new WeakMap()
|
|
|
15
15
|
*/
|
|
16
16
|
export function compile(definition) {
|
|
17
17
|
const cached = programs.get(definition)
|
|
18
|
-
if (cached)
|
|
18
|
+
if (cached) {
|
|
19
|
+
return cached
|
|
20
|
+
}
|
|
19
21
|
const source_nodes = definition.list.slice(0, definition.count)
|
|
20
22
|
/** @type {Map<unknown, string>} */
|
|
21
23
|
const by_ref = new Map(
|
|
@@ -24,45 +26,6 @@ export function compile(definition) {
|
|
|
24
26
|
)
|
|
25
27
|
)
|
|
26
28
|
const by_name = new Set(by_ref.values())
|
|
27
|
-
/**
|
|
28
|
-
* @param {unknown} target
|
|
29
|
-
* @returns {string | undefined}
|
|
30
|
-
*/
|
|
31
|
-
function name_of(target) {
|
|
32
|
-
return typeof target == "string"
|
|
33
|
-
? by_name.has(target)
|
|
34
|
-
? target
|
|
35
|
-
: void 0
|
|
36
|
-
: by_ref.get(target)
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* @param {unknown} target
|
|
40
|
-
* @param {string} message
|
|
41
|
-
* @returns {string}
|
|
42
|
-
*/
|
|
43
|
-
function resolve(target, message) {
|
|
44
|
-
const name = name_of(target)
|
|
45
|
-
if (name == null) throw Error(
|
|
46
|
-
message.replace("%s", describe(target))
|
|
47
|
-
)
|
|
48
|
-
return name
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* @param {DefinitionNode} node
|
|
52
|
-
* @returns {Record<string, "keep" | "queue" | "restart"> | undefined}
|
|
53
|
-
*/
|
|
54
|
-
function to_arrive(node) {
|
|
55
|
-
/** @type {Record<string, "keep" | "queue" | "restart"> | undefined} */
|
|
56
|
-
let arrive
|
|
57
|
-
for (const dep of node.deps) {
|
|
58
|
-
if (!/** @type {RefProbe} */(dep)/**/?.[ARRIVE]) continue
|
|
59
|
-
(arrive ??= Object.create(null))[resolve(
|
|
60
|
-
to_ref(dep),
|
|
61
|
-
`Flow node "${node.name}" depends on %s, which is not added`
|
|
62
|
-
)] = /** @type {{ policy: "keep" | "queue" | "restart" }} */(/** @type {unknown} */(dep))/**/.policy
|
|
63
|
-
}
|
|
64
|
-
return arrive
|
|
65
|
-
}
|
|
66
29
|
/** @type {ProgramNode[]} */
|
|
67
30
|
const nodes = source_nodes.map(
|
|
68
31
|
node => ({
|
|
@@ -100,7 +63,9 @@ export function compile(definition) {
|
|
|
100
63
|
for (const dep of node.deps) {
|
|
101
64
|
const source = /** @type {ProgramNode} */(nodes_by_name.get(dep))/**/
|
|
102
65
|
source.dependents.push(node.name)
|
|
103
|
-
if (node.arrive?.[dep] == "queue")
|
|
66
|
+
if (node.arrive?.[dep] == "queue") {
|
|
67
|
+
source.queuers.push(node.name)
|
|
68
|
+
}
|
|
104
69
|
}
|
|
105
70
|
}
|
|
106
71
|
/** @type {Map<string, number>} */
|
|
@@ -108,7 +73,9 @@ export function compile(definition) {
|
|
|
108
73
|
/** @type {string[]} */
|
|
109
74
|
const sorted = []
|
|
110
75
|
for (const root of nodes) {
|
|
111
|
-
if (marks.has(root.name))
|
|
76
|
+
if (marks.has(root.name)) {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
112
79
|
marks.set(root.name, 1)
|
|
113
80
|
const path = [ root.name ]
|
|
114
81
|
const cursors = [ 0 ]
|
|
@@ -127,10 +94,14 @@ export function compile(definition) {
|
|
|
127
94
|
cursors[top] = cursor + 1
|
|
128
95
|
const dep = /** @type {string} */(node.deps[cursor])/**/
|
|
129
96
|
const mark = marks.get(dep)
|
|
130
|
-
if (mark == 2)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
97
|
+
if (mark == 2) {
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
if (mark == 1) {
|
|
101
|
+
throw Error(
|
|
102
|
+
`Circular dependency: ${[ ...path.slice(path.indexOf(dep)), dep ].join(" -> ")}`
|
|
103
|
+
)
|
|
104
|
+
}
|
|
134
105
|
marks.set(dep, 1)
|
|
135
106
|
path.push(dep)
|
|
136
107
|
cursors.push(0)
|
|
@@ -138,26 +109,6 @@ export function compile(definition) {
|
|
|
138
109
|
}
|
|
139
110
|
/** @type {Map<string, Set<string>>} */
|
|
140
111
|
const downstream = new Map()
|
|
141
|
-
/**
|
|
142
|
-
* @param {string} name
|
|
143
|
-
* @returns {Set<string>}
|
|
144
|
-
*/
|
|
145
|
-
function downstream_of(name) {
|
|
146
|
-
let found = downstream.get(name)
|
|
147
|
-
if (found) return found
|
|
148
|
-
found = new Set()
|
|
149
|
-
const stack = [ name ]
|
|
150
|
-
while (stack.length) {
|
|
151
|
-
const current = /** @type {string} */(stack.pop())/**/
|
|
152
|
-
for (const dependent of /** @type {ProgramNode} */(nodes_by_name.get(current))/**/.dependents) {
|
|
153
|
-
if (found.has(dependent)) continue
|
|
154
|
-
found.add(dependent)
|
|
155
|
-
stack.push(dependent)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
downstream.set(name, found)
|
|
159
|
-
return found
|
|
160
|
-
}
|
|
161
112
|
/** @type {Map<string, ProgramEdge>} */
|
|
162
113
|
const edges = new Map()
|
|
163
114
|
for (const edge of definition.edges) {
|
|
@@ -249,4 +200,73 @@ export function compile(definition) {
|
|
|
249
200
|
}
|
|
250
201
|
programs.set(definition, program)
|
|
251
202
|
return program
|
|
203
|
+
/**
|
|
204
|
+
* @param {string} name
|
|
205
|
+
* @returns {Set<string>}
|
|
206
|
+
*/
|
|
207
|
+
function downstream_of(name) {
|
|
208
|
+
let found = downstream.get(name)
|
|
209
|
+
if (found) {
|
|
210
|
+
return found
|
|
211
|
+
}
|
|
212
|
+
found = new Set()
|
|
213
|
+
const stack = [ name ]
|
|
214
|
+
while (stack.length) {
|
|
215
|
+
const current = /** @type {string} */(stack.pop())/**/
|
|
216
|
+
for (const dependent of /** @type {ProgramNode} */(nodes_by_name.get(current))/**/.dependents) {
|
|
217
|
+
if (found.has(dependent)) {
|
|
218
|
+
continue
|
|
219
|
+
}
|
|
220
|
+
found.add(dependent)
|
|
221
|
+
stack.push(dependent)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
downstream.set(name, found)
|
|
225
|
+
return found
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* @param {unknown} target
|
|
229
|
+
* @returns {string | undefined}
|
|
230
|
+
*/
|
|
231
|
+
function name_of(target) {
|
|
232
|
+
return typeof target == "string"
|
|
233
|
+
? by_name.has(target)
|
|
234
|
+
? target
|
|
235
|
+
: void 0
|
|
236
|
+
: by_ref.get(target)
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* @param {unknown} target
|
|
240
|
+
* @param {string} message
|
|
241
|
+
* @returns {string}
|
|
242
|
+
*/
|
|
243
|
+
function resolve(target, message) {
|
|
244
|
+
const name = name_of(target)
|
|
245
|
+
if (name == null) {
|
|
246
|
+
throw Error(
|
|
247
|
+
message.replace("%s", describe(target))
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
return name
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* @param {DefinitionNode} node
|
|
254
|
+
* @returns {Record<string, "keep" | "queue" | "restart"> | undefined}
|
|
255
|
+
*/
|
|
256
|
+
function to_arrive(node) {
|
|
257
|
+
/** @type {Record<string, "keep" | "queue" | "restart"> | undefined} */
|
|
258
|
+
let arrive
|
|
259
|
+
for (const dep of node.deps) {
|
|
260
|
+
if (!/** @type {RefProbe} */(dep)/**/?.[ARRIVE]) {
|
|
261
|
+
continue
|
|
262
|
+
}
|
|
263
|
+
/** @type {Record<string, "keep" | "queue" | "restart">} */
|
|
264
|
+
const target = arrive ??= Object.create(null)
|
|
265
|
+
target[resolve(
|
|
266
|
+
to_ref(dep),
|
|
267
|
+
`Flow node "${node.name}" depends on %s, which is not added`
|
|
268
|
+
)] = /** @type {{ policy: "keep" | "queue" | "restart" }} */(/** @type {unknown} */(dep))/**/.policy
|
|
269
|
+
}
|
|
270
|
+
return arrive
|
|
271
|
+
}
|
|
252
272
|
}
|
package/src/flow/graph.js
CHANGED
|
@@ -31,14 +31,18 @@ export function abandon(c, deps, race) {
|
|
|
31
31
|
)
|
|
32
32
|
while (stack.length) {
|
|
33
33
|
const name = /** @type {string} */(stack.pop())/**/
|
|
34
|
-
if (name == race || region.has(name))
|
|
34
|
+
if (name == race || region.has(name)) {
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
35
37
|
region.add(name)
|
|
36
38
|
stack.push(
|
|
37
39
|
...get_record(c, name).node.deps,
|
|
38
40
|
...c.program.sources.get(name) ?? []
|
|
39
41
|
)
|
|
40
42
|
}
|
|
41
|
-
if (!region.size)
|
|
43
|
+
if (!region.size) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
42
46
|
const dead = new Set(region).add(race)
|
|
43
47
|
/** @type {string[]} */
|
|
44
48
|
const needed = []
|
|
@@ -51,25 +55,33 @@ export function abandon(c, deps, race) {
|
|
|
51
55
|
)
|
|
52
56
|
: [
|
|
53
57
|
...dependents,
|
|
54
|
-
|
|
58
|
+
.../** @type {string[]} */(c.program.transitions.get(name))/**/
|
|
55
59
|
]
|
|
56
60
|
if (targets.some(
|
|
57
61
|
target => !dead.has(target) && may_run(c, target, dead)
|
|
58
|
-
))
|
|
62
|
+
)) {
|
|
63
|
+
needed.push(name)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const name of needed) {
|
|
67
|
+
region.delete(name)
|
|
59
68
|
}
|
|
60
|
-
for (const name of needed) region.delete(name)
|
|
61
69
|
while (needed.length) {
|
|
62
70
|
const name = /** @type {string} */(needed.pop())/**/
|
|
63
71
|
for (const source of [
|
|
64
72
|
...get_record(c, name).node.deps,
|
|
65
73
|
...c.program.sources.get(name) ?? []
|
|
66
74
|
]) {
|
|
67
|
-
if (region.delete(source))
|
|
75
|
+
if (region.delete(source)) {
|
|
76
|
+
needed.push(source)
|
|
77
|
+
}
|
|
68
78
|
}
|
|
69
79
|
}
|
|
70
80
|
for (const name of region) {
|
|
71
81
|
const record = get_record(c, name)
|
|
72
|
-
if (settled_statuses.has(record.status))
|
|
82
|
+
if (settled_statuses.has(record.status)) {
|
|
83
|
+
continue
|
|
84
|
+
}
|
|
73
85
|
record.backlog = []
|
|
74
86
|
record.backlog_head = 0
|
|
75
87
|
record.retrigger = false
|
|
@@ -89,7 +101,9 @@ export function abandon(c, deps, race) {
|
|
|
89
101
|
settle(c, name, "skipped", void 0, is_running)
|
|
90
102
|
record.abandoned = true
|
|
91
103
|
}
|
|
92
|
-
for (const name of region)
|
|
104
|
+
for (const name of region) {
|
|
105
|
+
release_unused(c, name, void 0)
|
|
106
|
+
}
|
|
93
107
|
}
|
|
94
108
|
/**
|
|
95
109
|
* @param {RunContext} c
|
|
@@ -99,19 +113,27 @@ export function abandon(c, deps, race) {
|
|
|
99
113
|
* @returns {void}
|
|
100
114
|
*/
|
|
101
115
|
export function enter(c, targets, source, force) {
|
|
102
|
-
if (c.run_status != "running" || !targets.length)
|
|
116
|
+
if (c.run_status != "running" || !targets.length) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
103
119
|
/** @type {Set<string>} */
|
|
104
120
|
const touched = new Set()
|
|
105
121
|
for (const target of targets) {
|
|
106
|
-
for (const name of reset(c, target, true, force))
|
|
122
|
+
for (const name of reset(c, target, true, force)) {
|
|
123
|
+
touched.add(name)
|
|
124
|
+
}
|
|
107
125
|
}
|
|
108
126
|
for (const target of targets) {
|
|
109
127
|
const record = get_record(c, target)
|
|
110
128
|
record.activators.add(source)
|
|
111
129
|
record.activated = true
|
|
112
130
|
}
|
|
113
|
-
for (const target of targets)
|
|
114
|
-
|
|
131
|
+
for (const target of targets) {
|
|
132
|
+
try_start(c, target)
|
|
133
|
+
}
|
|
134
|
+
for (const name of touched) {
|
|
135
|
+
try_start(c, name)
|
|
136
|
+
}
|
|
115
137
|
}
|
|
116
138
|
/**
|
|
117
139
|
* @param {RunContext} c
|
|
@@ -189,14 +211,26 @@ function is_live(c, name, target, visited) {
|
|
|
189
211
|
const stack = [ name ]
|
|
190
212
|
while (stack.length) {
|
|
191
213
|
const current = /** @type {string} */(stack.pop())/**/
|
|
192
|
-
if (visited.has(current))
|
|
214
|
+
if (visited.has(current)) {
|
|
215
|
+
continue
|
|
216
|
+
}
|
|
193
217
|
visited.add(current)
|
|
194
|
-
if (c.program.is_downstream(target, current))
|
|
218
|
+
if (c.program.is_downstream(target, current)) {
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
195
221
|
const record = get_record(c, current)
|
|
196
|
-
if (active_statuses.has(record.status))
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
222
|
+
if (active_statuses.has(record.status)) {
|
|
223
|
+
return true
|
|
224
|
+
}
|
|
225
|
+
if (record.status != "idle") {
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
if (record.activated) {
|
|
229
|
+
return true
|
|
230
|
+
}
|
|
231
|
+
for (const source of c.program.sources.get(current) ?? []) {
|
|
232
|
+
stack.push(source)
|
|
233
|
+
}
|
|
200
234
|
}
|
|
201
235
|
return false
|
|
202
236
|
}
|
|
@@ -211,10 +245,14 @@ function kill(c, name, source) {
|
|
|
211
245
|
c,
|
|
212
246
|
() => {
|
|
213
247
|
const record = get_record(c, name)
|
|
214
|
-
if (c.run_status != "running" || record.status != "idle" || record.activated)
|
|
248
|
+
if (c.run_status != "running" || record.status != "idle" || record.activated) {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
215
251
|
const visited = new Set([ name, source ])
|
|
216
|
-
for (const other of c.program.sources.get(name)
|
|
217
|
-
if (other != source && is_live(c, other, name, visited))
|
|
252
|
+
for (const other of /** @type {string[]} */(c.program.sources.get(name))/**/) {
|
|
253
|
+
if (other != source && is_live(c, other, name, visited)) {
|
|
254
|
+
return
|
|
255
|
+
}
|
|
218
256
|
}
|
|
219
257
|
settle(c, name, "skipped", void 0)
|
|
220
258
|
}
|
|
@@ -226,7 +264,9 @@ function kill(c, name, source) {
|
|
|
226
264
|
* @returns {void}
|
|
227
265
|
*/
|
|
228
266
|
export function kill_transitions(c, name) {
|
|
229
|
-
for (const target of c.program.transitions.get(name)
|
|
267
|
+
for (const target of /** @type {string[]} */(c.program.transitions.get(name))/**/) {
|
|
268
|
+
kill(c, target, name)
|
|
269
|
+
}
|
|
230
270
|
}
|
|
231
271
|
/**
|
|
232
272
|
* @param {RunContext} c
|
|
@@ -236,8 +276,12 @@ export function kill_transitions(c, name) {
|
|
|
236
276
|
*/
|
|
237
277
|
function may_run(c, name, dead) {
|
|
238
278
|
const record = get_record(c, name)
|
|
239
|
-
if (settled_statuses.has(record.status))
|
|
240
|
-
|
|
279
|
+
if (settled_statuses.has(record.status)) {
|
|
280
|
+
return false
|
|
281
|
+
}
|
|
282
|
+
if (record.status != "idle" || record.activated) {
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
241
285
|
return (c.program.sources.get(name) ?? []).some(
|
|
242
286
|
source => is_live(
|
|
243
287
|
c,
|
|
@@ -257,27 +301,37 @@ function may_run(c, name, dead) {
|
|
|
257
301
|
export function reset(c, name, is_root, force) {
|
|
258
302
|
/** @type {Set<string>} */
|
|
259
303
|
const names = new Set()
|
|
260
|
-
/** @type {{ from: string | undefined, name: string }[]} */
|
|
261
|
-
const stack = [
|
|
304
|
+
/** @type {{ forward: boolean, from: string | undefined, name: string }[]} */
|
|
305
|
+
const stack = [
|
|
306
|
+
{ forward: false, from: void 0, name }
|
|
307
|
+
]
|
|
262
308
|
while (stack.length) {
|
|
263
|
-
const entry = /** @type {{ from: string | undefined, name: string }} */(stack.pop())/**/
|
|
309
|
+
const entry = /** @type {{ forward: boolean, from: string | undefined, name: string }} */(stack.pop())/**/
|
|
264
310
|
const node_name = entry.name
|
|
265
|
-
if (names.has(node_name))
|
|
311
|
+
if (names.has(node_name)) {
|
|
312
|
+
continue
|
|
313
|
+
}
|
|
266
314
|
const node_record = get_record(c, node_name)
|
|
267
315
|
const arrive = entry.from == null ? void 0 : node_record.node.arrive?.[entry.from]
|
|
268
|
-
if (arrive == "keep" || arrive == "queue")
|
|
316
|
+
if (arrive == "keep" || arrive == "queue") {
|
|
317
|
+
continue
|
|
318
|
+
}
|
|
269
319
|
if (entry.from != null && node_record.status == "done") {
|
|
270
320
|
const from = get_record(c, entry.from)
|
|
271
321
|
if (node_record.node.options.join == "race"
|
|
272
322
|
? from.status != "done"
|
|
273
|
-
: from.fatal && from.node.options.release)
|
|
323
|
+
: from.fatal && from.node.options.release) {
|
|
324
|
+
continue
|
|
325
|
+
}
|
|
274
326
|
}
|
|
275
327
|
const overlap = arrive == "restart" ? "restart" : node_record.node.options.overlap ?? "restart"
|
|
276
328
|
const keeps = overlap == "restart"
|
|
277
329
|
? node_record.status == "running"
|
|
278
330
|
: active_statuses.has(node_record.status) || holds_backlog(node_record)
|
|
279
331
|
if (keeps && !(force && node_name == name)) {
|
|
280
|
-
if (overlap != "ignore"
|
|
332
|
+
if (overlap != "ignore" && !entry.forward) {
|
|
333
|
+
node_record.retrigger = true
|
|
334
|
+
}
|
|
281
335
|
continue
|
|
282
336
|
}
|
|
283
337
|
names.add(node_name)
|
|
@@ -285,26 +339,38 @@ export function reset(c, name, is_root, force) {
|
|
|
285
339
|
const next = [
|
|
286
340
|
...get_record(c, node_name).node.dependents
|
|
287
341
|
]
|
|
288
|
-
|
|
342
|
+
const dependent_count = next.length
|
|
343
|
+
for (const target of /** @type {string[]} */(c.program.forward.get(node_name))/**/) {
|
|
289
344
|
const target_record = get_record(c, target)
|
|
290
345
|
const is_owned = target_record.activators.has(node_name) || target_record.activators.has("*")
|
|
291
|
-
if (is_owned || !target_record.activated && settled_statuses.has(target_record.status))
|
|
292
|
-
|
|
293
|
-
for (let i = next.length; i--;) stack.push(
|
|
294
|
-
{
|
|
295
|
-
from: node_name,
|
|
296
|
-
name: /** @type {string} */(next[i])/**/
|
|
346
|
+
if (is_owned || !target_record.activated && settled_statuses.has(target_record.status)) {
|
|
347
|
+
next.push(target)
|
|
297
348
|
}
|
|
298
|
-
|
|
349
|
+
}
|
|
350
|
+
for (let i = next.length; i--;) {
|
|
351
|
+
stack.push(
|
|
352
|
+
{
|
|
353
|
+
forward: i >= dependent_count,
|
|
354
|
+
from: node_name,
|
|
355
|
+
name: /** @type {string} */(next[i])/**/
|
|
356
|
+
}
|
|
357
|
+
)
|
|
358
|
+
}
|
|
299
359
|
}
|
|
300
360
|
for (const node_name of names) {
|
|
301
361
|
const record = get_record(c, node_name)
|
|
302
362
|
clear_record(c, record)
|
|
303
|
-
if (node_name == name && is_root)
|
|
363
|
+
if (node_name == name && is_root) {
|
|
364
|
+
continue
|
|
365
|
+
}
|
|
304
366
|
for (const source of record.activators) {
|
|
305
|
-
if (names.has(source))
|
|
367
|
+
if (names.has(source)) {
|
|
368
|
+
record.activators.delete(source)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if ((c.program.sources.get(node_name) ?? []).some(source => names.has(source))) {
|
|
372
|
+
record.activators.delete("*")
|
|
306
373
|
}
|
|
307
|
-
if ((c.program.sources.get(node_name) ?? []).some(source => names.has(source))) record.activators.delete("*")
|
|
308
374
|
record.activated = !c.incoming.has(node_name) || record.activators.size > 0
|
|
309
375
|
}
|
|
310
376
|
return names
|
package/src/flow/index.js
CHANGED
|
@@ -9,14 +9,20 @@ export default /** @type {import("../../public.js").FlowFunction} */(/** @type {
|
|
|
9
9
|
*/
|
|
10
10
|
function(options = {}) {
|
|
11
11
|
for (const key of Object.keys(options)) {
|
|
12
|
-
if (key != "concurrency" && key != "maxSteps")
|
|
12
|
+
if (key != "concurrency" && key != "maxSteps") {
|
|
13
|
+
throw TypeError(`Unknown flow option "${key}"`)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (options.concurrency != null && !is_count(options.concurrency)) {
|
|
17
|
+
throw TypeError(
|
|
18
|
+
"The concurrency of a flow must be a positive integer or Infinity"
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
if (options.maxSteps != null && !is_count(options.maxSteps)) {
|
|
22
|
+
throw TypeError(
|
|
23
|
+
"The maxSteps of a flow must be a positive integer or Infinity"
|
|
24
|
+
)
|
|
13
25
|
}
|
|
14
|
-
if (options.concurrency != null && !is_count(options.concurrency)) throw TypeError(
|
|
15
|
-
"The concurrency of a flow must be a positive integer or Infinity"
|
|
16
|
-
)
|
|
17
|
-
if (options.maxSteps != null && !is_count(options.maxSteps)) throw TypeError(
|
|
18
|
-
"The maxSteps of a flow must be a positive integer or Infinity"
|
|
19
|
-
)
|
|
20
26
|
return create_builder(
|
|
21
27
|
{
|
|
22
28
|
count: 0,
|
package/src/flow/items.js
CHANGED
|
@@ -17,12 +17,16 @@ async function collect_items(iterator, signal) {
|
|
|
17
17
|
for (;;) {
|
|
18
18
|
const step = iterator.next()
|
|
19
19
|
const { done, value } = signal ? await race_signal(step, signal) : await step
|
|
20
|
-
if (done)
|
|
20
|
+
if (done) {
|
|
21
|
+
return items
|
|
22
|
+
}
|
|
21
23
|
items.push(value)
|
|
22
24
|
}
|
|
23
25
|
} finally {
|
|
24
|
-
if (signal?.aborted)
|
|
25
|
-
.
|
|
26
|
+
if (signal?.aborted) {
|
|
27
|
+
await iterator.return?.()
|
|
28
|
+
.catch(noop)
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
/**
|
|
@@ -31,9 +35,11 @@ async function collect_items(iterator, signal) {
|
|
|
31
35
|
*/
|
|
32
36
|
export function each(target) {
|
|
33
37
|
const is_flow = !!target?.[DEFINITION]
|
|
34
|
-
if (!is_flow && typeof target != "function")
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
if (!is_flow && typeof target != "function") {
|
|
39
|
+
throw TypeError(
|
|
40
|
+
"flow.each() needs a function or a flow"
|
|
41
|
+
)
|
|
42
|
+
}
|
|
37
43
|
/** @type {TaskRef} */
|
|
38
44
|
const run_all = is_flow
|
|
39
45
|
? async (/** @type {unknown} */ items) => Promise.all(
|
|
@@ -68,7 +74,7 @@ export function each(target) {
|
|
|
68
74
|
export async function run_each(c, record, token) {
|
|
69
75
|
const { node } = record
|
|
70
76
|
const { name, options } = node
|
|
71
|
-
const [ source, ...rest ] = node.deps.length ? args_of(
|
|
77
|
+
const [ source, ...rest ] = node.deps.length ? args_of(record) : [ c.state ]
|
|
72
78
|
const parent = /** @type {AbortController} */(record.controller)/**/.signal
|
|
73
79
|
/** @type {unknown[]} */
|
|
74
80
|
let items
|
|
@@ -79,10 +85,14 @@ export async function run_each(c, record, token) {
|
|
|
79
85
|
: to_items(source, parent)
|
|
80
86
|
items = Array.isArray(collected) ? collected : await collected
|
|
81
87
|
} catch (error) {
|
|
82
|
-
if (token != record.token || parent.aborted)
|
|
88
|
+
if (token != record.token || parent.aborted) {
|
|
89
|
+
return { kind: "stale" }
|
|
90
|
+
}
|
|
83
91
|
return { kind: "error", value: error }
|
|
84
92
|
}
|
|
85
|
-
if (token != record.token)
|
|
93
|
+
if (token != record.token) {
|
|
94
|
+
return { kind: "stale" }
|
|
95
|
+
}
|
|
86
96
|
record.item_list = items
|
|
87
97
|
record.item_source = source
|
|
88
98
|
const controller = new AbortController()
|
|
@@ -97,6 +107,35 @@ export async function run_each(c, record, token) {
|
|
|
97
107
|
/** @type {{ error: unknown, index: number | undefined } | undefined} */
|
|
98
108
|
let failure
|
|
99
109
|
let next = 0
|
|
110
|
+
const workers = Math.min(
|
|
111
|
+
options.concurrency ?? Infinity,
|
|
112
|
+
items.length
|
|
113
|
+
)
|
|
114
|
+
try {
|
|
115
|
+
await Promise.all(
|
|
116
|
+
Array.from({ length: workers }, worker)
|
|
117
|
+
)
|
|
118
|
+
} finally {
|
|
119
|
+
unlink()
|
|
120
|
+
}
|
|
121
|
+
if (token != record.token) {
|
|
122
|
+
return { kind: "stale" }
|
|
123
|
+
}
|
|
124
|
+
if (failure) {
|
|
125
|
+
return {
|
|
126
|
+
index: failure.index,
|
|
127
|
+
items: true,
|
|
128
|
+
kind: "error",
|
|
129
|
+
value: failure.error
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const values = items.map(
|
|
133
|
+
(_, index) => done[index]?.value
|
|
134
|
+
)
|
|
135
|
+
if (!done.some(item => item?.failed) && !failed_sub_keys(record).length) {
|
|
136
|
+
record.items = void 0
|
|
137
|
+
}
|
|
138
|
+
return { kind: "done", value: values }
|
|
100
139
|
/**
|
|
101
140
|
* @param {unknown} error
|
|
102
141
|
* @param {number | undefined} index
|
|
@@ -108,10 +147,13 @@ export async function run_each(c, record, token) {
|
|
|
108
147
|
new CancelError("Another item failed")
|
|
109
148
|
)
|
|
110
149
|
}
|
|
150
|
+
/** @returns {Promise<void>} */
|
|
111
151
|
async function worker() {
|
|
112
152
|
while (!failure && next < items.length) {
|
|
113
153
|
const index = next++
|
|
114
|
-
if (done[index])
|
|
154
|
+
if (done[index]) {
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
115
157
|
const item = items[index]
|
|
116
158
|
const key = String(index)
|
|
117
159
|
const outcome = await run_attempts(
|
|
@@ -143,7 +185,9 @@ export async function run_each(c, record, token) {
|
|
|
143
185
|
)
|
|
144
186
|
)
|
|
145
187
|
)
|
|
146
|
-
if (token != record.token || outcome.kind == "stale")
|
|
188
|
+
if (token != record.token || outcome.kind == "stale") {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
147
191
|
if (outcome.kind == "done") {
|
|
148
192
|
if (is_sentinel(outcome.value)) {
|
|
149
193
|
stop(
|
|
@@ -172,14 +216,20 @@ export async function run_each(c, record, token) {
|
|
|
172
216
|
record.runs.get(key)
|
|
173
217
|
)
|
|
174
218
|
)
|
|
175
|
-
if (token != record.token)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
)
|
|
219
|
+
if (token != record.token) {
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
if (is_sentinel(value)) {
|
|
223
|
+
throw TypeError(
|
|
224
|
+
`The catch of flow node "${name}" cannot return goto() or skip() for an item`
|
|
225
|
+
)
|
|
226
|
+
}
|
|
179
227
|
c.errors[name + "." + key] = outcome.value
|
|
180
228
|
done[index] = { value }
|
|
181
229
|
} catch (catch_error) {
|
|
182
|
-
if (token == record.token)
|
|
230
|
+
if (token == record.token) {
|
|
231
|
+
stop(catch_error, index)
|
|
232
|
+
}
|
|
183
233
|
return
|
|
184
234
|
}
|
|
185
235
|
} else if (options.optional) {
|
|
@@ -191,29 +241,6 @@ export async function run_each(c, record, token) {
|
|
|
191
241
|
}
|
|
192
242
|
}
|
|
193
243
|
}
|
|
194
|
-
const workers = Math.min(
|
|
195
|
-
options.concurrency ?? Infinity,
|
|
196
|
-
items.length
|
|
197
|
-
)
|
|
198
|
-
try {
|
|
199
|
-
await Promise.all(
|
|
200
|
-
Array.from({ length: workers }, worker)
|
|
201
|
-
)
|
|
202
|
-
} finally {
|
|
203
|
-
unlink()
|
|
204
|
-
}
|
|
205
|
-
if (token != record.token) return { kind: "stale" }
|
|
206
|
-
if (failure) return {
|
|
207
|
-
index: failure.index,
|
|
208
|
-
items: true,
|
|
209
|
-
kind: "error",
|
|
210
|
-
value: failure.error
|
|
211
|
-
}
|
|
212
|
-
const values = items.map(
|
|
213
|
-
(_, index) => done[index]?.value
|
|
214
|
-
)
|
|
215
|
-
if (!done.some(item => item?.failed) && !failed_sub_keys(record).length) record.items = void 0
|
|
216
|
-
return { kind: "done", value: values }
|
|
217
244
|
}
|
|
218
245
|
/**
|
|
219
246
|
* @param {unknown} source
|
|
@@ -221,13 +248,19 @@ export async function run_each(c, record, token) {
|
|
|
221
248
|
* @returns {unknown[] | Promise<unknown[]>}
|
|
222
249
|
*/
|
|
223
250
|
export function to_items(source, signal) {
|
|
224
|
-
if (source == null)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
251
|
+
if (source == null) {
|
|
252
|
+
return []
|
|
253
|
+
}
|
|
254
|
+
if (typeof /** @type {Partial<Iterable<unknown>>} */(source)/**/[Symbol.iterator] == "function") {
|
|
255
|
+
return Array.from(
|
|
256
|
+
/** @type {Iterable<unknown>} */(source)/**/
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
if (typeof /** @type {Partial<AsyncIterable<unknown>>} */(source)/**/[Symbol.asyncIterator] != "function") {
|
|
260
|
+
throw TypeError(
|
|
261
|
+
"flow.each() needs an iterable or an async iterable as the first dependency"
|
|
262
|
+
)
|
|
263
|
+
}
|
|
231
264
|
return collect_items(
|
|
232
265
|
/** @type {AsyncIterable<unknown>} */(source)/**/[Symbol.asyncIterator](),
|
|
233
266
|
signal
|