@cfasim-ui/docs 0.3.11

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.
Files changed (60) hide show
  1. package/LICENSE +201 -0
  2. package/charts/ChartMenu/ChartMenu.vue +140 -0
  3. package/charts/ChartMenu/download.ts +44 -0
  4. package/charts/ChartTooltip/ChartTooltip.vue +97 -0
  5. package/charts/ChoroplethMap/ChoroplethMap.md +398 -0
  6. package/charts/ChoroplethMap/ChoroplethMap.vue +777 -0
  7. package/charts/ChoroplethMap/hsaMapping.ts +4116 -0
  8. package/charts/DataTable/DataTable.md +143 -0
  9. package/charts/DataTable/DataTable.vue +277 -0
  10. package/charts/LineChart/LineChart.md +472 -0
  11. package/charts/LineChart/LineChart.vue +1216 -0
  12. package/charts/index.ts +23 -0
  13. package/charts/tooltip-position.ts +49 -0
  14. package/components/Box/Box.md +49 -0
  15. package/components/Box/Box.vue +52 -0
  16. package/components/Button/Button.md +67 -0
  17. package/components/Button/Button.vue +81 -0
  18. package/components/Expander/Expander.md +34 -0
  19. package/components/Expander/Expander.vue +95 -0
  20. package/components/Hint/Hint.md +29 -0
  21. package/components/Hint/Hint.vue +83 -0
  22. package/components/Icon/Icon.md +67 -0
  23. package/components/Icon/Icon.vue +112 -0
  24. package/components/LightDarkToggle/LightDarkToggle.vue +49 -0
  25. package/components/NumberInput/NumberInput.md +305 -0
  26. package/components/NumberInput/NumberInput.vue +531 -0
  27. package/components/SelectBox/SelectBox.md +110 -0
  28. package/components/SelectBox/SelectBox.vue +195 -0
  29. package/components/SidebarLayout/SidebarLayout.md +104 -0
  30. package/components/SidebarLayout/SidebarLayout.vue +466 -0
  31. package/components/Spinner/Spinner.md +51 -0
  32. package/components/Spinner/Spinner.vue +55 -0
  33. package/components/TextInput/TextInput.md +82 -0
  34. package/components/TextInput/TextInput.vue +94 -0
  35. package/components/Toggle/Toggle.md +81 -0
  36. package/components/Toggle/Toggle.vue +81 -0
  37. package/components/index.ts +15 -0
  38. package/index.json +121 -0
  39. package/package.json +24 -0
  40. package/pyodide/index.ts +7 -0
  41. package/pyodide/pyodide.worker.ts +233 -0
  42. package/pyodide/pyodideWorkerApi.ts +102 -0
  43. package/pyodide/useModel.ts +86 -0
  44. package/pyodide/vitePlugin.js +51 -0
  45. package/shared/ModelOutput.ts +88 -0
  46. package/shared/csv.ts +22 -0
  47. package/shared/index.ts +24 -0
  48. package/shared/transferUtils.ts +126 -0
  49. package/shared/useUrlParams.ts +296 -0
  50. package/theme/all.js +5 -0
  51. package/theme/base.css +176 -0
  52. package/theme/cfasim.css +3 -0
  53. package/theme/theme.css +113 -0
  54. package/theme/themes/cdc.css +22 -0
  55. package/theme/utilities.css +518 -0
  56. package/wasm/index.ts +2 -0
  57. package/wasm/useModel.ts +53 -0
  58. package/wasm/vitePlugin.js +35 -0
  59. package/wasm/wasm.worker.ts +74 -0
  60. package/wasm/wasmWorkerApi.ts +38 -0
