42 lines
2.3 KiB
TypeScript
42 lines
2.3 KiB
TypeScript
import type { Id, Registry } from '../canvas-core/types.ts';
|
|
import { fault, readMap } from '../canvas-core/values.ts';
|
|
import type { WorkspaceRuntime } from './workspace.ts';
|
|
|
|
/** Host lifetime matches the signed-in application, independently of navigation. */
|
|
export function createRuntimeHost(options: { registry: Registry; workspace(accountId: Id, workspaceId: Id, authEpoch: number, current: () => boolean): WorkspaceRuntime; onError?: (error: unknown) => void }) {
|
|
let accountId: Id | null = null, authEpoch = 0, disposed = false;
|
|
const workspaces = new Map<Id, WorkspaceRuntime>(), retired = new Set<WorkspaceRuntime>();
|
|
const closeRetired = async () => {
|
|
const results = await Promise.allSettled([...retired].map(async runtime => { await runtime.close(); retired.delete(runtime); }));
|
|
const errors = results.flatMap(result => result.status === 'rejected' ? [result.reason] : []);
|
|
if (errors.length) throw new AggregateError(errors, 'host_close_failed');
|
|
};
|
|
function retire() { authEpoch++; accountId = null; for (const runtime of workspaces.values()) retired.add(runtime); workspaces.clear(); }
|
|
function activate(account: Id) {
|
|
if (disposed) throw fault('host_disposed');
|
|
if (accountId !== account) {
|
|
retire(); accountId = account;
|
|
void closeRetired().catch(error => { try { options.onError?.(error); } catch { /* Reporting cannot change scope. */ } });
|
|
}
|
|
}
|
|
return Object.freeze({
|
|
registry: options.registry,
|
|
bindAccount(account: Id) { activate(account); const epoch = authEpoch; return () => !disposed && accountId === account && authEpoch === epoch; },
|
|
identity: () => Object.freeze({ accountId, authEpoch }),
|
|
getWorkspaces: () => readMap(workspaces),
|
|
workspace(account: Id, workspace: Id) {
|
|
activate(account);
|
|
let runtime = workspaces.get(workspace);
|
|
if (!runtime) {
|
|
const epoch = authEpoch;
|
|
runtime = options.workspace(account, workspace, epoch, () => !disposed && accountId === account && authEpoch === epoch);
|
|
workspaces.set(workspace, runtime);
|
|
}
|
|
return runtime;
|
|
},
|
|
retireIdentity() { retire(); return closeRetired(); },
|
|
close() { disposed = true; retire(); return closeRetired(); },
|
|
});
|
|
}
|
|
export type RuntimeHost = ReturnType<typeof createRuntimeHost>;
|