Package portable headless runtime with independent Node and browser consumers

This commit is contained in:
2026-11-09 01:53:53 +08:00
parent 8219209284
commit 0205f24904
20 changed files with 320 additions and 29 deletions
@@ -6,11 +6,8 @@ import { createReactRenderers, type CanvasRuntimeExtension } from "@/lib/canvas-
import { notify } from "@/lib/notifications";
import { errorText } from "@/lib/api";
import { createRegistry, builtinComponents } from "@/lib/canvas-core/index";
import { createRuntimeHost, type RuntimeHost } from "@/lib/canvas-runtime/host";
import { createWorkspaceRuntime } from "@/lib/canvas-runtime/workspace";
import { createBrowserDigests } from "@/lib/canvas-runtime/browser-digests";
import {createBrowserImageDecoder} from "@/lib/canvas-runtime/browser-images";
import { createBrowserStorage } from "@/lib/canvas-runtime/storage";
import { createRuntimeHost, createWorkspaceRuntime, type RuntimeHost } from "@/lib/canvas-runtime/index";
import { createBrowserDigests, createBrowserImageDecoder, createBrowserStorage } from "@/lib/canvas-runtime/browser";
import { createFoundryAssets, createFoundryPersistence, createFoundryTransport, createFoundryRuns,createFoundryModelLoader } from "@/lib/canvas-service/index";
const Context = createContext<RuntimeHost | null>(null);
+4
View File
@@ -0,0 +1,4 @@
/** Opt-in browser adapters. Importing the portable runtime does not load these. */
export { createBrowserStorage } from './storage.ts';
export { createBrowserDigests } from './browser-digests.ts';
export { createBrowserImageDecoder } from './browser-images.ts';
+1 -1
View File
@@ -31,7 +31,7 @@ import { createScheduler, type Clock, type OperationStatus } from './scheduler.t
import { planConflictRecovery, type ConflictPlan, type RecoveryChoice } from './conflicts.ts';
import { stageAppliedRecovery } from './applied-recovery.ts';
import { conflictsWith, maxResourceBytes, operationFootprint, referencedResources, type AssetPort, type ResourceRecord, type ResourceHandle, type StagedFile } from './resources.ts';
import type { StoragePort, StorageScope } from './storage.ts';
import type { StoragePort, StorageScope } from './storage-port.ts';
const digestFailures=new Set(['digest_capacity','digest_worker_failed','digest_worker_unavailable','invalid_digest_response','digest_pool_closed']);
export type EngineOptions = {
+24
View File
@@ -0,0 +1,24 @@
/** Portable runtime entry. Browser adapters are an explicit separate entry. */
export { openProjectEngine } from './engine.ts';
export type { EngineOptions, RecoverySession } from './engine.ts';
export { createWorkspaceRuntime } from './workspace.ts';
export type { WorkspaceOptions, WorkspaceRuntime, ProjectEngine } from './workspace.ts';
export { createRuntimeHost } from './host.ts';
export type { RuntimeHost } from './host.ts';
export type * from './contracts.ts';
export type * from './storage-port.ts';
export type { AssetPort, ResourceRecord, ResourceHandle, StagedFile, StoredAsset, DisplayHandle } from './resources.ts';
export type { RunPort, RunRecord, RunPage, RunEvent, RunStatus, RunModel } from './runs.ts';
export type { FrozenRequest, LedgerSnapshot, OperationRecord, Presence } from './ledger.ts';
export type { Clock, OperationStatus, CompiledRequest } from './scheduler.ts';
export type { ImportJob, ImportSource, ImportLine, ImportUnit, ImportFailure, NativeImportSource } from './imports.ts';
export type { RecoveryChoice, ConflictPlan, RecoveryAudit } from './conflicts.ts';
export { encodeRecovery, decodeRecovery } from './recovery.ts';
export { createScopedLoader, loadQueryKey } from './loader.ts';
export type { LoadScope, LoadChunk, LoadSnapshot, LoadPort, LoadHandle, ScopedLoader } from './loader.ts';
export { createDisplayCache } from './display-cache.ts';
export type { DisplayCache, DisplaySource, DisplayLease } from './display-cache.ts';
export { createDigestPool } from './digest-pool.ts';
export type { DigestPort } from './digest-pool.ts';
export { createImageDecodePool } from './image-decoder.ts';
export type { ImageDecoderPort, ImageDimensions, ImageDecodeState } from './image-decoder.ts';
+1 -1
View File
@@ -1,6 +1,6 @@
import type { Json } from '../canvas-core/types.ts';
import { fault, freeze, validJson } from '../canvas-core/values.ts';
import type { RecoveryImage } from './storage.ts';
import type { RecoveryImage } from './storage-port.ts';
type Encoded = {value:Json|undefined;height:number;reusable:boolean};
const immutable=new WeakMap<object,Encoded>(),sealed=new WeakSet<object>();
+1 -1
View File
@@ -16,7 +16,7 @@ import type { EngineSnapshot, RuntimeState } from './contracts.ts';
import type { FrozenRequest, LedgerSnapshot, OperationRecord } from './ledger.ts';
import { validateRunInput, type RunRecord } from './runs.ts';
import type { ResourceRecord } from './resources.ts';
import type { RecoveryImage } from './storage.ts';
import type { RecoveryImage } from './storage-port.ts';
const documentValue = (document: Document) => ({ projectId: document.projectId, canvases: [...document.canvases.values()], elements: [...document.elements.values()], tombstones: [...document.tombstones] });
/** Plain records only: functions, credentials, renderer objects, URLs and Map methods never serialize. */
+18
View File
@@ -0,0 +1,18 @@
import type { Json } from '../canvas-core/types.ts';
/** Portable durable-store contract. Browser database types belong to the adapter. */
export type StorageScope = { readonly accountId: string; readonly workspaceId: string; readonly projectId: string };
export type RecoveryImage = { readonly formatVersion: 1 | 2 | 3 | 4 | 5 | 6; readonly rootRevision: number; readonly localRevision: number; readonly payload: Json };
export type DurableBatch = { readonly image: RecoveryImage; readonly deleteFiles?:readonly string[]; readonly files?: readonly { readonly id: string; readonly bytes: Blob }[] };
export type StorageLeader = { readonly scope: StorageScope; readonly fence: number; readonly active: boolean; release(): Promise<void> };
export type StoredFileInfo={readonly id:string;readonly byteLength:number;readonly mimeType:string};
export type FileInventoryPage={readonly files:readonly StoredFileInfo[];readonly rootRevision:number;readonly nextAfter?:string};
export type StoragePort = {
scanFiles?(leader:StorageLeader,input:{readonly expectedRootRevision:number;readonly after?:string;readonly limit:number}):Promise<FileInventoryPage>;
readonly fileDeletion?: 'atomic-v1';
claim(scope: StorageScope): Promise<StorageLeader>;
recover(leader: StorageLeader): Promise<RecoveryImage | null>;
commit(leader: StorageLeader, batch: DurableBatch): Promise<{ readonly durableRootRevision: number }>;
readFile(leader: StorageLeader, id: string): Promise<Blob | undefined>;
close(): void;
};
+2 -15
View File
@@ -3,21 +3,8 @@ import { isSealedRecoveryImage } from './recovery-encoding.ts';
import type { Json } from '../canvas-core/types.ts';
import { clone, equal, fault, freeze, validJson } from '../canvas-core/values.ts';
export type StorageScope = { readonly accountId: string; readonly workspaceId: string; readonly projectId: string };
export type RecoveryImage = { readonly formatVersion: 1 | 2 | 3 | 4 | 5 | 6; readonly rootRevision: number; readonly localRevision: number; readonly payload: Json };
export type DurableBatch = { readonly image: RecoveryImage; readonly deleteFiles?:readonly string[]; readonly files?: readonly { readonly id: string; readonly bytes: Blob }[] };
export type StorageLeader = { readonly scope: StorageScope; readonly fence: number; readonly active: boolean; release(): Promise<void> };
export type StoredFileInfo={readonly id:string;readonly byteLength:number;readonly mimeType:string};
export type FileInventoryPage={readonly files:readonly StoredFileInfo[];readonly rootRevision:number;readonly nextAfter?:string};
export type StoragePort = {
scanFiles?(leader:StorageLeader,input:{readonly expectedRootRevision:number;readonly after?:string;readonly limit:number}):Promise<FileInventoryPage>;
readonly fileDeletion?: 'atomic-v1';
claim(scope: StorageScope): Promise<StorageLeader>;
recover(leader: StorageLeader): Promise<RecoveryImage | null>;
commit(leader: StorageLeader, batch: DurableBatch): Promise<{ readonly durableRootRevision: number }>;
readFile(leader: StorageLeader, id: string): Promise<Blob | undefined>;
close(): void;
};
import type {StorageScope,RecoveryImage,DurableBatch,StorageLeader,StoredFileInfo,FileInventoryPage,StoragePort} from './storage-port.ts';
export type {StorageScope,RecoveryImage,DurableBatch,StorageLeader,StoredFileInfo,FileInventoryPage,StoragePort} from './storage-port.ts';
type Metadata = { fence: number; throughRevision?: number; checkpointRevision?: number };
type StoredImage = { readonly image: RecoveryImage; readonly checksum: string };
type StoredDelta = {journalVersion:1;baseRevision:number;baseChecksum:string;header:Omit<RecoveryImage,'payload'>;patches:JournalPatch[];checksum:string};
+1 -1
View File
@@ -2,7 +2,7 @@ import type { Document, Id, Registry } from '../canvas-core/types.ts';
import { fault, readMap } from '../canvas-core/values.ts';
import { openProjectEngine, type EngineOptions } from './engine.ts';
import {createDigestPool,type DigestPort} from './digest-pool.ts';
import type { StoragePort } from './storage.ts';
import type { StoragePort } from './storage-port.ts';
import { createDisplayCache } from './display-cache.ts';
export type ProjectEngine = Awaited<ReturnType<typeof openProjectEngine>>;