nginx-lint-plugin 0.8.2 → 0.8.3

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.
@@ -0,0 +1,2177 @@
1
+ "use jco";
2
+
3
+ const _debugLog = (...args) => {
4
+ if (!globalThis?.process?.env?.JCO_DEBUG) { return; }
5
+ console.debug(...args);
6
+ }
7
+ const ASYNC_DETERMINISM = 'random';
8
+
9
+ class GlobalComponentAsyncLowers {
10
+ static map = new Map();
11
+
12
+ constructor() { throw new Error('GlobalComponentAsyncLowers should not be constructed'); }
13
+
14
+ static define(args) {
15
+ const { componentIdx, qualifiedImportFn, fn } = args;
16
+ let inner = GlobalComponentAsyncLowers.map.get(componentIdx);
17
+ if (!inner) {
18
+ inner = new Map();
19
+ GlobalComponentAsyncLowers.map.set(componentIdx, inner);
20
+ }
21
+
22
+ inner.set(qualifiedImportFn, fn);
23
+ }
24
+
25
+ static lookup(componentIdx, qualifiedImportFn) {
26
+ let inner = GlobalComponentAsyncLowers.map.get(componentIdx);
27
+ if (!inner) {
28
+ inner = new Map();
29
+ GlobalComponentAsyncLowers.map.set(componentIdx, inner);
30
+ }
31
+
32
+ const found = inner.get(qualifiedImportFn);
33
+ if (found) { return found; }
34
+
35
+ // In some cases, async lowers are *not* host provided, and
36
+ // but contain/will call an async function in the host.
37
+ //
38
+ // One such case is `stream.write`/`stream.read` trampolines which are
39
+ // actually re-exported through a patch up container *before*
40
+ // they call the relevant async host trampoline.
41
+ //
42
+ // So the path of execution from a component export would be:
43
+ //
44
+ // async guest export --> stream.write import (host wired) -> guest export (patch component) -> async host trampoline
45
+ //
46
+ // On top of all this, the trampoline that is eventually called is async,
47
+ // so we must await the patched guest export call.
48
+ //
49
+ if (qualifiedImportFn.includes("[stream-write-") || qualifiedImportFn.includes("[stream-read-")) {
50
+ return async (...args) => {
51
+ const [originalFn, ...params] = args;
52
+ return await originalFn(...params);
53
+ };
54
+ }
55
+
56
+ // All other cases can call the registered function directly
57
+ return (...args) => {
58
+ const [originalFn, ...params] = args;
59
+ return originalFn(...params);
60
+ };
61
+ }
62
+ }
63
+
64
+ class GlobalAsyncParamLowers {
65
+ static map = new Map();
66
+
67
+ static generateKey(args) {
68
+ const { componentIdx, iface, fnName } = args;
69
+ if (componentIdx === undefined) { throw new TypeError("missing component idx"); }
70
+ if (iface === undefined) { throw new TypeError("missing iface name"); }
71
+ if (fnName === undefined) { throw new TypeError("missing function name"); }
72
+ return `${componentIdx}-${iface}-${fnName}`;
73
+ }
74
+
75
+ static define(args) {
76
+ const { componentIdx, iface, fnName, fn } = args;
77
+ if (!fn) { throw new TypeError('missing function'); }
78
+ const key = GlobalAsyncParamLowers.generateKey(args);
79
+ GlobalAsyncParamLowers.map.set(key, fn);
80
+ }
81
+
82
+ static lookup(args) {
83
+ const { componentIdx, iface, fnName } = args;
84
+ const key = GlobalAsyncParamLowers.generateKey(args);
85
+ return GlobalAsyncParamLowers.map.get(key);
86
+ }
87
+ }
88
+
89
+ class GlobalComponentMemories {
90
+ static map = new Map();
91
+
92
+ constructor() { throw new Error('GlobalComponentMemories should not be constructed'); }
93
+
94
+ static save(args) {
95
+ const { idx, componentIdx, memory } = args;
96
+ let inner = GlobalComponentMemories.map.get(componentIdx);
97
+ if (!inner) {
98
+ inner = [];
99
+ GlobalComponentMemories.map.set(componentIdx, inner);
100
+ }
101
+ inner.push({ memory, idx });
102
+ }
103
+
104
+ static getMemoriesForComponentIdx(componentIdx) {
105
+ const metas = GlobalComponentMemories.map.get(componentIdx);
106
+ return metas.map(meta => meta.memory);
107
+ }
108
+
109
+ static getMemory(componentIdx, idx) {
110
+ const metas = GlobalComponentMemories.map.get(componentIdx);
111
+ return metas.find(meta => meta.idx === idx)?.memory;
112
+ }
113
+ }
114
+
115
+ class RepTable {
116
+ #data = [0, null];
117
+ #target;
118
+
119
+ constructor(args) {
120
+ this.target = args?.target;
121
+ }
122
+
123
+ insert(val) {
124
+ _debugLog('[RepTable#insert()] args', { val, target: this.target });
125
+ const freeIdx = this.#data[0];
126
+ if (freeIdx === 0) {
127
+ this.#data.push(val);
128
+ this.#data.push(null);
129
+ return (this.#data.length >> 1) - 1;
130
+ }
131
+ this.#data[0] = this.#data[freeIdx << 1];
132
+ const placementIdx = freeIdx << 1;
133
+ this.#data[placementIdx] = val;
134
+ this.#data[placementIdx + 1] = null;
135
+ return freeIdx;
136
+ }
137
+
138
+ get(rep) {
139
+ _debugLog('[RepTable#get()] args', { rep, target: this.target });
140
+ const baseIdx = rep << 1;
141
+ const val = this.#data[baseIdx];
142
+ return val;
143
+ }
144
+
145
+ contains(rep) {
146
+ _debugLog('[RepTable#contains()] args', { rep, target: this.target });
147
+ const baseIdx = rep << 1;
148
+ return !!this.#data[baseIdx];
149
+ }
150
+
151
+ remove(rep) {
152
+ _debugLog('[RepTable#remove()] args', { rep, target: this.target });
153
+ if (this.#data.length === 2) { throw new Error('invalid'); }
154
+
155
+ const baseIdx = rep << 1;
156
+ const val = this.#data[baseIdx];
157
+ if (val === 0) { throw new Error('invalid resource rep (cannot be 0)'); }
158
+
159
+ this.#data[baseIdx] = this.#data[0];
160
+ this.#data[0] = rep;
161
+
162
+ return val;
163
+ }
164
+
165
+ clear() {
166
+ _debugLog('[RepTable#clear()] args', { rep, target: this.target });
167
+ this.#data = [0, null];
168
+ }
169
+ }
170
+ const _coinFlip = () => { return Math.random() > 0.5; };
171
+ let SCOPE_ID = 0;
172
+ const I32_MIN = -2_147_483_648;
173
+ const I32_MAX = 2_147_483_647;
174
+ const _typeCheckValidI32 = (n) => typeof n === 'number' && n >= I32_MIN && n <= I32_MAX;
175
+
176
+ const _typeCheckAsyncFn= (f) => {
177
+ return f instanceof ASYNC_FN_CTOR;
178
+ };
179
+
180
+ const ASYNC_FN_CTOR = (async () => {}).constructor;
181
+ const ASYNC_CURRENT_TASK_IDS = [];
182
+ const ASYNC_CURRENT_COMPONENT_IDXS = [];
183
+
184
+ function unpackCallbackResult(result) {
185
+ _debugLog('[unpackCallbackResult()] args', { result });
186
+ if (!(_typeCheckValidI32(result))) { throw new Error('invalid callback return value [' + result + '], not a valid i32'); }
187
+ const eventCode = result & 0xF;
188
+ if (eventCode < 0 || eventCode > 3) {
189
+ throw new Error('invalid async return value [' + eventCode + '], outside callback code range');
190
+ }
191
+ if (result < 0 || result >= 2**32) { throw new Error('invalid callback result'); }
192
+ // TODO: table max length check?
193
+ const waitableSetRep = result >> 4;
194
+ return [eventCode, waitableSetRep];
195
+ }
196
+
197
+ function promiseWithResolvers() {
198
+ if (Promise.withResolvers) {
199
+ return Promise.withResolvers();
200
+ } else {
201
+ let resolve;
202
+ let reject;
203
+ const promise = new Promise((res, rej) => {
204
+ resolve = res;
205
+ reject = rej;
206
+ });
207
+ return { promise, resolve, reject };
208
+ }
209
+ }
210
+
211
+ function _prepareCall(
212
+ memoryIdx,
213
+ getMemoryFn,
214
+ startFn,
215
+ returnFn,
216
+ callerInstanceIdx,
217
+ calleeInstanceIdx,
218
+ taskReturnTypeIdx,
219
+ isCalleeAsyncInt,
220
+ stringEncoding,
221
+ resultCountOrAsync,
222
+ ) {
223
+ _debugLog('[_prepareCall()]', {
224
+ callerInstanceIdx,
225
+ calleeInstanceIdx,
226
+ taskReturnTypeIdx,
227
+ isCalleeAsyncInt,
228
+ stringEncoding,
229
+ resultCountOrAsync,
230
+ });
231
+ const argArray = [...arguments];
232
+
233
+ // Since Rust will happily pass large u32s over, resultCountOrAsync should be one of:
234
+ // (a) u32 max size => callee is async fn with no result
235
+ // (b) u32 max size - 1 => callee is async fn with result
236
+ // (c) any other value => callee is sync with the given result count
237
+ //
238
+ // Due to JS handling the value as 2s complement, the `resultCountOrAsync` ends up being:
239
+ // (a) -1 as u32 max size
240
+ // (b) -2 as u32 max size - 1
241
+ // (c) x
242
+ //
243
+ // Due to JS mishandling the value as 2s complement, the actual values we get are:
244
+ // see. https://github.com/wasm-bindgen/wasm-bindgen/issues/1388
245
+ let isAsync = false;
246
+ let hasResultPointer = false;
247
+ if (resultCountOrAsync === -1) {
248
+ isAsync = true;
249
+ hasResultPointer = false;
250
+ } else if (resultCountOrAsync === -2) {
251
+ isAsync = true;
252
+ hasResultPointer = true;
253
+ }
254
+
255
+ const currentCallerTaskMeta = getCurrentTask(callerInstanceIdx);
256
+ if (!currentCallerTaskMeta) {
257
+ throw new Error('invalid/missing current task for caller during prepare call');
258
+ }
259
+
260
+ const currentCallerTask = currentCallerTaskMeta.task;
261
+ if (!currentCallerTask) {
262
+ throw new Error('unexpectedly missing task in meta for caller during prepare call');
263
+ }
264
+
265
+ if (currentCallerTask.componentIdx() !== callerInstanceIdx) {
266
+ throw new Error(`task component idx [${ currentCallerTask.componentIdx() }] !== [${ callerInstanceIdx }] (callee ${ calleeInstanceIdx })`);
267
+ }
268
+
269
+ let getCalleeParamsFn;
270
+ let resultPtr = null;
271
+ if (hasResultPointer) {
272
+ const directParamsArr = argArray.slice(11);
273
+ getCalleeParamsFn = () => directParamsArr;
274
+ resultPtr = argArray[10];
275
+ } else {
276
+ const directParamsArr = argArray.slice(10);
277
+ getCalleeParamsFn = () => directParamsArr;
278
+ }
279
+
280
+ let encoding;
281
+ switch (stringEncoding) {
282
+ case 0:
283
+ encoding = 'utf8';
284
+ break;
285
+ case 1:
286
+ encoding = 'utf16';
287
+ break;
288
+ case 2:
289
+ encoding = 'compact-utf16';
290
+ break;
291
+ default:
292
+ throw new Error(`unrecognized string encoding enum [${stringEncoding}]`);
293
+ }
294
+
295
+ const [newTask, newTaskID] = createNewCurrentTask({
296
+ componentIdx: calleeInstanceIdx,
297
+ isAsync: isCalleeAsyncInt !== 0,
298
+ getCalleeParamsFn,
299
+ // TODO: find a way to pass the import name through here
300
+ entryFnName: 'task/' + currentCallerTask.id() + '/new-prepare-task',
301
+ stringEncoding,
302
+ });
303
+
304
+ const subtask = currentCallerTask.createSubtask({
305
+ componentIdx: callerInstanceIdx,
306
+ parentTask: currentCallerTask,
307
+ childTask: newTask,
308
+ callMetadata: {
309
+ memory: getMemoryFn(),
310
+ memoryIdx,
311
+ resultPtr,
312
+ returnFn,
313
+ startFn,
314
+ }
315
+ });
316
+
317
+ newTask.setParentSubtask(subtask);
318
+ // NOTE: This isn't really a return memory idx for the caller, it's for checking
319
+ // against the task.return (which will be called from the callee)
320
+ newTask.setReturnMemoryIdx(memoryIdx);
321
+ }
322
+
323
+ function _asyncStartCall(args, callee, paramCount, resultCount, flags) {
324
+ const { getCallbackFn, callbackIdx, getPostReturnFn, postReturnIdx } = args;
325
+ _debugLog('[_asyncStartCall()] args', args);
326
+
327
+ const taskMeta = getCurrentTask(ASYNC_CURRENT_COMPONENT_IDXS.at(-1), ASYNC_CURRENT_TASK_IDS.at(-1));
328
+ if (!taskMeta) { throw new Error('invalid/missing current async task meta during prepare call'); }
329
+
330
+ const argArray = [...arguments];
331
+
332
+ // NOTE: at this point we know the current task is the one that was started
333
+ // in PrepareCall, so we *should* be able to pop it back off and be left with
334
+ // the previous task
335
+ const preparedTask = taskMeta.task;
336
+ if (!preparedTask) { throw new Error('unexpectedly missing task in task meta during prepare call'); }
337
+
338
+ if (resultCount < 0 || resultCount > 1) { throw new Error('invalid/unsupported result count'); }
339
+
340
+ const callbackFnName = 'callback_' + callbackIdx;
341
+ const callbackFn = getCallbackFn();
342
+ preparedTask.setCallbackFn(callbackFn, callbackFnName);
343
+ preparedTask.setPostReturnFn(getPostReturnFn());
344
+
345
+ const subtask = preparedTask.getParentSubtask();
346
+
347
+ if (resultCount < 0 || resultCount > 1) { throw new Error(`unsupported result count [${ resultCount }]`); }
348
+
349
+ const params = preparedTask.getCalleeParams();
350
+ if (paramCount !== params.length) {
351
+ throw new Error(`unexpected callee param count [${ params.length }], _asyncStartCall invocation expected [${ paramCount }]`);
352
+ }
353
+
354
+ subtask.setOnProgressFn(() => {
355
+ subtask.setPendingEventFn(() => {
356
+ if (subtask.resolved()) { subtask.deliverResolve(); }
357
+ return {
358
+ code: ASYNC_EVENT_CODE.SUBTASK,
359
+ index: rep,
360
+ result: subtask.getStateNumber(),
361
+ }
362
+ });
363
+ });
364
+
365
+ const subtaskState = subtask.getStateNumber();
366
+ if (subtaskState < 0 || subtaskState > 2**5) {
367
+ throw new Error('invalid subtask state, out of valid range');
368
+ }
369
+
370
+ const callerComponentState = getOrCreateAsyncState(subtask.componentIdx());
371
+ const rep = callerComponentState.subtasks.insert(subtask);
372
+ subtask.setRep(rep);
373
+
374
+ const calleeComponentState = getOrCreateAsyncState(preparedTask.componentIdx());
375
+ const calleeBackpressure = calleeComponentState.hasBackpressure();
376
+
377
+ // Set up a handler on subtask completion to lower results from the call into the caller's memory region.
378
+ //
379
+ // NOTE: during fused guest->guest calls this handler is triggered, but does not actually perform
380
+ // lowering manually, as fused modules provider helper functions that can
381
+ subtask.registerOnResolveHandler((res) => {
382
+ _debugLog('[_asyncStartCall()] handling subtask result', { res, subtaskID: subtask.id() });
383
+ let subtaskCallMeta = subtask.getCallMetadata();
384
+
385
+ // NOTE: in the case of guest -> guest async calls, there may be no memory/realloc present,
386
+ // as the host will intermediate the value storage/movement between calls.
387
+ //
388
+ // We can simply take the value and lower it as a parameter
389
+ if (subtaskCallMeta.memory || subtaskCallMeta.realloc) {
390
+ throw new Error("call metadata unexpectedly contains memory/realloc for guest->guest call");
391
+ }
392
+
393
+ const callerTask = subtask.getParentTask();
394
+ const calleeTask = preparedTask;
395
+ const callerMemoryIdx = callerTask.getReturnMemoryIdx();
396
+ const callerComponentIdx = callerTask.componentIdx();
397
+
398
+ // If a helper function was provided we are likely in a fused guest->guest call,
399
+ // and the result will be delivered (lift/lowered) via helper function
400
+ if (subtaskCallMeta.returnFn) {
401
+ _debugLog('[_asyncStartCall()] return function present while ahndling subtask result, returning early (skipping lower)');
402
+ return;
403
+ }
404
+
405
+ // If there is no where to lower the results, exit early
406
+ if (!subtaskCallMeta.resultPtr) {
407
+ _debugLog('[_asyncStartCall()] no result ptr during subtask result handling, returning early (skipping lower)');
408
+ return;
409
+ }
410
+
411
+ let callerMemory;
412
+ if (callerMemoryIdx) {
413
+ callerMemory = GlobalComponentMemories.getMemory(callerComponentIdx, callerMemoryIdx);
414
+ } else {
415
+ const callerMemories = GlobalComponentMemories.getMemoriesForComponentIdx(callerComponentIdx);
416
+ if (callerMemories.length != 1) { throw new Error(`unsupported amount of caller memories`); }
417
+ callerMemory = callerMemories[0];
418
+ }
419
+
420
+ if (!callerMemory) {
421
+ throw new Error(`missing memory for to guest->guest call result (subtask [${subtask.id()}])`);
422
+ }
423
+
424
+ const lowerFns = calleeTask.getReturnLowerFns();
425
+ if (!lowerFns || lowerFns.length === 0) {
426
+ throw new Error(`missing result lower metadata for guest->guests call (subtask [${subtask.id()}])`);
427
+ }
428
+
429
+ if (lowerFns.length !== 1) {
430
+ throw new Error(`only single result supported for guest->guest calls (subtask [${subtask.id()}])`);
431
+ }
432
+
433
+ lowerFns[0]({
434
+ realloc: undefined,
435
+ memory: callerMemory,
436
+ vals: [res],
437
+ storagePtr: subtaskCallMeta.resultPtr,
438
+ componentIdx: callerComponentIdx
439
+ });
440
+
441
+ });
442
+
443
+ // Build call params
444
+ const subtaskCallMeta = subtask.getCallMetadata();
445
+ let startFnParams = [];
446
+ let calleeParams = [];
447
+ if (subtaskCallMeta.startFn && subtaskCallMeta.resultPtr) {
448
+ // If we're using a fused component start fn and a result pointer is present,
449
+ // then we need to pass the result pointer and other params to the start fn
450
+ startFnParams.push(subtaskCallMeta.resultPtr, ...params);
451
+ } else {
452
+ // if not we need to pass params to the callee instead
453
+ startFnParams.push(...params);
454
+ calleeParams.push(...params);
455
+ }
456
+
457
+ preparedTask.registerOnResolveHandler((res) => {
458
+ _debugLog('[_asyncStartCall()] signaling subtask completion due to task completion', {
459
+ childTaskID: preparedTask.id(),
460
+ subtaskID: subtask.id(),
461
+ parentTaskID: subtask.getParentTask().id(),
462
+ });
463
+ subtask.onResolve(res);
464
+ });
465
+
466
+ // TODO(fix): start fns sometimes produce results, how should they be used?
467
+ // the result should theoretically be used for flat lowering, but fused components do
468
+ // this automatically!
469
+ subtask.onStart({ startFnParams });
470
+
471
+ _debugLog("[_asyncStartCall()] initial call", {
472
+ task: preparedTask.id(),
473
+ subtaskID: subtask.id(),
474
+ calleeFnName: callee.name,
475
+ });
476
+
477
+ const callbackResult = callee.apply(null, calleeParams);
478
+
479
+ _debugLog("[_asyncStartCall()] after initial call", {
480
+ task: preparedTask.id(),
481
+ subtaskID: subtask.id(),
482
+ calleeFnName: callee.name,
483
+ });
484
+
485
+ const doSubtaskResolve = () => {
486
+ subtask.deliverResolve();
487
+ };
488
+
489
+ // If a single call resolved the subtask and there is no backpressure in the guest,
490
+ // we can return immediately
491
+ if (subtask.resolved() && !calleeBackpressure) {
492
+ _debugLog("[_asyncStartCall()] instantly resolved", {
493
+ calleeComponentIdx: preparedTask.componentIdx(),
494
+ task: preparedTask.id(),
495
+ subtaskID: subtask.id(),
496
+ callerComponentIdx: subtask.componentIdx(),
497
+ });
498
+
499
+ // If a fused component return function was specified for the subtask,
500
+ // we've likely already called it during resolution of the task.
501
+ //
502
+ // In this case, we do not want to actually return 2 AKA "RETURNED",
503
+ // but the normal started task state, because the fused component expects to get
504
+ // the waitable + the original subtask state (0 AKA "STARTING")
505
+ //
506
+ if (subtask.getCallMetadata().returnFn) {
507
+ return Number(subtask.waitableRep()) << 4 | subtaskState;
508
+ }
509
+
510
+ doSubtaskResolve();
511
+ return AsyncSubtask.State.RETURNED;
512
+ }
513
+
514
+ // Start the (event) driver loop that will resolve the task
515
+ new Promise(async (resolve, reject) => {
516
+ if (subtask.resolved() && calleeBackpressure) {
517
+ await calleeComponentState.waitForBackpressure();
518
+
519
+ _debugLog("[_asyncStartCall()] instantly resolved after cleared backpressure", {
520
+ calleeComponentIdx: preparedTask.componentIdx(),
521
+ task: preparedTask.id(),
522
+ subtaskID: subtask.id(),
523
+ callerComponentIdx: subtask.componentIdx(),
524
+ });
525
+ return;
526
+ }
527
+
528
+ const started = await preparedTask.enter();
529
+ if (!started) {
530
+ _debugLog('[_asyncStartCall()] task failed early', {
531
+ taskID: preparedTask.id(),
532
+ subtaskID: subtask.id(),
533
+ });
534
+ throw new Error("task failed to start");
535
+ return;
536
+ }
537
+
538
+ // TODO: retrieve/pass along actual fn name the callback corresponds to
539
+ // (at least something like `<lifted fn name>_callback`)
540
+ const fnName = [
541
+ '<task ',
542
+ subtask.parentTaskID(),
543
+ '/subtask ',
544
+ subtask.id(),
545
+ '/task ',
546
+ preparedTask.id(),
547
+ '>',
548
+ ].join("");
549
+
550
+ try {
551
+ _debugLog("[_asyncStartCall()] starting driver loop", { fnName, componentIdx: preparedTask.componentIdx(), });
552
+ await _driverLoop({
553
+ componentState: calleeComponentState,
554
+ task: preparedTask,
555
+ fnName,
556
+ isAsync: true,
557
+ callbackResult,
558
+ resolve,
559
+ reject
560
+ });
561
+ } catch (err) {
562
+ _debugLog("[AsyncStartCall] drive loop call failure", { err });
563
+ }
564
+
565
+ });
566
+
567
+ return Number(subtask.waitableRep()) << 4 | subtaskState;
568
+ }
569
+
570
+ function _syncStartCall(callbackIdx) {
571
+ _debugLog('[_syncStartCall()] args', { callbackIdx });
572
+ throw new Error('synchronous start call not implemented!');
573
+ }
574
+
575
+ let dv = new DataView(new ArrayBuffer());
576
+ const dataView = mem => dv.buffer === mem.buffer ? dv : dv = new DataView(mem.buffer);
577
+ const TEXT_DECODER_UTF8 = new TextDecoder();
578
+ const TEXT_ENCODER_UTF8 = new TextEncoder();
579
+
580
+ function _utf8AllocateAndEncode(s, realloc, memory) {
581
+ if (typeof s !== 'string') {
582
+ throw new TypeError('expected a string, received [' + typeof s + ']');
583
+ }
584
+ if (s.length === 0) { return { ptr: 1, len: 0 }; }
585
+ let buf = TEXT_ENCODER_UTF8.encode(s);
586
+ let ptr = realloc(0, 0, 1, buf.length);
587
+ new Uint8Array(memory.buffer).set(buf, ptr);
588
+ return { ptr, len: buf.length, codepoints: [...s].length };
589
+ }
590
+
591
+
592
+ function createNewCurrentTask(args) {
593
+ _debugLog('[createNewCurrentTask()] args', args);
594
+ const {
595
+ componentIdx,
596
+ isAsync,
597
+ entryFnName,
598
+ parentSubtaskID,
599
+ callbackFnName,
600
+ getCallbackFn,
601
+ getParamsFn,
602
+ stringEncoding,
603
+ errHandling,
604
+ getCalleeParamsFn,
605
+ resultPtr,
606
+ callingWasmExport,
607
+ } = args;
608
+ if (componentIdx === undefined || componentIdx === null) {
609
+ throw new Error('missing/invalid component instance index while starting task');
610
+ }
611
+ const taskMetas = ASYNC_TASKS_BY_COMPONENT_IDX.get(componentIdx);
612
+ const callbackFn = getCallbackFn ? getCallbackFn() : null;
613
+
614
+ const newTask = new AsyncTask({
615
+ componentIdx,
616
+ isAsync,
617
+ entryFnName,
618
+ callbackFn,
619
+ callbackFnName,
620
+ stringEncoding,
621
+ getCalleeParamsFn,
622
+ resultPtr,
623
+ errHandling,
624
+ });
625
+
626
+ const newTaskID = newTask.id();
627
+ const newTaskMeta = { id: newTaskID, componentIdx, task: newTask };
628
+
629
+ ASYNC_CURRENT_TASK_IDS.push(newTaskID);
630
+ ASYNC_CURRENT_COMPONENT_IDXS.push(componentIdx);
631
+
632
+ if (!taskMetas) {
633
+ ASYNC_TASKS_BY_COMPONENT_IDX.set(componentIdx, [newTaskMeta]);
634
+ } else {
635
+ taskMetas.push(newTaskMeta);
636
+ }
637
+
638
+ return [newTask, newTaskID];
639
+ }
640
+
641
+ function endCurrentTask(componentIdx, taskID) {
642
+ componentIdx ??= ASYNC_CURRENT_COMPONENT_IDXS.at(-1);
643
+ taskID ??= ASYNC_CURRENT_TASK_IDS.at(-1);
644
+ _debugLog('[endCurrentTask()] args', { componentIdx, taskID });
645
+
646
+ if (componentIdx === undefined || componentIdx === null) {
647
+ throw new Error('missing/invalid component instance index while ending current task');
648
+ }
649
+
650
+ const tasks = ASYNC_TASKS_BY_COMPONENT_IDX.get(componentIdx);
651
+ if (!tasks || !Array.isArray(tasks)) {
652
+ throw new Error('missing/invalid tasks for component instance while ending task');
653
+ }
654
+ if (tasks.length == 0) {
655
+ throw new Error('no current task(s) for component instance while ending task');
656
+ }
657
+
658
+ if (taskID) {
659
+ const last = tasks[tasks.length - 1];
660
+ if (last.id !== taskID) {
661
+ // throw new Error('current task does not match expected task ID');
662
+ return;
663
+ }
664
+ }
665
+
666
+ ASYNC_CURRENT_TASK_IDS.pop();
667
+ ASYNC_CURRENT_COMPONENT_IDXS.pop();
668
+
669
+ const taskMeta = tasks.pop();
670
+ return taskMeta.task;
671
+ }
672
+ const ASYNC_TASKS_BY_COMPONENT_IDX = new Map();
673
+
674
+ class AsyncTask {
675
+ static _ID = 0n;
676
+
677
+ static State = {
678
+ INITIAL: 'initial',
679
+ CANCELLED: 'cancelled',
680
+ CANCEL_PENDING: 'cancel-pending',
681
+ CANCEL_DELIVERED: 'cancel-delivered',
682
+ RESOLVED: 'resolved',
683
+ }
684
+
685
+ static BlockResult = {
686
+ CANCELLED: 'block.cancelled',
687
+ NOT_CANCELLED: 'block.not-cancelled',
688
+ }
689
+
690
+ #id;
691
+ #componentIdx;
692
+ #state;
693
+ #isAsync;
694
+ #entryFnName = null;
695
+ #subtasks = [];
696
+
697
+ #onResolveHandlers = [];
698
+ #completionPromise = null;
699
+
700
+ #memoryIdx = null;
701
+
702
+ #callbackFn = null;
703
+ #callbackFnName = null;
704
+
705
+ #postReturnFn = null;
706
+
707
+ #getCalleeParamsFn = null;
708
+
709
+ #stringEncoding = null;
710
+
711
+ #parentSubtask = null;
712
+
713
+ #needsExclusiveLock = false;
714
+
715
+ #errHandling;
716
+
717
+ #backpressurePromise;
718
+ #backpressureWaiters = 0n;
719
+
720
+ #returnLowerFns = null;
721
+
722
+ cancelled = false;
723
+ requested = false;
724
+ alwaysTaskReturn = false;
725
+
726
+ returnCalls = 0;
727
+ storage = [0, 0];
728
+ borrowedHandles = {};
729
+
730
+ awaitableResume = null;
731
+ awaitableCancel = null;
732
+
733
+ constructor(opts) {
734
+ this.#id = ++AsyncTask._ID;
735
+
736
+ if (opts?.componentIdx === undefined) {
737
+ throw new TypeError('missing component id during task creation');
738
+ }
739
+ this.#componentIdx = opts.componentIdx;
740
+
741
+ this.#state = AsyncTask.State.INITIAL;
742
+ this.#isAsync = opts?.isAsync ?? false;
743
+ this.#entryFnName = opts.entryFnName;
744
+
745
+ const {
746
+ promise: completionPromise,
747
+ resolve: resolveCompletionPromise,
748
+ reject: rejectCompletionPromise,
749
+ } = promiseWithResolvers();
750
+ this.#completionPromise = completionPromise;
751
+
752
+ this.#onResolveHandlers.push((results) => {
753
+ resolveCompletionPromise(results);
754
+ })
755
+
756
+ if (opts.callbackFn) { this.#callbackFn = opts.callbackFn; }
757
+ if (opts.callbackFnName) { this.#callbackFnName = opts.callbackFnName; }
758
+
759
+ if (opts.getCalleeParamsFn) { this.#getCalleeParamsFn = opts.getCalleeParamsFn; }
760
+
761
+ if (opts.stringEncoding) { this.#stringEncoding = opts.stringEncoding; }
762
+
763
+ if (opts.parentSubtask) { this.#parentSubtask = opts.parentSubtask; }
764
+
765
+ this.#needsExclusiveLock = this.isSync() || !this.hasCallback();
766
+
767
+ if (opts.errHandling) { this.#errHandling = opts.errHandling; }
768
+ }
769
+
770
+ taskState() { return this.#state; }
771
+ id() { return this.#id; }
772
+ componentIdx() { return this.#componentIdx; }
773
+ isAsync() { return this.#isAsync; }
774
+ entryFnName() { return this.#entryFnName; }
775
+ completionPromise() { return this.#completionPromise; }
776
+
777
+ isAsync() { return this.#isAsync; }
778
+ isSync() { return !this.isAsync(); }
779
+
780
+ getErrHandling() { return this.#errHandling; }
781
+
782
+ hasCallback() { return this.#callbackFn !== null; }
783
+
784
+ setReturnMemoryIdx(idx) { this.#memoryIdx = idx; }
785
+ getReturnMemoryIdx() { return this.#memoryIdx; }
786
+
787
+ setReturnLowerFns(fns) { this.#returnLowerFns = fns; }
788
+ getReturnLowerFns() { return this.#returnLowerFns; }
789
+
790
+ setParentSubtask(subtask) {
791
+ if (!subtask || !(subtask instanceof AsyncSubtask)) { return }
792
+ if (this.#parentSubtask) { throw new Error('parent subtask can only be set once'); }
793
+ this.#parentSubtask = subtask;
794
+ }
795
+
796
+ getParentSubtask() { return this.#parentSubtask; }
797
+
798
+ // TODO(threads): this is very inefficient, we can pass along a root task,
799
+ // and ideally do not need this once thread support is in place
800
+ getRootTask() {
801
+ let currentSubtask = this.getParentSubtask();
802
+ let task = this;
803
+ while (currentSubtask) {
804
+ task = currentSubtask.getParentTask();
805
+ currentSubtask = task.getParentSubtask();
806
+ }
807
+ return task;
808
+ }
809
+
810
+ setPostReturnFn(f) {
811
+ if (!f) { return; }
812
+ if (this.#postReturnFn) { throw new Error('postReturn fn can only be set once'); }
813
+ this.#postReturnFn = f;
814
+ }
815
+
816
+ setCallbackFn(f, name) {
817
+ if (!f) { return; }
818
+ if (this.#callbackFn) { throw new Error('callback fn can only be set once'); }
819
+ this.#callbackFn = f;
820
+ this.#callbackFnName = name;
821
+ }
822
+
823
+ getCallbackFnName() {
824
+ if (!this.#callbackFnName) { return undefined; }
825
+ return this.#callbackFnName;
826
+ }
827
+
828
+ runCallbackFn(...args) {
829
+ if (!this.#callbackFn) { throw new Error('on callback function has been set for task'); }
830
+ return this.#callbackFn.apply(null, args);
831
+ }
832
+
833
+ getCalleeParams() {
834
+ if (!this.#getCalleeParamsFn) { throw new Error('missing/invalid getCalleeParamsFn'); }
835
+ return this.#getCalleeParamsFn();
836
+ }
837
+
838
+ mayEnter(task) {
839
+ const cstate = getOrCreateAsyncState(this.#componentIdx);
840
+ if (cstate.hasBackpressure()) {
841
+ _debugLog('[AsyncTask#mayEnter()] disallowed due to backpressure', { taskID: this.#id });
842
+ return false;
843
+ }
844
+ if (!cstate.callingSyncImport()) {
845
+ _debugLog('[AsyncTask#mayEnter()] disallowed due to sync import call', { taskID: this.#id });
846
+ return false;
847
+ }
848
+ const callingSyncExportWithSyncPending = cstate.callingSyncExport && !task.isAsync;
849
+ if (!callingSyncExportWithSyncPending) {
850
+ _debugLog('[AsyncTask#mayEnter()] disallowed due to sync export w/ sync pending', { taskID: this.#id });
851
+ return false;
852
+ }
853
+ return true;
854
+ }
855
+
856
+ async enter() {
857
+ _debugLog('[AsyncTask#enter()] args', { taskID: this.#id });
858
+ const cstate = getOrCreateAsyncState(this.#componentIdx);
859
+
860
+ if (this.isSync()) { return true; }
861
+
862
+ if (cstate.hasBackpressure()) {
863
+ cstate.addBackpressureWaiter();
864
+
865
+ const result = await this.waitUntil({
866
+ readyFn: () => !cstate.hasBackpressure(),
867
+ cancellable: true,
868
+ });
869
+
870
+ cstate.removeBackpressureWaiter();
871
+
872
+ if (result === AsyncTask.BlockResult.CANCELLED) {
873
+ this.cancel();
874
+ return false;
875
+ }
876
+ }
877
+
878
+ if (this.needsExclusiveLock()) { cstate.exclusiveLock(); }
879
+
880
+ return true;
881
+ }
882
+
883
+ isRunning() {
884
+ return this.#state !== AsyncTask.State.RESOLVED;
885
+ }
886
+
887
+ async waitUntil(opts) {
888
+ const { readyFn, waitableSetRep, cancellable } = opts;
889
+ _debugLog('[AsyncTask#waitUntil()] args', { taskID: this.#id, waitableSetRep, cancellable });
890
+
891
+ const state = getOrCreateAsyncState(this.#componentIdx);
892
+ const wset = state.waitableSets.get(waitableSetRep);
893
+
894
+ let event;
895
+
896
+ wset.incrementNumWaiting();
897
+
898
+ const keepGoing = await this.suspendUntil({
899
+ readyFn: () => {
900
+ const hasPendingEvent = wset.hasPendingEvent();
901
+ return readyFn() && hasPendingEvent;
902
+ },
903
+ cancellable,
904
+ });
905
+
906
+ if (keepGoing) {
907
+ event = wset.getPendingEvent();
908
+ } else {
909
+ event = {
910
+ code: ASYNC_EVENT_CODE.TASK_CANCELLED,
911
+ index: 0,
912
+ result: 0,
913
+ };
914
+ }
915
+
916
+ wset.decrementNumWaiting();
917
+
918
+ return event;
919
+ }
920
+
921
+ async onBlock(awaitable) {
922
+ _debugLog('[AsyncTask#onBlock()] args', { taskID: this.#id, awaitable });
923
+ if (!(awaitable instanceof Awaitable)) {
924
+ throw new Error('invalid awaitable during onBlock');
925
+ }
926
+
927
+ // Build a promise that this task can await on which resolves when it is awoken
928
+ const { promise, resolve, reject } = promiseWithResolvers();
929
+ this.awaitableResume = () => {
930
+ _debugLog('[AsyncTask] resuming after onBlock', { taskID: this.#id });
931
+ resolve();
932
+ };
933
+ this.awaitableCancel = (err) => {
934
+ _debugLog('[AsyncTask] rejecting after onBlock', { taskID: this.#id, err });
935
+ reject(err);
936
+ };
937
+
938
+ // Park this task/execution to be handled later
939
+ const state = getOrCreateAsyncState(this.#componentIdx);
940
+ state.parkTaskOnAwaitable({ awaitable, task: this });
941
+
942
+ try {
943
+ await promise;
944
+ return AsyncTask.BlockResult.NOT_CANCELLED;
945
+ } catch (err) {
946
+ // rejection means task cancellation
947
+ return AsyncTask.BlockResult.CANCELLED;
948
+ }
949
+ }
950
+
951
+ async asyncOnBlock(awaitable) {
952
+ _debugLog('[AsyncTask#asyncOnBlock()] args', { taskID: this.#id, awaitable });
953
+ if (!(awaitable instanceof Awaitable)) {
954
+ throw new Error('invalid awaitable during onBlock');
955
+ }
956
+ // TODO: watch for waitable AND cancellation
957
+ // TODO: if it WAS cancelled:
958
+ // - return true
959
+ // - only once per subtask
960
+ // - do not wait on the scheduler
961
+ // - control flow should go to the subtask (only once)
962
+ // - Once subtask blocks/resolves, reqlinquishControl() will tehn resolve request_cancel_end (without scheduler lock release)
963
+ // - control flow goes back to request_cancel
964
+ //
965
+ // Subtask cancellation should work similarly to an async import call -- runs sync up until
966
+ // the subtask blocks or resolves
967
+ //
968
+ throw new Error('AsyncTask#asyncOnBlock() not yet implemented');
969
+ }
970
+
971
+ async yieldUntil(opts) {
972
+ const { readyFn, cancellable } = opts;
973
+ _debugLog('[AsyncTask#yieldUntil()] args', { taskID: this.#id, cancellable });
974
+
975
+ const keepGoing = await this.suspendUntil({ readyFn, cancellable });
976
+ if (!keepGoing) {
977
+ return {
978
+ code: ASYNC_EVENT_CODE.TASK_CANCELLED,
979
+ index: 0,
980
+ result: 0,
981
+ };
982
+ }
983
+
984
+ return {
985
+ code: ASYNC_EVENT_CODE.NONE,
986
+ index: 0,
987
+ result: 0,
988
+ };
989
+ }
990
+
991
+ async suspendUntil(opts) {
992
+ const { cancellable, readyFn } = opts;
993
+ _debugLog('[AsyncTask#suspendUntil()] args', { cancellable });
994
+
995
+ const pendingCancelled = this.deliverPendingCancel({ cancellable });
996
+ if (pendingCancelled) { return false; }
997
+
998
+ const completed = await this.immediateSuspendUntil({ readyFn, cancellable });
999
+ return completed;
1000
+ }
1001
+
1002
+ // TODO(threads): equivalent to thread.suspend_until()
1003
+ async immediateSuspendUntil(opts) {
1004
+ const { cancellable, readyFn } = opts;
1005
+ _debugLog('[AsyncTask#immediateSuspendUntil()] args', { cancellable, readyFn });
1006
+
1007
+ const ready = readyFn();
1008
+ if (ready && !ASYNC_DETERMINISM && _coinFlip()) {
1009
+ return true;
1010
+ }
1011
+
1012
+ const cstate = getOrCreateAsyncState(this.#componentIdx);
1013
+ cstate.addPendingTask(this);
1014
+
1015
+ const keepGoing = await this.immediateSuspend({ cancellable, readyFn });
1016
+ return keepGoing;
1017
+ }
1018
+
1019
+ async immediateSuspend(opts) { // NOTE: equivalent to thread.suspend()
1020
+ // TODO(threads): store readyFn on the thread
1021
+ const { cancellable, readyFn } = opts;
1022
+ _debugLog('[AsyncTask#immediateSuspend()] args', { cancellable, readyFn });
1023
+
1024
+ const pendingCancelled = this.deliverPendingCancel({ cancellable });
1025
+ if (pendingCancelled) { return false; }
1026
+
1027
+ const cstate = getOrCreateAsyncState(this.#componentIdx);
1028
+
1029
+ // TODO(fix): update this to tick until there is no more action to take.
1030
+ setTimeout(() => cstate.tick(), 0);
1031
+
1032
+ const taskWait = await cstate.suspendTask({ task: this, readyFn });
1033
+ const keepGoing = await taskWait;
1034
+ return keepGoing;
1035
+ }
1036
+
1037
+ deliverPendingCancel(opts) {
1038
+ const { cancellable } = opts;
1039
+ _debugLog('[AsyncTask#deliverPendingCancel()] args', { cancellable });
1040
+
1041
+ if (cancellable && this.#state === AsyncTask.State.PENDING_CANCEL) {
1042
+ this.#state = Task.State.CANCEL_DELIVERED;
1043
+ return true;
1044
+ }
1045
+
1046
+ return false;
1047
+ }
1048
+
1049
+ isCancelled() { return this.cancelled }
1050
+
1051
+ cancel() {
1052
+ _debugLog('[AsyncTask#cancel()] args', { });
1053
+ if (!this.taskState() !== AsyncTask.State.CANCEL_DELIVERED) {
1054
+ throw new Error(`(component [${this.#componentIdx}]) task [${this.#id}] invalid task state for cancellation`);
1055
+ }
1056
+ if (this.borrowedHandles.length > 0) { throw new Error('task still has borrow handles'); }
1057
+ this.cancelled = true;
1058
+ this.onResolve(new Error('cancelled'));
1059
+ this.#state = AsyncTask.State.RESOLVED;
1060
+ }
1061
+
1062
+ onResolve(taskValue) {
1063
+ for (const f of this.#onResolveHandlers) {
1064
+ try {
1065
+ f(taskValue);
1066
+ } catch (err) {
1067
+ console.error("error during task resolve handler", err);
1068
+ throw err;
1069
+ }
1070
+ }
1071
+
1072
+ if (this.#postReturnFn) {
1073
+ _debugLog('[AsyncTask#onResolve()] running post return ', {
1074
+ componentIdx: this.#componentIdx,
1075
+ taskID: this.#id,
1076
+ });
1077
+ this.#postReturnFn();
1078
+ }
1079
+ }
1080
+
1081
+ registerOnResolveHandler(f) {
1082
+ this.#onResolveHandlers.push(f);
1083
+ }
1084
+
1085
+ resolve(results) {
1086
+ _debugLog('[AsyncTask#resolve()] args', {
1087
+ results,
1088
+ componentIdx: this.#componentIdx,
1089
+ taskID: this.#id,
1090
+ });
1091
+
1092
+ if (this.#state === AsyncTask.State.RESOLVED) {
1093
+ throw new Error(`(component [${this.#componentIdx}]) task [${this.#id}] is already resolved (did you forget to wait for an import?)`);
1094
+ }
1095
+ if (this.borrowedHandles.length > 0) { throw new Error('task still has borrow handles'); }
1096
+ switch (results.length) {
1097
+ case 0:
1098
+ this.onResolve(undefined);
1099
+ break;
1100
+ case 1:
1101
+ this.onResolve(results[0]);
1102
+ break;
1103
+ default:
1104
+ throw new Error('unexpected number of results');
1105
+ }
1106
+ this.#state = AsyncTask.State.RESOLVED;
1107
+ }
1108
+
1109
+ exit() {
1110
+ _debugLog('[AsyncTask#exit()] args', { });
1111
+
1112
+ // TODO: ensure there is only one task at a time (scheduler.lock() functionality)
1113
+ if (this.#state !== AsyncTask.State.RESOLVED) {
1114
+ // TODO(fix): only fused, manually specified post returns seem to break this invariant,
1115
+ // as the TaskReturn trampoline is not activated it seems.
1116
+ //
1117
+ // see: test/p3/ported/wasmtime/component-async/post-return.js
1118
+ //
1119
+ // We *should* be able to upgrade this to be more strict and throw at some point,
1120
+ // which may involve rewriting the upstream test to surface task return manually somehow.
1121
+ //
1122
+ //throw new Error(`(component [${this.#componentIdx}]) task [${this.#id}] exited without resolution`);
1123
+ _debugLog('[AsyncTask#exit()] task exited without resolution', {
1124
+ componentIdx: this.#componentIdx,
1125
+ taskID: this.#id,
1126
+ subtask: this.getParentSubtask(),
1127
+ subtaskID: this.getParentSubtask()?.id(),
1128
+ });
1129
+ this.#state = AsyncTask.State.RESOLVED;
1130
+ }
1131
+
1132
+ if (this.borrowedHandles > 0) {
1133
+ throw new Error('task [${this.#id}] exited without clearing borrowed handles');
1134
+ }
1135
+
1136
+ const state = getOrCreateAsyncState(this.#componentIdx);
1137
+ if (!state) { throw new Error('missing async state for component [' + this.#componentIdx + ']'); }
1138
+ if (!this.#isAsync && !state.inSyncExportCall) {
1139
+ throw new Error('sync task must be run from components known to be in a sync export call');
1140
+ }
1141
+ state.inSyncExportCall = false;
1142
+
1143
+ if (this.needsExclusiveLock() && !state.isExclusivelyLocked()) {
1144
+ throw new Error('task [' + this.#id + '] exit: component [' + this.#componentIdx + '] should have been exclusively locked');
1145
+ }
1146
+
1147
+ state.exclusiveRelease();
1148
+ }
1149
+
1150
+ needsExclusiveLock() { return this.#needsExclusiveLock; }
1151
+
1152
+ createSubtask(args) {
1153
+ _debugLog('[AsyncTask#createSubtask()] args', args);
1154
+ const { componentIdx, childTask, callMetadata } = args;
1155
+ const newSubtask = new AsyncSubtask({
1156
+ componentIdx,
1157
+ childTask,
1158
+ parentTask: this,
1159
+ callMetadata,
1160
+ });
1161
+ this.#subtasks.push(newSubtask);
1162
+ return newSubtask;
1163
+ }
1164
+
1165
+ getLatestSubtask() { return this.#subtasks.at(-1); }
1166
+
1167
+ currentSubtask() {
1168
+ _debugLog('[AsyncTask#currentSubtask()]');
1169
+ if (this.#subtasks.length === 0) { return undefined; }
1170
+ return this.#subtasks.at(-1);
1171
+ }
1172
+
1173
+ endCurrentSubtask() {
1174
+ _debugLog('[AsyncTask#endCurrentSubtask()]');
1175
+ if (this.#subtasks.length === 0) { throw new Error('cannot end current subtask: no current subtask'); }
1176
+ const subtask = this.#subtasks.pop();
1177
+ subtask.drop();
1178
+ return subtask;
1179
+ }
1180
+ }
1181
+ const ASYNC_STATE = new Map();
1182
+
1183
+ function getOrCreateAsyncState(componentIdx, init) {
1184
+ if (!ASYNC_STATE.has(componentIdx)) {
1185
+ const newState = new ComponentAsyncState({ componentIdx });
1186
+ ASYNC_STATE.set(componentIdx, newState);
1187
+ }
1188
+ return ASYNC_STATE.get(componentIdx);
1189
+ }
1190
+
1191
+ class ComponentAsyncState {
1192
+ static EVENT_HANDLER_EVENTS = [ 'backpressure-change' ];
1193
+
1194
+ #componentIdx;
1195
+ #callingAsyncImport = false;
1196
+ #syncImportWait = promiseWithResolvers();
1197
+ #locked = false;
1198
+ #parkedTasks = new Map();
1199
+ #suspendedTasksByTaskID = new Map();
1200
+ #suspendedTaskIDs = [];
1201
+ #pendingTasks = [];
1202
+ #errored = null;
1203
+
1204
+ #backpressure = 0;
1205
+ #backpressureWaiters = 0n;
1206
+
1207
+ #handlerMap = new Map();
1208
+ #nextHandlerID = 0n;
1209
+
1210
+ mayLeave = true;
1211
+
1212
+ #streams;
1213
+
1214
+ waitableSets;
1215
+ waitables;
1216
+ subtasks;
1217
+
1218
+ constructor(args) {
1219
+ this.#componentIdx = args.componentIdx;
1220
+ this.waitableSets = new RepTable({ target: `component [${this.#componentIdx}] waitable sets` });
1221
+ this.waitables = new RepTable({ target: `component [${this.#componentIdx}] waitables` });
1222
+ this.subtasks = new RepTable({ target: `component [${this.#componentIdx}] subtasks` });
1223
+ this.#streams = new Map();
1224
+ };
1225
+
1226
+ componentIdx() { return this.#componentIdx; }
1227
+ streams() { return this.#streams; }
1228
+
1229
+ errored() { return this.#errored !== null; }
1230
+ setErrored(err) {
1231
+ _debugLog('[ComponentAsyncState#setErrored()] component errored', { err, componentIdx: this.#componentIdx });
1232
+ if (this.#errored) { return; }
1233
+ if (!err) {
1234
+ err = new Error('error elswehere (see other component instance error)')
1235
+ err.componentIdx = this.#componentIdx;
1236
+ }
1237
+ this.#errored = err;
1238
+ }
1239
+
1240
+ callingSyncImport(val) {
1241
+ if (val === undefined) { return this.#callingAsyncImport; }
1242
+ if (typeof val !== 'boolean') { throw new TypeError('invalid setting for async import'); }
1243
+ const prev = this.#callingAsyncImport;
1244
+ this.#callingAsyncImport = val;
1245
+ if (prev === true && this.#callingAsyncImport === false) {
1246
+ this.#notifySyncImportEnd();
1247
+ }
1248
+ }
1249
+
1250
+ #notifySyncImportEnd() {
1251
+ const existing = this.#syncImportWait;
1252
+ this.#syncImportWait = promiseWithResolvers();
1253
+ existing.resolve();
1254
+ }
1255
+
1256
+ async waitForSyncImportCallEnd() {
1257
+ await this.#syncImportWait.promise;
1258
+ }
1259
+
1260
+ setBackpressure(v) { this.#backpressure = v; }
1261
+ getBackpressure(v) { return this.#backpressure; }
1262
+ incrementBackpressure() {
1263
+ const newValue = this.getBackpressure() + 1;
1264
+ if (newValue > 2**16) { throw new Error("invalid backpressure value, overflow"); }
1265
+ this.setBackpressure(newValue);
1266
+ }
1267
+ decrementBackpressure() {
1268
+ this.setBackpressure(Math.max(0, this.getBackpressure() - 1));
1269
+ }
1270
+ hasBackpressure() { return this.#backpressure > 0; }
1271
+
1272
+ waitForBackpressure() {
1273
+ let backpressureCleared = false;
1274
+ const cstate = this;
1275
+ cstate.addBackpressureWaiter();
1276
+ const handlerID = this.registerHandler({
1277
+ event: 'backpressure-change',
1278
+ fn: (bp) => {
1279
+ if (bp === 0) {
1280
+ cstate.removeHandler(handlerID);
1281
+ backpressureCleared = true;
1282
+ }
1283
+ }
1284
+ });
1285
+ return new Promise((resolve) => {
1286
+ const interval = setInterval(() => {
1287
+ if (backpressureCleared) { return; }
1288
+ clearInterval(interval);
1289
+ cstate.removeBackpressureWaiter();
1290
+ resolve(null);
1291
+ }, 0);
1292
+ });
1293
+ }
1294
+
1295
+ registerHandler(args) {
1296
+ const { event, fn } = args;
1297
+ if (!event) { throw new Error("missing handler event"); }
1298
+ if (!fn) { throw new Error("missing handler fn"); }
1299
+
1300
+ if (!ComponentAsyncState.EVENT_HANDLER_EVENTS.includes(event)) {
1301
+ throw new Error(`unrecognized event handler [${event}]`);
1302
+ }
1303
+
1304
+ const handlerID = this.#nextHandlerID++;
1305
+ let handlers = this.#handlerMap.get(event);
1306
+ if (!handlers) {
1307
+ handlers = [];
1308
+ this.#handlerMap.set(event, handlers)
1309
+ }
1310
+
1311
+ handlers.push({ id: handlerID, fn, event });
1312
+ return handlerID;
1313
+ }
1314
+
1315
+ removeHandler(args) {
1316
+ const { event, handlerID } = args;
1317
+ const registeredHandlers = this.#handlerMap.get(event);
1318
+ if (!registeredHandlers) { return; }
1319
+ const found = registeredHandlers.find(h => h.id === handlerID);
1320
+ if (!found) { return; }
1321
+ this.#handlerMap.set(event, this.#handlerMap.get(event).filter(h => h.id !== handlerID));
1322
+ }
1323
+
1324
+ getBackpressureWaiters() { return this.#backpressureWaiters; }
1325
+ addBackpressureWaiter() { this.#backpressureWaiters++; }
1326
+ removeBackpressureWaiter() {
1327
+ this.#backpressureWaiters--;
1328
+ if (this.#backpressureWaiters < 0) {
1329
+ throw new Error("unexepctedly negative number of backpressure waiters");
1330
+ }
1331
+ }
1332
+
1333
+ parkTaskOnAwaitable(args) {
1334
+ if (!args.awaitable) { throw new TypeError('missing awaitable when trying to park'); }
1335
+ if (!args.task) { throw new TypeError('missing task when trying to park'); }
1336
+ const { awaitable, task } = args;
1337
+
1338
+ let taskList = this.#parkedTasks.get(awaitable.id());
1339
+ if (!taskList) {
1340
+ taskList = [];
1341
+ this.#parkedTasks.set(awaitable.id(), taskList);
1342
+ }
1343
+ taskList.push(task);
1344
+
1345
+ this.wakeNextTaskForAwaitable(awaitable);
1346
+ }
1347
+
1348
+ wakeNextTaskForAwaitable(awaitable) {
1349
+ if (!awaitable) { throw new TypeError('missing awaitable when waking next task'); }
1350
+ const awaitableID = awaitable.id();
1351
+
1352
+ const taskList = this.#parkedTasks.get(awaitableID);
1353
+ if (!taskList || taskList.length === 0) {
1354
+ _debugLog('[ComponentAsyncState] no tasks waiting for awaitable', { awaitableID: awaitable.id() });
1355
+ return;
1356
+ }
1357
+
1358
+ let task = taskList.shift(); // todo(perf)
1359
+ if (!task) { throw new Error('no task in parked list despite previous check'); }
1360
+
1361
+ if (!task.awaitableResume) {
1362
+ throw new Error('task ready due to awaitable is missing resume', { taskID: task.id(), awaitableID });
1363
+ }
1364
+ task.awaitableResume();
1365
+ }
1366
+
1367
+ // TODO: we might want to check for pre-locked status here
1368
+ exclusiveLock() {
1369
+ this.#locked = true;
1370
+ }
1371
+
1372
+ exclusiveRelease() {
1373
+ _debugLog('[ComponentAsyncState#exclusiveRelease()] releasing', {
1374
+ locked: this.#locked,
1375
+ componentIdx: this.#componentIdx,
1376
+ });
1377
+
1378
+ this.#locked = false
1379
+ }
1380
+
1381
+ isExclusivelyLocked() { return this.#locked === true; }
1382
+
1383
+ #getSuspendedTaskMeta(taskID) {
1384
+ return this.#suspendedTasksByTaskID.get(taskID);
1385
+ }
1386
+
1387
+ #removeSuspendedTaskMeta(taskID) {
1388
+ _debugLog('[ComponentAsyncState#removeSuspendedTaskMeta()] removing suspended task', { taskID });
1389
+ const idx = this.#suspendedTaskIDs.findIndex(t => t === taskID);
1390
+ const meta = this.#suspendedTasksByTaskID.get(taskID);
1391
+ this.#suspendedTaskIDs[idx] = null;
1392
+ this.#suspendedTasksByTaskID.delete(taskID);
1393
+ return meta;
1394
+ }
1395
+
1396
+ #addSuspendedTaskMeta(meta) {
1397
+ if (!meta) { throw new Error('missing task meta'); }
1398
+ const taskID = meta.taskID;
1399
+ this.#suspendedTasksByTaskID.set(taskID, meta);
1400
+ this.#suspendedTaskIDs.push(taskID);
1401
+ if (this.#suspendedTasksByTaskID.size < this.#suspendedTaskIDs.length - 10) {
1402
+ this.#suspendedTaskIDs = this.#suspendedTaskIDs.filter(t => t !== null);
1403
+ }
1404
+ }
1405
+
1406
+ suspendTask(args) {
1407
+ // TODO(threads): readyFn is normally on the thread
1408
+ const { task, readyFn } = args;
1409
+ const taskID = task.id();
1410
+ _debugLog('[ComponentAsyncState#suspendTask()]', { taskID });
1411
+
1412
+ if (this.#getSuspendedTaskMeta(taskID)) {
1413
+ throw new Error('task [' + taskID + '] already suspended');
1414
+ }
1415
+
1416
+ const { promise, resolve } = Promise.withResolvers();
1417
+ this.#addSuspendedTaskMeta({
1418
+ task,
1419
+ taskID,
1420
+ readyFn,
1421
+ resume: () => {
1422
+ _debugLog('[ComponentAsyncState#suspendTask()] resuming suspended task', { taskID });
1423
+ // TODO(threads): it's thread cancellation we should be checking for below, not task
1424
+ resolve(!task.isCancelled());
1425
+ },
1426
+ });
1427
+
1428
+ return promise;
1429
+ }
1430
+
1431
+ resumeTaskByID(taskID) {
1432
+ const meta = this.#removeSuspendedTaskMeta(taskID);
1433
+ if (!meta) { return; }
1434
+ if (meta.taskID !== taskID) { throw new Error('task ID does not match'); }
1435
+ meta.resume();
1436
+ }
1437
+
1438
+ tick() {
1439
+ _debugLog('[ComponentAsyncState#tick()]', { suspendedTaskIDs: this.#suspendedTaskIDs });
1440
+ const resumableTasks = this.#suspendedTaskIDs.filter(t => t !== null);
1441
+ for (const taskID of resumableTasks) {
1442
+ const meta = this.#suspendedTasksByTaskID.get(taskID);
1443
+ if (!meta || !meta.readyFn) {
1444
+ throw new Error(`missing/invalid task despite ID [${taskID}] being present`);
1445
+ }
1446
+
1447
+ const isReady = meta.readyFn();
1448
+ if (!isReady) { continue; }
1449
+
1450
+ this.resumeTaskByID(taskID);
1451
+ }
1452
+
1453
+ return this.#suspendedTaskIDs.filter(t => t !== null).length === 0;
1454
+ }
1455
+
1456
+ addPendingTask(task) {
1457
+ this.#pendingTasks.push(task);
1458
+ }
1459
+
1460
+ addStreamEnd(args) {
1461
+ _debugLog('[ComponentAsyncState#addStreamEnd()] args', args);
1462
+ const { tableIdx, streamEnd } = args;
1463
+
1464
+ let tbl = this.#streams.get(tableIdx);
1465
+ if (!tbl) {
1466
+ tbl = new RepTable({ target: `component [${this.#componentIdx}] streams` });
1467
+ this.#streams.set(tableIdx, tbl);
1468
+ }
1469
+
1470
+ const streamIdx = tbl.insert(streamEnd);
1471
+ return streamIdx;
1472
+ }
1473
+
1474
+ createStream(args) {
1475
+ _debugLog('[ComponentAsyncState#createStream()] args', args);
1476
+ const { tableIdx, elemMeta } = args;
1477
+ if (tableIdx === undefined) { throw new Error("missing table idx while adding stream"); }
1478
+ if (elemMeta === undefined) { throw new Error("missing element metadata while adding stream"); }
1479
+
1480
+ let tbl = this.#streams.get(tableIdx);
1481
+ if (!tbl) {
1482
+ tbl = new RepTable({ target: `component [${this.#componentIdx}] streams` });
1483
+ this.#streams.set(tableIdx, tbl);
1484
+ }
1485
+
1486
+ const stream = new InternalStream({
1487
+ tableIdx,
1488
+ componentIdx: this.#componentIdx,
1489
+ elemMeta,
1490
+ });
1491
+ const writeEndIdx = tbl.insert(stream.getWriteEnd());
1492
+ stream.setWriteEndIdx(writeEndIdx);
1493
+ const readEndIdx = tbl.insert(stream.getReadEnd());
1494
+ stream.setReadEndIdx(readEndIdx);
1495
+
1496
+ const rep = STREAMS.insert(stream);
1497
+ stream.setRep(rep);
1498
+
1499
+ return { writeEndIdx, readEndIdx };
1500
+ }
1501
+
1502
+ getStreamEnd(args) {
1503
+ _debugLog('[ComponentAsyncState#getStreamEnd()] args', args);
1504
+ const { tableIdx, streamIdx } = args;
1505
+ if (tableIdx === undefined) { throw new Error('missing table idx while retrieveing stream end'); }
1506
+ if (streamIdx === undefined) { throw new Error('missing stream idx while retrieveing stream end'); }
1507
+
1508
+ const tbl = this.#streams.get(tableIdx);
1509
+ if (!tbl) {
1510
+ throw new Error(`missing stream table [${tableIdx}] in component [${this.#componentIdx}] while getting stream`);
1511
+ }
1512
+
1513
+ const stream = tbl.get(streamIdx);
1514
+ return stream;
1515
+ }
1516
+
1517
+ removeStreamEnd(args) {
1518
+ _debugLog('[ComponentAsyncState#removeStreamEnd()] args', args);
1519
+ const { tableIdx, streamIdx } = args;
1520
+ if (tableIdx === undefined) { throw new Error("missing table idx while removing stream end"); }
1521
+ if (streamIdx === undefined) { throw new Error("missing stream idx while removing stream end"); }
1522
+
1523
+ const tbl = this.#streams.get(tableIdx);
1524
+ if (!tbl) {
1525
+ throw new Error(`missing stream table [${tableIdx}] in component [${this.#componentIdx}] while removing stream end`);
1526
+ }
1527
+
1528
+ const stream = tbl.get(streamIdx);
1529
+ if (!stream) { throw new Error(`component [${this.#componentIdx}] missing stream [${streamIdx}]`); }
1530
+
1531
+ const removed = tbl.remove(streamIdx);
1532
+ if (!removed) {
1533
+ throw new Error(`missing stream [${streamIdx}] (table [${tableIdx}]) in component [${this.#componentIdx}] while removing stream end`);
1534
+ }
1535
+
1536
+ return stream;
1537
+ }
1538
+ }
1539
+
1540
+ const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
1541
+ let _fs;
1542
+ async function fetchCompile (url) {
1543
+ if (isNode) {
1544
+ _fs = _fs || await import('node:fs/promises');
1545
+ return WebAssembly.compile(await _fs.readFile(url));
1546
+ }
1547
+ return fetch(url).then(WebAssembly.compileStreaming);
1548
+ }
1549
+
1550
+ class ComponentError extends Error {
1551
+ constructor (value) {
1552
+ const enumerable = typeof value !== 'string';
1553
+ super(enumerable ? `${String(value)} (see error.payload)` : value);
1554
+ Object.defineProperty(this, 'payload', { value, enumerable });
1555
+ }
1556
+ }
1557
+
1558
+ function throwInvalidBool() {
1559
+ throw new TypeError('invalid variant discriminant for bool');
1560
+ }
1561
+
1562
+ const instantiateCore = WebAssembly.instantiate;
1563
+
1564
+
1565
+ let exports0;
1566
+ let memory0;
1567
+ let realloc0;
1568
+ let postReturn0;
1569
+ let exports0ParseConfig;
1570
+
1571
+ function parseConfig(arg0, arg1) {
1572
+
1573
+ var encodeRes = _utf8AllocateAndEncode(arg0, realloc0, memory0);
1574
+ var ptr0= encodeRes.ptr;
1575
+ var len0 = encodeRes.len;
1576
+
1577
+ var vec2 = arg1;
1578
+ var len2 = vec2.length;
1579
+ var result2 = realloc0(0, 0, 4, len2 * 8);
1580
+ for (let i = 0; i < vec2.length; i++) {
1581
+ const e = vec2[i];
1582
+ const base = result2 + i * 8;
1583
+ var encodeRes = _utf8AllocateAndEncode(e, realloc0, memory0);
1584
+ var ptr1= encodeRes.ptr;
1585
+ var len1 = encodeRes.len;
1586
+
1587
+ dataView(memory0).setUint32(base + 4, len1, true);
1588
+ dataView(memory0).setUint32(base + 0, ptr1, true);
1589
+ }
1590
+ _debugLog('[iface="parse-config", function="parse-config"][Instruction::CallWasm] enter', {
1591
+ funcName: 'parse-config',
1592
+ paramCount: 4,
1593
+ async: false,
1594
+ postReturn: true,
1595
+ });
1596
+ const hostProvided = false;
1597
+
1598
+ const [task, _wasm_call_currentTaskID] = createNewCurrentTask({
1599
+ componentIdx: 0,
1600
+ isAsync: false,
1601
+ entryFnName: 'exports0ParseConfig',
1602
+ getCallbackFn: () => null,
1603
+ callbackFnName: 'null',
1604
+ errHandling: 'throw-result-err',
1605
+ callingWasmExport: true,
1606
+ });
1607
+
1608
+ let ret = exports0ParseConfig(ptr0, len0, result2, len2);
1609
+ endCurrentTask(0);
1610
+ let variant60;
1611
+ switch (dataView(memory0).getUint8(ret + 0, true)) {
1612
+ case 0: {
1613
+ var len27 = dataView(memory0).getUint32(ret + 8, true);
1614
+ var base27 = dataView(memory0).getUint32(ret + 4, true);
1615
+ var result27 = [];
1616
+ for (let i = 0; i < len27; i++) {
1617
+ const base = base27 + i * 168;
1618
+ var ptr3 = dataView(memory0).getUint32(base + 0, true);
1619
+ var len3 = dataView(memory0).getUint32(base + 4, true);
1620
+ var result3 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr3, len3));
1621
+ var len7 = dataView(memory0).getUint32(base + 12, true);
1622
+ var base7 = dataView(memory0).getUint32(base + 8, true);
1623
+ var result7 = [];
1624
+ for (let i = 0; i < len7; i++) {
1625
+ const base = base7 + i * 36;
1626
+ var ptr4 = dataView(memory0).getUint32(base + 0, true);
1627
+ var len4 = dataView(memory0).getUint32(base + 4, true);
1628
+ var result4 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr4, len4));
1629
+ var ptr5 = dataView(memory0).getUint32(base + 8, true);
1630
+ var len5 = dataView(memory0).getUint32(base + 12, true);
1631
+ var result5 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr5, len5));
1632
+ let enum6;
1633
+ switch (dataView(memory0).getUint8(base + 16, true)) {
1634
+ case 0: {
1635
+ enum6 = 'literal';
1636
+ break;
1637
+ }
1638
+ case 1: {
1639
+ enum6 = 'quoted-string';
1640
+ break;
1641
+ }
1642
+ case 2: {
1643
+ enum6 = 'single-quoted-string';
1644
+ break;
1645
+ }
1646
+ case 3: {
1647
+ enum6 = 'variable';
1648
+ break;
1649
+ }
1650
+ default: {
1651
+ throw new TypeError('invalid discriminant specified for ArgumentType');
1652
+ }
1653
+ }
1654
+ result7.push({
1655
+ value: result4,
1656
+ raw: result5,
1657
+ argType: enum6,
1658
+ line: dataView(memory0).getInt32(base + 20, true) >>> 0,
1659
+ column: dataView(memory0).getInt32(base + 24, true) >>> 0,
1660
+ startOffset: dataView(memory0).getInt32(base + 28, true) >>> 0,
1661
+ endOffset: dataView(memory0).getInt32(base + 32, true) >>> 0,
1662
+ });
1663
+ }
1664
+ var ptr8 = dataView(memory0).getUint32(base + 40, true);
1665
+ var len8 = dataView(memory0).getUint32(base + 44, true);
1666
+ var result8 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr8, len8));
1667
+ var ptr9 = dataView(memory0).getUint32(base + 48, true);
1668
+ var len9 = dataView(memory0).getUint32(base + 52, true);
1669
+ var result9 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr9, len9));
1670
+ var ptr10 = dataView(memory0).getUint32(base + 56, true);
1671
+ var len10 = dataView(memory0).getUint32(base + 60, true);
1672
+ var result10 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr10, len10));
1673
+ var bool11 = dataView(memory0).getUint8(base + 64, true);
1674
+ var bool12 = dataView(memory0).getUint8(base + 65, true);
1675
+ let variant14;
1676
+ switch (dataView(memory0).getUint8(base + 68, true)) {
1677
+ case 0: {
1678
+ variant14 = undefined;
1679
+ break;
1680
+ }
1681
+ case 1: {
1682
+ var ptr13 = dataView(memory0).getUint32(base + 72, true);
1683
+ var len13 = dataView(memory0).getUint32(base + 76, true);
1684
+ var result13 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr13, len13));
1685
+ variant14 = result13;
1686
+ break;
1687
+ }
1688
+ default: {
1689
+ throw new TypeError('invalid variant discriminant for option');
1690
+ }
1691
+ }
1692
+ let variant16;
1693
+ switch (dataView(memory0).getUint8(base + 80, true)) {
1694
+ case 0: {
1695
+ variant16 = undefined;
1696
+ break;
1697
+ }
1698
+ case 1: {
1699
+ var ptr15 = dataView(memory0).getUint32(base + 84, true);
1700
+ var len15 = dataView(memory0).getUint32(base + 88, true);
1701
+ var result15 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr15, len15));
1702
+ variant16 = result15;
1703
+ break;
1704
+ }
1705
+ default: {
1706
+ throw new TypeError('invalid variant discriminant for option');
1707
+ }
1708
+ }
1709
+ let variant18;
1710
+ switch (dataView(memory0).getUint8(base + 92, true)) {
1711
+ case 0: {
1712
+ variant18 = undefined;
1713
+ break;
1714
+ }
1715
+ case 1: {
1716
+ var ptr17 = dataView(memory0).getUint32(base + 96, true);
1717
+ var len17 = dataView(memory0).getUint32(base + 100, true);
1718
+ var result17 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr17, len17));
1719
+ variant18 = result17;
1720
+ break;
1721
+ }
1722
+ default: {
1723
+ throw new TypeError('invalid variant discriminant for option');
1724
+ }
1725
+ }
1726
+ let variant20;
1727
+ switch (dataView(memory0).getUint8(base + 104, true)) {
1728
+ case 0: {
1729
+ variant20 = undefined;
1730
+ break;
1731
+ }
1732
+ case 1: {
1733
+ var ptr19 = dataView(memory0).getUint32(base + 108, true);
1734
+ var len19 = dataView(memory0).getUint32(base + 112, true);
1735
+ var result19 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr19, len19));
1736
+ variant20 = result19;
1737
+ break;
1738
+ }
1739
+ default: {
1740
+ throw new TypeError('invalid variant discriminant for option');
1741
+ }
1742
+ }
1743
+ let variant21;
1744
+ switch (dataView(memory0).getUint8(base + 124, true)) {
1745
+ case 0: {
1746
+ variant21 = undefined;
1747
+ break;
1748
+ }
1749
+ case 1: {
1750
+ variant21 = dataView(memory0).getInt32(base + 128, true) >>> 0;
1751
+ break;
1752
+ }
1753
+ default: {
1754
+ throw new TypeError('invalid variant discriminant for option');
1755
+ }
1756
+ }
1757
+ let variant22;
1758
+ switch (dataView(memory0).getUint8(base + 132, true)) {
1759
+ case 0: {
1760
+ variant22 = undefined;
1761
+ break;
1762
+ }
1763
+ case 1: {
1764
+ variant22 = dataView(memory0).getInt32(base + 136, true) >>> 0;
1765
+ break;
1766
+ }
1767
+ default: {
1768
+ throw new TypeError('invalid variant discriminant for option');
1769
+ }
1770
+ }
1771
+ let variant23;
1772
+ switch (dataView(memory0).getUint8(base + 140, true)) {
1773
+ case 0: {
1774
+ variant23 = undefined;
1775
+ break;
1776
+ }
1777
+ case 1: {
1778
+ variant23 = dataView(memory0).getInt32(base + 144, true) >>> 0;
1779
+ break;
1780
+ }
1781
+ default: {
1782
+ throw new TypeError('invalid variant discriminant for option');
1783
+ }
1784
+ }
1785
+ var ptr24 = dataView(memory0).getUint32(base + 148, true);
1786
+ var len24 = dataView(memory0).getUint32(base + 152, true);
1787
+ var result24 = new Uint32Array(memory0.buffer.slice(ptr24, ptr24 + len24 * 4));
1788
+ var len26 = dataView(memory0).getUint32(base + 160, true);
1789
+ var base26 = dataView(memory0).getUint32(base + 156, true);
1790
+ var result26 = [];
1791
+ for (let i = 0; i < len26; i++) {
1792
+ const base = base26 + i * 8;
1793
+ var ptr25 = dataView(memory0).getUint32(base + 0, true);
1794
+ var len25 = dataView(memory0).getUint32(base + 4, true);
1795
+ var result25 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr25, len25));
1796
+ result26.push(result25);
1797
+ }
1798
+ result27.push({
1799
+ data: {
1800
+ name: result3,
1801
+ args: result7,
1802
+ line: dataView(memory0).getInt32(base + 16, true) >>> 0,
1803
+ column: dataView(memory0).getInt32(base + 20, true) >>> 0,
1804
+ startOffset: dataView(memory0).getInt32(base + 24, true) >>> 0,
1805
+ endOffset: dataView(memory0).getInt32(base + 28, true) >>> 0,
1806
+ endLine: dataView(memory0).getInt32(base + 32, true) >>> 0,
1807
+ endColumn: dataView(memory0).getInt32(base + 36, true) >>> 0,
1808
+ leadingWhitespace: result8,
1809
+ trailingWhitespace: result9,
1810
+ spaceBeforeTerminator: result10,
1811
+ hasBlock: bool11 == 0 ? false : (bool11 == 1 ? true : throwInvalidBool()),
1812
+ blockIsRaw: bool12 == 0 ? false : (bool12 == 1 ? true : throwInvalidBool()),
1813
+ blockRawContent: variant14,
1814
+ closingBraceLeadingWhitespace: variant16,
1815
+ blockTrailingWhitespace: variant18,
1816
+ trailingCommentText: variant20,
1817
+ nameEndColumn: dataView(memory0).getInt32(base + 116, true) >>> 0,
1818
+ nameEndOffset: dataView(memory0).getInt32(base + 120, true) >>> 0,
1819
+ blockStartLine: variant21,
1820
+ blockStartColumn: variant22,
1821
+ blockStartOffset: variant23,
1822
+ },
1823
+ blockItemIndices: result24,
1824
+ parentStack: result26,
1825
+ depth: dataView(memory0).getInt32(base + 164, true) >>> 0,
1826
+ });
1827
+ }
1828
+ var len29 = dataView(memory0).getUint32(ret + 16, true);
1829
+ var base29 = dataView(memory0).getUint32(ret + 12, true);
1830
+ var result29 = [];
1831
+ for (let i = 0; i < len29; i++) {
1832
+ const base = base29 + i * 8;
1833
+ var ptr28 = dataView(memory0).getUint32(base + 0, true);
1834
+ var len28 = dataView(memory0).getUint32(base + 4, true);
1835
+ var result28 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr28, len28));
1836
+ result29.push(result28);
1837
+ }
1838
+ var len57 = dataView(memory0).getUint32(ret + 24, true);
1839
+ var base57 = dataView(memory0).getUint32(ret + 20, true);
1840
+ var result57 = [];
1841
+ for (let i = 0; i < len57; i++) {
1842
+ const base = base57 + i * 160;
1843
+ let variant55;
1844
+ switch (dataView(memory0).getUint8(base + 0, true)) {
1845
+ case 0: {
1846
+ var ptr30 = dataView(memory0).getUint32(base + 4, true);
1847
+ var len30 = dataView(memory0).getUint32(base + 8, true);
1848
+ var result30 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr30, len30));
1849
+ var len34 = dataView(memory0).getUint32(base + 16, true);
1850
+ var base34 = dataView(memory0).getUint32(base + 12, true);
1851
+ var result34 = [];
1852
+ for (let i = 0; i < len34; i++) {
1853
+ const base = base34 + i * 36;
1854
+ var ptr31 = dataView(memory0).getUint32(base + 0, true);
1855
+ var len31 = dataView(memory0).getUint32(base + 4, true);
1856
+ var result31 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr31, len31));
1857
+ var ptr32 = dataView(memory0).getUint32(base + 8, true);
1858
+ var len32 = dataView(memory0).getUint32(base + 12, true);
1859
+ var result32 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr32, len32));
1860
+ let enum33;
1861
+ switch (dataView(memory0).getUint8(base + 16, true)) {
1862
+ case 0: {
1863
+ enum33 = 'literal';
1864
+ break;
1865
+ }
1866
+ case 1: {
1867
+ enum33 = 'quoted-string';
1868
+ break;
1869
+ }
1870
+ case 2: {
1871
+ enum33 = 'single-quoted-string';
1872
+ break;
1873
+ }
1874
+ case 3: {
1875
+ enum33 = 'variable';
1876
+ break;
1877
+ }
1878
+ default: {
1879
+ throw new TypeError('invalid discriminant specified for ArgumentType');
1880
+ }
1881
+ }
1882
+ result34.push({
1883
+ value: result31,
1884
+ raw: result32,
1885
+ argType: enum33,
1886
+ line: dataView(memory0).getInt32(base + 20, true) >>> 0,
1887
+ column: dataView(memory0).getInt32(base + 24, true) >>> 0,
1888
+ startOffset: dataView(memory0).getInt32(base + 28, true) >>> 0,
1889
+ endOffset: dataView(memory0).getInt32(base + 32, true) >>> 0,
1890
+ });
1891
+ }
1892
+ var ptr35 = dataView(memory0).getUint32(base + 44, true);
1893
+ var len35 = dataView(memory0).getUint32(base + 48, true);
1894
+ var result35 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr35, len35));
1895
+ var ptr36 = dataView(memory0).getUint32(base + 52, true);
1896
+ var len36 = dataView(memory0).getUint32(base + 56, true);
1897
+ var result36 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr36, len36));
1898
+ var ptr37 = dataView(memory0).getUint32(base + 60, true);
1899
+ var len37 = dataView(memory0).getUint32(base + 64, true);
1900
+ var result37 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr37, len37));
1901
+ var bool38 = dataView(memory0).getUint8(base + 68, true);
1902
+ var bool39 = dataView(memory0).getUint8(base + 69, true);
1903
+ let variant41;
1904
+ switch (dataView(memory0).getUint8(base + 72, true)) {
1905
+ case 0: {
1906
+ variant41 = undefined;
1907
+ break;
1908
+ }
1909
+ case 1: {
1910
+ var ptr40 = dataView(memory0).getUint32(base + 76, true);
1911
+ var len40 = dataView(memory0).getUint32(base + 80, true);
1912
+ var result40 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr40, len40));
1913
+ variant41 = result40;
1914
+ break;
1915
+ }
1916
+ default: {
1917
+ throw new TypeError('invalid variant discriminant for option');
1918
+ }
1919
+ }
1920
+ let variant43;
1921
+ switch (dataView(memory0).getUint8(base + 84, true)) {
1922
+ case 0: {
1923
+ variant43 = undefined;
1924
+ break;
1925
+ }
1926
+ case 1: {
1927
+ var ptr42 = dataView(memory0).getUint32(base + 88, true);
1928
+ var len42 = dataView(memory0).getUint32(base + 92, true);
1929
+ var result42 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr42, len42));
1930
+ variant43 = result42;
1931
+ break;
1932
+ }
1933
+ default: {
1934
+ throw new TypeError('invalid variant discriminant for option');
1935
+ }
1936
+ }
1937
+ let variant45;
1938
+ switch (dataView(memory0).getUint8(base + 96, true)) {
1939
+ case 0: {
1940
+ variant45 = undefined;
1941
+ break;
1942
+ }
1943
+ case 1: {
1944
+ var ptr44 = dataView(memory0).getUint32(base + 100, true);
1945
+ var len44 = dataView(memory0).getUint32(base + 104, true);
1946
+ var result44 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr44, len44));
1947
+ variant45 = result44;
1948
+ break;
1949
+ }
1950
+ default: {
1951
+ throw new TypeError('invalid variant discriminant for option');
1952
+ }
1953
+ }
1954
+ let variant47;
1955
+ switch (dataView(memory0).getUint8(base + 108, true)) {
1956
+ case 0: {
1957
+ variant47 = undefined;
1958
+ break;
1959
+ }
1960
+ case 1: {
1961
+ var ptr46 = dataView(memory0).getUint32(base + 112, true);
1962
+ var len46 = dataView(memory0).getUint32(base + 116, true);
1963
+ var result46 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr46, len46));
1964
+ variant47 = result46;
1965
+ break;
1966
+ }
1967
+ default: {
1968
+ throw new TypeError('invalid variant discriminant for option');
1969
+ }
1970
+ }
1971
+ let variant48;
1972
+ switch (dataView(memory0).getUint8(base + 128, true)) {
1973
+ case 0: {
1974
+ variant48 = undefined;
1975
+ break;
1976
+ }
1977
+ case 1: {
1978
+ variant48 = dataView(memory0).getInt32(base + 132, true) >>> 0;
1979
+ break;
1980
+ }
1981
+ default: {
1982
+ throw new TypeError('invalid variant discriminant for option');
1983
+ }
1984
+ }
1985
+ let variant49;
1986
+ switch (dataView(memory0).getUint8(base + 136, true)) {
1987
+ case 0: {
1988
+ variant49 = undefined;
1989
+ break;
1990
+ }
1991
+ case 1: {
1992
+ variant49 = dataView(memory0).getInt32(base + 140, true) >>> 0;
1993
+ break;
1994
+ }
1995
+ default: {
1996
+ throw new TypeError('invalid variant discriminant for option');
1997
+ }
1998
+ }
1999
+ let variant50;
2000
+ switch (dataView(memory0).getUint8(base + 144, true)) {
2001
+ case 0: {
2002
+ variant50 = undefined;
2003
+ break;
2004
+ }
2005
+ case 1: {
2006
+ variant50 = dataView(memory0).getInt32(base + 148, true) >>> 0;
2007
+ break;
2008
+ }
2009
+ default: {
2010
+ throw new TypeError('invalid variant discriminant for option');
2011
+ }
2012
+ }
2013
+ variant55= {
2014
+ tag: 'directive-item',
2015
+ val: {
2016
+ name: result30,
2017
+ args: result34,
2018
+ line: dataView(memory0).getInt32(base + 20, true) >>> 0,
2019
+ column: dataView(memory0).getInt32(base + 24, true) >>> 0,
2020
+ startOffset: dataView(memory0).getInt32(base + 28, true) >>> 0,
2021
+ endOffset: dataView(memory0).getInt32(base + 32, true) >>> 0,
2022
+ endLine: dataView(memory0).getInt32(base + 36, true) >>> 0,
2023
+ endColumn: dataView(memory0).getInt32(base + 40, true) >>> 0,
2024
+ leadingWhitespace: result35,
2025
+ trailingWhitespace: result36,
2026
+ spaceBeforeTerminator: result37,
2027
+ hasBlock: bool38 == 0 ? false : (bool38 == 1 ? true : throwInvalidBool()),
2028
+ blockIsRaw: bool39 == 0 ? false : (bool39 == 1 ? true : throwInvalidBool()),
2029
+ blockRawContent: variant41,
2030
+ closingBraceLeadingWhitespace: variant43,
2031
+ blockTrailingWhitespace: variant45,
2032
+ trailingCommentText: variant47,
2033
+ nameEndColumn: dataView(memory0).getInt32(base + 120, true) >>> 0,
2034
+ nameEndOffset: dataView(memory0).getInt32(base + 124, true) >>> 0,
2035
+ blockStartLine: variant48,
2036
+ blockStartColumn: variant49,
2037
+ blockStartOffset: variant50,
2038
+ }
2039
+ };
2040
+ break;
2041
+ }
2042
+ case 1: {
2043
+ var ptr51 = dataView(memory0).getUint32(base + 4, true);
2044
+ var len51 = dataView(memory0).getUint32(base + 8, true);
2045
+ var result51 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr51, len51));
2046
+ var ptr52 = dataView(memory0).getUint32(base + 20, true);
2047
+ var len52 = dataView(memory0).getUint32(base + 24, true);
2048
+ var result52 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr52, len52));
2049
+ var ptr53 = dataView(memory0).getUint32(base + 28, true);
2050
+ var len53 = dataView(memory0).getUint32(base + 32, true);
2051
+ var result53 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr53, len53));
2052
+ variant55= {
2053
+ tag: 'comment-item',
2054
+ val: {
2055
+ text: result51,
2056
+ line: dataView(memory0).getInt32(base + 12, true) >>> 0,
2057
+ column: dataView(memory0).getInt32(base + 16, true) >>> 0,
2058
+ leadingWhitespace: result52,
2059
+ trailingWhitespace: result53,
2060
+ startOffset: dataView(memory0).getInt32(base + 36, true) >>> 0,
2061
+ endOffset: dataView(memory0).getInt32(base + 40, true) >>> 0,
2062
+ }
2063
+ };
2064
+ break;
2065
+ }
2066
+ case 2: {
2067
+ var ptr54 = dataView(memory0).getUint32(base + 8, true);
2068
+ var len54 = dataView(memory0).getUint32(base + 12, true);
2069
+ var result54 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr54, len54));
2070
+ variant55= {
2071
+ tag: 'blank-line-item',
2072
+ val: {
2073
+ line: dataView(memory0).getInt32(base + 4, true) >>> 0,
2074
+ content: result54,
2075
+ startOffset: dataView(memory0).getInt32(base + 16, true) >>> 0,
2076
+ }
2077
+ };
2078
+ break;
2079
+ }
2080
+ default: {
2081
+ throw new TypeError('invalid variant discriminant for ConfigItemValue');
2082
+ }
2083
+ }
2084
+ var ptr56 = dataView(memory0).getUint32(base + 152, true);
2085
+ var len56 = dataView(memory0).getUint32(base + 156, true);
2086
+ var result56 = new Uint32Array(memory0.buffer.slice(ptr56, ptr56 + len56 * 4));
2087
+ result57.push({
2088
+ value: variant55,
2089
+ childIndices: result56,
2090
+ });
2091
+ }
2092
+ var ptr58 = dataView(memory0).getUint32(ret + 28, true);
2093
+ var len58 = dataView(memory0).getUint32(ret + 32, true);
2094
+ var result58 = new Uint32Array(memory0.buffer.slice(ptr58, ptr58 + len58 * 4));
2095
+ variant60= {
2096
+ tag: 'ok',
2097
+ val: {
2098
+ directivesWithContext: result27,
2099
+ includeContext: result29,
2100
+ allItems: result57,
2101
+ topLevelIndices: result58,
2102
+ }
2103
+ };
2104
+ break;
2105
+ }
2106
+ case 1: {
2107
+ var ptr59 = dataView(memory0).getUint32(ret + 4, true);
2108
+ var len59 = dataView(memory0).getUint32(ret + 8, true);
2109
+ var result59 = TEXT_DECODER_UTF8.decode(new Uint8Array(memory0.buffer, ptr59, len59));
2110
+ variant60= {
2111
+ tag: 'err',
2112
+ val: result59
2113
+ };
2114
+ break;
2115
+ }
2116
+ default: {
2117
+ throw new TypeError('invalid variant discriminant for expected');
2118
+ }
2119
+ }
2120
+ _debugLog('[iface="parse-config", function="parse-config"][Instruction::Return]', {
2121
+ funcName: 'parse-config',
2122
+ paramCount: 1,
2123
+ async: false,
2124
+ postReturn: true
2125
+ });
2126
+ const retCopy = variant60;
2127
+
2128
+ let cstate = getOrCreateAsyncState(0);
2129
+ cstate.mayLeave = false;
2130
+ postReturn0(ret);
2131
+ cstate.mayLeave = true;
2132
+
2133
+
2134
+
2135
+ if (typeof retCopy === 'object' && retCopy.tag === 'err') {
2136
+ throw new ComponentError(retCopy.val);
2137
+ }
2138
+ return retCopy.val;
2139
+
2140
+ }
2141
+
2142
+ const $init = (() => {
2143
+ let gen = (function* _initGenerator () {
2144
+ const module0 = fetchCompile(new URL('./parser.core.wasm', import.meta.url));
2145
+ ({ exports: exports0 } = yield instantiateCore(yield module0));
2146
+ memory0 = exports0.memory;
2147
+ GlobalComponentMemories.save({ idx: 0, componentIdx: 0, memory: memory0 });
2148
+ realloc0 = exports0.cabi_realloc;
2149
+ postReturn0 = exports0['cabi_post_parse-config'];
2150
+ exports0ParseConfig = exports0['parse-config'];
2151
+ })();
2152
+ let promise, resolve, reject;
2153
+ function runNext (value) {
2154
+ try {
2155
+ let done;
2156
+ do {
2157
+ ({ value, done } = gen.next(value));
2158
+ } while (!(value instanceof Promise) && !done);
2159
+ if (done) {
2160
+ if (resolve) resolve(value);
2161
+ else return value;
2162
+ }
2163
+ if (!promise) promise = new Promise((_resolve, _reject) => (resolve = _resolve, reject = _reject));
2164
+ value.then(runNext, reject);
2165
+ }
2166
+ catch (e) {
2167
+ if (reject) reject(e);
2168
+ else throw e;
2169
+ }
2170
+ }
2171
+ const maybeSyncReturn = runNext(null);
2172
+ return promise || maybeSyncReturn;
2173
+ })();
2174
+
2175
+ await $init;
2176
+
2177
+ export { parseConfig, }