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
41 changes: 19 additions & 22 deletions packages/extension/src/background.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import './background-trusted-prelude.js';
import type { Json } from '@metamask/utils';
import { CommandMethod, isCommandReply } from '@ocap/kernel';
import type { Command, CommandFunction } from '@ocap/kernel';
import { ClusterCommandMethod, isClusterCommandReply } from '@ocap/kernel';
import type { ClusterCommand, ClusterCommandFunction } from '@ocap/kernel';
import {
ChromeRuntimeTarget,
makeChromeRuntimeStreamPair,
Expand All @@ -26,9 +26,9 @@ async function main(): Promise<void> {
* @param params - The message data.
* @param params.name - The name to include in the message.
*/
const sendCommand: CommandFunction<Promise<void>> = async (
method: CommandMethod,
params?: Command['params'],
const sendClusterCommand: ClusterCommandFunction<Promise<void>> = async (
method: ClusterCommand['method'],
params?: ClusterCommand['params'],
) => {
await provideOffScreenDocument();

Expand All @@ -42,27 +42,25 @@ async function main(): Promise<void> {
Object.defineProperties(globalThis.kernel, {
capTpCall: {
value: async (method: string, params: Json[]) =>
sendCommand(CommandMethod.CapTpCall, { method, params }),
},
capTpInit: {
value: async () => sendCommand(CommandMethod.CapTpInit),
sendClusterCommand(ClusterCommandMethod.CapTpCall, { method, params }),
},
evaluate: {
value: async (source: string) =>
sendCommand(CommandMethod.Evaluate, source),
sendClusterCommand(ClusterCommandMethod.Evaluate, source),
},
ping: {
value: async () => sendCommand(CommandMethod.Ping),
value: async () => sendClusterCommand(ClusterCommandMethod.Ping),
},
sendMessage: {
value: sendCommand,
value: sendClusterCommand,
},
kvGet: {
value: async (key: string) => sendCommand(CommandMethod.KVGet, key),
value: async (key: string) =>
sendClusterCommand(ClusterCommandMethod.KVGet, key),
},
kvSet: {
value: async (key: string, value: string) =>
sendCommand(CommandMethod.KVSet, { key, value }),
sendClusterCommand(ClusterCommandMethod.KVSet, { key, value }),
},
});
harden(globalThis.kernel);
Expand All @@ -71,7 +69,7 @@ async function main(): Promise<void> {

// With this we can click the extension action button to wake up the service worker.
chrome.action.onClicked.addListener(() => {
sendCommand(CommandMethod.Ping).catch(console.error);
sendClusterCommand(ClusterCommandMethod.Ping).catch(console.error);
});

/**
Expand All @@ -89,18 +87,17 @@ async function main(): Promise<void> {

// Handle replies from the offscreen document
for await (const message of offscreenStreams.reader) {
if (!isCommandReply(message)) {
if (!isClusterCommandReply(message)) {
console.error('Background received unexpected message', message);
continue;
}

switch (message.method) {
case CommandMethod.Evaluate:
case CommandMethod.CapTpCall:
case CommandMethod.CapTpInit:
case CommandMethod.Ping:
case CommandMethod.KVGet:
case CommandMethod.KVSet:
case ClusterCommandMethod.Evaluate:
case ClusterCommandMethod.CapTpCall:
case ClusterCommandMethod.Ping:
case ClusterCommandMethod.KVGet:
case ClusterCommandMethod.KVSet:
console.log(message.params);
break;
default:
Expand Down
109 changes: 49 additions & 60 deletions packages/extension/src/kernel-worker.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import './kernel-worker-trusted-prelude.js';
import { CommandMethod, isCommand } from '@ocap/kernel';
import type { CommandReply, CommandReplyFunction } from '@ocap/kernel';
import { stringify } from '@ocap/utils';
import type { KernelCommand, KernelCommandReply } from '@ocap/kernel';
import { isKernelCommand, KernelCommandMethod } from '@ocap/kernel';
import type { Database } from '@sqlite.org/sqlite-wasm';
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';

main().catch(console.error);

// We temporarily have the kernel commands split between offscreen and kernel-worker
type KernelWorkerCommand = Extract<
KernelCommand,
| { method: typeof KernelCommandMethod.KVSet }
| { method: typeof KernelCommandMethod.KVGet }
>;

const isKernelWorkerCommand = (value: unknown): value is KernelWorkerCommand =>
isKernelCommand(value) &&
(value.method === KernelCommandMethod.KVSet ||
value.method === KernelCommandMethod.KVGet);

type Queue<Type> = Type[];

type VatId = `v${number}`;
Expand Down Expand Up @@ -165,10 +176,10 @@ async function main(): Promise<void> {
* @param method - The message method.
* @param params - The message params.
*/
const reply: CommandReplyFunction = (
method: CommandMethod,
params?: CommandReply['params'],
) => {
const reply = (
method: KernelCommandReply['method'],
params?: KernelCommandReply['params'],
): void => {
postMessage({ method, params });
};

Expand All @@ -183,71 +194,49 @@ async function main(): Promise<void> {
? problem
: new Error('Unknown', { cause: problem });

// Handle messages from the console service worker
globalThis.onmessage = async (event: MessageEvent<unknown>) => {
if (!isCommand(event.data)) {
console.error('Received unexpected message', event.data);
return;
}

const { method, params } = event.data;
console.log('received message: ', method, params);

/**
* Handle a KernelCommand sent from the offscreen.
*
* @param command - The KernelCommand to handle.
* @param command.method - The command method.
* @param command.params - The command params.
*/
const handleKernelCommand = ({
method,
params,
}: KernelWorkerCommand): void => {
switch (method) {
case CommandMethod.Evaluate:
reply(CommandMethod.Evaluate, await evaluate(params));
break;
case CommandMethod.CapTpCall: {
reply(
CommandMethod.CapTpCall,
'Error: CapTpCall not implemented here (yet)',
);
break;
}
case CommandMethod.CapTpInit:
reply(
CommandMethod.CapTpInit,
'Error: CapTpInit not implemented here (yet)',
);
break;
case CommandMethod.Ping:
reply(CommandMethod.Ping, 'pong');
break;
case CommandMethod.KVSet: {
const { key, value } = params;
kvSet(key, value);
reply(CommandMethod.KVSet, `~~~ set "${key}" to "${value}" ~~~`);
case KernelCommandMethod.KVSet:
kvSet(params.key, params.value);
reply(method, `~~~ set "${params.key}" to "${params.value}" ~~~`);
break;
}
case CommandMethod.KVGet: {
case KernelCommandMethod.KVGet: {
try {
const result = kvGet(params);
reply(CommandMethod.KVGet, result);
reply(method, result);
} catch (problem) {
// The below will work because we call into globalThis.postMessage() directly,
// which can handle errors. This will need to be addressed once we use streams here.
// @ts-expect-error TODO: Fix when we have streams.
reply(CommandMethod.KVGet, asError(problem));
// TODO: marshal
reply(method, String(asError(problem)));
}
break;
}
default:
console.error(
`Kernel received unexpected method in message: "${stringify(
// @ts-expect-error Runtime does not respect "never".
method.valueOf(),
)}"`,
'kernel worker received unexpected command',
// @ts-expect-error Runtime does not respect "never".
{ method: method.valueOf(), params },
);
}
};

/**
* Evaluate a string in the default iframe.
*
* @param _source - The source string to evaluate.
* @returns The result of the evaluation, or an error message.
*/
async function evaluate(_source: string): Promise<string> {
return `Error: evaluate not implemented here (yet)`;
}
// Handle messages from the console service worker
globalThis.onmessage = async (
event: MessageEvent<unknown>,
): Promise<void> => {
if (isKernelWorkerCommand(event.data)) {
handleKernelCommand(event.data);
} else {
console.error('Received unexpected message', event.data);
}
};
}
Loading