Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UPSTREAM="localhost:5173"
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"deno.enable": true
"deno.enable": false
}
11 changes: 10 additions & 1 deletion typescript/common/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"@commontools/data": "^0.0.1",
"@commontools/io": "^0.0.1",
"@commontools/module": "^0.0.1",
"@commontools/usuba-rt": "^0.0.1"
"@commontools/usuba-rt": "^0.0.1",
"@commontools/usuba-ses": "^0.0.1"
},
"devDependencies": {
"tslib": "^2.6.2",
Expand All @@ -42,6 +43,7 @@
"build": {
"dependencies": [
"../../packages/usuba-rt:build",
"../../packages/usuba-ses:build",
"../data:build",
"../io:build",
"../module:build"
Expand All @@ -55,6 +57,13 @@
"command": "tsc --build -f"
},
"clean": {
"dependencies": [
"../../packages/usuba-rt:clean",
"../../packages/usuba-ses:clean",
"../data:clean",
"../io:clean",
"../module:clean"
],
"command": "rm -rf ./lib ./.wireit"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {
Dictionary as CommonDictionary,
Value,
} from '@commontools/data/interfaces/common-data-types.js';
import { IO as CommonIO } from './io.js';
import { IO as CommonIO } from '../../state/io/index.js';
import { infer } from './infer.js';
import { Reference } from './reference.js';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {
Value,
Reference as CommonReference,
} from '@commontools/data/interfaces/common-data-types.js';
import { IO } from './io.js';
import { IO } from '../../state/io/index.js';

export class Reference implements CommonReference {
#io;
Expand Down
38 changes: 38 additions & 0 deletions typescript/common/runtime/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export type ElementUnion<T extends readonly any[]> = T[number];

export type EventMap<Events> = {
[E in keyof Events]: never;
};

export const assertNever = (value: never): never => {
throw new Error(`Unhandled value: ${value}`);
};

export const downcast = <T>(value: unknown): T => {
return value as T;
};

export const isEvent = <AllEvents, Event extends AllEvents>(
event: AllEvents,
check: Event
): event is Event => {
return event === check;
};

export const isError = (candidate: unknown): candidate is { error: string } => {
return (
candidate != null &&
typeof candidate == 'object' &&
'error' in candidate &&
typeof candidate.error != 'undefined'
);
};

export const throwIfError = (
candidate: unknown
): candidate is { error: string } => {
if (isError(candidate)) {
throw new Error(candidate.error);
}
return false;
};
117 changes: 78 additions & 39 deletions typescript/common/runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,95 @@
import { Runtime as UsubaRuntime } from '@commontools/usuba-rt';
import { wit as commonDataWit } from '@commontools/data';
import { wit as commonIoWit } from '@commontools/io';
import { wit as commonModuleWit } from '@commontools/module';
import type { Value } from '@commontools/data/interfaces/common-data-types.js';
import type { IO } from './io.js';
import { Reference } from './reference.js';
import { Dictionary } from './dictionary.js';
import { WorkerPool } from './worker/pool.js';
import { Input } from './state/input.js';
import { HostToModuleRPC } from './rpc/index.js';
import { Output } from './state/output.js';
import { assertNever, throwIfError } from './helpers.js';
import { HostModuleEventHandler } from './rpc/host.js';

export type { Value } from '@commontools/data/interfaces/common-data-types.js';

export * from './io.js';
export * from './dictionary.js';
export * from './reference.js';
export * from './infer.js';
export * from './state/io/index.js';
export * from './state/input.js';
export * from './state/output.js';
export * from './state/storage/index.js';
export * from './state/storage/localstorage.js';
export * from './common/data/dictionary.js';
export * from './common/data/reference.js';
export * from './common/data/infer.js';

export type ContentType = 'text/javascript';

export interface Module {
run: () => void;
export const SES_SANDBOX = 'ses';
export const WASM_SANDBOX = 'wasm';
export const CONFIDENTIAL_COMPUTE_SANDBOX = 'confidential-compute';

export type Sandbox =
| typeof SES_SANDBOX
| typeof WASM_SANDBOX
| typeof CONFIDENTIAL_COMPUTE_SANDBOX;

export class Module {
#rpc;
#input;

constructor(port: MessagePort, input: Input) {
this.#rpc = new HostToModuleRPC(port, this.#handleModuleRPCEvent);
this.#input = input;
}

async run(): Promise<void> {
throwIfError(await this.#rpc.send('module:run', undefined));
}

output(keys: string[]) {
return new Output(this.#rpc, keys);
}

#handleModuleRPCEvent = (async (event, detail) => {
switch (event) {
case 'host:storage:read':
try {
return {
value: await this.#input.read(detail.key),
};
} catch (error) {
return {
error: `${error}`,
};
}
}

return assertNever(event as never);
}) as HostModuleEventHandler;
}

export class Runtime {
#inner = new UsubaRuntime([commonDataWit, commonIoWit]);
#workerPool = new WorkerPool();

async eval(
contentType: ContentType,
id: string,
sandbox: Sandbox,
contentType: 'text/javascript',
sourceCode: string,
io: IO
input: Input
): Promise<Module> {
const blueprint = await this.#inner.defineModule<{
module: { create(): { run: () => void } };
}>({
contentType,
sourceCode,
wit: commonModuleWit,
});

const { module } = await blueprint.instantiate({
'common:data/types': {
Reference,
Dictionary,
},
'common:io/state': {
read(name: string) {
return new Reference(io, name);
},
write(name: string, value: Value) {
io.write(name, value);
const { rpc: runtimeRpc } = await this.#workerPool.get(sandbox);
const { port1: moduleTx, port2: moduleRx } = new MessageChannel();
const module = new Module(moduleTx, input);

throwIfError(
await runtimeRpc.send(
'runtime:eval',
{
id,
contentType,
sourceCode,
inputKeys: input.keys,
port: moduleRx,
},
},
});
[moduleRx]
)
);

return module.create();
return module;
}
}
57 changes: 57 additions & 0 deletions typescript/common/runtime/src/rpc/host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// import { Value } from '@commontools/data/interfaces/common-data-types.js';

import { ElementUnion, EventMap } from '../helpers.js';
import { Value } from '../index.js';
import { RPCEventHandler } from './index.js';

/**
* Messages sent to the host runtime from a worker
*/

export const HOST_WORKER_EVENTS = ['rpc:handshake:confirmed'] as const;

export type HostWorkerEvents = ElementUnion<typeof HOST_WORKER_EVENTS>;

export type HostWorkerRequests = EventMap<HostWorkerEvents> & {
'rpc:handshake:confirmed': void;
};

export type HostWorkerResponses = EventMap<HostWorkerEvents> & {
'rpc:handshake:confirmed': void;
};

export type HostWorkerEventHandler = RPCEventHandler<
HostWorkerEvents,
HostWorkerRequests,
HostWorkerResponses
>;

/**
* Messages sent to the host runtime from a module-within-a-worker
*/

export const HOST_MODULE_EVENTS = ['host:storage:read'] as const;

export type HostModuleEvents = ElementUnion<typeof HOST_MODULE_EVENTS>;

export type HostModuleRequests = EventMap<HostModuleEvents> & {
'host:storage:read': {
key: string;
};
};

export type HostModuleResponses = EventMap<HostModuleEvents> & {
'host:storage:read':
| {
error: string;
}
| {
value: Value;
};
};

export type HostModuleEventHandler = RPCEventHandler<
HostModuleEvents,
HostModuleRequests,
HostModuleResponses
>;
Loading