@@ -0,0 +1,518 @@
1
+ @layer utilities {
2
+ /* Margin */
3
+ .m-0 {
4
+ margin: 0 !important;
5
+ }
6
+ .m-1 {
7
+ margin: var(--space-1) !important;
8
+ }
9
+ .m-2 {
10
+ margin: var(--space-2) !important;
11
+ }
12
+ .m-3 {
13
+ margin: var(--space-3) !important;
14
+ }
15
+ .m-4 {
16
+ margin: var(--space-4) !important;
17
+ }
18
+ .m-6 {
19
+ margin: var(--space-6) !important;
20
+ }
21
+ .m-8 {
22
+ margin: var(--space-8) !important;
23
+ }
24
+ .m-12 {
25
+ margin: var(--space-12) !important;
26
+ }
27
+ .m-20 {
28
+ margin: var(--space-20) !important;
29
+ }
30
+ .m-auto {
31
+ margin: auto !important;
32
+ }
33
+
34
+ .mx-0 {
35
+ margin-inline: 0 !important;
36
+ }
37
+ .mx-1 {
38
+ margin-inline: var(--space-1) !important;
39
+ }
40
+ .mx-2 {
41
+ margin-inline: var(--space-2) !important;
42
+ }
43
+ .mx-3 {
44
+ margin-inline: var(--space-3) !important;
45
+ }
46
+ .mx-4 {
47
+ margin-inline: var(--space-4) !important;
48
+ }
49
+ .mx-6 {
50
+ margin-inline: var(--space-6) !important;
51
+ }
52
+ .mx-8 {
53
+ margin-inline: var(--space-8) !important;
54
+ }
55
+ .mx-12 {
56
+ margin-inline: var(--space-12) !important;
57
+ }
58
+ .mx-20 {
59
+ margin-inline: var(--space-20) !important;
60
+ }
61
+ .mx-auto {
62
+ margin-inline: auto !important;
63
+ }
64
+
65
+ .my-0 {
66
+ margin-block: 0 !important;
67
+ }
68
+ .my-1 {
69
+ margin-block: var(--space-1) !important;
70
+ }
71
+ .my-2 {
72
+ margin-block: var(--space-2) !important;
73
+ }
74
+ .my-3 {
75
+ margin-block: var(--space-3) !important;
76
+ }
77
+ .my-4 {
78
+ margin-block: var(--space-4) !important;
79
+ }
80
+ .my-6 {
81
+ margin-block: var(--space-6) !important;
82
+ }
83
+ .my-8 {
84
+ margin-block: var(--space-8) !important;
85
+ }
86
+ .my-12 {
87
+ margin-block: var(--space-12) !important;
88
+ }
89
+ .my-20 {
90
+ margin-block: var(--space-20) !important;
91
+ }
92
+ .my-auto {
93
+ margin-block: auto !important;
94
+ }
95
+
96
+ .mt-0 {
97
+ margin-block-start: 0 !important;
98
+ }
99
+ .mt-1 {
100
+ margin-block-start: var(--space-1) !important;
101
+ }
102
+ .mt-2 {
103
+ margin-block-start: var(--space-2) !important;
104
+ }
105
+ .mt-3 {
106
+ margin-block-start: var(--space-3) !important;
107
+ }
108
+ .mt-4 {
109
+ margin-block-start: var(--space-4) !important;
110
+ }
111
+ .mt-6 {
112
+ margin-block-start: var(--space-6) !important;
113
+ }
114
+ .mt-8 {
115
+ margin-block-start: var(--space-8) !important;
116
+ }
117
+ .mt-12 {
118
+ margin-block-start: var(--space-12) !important;
119
+ }
120
+ .mt-20 {
121
+ margin-block-start: var(--space-20) !important;
122
+ }
123
+
124
+ .mb-0 {
125
+ margin-block-end: 0 !important;
126
+ }
127
+ .mb-1 {
128
+ margin-block-end: var(--space-1) !important;
129
+ }
130
+ .mb-2 {
131
+ margin-block-end: var(--space-2) !important;
132
+ }
133
+ .mb-3 {
134
+ margin-block-end: var(--space-3) !important;
135
+ }
136
+ .mb-4 {
137
+ margin-block-end: var(--space-4) !important;
138
+ }
139
+ .mb-6 {
140
+ margin-block-end: var(--space-6) !important;
141
+ }
142
+ .mb-8 {
143
+ margin-block-end: var(--space-8) !important;
144
+ }
145
+ .mb-12 {
146
+ margin-block-end: var(--space-12) !important;
147
+ }
148
+ .mb-20 {
149
+ margin-block-end: var(--space-20) !important;
150
+ }
151
+
152
+ .ml-0 {
153
+ margin-inline-start: 0 !important;
154
+ }
155
+ .ml-1 {
156
+ margin-inline-start: var(--space-1) !important;
157
+ }
158
+ .ml-2 {
159
+ margin-inline-start: var(--space-2) !important;
160
+ }
161
+ .ml-3 {
162
+ margin-inline-start: var(--space-3) !important;
163
+ }
164
+ .ml-4 {
165
+ margin-inline-start: var(--space-4) !important;
166
+ }
167
+ .ml-6 {
168
+ margin-inline-start: var(--space-6) !important;
169
+ }
170
+ .ml-8 {
171
+ margin-inline-start: var(--space-8) !important;
172
+ }
173
+ .ml-12 {
174
+ margin-inline-start: var(--space-12) !important;
175
+ }
176
+ .ml-20 {
177
+ margin-inline-start: var(--space-20) !important;
178
+ }
179
+ .ml-auto {
180
+ margin-inline-start: auto !important;
181
+ }
182
+
183
+ .mr-0 {
184
+ margin-inline-end: 0 !important;
185
+ }
186
+ .mr-1 {
187
+ margin-inline-end: var(--space-1) !important;
188
+ }
189
+ .mr-2 {
190
+ margin-inline-end: var(--space-2) !important;
191
+ }
192
+ .mr-3 {
193
+ margin-inline-end: var(--space-3) !important;
194
+ }
195
+ .mr-4 {
196
+ margin-inline-end: var(--space-4) !important;
197
+ }
198
+ .mr-6 {
199
+ margin-inline-end: var(--space-6) !important;
200
+ }
201
+ .mr-8 {
202
+ margin-inline-end: var(--space-8) !important;
203
+ }
204
+ .mr-12 {
205
+ margin-inline-end: var(--space-12) !important;
206
+ }
207
+ .mr-20 {
208
+ margin-inline-end: var(--space-20) !important;
209
+ }
210
+ .mr-auto {
211
+ margin-inline-end: auto !important;
212
+ }
213
+
214
+ /* Padding */
215
+ .p-0 {
216
+ padding: 0 !important;
217
+ }
218
+ .p-1 {
219
+ padding: var(--space-1) !important;
220
+ }
221
+ .p-2 {
222
+ padding: var(--space-2) !important;
223
+ }
224
+ .p-3 {
225
+ padding: var(--space-3) !important;
226
+ }
227
+ .p-4 {
228
+ padding: var(--space-4) !important;
229
+ }
230
+ .p-6 {
231
+ padding: var(--space-6) !important;
232
+ }
233
+ .p-8 {
234
+ padding: var(--space-8) !important;
235
+ }
236
+ .p-12 {
237
+ padding: var(--space-12) !important;
238
+ }
239
+ .p-20 {
240
+ padding: var(--space-20) !important;
241
+ }
242
+
243
+ .px-0 {
244
+ padding-inline: 0 !important;
245
+ }
246
+ .px-1 {
247
+ padding-inline: var(--space-1) !important;
248
+ }
249
+ .px-2 {
250
+ padding-inline: var(--space-2) !important;
251
+ }
252
+ .px-3 {
253
+ padding-inline: var(--space-3) !important;
254
+ }
255
+ .px-4 {
256
+ padding-inline: var(--space-4) !important;
257
+ }
258
+ .px-6 {
259
+ padding-inline: var(--space-6) !important;
260
+ }
261
+ .px-8 {
262
+ padding-inline: var(--space-8) !important;
263
+ }
264
+ .px-12 {
265
+ padding-inline: var(--space-12) !important;
266
+ }
267
+ .px-20 {
268
+ padding-inline: var(--space-20) !important;
269
+ }
270
+
271
+ .py-0 {
272
+ padding-block: 0 !important;
273
+ }
274
+ .py-1 {
275
+ padding-block: var(--space-1) !important;
276
+ }
277
+ .py-2 {
278
+ padding-block: var(--space-2) !important;
279
+ }
280
+ .py-3 {
281
+ padding-block: var(--space-3) !important;
282
+ }
283
+ .py-4 {
284
+ padding-block: var(--space-4) !important;
285
+ }
286
+ .py-6 {
287
+ padding-block: var(--space-6) !important;
288
+ }
289
+ .py-8 {
290
+ padding-block: var(--space-8) !important;
291
+ }
292
+ .py-12 {
293
+ padding-block: var(--space-12) !important;
294
+ }
295
+ .py-20 {
296
+ padding-block: var(--space-20) !important;
297
+ }
298
+
299
+ .pt-0 {
300
+ padding-block-start: 0 !important;
301
+ }
302
+ .pt-1 {
303
+ padding-block-start: var(--space-1) !important;
304
+ }
305
+ .pt-2 {
306
+ padding-block-start: var(--space-2) !important;
307
+ }
308
+ .pt-3 {
309
+ padding-block-start: var(--space-3) !important;
310
+ }
311
+ .pt-4 {
312
+ padding-block-start: var(--space-4) !important;
313
+ }
314
+ .pt-6 {
315
+ padding-block-start: var(--space-6) !important;
316
+ }
317
+ .pt-8 {
318
+ padding-block-start: var(--space-8) !important;
319
+ }
320
+ .pt-12 {
321
+ padding-block-start: var(--space-12) !important;
322
+ }
323
+ .pt-20 {
324
+ padding-block-start: var(--space-20) !important;
325
+ }
326
+
327
+ .pb-0 {
328
+ padding-block-end: 0 !important;
329
+ }
330
+ .pb-1 {
331
+ padding-block-end: var(--space-1) !important;
332
+ }
333
+ .pb-2 {
334
+ padding-block-end: var(--space-2) !important;
335
+ }
336
+ .pb-3 {
337
+ padding-block-end: var(--space-3) !important;
338
+ }
339
+ .pb-4 {
340
+ padding-block-end: var(--space-4) !important;
341
+ }
342
+ .pb-6 {
343
+ padding-block-end: var(--space-6) !important;
344
+ }
345
+ .pb-8 {
346
+ padding-block-end: var(--space-8) !important;
347
+ }
348
+ .pb-12 {
349
+ padding-block-end: var(--space-12) !important;
350
+ }
351
+ .pb-20 {
352
+ padding-block-end: var(--space-20) !important;
353
+ }
354
+
355
+ .pl-0 {
356
+ padding-inline-start: 0 !important;
357
+ }
358
+ .pl-1 {
359
+ padding-inline-start: var(--space-1) !important;
360
+ }
361
+ .pl-2 {
362
+ padding-inline-start: var(--space-2) !important;
363
+ }
364
+ .pl-3 {
365
+ padding-inline-start: var(--space-3) !important;
366
+ }
367
+ .pl-4 {
368
+ padding-inline-start: var(--space-4) !important;
369
+ }
370
+ .pl-6 {
371
+ padding-inline-start: var(--space-6) !important;
372
+ }
373
+ .pl-8 {
374
+ padding-inline-start: var(--space-8) !important;
375
+ }
376
+ .pl-12 {
377
+ padding-inline-start: var(--space-12) !important;
378
+ }
379
+ .pl-20 {
380
+ padding-inline-start: var(--space-20) !important;
381
+ }
382
+
383
+ .pr-0 {
384
+ padding-inline-end: 0 !important;
385
+ }
386
+ .pr-1 {
387
+ padding-inline-end: var(--space-1) !important;
388
+ }
389
+ .pr-2 {
390
+ padding-inline-end: var(--space-2) !important;
391
+ }
392
+ .pr-3 {
393
+ padding-inline-end: var(--space-3) !important;
394
+ }
395
+ .pr-4 {
396
+ padding-inline-end: var(--space-4) !important;
397
+ }
398
+ .pr-6 {
399
+ padding-inline-end: var(--space-6) !important;
400
+ }
401
+ .pr-8 {
402
+ padding-inline-end: var(--space-8) !important;
403
+ }
404
+ .pr-12 {
405
+ padding-inline-end: var(--space-12) !important;
406
+ }
407
+ .pr-20 {
408
+ padding-inline-end: var(--space-20) !important;
409
+ }
410
+
411
+ /* Gap */
412
+ .gap-0 {
413
+ gap: 0 !important;
414
+ }
415
+ .gap-1 {
416
+ gap: var(--space-1) !important;
417
+ }
418
+ .gap-2 {
419
+ gap: var(--space-2) !important;
420
+ }
421
+ .gap-3 {
422
+ gap: var(--space-3) !important;
423
+ }
424
+ .gap-4 {
425
+ gap: var(--space-4) !important;
426
+ }
427
+ .gap-6 {
428
+ gap: var(--space-6) !important;
429
+ }
430
+ .gap-8 {
431
+ gap: var(--space-8) !important;
432
+ }
433
+ .gap-12 {
434
+ gap: var(--space-12) !important;
435
+ }
436
+ .gap-20 {
437
+ gap: var(--space-20) !important;
438
+ }
439
+
440
+ .gap-x-0 {
441
+ column-gap: 0 !important;
442
+ }
443
+ .gap-x-1 {
444
+ column-gap: var(--space-1) !important;
445
+ }
446
+ .gap-x-2 {
447
+ column-gap: var(--space-2) !important;
448
+ }
449
+ .gap-x-3 {
450
+ column-gap: var(--space-3) !important;
451
+ }
452
+ .gap-x-4 {
453
+ column-gap: var(--space-4) !important;
454
+ }
455
+ .gap-x-6 {
456
+ column-gap: var(--space-6) !important;
457
+ }
458
+ .gap-x-8 {
459
+ column-gap: var(--space-8) !important;
460
+ }
461
+ .gap-x-12 {
462
+ column-gap: var(--space-12) !important;
463
+ }
464
+ .gap-x-20 {
465
+ column-gap: var(--space-20) !important;
466
+ }
467
+
468
+ .gap-y-0 {
469
+ row-gap: 0 !important;
470
+ }
471
+ .gap-y-1 {
472
+ row-gap: var(--space-1) !important;
473
+ }
474
+ .gap-y-2 {
475
+ row-gap: var(--space-2) !important;
476
+ }
477
+ .gap-y-3 {
478
+ row-gap: var(--space-3) !important;
479
+ }
480
+ .gap-y-4 {
481
+ row-gap: var(--space-4) !important;
482
+ }
483
+ .gap-y-6 {
484
+ row-gap: var(--space-6) !important;
485
+ }
486
+ .gap-y-8 {
487
+ row-gap: var(--space-8) !important;
488
+ }
489
+ .gap-y-12 {
490
+ row-gap: var(--space-12) !important;
491
+ }
492
+ .gap-y-20 {
493
+ row-gap: var(--space-20) !important;
494
+ }
495
+
496
+ /* Text color */
497
+ .text-secondary {
498
+ color: var(--color-text-secondary) !important;
499
+ }
500
+ .text-tertiary {
501
+ color: var(--color-text-tertiary) !important;
502
+ }
503
+
504
+ /* Visually hidden — hides content visually but keeps it available to
505
+ screen readers. Focusable elements reappear on keyboard focus. */
506
+ .visually-hidden:not(:focus):not(:focus-within):not(:active) {
507
+ position: absolute !important;
508
+ width: 1px !important;
509
+ height: 1px !important;
510
+ padding: 0 !important;
511
+ margin: -1px !important;
512
+ overflow: hidden !important;
513
+ clip: rect(0 0 0 0) !important;
514
+ clip-path: inset(50%) !important;
515
+ white-space: nowrap !important;
516
+ border: 0 !important;
517
+ }
518
+ }
package/wasm/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { runWasm } from "./wasmWorkerApi.js";
2
+ export { useModel } from "./useModel.js";
@@ -0,0 +1,53 @@
1
+ import { ref, toValue, watch } from "vue";
2
+ import type { MaybeRef } from "vue";
3
+ import type { ModelOutput } from "@cfasim-ui/shared";
4
+ import { runWasm } from "./wasmWorkerApi.js";
5
+
6
+ export function useModel<T = unknown>(model: string) {
7
+ const result = ref<T>();
8
+ const error = ref<string>();
9
+ const loading = ref(false);
10
+
11
+ async function run(fn: string, ...args: (string | number)[]) {
12
+ loading.value = true;
13
+ error.value = undefined;
14
+ try {
15
+ result.value = (await runWasm(model, fn, ...args.map(String))) as T;
16
+ } catch (e) {
17
+ error.value = e instanceof Error ? e.message : String(e);
18
+ } finally {
19
+ loading.value = false;
20
+ }
21
+ }
22
+
23
+ function useOutputs<P extends Record<string, unknown>>(
24
+ fn: string,
25
+ params: MaybeRef<P>,
26
+ ) {
27
+ const outputs = ref<Record<string, ModelOutput>>();
28
+ const outputsError = ref<string>();
29
+ const outputsLoading = ref(false);
30
+
31
+ watch(
32
+ () => toValue(params),
33
+ async (p) => {
34
+ outputsLoading.value = true;
35
+ outputsError.value = undefined;
36
+ try {
37
+ const args = Object.values(p).map(String);
38
+ const res = await runWasm(model, fn, ...args);
39
+ outputs.value = res as Record<string, ModelOutput>;
40
+ } catch (e) {
41
+ outputsError.value = e instanceof Error ? e.message : String(e);
42
+ } finally {
43
+ outputsLoading.value = false;
44
+ }
45
+ },
46
+ { immediate: true, deep: true },
47
+ );
48
+
49
+ return { outputs, error: outputsError, loading: outputsLoading };
50
+ }
51
+
52
+ return { run, result, error, loading, useOutputs };
53
+ }
@@ -0,0 +1,35 @@
1
+ import { execSync } from "node:child_process";
2
+ import { basename, resolve } from "node:path";
3
+
4
+ /**
5
+ * @typedef {object} CfasimWasmOptions
6
+ * @property {string} [model] Path to the Rust model directory (default: "model")
7
+ * @property {string} [name] Output name used for the wasm module (default: project directory name)
8
+ */
9
+
10
+ /**
11
+ * Vite plugin that builds a Rust model to WebAssembly via wasm-pack
12
+ * and outputs it to public/wasm/{name}/.
13
+ *
14
+ * @param {CfasimWasmOptions} [options]
15
+ * @returns {import("vite").Plugin}
16
+ */
17
+ export function cfasimWasm(options) {
18
+ const modelDir = options?.model ?? "model";
19
+
20
+ function build(root) {
21
+ const name = (options?.name ?? basename(root)).replace(/-/g, "_");
22
+ const outDir = resolve(root, "public", "wasm", name);
23
+ execSync(`wasm-pack build ${modelDir} --target web --out-dir ${outDir}`, {
24
+ cwd: root,
25
+ stdio: "pipe",
26
+ });
27
+ }
28
+
29
+ return {
30
+ name: "cfasim-wasm",
31
+ configResolved(config) {
32
+ build(config.root);
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,74 @@
1
+ import {
2
+ postWithTransfer,
3
+ postModelOutputsWithTransfer,
4
+ postErrorWithTransfer,
5
+ } from "@cfasim-ui/shared";
6
+
7
+ interface WorkerMessage {
8
+ id: number;
9
+ model: string;
10
+ fn: string;
11
+ args: string[];
12
+ }
13
+
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ const modulePromises = new Map<string, Promise<Record<string, any>>>();
16
+
17
+ const baseUrl = import.meta.env.BASE_URL ?? "/";
18
+
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ function ensureModule(model: string): Promise<Record<string, any>> {
21
+ if (!modulePromises.has(model)) {
22
+ const promise = (async () => {
23
+ const url = `${self.location.origin}${baseUrl}wasm/${model}/${model}.js`;
24
+ const mod = await import(/* @vite-ignore */ url);
25
+ await mod.default();
26
+ return mod;
27
+ })();
28
+ promise.catch(() => {
29
+ modulePromises.delete(model);
30
+ });
31
+ modulePromises.set(model, promise);
32
+ }
33
+ return modulePromises.get(model)!;
34
+ }
35
+
36
+ self.onmessage = async (event: MessageEvent<WorkerMessage>) => {
37
+ const { id, model, fn, args } = event.data;
38
+ try {
39
+ const t0 = performance.now();
40
+ const mod = await ensureModule(model);
41
+ const result = mod[fn](...args);
42
+ const elapsed = Math.round((performance.now() - t0) * 10) / 10;
43
+ console.log(`[wasm-worker] ${model}.${fn} ${elapsed}ms`);
44
+
45
+ if (result && typeof result === "object" && result.__modelOutputs) {
46
+ // Convert js_sys Arrays to real JS arrays for each output
47
+ const outputs: Record<
48
+ string,
49
+ { length: number; columns: unknown[]; buffers: ArrayBuffer[] }
50
+ > = {};
51
+ for (const [key, wire] of Object.entries(result.outputs)) {
52
+ const w = wire as {
53
+ length: number;
54
+ columns: unknown;
55
+ buffers: unknown;
56
+ };
57
+ outputs[key] = {
58
+ length: w.length,
59
+ columns: Array.from(w.columns as Iterable<unknown>),
60
+ buffers: Array.from(w.buffers as Iterable<unknown>) as ArrayBuffer[],
61
+ };
62
+ }
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ postModelOutputsWithTransfer(self, id, {
65
+ __modelOutputs: true,
66
+ outputs,
67
+ } as any);
68
+ } else {
69
+ postWithTransfer(self, id, result);
70
+ }
71
+ } catch (error) {
72
+ postErrorWithTransfer(self, id, error);
73
+ }
74
+ };
@@ -0,0 +1,38 @@
1
+ import type { TransferableResponse } from "@cfasim-ui/shared";
2
+ import { unwrapResponse } from "@cfasim-ui/shared";
3
+
4
+ interface WorkerMessage {
5
+ id: number;
6
+ model: string;
7
+ fn: string;
8
+ args: string[];
9
+ }
10
+
11
+ let lastId = 0;
12
+
13
+ const worker = new Worker(new URL("./wasm.worker.ts", import.meta.url), {
14
+ type: "module",
15
+ });
16
+
17
+ export function runWasm(
18
+ model: string,
19
+ fn: string,
20
+ ...args: string[]
21
+ ): Promise<unknown> {
22
+ return new Promise((resolve, reject) => {
23
+ const id = ++lastId;
24
+
25
+ function listener(event: MessageEvent<TransferableResponse>) {
26
+ if (event.data?.id !== id) return;
27
+ worker.removeEventListener("message", listener);
28
+ if (event.data.error) {
29
+ reject(new Error(event.data.error));
30
+ } else {
31
+ resolve(unwrapResponse(event.data));
32
+ }
33
+ }
34
+
35
+ worker.addEventListener("message", listener);
36
+ worker.postMessage({ id, model, fn, args } as WorkerMessage);
37
+ });
38
+ }