@effindomv2/fui-as 0.1.27 → 0.1.29
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/browser/src/common-harness/managed-harness-file-host.ts +123 -158
- package/browser/src/common-harness/managed-harness-file-types.ts +2 -37
- package/browser/src/common-harness/managed-harness.ts +4 -0
- package/browser/src/worker-bootstrap.ts +406 -0
- package/browser/src/worker-manager.ts +4 -0
- package/browser/src/worker-types.ts +18 -0
- package/package.json +2 -2
- package/src/Fui.ts +1 -0
- package/src/FuiWorker.ts +5 -1
- package/src/controls/Button.ts +39 -6
- package/src/controls/ButtonColors.ts +73 -0
- package/src/controls/Checkbox.ts +1 -0
- package/src/controls/LabeledControlColors.ts +36 -0
- package/src/controls/RadioButton.ts +1 -0
- package/src/controls/Switch.ts +1 -0
- package/src/controls/index.ts +1 -0
- package/src/controls/internal/ButtonPresenter.ts +3 -2
- package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -8
- package/src/controls/internal/PressableLabeledControl.ts +2 -1
- package/src/controls/internal/RadioIndicatorPresenter.ts +9 -5
- package/src/controls/internal/SwitchIndicatorPresenter.ts +16 -7
- package/src/core/File.ts +5 -2
- package/src/core/event_exports.ts +15 -1
- package/src/worker/ffi.ts +6 -0
|
@@ -23,14 +23,11 @@ import {
|
|
|
23
23
|
type ActiveFileProcessingRecord,
|
|
24
24
|
type ActiveFileWriterRecord,
|
|
25
25
|
type ExternalHarnessDropItem,
|
|
26
|
-
type FileProcessingWorkerCancelMessage,
|
|
27
|
-
type FileProcessingWorkerNextMessage,
|
|
28
|
-
type FileProcessingWorkerOutboundMessage,
|
|
29
|
-
type FileProcessingWorkerStartMessage,
|
|
30
26
|
type SavePickerWindow,
|
|
31
27
|
type StoredFileRecord,
|
|
32
28
|
type WritableFileStreamLike,
|
|
33
29
|
} from './managed-harness-file-types';
|
|
30
|
+
import type { WorkerHostServicesBundleConfig } from '../worker-types';
|
|
34
31
|
import type { HarnessAppSession } from './managed-harness-session';
|
|
35
32
|
|
|
36
33
|
interface ManagedHarnessFileHostDependencies {
|
|
@@ -40,10 +37,11 @@ interface ManagedHarnessFileHostDependencies {
|
|
|
40
37
|
readAppBytes(ptr: number, len: number): Uint8Array;
|
|
41
38
|
writeTextCallbackPayload(session: HarnessAppSession, text: string, context: string): number;
|
|
42
39
|
describeHarnessError(error: unknown): string;
|
|
40
|
+
workerBootstrapUrl: string;
|
|
41
|
+
getCurrentWorkerHostServices(): WorkerHostServicesBundleConfig | undefined;
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
const encoder = new TextEncoder();
|
|
46
|
-
const FILE_PROCESSING_WORKER_URL = new URL('./file-processing-worker.js', import.meta.url).toString();
|
|
47
45
|
|
|
48
46
|
function copyBytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {
|
|
49
47
|
const copied = new Uint8Array(bytes.byteLength);
|
|
@@ -303,13 +301,16 @@ export function createManagedHarnessFileHost(dependencies: ManagedHarnessFileHos
|
|
|
303
301
|
requestId: number,
|
|
304
302
|
processedBytes: bigint,
|
|
305
303
|
outputFileName: string | null,
|
|
304
|
+
workerResult: string | null = null,
|
|
306
305
|
): void {
|
|
307
306
|
if (session === null) {
|
|
308
307
|
return;
|
|
309
308
|
}
|
|
309
|
+
// Encode outputFileName\0workerResult so the wasm handler splits them.
|
|
310
|
+
const combined = (outputFileName ?? '') + '\0' + (workerResult ?? '');
|
|
310
311
|
const payloadLength = dependencies.writeTextCallbackPayload(
|
|
311
312
|
session,
|
|
312
|
-
|
|
313
|
+
combined,
|
|
313
314
|
'File worker process completion',
|
|
314
315
|
);
|
|
315
316
|
session.exports.__fui_on_file_worker_process_complete(
|
|
@@ -340,107 +341,146 @@ export function createManagedHarnessFileHost(dependencies: ManagedHarnessFileHos
|
|
|
340
341
|
|
|
341
342
|
function cleanupFileProcessingRequest(requestId: number): void {
|
|
342
343
|
cancelledFileProcessingRequestIds.delete(requestId);
|
|
343
|
-
const record = activeFileProcessingRequests.get(requestId);
|
|
344
|
-
if (record === undefined) {
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
record.worker.terminate();
|
|
348
344
|
activeFileProcessingRequests.delete(requestId);
|
|
349
345
|
}
|
|
350
346
|
|
|
351
|
-
|
|
347
|
+
function failFileProcessingRequest(record: ActiveFileProcessingRecord, status: number, message: string): void {
|
|
352
348
|
cleanupFileProcessingRequest(record.requestId);
|
|
353
|
-
if (record.stream !== null) {
|
|
354
|
-
try {
|
|
355
|
-
await abortWritableStream(record.stream);
|
|
356
|
-
} catch {
|
|
357
|
-
// Ignore cleanup failures after surfacing the original worker-processing error.
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
349
|
if (dependencies.getCurrentSession() === record.session) {
|
|
361
350
|
emitFileWorkerProcessError(record.session, record.requestId, status, message);
|
|
362
351
|
}
|
|
363
352
|
}
|
|
364
353
|
|
|
365
|
-
async function
|
|
366
|
-
|
|
367
|
-
|
|
354
|
+
async function startFileProcessing(
|
|
355
|
+
requestId: number,
|
|
356
|
+
session: HarnessAppSession,
|
|
357
|
+
sourceFile: File,
|
|
358
|
+
suggestedName: string,
|
|
359
|
+
chunkBytes: number,
|
|
360
|
+
saveToPickedFile: boolean,
|
|
361
|
+
workerHostServices: WorkerHostServicesBundleConfig | undefined,
|
|
368
362
|
): Promise<void> {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if (
|
|
373
|
-
|
|
363
|
+
const workerId = requestId;
|
|
364
|
+
const manifestUrl = new URL('./worker-manifest.json', dependencies.workerBootstrapUrl).toString();
|
|
365
|
+
const manifestResponse = await fetch(manifestUrl);
|
|
366
|
+
if (!manifestResponse.ok) {
|
|
367
|
+
emitFileWorkerProcessError(session, requestId, FILE_STATUS_ERROR, 'Failed to load worker manifest.');
|
|
374
368
|
return;
|
|
375
369
|
}
|
|
376
|
-
|
|
370
|
+
const manifest = await manifestResponse.json();
|
|
371
|
+
const wasmUrl = manifest.entries?.['fileProcessorWorker'];
|
|
372
|
+
if (typeof wasmUrl !== 'string' || wasmUrl.length === 0) {
|
|
373
|
+
emitFileWorkerProcessError(session, requestId, FILE_STATUS_ERROR, 'Missing "fileProcessorWorker" entry in worker manifest.');
|
|
377
374
|
return;
|
|
378
375
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
376
|
+
const resolvedWasmUrl = new URL(wasmUrl, manifestUrl).toString();
|
|
377
|
+
const startProcessor = (targetFileName: string | null, stream: WritableFileStreamLike | null): void => {
|
|
378
|
+
const worker = new Worker(dependencies.workerBootstrapUrl);
|
|
379
|
+
const record: ActiveFileProcessingRecord = {
|
|
380
|
+
requestId,
|
|
381
|
+
session,
|
|
382
|
+
sourceFileName: sourceFile.name,
|
|
383
|
+
targetFileName,
|
|
384
|
+
totalBytes: sourceFile.size,
|
|
385
|
+
stream,
|
|
386
|
+
saveToPickedFile,
|
|
387
|
+
cancelled: false,
|
|
388
|
+
worker,
|
|
389
|
+
processedBytes: 0,
|
|
390
|
+
};
|
|
391
|
+
activeFileProcessingRequests.set(requestId, record);
|
|
392
|
+
worker.addEventListener('message', (event: MessageEvent) => {
|
|
393
|
+
const msg = event.data;
|
|
394
|
+
if (msg.type === 'file-process-chunk' && stream !== null) {
|
|
395
|
+
void stream.write(msg.bytes).catch((error: unknown) => {
|
|
396
|
+
failFileProcessingRequest(record, FILE_STATUS_ERROR, dependencies.describeHarnessError(error));
|
|
397
|
+
});
|
|
398
|
+
} else if (msg.type === 'progress') {
|
|
399
|
+
if (dependencies.getCurrentSession() === record.session) {
|
|
400
|
+
const parts = (msg.text as string).split(' ');
|
|
401
|
+
const processed = parseInt(parts[0] ?? '0', 10);
|
|
402
|
+
record.processedBytes = processed;
|
|
403
|
+
emitFileWorkerProcessProgress(
|
|
404
|
+
record.session,
|
|
405
|
+
record.requestId,
|
|
406
|
+
BigInt(processed),
|
|
407
|
+
BigInt(record.totalBytes),
|
|
408
|
+
record.targetFileName,
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
} else if (msg.type === 'complete') {
|
|
398
412
|
if (dependencies.getCurrentSession() === record.session) {
|
|
413
|
+
if (stream !== null) {
|
|
414
|
+
void stream.close().catch(() => {
|
|
415
|
+
// Swallow close errors.
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
const hashText = (msg.text as string);
|
|
399
419
|
emitFileWorkerProcessComplete(
|
|
400
420
|
record.session,
|
|
401
421
|
record.requestId,
|
|
402
|
-
BigInt(
|
|
422
|
+
BigInt(record.processedBytes),
|
|
403
423
|
record.targetFileName,
|
|
424
|
+
hashText,
|
|
404
425
|
);
|
|
405
426
|
}
|
|
406
427
|
cleanupFileProcessingRequest(record.requestId);
|
|
407
|
-
|
|
428
|
+
} else if (msg.type === 'error') {
|
|
429
|
+
failFileProcessingRequest(record, FILE_STATUS_ERROR, msg.text);
|
|
408
430
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
431
|
+
});
|
|
432
|
+
worker.addEventListener('error', () => {
|
|
433
|
+
failFileProcessingRequest(record, FILE_STATUS_ERROR, 'Worker crashed.');
|
|
434
|
+
});
|
|
435
|
+
worker.postMessage({
|
|
436
|
+
type: 'start-file-process',
|
|
437
|
+
workerId,
|
|
438
|
+
file: sourceFile,
|
|
439
|
+
wasmUrl: resolvedWasmUrl,
|
|
440
|
+
entryName: 'fileProcessorWorker',
|
|
441
|
+
chunkSize: Math.max(1, Math.floor(chunkBytes)),
|
|
442
|
+
workerHostServices,
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
if (!saveToPickedFile) {
|
|
447
|
+
startProcessor(null, null);
|
|
414
448
|
return;
|
|
415
449
|
}
|
|
416
|
-
if (
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
450
|
+
if (!supportsNativeSavePicker()) {
|
|
451
|
+
emitFileWorkerProcessError(session, requestId, FILE_STATUS_ERROR, 'Worker file processing requires the native save picker.');
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const savePicker = (window as SavePickerWindow).showSaveFilePicker;
|
|
455
|
+
if (typeof savePicker !== 'function') {
|
|
456
|
+
emitFileWorkerProcessError(session, requestId, FILE_STATUS_ERROR, 'Worker file processing requires the native save picker.');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
void savePicker({ suggestedName }).then((handle) =>
|
|
460
|
+
handle.createWritable().then((writableStream) => {
|
|
461
|
+
if (dependencies.getCurrentSession() !== session || cancelledFileProcessingRequestIds.has(requestId)) {
|
|
462
|
+
void abortWritableStream(writableStream).catch(() => {
|
|
463
|
+
// Ignore cleanup failures after the session moved on.
|
|
464
|
+
});
|
|
465
|
+
cancelledFileProcessingRequestIds.delete(requestId);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
startProcessor(handle.name ?? suggestedName, writableStream);
|
|
469
|
+
}),
|
|
470
|
+
).catch((error: unknown) => {
|
|
471
|
+
cancelledFileProcessingRequestIds.delete(requestId);
|
|
472
|
+
if (dependencies.getCurrentSession() !== session) {
|
|
439
473
|
return;
|
|
440
474
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
475
|
+
emitFileWorkerProcessError(
|
|
476
|
+
session,
|
|
477
|
+
requestId,
|
|
478
|
+
error instanceof DOMException && error.name === 'AbortError'
|
|
479
|
+
? FILE_STATUS_CANCELLED
|
|
480
|
+
: FILE_STATUS_ERROR,
|
|
481
|
+
dependencies.describeHarnessError(error),
|
|
482
|
+
);
|
|
483
|
+
});
|
|
444
484
|
}
|
|
445
485
|
|
|
446
486
|
function cancelFileProcessingRequest(requestId: number): void {
|
|
@@ -450,14 +490,7 @@ export function createManagedHarnessFileHost(dependencies: ManagedHarnessFileHos
|
|
|
450
490
|
return;
|
|
451
491
|
}
|
|
452
492
|
record.cancelled = true;
|
|
453
|
-
|
|
454
|
-
record.worker.postMessage(cancelMessage);
|
|
455
|
-
cleanupFileProcessingRequest(requestId);
|
|
456
|
-
if (record.stream !== null) {
|
|
457
|
-
void abortWritableStream(record.stream).catch(() => {
|
|
458
|
-
// Ignore cancellation cleanup failures.
|
|
459
|
-
});
|
|
460
|
-
}
|
|
493
|
+
record.worker.terminate();
|
|
461
494
|
}
|
|
462
495
|
|
|
463
496
|
function storeBrowserFile(file: File, prefix: string): StoredFileRecord {
|
|
@@ -1015,10 +1048,6 @@ export function createManagedHarnessFileHost(dependencies: ManagedHarnessFileHos
|
|
|
1015
1048
|
if (session === null) {
|
|
1016
1049
|
return;
|
|
1017
1050
|
}
|
|
1018
|
-
if (typeof Worker !== 'function') {
|
|
1019
|
-
emitFileWorkerProcessError(session, requestId, FILE_STATUS_ERROR, 'Worker file processing requires browser Worker support.');
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
1051
|
const fileId = dependencies.readAppUtf8(fileIdPtr, fileIdLen);
|
|
1023
1052
|
const sourceFile = storedBrowserFiles.get(fileId);
|
|
1024
1053
|
if (sourceFile === undefined) {
|
|
@@ -1026,72 +1055,8 @@ export function createManagedHarnessFileHost(dependencies: ManagedHarnessFileHos
|
|
|
1026
1055
|
return;
|
|
1027
1056
|
}
|
|
1028
1057
|
const suggestedName = resolveSuggestedName(dependencies.readAppUtf8(suggestedNamePtr, suggestedNameLen), '');
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
const worker = new Worker(FILE_PROCESSING_WORKER_URL);
|
|
1032
|
-
const record: ActiveFileProcessingRecord = {
|
|
1033
|
-
requestId,
|
|
1034
|
-
session,
|
|
1035
|
-
sourceFileName: sourceFile.name,
|
|
1036
|
-
targetFileName,
|
|
1037
|
-
totalBytes: sourceFile.size,
|
|
1038
|
-
worker,
|
|
1039
|
-
stream,
|
|
1040
|
-
saveToPickedFile,
|
|
1041
|
-
cancelled: false,
|
|
1042
|
-
};
|
|
1043
|
-
activeFileProcessingRequests.set(requestId, record);
|
|
1044
|
-
worker.addEventListener('message', (event: MessageEvent<FileProcessingWorkerOutboundMessage>) => {
|
|
1045
|
-
void handleFileProcessingWorkerMessage(record, event.data);
|
|
1046
|
-
});
|
|
1047
|
-
worker.addEventListener('error', (event: ErrorEvent) => {
|
|
1048
|
-
void failFileProcessingRequest(
|
|
1049
|
-
record,
|
|
1050
|
-
FILE_STATUS_ERROR,
|
|
1051
|
-
event.message.length > 0 ? event.message : 'Worker file processing crashed.',
|
|
1052
|
-
);
|
|
1053
|
-
});
|
|
1054
|
-
const startMessage: FileProcessingWorkerStartMessage = {
|
|
1055
|
-
type: 'start',
|
|
1056
|
-
file: sourceFile,
|
|
1057
|
-
chunkSize: safeChunkBytes,
|
|
1058
|
-
};
|
|
1059
|
-
worker.postMessage(startMessage);
|
|
1060
|
-
};
|
|
1061
|
-
if (!saveToPickedFile) {
|
|
1062
|
-
startWorker(null, null);
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
if (!supportsNativeSavePicker()) {
|
|
1066
|
-
emitFileWorkerProcessError(session, requestId, FILE_STATUS_ERROR, 'Worker file processing requires the native save picker.');
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
const savePicker = (window as SavePickerWindow).showSaveFilePicker;
|
|
1070
|
-
if (typeof savePicker !== 'function') {
|
|
1071
|
-
emitFileWorkerProcessError(session, requestId, FILE_STATUS_ERROR, 'Worker file processing requires the native save picker.');
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1074
|
-
void savePicker({ suggestedName }).then((handle) => handle.createWritable().then((stream) => {
|
|
1075
|
-
if (dependencies.getCurrentSession() !== session || cancelledFileProcessingRequestIds.has(requestId)) {
|
|
1076
|
-
void abortWritableStream(stream).catch(() => {
|
|
1077
|
-
// Ignore cleanup failures after the session moved on.
|
|
1078
|
-
});
|
|
1079
|
-
cancelledFileProcessingRequestIds.delete(requestId);
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
startWorker(handle.name ?? suggestedName, stream);
|
|
1083
|
-
})).catch((error: unknown) => {
|
|
1084
|
-
cancelledFileProcessingRequestIds.delete(requestId);
|
|
1085
|
-
if (dependencies.getCurrentSession() !== session) {
|
|
1086
|
-
return;
|
|
1087
|
-
}
|
|
1088
|
-
emitFileWorkerProcessError(
|
|
1089
|
-
session,
|
|
1090
|
-
requestId,
|
|
1091
|
-
error instanceof DOMException && error.name === 'AbortError' ? FILE_STATUS_CANCELLED : FILE_STATUS_ERROR,
|
|
1092
|
-
dependencies.describeHarnessError(error),
|
|
1093
|
-
);
|
|
1094
|
-
});
|
|
1058
|
+
const workerHostServices = dependencies.getCurrentWorkerHostServices();
|
|
1059
|
+
void startFileProcessing(requestId, session, sourceFile, suggestedName, chunkBytes, saveToPickedFile, workerHostServices);
|
|
1095
1060
|
},
|
|
1096
1061
|
fui_file_process_worker_cancel(requestId: number): void {
|
|
1097
1062
|
cancelFileProcessingRequest(requestId);
|
|
@@ -57,50 +57,15 @@ export interface ActiveFileWriterRecord {
|
|
|
57
57
|
writtenBytes: number;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export interface FileProcessingWorkerStartMessage {
|
|
61
|
-
readonly type: 'start';
|
|
62
|
-
readonly file: File;
|
|
63
|
-
readonly chunkSize: number;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface FileProcessingWorkerNextMessage {
|
|
67
|
-
readonly type: 'next';
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface FileProcessingWorkerCancelMessage {
|
|
71
|
-
readonly type: 'cancel';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export type FileProcessingWorkerInboundMessage =
|
|
75
|
-
| FileProcessingWorkerStartMessage
|
|
76
|
-
| FileProcessingWorkerNextMessage
|
|
77
|
-
| FileProcessingWorkerCancelMessage;
|
|
78
|
-
|
|
79
|
-
export interface FileProcessingWorkerChunkMessage {
|
|
80
|
-
readonly type: 'chunk';
|
|
81
|
-
readonly offsetBytes: number;
|
|
82
|
-
readonly bytes: ArrayBuffer;
|
|
83
|
-
readonly copiedBytes: number;
|
|
84
|
-
readonly totalBytes: number;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface FileProcessingWorkerErrorMessage {
|
|
88
|
-
readonly type: 'error';
|
|
89
|
-
readonly message: string;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export type FileProcessingWorkerOutboundMessage =
|
|
93
|
-
| FileProcessingWorkerChunkMessage
|
|
94
|
-
| FileProcessingWorkerErrorMessage;
|
|
95
|
-
|
|
96
60
|
export interface ActiveFileProcessingRecord {
|
|
97
61
|
readonly requestId: number;
|
|
98
62
|
readonly session: HarnessAppSession;
|
|
99
63
|
readonly sourceFileName: string;
|
|
100
64
|
readonly targetFileName: string | null;
|
|
101
65
|
readonly totalBytes: number;
|
|
102
|
-
readonly worker: Worker;
|
|
103
66
|
readonly stream: WritableFileStreamLike | null;
|
|
104
67
|
readonly saveToPickedFile: boolean;
|
|
68
|
+
readonly worker: Worker;
|
|
105
69
|
cancelled: boolean;
|
|
70
|
+
processedBytes: number;
|
|
106
71
|
}
|
|
@@ -458,6 +458,8 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
|
|
|
458
458
|
notifyRouteChanged(session, `${window.location.pathname}${window.location.search}${window.location.hash}`);
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
+
const workerBootstrapUrl = new URL('./worker-bootstrap.js', import.meta.url).toString();
|
|
462
|
+
|
|
461
463
|
const fileHost = createManagedHarnessFileHost({
|
|
462
464
|
getCurrentSession: () => currentSession,
|
|
463
465
|
getRuntime: () => runtime,
|
|
@@ -465,6 +467,8 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
|
|
|
465
467
|
readAppBytes,
|
|
466
468
|
writeTextCallbackPayload,
|
|
467
469
|
describeHarnessError,
|
|
470
|
+
workerBootstrapUrl,
|
|
471
|
+
getCurrentWorkerHostServices: () => currentSession?.workerHostServices,
|
|
468
472
|
});
|
|
469
473
|
|
|
470
474
|
const fetchHost = createManagedHarnessFetchHost({
|