brass-runtime 1.13.8 → 1.14.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/dist/agent/cli/main.cjs +43 -43
- package/dist/agent/cli/main.js +2 -2
- package/dist/agent/cli/main.mjs +2 -2
- package/dist/agent/index.cjs +3 -3
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +2 -2
- package/dist/agent/index.mjs +2 -2
- package/dist/chunk-4N2JEK4H.mjs +3897 -0
- package/dist/chunk-BKBFSOGT.cjs +3897 -0
- package/dist/{chunk-XNOTJSMZ.mjs → chunk-BMRF4FN6.js} +268 -8
- package/dist/chunk-JT7D6M5H.js +3897 -0
- package/dist/{chunk-3R7ZYRK2.mjs → chunk-MQF7HZ7Y.mjs} +1 -1
- package/dist/chunk-SKVY72E5.cjs +667 -0
- package/dist/{chunk-ATHSSDUF.js → chunk-UWMMYKVK.mjs} +268 -8
- package/dist/{chunk-INZBKOHY.js → chunk-WJESVBWN.js} +1 -1
- package/dist/{chunk-XDINDYNA.cjs → chunk-XTMZTVIT.cjs} +134 -134
- package/dist/{effect-ISvXPLgc.d.ts → effect-DM56H743.d.ts} +191 -21
- package/dist/http/index.cjs +808 -140
- package/dist/http/index.d.ts +181 -8
- package/dist/http/index.js +793 -125
- package/dist/http/index.mjs +793 -125
- package/dist/index.cjs +1785 -137
- package/dist/index.d.ts +979 -36
- package/dist/index.js +1675 -27
- package/dist/index.mjs +1675 -27
- package/dist/stream-Oqe6WeLE.d.ts +173 -0
- package/package.json +1 -1
- package/wasm/pkg/brass_runtime_wasm_engine.d.ts +95 -16
- package/wasm/pkg/brass_runtime_wasm_engine.js +715 -15
- package/wasm/pkg/brass_runtime_wasm_engine_bg.wasm +0 -0
- package/wasm/pkg/brass_runtime_wasm_engine_bg.wasm.d.ts +78 -7
- package/dist/chunk-2P4PD6D7.cjs +0 -2557
- package/dist/chunk-7F2R7A2V.mjs +0 -2557
- package/dist/chunk-L6KKKM66.js +0 -2557
- package/dist/chunk-ZTDK2DLG.cjs +0 -407
- package/dist/stream-BvukHxCv.d.ts +0 -66
package/dist/chunk-7F2R7A2V.mjs
DELETED
|
@@ -1,2557 +0,0 @@
|
|
|
1
|
-
// src/core/types/asyncEffect.ts
|
|
2
|
-
var Async = {
|
|
3
|
-
succeed: (value) => ({ _tag: "Succeed", value }),
|
|
4
|
-
fail: (error) => ({ _tag: "Fail", error }),
|
|
5
|
-
sync: (thunk) => ({ _tag: "Sync", thunk }),
|
|
6
|
-
async: (register) => ({ _tag: "Async", register })
|
|
7
|
-
};
|
|
8
|
-
function asyncFold(fa, onFailure, onSuccess) {
|
|
9
|
-
return { _tag: "Fold", first: fa, onFailure, onSuccess };
|
|
10
|
-
}
|
|
11
|
-
function asyncCatchAll(fa, handler) {
|
|
12
|
-
return asyncFold(
|
|
13
|
-
fa,
|
|
14
|
-
(e) => handler(e),
|
|
15
|
-
(a) => asyncSucceed(a)
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
function asyncMapError(fa, f) {
|
|
19
|
-
return asyncFold(
|
|
20
|
-
fa,
|
|
21
|
-
(e) => asyncFail(f(e)),
|
|
22
|
-
(a) => asyncSucceed(a)
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
var SUCCEED_UNDEFINED = { _tag: "Succeed", value: void 0 };
|
|
26
|
-
var SUCCEED_TRUE = { _tag: "Succeed", value: true };
|
|
27
|
-
var SUCCEED_FALSE = { _tag: "Succeed", value: false };
|
|
28
|
-
var SUCCEED_NULL = { _tag: "Succeed", value: null };
|
|
29
|
-
var unit = () => SUCCEED_UNDEFINED;
|
|
30
|
-
var asyncSucceed = (value) => {
|
|
31
|
-
if (value === void 0) return SUCCEED_UNDEFINED;
|
|
32
|
-
if (value === true) return SUCCEED_TRUE;
|
|
33
|
-
if (value === false) return SUCCEED_FALSE;
|
|
34
|
-
if (value === null) return SUCCEED_NULL;
|
|
35
|
-
return { _tag: "Succeed", value };
|
|
36
|
-
};
|
|
37
|
-
var asyncFail = (error) => ({
|
|
38
|
-
_tag: "Fail",
|
|
39
|
-
error
|
|
40
|
-
});
|
|
41
|
-
var asyncSync = (thunk) => ({
|
|
42
|
-
_tag: "Sync",
|
|
43
|
-
thunk
|
|
44
|
-
});
|
|
45
|
-
var asyncTotal = (thunk) => asyncSync(() => thunk());
|
|
46
|
-
var async = (register) => ({
|
|
47
|
-
_tag: "Async",
|
|
48
|
-
register
|
|
49
|
-
});
|
|
50
|
-
function asyncMap(fa, f) {
|
|
51
|
-
return asyncFlatMap(fa, (a) => asyncSucceed(f(a)));
|
|
52
|
-
}
|
|
53
|
-
function asyncFlatMap(fa, f) {
|
|
54
|
-
return {
|
|
55
|
-
_tag: "FlatMap",
|
|
56
|
-
first: fa,
|
|
57
|
-
andThen: f
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function acquireRelease(acquire, release, scope) {
|
|
61
|
-
return asyncFlatMap(acquire, (resource) => {
|
|
62
|
-
scope.addFinalizer((exit) => release(resource, exit));
|
|
63
|
-
return asyncSucceed(resource);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
function asyncInterruptible(register) {
|
|
67
|
-
return async(register);
|
|
68
|
-
}
|
|
69
|
-
var withAsyncPromise = (run) => (eff) => {
|
|
70
|
-
const anyEff = eff;
|
|
71
|
-
if (!anyEff.toPromise) {
|
|
72
|
-
anyEff.toPromise = (env) => run(eff, env);
|
|
73
|
-
anyEff.unsafeRunPromise = () => run(eff, {});
|
|
74
|
-
}
|
|
75
|
-
return anyEff;
|
|
76
|
-
};
|
|
77
|
-
var mapAsync = (fa, f) => asyncFlatMap(fa, (a) => asyncSucceed(f(a)));
|
|
78
|
-
var mapTryAsync = (fa, f) => asyncFlatMap(fa, (a) => asyncSync(() => f(a)));
|
|
79
|
-
|
|
80
|
-
// src/core/types/option.ts
|
|
81
|
-
var none = { _tag: "None" };
|
|
82
|
-
var some = (value) => ({ _tag: "Some", value });
|
|
83
|
-
|
|
84
|
-
// src/core/types/effect.ts
|
|
85
|
-
var Cause = {
|
|
86
|
-
fail: (error) => ({ _tag: "Fail", error }),
|
|
87
|
-
interrupt: () => ({ _tag: "Interrupt" }),
|
|
88
|
-
die: (defect) => ({ _tag: "Die", defect })
|
|
89
|
-
};
|
|
90
|
-
var Exit = {
|
|
91
|
-
succeed: (value) => ({
|
|
92
|
-
_tag: "Success",
|
|
93
|
-
value
|
|
94
|
-
}),
|
|
95
|
-
failCause: (cause) => ({
|
|
96
|
-
_tag: "Failure",
|
|
97
|
-
cause
|
|
98
|
-
})
|
|
99
|
-
};
|
|
100
|
-
var succeed = (value) => asyncSucceed(value);
|
|
101
|
-
var fail = (error) => asyncFail(error);
|
|
102
|
-
var sync = (thunk) => asyncSync((env) => thunk(env));
|
|
103
|
-
var map = (fa, f) => asyncMap(fa, f);
|
|
104
|
-
var flatMap = (fa, f) => asyncFlatMap(fa, (a) => f(a));
|
|
105
|
-
var mapError = (fa, f) => asyncMapError(fa, f);
|
|
106
|
-
var catchAll = (fa, handler) => asyncFold(
|
|
107
|
-
fa,
|
|
108
|
-
(e) => handler(e),
|
|
109
|
-
(a) => asyncSucceed(a)
|
|
110
|
-
);
|
|
111
|
-
function orElseOptional(fa, that) {
|
|
112
|
-
return asyncFold(
|
|
113
|
-
fa,
|
|
114
|
-
(opt) => opt._tag === "Some" ? asyncFail(opt) : that(),
|
|
115
|
-
(a) => asyncSucceed(a)
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
var end = () => fail(none);
|
|
119
|
-
|
|
120
|
-
// src/core/runtime/ringBuffer.ts
|
|
121
|
-
var PushStatus = /* @__PURE__ */ ((PushStatus3) => {
|
|
122
|
-
PushStatus3[PushStatus3["Ok"] = 0] = "Ok";
|
|
123
|
-
PushStatus3[PushStatus3["Grew"] = 1] = "Grew";
|
|
124
|
-
PushStatus3[PushStatus3["Dropped"] = 2] = "Dropped";
|
|
125
|
-
return PushStatus3;
|
|
126
|
-
})(PushStatus || {});
|
|
127
|
-
var RingBuffer = class {
|
|
128
|
-
buf;
|
|
129
|
-
head = 0;
|
|
130
|
-
tail = 0;
|
|
131
|
-
size_ = 0;
|
|
132
|
-
// nuevo
|
|
133
|
-
maxCap;
|
|
134
|
-
constructor(initialCapacity = 1024, maxCapacity = initialCapacity) {
|
|
135
|
-
let initPow = Math.max(2, initialCapacity);
|
|
136
|
-
initPow--;
|
|
137
|
-
initPow |= initPow >>> 1;
|
|
138
|
-
initPow |= initPow >>> 2;
|
|
139
|
-
initPow |= initPow >>> 4;
|
|
140
|
-
initPow |= initPow >>> 8;
|
|
141
|
-
initPow |= initPow >>> 16;
|
|
142
|
-
initPow++;
|
|
143
|
-
let maxPow = Math.max(initPow, maxCapacity);
|
|
144
|
-
maxPow--;
|
|
145
|
-
maxPow |= maxPow >>> 1;
|
|
146
|
-
maxPow |= maxPow >>> 2;
|
|
147
|
-
maxPow |= maxPow >>> 4;
|
|
148
|
-
maxPow |= maxPow >>> 8;
|
|
149
|
-
maxPow |= maxPow >>> 16;
|
|
150
|
-
maxPow++;
|
|
151
|
-
this.buf = new Array(initPow);
|
|
152
|
-
this.maxCap = maxPow;
|
|
153
|
-
}
|
|
154
|
-
get length() {
|
|
155
|
-
return this.size_;
|
|
156
|
-
}
|
|
157
|
-
get capacity() {
|
|
158
|
-
return this.buf.length;
|
|
159
|
-
}
|
|
160
|
-
isEmpty() {
|
|
161
|
-
return this.size_ === 0;
|
|
162
|
-
}
|
|
163
|
-
push(value) {
|
|
164
|
-
if (this.size_ === this.buf.length) {
|
|
165
|
-
if (this.buf.length >= this.maxCap) {
|
|
166
|
-
return 2 /* Dropped */;
|
|
167
|
-
}
|
|
168
|
-
this.grow();
|
|
169
|
-
this.buf[this.tail] = value;
|
|
170
|
-
this.tail = this.tail + 1 & this.buf.length - 1;
|
|
171
|
-
this.size_++;
|
|
172
|
-
return 0 /* Ok */ | 1 /* Grew */;
|
|
173
|
-
}
|
|
174
|
-
this.buf[this.tail] = value;
|
|
175
|
-
this.tail = this.tail + 1 & this.buf.length - 1;
|
|
176
|
-
this.size_++;
|
|
177
|
-
return 0 /* Ok */;
|
|
178
|
-
}
|
|
179
|
-
shift() {
|
|
180
|
-
if (this.size_ === 0) return void 0;
|
|
181
|
-
const value = this.buf[this.head];
|
|
182
|
-
this.buf[this.head] = void 0;
|
|
183
|
-
this.head = this.head + 1 & this.buf.length - 1;
|
|
184
|
-
this.size_--;
|
|
185
|
-
return value;
|
|
186
|
-
}
|
|
187
|
-
clear() {
|
|
188
|
-
this.head = 0;
|
|
189
|
-
this.tail = 0;
|
|
190
|
-
this.size_ = 0;
|
|
191
|
-
}
|
|
192
|
-
grow() {
|
|
193
|
-
const old = this.buf;
|
|
194
|
-
const nextLen = Math.min(old.length * 2, this.maxCap);
|
|
195
|
-
const newBuf = new Array(nextLen);
|
|
196
|
-
for (let i = 0; i < this.size_; i++) {
|
|
197
|
-
newBuf[i] = old[this.head + i & old.length - 1];
|
|
198
|
-
}
|
|
199
|
-
this.buf = newBuf;
|
|
200
|
-
this.head = 0;
|
|
201
|
-
this.tail = this.size_;
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
// src/core/runtime/wasmModule.ts
|
|
206
|
-
import { createRequire } from "module";
|
|
207
|
-
var cachedWasmModule;
|
|
208
|
-
var cachedWasmModuleErrors = [];
|
|
209
|
-
function resolveWasmModule(options = {}) {
|
|
210
|
-
if (!options.fresh && options.modulePath == null && cachedWasmModule !== void 0) {
|
|
211
|
-
return cachedWasmModule;
|
|
212
|
-
}
|
|
213
|
-
const req = getBestRequire();
|
|
214
|
-
const candidates = wasmModuleCandidates(options.modulePath);
|
|
215
|
-
const errors = [];
|
|
216
|
-
if (!req) {
|
|
217
|
-
errors.push("no CommonJS-compatible require/createRequire was available");
|
|
218
|
-
return remember(options, null, errors);
|
|
219
|
-
}
|
|
220
|
-
for (const candidate of candidates) {
|
|
221
|
-
try {
|
|
222
|
-
return remember(options, req(candidate), errors);
|
|
223
|
-
} catch (error) {
|
|
224
|
-
errors.push(`${candidate}: ${formatError(error)}`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return remember(options, null, errors);
|
|
228
|
-
}
|
|
229
|
-
function wasmModuleResolutionErrors() {
|
|
230
|
-
return cachedWasmModuleErrors.slice();
|
|
231
|
-
}
|
|
232
|
-
function wasmModuleCandidates(modulePath) {
|
|
233
|
-
if (modulePath) return [modulePath];
|
|
234
|
-
return [
|
|
235
|
-
"brass-runtime/wasm/pkg/brass_runtime_wasm_engine.js",
|
|
236
|
-
"../wasm/pkg/brass_runtime_wasm_engine.js",
|
|
237
|
-
"../../../wasm/pkg/brass_runtime_wasm_engine.js",
|
|
238
|
-
"../../../../../wasm/pkg/brass_runtime_wasm_engine.js",
|
|
239
|
-
`${getCwd()}/wasm/pkg/brass_runtime_wasm_engine.js`
|
|
240
|
-
];
|
|
241
|
-
}
|
|
242
|
-
function remember(options, value, errors) {
|
|
243
|
-
if (!options.fresh && options.modulePath == null) {
|
|
244
|
-
cachedWasmModule = value;
|
|
245
|
-
cachedWasmModuleErrors = errors;
|
|
246
|
-
}
|
|
247
|
-
return value;
|
|
248
|
-
}
|
|
249
|
-
function getBestRequire() {
|
|
250
|
-
return getNonWebpackRequire() ?? getRuntimeRequire() ?? getCreateRequire();
|
|
251
|
-
}
|
|
252
|
-
function getNonWebpackRequire() {
|
|
253
|
-
try {
|
|
254
|
-
return (0, eval)(
|
|
255
|
-
"typeof __non_webpack_require__ === 'function' ? __non_webpack_require__ : undefined"
|
|
256
|
-
);
|
|
257
|
-
} catch {
|
|
258
|
-
return void 0;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
function getRuntimeRequire() {
|
|
262
|
-
try {
|
|
263
|
-
return (0, eval)(
|
|
264
|
-
"typeof require === 'function' ? require : undefined"
|
|
265
|
-
);
|
|
266
|
-
} catch {
|
|
267
|
-
return void 0;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
function getCreateRequire() {
|
|
271
|
-
try {
|
|
272
|
-
return createRequire(`${getCwd()}/package.json`);
|
|
273
|
-
} catch {
|
|
274
|
-
return void 0;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
function getCwd() {
|
|
278
|
-
try {
|
|
279
|
-
return (0, eval)(
|
|
280
|
-
"typeof process !== 'undefined' ? process.cwd() : ''"
|
|
281
|
-
);
|
|
282
|
-
} catch {
|
|
283
|
-
return "";
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
function formatError(error) {
|
|
287
|
-
return error instanceof Error ? error.message : String(error);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// src/core/runtime/boundedRingBuffer.ts
|
|
291
|
-
var cachedWasmCtor;
|
|
292
|
-
function resolveWasmRingBuffer() {
|
|
293
|
-
if (cachedWasmCtor !== void 0) return cachedWasmCtor;
|
|
294
|
-
const mod = resolveWasmModule();
|
|
295
|
-
cachedWasmCtor = mod?.BrassWasmRingBuffer ?? null;
|
|
296
|
-
return cachedWasmCtor;
|
|
297
|
-
}
|
|
298
|
-
var WasmRingBuffer = class {
|
|
299
|
-
engine = "wasm";
|
|
300
|
-
fallbackUsed = false;
|
|
301
|
-
inner;
|
|
302
|
-
pushes = 0;
|
|
303
|
-
shifts = 0;
|
|
304
|
-
clears = 0;
|
|
305
|
-
dropped = 0;
|
|
306
|
-
constructor(initialCapacity, maxCapacity) {
|
|
307
|
-
const Ctor = resolveWasmRingBuffer();
|
|
308
|
-
if (!Ctor) {
|
|
309
|
-
throw new Error("brass-runtime wasm ring buffer is not available. Run npm run build:wasm first.");
|
|
310
|
-
}
|
|
311
|
-
this.inner = new Ctor(initialCapacity, maxCapacity);
|
|
312
|
-
}
|
|
313
|
-
get length() {
|
|
314
|
-
return this.inner.len();
|
|
315
|
-
}
|
|
316
|
-
get capacity() {
|
|
317
|
-
return this.inner.capacity();
|
|
318
|
-
}
|
|
319
|
-
isEmpty() {
|
|
320
|
-
return this.inner.is_empty();
|
|
321
|
-
}
|
|
322
|
-
push(value) {
|
|
323
|
-
this.pushes++;
|
|
324
|
-
const status = this.inner.push(value);
|
|
325
|
-
if ((status & 2) !== 0) this.dropped++;
|
|
326
|
-
return status;
|
|
327
|
-
}
|
|
328
|
-
shift() {
|
|
329
|
-
const value = this.inner.shift();
|
|
330
|
-
if (value !== void 0) this.shifts++;
|
|
331
|
-
return value === void 0 ? void 0 : value;
|
|
332
|
-
}
|
|
333
|
-
clear() {
|
|
334
|
-
this.clears++;
|
|
335
|
-
this.inner.clear();
|
|
336
|
-
}
|
|
337
|
-
stats() {
|
|
338
|
-
return {
|
|
339
|
-
engine: "wasm",
|
|
340
|
-
fallbackUsed: false,
|
|
341
|
-
data: {
|
|
342
|
-
len: this.length,
|
|
343
|
-
capacity: this.capacity,
|
|
344
|
-
pushes: this.pushes,
|
|
345
|
-
shifts: this.shifts,
|
|
346
|
-
clears: this.clears,
|
|
347
|
-
dropped: this.dropped
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
var JsBoundedRingBuffer = class {
|
|
353
|
-
constructor(inner, fallbackUsed) {
|
|
354
|
-
this.inner = inner;
|
|
355
|
-
this.fallbackUsed = fallbackUsed;
|
|
356
|
-
}
|
|
357
|
-
inner;
|
|
358
|
-
fallbackUsed;
|
|
359
|
-
engine = "js";
|
|
360
|
-
pushes = 0;
|
|
361
|
-
shifts = 0;
|
|
362
|
-
clears = 0;
|
|
363
|
-
dropped = 0;
|
|
364
|
-
get length() {
|
|
365
|
-
return this.inner.length;
|
|
366
|
-
}
|
|
367
|
-
get capacity() {
|
|
368
|
-
return this.inner.capacity;
|
|
369
|
-
}
|
|
370
|
-
isEmpty() {
|
|
371
|
-
return this.inner.isEmpty();
|
|
372
|
-
}
|
|
373
|
-
push(value) {
|
|
374
|
-
this.pushes++;
|
|
375
|
-
const status = this.inner.push(value);
|
|
376
|
-
if ((status & 2) !== 0) this.dropped++;
|
|
377
|
-
return status;
|
|
378
|
-
}
|
|
379
|
-
shift() {
|
|
380
|
-
const value = this.inner.shift();
|
|
381
|
-
if (value !== void 0) this.shifts++;
|
|
382
|
-
return value;
|
|
383
|
-
}
|
|
384
|
-
clear() {
|
|
385
|
-
this.clears++;
|
|
386
|
-
this.inner.clear();
|
|
387
|
-
}
|
|
388
|
-
stats() {
|
|
389
|
-
return {
|
|
390
|
-
engine: "js",
|
|
391
|
-
fallbackUsed: this.fallbackUsed,
|
|
392
|
-
data: {
|
|
393
|
-
len: this.length,
|
|
394
|
-
capacity: this.capacity,
|
|
395
|
-
pushes: this.pushes,
|
|
396
|
-
shifts: this.shifts,
|
|
397
|
-
clears: this.clears,
|
|
398
|
-
dropped: this.dropped
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
};
|
|
403
|
-
function makeBoundedRingBuffer(initialCapacity, maxCapacity = initialCapacity, options = {}) {
|
|
404
|
-
const engine = options.engine ?? "auto";
|
|
405
|
-
if (engine === "js") {
|
|
406
|
-
return new JsBoundedRingBuffer(new RingBuffer(initialCapacity, maxCapacity), false);
|
|
407
|
-
}
|
|
408
|
-
if (engine === "wasm") {
|
|
409
|
-
return new WasmRingBuffer(initialCapacity, maxCapacity);
|
|
410
|
-
}
|
|
411
|
-
const Ctor = resolveWasmRingBuffer();
|
|
412
|
-
if (Ctor) {
|
|
413
|
-
return new WasmRingBuffer(initialCapacity, maxCapacity);
|
|
414
|
-
}
|
|
415
|
-
return new JsBoundedRingBuffer(new RingBuffer(initialCapacity, maxCapacity), true);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// src/core/runtime/scheduler.ts
|
|
419
|
-
var FLUSH_BUDGET = 2048;
|
|
420
|
-
var MICRO_THRESHOLD = 4096;
|
|
421
|
-
var SCHEDULER_QUEUE_CAPACITY = 8192;
|
|
422
|
-
var scheduleMacro = (() => {
|
|
423
|
-
if (typeof globalThis.setImmediate === "function") {
|
|
424
|
-
return (f) => globalThis.setImmediate(f);
|
|
425
|
-
}
|
|
426
|
-
if (typeof globalThis.MessageChannel === "function") {
|
|
427
|
-
const ch = new globalThis.MessageChannel();
|
|
428
|
-
let cb = null;
|
|
429
|
-
ch.port1.onmessage = () => {
|
|
430
|
-
const f = cb;
|
|
431
|
-
cb = null;
|
|
432
|
-
f?.();
|
|
433
|
-
};
|
|
434
|
-
return (f) => {
|
|
435
|
-
cb = f;
|
|
436
|
-
ch.port2.postMessage(0);
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
return (f) => setTimeout(f, 0);
|
|
440
|
-
})();
|
|
441
|
-
function resolveWasmScheduler() {
|
|
442
|
-
const mod = resolveWasmModule();
|
|
443
|
-
return mod?.BrassWasmSchedulerStateMachine ?? null;
|
|
444
|
-
}
|
|
445
|
-
var JsSchedulerState = class {
|
|
446
|
-
tasks;
|
|
447
|
-
tags;
|
|
448
|
-
flushing = false;
|
|
449
|
-
scheduled = false;
|
|
450
|
-
scheduledFlushes = 0;
|
|
451
|
-
completedFlushes = 0;
|
|
452
|
-
enqueuedTasks = 0;
|
|
453
|
-
executedTasks = 0;
|
|
454
|
-
droppedTasks = 0;
|
|
455
|
-
yieldedByBudget = 0;
|
|
456
|
-
constructor(options) {
|
|
457
|
-
const initial = options.initialCapacity ?? 1024;
|
|
458
|
-
const max = options.maxCapacity ?? initial;
|
|
459
|
-
this.tasks = makeBoundedRingBuffer(initial, max, { engine: options.engine });
|
|
460
|
-
this.tags = makeBoundedRingBuffer(initial, max, { engine: options.engine });
|
|
461
|
-
}
|
|
462
|
-
};
|
|
463
|
-
var WasmSchedulerState = class {
|
|
464
|
-
machine;
|
|
465
|
-
nextRef = 1;
|
|
466
|
-
tasks = /* @__PURE__ */ new Map();
|
|
467
|
-
tags = /* @__PURE__ */ new Map();
|
|
468
|
-
constructor(options) {
|
|
469
|
-
const Ctor = resolveWasmScheduler();
|
|
470
|
-
if (!Ctor) throw new Error("brass-runtime wasm scheduler is not available. Run npm run build:wasm first.");
|
|
471
|
-
this.machine = new Ctor(
|
|
472
|
-
options.initialCapacity ?? 1024,
|
|
473
|
-
options.maxCapacity ?? SCHEDULER_QUEUE_CAPACITY,
|
|
474
|
-
options.flushBudget ?? FLUSH_BUDGET,
|
|
475
|
-
options.microThreshold ?? MICRO_THRESHOLD
|
|
476
|
-
);
|
|
477
|
-
}
|
|
478
|
-
enqueue(task, tag) {
|
|
479
|
-
const ref = this.nextRef++;
|
|
480
|
-
this.tasks.set(ref, task);
|
|
481
|
-
this.tags.set(ref, tag);
|
|
482
|
-
const policy = this.machine.enqueue(ref);
|
|
483
|
-
if (policy === 3) {
|
|
484
|
-
this.tasks.delete(ref);
|
|
485
|
-
this.tags.delete(ref);
|
|
486
|
-
}
|
|
487
|
-
return policy;
|
|
488
|
-
}
|
|
489
|
-
beginFlush() {
|
|
490
|
-
return this.machine.begin_flush();
|
|
491
|
-
}
|
|
492
|
-
shift() {
|
|
493
|
-
const ref = this.machine.shift();
|
|
494
|
-
if (ref === 0) return void 0;
|
|
495
|
-
const task = this.tasks.get(ref);
|
|
496
|
-
const tag = this.tags.get(ref) ?? "anonymous";
|
|
497
|
-
this.tasks.delete(ref);
|
|
498
|
-
this.tags.delete(ref);
|
|
499
|
-
return task ? { task, tag } : void 0;
|
|
500
|
-
}
|
|
501
|
-
endFlush(ran) {
|
|
502
|
-
return this.machine.end_flush(ran);
|
|
503
|
-
}
|
|
504
|
-
get length() {
|
|
505
|
-
return this.machine.len();
|
|
506
|
-
}
|
|
507
|
-
stats() {
|
|
508
|
-
return { engine: "wasm", fallbackUsed: false, data: JSON.parse(this.machine.stats_json()) };
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
var Scheduler = class {
|
|
512
|
-
engine;
|
|
513
|
-
js;
|
|
514
|
-
wasm;
|
|
515
|
-
flushBudget;
|
|
516
|
-
microThreshold;
|
|
517
|
-
fallbackUsed;
|
|
518
|
-
// Cached flush closure — avoids creating a new closure on every requestFlush
|
|
519
|
-
boundFlush = () => this.flush();
|
|
520
|
-
constructor(options = {}) {
|
|
521
|
-
this.flushBudget = options.flushBudget ?? FLUSH_BUDGET;
|
|
522
|
-
this.microThreshold = options.microThreshold ?? MICRO_THRESHOLD;
|
|
523
|
-
const requested = options.engine ?? "js";
|
|
524
|
-
if (requested === "wasm") {
|
|
525
|
-
this.wasm = new WasmSchedulerState(options);
|
|
526
|
-
this.engine = "wasm";
|
|
527
|
-
this.fallbackUsed = false;
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
if (requested === "auto" && resolveWasmScheduler()) {
|
|
531
|
-
this.wasm = new WasmSchedulerState(options);
|
|
532
|
-
this.engine = "wasm";
|
|
533
|
-
this.fallbackUsed = false;
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
this.js = new JsSchedulerState({ ...options, engine: "js" });
|
|
537
|
-
this.engine = "js";
|
|
538
|
-
this.fallbackUsed = requested === "auto";
|
|
539
|
-
}
|
|
540
|
-
schedule(task, tag = "anonymous") {
|
|
541
|
-
if (typeof task !== "function") return;
|
|
542
|
-
if (this.wasm) return this.scheduleWasm(task, tag);
|
|
543
|
-
this.scheduleJs(task, tag);
|
|
544
|
-
}
|
|
545
|
-
stats() {
|
|
546
|
-
if (this.wasm) return this.wasm.stats();
|
|
547
|
-
const js = this.js;
|
|
548
|
-
return {
|
|
549
|
-
engine: "js",
|
|
550
|
-
fallbackUsed: this.fallbackUsed,
|
|
551
|
-
data: {
|
|
552
|
-
len: js.tasks.length,
|
|
553
|
-
capacity: js.tasks.capacity,
|
|
554
|
-
phase: js.flushing ? "flushing" : js.scheduled ? "scheduled" : "idle",
|
|
555
|
-
scheduledFlushes: js.scheduledFlushes,
|
|
556
|
-
completedFlushes: js.completedFlushes,
|
|
557
|
-
enqueuedTasks: js.enqueuedTasks,
|
|
558
|
-
executedTasks: js.executedTasks,
|
|
559
|
-
droppedTasks: js.droppedTasks,
|
|
560
|
-
yieldedByBudget: js.yieldedByBudget
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
scheduleWasm(task, tag) {
|
|
565
|
-
const policy = this.wasm.enqueue(task, tag);
|
|
566
|
-
if (policy === 0) this.requestFlush("micro");
|
|
567
|
-
else if (policy === 1) this.requestFlush("macro");
|
|
568
|
-
}
|
|
569
|
-
scheduleJs(task, tag) {
|
|
570
|
-
const js = this.js;
|
|
571
|
-
const taskStatus = js.tasks.push(task);
|
|
572
|
-
const tagStatus = js.tags.push(tag);
|
|
573
|
-
js.enqueuedTasks++;
|
|
574
|
-
if ((taskStatus & 2) !== 0 || (tagStatus & 2) !== 0) js.droppedTasks++;
|
|
575
|
-
if (js.flushing) return;
|
|
576
|
-
if (!js.scheduled) {
|
|
577
|
-
js.scheduled = true;
|
|
578
|
-
js.scheduledFlushes++;
|
|
579
|
-
const kind = js.tasks.length > this.microThreshold ? "macro" : "micro";
|
|
580
|
-
this.requestFlush(kind);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
requestFlush(kind) {
|
|
584
|
-
if (kind === "micro") queueMicrotask(this.boundFlush);
|
|
585
|
-
else scheduleMacro(this.boundFlush);
|
|
586
|
-
}
|
|
587
|
-
flush() {
|
|
588
|
-
if (this.wasm) return this.flushWasm();
|
|
589
|
-
this.flushJs();
|
|
590
|
-
}
|
|
591
|
-
flushWasm() {
|
|
592
|
-
const wasm = this.wasm;
|
|
593
|
-
const budget = wasm.beginFlush();
|
|
594
|
-
if (budget <= 0) return;
|
|
595
|
-
let ran = 0;
|
|
596
|
-
try {
|
|
597
|
-
while (ran < budget) {
|
|
598
|
-
const next = wasm.shift();
|
|
599
|
-
if (!next) break;
|
|
600
|
-
ran++;
|
|
601
|
-
try {
|
|
602
|
-
next.task();
|
|
603
|
-
} catch (e) {
|
|
604
|
-
console.error(`[Scheduler] task threw (tag=${next.tag})`, e);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
} finally {
|
|
608
|
-
const policy = wasm.endFlush(ran);
|
|
609
|
-
if (policy === 0) this.requestFlush("micro");
|
|
610
|
-
else if (policy === 1) this.requestFlush("macro");
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
flushJs() {
|
|
614
|
-
const js = this.js;
|
|
615
|
-
if (js.flushing) return;
|
|
616
|
-
js.flushing = true;
|
|
617
|
-
js.scheduled = false;
|
|
618
|
-
let ran = 0;
|
|
619
|
-
try {
|
|
620
|
-
while (ran < this.flushBudget) {
|
|
621
|
-
const task = js.tasks.shift();
|
|
622
|
-
if (!task) break;
|
|
623
|
-
const tag = js.tags.shift();
|
|
624
|
-
ran++;
|
|
625
|
-
js.executedTasks++;
|
|
626
|
-
try {
|
|
627
|
-
task();
|
|
628
|
-
} catch (e) {
|
|
629
|
-
console.error(`[Scheduler] task threw (tag=${tag})`, e);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
} finally {
|
|
633
|
-
js.flushing = false;
|
|
634
|
-
js.completedFlushes++;
|
|
635
|
-
if (js.tasks.length > 0 && !js.scheduled) {
|
|
636
|
-
js.scheduled = true;
|
|
637
|
-
js.scheduledFlushes++;
|
|
638
|
-
const kind = ran >= this.flushBudget || js.tasks.length > this.microThreshold ? "macro" : "micro";
|
|
639
|
-
if (ran >= this.flushBudget) js.yieldedByBudget++;
|
|
640
|
-
this.requestFlush(kind);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
};
|
|
645
|
-
var globalScheduler = new Scheduler();
|
|
646
|
-
|
|
647
|
-
// src/core/runtime/contex.ts
|
|
648
|
-
var emptyContext = { parent: null, patch: /* @__PURE__ */ Object.create(null) };
|
|
649
|
-
|
|
650
|
-
// src/core/runtime/forkPolicy.ts
|
|
651
|
-
function makeForkPolicy(env, hooks) {
|
|
652
|
-
const svc = resolveForkServices(env);
|
|
653
|
-
return {
|
|
654
|
-
initChild(fiber, parent, scopeId) {
|
|
655
|
-
const parentCtx = parent?.fiberContext;
|
|
656
|
-
const trace = forkTrace(svc, parentCtx?.trace ?? null);
|
|
657
|
-
fiber.fiberContext = {
|
|
658
|
-
log: parentCtx?.log ?? emptyContext,
|
|
659
|
-
trace
|
|
660
|
-
};
|
|
661
|
-
fiber.parentFiberId = parent?.id;
|
|
662
|
-
fiber.name = svc.childName(parent?.name);
|
|
663
|
-
if (scopeId !== void 0) fiber.scopeId = scopeId;
|
|
664
|
-
hooks.emit(
|
|
665
|
-
{
|
|
666
|
-
type: "fiber.start",
|
|
667
|
-
fiberId: fiber.id,
|
|
668
|
-
parentFiberId: parent?.id,
|
|
669
|
-
scopeId: fiber.scopeId,
|
|
670
|
-
// ✅ ahora viaja
|
|
671
|
-
name: fiber.name
|
|
672
|
-
},
|
|
673
|
-
{ fiberId: parent?.id, traceId: parentCtx?.trace?.traceId, spanId: parentCtx?.trace?.spanId }
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
function resolveForkServices(env) {
|
|
679
|
-
const defaultTracer = {
|
|
680
|
-
newTraceId: () => randomRuntimeId("trace"),
|
|
681
|
-
newSpanId: () => randomRuntimeId("span")
|
|
682
|
-
};
|
|
683
|
-
const brass = env?.brass;
|
|
684
|
-
const tracer = brass?.tracer ?? defaultTracer;
|
|
685
|
-
const seed = brass?.traceSeed;
|
|
686
|
-
const childName = brass?.childName ?? ((p) => p ? `${p}/child` : void 0);
|
|
687
|
-
return { tracer, seed, childName };
|
|
688
|
-
}
|
|
689
|
-
function randomRuntimeId(prefix) {
|
|
690
|
-
const cryptoLike = globalThis.crypto;
|
|
691
|
-
if (typeof cryptoLike?.randomUUID === "function") {
|
|
692
|
-
return cryptoLike.randomUUID();
|
|
693
|
-
}
|
|
694
|
-
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
695
|
-
}
|
|
696
|
-
function forkTrace(svc, parentTrace) {
|
|
697
|
-
if (parentTrace) {
|
|
698
|
-
return {
|
|
699
|
-
traceId: parentTrace.traceId,
|
|
700
|
-
spanId: svc.tracer.newSpanId(),
|
|
701
|
-
parentSpanId: parentTrace.spanId,
|
|
702
|
-
sampled: parentTrace.sampled
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
if (svc.seed) return { ...svc.seed };
|
|
706
|
-
return { traceId: svc.tracer.newTraceId(), spanId: svc.tracer.newSpanId(), sampled: true };
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// src/core/runtime/hostAction.ts
|
|
710
|
-
var DefaultHostExecutor = {
|
|
711
|
-
async execute(action) {
|
|
712
|
-
return {
|
|
713
|
-
kind: "error",
|
|
714
|
-
actionId: action.actionId,
|
|
715
|
-
error: new Error(`No HostExecutor configured for HostAction kind=${action.kind} target=${action.target}`)
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
// src/core/runtime/engine/JsFiberEngine.ts
|
|
721
|
-
var JsFiberEngine = class {
|
|
722
|
-
constructor(runtime) {
|
|
723
|
-
this.runtime = runtime;
|
|
724
|
-
}
|
|
725
|
-
runtime;
|
|
726
|
-
kind = "js";
|
|
727
|
-
startedFibers = 0;
|
|
728
|
-
fork(effect, scopeId) {
|
|
729
|
-
this.startedFibers += 1;
|
|
730
|
-
const fiber = new RuntimeFiber(this.runtime, effect);
|
|
731
|
-
if (scopeId !== void 0) fiber.scopeId = scopeId;
|
|
732
|
-
return fiber;
|
|
733
|
-
}
|
|
734
|
-
stats() {
|
|
735
|
-
return {
|
|
736
|
-
engine: this.kind,
|
|
737
|
-
startedFibers: this.startedFibers,
|
|
738
|
-
runningFibers: 0,
|
|
739
|
-
suspendedFibers: 0,
|
|
740
|
-
queuedFibers: 0,
|
|
741
|
-
completedFibers: 0,
|
|
742
|
-
failedFibers: 0,
|
|
743
|
-
interruptedFibers: 0,
|
|
744
|
-
pendingHostEffects: 0
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
};
|
|
748
|
-
|
|
749
|
-
// src/core/runtime/engine/opcodes.ts
|
|
750
|
-
var HostRegistry = class {
|
|
751
|
-
nextRef = 1;
|
|
752
|
-
refs = /* @__PURE__ */ new Map();
|
|
753
|
-
register(value) {
|
|
754
|
-
const ref = this.nextRef++;
|
|
755
|
-
this.refs.set(ref, value);
|
|
756
|
-
return ref;
|
|
757
|
-
}
|
|
758
|
-
get(ref) {
|
|
759
|
-
if (!this.refs.has(ref)) {
|
|
760
|
-
throw new Error(`Missing host registry ref ${ref}`);
|
|
761
|
-
}
|
|
762
|
-
return this.refs.get(ref);
|
|
763
|
-
}
|
|
764
|
-
set(ref, value) {
|
|
765
|
-
this.refs.set(ref, value);
|
|
766
|
-
}
|
|
767
|
-
delete(ref) {
|
|
768
|
-
this.refs.delete(ref);
|
|
769
|
-
}
|
|
770
|
-
clear() {
|
|
771
|
-
this.refs.clear();
|
|
772
|
-
}
|
|
773
|
-
size() {
|
|
774
|
-
return this.refs.size;
|
|
775
|
-
}
|
|
776
|
-
};
|
|
777
|
-
var ProgramBuilder = class {
|
|
778
|
-
nodes = [];
|
|
779
|
-
registry = new HostRegistry();
|
|
780
|
-
compile(effect) {
|
|
781
|
-
const root = this.visit(effect);
|
|
782
|
-
return {
|
|
783
|
-
program: {
|
|
784
|
-
version: 1,
|
|
785
|
-
root,
|
|
786
|
-
nodes: this.nodes
|
|
787
|
-
},
|
|
788
|
-
registry: this.registry
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
append(effect) {
|
|
792
|
-
const previousNodes = this.nodes.length;
|
|
793
|
-
const root = this.visit(effect);
|
|
794
|
-
return {
|
|
795
|
-
root,
|
|
796
|
-
nodes: this.nodes.slice(previousNodes)
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
add(node) {
|
|
800
|
-
const id = this.nodes.length;
|
|
801
|
-
this.nodes.push(node);
|
|
802
|
-
return id;
|
|
803
|
-
}
|
|
804
|
-
visit(effect) {
|
|
805
|
-
const current = effect;
|
|
806
|
-
switch (current._tag) {
|
|
807
|
-
case "Succeed":
|
|
808
|
-
return this.add({ tag: "Succeed", valueRef: this.registry.register(current.value) });
|
|
809
|
-
case "Fail":
|
|
810
|
-
return this.add({ tag: "Fail", errorRef: this.registry.register(current.error) });
|
|
811
|
-
case "Sync":
|
|
812
|
-
return this.add({ tag: "Sync", fnRef: this.registry.register(current.thunk) });
|
|
813
|
-
case "Async":
|
|
814
|
-
return this.add({ tag: "Async", registerRef: this.registry.register(current.register) });
|
|
815
|
-
case "FlatMap":
|
|
816
|
-
return this.add({
|
|
817
|
-
tag: "FlatMap",
|
|
818
|
-
first: this.visit(current.first),
|
|
819
|
-
fnRef: this.registry.register(current.andThen)
|
|
820
|
-
});
|
|
821
|
-
case "Fold":
|
|
822
|
-
return this.add({
|
|
823
|
-
tag: "Fold",
|
|
824
|
-
first: this.visit(current.first),
|
|
825
|
-
onFailureRef: this.registry.register(current.onFailure),
|
|
826
|
-
onSuccessRef: this.registry.register(current.onSuccess)
|
|
827
|
-
});
|
|
828
|
-
case "Fork": {
|
|
829
|
-
const base = {
|
|
830
|
-
tag: "Fork",
|
|
831
|
-
effectRef: this.registry.register(current.effect)
|
|
832
|
-
};
|
|
833
|
-
return this.add(current.scopeId === void 0 ? base : { ...base, scopeId: current.scopeId });
|
|
834
|
-
}
|
|
835
|
-
case "HostAction": {
|
|
836
|
-
const base = {
|
|
837
|
-
tag: "HostAction",
|
|
838
|
-
actionRef: this.registry.register(current.action)
|
|
839
|
-
};
|
|
840
|
-
return this.add(current.decode === void 0 ? base : { ...base, decodeRef: this.registry.register(current.decode) });
|
|
841
|
-
}
|
|
842
|
-
default:
|
|
843
|
-
return this.add({ tag: "Fail", errorRef: this.registry.register(new Error(`Unknown Async opcode: ${current?._tag}`)) });
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
};
|
|
847
|
-
|
|
848
|
-
// src/core/runtime/engine/FiberHandleImpl.ts
|
|
849
|
-
var EngineFiberHandle = class {
|
|
850
|
-
constructor(id, runtime, onScheduledStep, onInterrupt, onJoiner, onQueued) {
|
|
851
|
-
this.onScheduledStep = onScheduledStep;
|
|
852
|
-
this.onInterrupt = onInterrupt;
|
|
853
|
-
this.onJoiner = onJoiner;
|
|
854
|
-
this.onQueued = onQueued;
|
|
855
|
-
this.id = id;
|
|
856
|
-
this.runtime = runtime;
|
|
857
|
-
}
|
|
858
|
-
onScheduledStep;
|
|
859
|
-
onInterrupt;
|
|
860
|
-
onJoiner;
|
|
861
|
-
onQueued;
|
|
862
|
-
id;
|
|
863
|
-
runtime;
|
|
864
|
-
fiberContext;
|
|
865
|
-
name;
|
|
866
|
-
scopeId;
|
|
867
|
-
parentFiberId;
|
|
868
|
-
result = null;
|
|
869
|
-
joiners = [];
|
|
870
|
-
finalizers = [];
|
|
871
|
-
finalizersDrained = false;
|
|
872
|
-
internalStatus = "running";
|
|
873
|
-
queued = false;
|
|
874
|
-
status() {
|
|
875
|
-
if (this.result == null) return "Running";
|
|
876
|
-
if (this.result._tag === "Failure" && this.result.cause._tag === "Interrupt") return "Interrupted";
|
|
877
|
-
return "Done";
|
|
878
|
-
}
|
|
879
|
-
engineStatus() {
|
|
880
|
-
return this.internalStatus;
|
|
881
|
-
}
|
|
882
|
-
setEngineStatus(status) {
|
|
883
|
-
this.internalStatus = status;
|
|
884
|
-
}
|
|
885
|
-
join(cb) {
|
|
886
|
-
if (this.result != null) cb(this.result);
|
|
887
|
-
else {
|
|
888
|
-
this.onJoiner?.(this.id);
|
|
889
|
-
this.joiners.push(cb);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
interrupt() {
|
|
893
|
-
if (this.result != null) return;
|
|
894
|
-
this.onInterrupt(this.id, Cause.interrupt());
|
|
895
|
-
}
|
|
896
|
-
addFinalizer(f) {
|
|
897
|
-
this.finalizers.push(f);
|
|
898
|
-
}
|
|
899
|
-
schedule(tag = "step") {
|
|
900
|
-
if (this.result != null || this.queued) return;
|
|
901
|
-
this.queued = true;
|
|
902
|
-
this.internalStatus = "queued";
|
|
903
|
-
this.onQueued?.(this.id);
|
|
904
|
-
this.runtime.scheduler.schedule(() => {
|
|
905
|
-
this.queued = false;
|
|
906
|
-
if (this.result != null) return;
|
|
907
|
-
this.onScheduledStep(this.id);
|
|
908
|
-
}, `wasm-fiber#${this.id}.${tag}`);
|
|
909
|
-
}
|
|
910
|
-
emit(ev) {
|
|
911
|
-
this.runtime.hooks.emit(ev, {
|
|
912
|
-
fiberId: this.id,
|
|
913
|
-
scopeId: this.scopeId,
|
|
914
|
-
traceId: this.fiberContext?.trace?.traceId,
|
|
915
|
-
spanId: this.fiberContext?.trace?.spanId
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
succeed(value) {
|
|
919
|
-
this.complete(Exit.succeed(value));
|
|
920
|
-
}
|
|
921
|
-
fail(error) {
|
|
922
|
-
this.complete(Exit.failCause(Cause.fail(error)));
|
|
923
|
-
}
|
|
924
|
-
die(defect) {
|
|
925
|
-
this.complete(Exit.failCause(Cause.die(defect)));
|
|
926
|
-
}
|
|
927
|
-
interrupted() {
|
|
928
|
-
this.complete(Exit.failCause(Cause.interrupt()));
|
|
929
|
-
}
|
|
930
|
-
complete(exit) {
|
|
931
|
-
if (this.result != null) return;
|
|
932
|
-
this.runFinalizersOnce(exit);
|
|
933
|
-
this.result = exit;
|
|
934
|
-
this.internalStatus = exit._tag === "Success" ? "done" : exit.cause._tag === "Interrupt" ? "interrupted" : "failed";
|
|
935
|
-
const status = exit._tag === "Success" ? "success" : exit.cause._tag === "Interrupt" ? "interrupted" : "failure";
|
|
936
|
-
this.emit({
|
|
937
|
-
type: "fiber.end",
|
|
938
|
-
fiberId: this.id,
|
|
939
|
-
status,
|
|
940
|
-
error: exit._tag === "Failure" ? exit.cause : void 0
|
|
941
|
-
});
|
|
942
|
-
for (const joiner of this.joiners) joiner(exit);
|
|
943
|
-
this.joiners.length = 0;
|
|
944
|
-
}
|
|
945
|
-
runFinalizersOnce(exit) {
|
|
946
|
-
if (this.finalizersDrained) return;
|
|
947
|
-
this.finalizersDrained = true;
|
|
948
|
-
while (this.finalizers.length > 0) {
|
|
949
|
-
const finalizer = this.finalizers.pop();
|
|
950
|
-
try {
|
|
951
|
-
const eff = finalizer(exit);
|
|
952
|
-
if (eff && typeof eff === "object" && "_tag" in eff) {
|
|
953
|
-
this.runtime.fork(eff);
|
|
954
|
-
}
|
|
955
|
-
} catch {
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
};
|
|
960
|
-
|
|
961
|
-
// src/core/runtime/engine/bridge/ReferenceWasmBridge.ts
|
|
962
|
-
var ReferenceWasmBridge = class {
|
|
963
|
-
kind = "wasm-reference";
|
|
964
|
-
nextFiberId = 1;
|
|
965
|
-
fibers = /* @__PURE__ */ new Map();
|
|
966
|
-
started = 0;
|
|
967
|
-
completed = 0;
|
|
968
|
-
failed = 0;
|
|
969
|
-
interrupted = 0;
|
|
970
|
-
createFiber(program) {
|
|
971
|
-
const id = this.nextFiberId++;
|
|
972
|
-
this.started += 1;
|
|
973
|
-
this.fibers.set(id, {
|
|
974
|
-
id,
|
|
975
|
-
program: cloneProgram(program),
|
|
976
|
-
current: program.root,
|
|
977
|
-
stack: [],
|
|
978
|
-
status: "running",
|
|
979
|
-
lastEvent: void 0
|
|
980
|
-
});
|
|
981
|
-
return id;
|
|
982
|
-
}
|
|
983
|
-
poll(fiberId) {
|
|
984
|
-
const fiber = this.mustFiber(fiberId);
|
|
985
|
-
if (fiber.status === "suspended" && fiber.lastEvent) return fiber.lastEvent;
|
|
986
|
-
return this.step(fiber);
|
|
987
|
-
}
|
|
988
|
-
provideValue(fiberId, valueRef) {
|
|
989
|
-
const fiber = this.mustFiber(fiberId);
|
|
990
|
-
fiber.status = "running";
|
|
991
|
-
fiber.lastEvent = void 0;
|
|
992
|
-
return this.success(fiber, valueRef);
|
|
993
|
-
}
|
|
994
|
-
provideError(fiberId, errorRef) {
|
|
995
|
-
const fiber = this.mustFiber(fiberId);
|
|
996
|
-
fiber.status = "running";
|
|
997
|
-
fiber.lastEvent = void 0;
|
|
998
|
-
return this.failure(fiber, errorRef);
|
|
999
|
-
}
|
|
1000
|
-
provideEffect(fiberId, root, nodes) {
|
|
1001
|
-
const fiber = this.mustFiber(fiberId);
|
|
1002
|
-
fiber.status = "running";
|
|
1003
|
-
fiber.lastEvent = void 0;
|
|
1004
|
-
fiber.program.nodes.push(...nodes.map((node) => ({ ...node })));
|
|
1005
|
-
fiber.current = root;
|
|
1006
|
-
return this.step(fiber);
|
|
1007
|
-
}
|
|
1008
|
-
interrupt(fiberId, reasonRef) {
|
|
1009
|
-
const fiber = this.mustFiber(fiberId);
|
|
1010
|
-
fiber.status = "interrupted";
|
|
1011
|
-
fiber.current = void 0;
|
|
1012
|
-
fiber.stack = [];
|
|
1013
|
-
this.interrupted += 1;
|
|
1014
|
-
const event = { kind: "Interrupted", fiberId, reasonRef };
|
|
1015
|
-
fiber.lastEvent = event;
|
|
1016
|
-
return event;
|
|
1017
|
-
}
|
|
1018
|
-
dropFiber(fiberId) {
|
|
1019
|
-
this.fibers.delete(fiberId);
|
|
1020
|
-
}
|
|
1021
|
-
stats() {
|
|
1022
|
-
let running = 0;
|
|
1023
|
-
let suspended = 0;
|
|
1024
|
-
for (const fiber of this.fibers.values()) {
|
|
1025
|
-
if (fiber.status === "running") running += 1;
|
|
1026
|
-
if (fiber.status === "suspended") suspended += 1;
|
|
1027
|
-
}
|
|
1028
|
-
return {
|
|
1029
|
-
started: this.started,
|
|
1030
|
-
live: this.fibers.size,
|
|
1031
|
-
running,
|
|
1032
|
-
suspended,
|
|
1033
|
-
completed: this.completed,
|
|
1034
|
-
failed: this.failed,
|
|
1035
|
-
interrupted: this.interrupted
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
mustFiber(fiberId) {
|
|
1039
|
-
const fiber = this.fibers.get(fiberId);
|
|
1040
|
-
if (!fiber) throw new Error(`Fiber ${fiberId} not found`);
|
|
1041
|
-
return fiber;
|
|
1042
|
-
}
|
|
1043
|
-
step(fiber) {
|
|
1044
|
-
while (true) {
|
|
1045
|
-
if (fiber.status === "interrupted") {
|
|
1046
|
-
const event = fiber.lastEvent ?? { kind: "Interrupted", fiberId: fiber.id, reasonRef: 0 };
|
|
1047
|
-
fiber.lastEvent = event;
|
|
1048
|
-
return event;
|
|
1049
|
-
}
|
|
1050
|
-
if (fiber.current === void 0) return this.markFailed(fiber, 0);
|
|
1051
|
-
const node = fiber.program.nodes[fiber.current];
|
|
1052
|
-
if (!node) return this.markFailed(fiber, 0);
|
|
1053
|
-
switch (node.tag) {
|
|
1054
|
-
case "Succeed":
|
|
1055
|
-
fiber.current = void 0;
|
|
1056
|
-
return this.success(fiber, node.valueRef);
|
|
1057
|
-
case "Fail":
|
|
1058
|
-
fiber.current = void 0;
|
|
1059
|
-
return this.failure(fiber, node.errorRef);
|
|
1060
|
-
case "Sync":
|
|
1061
|
-
return this.suspend(fiber, { kind: "InvokeSync", fiberId: fiber.id, fnRef: node.fnRef });
|
|
1062
|
-
case "Async":
|
|
1063
|
-
return this.suspend(fiber, { kind: "InvokeAsync", fiberId: fiber.id, registerRef: node.registerRef });
|
|
1064
|
-
case "FlatMap":
|
|
1065
|
-
fiber.stack.push({ tag: "SuccessCont", fnRef: node.fnRef });
|
|
1066
|
-
fiber.current = node.first;
|
|
1067
|
-
continue;
|
|
1068
|
-
case "Fold":
|
|
1069
|
-
fiber.stack.push({ tag: "FoldCont", onFailureRef: node.onFailureRef, onSuccessRef: node.onSuccessRef });
|
|
1070
|
-
fiber.current = node.first;
|
|
1071
|
-
continue;
|
|
1072
|
-
case "Fork": {
|
|
1073
|
-
const event = node.scopeId === void 0 ? { kind: "InvokeFork", fiberId: fiber.id, effectRef: node.effectRef } : { kind: "InvokeFork", fiberId: fiber.id, effectRef: node.effectRef, scopeId: node.scopeId };
|
|
1074
|
-
return this.suspend(fiber, event);
|
|
1075
|
-
}
|
|
1076
|
-
case "HostAction": {
|
|
1077
|
-
const event = node.decodeRef === void 0 ? { kind: "InvokeHostAction", fiberId: fiber.id, actionRef: node.actionRef } : { kind: "InvokeHostAction", fiberId: fiber.id, actionRef: node.actionRef, decodeRef: node.decodeRef };
|
|
1078
|
-
return this.suspend(fiber, event);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
success(fiber, valueRef) {
|
|
1084
|
-
const frame = fiber.stack.pop();
|
|
1085
|
-
if (!frame) return this.markDone(fiber, valueRef);
|
|
1086
|
-
if (frame.tag === "SuccessCont") {
|
|
1087
|
-
return this.suspend(fiber, { kind: "InvokeFlatMap", fiberId: fiber.id, fnRef: frame.fnRef, valueRef });
|
|
1088
|
-
}
|
|
1089
|
-
return this.suspend(fiber, { kind: "InvokeFoldSuccess", fiberId: fiber.id, fnRef: frame.onSuccessRef, valueRef });
|
|
1090
|
-
}
|
|
1091
|
-
failure(fiber, errorRef) {
|
|
1092
|
-
while (fiber.stack.length > 0) {
|
|
1093
|
-
const frame = fiber.stack.pop();
|
|
1094
|
-
if (frame.tag === "FoldCont") {
|
|
1095
|
-
return this.suspend(fiber, { kind: "InvokeFoldFailure", fiberId: fiber.id, fnRef: frame.onFailureRef, errorRef });
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
return this.markFailed(fiber, errorRef);
|
|
1099
|
-
}
|
|
1100
|
-
suspend(fiber, event) {
|
|
1101
|
-
fiber.status = "suspended";
|
|
1102
|
-
fiber.lastEvent = event;
|
|
1103
|
-
return event;
|
|
1104
|
-
}
|
|
1105
|
-
markDone(fiber, valueRef) {
|
|
1106
|
-
fiber.status = "done";
|
|
1107
|
-
fiber.current = void 0;
|
|
1108
|
-
this.completed += 1;
|
|
1109
|
-
const event = { kind: "Done", fiberId: fiber.id, valueRef };
|
|
1110
|
-
fiber.lastEvent = event;
|
|
1111
|
-
return event;
|
|
1112
|
-
}
|
|
1113
|
-
markFailed(fiber, errorRef) {
|
|
1114
|
-
fiber.status = "failed";
|
|
1115
|
-
fiber.current = void 0;
|
|
1116
|
-
this.failed += 1;
|
|
1117
|
-
const event = { kind: "Failed", fiberId: fiber.id, errorRef };
|
|
1118
|
-
fiber.lastEvent = event;
|
|
1119
|
-
return event;
|
|
1120
|
-
}
|
|
1121
|
-
};
|
|
1122
|
-
function cloneProgram(program) {
|
|
1123
|
-
return {
|
|
1124
|
-
version: program.version,
|
|
1125
|
-
root: program.root,
|
|
1126
|
-
nodes: program.nodes.map((node) => ({ ...node }))
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// src/core/runtime/engine/bridge/WasmPackFiberBridge.ts
|
|
1131
|
-
var WasmPackFiberBridge = class {
|
|
1132
|
-
kind = "wasm";
|
|
1133
|
-
vm;
|
|
1134
|
-
constructor(modulePath) {
|
|
1135
|
-
const mod = loadWasmModule(modulePath);
|
|
1136
|
-
this.vm = new mod.BrassWasmVm();
|
|
1137
|
-
}
|
|
1138
|
-
createFiber(program) {
|
|
1139
|
-
return this.vm.create_fiber(JSON.stringify(program));
|
|
1140
|
-
}
|
|
1141
|
-
poll(fiberId) {
|
|
1142
|
-
return JSON.parse(this.vm.poll(fiberId));
|
|
1143
|
-
}
|
|
1144
|
-
provideValue(fiberId, valueRef) {
|
|
1145
|
-
return JSON.parse(this.vm.provide_value(fiberId, valueRef));
|
|
1146
|
-
}
|
|
1147
|
-
provideError(fiberId, errorRef) {
|
|
1148
|
-
return JSON.parse(this.vm.provide_error(fiberId, errorRef));
|
|
1149
|
-
}
|
|
1150
|
-
provideEffect(fiberId, root, nodes) {
|
|
1151
|
-
return JSON.parse(this.vm.provide_effect(fiberId, root, JSON.stringify(nodes)));
|
|
1152
|
-
}
|
|
1153
|
-
interrupt(fiberId, reasonRef) {
|
|
1154
|
-
return JSON.parse(this.vm.interrupt(fiberId, reasonRef));
|
|
1155
|
-
}
|
|
1156
|
-
dropFiber(fiberId) {
|
|
1157
|
-
this.vm.drop_fiber(fiberId);
|
|
1158
|
-
}
|
|
1159
|
-
stats() {
|
|
1160
|
-
return JSON.parse(this.vm.stats_json());
|
|
1161
|
-
}
|
|
1162
|
-
};
|
|
1163
|
-
function loadWasmModule(modulePath) {
|
|
1164
|
-
const mod = resolveWasmModule({ modulePath });
|
|
1165
|
-
if (mod) return mod;
|
|
1166
|
-
const errors = wasmModuleResolutionErrors();
|
|
1167
|
-
throw new Error([
|
|
1168
|
-
"engine='wasm' could not load wasm/pkg/brass_runtime_wasm_engine.js.",
|
|
1169
|
-
"Run `npm run build:wasm` first and make sure wasm/pkg is present in the package.",
|
|
1170
|
-
"For Node 18 + webpack, keep brass-runtime's wasm/pkg files available at runtime or externalize brass-runtime.",
|
|
1171
|
-
...errors.map((error) => `- ${error}`)
|
|
1172
|
-
].join("\n"));
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
// src/core/runtime/engine/bridge/WasmFiberRegistryBridge.ts
|
|
1176
|
-
var FIBER_STATE_QUEUED = 0;
|
|
1177
|
-
var FIBER_STATE_RUNNING = 1;
|
|
1178
|
-
var FIBER_STATE_SUSPENDED = 2;
|
|
1179
|
-
var FIBER_STATE_DONE = 3;
|
|
1180
|
-
var FIBER_STATE_FAILED = 4;
|
|
1181
|
-
var FIBER_STATE_INTERRUPTED = 5;
|
|
1182
|
-
var WasmFiberRegistryBridge = class {
|
|
1183
|
-
registry;
|
|
1184
|
-
constructor() {
|
|
1185
|
-
const mod = resolveWasmModule();
|
|
1186
|
-
const Ctor = mod?.BrassWasmFiberRegistry;
|
|
1187
|
-
if (!Ctor) {
|
|
1188
|
-
throw new Error("brass-runtime wasm fiber registry is not available. Run npm run build:wasm first.");
|
|
1189
|
-
}
|
|
1190
|
-
this.registry = new Ctor();
|
|
1191
|
-
}
|
|
1192
|
-
registerFiber(fiberId, parentId, scopeId) {
|
|
1193
|
-
this.registry.register_fiber(fiberId, parentId ?? 0, scopeId ?? 0, Date.now());
|
|
1194
|
-
}
|
|
1195
|
-
markQueued(fiberId) {
|
|
1196
|
-
this.registry.mark_queued(fiberId, Date.now());
|
|
1197
|
-
}
|
|
1198
|
-
markRunning(fiberId) {
|
|
1199
|
-
this.registry.mark_running(fiberId, Date.now());
|
|
1200
|
-
}
|
|
1201
|
-
markSuspended(fiberId) {
|
|
1202
|
-
this.registry.mark_suspended(fiberId, Date.now());
|
|
1203
|
-
}
|
|
1204
|
-
markDone(fiberId, status) {
|
|
1205
|
-
return this.registry.mark_done(fiberId, statusToCode(status), Date.now());
|
|
1206
|
-
}
|
|
1207
|
-
dropFiber(fiberId) {
|
|
1208
|
-
this.registry.drop_fiber(fiberId);
|
|
1209
|
-
}
|
|
1210
|
-
addJoiner(fiberId) {
|
|
1211
|
-
this.registry.add_joiner(fiberId);
|
|
1212
|
-
}
|
|
1213
|
-
wake(fiberId) {
|
|
1214
|
-
return this.registry.wake(fiberId);
|
|
1215
|
-
}
|
|
1216
|
-
drainWakeup() {
|
|
1217
|
-
const id = this.registry.drain_wakeup();
|
|
1218
|
-
return id === 0 ? void 0 : id;
|
|
1219
|
-
}
|
|
1220
|
-
drainWakeups() {
|
|
1221
|
-
const ids = [];
|
|
1222
|
-
for (; ; ) {
|
|
1223
|
-
const id = this.drainWakeup();
|
|
1224
|
-
if (id === void 0) return ids;
|
|
1225
|
-
ids.push(id);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
wakeQueueLength() {
|
|
1229
|
-
return this.registry.wake_queue_len();
|
|
1230
|
-
}
|
|
1231
|
-
stateOf(fiberId) {
|
|
1232
|
-
const code = this.registry.state_of(fiberId);
|
|
1233
|
-
if (code === 4294967295) return "missing";
|
|
1234
|
-
return codeToStatus(code);
|
|
1235
|
-
}
|
|
1236
|
-
stats() {
|
|
1237
|
-
return JSON.parse(this.registry.stats_json());
|
|
1238
|
-
}
|
|
1239
|
-
};
|
|
1240
|
-
function statusToCode(status) {
|
|
1241
|
-
switch (status) {
|
|
1242
|
-
case "queued":
|
|
1243
|
-
return FIBER_STATE_QUEUED;
|
|
1244
|
-
case "running":
|
|
1245
|
-
return FIBER_STATE_RUNNING;
|
|
1246
|
-
case "suspended":
|
|
1247
|
-
return FIBER_STATE_SUSPENDED;
|
|
1248
|
-
case "done":
|
|
1249
|
-
return FIBER_STATE_DONE;
|
|
1250
|
-
case "failed":
|
|
1251
|
-
return FIBER_STATE_FAILED;
|
|
1252
|
-
case "interrupted":
|
|
1253
|
-
return FIBER_STATE_INTERRUPTED;
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
function codeToStatus(code) {
|
|
1257
|
-
switch (code) {
|
|
1258
|
-
case FIBER_STATE_QUEUED:
|
|
1259
|
-
return "queued";
|
|
1260
|
-
case FIBER_STATE_RUNNING:
|
|
1261
|
-
return "running";
|
|
1262
|
-
case FIBER_STATE_SUSPENDED:
|
|
1263
|
-
return "suspended";
|
|
1264
|
-
case FIBER_STATE_DONE:
|
|
1265
|
-
return "done";
|
|
1266
|
-
case FIBER_STATE_FAILED:
|
|
1267
|
-
return "failed";
|
|
1268
|
-
case FIBER_STATE_INTERRUPTED:
|
|
1269
|
-
return "interrupted";
|
|
1270
|
-
default:
|
|
1271
|
-
return "missing";
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
// src/core/runtime/engine/WasmFiberEngine.ts
|
|
1276
|
-
var DEFAULT_BUDGET = 4096;
|
|
1277
|
-
var WasmFiberEngine = class {
|
|
1278
|
-
constructor(runtime, options = {}) {
|
|
1279
|
-
this.runtime = runtime;
|
|
1280
|
-
this.bridge = options.bridge ?? (options.reference ? new ReferenceWasmBridge() : new WasmPackFiberBridge(options.modulePath));
|
|
1281
|
-
this.kind = this.bridge.kind;
|
|
1282
|
-
this.fiberRegistry = this.kind === "wasm" ? new WasmFiberRegistryBridge() : void 0;
|
|
1283
|
-
}
|
|
1284
|
-
runtime;
|
|
1285
|
-
kind;
|
|
1286
|
-
bridge;
|
|
1287
|
-
startedFibers = 0;
|
|
1288
|
-
runningFibers = 0;
|
|
1289
|
-
suspendedFibers = 0;
|
|
1290
|
-
completedFibers = 0;
|
|
1291
|
-
failedFibers = 0;
|
|
1292
|
-
interruptedFibers = 0;
|
|
1293
|
-
pendingHostEffects = 0;
|
|
1294
|
-
states = /* @__PURE__ */ new Map();
|
|
1295
|
-
fiberRegistry;
|
|
1296
|
-
fork(effect, scopeId) {
|
|
1297
|
-
const builder = new ProgramBuilder();
|
|
1298
|
-
const compiled = builder.compile(effect);
|
|
1299
|
-
const fiberId = this.bridge.createFiber(compiled.program);
|
|
1300
|
-
const controller = new AbortController();
|
|
1301
|
-
const handle = new EngineFiberHandle(
|
|
1302
|
-
fiberId,
|
|
1303
|
-
this.runtime,
|
|
1304
|
-
(id) => this.scheduleWakeup(id),
|
|
1305
|
-
(id, reason) => this.interruptById(id, reason),
|
|
1306
|
-
(id) => this.fiberRegistry?.addJoiner(id),
|
|
1307
|
-
(id) => this.fiberRegistry?.markQueued(id)
|
|
1308
|
-
);
|
|
1309
|
-
if (scopeId !== void 0) handle.scopeId = scopeId;
|
|
1310
|
-
const state = {
|
|
1311
|
-
fiberId,
|
|
1312
|
-
handle,
|
|
1313
|
-
builder,
|
|
1314
|
-
registry: compiled.registry,
|
|
1315
|
-
controller,
|
|
1316
|
-
startedAt: Date.now(),
|
|
1317
|
-
completed: false,
|
|
1318
|
-
status: "running",
|
|
1319
|
-
pendingCleanups: /* @__PURE__ */ new Set()
|
|
1320
|
-
};
|
|
1321
|
-
this.states.set(fiberId, state);
|
|
1322
|
-
this.fiberRegistry?.registerFiber(fiberId, void 0, scopeId);
|
|
1323
|
-
this.startedFibers += 1;
|
|
1324
|
-
this.runningFibers += 1;
|
|
1325
|
-
return handle;
|
|
1326
|
-
}
|
|
1327
|
-
stats() {
|
|
1328
|
-
let hostRefs = 0;
|
|
1329
|
-
for (const state of this.states.values()) hostRefs += state.registry.size();
|
|
1330
|
-
return {
|
|
1331
|
-
engine: this.kind,
|
|
1332
|
-
startedFibers: this.startedFibers,
|
|
1333
|
-
runningFibers: this.runningFibers,
|
|
1334
|
-
suspendedFibers: this.suspendedFibers,
|
|
1335
|
-
queuedFibers: 0,
|
|
1336
|
-
completedFibers: this.completedFibers,
|
|
1337
|
-
failedFibers: this.failedFibers,
|
|
1338
|
-
interruptedFibers: this.interruptedFibers,
|
|
1339
|
-
pendingHostEffects: this.pendingHostEffects,
|
|
1340
|
-
hostRegistryRefs: hostRefs,
|
|
1341
|
-
wasm: this.bridge.stats(),
|
|
1342
|
-
fiberRegistry: this.fiberRegistry?.stats()
|
|
1343
|
-
};
|
|
1344
|
-
}
|
|
1345
|
-
async shutdown() {
|
|
1346
|
-
for (const state of Array.from(this.states.values())) {
|
|
1347
|
-
this.interruptState(state, Cause.interrupt());
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
scheduleWakeup(fiberId) {
|
|
1351
|
-
const state = this.states.get(fiberId);
|
|
1352
|
-
if (!state || state.completed) return;
|
|
1353
|
-
if (!this.fiberRegistry) {
|
|
1354
|
-
this.driveById(fiberId);
|
|
1355
|
-
return;
|
|
1356
|
-
}
|
|
1357
|
-
if (!this.fiberRegistry.wake(fiberId)) return;
|
|
1358
|
-
this.drainWakeups();
|
|
1359
|
-
}
|
|
1360
|
-
drainWakeups() {
|
|
1361
|
-
if (!this.fiberRegistry) return;
|
|
1362
|
-
for (const fiberId of this.fiberRegistry.drainWakeups()) {
|
|
1363
|
-
this.driveById(fiberId);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
driveById(fiberId) {
|
|
1367
|
-
const state = this.states.get(fiberId);
|
|
1368
|
-
if (!state || state.completed) return;
|
|
1369
|
-
this.fiberRegistry?.markRunning(fiberId);
|
|
1370
|
-
withCurrentFiber(state.handle, () => this.drive(state));
|
|
1371
|
-
}
|
|
1372
|
-
drive(state, initialEvent) {
|
|
1373
|
-
if (state.completed) return;
|
|
1374
|
-
let budget = DEFAULT_BUDGET;
|
|
1375
|
-
let event = initialEvent ?? this.bridge.poll(state.fiberId);
|
|
1376
|
-
state.status = "running";
|
|
1377
|
-
state.handle.setEngineStatus("running");
|
|
1378
|
-
while (!state.completed && budget-- > 0) {
|
|
1379
|
-
try {
|
|
1380
|
-
switch (event.kind) {
|
|
1381
|
-
case "Continue":
|
|
1382
|
-
event = this.bridge.poll(state.fiberId);
|
|
1383
|
-
continue;
|
|
1384
|
-
case "Done": {
|
|
1385
|
-
const value = state.registry.get(event.valueRef);
|
|
1386
|
-
this.completeSuccess(state, value);
|
|
1387
|
-
return;
|
|
1388
|
-
}
|
|
1389
|
-
case "Failed": {
|
|
1390
|
-
const error = event.errorRef === 0 ? new Error("WASM fiber failed") : state.registry.get(event.errorRef);
|
|
1391
|
-
this.completeFailure(state, error);
|
|
1392
|
-
return;
|
|
1393
|
-
}
|
|
1394
|
-
case "Interrupted": {
|
|
1395
|
-
this.completeInterrupted(state);
|
|
1396
|
-
return;
|
|
1397
|
-
}
|
|
1398
|
-
case "InvokeSync": {
|
|
1399
|
-
const fn = state.registry.get(event.fnRef);
|
|
1400
|
-
try {
|
|
1401
|
-
const valueRef = state.registry.register(fn(this.runtime.env));
|
|
1402
|
-
event = this.bridge.provideValue(state.fiberId, valueRef);
|
|
1403
|
-
} catch (error) {
|
|
1404
|
-
event = this.bridge.provideError(state.fiberId, state.registry.register(error));
|
|
1405
|
-
}
|
|
1406
|
-
continue;
|
|
1407
|
-
}
|
|
1408
|
-
case "InvokeFlatMap": {
|
|
1409
|
-
const fn = state.registry.get(event.fnRef);
|
|
1410
|
-
const value = state.registry.get(event.valueRef);
|
|
1411
|
-
try {
|
|
1412
|
-
const next = fn(value);
|
|
1413
|
-
const patch = state.builder.append(next);
|
|
1414
|
-
event = this.bridge.provideEffect(state.fiberId, patch.root, patch.nodes);
|
|
1415
|
-
} catch (error) {
|
|
1416
|
-
event = this.bridge.provideError(state.fiberId, state.registry.register(error));
|
|
1417
|
-
}
|
|
1418
|
-
continue;
|
|
1419
|
-
}
|
|
1420
|
-
case "InvokeFoldFailure": {
|
|
1421
|
-
const fn = state.registry.get(event.fnRef);
|
|
1422
|
-
const errorValue = state.registry.get(event.errorRef);
|
|
1423
|
-
try {
|
|
1424
|
-
const next = fn(errorValue);
|
|
1425
|
-
const patch = state.builder.append(next);
|
|
1426
|
-
event = this.bridge.provideEffect(state.fiberId, patch.root, patch.nodes);
|
|
1427
|
-
} catch (error) {
|
|
1428
|
-
event = this.bridge.provideError(state.fiberId, state.registry.register(error));
|
|
1429
|
-
}
|
|
1430
|
-
continue;
|
|
1431
|
-
}
|
|
1432
|
-
case "InvokeFoldSuccess": {
|
|
1433
|
-
const fn = state.registry.get(event.fnRef);
|
|
1434
|
-
const value = state.registry.get(event.valueRef);
|
|
1435
|
-
try {
|
|
1436
|
-
const next = fn(value);
|
|
1437
|
-
const patch = state.builder.append(next);
|
|
1438
|
-
event = this.bridge.provideEffect(state.fiberId, patch.root, patch.nodes);
|
|
1439
|
-
} catch (error) {
|
|
1440
|
-
event = this.bridge.provideError(state.fiberId, state.registry.register(error));
|
|
1441
|
-
}
|
|
1442
|
-
continue;
|
|
1443
|
-
}
|
|
1444
|
-
case "InvokeFork": {
|
|
1445
|
-
const effect = state.registry.get(event.effectRef);
|
|
1446
|
-
try {
|
|
1447
|
-
const child = this.runtime.fork(effect, event.scopeId);
|
|
1448
|
-
event = this.bridge.provideValue(state.fiberId, state.registry.register(child));
|
|
1449
|
-
} catch (error) {
|
|
1450
|
-
event = this.bridge.provideError(state.fiberId, state.registry.register(error));
|
|
1451
|
-
}
|
|
1452
|
-
continue;
|
|
1453
|
-
}
|
|
1454
|
-
case "InvokeAsync": {
|
|
1455
|
-
this.scheduleAsync(state, event.registerRef);
|
|
1456
|
-
return;
|
|
1457
|
-
}
|
|
1458
|
-
case "InvokeHostAction": {
|
|
1459
|
-
this.scheduleHostAction(state, event.actionRef, event.decodeRef);
|
|
1460
|
-
return;
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
} catch (error) {
|
|
1464
|
-
event = this.bridge.provideError(state.fiberId, state.registry.register(error));
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
if (!state.completed) state.handle.schedule("budget-yield");
|
|
1468
|
-
}
|
|
1469
|
-
scheduleAsync(state, registerRef) {
|
|
1470
|
-
this.markSuspended(state, "async");
|
|
1471
|
-
this.pendingHostEffects += 1;
|
|
1472
|
-
const register = state.registry.get(registerRef);
|
|
1473
|
-
let done = false;
|
|
1474
|
-
let asyncRegistered = false;
|
|
1475
|
-
let syncExit = null;
|
|
1476
|
-
const cleanup = () => {
|
|
1477
|
-
done = true;
|
|
1478
|
-
state.pendingCleanups.delete(cleanup);
|
|
1479
|
-
};
|
|
1480
|
-
state.pendingCleanups.add(cleanup);
|
|
1481
|
-
const cb = (exitLike) => {
|
|
1482
|
-
if (done) return;
|
|
1483
|
-
const exit = exitLike;
|
|
1484
|
-
if (!asyncRegistered) {
|
|
1485
|
-
syncExit = exit;
|
|
1486
|
-
return;
|
|
1487
|
-
}
|
|
1488
|
-
cleanup();
|
|
1489
|
-
this.pendingHostEffects = Math.max(0, this.pendingHostEffects - 1);
|
|
1490
|
-
this.markRunning(state, "async");
|
|
1491
|
-
this.resumeWithExit(state, exit);
|
|
1492
|
-
};
|
|
1493
|
-
try {
|
|
1494
|
-
const canceler = register(this.runtime.env, cb);
|
|
1495
|
-
asyncRegistered = true;
|
|
1496
|
-
if (syncExit) {
|
|
1497
|
-
cleanup();
|
|
1498
|
-
this.pendingHostEffects = Math.max(0, this.pendingHostEffects - 1);
|
|
1499
|
-
this.markRunning(state, "async-sync");
|
|
1500
|
-
this.resumeWithExit(state, syncExit);
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
|
-
if (typeof canceler === "function") {
|
|
1504
|
-
const cancelCleanup = () => {
|
|
1505
|
-
if (done) return;
|
|
1506
|
-
done = true;
|
|
1507
|
-
try {
|
|
1508
|
-
canceler();
|
|
1509
|
-
} catch {
|
|
1510
|
-
}
|
|
1511
|
-
state.pendingCleanups.delete(cancelCleanup);
|
|
1512
|
-
};
|
|
1513
|
-
state.pendingCleanups.add(cancelCleanup);
|
|
1514
|
-
state.handle.addFinalizer(() => cancelCleanup());
|
|
1515
|
-
}
|
|
1516
|
-
} catch (error) {
|
|
1517
|
-
cleanup();
|
|
1518
|
-
this.pendingHostEffects = Math.max(0, this.pendingHostEffects - 1);
|
|
1519
|
-
this.markRunning(state, "async-register-error");
|
|
1520
|
-
this.resumeWithError(state, error);
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
scheduleHostAction(state, actionRef, decodeRef) {
|
|
1524
|
-
this.markSuspended(state, "host-action");
|
|
1525
|
-
this.pendingHostEffects += 1;
|
|
1526
|
-
const action = state.registry.get(actionRef);
|
|
1527
|
-
const startedAt = Date.now();
|
|
1528
|
-
const cleanup = () => {
|
|
1529
|
-
state.pendingCleanups.delete(cleanup);
|
|
1530
|
-
};
|
|
1531
|
-
state.pendingCleanups.add(cleanup);
|
|
1532
|
-
this.runtime.hostExecutor.execute(action, {
|
|
1533
|
-
fiberId: state.fiberId,
|
|
1534
|
-
env: this.runtime.env,
|
|
1535
|
-
signal: state.controller.signal,
|
|
1536
|
-
deadlineAt: action.timeoutMs === void 0 ? void 0 : Date.now() + action.timeoutMs
|
|
1537
|
-
}).then((result) => {
|
|
1538
|
-
cleanup();
|
|
1539
|
-
if (state.completed) return;
|
|
1540
|
-
this.pendingHostEffects = Math.max(0, this.pendingHostEffects - 1);
|
|
1541
|
-
this.markRunning(state, "host-action");
|
|
1542
|
-
try {
|
|
1543
|
-
if (decodeRef !== void 0) {
|
|
1544
|
-
const decode = state.registry.get(decodeRef);
|
|
1545
|
-
this.resumeWithValue(state, decode(result));
|
|
1546
|
-
return;
|
|
1547
|
-
}
|
|
1548
|
-
if (result.kind === "error") {
|
|
1549
|
-
this.resumeWithError(state, result.error);
|
|
1550
|
-
return;
|
|
1551
|
-
}
|
|
1552
|
-
this.resumeWithValue(state, result.value);
|
|
1553
|
-
} catch (error) {
|
|
1554
|
-
this.resumeWithError(state, error);
|
|
1555
|
-
}
|
|
1556
|
-
}).catch((error) => {
|
|
1557
|
-
cleanup();
|
|
1558
|
-
if (state.completed) return;
|
|
1559
|
-
this.pendingHostEffects = Math.max(0, this.pendingHostEffects - 1);
|
|
1560
|
-
this.markRunning(state, "host-action-error");
|
|
1561
|
-
this.resumeWithError(state, error);
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
resumeWithExit(state, exit) {
|
|
1565
|
-
if (state.completed) return;
|
|
1566
|
-
if (exit._tag === "Success") {
|
|
1567
|
-
this.resumeWithValue(state, exit.value);
|
|
1568
|
-
return;
|
|
1569
|
-
}
|
|
1570
|
-
const cause = exit.cause;
|
|
1571
|
-
if (cause._tag === "Interrupt") {
|
|
1572
|
-
this.interruptState(state, cause);
|
|
1573
|
-
return;
|
|
1574
|
-
}
|
|
1575
|
-
if (cause._tag === "Fail") {
|
|
1576
|
-
this.resumeWithError(state, cause.error);
|
|
1577
|
-
return;
|
|
1578
|
-
}
|
|
1579
|
-
this.completeDie(state, cause.defect);
|
|
1580
|
-
}
|
|
1581
|
-
resumeWithValue(state, value) {
|
|
1582
|
-
const event = this.bridge.provideValue(state.fiberId, state.registry.register(value));
|
|
1583
|
-
this.runtime.scheduler.schedule(() => withCurrentFiber(state.handle, () => this.drive(state, event)), `wasm-fiber#${state.fiberId}.resume`);
|
|
1584
|
-
}
|
|
1585
|
-
resumeWithError(state, error) {
|
|
1586
|
-
const event = this.bridge.provideError(state.fiberId, state.registry.register(error));
|
|
1587
|
-
this.runtime.scheduler.schedule(() => withCurrentFiber(state.handle, () => this.drive(state, event)), `wasm-fiber#${state.fiberId}.resume-error`);
|
|
1588
|
-
}
|
|
1589
|
-
interruptById(fiberId, reason) {
|
|
1590
|
-
const state = this.states.get(fiberId);
|
|
1591
|
-
if (!state) return;
|
|
1592
|
-
this.interruptState(state, reason);
|
|
1593
|
-
}
|
|
1594
|
-
interruptState(state, reason) {
|
|
1595
|
-
if (state.completed) return;
|
|
1596
|
-
if (!state.controller.signal.aborted) state.controller.abort(reason);
|
|
1597
|
-
for (const cleanup of Array.from(state.pendingCleanups)) cleanup();
|
|
1598
|
-
const event = this.bridge.interrupt(state.fiberId, state.registry.register(reason));
|
|
1599
|
-
this.drive(state, event);
|
|
1600
|
-
}
|
|
1601
|
-
markSuspended(state, reason) {
|
|
1602
|
-
if (state.status !== "suspended") {
|
|
1603
|
-
this.suspendedFibers += 1;
|
|
1604
|
-
state.status = "suspended";
|
|
1605
|
-
this.fiberRegistry?.markSuspended(state.fiberId);
|
|
1606
|
-
state.handle.setEngineStatus("suspended");
|
|
1607
|
-
state.handle.emit({ type: "fiber.suspend", fiberId: state.fiberId, reason });
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
markRunning(state, _reason) {
|
|
1611
|
-
if (state.status === "suspended") {
|
|
1612
|
-
this.suspendedFibers = Math.max(0, this.suspendedFibers - 1);
|
|
1613
|
-
state.status = "running";
|
|
1614
|
-
this.fiberRegistry?.markRunning(state.fiberId);
|
|
1615
|
-
state.handle.setEngineStatus("running");
|
|
1616
|
-
state.handle.emit({ type: "fiber.resume", fiberId: state.fiberId });
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
completeSuccess(state, value) {
|
|
1620
|
-
if (state.completed) return;
|
|
1621
|
-
state.completed = true;
|
|
1622
|
-
state.status = "done";
|
|
1623
|
-
this.fiberRegistry?.markDone(state.fiberId, "done");
|
|
1624
|
-
this.completedFibers += 1;
|
|
1625
|
-
this.cleanupState(state);
|
|
1626
|
-
state.handle.succeed(value);
|
|
1627
|
-
}
|
|
1628
|
-
completeFailure(state, error) {
|
|
1629
|
-
if (state.completed) return;
|
|
1630
|
-
state.completed = true;
|
|
1631
|
-
state.status = "failed";
|
|
1632
|
-
this.fiberRegistry?.markDone(state.fiberId, "failed");
|
|
1633
|
-
this.failedFibers += 1;
|
|
1634
|
-
this.cleanupState(state);
|
|
1635
|
-
state.handle.fail(error);
|
|
1636
|
-
}
|
|
1637
|
-
completeDie(state, defect) {
|
|
1638
|
-
if (state.completed) return;
|
|
1639
|
-
state.completed = true;
|
|
1640
|
-
state.status = "failed";
|
|
1641
|
-
this.fiberRegistry?.markDone(state.fiberId, "failed");
|
|
1642
|
-
this.failedFibers += 1;
|
|
1643
|
-
this.cleanupState(state);
|
|
1644
|
-
state.handle.die(defect);
|
|
1645
|
-
}
|
|
1646
|
-
completeInterrupted(state) {
|
|
1647
|
-
if (state.completed) return;
|
|
1648
|
-
state.completed = true;
|
|
1649
|
-
state.status = "interrupted";
|
|
1650
|
-
this.fiberRegistry?.markDone(state.fiberId, "interrupted");
|
|
1651
|
-
this.interruptedFibers += 1;
|
|
1652
|
-
this.cleanupState(state);
|
|
1653
|
-
state.handle.interrupted();
|
|
1654
|
-
}
|
|
1655
|
-
cleanupState(state) {
|
|
1656
|
-
this.runningFibers = Math.max(0, this.runningFibers - 1);
|
|
1657
|
-
if (state.status === "suspended") this.suspendedFibers = Math.max(0, this.suspendedFibers - 1);
|
|
1658
|
-
for (const cleanup of Array.from(state.pendingCleanups)) cleanup();
|
|
1659
|
-
state.pendingCleanups.clear();
|
|
1660
|
-
this.bridge.dropFiber(state.fiberId);
|
|
1661
|
-
this.fiberRegistry?.dropFiber(state.fiberId);
|
|
1662
|
-
state.registry.clear();
|
|
1663
|
-
this.states.delete(state.fiberId);
|
|
1664
|
-
}
|
|
1665
|
-
};
|
|
1666
|
-
|
|
1667
|
-
// src/core/runtime/capabilities.ts
|
|
1668
|
-
function runtimeCapabilities() {
|
|
1669
|
-
const mod = resolveWasmModule();
|
|
1670
|
-
return {
|
|
1671
|
-
wasmAvailable: !!mod,
|
|
1672
|
-
wasmFiberEngine: typeof mod?.BrassWasmVm === "function",
|
|
1673
|
-
wasmRingBuffer: typeof mod?.BrassWasmRingBuffer === "function",
|
|
1674
|
-
wasmScheduler: typeof mod?.BrassWasmSchedulerStateMachine === "function",
|
|
1675
|
-
wasmFiberRegistry: typeof mod?.BrassWasmFiberRegistry === "function",
|
|
1676
|
-
wasmStreamChunks: typeof mod?.BrassWasmChunkBuffer === "function"
|
|
1677
|
-
};
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
// src/core/runtime/runtime.ts
|
|
1681
|
-
var NoopHooks = {
|
|
1682
|
-
emit() {
|
|
1683
|
-
}
|
|
1684
|
-
};
|
|
1685
|
-
var Runtime = class _Runtime {
|
|
1686
|
-
env;
|
|
1687
|
-
scheduler;
|
|
1688
|
-
hooks;
|
|
1689
|
-
hostExecutor;
|
|
1690
|
-
engineMode;
|
|
1691
|
-
wasmOptions;
|
|
1692
|
-
fiberEngine;
|
|
1693
|
-
fallbackUsed;
|
|
1694
|
-
forkPolicy;
|
|
1695
|
-
// opcional: registry para observabilidad
|
|
1696
|
-
registry;
|
|
1697
|
-
constructor(args) {
|
|
1698
|
-
this.env = args.env;
|
|
1699
|
-
this.scheduler = args.scheduler ?? globalScheduler;
|
|
1700
|
-
this.hooks = args.hooks ?? NoopHooks;
|
|
1701
|
-
this.hostExecutor = args.hostExecutor ?? DefaultHostExecutor;
|
|
1702
|
-
this.engineMode = args.engine ?? "auto";
|
|
1703
|
-
this.wasmOptions = args.wasm;
|
|
1704
|
-
this.forkPolicy = makeForkPolicy(this.env, this.hooks);
|
|
1705
|
-
const selected = this.makeFiberEngine(this.engineMode, args.wasm);
|
|
1706
|
-
this.fiberEngine = selected.engine;
|
|
1707
|
-
this.fallbackUsed = selected.fallbackUsed;
|
|
1708
|
-
}
|
|
1709
|
-
makeFiberEngine(mode, wasm) {
|
|
1710
|
-
if (mode === "js") return { engine: new JsFiberEngine(this), fallbackUsed: false };
|
|
1711
|
-
if (mode === "wasm-reference") return { engine: new WasmFiberEngine(this, { ...wasm, reference: true }), fallbackUsed: false };
|
|
1712
|
-
if (mode === "wasm") return { engine: new WasmFiberEngine(this, wasm), fallbackUsed: false };
|
|
1713
|
-
try {
|
|
1714
|
-
const capabilities = runtimeCapabilities();
|
|
1715
|
-
if (capabilities.wasmFiberEngine) {
|
|
1716
|
-
return { engine: new WasmFiberEngine(this, wasm), fallbackUsed: false };
|
|
1717
|
-
}
|
|
1718
|
-
} catch {
|
|
1719
|
-
}
|
|
1720
|
-
return { engine: new JsFiberEngine(this), fallbackUsed: true };
|
|
1721
|
-
}
|
|
1722
|
-
/** Returns true when the runtime has real hooks (not the no-op singleton). */
|
|
1723
|
-
hasActiveHooks() {
|
|
1724
|
-
return this.hooks !== NoopHooks;
|
|
1725
|
-
}
|
|
1726
|
-
/** Deriva un runtime con env extendido (estilo provide/locally) */
|
|
1727
|
-
provide(env) {
|
|
1728
|
-
return new _Runtime({
|
|
1729
|
-
env: Object.assign({}, this.env, env),
|
|
1730
|
-
scheduler: this.scheduler,
|
|
1731
|
-
hooks: this.hooks,
|
|
1732
|
-
engine: this.engineMode,
|
|
1733
|
-
hostExecutor: this.hostExecutor,
|
|
1734
|
-
wasm: this.wasmOptions
|
|
1735
|
-
});
|
|
1736
|
-
}
|
|
1737
|
-
emit(ev) {
|
|
1738
|
-
if (this.hooks === NoopHooks) return;
|
|
1739
|
-
const f = getCurrentFiber();
|
|
1740
|
-
const ctx = {
|
|
1741
|
-
fiberId: f?.id,
|
|
1742
|
-
scopeId: f?.scopeId,
|
|
1743
|
-
// ✅ FIX: era f?.scope
|
|
1744
|
-
traceId: f?.fiberContext?.trace?.traceId,
|
|
1745
|
-
spanId: f?.fiberContext?.trace?.spanId
|
|
1746
|
-
};
|
|
1747
|
-
this.hooks.emit(ev, ctx);
|
|
1748
|
-
}
|
|
1749
|
-
/**
|
|
1750
|
-
* ✅ CAMBIO: fork(effect, scopeId?) y pasa scopeId a forkPolicy
|
|
1751
|
-
*/
|
|
1752
|
-
fork(effect, scopeId) {
|
|
1753
|
-
const parent = getCurrentFiber();
|
|
1754
|
-
const fiber = this.fiberEngine.fork(effect, scopeId);
|
|
1755
|
-
if (scopeId !== void 0) fiber.scopeId = scopeId;
|
|
1756
|
-
this.forkPolicy.initChild(fiber, parent, scopeId);
|
|
1757
|
-
fiber.schedule?.("initial-step");
|
|
1758
|
-
return fiber;
|
|
1759
|
-
}
|
|
1760
|
-
stats() {
|
|
1761
|
-
const data = this.fiberEngine.stats();
|
|
1762
|
-
const engine = this.fiberEngine.kind === "js" ? "js" : "wasm";
|
|
1763
|
-
return { engine, fallbackUsed: this.fallbackUsed, data };
|
|
1764
|
-
}
|
|
1765
|
-
capabilities() {
|
|
1766
|
-
return runtimeCapabilities();
|
|
1767
|
-
}
|
|
1768
|
-
shutdown() {
|
|
1769
|
-
return this.fiberEngine.shutdown?.();
|
|
1770
|
-
}
|
|
1771
|
-
unsafeRunAsync(effect, cb) {
|
|
1772
|
-
const fiber = this.fork(effect);
|
|
1773
|
-
fiber.join(cb);
|
|
1774
|
-
}
|
|
1775
|
-
toPromise(effect) {
|
|
1776
|
-
return new Promise((resolve, reject) => {
|
|
1777
|
-
const fiber = this.fork(effect);
|
|
1778
|
-
fiber.join((exit) => {
|
|
1779
|
-
if (exit._tag === "Success") resolve(exit.value);
|
|
1780
|
-
else {
|
|
1781
|
-
const c = exit.cause;
|
|
1782
|
-
if (c?._tag === "Fail") reject(c.error);
|
|
1783
|
-
else reject(new Error("Interrupted"));
|
|
1784
|
-
}
|
|
1785
|
-
});
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1788
|
-
// helper: correr un efecto y “tirar” el resultado
|
|
1789
|
-
unsafeRun(effect) {
|
|
1790
|
-
this.unsafeRunAsync(effect, () => {
|
|
1791
|
-
});
|
|
1792
|
-
}
|
|
1793
|
-
delay(ms, eff) {
|
|
1794
|
-
return async((_env, cb) => {
|
|
1795
|
-
const handle = setTimeout(() => {
|
|
1796
|
-
this.unsafeRunAsync(eff, cb);
|
|
1797
|
-
}, ms);
|
|
1798
|
-
return () => clearTimeout(handle);
|
|
1799
|
-
});
|
|
1800
|
-
}
|
|
1801
|
-
// util para crear runtime default
|
|
1802
|
-
static make(env, scheduler = globalScheduler) {
|
|
1803
|
-
return new _Runtime({ env, scheduler });
|
|
1804
|
-
}
|
|
1805
|
-
static makeWithEngine(env, engine, options = {}) {
|
|
1806
|
-
return new _Runtime({ ...options, env, engine });
|
|
1807
|
-
}
|
|
1808
|
-
/** Convenience logger: emits a RuntimeEvent of type "log". */
|
|
1809
|
-
log(level, message, fields) {
|
|
1810
|
-
this.emit({ type: "log", level, message, fields });
|
|
1811
|
-
}
|
|
1812
|
-
};
|
|
1813
|
-
function fork(effect, env) {
|
|
1814
|
-
return Runtime.make(env ?? {}).fork(effect);
|
|
1815
|
-
}
|
|
1816
|
-
function unsafeRunAsync(effect, env, cb) {
|
|
1817
|
-
Runtime.make(env ?? {}).unsafeRunAsync(effect, cb);
|
|
1818
|
-
}
|
|
1819
|
-
function toPromise(effect, env) {
|
|
1820
|
-
return Runtime.make(env ?? {}).toPromise(effect);
|
|
1821
|
-
}
|
|
1822
|
-
function fromPromiseAbortable(make, onReject) {
|
|
1823
|
-
return {
|
|
1824
|
-
_tag: "Async",
|
|
1825
|
-
register: (env, cb) => {
|
|
1826
|
-
const controller = new AbortController();
|
|
1827
|
-
let done = false;
|
|
1828
|
-
make(controller.signal, env).then((value) => {
|
|
1829
|
-
if (done) return;
|
|
1830
|
-
done = true;
|
|
1831
|
-
cb(Exit.succeed(value));
|
|
1832
|
-
}).catch((err) => {
|
|
1833
|
-
if (done) return;
|
|
1834
|
-
done = true;
|
|
1835
|
-
cb(Exit.failCause(Cause.fail(onReject(err))));
|
|
1836
|
-
});
|
|
1837
|
-
return () => {
|
|
1838
|
-
if (done) return;
|
|
1839
|
-
done = true;
|
|
1840
|
-
controller.abort();
|
|
1841
|
-
cb(Exit.failCause(Cause.interrupt()));
|
|
1842
|
-
};
|
|
1843
|
-
}
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1846
|
-
function unsafeRunFoldWithEnv(eff, env, onFailure, onSuccess) {
|
|
1847
|
-
unsafeRunAsync(eff, env, (ex) => {
|
|
1848
|
-
if (ex._tag === "Failure") onFailure(ex.cause);
|
|
1849
|
-
else onSuccess(ex.value);
|
|
1850
|
-
});
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
// src/core/runtime/fiber.ts
|
|
1854
|
-
var _current = null;
|
|
1855
|
-
var STEP = {
|
|
1856
|
-
CONTINUE: "Continue",
|
|
1857
|
-
SUSPEND: "Suspend",
|
|
1858
|
-
DONE: "Done"
|
|
1859
|
-
};
|
|
1860
|
-
var RUN = {
|
|
1861
|
-
QUEUED: "Queued",
|
|
1862
|
-
RUNNING: "Running",
|
|
1863
|
-
SUSPENDED: "Suspended",
|
|
1864
|
-
DONE: "Done"
|
|
1865
|
-
};
|
|
1866
|
-
var nextId = 1;
|
|
1867
|
-
var DEFAULT_BUDGET2 = 4096;
|
|
1868
|
-
var __benchmarkBudget;
|
|
1869
|
-
function setBenchmarkBudget(budget) {
|
|
1870
|
-
__benchmarkBudget = budget;
|
|
1871
|
-
}
|
|
1872
|
-
function getBenchmarkBudget() {
|
|
1873
|
-
return __benchmarkBudget;
|
|
1874
|
-
}
|
|
1875
|
-
function reassociateFlatMap(cur) {
|
|
1876
|
-
let current = cur;
|
|
1877
|
-
while (current._tag === "FlatMap" && current.first?._tag === "FlatMap") {
|
|
1878
|
-
const inner = current.first;
|
|
1879
|
-
const g = current.andThen;
|
|
1880
|
-
current = {
|
|
1881
|
-
_tag: "FlatMap",
|
|
1882
|
-
first: inner.first,
|
|
1883
|
-
andThen: (a) => ({
|
|
1884
|
-
_tag: "FlatMap",
|
|
1885
|
-
first: inner.andThen(a),
|
|
1886
|
-
andThen: g
|
|
1887
|
-
})
|
|
1888
|
-
};
|
|
1889
|
-
}
|
|
1890
|
-
return current;
|
|
1891
|
-
}
|
|
1892
|
-
var RuntimeFiber = class {
|
|
1893
|
-
id;
|
|
1894
|
-
// 👇 CLAVE: guardar el runtime en el fiber (para getCurrentRuntime())
|
|
1895
|
-
runtime;
|
|
1896
|
-
closing = null;
|
|
1897
|
-
finishing = false;
|
|
1898
|
-
runState = RUN.RUNNING;
|
|
1899
|
-
interrupted = false;
|
|
1900
|
-
result = null;
|
|
1901
|
-
joiners = [];
|
|
1902
|
-
// estado de evaluación
|
|
1903
|
-
current;
|
|
1904
|
-
stack = [];
|
|
1905
|
-
fiberFinalizers = [];
|
|
1906
|
-
finalizersDrained = false;
|
|
1907
|
-
fiberContext;
|
|
1908
|
-
name;
|
|
1909
|
-
scopeId;
|
|
1910
|
-
/**
|
|
1911
|
-
* Cached closure for the scheduler callback — avoids creating a new
|
|
1912
|
-
* closure on every `schedule()` call. The tag parameter used by the
|
|
1913
|
-
* scheduler is only part of the label string, not the callback logic,
|
|
1914
|
-
* so a single cached closure is sufficient.
|
|
1915
|
-
*/
|
|
1916
|
-
boundStep;
|
|
1917
|
-
constructor(runtime, effect) {
|
|
1918
|
-
this.id = nextId++;
|
|
1919
|
-
this.runtime = runtime;
|
|
1920
|
-
this.current = effect;
|
|
1921
|
-
this.boundStep = () => {
|
|
1922
|
-
withCurrentFiber(this, () => {
|
|
1923
|
-
if (this.runState === RUN.DONE) return;
|
|
1924
|
-
this.runState = RUN.RUNNING;
|
|
1925
|
-
const decision = this.step();
|
|
1926
|
-
switch (decision) {
|
|
1927
|
-
case STEP.CONTINUE:
|
|
1928
|
-
this.schedule("continue");
|
|
1929
|
-
return;
|
|
1930
|
-
case STEP.SUSPEND:
|
|
1931
|
-
this.runState = RUN.SUSPENDED;
|
|
1932
|
-
this.emit({ type: "fiber.suspend", fiberId: this.id });
|
|
1933
|
-
return;
|
|
1934
|
-
case STEP.DONE:
|
|
1935
|
-
this.runState = RUN.DONE;
|
|
1936
|
-
return;
|
|
1937
|
-
}
|
|
1938
|
-
});
|
|
1939
|
-
};
|
|
1940
|
-
}
|
|
1941
|
-
// helpers para no tocar el resto del código
|
|
1942
|
-
get env() {
|
|
1943
|
-
return this.runtime.env;
|
|
1944
|
-
}
|
|
1945
|
-
get scheduler() {
|
|
1946
|
-
return this.runtime.scheduler ?? globalScheduler;
|
|
1947
|
-
}
|
|
1948
|
-
emit(ev) {
|
|
1949
|
-
this.runtime.hooks.emit(ev, {
|
|
1950
|
-
fiberId: this.id,
|
|
1951
|
-
scopeId: this.scopeId,
|
|
1952
|
-
traceId: this.fiberContext?.trace?.traceId,
|
|
1953
|
-
spanId: this.fiberContext?.trace?.spanId
|
|
1954
|
-
});
|
|
1955
|
-
}
|
|
1956
|
-
addFinalizer(f) {
|
|
1957
|
-
this.fiberFinalizers.push(f);
|
|
1958
|
-
}
|
|
1959
|
-
status() {
|
|
1960
|
-
if (this.result == null) return "Running";
|
|
1961
|
-
if (this.result._tag === "Failure" && this.result.cause._tag === "Interrupt") return "Interrupted";
|
|
1962
|
-
return "Done";
|
|
1963
|
-
}
|
|
1964
|
-
join(cb) {
|
|
1965
|
-
if (this.result != null) cb(this.result);
|
|
1966
|
-
else this.joiners.push(cb);
|
|
1967
|
-
}
|
|
1968
|
-
interrupt() {
|
|
1969
|
-
if (this.result != null) return;
|
|
1970
|
-
if (this.interrupted) return;
|
|
1971
|
-
this.interrupted = true;
|
|
1972
|
-
this.schedule("interrupt-step");
|
|
1973
|
-
}
|
|
1974
|
-
schedule(tag = "step") {
|
|
1975
|
-
if (this.runState === RUN.DONE || this.runState === RUN.QUEUED) return;
|
|
1976
|
-
if (this.runState === RUN.SUSPENDED) {
|
|
1977
|
-
this.emit({ type: "fiber.resume", fiberId: this.id });
|
|
1978
|
-
}
|
|
1979
|
-
this.runState = RUN.QUEUED;
|
|
1980
|
-
this.scheduler.schedule(
|
|
1981
|
-
this.boundStep,
|
|
1982
|
-
`fiber#${this.id}.${tag}`
|
|
1983
|
-
);
|
|
1984
|
-
}
|
|
1985
|
-
runFinalizersOnce(exit) {
|
|
1986
|
-
if (this.finalizersDrained) return;
|
|
1987
|
-
this.finalizersDrained = true;
|
|
1988
|
-
while (this.fiberFinalizers.length > 0) {
|
|
1989
|
-
const fin = this.fiberFinalizers.pop();
|
|
1990
|
-
try {
|
|
1991
|
-
const eff = fin(exit);
|
|
1992
|
-
if (eff && typeof eff === "object" && "_tag" in eff) {
|
|
1993
|
-
unsafeRunAsync(eff, this.env, () => {
|
|
1994
|
-
});
|
|
1995
|
-
}
|
|
1996
|
-
} catch {
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
notify(exit) {
|
|
2001
|
-
if (this.result != null) return;
|
|
2002
|
-
if (this.closing != null) return;
|
|
2003
|
-
this.finishing = true;
|
|
2004
|
-
this.closing = exit;
|
|
2005
|
-
this.runFinalizersOnce(exit);
|
|
2006
|
-
this.result = exit;
|
|
2007
|
-
const status = exit._tag === "Success" ? "success" : exit.cause._tag === "Interrupt" ? "interrupted" : "failure";
|
|
2008
|
-
this.emit({
|
|
2009
|
-
type: "fiber.end",
|
|
2010
|
-
fiberId: this.id,
|
|
2011
|
-
status,
|
|
2012
|
-
error: exit._tag === "Failure" ? exit.cause : void 0
|
|
2013
|
-
});
|
|
2014
|
-
for (const j of this.joiners) j(exit);
|
|
2015
|
-
this.joiners.length = 0;
|
|
2016
|
-
}
|
|
2017
|
-
onSuccess(value) {
|
|
2018
|
-
const frame = this.stack.pop();
|
|
2019
|
-
if (!frame) {
|
|
2020
|
-
this.notify(Exit.succeed(value));
|
|
2021
|
-
return;
|
|
2022
|
-
}
|
|
2023
|
-
if (frame._tag === "SuccessCont") {
|
|
2024
|
-
try {
|
|
2025
|
-
this.current = frame.k(value);
|
|
2026
|
-
} catch (e) {
|
|
2027
|
-
this.notify(Exit.failCause(Cause.die(e)));
|
|
2028
|
-
}
|
|
2029
|
-
return;
|
|
2030
|
-
}
|
|
2031
|
-
try {
|
|
2032
|
-
this.current = frame.onSuccess(value);
|
|
2033
|
-
} catch (e) {
|
|
2034
|
-
this.notify(Exit.failCause(Cause.die(e)));
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
onFailure(error) {
|
|
2038
|
-
while (this.stack.length > 0) {
|
|
2039
|
-
const fr = this.stack.pop();
|
|
2040
|
-
if (fr._tag === "FoldCont") {
|
|
2041
|
-
try {
|
|
2042
|
-
this.current = fr.onFailure(error);
|
|
2043
|
-
return;
|
|
2044
|
-
} catch (e) {
|
|
2045
|
-
error = e;
|
|
2046
|
-
continue;
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2050
|
-
this.notify(Exit.failCause(Cause.fail(error)));
|
|
2051
|
-
}
|
|
2052
|
-
budget = DEFAULT_BUDGET2;
|
|
2053
|
-
step() {
|
|
2054
|
-
if (this.result != null) return STEP.DONE;
|
|
2055
|
-
if (this.interrupted) {
|
|
2056
|
-
this.notify(Exit.failCause(Cause.interrupt()));
|
|
2057
|
-
return STEP.DONE;
|
|
2058
|
-
}
|
|
2059
|
-
this.budget = __benchmarkBudget ?? DEFAULT_BUDGET2;
|
|
2060
|
-
while (this.budget-- > 0) {
|
|
2061
|
-
this.current = reassociateFlatMap(this.current);
|
|
2062
|
-
const current = this.current;
|
|
2063
|
-
switch (current._tag) {
|
|
2064
|
-
case "Succeed": {
|
|
2065
|
-
this.onSuccess(current.value);
|
|
2066
|
-
break;
|
|
2067
|
-
}
|
|
2068
|
-
case "Fail": {
|
|
2069
|
-
this.onFailure(current.error);
|
|
2070
|
-
break;
|
|
2071
|
-
}
|
|
2072
|
-
case "FlatMap": {
|
|
2073
|
-
this.stack.push({ _tag: "SuccessCont", k: current.andThen });
|
|
2074
|
-
this.current = current.first;
|
|
2075
|
-
break;
|
|
2076
|
-
}
|
|
2077
|
-
case "Fold": {
|
|
2078
|
-
this.stack.push({
|
|
2079
|
-
_tag: "FoldCont",
|
|
2080
|
-
onFailure: current.onFailure,
|
|
2081
|
-
onSuccess: current.onSuccess
|
|
2082
|
-
});
|
|
2083
|
-
this.current = current.first;
|
|
2084
|
-
break;
|
|
2085
|
-
}
|
|
2086
|
-
case "Async": {
|
|
2087
|
-
if (this.finishing) {
|
|
2088
|
-
return this.result != null ? STEP.DONE : STEP.CONTINUE;
|
|
2089
|
-
}
|
|
2090
|
-
let done = false;
|
|
2091
|
-
let asyncRegistered = false;
|
|
2092
|
-
let syncResolved = false;
|
|
2093
|
-
let syncExit = null;
|
|
2094
|
-
const cb = (exit) => {
|
|
2095
|
-
if (done) return;
|
|
2096
|
-
done = true;
|
|
2097
|
-
if (this.result != null || this.closing != null) return;
|
|
2098
|
-
if (!asyncRegistered) {
|
|
2099
|
-
syncResolved = true;
|
|
2100
|
-
syncExit = exit;
|
|
2101
|
-
return;
|
|
2102
|
-
}
|
|
2103
|
-
if (exit._tag === "Success") {
|
|
2104
|
-
this.current = Async.succeed(exit.value);
|
|
2105
|
-
this.schedule("async-resume");
|
|
2106
|
-
return;
|
|
2107
|
-
}
|
|
2108
|
-
const cause = exit.cause;
|
|
2109
|
-
if (cause._tag === "Interrupt") {
|
|
2110
|
-
this.notify(Exit.failCause(Cause.interrupt()));
|
|
2111
|
-
return;
|
|
2112
|
-
}
|
|
2113
|
-
if (cause._tag === "Fail") {
|
|
2114
|
-
this.current = Async.fail(cause.error);
|
|
2115
|
-
this.schedule("async-resume");
|
|
2116
|
-
return;
|
|
2117
|
-
}
|
|
2118
|
-
this.notify(Exit.failCause(Cause.die(cause.defect)));
|
|
2119
|
-
};
|
|
2120
|
-
const canceler = current.register(this.env, cb);
|
|
2121
|
-
asyncRegistered = true;
|
|
2122
|
-
if (syncResolved && syncExit) {
|
|
2123
|
-
const resolvedExit = syncExit;
|
|
2124
|
-
if (resolvedExit._tag === "Success") {
|
|
2125
|
-
this.onSuccess(resolvedExit.value);
|
|
2126
|
-
} else {
|
|
2127
|
-
const cause = resolvedExit.cause;
|
|
2128
|
-
if (cause._tag === "Interrupt") {
|
|
2129
|
-
this.notify(Exit.failCause(Cause.interrupt()));
|
|
2130
|
-
} else if (cause._tag === "Fail") {
|
|
2131
|
-
this.onFailure(cause.error);
|
|
2132
|
-
} else {
|
|
2133
|
-
this.notify(Exit.failCause(Cause.die(cause.defect)));
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
break;
|
|
2137
|
-
}
|
|
2138
|
-
if (typeof canceler === "function") {
|
|
2139
|
-
this.addFinalizer(() => {
|
|
2140
|
-
done = true;
|
|
2141
|
-
try {
|
|
2142
|
-
canceler();
|
|
2143
|
-
} catch {
|
|
2144
|
-
}
|
|
2145
|
-
});
|
|
2146
|
-
}
|
|
2147
|
-
return STEP.SUSPEND;
|
|
2148
|
-
}
|
|
2149
|
-
case "Fork": {
|
|
2150
|
-
const child = this.runtime.fork(current.effect, current.scopeId);
|
|
2151
|
-
this.onSuccess(child);
|
|
2152
|
-
break;
|
|
2153
|
-
}
|
|
2154
|
-
case "Sync": {
|
|
2155
|
-
try {
|
|
2156
|
-
const a = current.thunk(this.env);
|
|
2157
|
-
this.onSuccess(a);
|
|
2158
|
-
} catch (e) {
|
|
2159
|
-
this.onFailure(e);
|
|
2160
|
-
}
|
|
2161
|
-
break;
|
|
2162
|
-
}
|
|
2163
|
-
default: {
|
|
2164
|
-
this.onFailure(new Error(`Unknown opcode: ${current._tag}`));
|
|
2165
|
-
return STEP.CONTINUE;
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
if (this.result != null) return STEP.DONE;
|
|
2169
|
-
}
|
|
2170
|
-
return STEP.CONTINUE;
|
|
2171
|
-
}
|
|
2172
|
-
};
|
|
2173
|
-
function getCurrentFiber() {
|
|
2174
|
-
return _current;
|
|
2175
|
-
}
|
|
2176
|
-
function unsafeGetCurrentRuntime() {
|
|
2177
|
-
const f = getCurrentFiber();
|
|
2178
|
-
if (!f?.runtime) {
|
|
2179
|
-
throw new Error("unsafeGetCurrentRuntime: no current fiber/runtime");
|
|
2180
|
-
}
|
|
2181
|
-
return f.runtime;
|
|
2182
|
-
}
|
|
2183
|
-
function withCurrentFiber(fiber, f) {
|
|
2184
|
-
const prev = _current;
|
|
2185
|
-
_current = fiber;
|
|
2186
|
-
try {
|
|
2187
|
-
return f();
|
|
2188
|
-
} finally {
|
|
2189
|
-
_current = prev;
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
// src/core/runtime/scope.ts
|
|
2194
|
-
var nextScopeId = 1;
|
|
2195
|
-
function awaitAll(fibers) {
|
|
2196
|
-
return async((_env, cb) => {
|
|
2197
|
-
let remaining = fibers.length;
|
|
2198
|
-
if (remaining === 0) {
|
|
2199
|
-
cb({ _tag: "Success", value: void 0 });
|
|
2200
|
-
return;
|
|
2201
|
-
}
|
|
2202
|
-
for (const f of fibers) {
|
|
2203
|
-
f.join(() => {
|
|
2204
|
-
remaining -= 1;
|
|
2205
|
-
if (remaining === 0) cb({ _tag: "Success", value: void 0 });
|
|
2206
|
-
});
|
|
2207
|
-
}
|
|
2208
|
-
});
|
|
2209
|
-
}
|
|
2210
|
-
var Scope = class _Scope {
|
|
2211
|
-
constructor(runtime, parentScopeId) {
|
|
2212
|
-
this.runtime = runtime;
|
|
2213
|
-
this.parentScopeId = parentScopeId;
|
|
2214
|
-
this.id = nextScopeId++;
|
|
2215
|
-
const inferredParent = this.parentScopeId ?? getCurrentFiber()?.scopeId;
|
|
2216
|
-
if (this.runtime.hasActiveHooks()) {
|
|
2217
|
-
this.runtime.emit({
|
|
2218
|
-
type: "scope.open",
|
|
2219
|
-
scopeId: this.id,
|
|
2220
|
-
parentScopeId: inferredParent
|
|
2221
|
-
});
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
runtime;
|
|
2225
|
-
parentScopeId;
|
|
2226
|
-
id;
|
|
2227
|
-
closed = false;
|
|
2228
|
-
children = /* @__PURE__ */ new Set();
|
|
2229
|
-
subScopes = /* @__PURE__ */ new Set();
|
|
2230
|
-
finalizers = [];
|
|
2231
|
-
/** registra un finalizer (LIFO) */
|
|
2232
|
-
addFinalizer(f) {
|
|
2233
|
-
if (this.closed) {
|
|
2234
|
-
throw new Error("Trying to add finalizer to closed scope");
|
|
2235
|
-
}
|
|
2236
|
-
this.finalizers.push(f);
|
|
2237
|
-
}
|
|
2238
|
-
/** crea un sub scope (mismo runtime) */
|
|
2239
|
-
subScope() {
|
|
2240
|
-
if (this.closed) throw new Error("Scope closed");
|
|
2241
|
-
const s = new _Scope(this.runtime, this.id);
|
|
2242
|
-
this.subScopes.add(s);
|
|
2243
|
-
return s;
|
|
2244
|
-
}
|
|
2245
|
-
/** ✅ fork en este scope */
|
|
2246
|
-
fork(eff) {
|
|
2247
|
-
if (this.closed) throw new Error("Scope closed");
|
|
2248
|
-
const f = this.runtime.fork(eff, this.id);
|
|
2249
|
-
this.children.add(f);
|
|
2250
|
-
f.join(() => this.children.delete(f));
|
|
2251
|
-
return f;
|
|
2252
|
-
}
|
|
2253
|
-
/** close fire-and-forget (no bloquea) */
|
|
2254
|
-
close(exit = { _tag: "Success", value: void 0 }) {
|
|
2255
|
-
this.runtime.fork(this.closeAsync(exit));
|
|
2256
|
-
}
|
|
2257
|
-
/** Emit the scope.close event if hooks are active. */
|
|
2258
|
-
emitCloseEvent(exit) {
|
|
2259
|
-
if (this.runtime.hasActiveHooks()) {
|
|
2260
|
-
const status = exit._tag === "Success" ? "success" : exit.cause._tag === "Interrupt" ? "interrupted" : "failure";
|
|
2261
|
-
this.runtime.emit({
|
|
2262
|
-
type: "scope.close",
|
|
2263
|
-
scopeId: this.id,
|
|
2264
|
-
status,
|
|
2265
|
-
error: exit._tag === "Failure" && exit.cause._tag === "Fail" ? exit.cause.error : void 0
|
|
2266
|
-
});
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
/**
|
|
2270
|
-
* Build an effect that executes finalizers in LIFO order.
|
|
2271
|
-
*
|
|
2272
|
-
* Optimization over the original: instead of wrapping every finalizer in
|
|
2273
|
-
* `asyncFold(fin(exit), () => unit(), () => unit())` which creates 3 effect
|
|
2274
|
-
* nodes per finalizer (Fold + 2 Succeed), we use a single Sync thunk per
|
|
2275
|
-
* finalizer that catches errors inline. When the finalizer returns a
|
|
2276
|
-
* Succeed effect (like `unit()`), the Sync thunk completes without creating
|
|
2277
|
-
* additional effect nodes.
|
|
2278
|
-
*/
|
|
2279
|
-
buildFinalizerEffect(exit) {
|
|
2280
|
-
const fins = this.finalizers;
|
|
2281
|
-
if (fins.length === 0) return unit();
|
|
2282
|
-
let chain = unit();
|
|
2283
|
-
for (let i = fins.length - 1; i >= 0; i--) {
|
|
2284
|
-
const fin = fins[i];
|
|
2285
|
-
chain = asyncFlatMap(chain, () => {
|
|
2286
|
-
let result;
|
|
2287
|
-
try {
|
|
2288
|
-
result = fin(exit);
|
|
2289
|
-
} catch {
|
|
2290
|
-
return unit();
|
|
2291
|
-
}
|
|
2292
|
-
if (result._tag === "Succeed") {
|
|
2293
|
-
return unit();
|
|
2294
|
-
}
|
|
2295
|
-
return asyncFold(
|
|
2296
|
-
result,
|
|
2297
|
-
() => unit(),
|
|
2298
|
-
() => unit()
|
|
2299
|
-
);
|
|
2300
|
-
});
|
|
2301
|
-
}
|
|
2302
|
-
return chain;
|
|
2303
|
-
}
|
|
2304
|
-
closeAsync(exit = { _tag: "Success", value: void 0 }, opts = { awaitChildren: true }) {
|
|
2305
|
-
return asyncFlatMap(
|
|
2306
|
-
unit(),
|
|
2307
|
-
() => async((env, cb) => {
|
|
2308
|
-
if (this.closed) {
|
|
2309
|
-
cb({ _tag: "Success", value: void 0 });
|
|
2310
|
-
return;
|
|
2311
|
-
}
|
|
2312
|
-
this.closed = true;
|
|
2313
|
-
const children = Array.from(this.children);
|
|
2314
|
-
const subScopes = Array.from(this.subScopes);
|
|
2315
|
-
for (const child of children) {
|
|
2316
|
-
child.interrupt();
|
|
2317
|
-
}
|
|
2318
|
-
const closeSubs = subScopes.reduceRight(
|
|
2319
|
-
(acc, s) => asyncFlatMap(acc, () => s.closeAsync(exit, opts)),
|
|
2320
|
-
unit()
|
|
2321
|
-
);
|
|
2322
|
-
const runFinalizers = this.buildFinalizerEffect(exit);
|
|
2323
|
-
const needsAwait = opts.awaitChildren && children.length > 0;
|
|
2324
|
-
const awaitChildrenEff = needsAwait ? awaitAll(children) : unit();
|
|
2325
|
-
const hasSubScopes = subScopes.length > 0;
|
|
2326
|
-
const hasNoFinalizers = this.finalizers.length === 0;
|
|
2327
|
-
if (!hasSubScopes && !needsAwait && hasNoFinalizers) {
|
|
2328
|
-
this.emitCloseEvent(exit);
|
|
2329
|
-
cb({ _tag: "Success", value: void 0 });
|
|
2330
|
-
return;
|
|
2331
|
-
}
|
|
2332
|
-
const all = asyncFlatMap(closeSubs, () => asyncFlatMap(awaitChildrenEff, () => runFinalizers));
|
|
2333
|
-
this.runtime.fork(all).join(() => {
|
|
2334
|
-
this.emitCloseEvent(exit);
|
|
2335
|
-
cb({ _tag: "Success", value: void 0 });
|
|
2336
|
-
});
|
|
2337
|
-
})
|
|
2338
|
-
);
|
|
2339
|
-
}
|
|
2340
|
-
};
|
|
2341
|
-
function withScopeAsync(runtime, f) {
|
|
2342
|
-
return async((_env, cb) => {
|
|
2343
|
-
const scope = new Scope(runtime);
|
|
2344
|
-
let done = false;
|
|
2345
|
-
const completeAfterClose = (exit) => {
|
|
2346
|
-
runtime.fork(scope.closeAsync(exit)).join(() => {
|
|
2347
|
-
if (done) return;
|
|
2348
|
-
done = true;
|
|
2349
|
-
cb(exit);
|
|
2350
|
-
});
|
|
2351
|
-
};
|
|
2352
|
-
const fiber = runtime.fork(f(scope));
|
|
2353
|
-
fiber.join(completeAfterClose);
|
|
2354
|
-
return () => {
|
|
2355
|
-
if (done) return;
|
|
2356
|
-
fiber.interrupt();
|
|
2357
|
-
runtime.fork(scope.closeAsync(Exit.failCause(Cause.interrupt())));
|
|
2358
|
-
};
|
|
2359
|
-
});
|
|
2360
|
-
}
|
|
2361
|
-
function withScope(runtime, f) {
|
|
2362
|
-
return withScopeAsync(runtime, (scope) => {
|
|
2363
|
-
const out = f(scope);
|
|
2364
|
-
if (out && typeof out === "object" && "_tag" in out) return out;
|
|
2365
|
-
return unit();
|
|
2366
|
-
});
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
// src/core/stream/structuredConcurrency.ts
|
|
2370
|
-
function race(left, right, parentScope) {
|
|
2371
|
-
return async((env, cb) => {
|
|
2372
|
-
const scope = parentScope.subScope();
|
|
2373
|
-
let done = false;
|
|
2374
|
-
const onResult = (exit) => {
|
|
2375
|
-
if (done) return;
|
|
2376
|
-
done = true;
|
|
2377
|
-
scope.close(exit);
|
|
2378
|
-
cb(exit);
|
|
2379
|
-
};
|
|
2380
|
-
const fiberLeft = scope.fork(left);
|
|
2381
|
-
const fiberRight = scope.fork(right);
|
|
2382
|
-
fiberLeft.join(onResult);
|
|
2383
|
-
fiberRight.join(onResult);
|
|
2384
|
-
});
|
|
2385
|
-
}
|
|
2386
|
-
function zipPar(left, right, parentScope) {
|
|
2387
|
-
return async((env, cb) => {
|
|
2388
|
-
const scope = parentScope.subScope();
|
|
2389
|
-
let leftExit = null;
|
|
2390
|
-
let rightExit = null;
|
|
2391
|
-
let done = false;
|
|
2392
|
-
const checkDone = () => {
|
|
2393
|
-
if (!leftExit || !rightExit || done) return;
|
|
2394
|
-
done = true;
|
|
2395
|
-
if (leftExit._tag === "Success" && rightExit._tag === "Success") {
|
|
2396
|
-
scope.close({ _tag: "Success", value: void 0 });
|
|
2397
|
-
cb({
|
|
2398
|
-
_tag: "Success",
|
|
2399
|
-
value: [leftExit.value, rightExit.value]
|
|
2400
|
-
});
|
|
2401
|
-
return;
|
|
2402
|
-
}
|
|
2403
|
-
let cause;
|
|
2404
|
-
if (leftExit._tag === "Failure") {
|
|
2405
|
-
cause = leftExit.cause;
|
|
2406
|
-
} else if (rightExit._tag === "Failure") {
|
|
2407
|
-
cause = rightExit.cause;
|
|
2408
|
-
} else {
|
|
2409
|
-
throw new Error("zipPar: unreachable state (no Failure exit)");
|
|
2410
|
-
}
|
|
2411
|
-
const errExit = {
|
|
2412
|
-
_tag: "Failure",
|
|
2413
|
-
cause
|
|
2414
|
-
};
|
|
2415
|
-
scope.close(errExit);
|
|
2416
|
-
cb(errExit);
|
|
2417
|
-
};
|
|
2418
|
-
const f1 = scope.fork(left);
|
|
2419
|
-
const f2 = scope.fork(right);
|
|
2420
|
-
f1.join((exit) => {
|
|
2421
|
-
leftExit = exit;
|
|
2422
|
-
checkDone();
|
|
2423
|
-
});
|
|
2424
|
-
f2.join((exit) => {
|
|
2425
|
-
rightExit = exit;
|
|
2426
|
-
checkDone();
|
|
2427
|
-
});
|
|
2428
|
-
});
|
|
2429
|
-
}
|
|
2430
|
-
function collectAllPar(effects, parentScope) {
|
|
2431
|
-
return async((env, cb) => {
|
|
2432
|
-
const scope = parentScope.subScope();
|
|
2433
|
-
const results = new Array(effects.length);
|
|
2434
|
-
let completed = 0;
|
|
2435
|
-
let done = false;
|
|
2436
|
-
effects.forEach((eff, i) => {
|
|
2437
|
-
const f = scope.fork(eff);
|
|
2438
|
-
f.join((exit) => {
|
|
2439
|
-
if (done) return;
|
|
2440
|
-
if (exit._tag === "Failure") {
|
|
2441
|
-
done = true;
|
|
2442
|
-
const errExit = {
|
|
2443
|
-
_tag: "Failure",
|
|
2444
|
-
cause: exit.cause
|
|
2445
|
-
};
|
|
2446
|
-
scope.close(errExit);
|
|
2447
|
-
cb(errExit);
|
|
2448
|
-
return;
|
|
2449
|
-
}
|
|
2450
|
-
results[i] = exit.value;
|
|
2451
|
-
completed++;
|
|
2452
|
-
if (completed === effects.length) {
|
|
2453
|
-
done = true;
|
|
2454
|
-
const successExit = {
|
|
2455
|
-
_tag: "Success",
|
|
2456
|
-
value: results
|
|
2457
|
-
};
|
|
2458
|
-
scope.close({ _tag: "Success", value: void 0 });
|
|
2459
|
-
cb(successExit);
|
|
2460
|
-
}
|
|
2461
|
-
});
|
|
2462
|
-
});
|
|
2463
|
-
});
|
|
2464
|
-
}
|
|
2465
|
-
function raceWith(left, right, parentScope, onLeft, onRight) {
|
|
2466
|
-
return async((env, cb) => {
|
|
2467
|
-
const scope = parentScope.subScope();
|
|
2468
|
-
let done = false;
|
|
2469
|
-
const fiberLeft = scope.fork(left);
|
|
2470
|
-
const fiberRight = scope.fork(right);
|
|
2471
|
-
const finish = (next) => {
|
|
2472
|
-
scope.fork(next).join((exitNext) => {
|
|
2473
|
-
scope.close(exitNext);
|
|
2474
|
-
cb(exitNext);
|
|
2475
|
-
});
|
|
2476
|
-
};
|
|
2477
|
-
fiberLeft.join((exitL) => {
|
|
2478
|
-
if (done) return;
|
|
2479
|
-
done = true;
|
|
2480
|
-
finish(onLeft(exitL, fiberRight, scope));
|
|
2481
|
-
});
|
|
2482
|
-
fiberRight.join((exitR) => {
|
|
2483
|
-
if (done) return;
|
|
2484
|
-
done = true;
|
|
2485
|
-
finish(onRight(exitR, fiberLeft, scope));
|
|
2486
|
-
});
|
|
2487
|
-
});
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
export {
|
|
2491
|
-
Async,
|
|
2492
|
-
asyncFold,
|
|
2493
|
-
asyncCatchAll,
|
|
2494
|
-
asyncMapError,
|
|
2495
|
-
unit,
|
|
2496
|
-
asyncSucceed,
|
|
2497
|
-
asyncFail,
|
|
2498
|
-
asyncSync,
|
|
2499
|
-
asyncTotal,
|
|
2500
|
-
async,
|
|
2501
|
-
asyncMap,
|
|
2502
|
-
asyncFlatMap,
|
|
2503
|
-
acquireRelease,
|
|
2504
|
-
asyncInterruptible,
|
|
2505
|
-
withAsyncPromise,
|
|
2506
|
-
mapAsync,
|
|
2507
|
-
mapTryAsync,
|
|
2508
|
-
none,
|
|
2509
|
-
some,
|
|
2510
|
-
Cause,
|
|
2511
|
-
Exit,
|
|
2512
|
-
succeed,
|
|
2513
|
-
fail,
|
|
2514
|
-
sync,
|
|
2515
|
-
map,
|
|
2516
|
-
flatMap,
|
|
2517
|
-
mapError,
|
|
2518
|
-
catchAll,
|
|
2519
|
-
orElseOptional,
|
|
2520
|
-
end,
|
|
2521
|
-
PushStatus,
|
|
2522
|
-
RingBuffer,
|
|
2523
|
-
resolveWasmModule,
|
|
2524
|
-
makeBoundedRingBuffer,
|
|
2525
|
-
Scheduler,
|
|
2526
|
-
globalScheduler,
|
|
2527
|
-
setBenchmarkBudget,
|
|
2528
|
-
getBenchmarkBudget,
|
|
2529
|
-
RuntimeFiber,
|
|
2530
|
-
getCurrentFiber,
|
|
2531
|
-
unsafeGetCurrentRuntime,
|
|
2532
|
-
withCurrentFiber,
|
|
2533
|
-
DefaultHostExecutor,
|
|
2534
|
-
JsFiberEngine,
|
|
2535
|
-
HostRegistry,
|
|
2536
|
-
ProgramBuilder,
|
|
2537
|
-
EngineFiberHandle,
|
|
2538
|
-
ReferenceWasmBridge,
|
|
2539
|
-
WasmPackFiberBridge,
|
|
2540
|
-
WasmFiberRegistryBridge,
|
|
2541
|
-
WasmFiberEngine,
|
|
2542
|
-
runtimeCapabilities,
|
|
2543
|
-
NoopHooks,
|
|
2544
|
-
Runtime,
|
|
2545
|
-
fork,
|
|
2546
|
-
unsafeRunAsync,
|
|
2547
|
-
toPromise,
|
|
2548
|
-
fromPromiseAbortable,
|
|
2549
|
-
unsafeRunFoldWithEnv,
|
|
2550
|
-
Scope,
|
|
2551
|
-
withScopeAsync,
|
|
2552
|
-
withScope,
|
|
2553
|
-
race,
|
|
2554
|
-
zipPar,
|
|
2555
|
-
collectAllPar,
|
|
2556
|
-
raceWith
|
|
2557
|
-
};
|