Offload functions to worker threads with shared memory primitives for Node.js.
1import type { Loadable } from './loadable.ts';
2
3type LoadableKeys<T extends Record<string, unknown>> = {
4 [K in keyof T]: T[K] extends Loadable<any> ? K : never;
5}[keyof T];
6
7type Simplify<T> = { [K in keyof T]: T[K] } & {};
8
9type FieldValues<T extends Record<string, unknown>> = Simplify<{
10 [K in LoadableKeys<T>]: T[K] extends Loadable<infer V> ? V : never;
11}>;
12
13function isLoadable(value: unknown): value is Loadable<unknown> {
14 return (
15 typeof value === 'object' &&
16 value !== null &&
17 typeof (value as any).load === 'function' &&
18 typeof (value as any).store === 'function'
19 );
20}
21
22const SHARED = Symbol.for('moroutine.shared');
23
24/** A named group of shared-memory fields with bulk `load()`/`store()` access. */
25export class SharedStruct<T extends Record<string, unknown>> implements Loadable<FieldValues<T>> {
26 readonly fields: T;
27
28 constructor(fields: T) {
29 this.fields = fields;
30 }
31
32 load(): FieldValues<T> {
33 const result = {} as any;
34 for (const key in this.fields) {
35 const field = this.fields[key];
36 if (isLoadable(field)) {
37 result[key] = field.load();
38 }
39 }
40 return result;
41 }
42
43 store(values: FieldValues<T>): void {
44 for (const key in this.fields) {
45 const field = this.fields[key];
46 if (isLoadable(field)) {
47 if (key in (values as any)) {
48 field.store((values as any)[key]);
49 }
50 }
51 }
52 }
53
54 [SHARED](): { tag: string; fields: Record<string, unknown> } {
55 const serializedFields: Record<string, unknown> = {};
56 for (const key in this.fields) {
57 const field = this.fields[key];
58 if (typeof field === 'object' && field !== null && SHARED in field) {
59 serializedFields[key] = (field as any)[SHARED]();
60 }
61 }
62 return { tag: 'SharedStruct', fields: serializedFields };
63 }
64}