UNPKG

@metamask/ocap-kernel

Version:
184 lines 5.68 kB
import { define, is, object, string, array, record, union, tuple, literal, boolean, exactOptional, type } from "@metamask/superstruct"; import { UnsafeJsonStruct } from "@metamask/utils"; import { Fail } from "./utils/assert.mjs"; export const ROOT_OBJECT_VREF = 'o+0'; export const CapDataStruct = object({ body: string(), slots: array(string()), }); export const VatOneResolutionStruct = tuple([ string(), boolean(), CapDataStruct, ]); export const MessageStruct = object({ methargs: CapDataStruct, result: exactOptional(union([string(), literal(null)])), }); /** * Coerce a {@link SwingsetMessage} to our own JSON-RPC-compatible {@link Message}. * * @param message - The SwingsetMessage to coerce. * @returns The coerced Message. */ export function coerceMessage(message) { if (message.result === undefined) { delete message.result; } return message; } /** * Coerce a {@link VatSyscallObject} to a JSON-RPC-compatible {@link JsonVatSyscallObject}. * * @param vso - The VatSyscallObject to coerce. * @returns The coerced VatSyscallObject. */ export function coerceVatSyscallObject(vso) { if (vso[0] === 'send') { return ['send', vso[1], coerceMessage(vso[2])]; } return vso; } const RunQueueItemSendStruct = object({ type: literal('send'), target: string(), // KRef message: MessageStruct, }); const RunQueueItemNotifyStruct = object({ type: literal('notify'), vatId: string(), kpid: string(), }); const GCRunQueueTypeStruct = union([ literal('dropExports'), literal('retireExports'), literal('retireImports'), ]); export const actionTypePriorities = [ 'dropExport', 'retireExport', 'retireImport', ]; const RunQueueItemGCActionStruct = object({ type: GCRunQueueTypeStruct, vatId: string(), // VatId krefs: array(string()), // KRefs }); const RunQueueItemBringOutYourDeadStruct = object({ type: literal('bringOutYourDead'), vatId: string(), }); export const RunQueueItemStruct = union([ RunQueueItemSendStruct, RunQueueItemNotifyStruct, RunQueueItemGCActionStruct, RunQueueItemBringOutYourDeadStruct, ]); /** * Assert that a value is a valid message. * * @param value - The value to check. * @throws if the value is not a valid message. */ export function insistMessage(value) { is(value, MessageStruct) || Fail `not a valid message`; } export const isVatId = (value) => typeof value === 'string' && value.at(0) === 'v' && value.slice(1) === String(Number(value.slice(1))); /** * Assert that a value is a valid vat id. * * @param value - The value to check. * @throws if the value is not a valid vat id. */ export function insistVatId(value) { isVatId(value) || Fail `not a valid VatId`; } export const VatIdStruct = define('VatId', isVatId); export const isSubclusterId = (value) => typeof value === 'string' && value.at(0) === 's' && value.slice(1) === String(Number(value.slice(1))); export const SubclusterIdStruct = define('SubclusterId', isSubclusterId); export const isVatMessageId = (value) => typeof value === 'string' && value.at(0) === 'm' && value.slice(1) === String(Number(value.slice(1))); export const VatMessageIdStruct = define('VatMessageId', isVatMessageId); const UserCodeSpecStruct = union([ object({ sourceSpec: string(), }), object({ bundleSpec: string(), }), object({ bundleName: string(), }), ]); export const VatConfigStruct = define('VatConfig', (value) => { if (!value) { return false; } const { creationOptions, parameters, ...specOnly } = value; return (is(specOnly, UserCodeSpecStruct) && (!creationOptions || is(creationOptions, UnsafeJsonStruct)) && (!parameters || is(parameters, UnsafeJsonStruct))); }); export const isVatConfig = (value) => is(value, VatConfigStruct); export const ClusterConfigStruct = object({ bootstrap: string(), forceReset: exactOptional(boolean()), vats: record(string(), VatConfigStruct), bundles: exactOptional(record(string(), VatConfigStruct)), }); export const isClusterConfig = (value) => is(value, ClusterConfigStruct); export const SubclusterStruct = object({ id: SubclusterIdStruct, config: ClusterConfigStruct, vats: array(VatIdStruct), }); export const KernelStatusStruct = type({ subclusters: array(SubclusterStruct), vats: array(object({ id: VatIdStruct, config: VatConfigStruct, subclusterId: SubclusterIdStruct, })), }); /** * A mapping of GC action type to queue event type. */ export const queueTypeFromActionType = new Map([ // Note: From singular to plural ['dropExport', 'dropExports'], ['retireExport', 'retireExports'], ['retireImport', 'retireImports'], ]); export const isGCActionType = (value) => actionTypePriorities.includes(value); /** * Assert that a value is a valid GC action type. * * @param value - The value to check. * @throws if the value is not a valid GC action type. */ export function insistGCActionType(value) { isGCActionType(value) || Fail `not a valid GCActionType ${value}`; } export const GCActionStruct = define('GCAction', (value) => { if (typeof value !== 'string') { return false; } const [vatId, actionType, kref] = value.split(' '); if (!isVatId(vatId)) { return false; } if (!isGCActionType(actionType)) { return false; } if (typeof kref !== 'string' || !kref.startsWith('ko')) { return false; } return true; }); export const isGCAction = (value) => is(value, GCActionStruct); //# sourceMappingURL=types.mjs.map