@go-go-golems/os-scripting 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +147 -0
  2. package/app/createAppStore.d.ts +89 -0
  3. package/app/createAppStore.js +90 -0
  4. package/app/index.d.ts +2 -0
  5. package/app/index.js +2 -0
  6. package/app/runtimeSessionLifecycleMiddleware.d.ts +6 -0
  7. package/app/runtimeSessionLifecycleMiddleware.js +42 -0
  8. package/features/runtimeSessions/capabilityPolicy.d.ts +12 -0
  9. package/features/runtimeSessions/capabilityPolicy.js +36 -0
  10. package/features/runtimeSessions/index.d.ts +3 -0
  11. package/features/runtimeSessions/index.js +3 -0
  12. package/features/runtimeSessions/runtimeSessionsSlice.d.ts +90 -0
  13. package/features/runtimeSessions/runtimeSessionsSlice.js +210 -0
  14. package/features/runtimeSessions/selectors.d.ts +16 -0
  15. package/features/runtimeSessions/selectors.js +40 -0
  16. package/hypercard/artifacts/artifactProjectionMiddleware.d.ts +1 -0
  17. package/hypercard/artifacts/artifactProjectionMiddleware.js +66 -0
  18. package/hypercard/artifacts/artifactRuntime.d.ts +22 -0
  19. package/hypercard/artifacts/artifactRuntime.js +137 -0
  20. package/hypercard/artifacts/artifactsSelectors.d.ts +10 -0
  21. package/hypercard/artifacts/artifactsSelectors.js +2 -0
  22. package/hypercard/artifacts/artifactsSlice.d.ts +38 -0
  23. package/hypercard/artifacts/artifactsSlice.js +94 -0
  24. package/hypercard/debug/RuntimeSurfaceDebugWindow.d.ts +7 -0
  25. package/hypercard/debug/RuntimeSurfaceDebugWindow.js +181 -0
  26. package/hypercard/debug/jsSessionDebugRegistry.d.ts +11 -0
  27. package/hypercard/debug/jsSessionDebugRegistry.js +43 -0
  28. package/hypercard/debug/runtimeDebugApp.d.ts +19 -0
  29. package/hypercard/debug/runtimeDebugApp.js +25 -0
  30. package/hypercard/debug/runtimeDebugRegistry.d.ts +6 -0
  31. package/hypercard/debug/runtimeDebugRegistry.js +49 -0
  32. package/hypercard/editor/CodeEditorWindow.d.ts +7 -0
  33. package/hypercard/editor/CodeEditorWindow.js +128 -0
  34. package/hypercard/editor/editorLaunch.d.ts +16 -0
  35. package/hypercard/editor/editorLaunch.js +58 -0
  36. package/hypercard/editor/runtimeSurfaceRef.d.ts +8 -0
  37. package/hypercard/editor/runtimeSurfaceRef.js +60 -0
  38. package/hypercard/index.d.ts +12 -0
  39. package/hypercard/index.js +12 -0
  40. package/hypercard/task-manager/TaskManagerWindow.d.ts +1 -0
  41. package/hypercard/task-manager/TaskManagerWindow.js +102 -0
  42. package/hypercard/task-manager/index.d.ts +6 -0
  43. package/hypercard/task-manager/index.js +6 -0
  44. package/hypercard/task-manager/jsSessionSource.d.ts +10 -0
  45. package/hypercard/task-manager/jsSessionSource.js +49 -0
  46. package/hypercard/task-manager/runtimeSessionSource.d.ts +26 -0
  47. package/hypercard/task-manager/runtimeSessionSource.js +123 -0
  48. package/hypercard/task-manager/taskManagerApp.d.ts +16 -0
  49. package/hypercard/task-manager/taskManagerApp.js +25 -0
  50. package/hypercard/task-manager/taskManagerRegistry.d.ts +10 -0
  51. package/hypercard/task-manager/taskManagerRegistry.js +75 -0
  52. package/hypercard/task-manager/types.d.ts +25 -0
  53. package/hypercard/task-manager/types.js +1 -0
  54. package/hypercard/timeline/hypercardCard.d.ts +5 -0
  55. package/hypercard/timeline/hypercardCard.js +104 -0
  56. package/index.d.ts +18 -0
  57. package/index.js +18 -0
  58. package/package.json +72 -0
  59. package/plugin-runtime/contracts.d.ts +116 -0
  60. package/plugin-runtime/contracts.js +32 -0
  61. package/plugin-runtime/fixtures/column-stack.vm.js +19 -0
  62. package/plugin-runtime/fixtures/dynamic-card.vm.js +13 -0
  63. package/plugin-runtime/fixtures/inventory-stack.vm.js +29 -0
  64. package/plugin-runtime/fixtures/kanban-card.vm.js +55 -0
  65. package/plugin-runtime/fixtures/loop-stack.vm.js +16 -0
  66. package/plugin-runtime/fixtures/patched-low-stock-handler.vm.js +3 -0
  67. package/plugin-runtime/fixtures/patched-low-stock-render.vm.js +5 -0
  68. package/plugin-runtime/index.d.ts +6 -0
  69. package/plugin-runtime/index.js +6 -0
  70. package/plugin-runtime/intentSchema.d.ts +3 -0
  71. package/plugin-runtime/intentSchema.js +25 -0
  72. package/plugin-runtime/jsEvalSupport.d.ts +6 -0
  73. package/plugin-runtime/jsEvalSupport.js +93 -0
  74. package/plugin-runtime/jsSessionService.d.ts +55 -0
  75. package/plugin-runtime/jsSessionService.js +136 -0
  76. package/plugin-runtime/quickJsSessionCore.d.ts +24 -0
  77. package/plugin-runtime/quickJsSessionCore.js +92 -0
  78. package/plugin-runtime/runtimeService.d.ts +34 -0
  79. package/plugin-runtime/runtimeService.js +248 -0
  80. package/plugin-runtime/runtimeSurfaceRegistry.d.ts +45 -0
  81. package/plugin-runtime/runtimeSurfaceRegistry.js +73 -0
  82. package/plugin-runtime/stack-bootstrap.vm.js +236 -0
  83. package/repl/attachedJsSessionRegistry.d.ts +25 -0
  84. package/repl/attachedJsSessionRegistry.js +81 -0
  85. package/repl/attachedRuntimeSessionRegistry.d.ts +11 -0
  86. package/repl/attachedRuntimeSessionRegistry.js +107 -0
  87. package/repl/hypercardReplDriver.d.ts +54 -0
  88. package/repl/hypercardReplDriver.js +600 -0
  89. package/repl/jsReplDriver.d.ts +8 -0
  90. package/repl/jsReplDriver.js +348 -0
  91. package/repl/jsSessionBroker.d.ts +21 -0
  92. package/repl/jsSessionBroker.js +75 -0
  93. package/repl/runtimeBroker.d.ts +43 -0
  94. package/repl/runtimeBroker.js +117 -0
  95. package/runtime-host/RuntimeSurfaceSessionHost.d.ts +8 -0
  96. package/runtime-host/RuntimeSurfaceSessionHost.js +384 -0
  97. package/runtime-host/fixtures/CardSessionHost.chat.vm.js +61 -0
  98. package/runtime-host/fixtures/CardSessionHost.list.vm.js +29 -0
  99. package/runtime-host/fixtures/CardSessionHost.nav.vm.js +101 -0
  100. package/runtime-host/fixtures/CardSessionHost.report.vm.js +34 -0
  101. package/runtime-host/pluginIntentRouting.d.ts +13 -0
  102. package/runtime-host/pluginIntentRouting.js +83 -0
  103. package/runtime-packages/index.d.ts +1 -0
  104. package/runtime-packages/index.js +1 -0
  105. package/runtime-packages/runtimePackageRegistry.d.ts +14 -0
  106. package/runtime-packages/runtimePackageRegistry.js +42 -0
  107. package/runtime-packages/ui.package.vm.js +84 -0
  108. package/runtime-packs/index.d.ts +1 -0
  109. package/runtime-packs/index.js +1 -0
  110. package/runtime-packs/runtimeSurfaceTypeRegistry.d.ts +20 -0
  111. package/runtime-packs/runtimeSurfaceTypeRegistry.js +37 -0
  112. package/runtime-session-manager/index.d.ts +2 -0
  113. package/runtime-session-manager/index.js +2 -0
  114. package/runtime-session-manager/runtimeOwnership.d.ts +10 -0
  115. package/runtime-session-manager/runtimeOwnership.js +19 -0
  116. package/runtime-session-manager/runtimeSessionManager.d.ts +47 -0
  117. package/runtime-session-manager/runtimeSessionManager.js +214 -0
  118. package/testRuntimeUi.d.ts +4 -0
  119. package/testRuntimeUi.js +39 -0
@@ -0,0 +1,83 @@
1
+ import { showToast } from '@go-go-golems/os-core';
2
+ import { authorizeDomainIntent, authorizeSystemIntent } from '../features/runtimeSessions';
3
+ import { ingestRuntimeAction } from '../features/runtimeSessions';
4
+ import { closeWindow, sessionNavBack, sessionNavGo } from '@go-go-golems/os-core/desktop-core';
5
+ import { getRuntimeActionDomain, getRuntimeActionKind } from '../plugin-runtime/contracts';
6
+ function isRecord(value) {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
9
+ function toSystemAction(action, context) {
10
+ if (action.type === 'nav.back') {
11
+ return sessionNavBack({ sessionId: context.sessionId });
12
+ }
13
+ if (action.type === 'nav.go') {
14
+ if (!isRecord(action.payload)) {
15
+ return null;
16
+ }
17
+ const surface = action.payload.surfaceId;
18
+ if (typeof surface !== 'string' || surface.length === 0) {
19
+ return null;
20
+ }
21
+ const param = typeof action.payload.param === 'string' ? action.payload.param : undefined;
22
+ return sessionNavGo({ sessionId: context.sessionId, surface, param });
23
+ }
24
+ if (action.type === 'notify.show') {
25
+ if (!isRecord(action.payload)) {
26
+ return null;
27
+ }
28
+ const message = action.payload.message;
29
+ if (typeof message !== 'string' || message.length === 0) {
30
+ return null;
31
+ }
32
+ return showToast(message);
33
+ }
34
+ if (action.type === 'window.close') {
35
+ return closeWindow(context.windowId);
36
+ }
37
+ return null;
38
+ }
39
+ function toDomainAction(action, context) {
40
+ return {
41
+ type: action.type,
42
+ payload: action.payload,
43
+ meta: {
44
+ source: 'plugin-runtime',
45
+ sessionId: context.sessionId,
46
+ runtimeSessionId: context.sessionId,
47
+ surfaceId: context.surfaceId,
48
+ windowId: context.windowId,
49
+ },
50
+ };
51
+ }
52
+ export function dispatchRuntimeAction(action, context) {
53
+ context.dispatch(ingestRuntimeAction({
54
+ sessionId: context.sessionId,
55
+ surfaceId: context.surfaceId,
56
+ action,
57
+ }));
58
+ const runtimeSession = context.getState?.()?.runtimeSessions?.sessions?.[context.sessionId];
59
+ const kind = getRuntimeActionKind(action.type);
60
+ if (kind === 'domain' && runtimeSession?.capabilities) {
61
+ const domain = getRuntimeActionDomain(action.type);
62
+ const decision = authorizeDomainIntent(runtimeSession.capabilities, domain ?? '');
63
+ if (!decision.allowed) {
64
+ return;
65
+ }
66
+ }
67
+ if (kind === 'system' && runtimeSession?.capabilities) {
68
+ const decision = authorizeSystemIntent(runtimeSession.capabilities, action.type);
69
+ if (!decision.allowed) {
70
+ return;
71
+ }
72
+ }
73
+ if (kind === 'domain') {
74
+ context.dispatch(toDomainAction(action, context));
75
+ return;
76
+ }
77
+ if (kind === 'system') {
78
+ const routed = toSystemAction(action, context);
79
+ if (routed) {
80
+ context.dispatch(routed);
81
+ }
82
+ }
83
+ }
@@ -0,0 +1 @@
1
+ export * from './runtimePackageRegistry';
@@ -0,0 +1 @@
1
+ export * from './runtimePackageRegistry';
@@ -0,0 +1,14 @@
1
+ export interface RuntimePackageDefinition {
2
+ packageId: string;
3
+ version: string;
4
+ summary?: string;
5
+ docsMetadata?: Record<string, unknown>;
6
+ installPrelude: string;
7
+ surfaceTypes: string[];
8
+ dependencies?: string[];
9
+ }
10
+ export declare function registerRuntimePackage(definition: RuntimePackageDefinition): void;
11
+ export declare function clearRuntimePackages(): void;
12
+ export declare function getRuntimePackageOrThrow(packageId: string): RuntimePackageDefinition;
13
+ export declare function listRuntimePackages(): string[];
14
+ export declare function resolveRuntimePackageInstallOrder(packageIds: string[]): string[];
@@ -0,0 +1,42 @@
1
+ const runtimePackages = new Map();
2
+ export function registerRuntimePackage(definition) {
3
+ runtimePackages.set(definition.packageId, definition);
4
+ }
5
+ export function clearRuntimePackages() {
6
+ runtimePackages.clear();
7
+ }
8
+ export function getRuntimePackageOrThrow(packageId) {
9
+ const runtimePackage = runtimePackages.get(packageId);
10
+ if (!runtimePackage) {
11
+ throw new Error(`Unknown runtime package: ${packageId}`);
12
+ }
13
+ return runtimePackage;
14
+ }
15
+ export function listRuntimePackages() {
16
+ return Array.from(runtimePackages.keys()).sort();
17
+ }
18
+ export function resolveRuntimePackageInstallOrder(packageIds) {
19
+ const ordered = [];
20
+ const visiting = new Set();
21
+ const visited = new Set();
22
+ function visit(packageId) {
23
+ if (visited.has(packageId)) {
24
+ return;
25
+ }
26
+ if (visiting.has(packageId)) {
27
+ throw new Error(`Runtime package dependency cycle detected at ${packageId}`);
28
+ }
29
+ visiting.add(packageId);
30
+ const runtimePackage = getRuntimePackageOrThrow(packageId);
31
+ for (const dependencyId of runtimePackage.dependencies ?? []) {
32
+ visit(dependencyId);
33
+ }
34
+ visiting.delete(packageId);
35
+ visited.add(packageId);
36
+ ordered.push(packageId);
37
+ }
38
+ for (const packageId of packageIds) {
39
+ visit(packageId);
40
+ }
41
+ return ordered;
42
+ }
@@ -0,0 +1,84 @@
1
+ const __ui = {
2
+ text(content) {
3
+ return { kind: 'text', text: String(content) };
4
+ },
5
+ button(label, props = {}) {
6
+ return { kind: 'button', props: { label: String(label), ...props } };
7
+ },
8
+ input(value, props = {}) {
9
+ return { kind: 'input', props: { value: String(value ?? ''), ...props } };
10
+ },
11
+ row(children = []) {
12
+ return { kind: 'row', children: Array.isArray(children) ? children : [] };
13
+ },
14
+ column(children = []) {
15
+ return { kind: 'column', children: Array.isArray(children) ? children : [] };
16
+ },
17
+ panel(children = []) {
18
+ return { kind: 'panel', children: Array.isArray(children) ? children : [] };
19
+ },
20
+ badge(text) {
21
+ return { kind: 'badge', text: String(text) };
22
+ },
23
+ table(rows = [], props = {}) {
24
+ return {
25
+ kind: 'table',
26
+ props: {
27
+ headers: Array.isArray(props?.headers) ? props.headers : [],
28
+ rows: Array.isArray(rows) ? rows : [],
29
+ },
30
+ };
31
+ },
32
+ dropdown(options = [], props = {}) {
33
+ const selected = Number.isFinite(Number(props?.selected)) ? Number(props.selected) : 0;
34
+ return {
35
+ kind: 'dropdown',
36
+ props: {
37
+ options: Array.isArray(options) ? options.map((option) => String(option)) : [],
38
+ selected,
39
+ onSelect: props?.onSelect,
40
+ width: props?.width,
41
+ },
42
+ };
43
+ },
44
+ selectableTable(rows = [], props = {}) {
45
+ return {
46
+ kind: 'selectableTable',
47
+ props: {
48
+ headers: Array.isArray(props?.headers) ? props.headers.map((header) => String(header)) : [],
49
+ rows: Array.isArray(rows) ? rows : [],
50
+ selectedRowKeys: Array.isArray(props?.selectedRowKeys)
51
+ ? props.selectedRowKeys.map((key) => String(key))
52
+ : [],
53
+ mode: props?.mode,
54
+ rowKeyIndex: Number.isFinite(Number(props?.rowKeyIndex)) ? Number(props.rowKeyIndex) : 0,
55
+ searchable: props?.searchable === true,
56
+ searchText: typeof props?.searchText === 'string' ? props.searchText : '',
57
+ searchPlaceholder: typeof props?.searchPlaceholder === 'string' ? props.searchPlaceholder : undefined,
58
+ emptyMessage: typeof props?.emptyMessage === 'string' ? props.emptyMessage : undefined,
59
+ onSelectionChange: props?.onSelectionChange,
60
+ onSearchChange: props?.onSearchChange,
61
+ onRowClick: props?.onRowClick,
62
+ },
63
+ };
64
+ },
65
+ gridBoard(props = {}) {
66
+ return {
67
+ kind: 'gridBoard',
68
+ props: {
69
+ rows: Number.isFinite(Number(props?.rows)) ? Number(props.rows) : 1,
70
+ cols: Number.isFinite(Number(props?.cols)) ? Number(props.cols) : 1,
71
+ cells: Array.isArray(props?.cells) ? props.cells : [],
72
+ selectedIndex:
73
+ props?.selectedIndex === null || Number.isFinite(Number(props?.selectedIndex))
74
+ ? props.selectedIndex
75
+ : undefined,
76
+ cellSize: props?.cellSize,
77
+ disabled: props?.disabled === true,
78
+ onSelect: props?.onSelect,
79
+ },
80
+ };
81
+ },
82
+ };
83
+
84
+ globalThis.registerRuntimePackageApi('ui', { ui: __ui });
@@ -0,0 +1 @@
1
+ export * from './runtimeSurfaceTypeRegistry';
@@ -0,0 +1 @@
1
+ export * from './runtimeSurfaceTypeRegistry';
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+ export declare const UI_CARD_V1_RUNTIME_SURFACE_TYPE_ID: "ui.card.v1";
3
+ export type RuntimeSurfaceTypeId = typeof UI_CARD_V1_RUNTIME_SURFACE_TYPE_ID | string;
4
+ export type RuntimeSurfaceTree = unknown;
5
+ export interface RuntimeSurfaceTypeRendererProps<TTree> {
6
+ tree: TTree;
7
+ onEvent: (handler: string, args?: unknown) => void;
8
+ }
9
+ export interface RuntimeSurfaceTypeDefinition<TTree> {
10
+ packId: RuntimeSurfaceTypeId;
11
+ validateTree: (value: unknown) => TTree;
12
+ render: (props: RuntimeSurfaceTypeRendererProps<TTree>) => ReactNode;
13
+ }
14
+ export declare function registerRuntimeSurfaceType<TTree>(definition: RuntimeSurfaceTypeDefinition<TTree>): void;
15
+ export declare function clearRuntimeSurfaceTypes(): void;
16
+ export declare function normalizeRuntimeSurfaceTypeId(packId?: string | null): string;
17
+ export declare function getRuntimeSurfaceTypeOrThrow(packId?: string | null): RuntimeSurfaceTypeDefinition<unknown>;
18
+ export declare function listRuntimeSurfaceTypes(): string[];
19
+ export declare function validateRuntimeSurfaceTree<TTree = RuntimeSurfaceTree>(packId: string | undefined, value: unknown): TTree;
20
+ export declare function renderRuntimeSurfaceTree(packId: string | undefined, value: unknown, onEvent: (handler: string, args?: unknown) => void): ReactNode;
@@ -0,0 +1,37 @@
1
+ export const UI_CARD_V1_RUNTIME_SURFACE_TYPE_ID = 'ui.card.v1';
2
+ const runtimeSurfaceTypes = new Map();
3
+ export function registerRuntimeSurfaceType(definition) {
4
+ runtimeSurfaceTypes.set(definition.packId, definition);
5
+ }
6
+ export function clearRuntimeSurfaceTypes() {
7
+ runtimeSurfaceTypes.clear();
8
+ }
9
+ export function normalizeRuntimeSurfaceTypeId(packId) {
10
+ if (typeof packId !== 'string') {
11
+ throw new Error('Runtime surface type id is required');
12
+ }
13
+ const trimmed = packId.trim();
14
+ if (trimmed.length === 0) {
15
+ throw new Error('Runtime surface type id is required');
16
+ }
17
+ return trimmed;
18
+ }
19
+ export function getRuntimeSurfaceTypeOrThrow(packId) {
20
+ const normalized = normalizeRuntimeSurfaceTypeId(packId);
21
+ const surfaceType = runtimeSurfaceTypes.get(normalized);
22
+ if (!surfaceType) {
23
+ throw new Error(`Unknown runtime surface type: ${normalized}`);
24
+ }
25
+ return surfaceType;
26
+ }
27
+ export function listRuntimeSurfaceTypes() {
28
+ return Array.from(runtimeSurfaceTypes.keys()).sort();
29
+ }
30
+ export function validateRuntimeSurfaceTree(packId, value) {
31
+ return getRuntimeSurfaceTypeOrThrow(packId).validateTree(value);
32
+ }
33
+ export function renderRuntimeSurfaceTree(packId, value, onEvent) {
34
+ const surfaceType = getRuntimeSurfaceTypeOrThrow(packId);
35
+ const tree = surfaceType.validateTree(value);
36
+ return surfaceType.render({ tree, onEvent });
37
+ }
@@ -0,0 +1,2 @@
1
+ export * from './runtimeSessionManager';
2
+ export * from './runtimeOwnership';
@@ -0,0 +1,2 @@
1
+ export * from './runtimeSessionManager';
2
+ export * from './runtimeOwnership';
@@ -0,0 +1,10 @@
1
+ export type RuntimeSessionOwnershipKind = 'window-owned' | 'broker-owned' | 'attached-read-only' | 'attached-writable';
2
+ export interface RuntimeSessionOwnership {
3
+ kind: RuntimeSessionOwnershipKind;
4
+ writable: boolean;
5
+ }
6
+ export declare const WINDOW_OWNED_RUNTIME_SESSION: RuntimeSessionOwnership;
7
+ export declare const BROKER_OWNED_RUNTIME_SESSION: RuntimeSessionOwnership;
8
+ export declare const ATTACHED_READ_ONLY_RUNTIME_SESSION: RuntimeSessionOwnership;
9
+ export declare const ATTACHED_WRITABLE_RUNTIME_SESSION: RuntimeSessionOwnership;
10
+ export declare function shouldDisposeOnLastSurfaceWindowClose(ownership: RuntimeSessionOwnership): boolean;
@@ -0,0 +1,19 @@
1
+ export const WINDOW_OWNED_RUNTIME_SESSION = {
2
+ kind: 'window-owned',
3
+ writable: true,
4
+ };
5
+ export const BROKER_OWNED_RUNTIME_SESSION = {
6
+ kind: 'broker-owned',
7
+ writable: true,
8
+ };
9
+ export const ATTACHED_READ_ONLY_RUNTIME_SESSION = {
10
+ kind: 'attached-read-only',
11
+ writable: false,
12
+ };
13
+ export const ATTACHED_WRITABLE_RUNTIME_SESSION = {
14
+ kind: 'attached-writable',
15
+ writable: true,
16
+ };
17
+ export function shouldDisposeOnLastSurfaceWindowClose(ownership) {
18
+ return ownership.kind === 'window-owned';
19
+ }
@@ -0,0 +1,47 @@
1
+ import type { RuntimeAction, RuntimeBundleMeta, RuntimeSurfaceId, SessionId, StackId } from '../plugin-runtime/contracts';
2
+ import { QuickJSRuntimeService, type QuickJSRuntimeServiceOptions } from '../plugin-runtime/runtimeService';
3
+ import { type RuntimeSessionOwnership } from './runtimeOwnership';
4
+ export interface EnsureRuntimeSessionRequest {
5
+ bundleId: StackId;
6
+ sessionId: SessionId;
7
+ packageIds: string[];
8
+ bundleCode: string;
9
+ }
10
+ export interface RuntimeSessionManagerSummary {
11
+ sessionId: SessionId;
12
+ bundleId: StackId;
13
+ packageIds: string[];
14
+ surfaces: string[];
15
+ surfaceTypes?: Record<string, string>;
16
+ title: string;
17
+ description?: string;
18
+ status: 'loading' | 'ready' | 'error';
19
+ error?: string;
20
+ attachedViewIds: string[];
21
+ ownership: RuntimeSessionOwnership;
22
+ }
23
+ export interface RuntimeSessionManagerHandle {
24
+ readonly sessionId: SessionId;
25
+ readonly bundleId: StackId;
26
+ getBundleMeta(): RuntimeBundleMeta;
27
+ evaluateSessionJs(code: string): ReturnType<QuickJSRuntimeService['evaluateSessionJs']>;
28
+ getSessionGlobalNames(): string[];
29
+ renderSurface(surfaceId: RuntimeSurfaceId, state: unknown): unknown;
30
+ eventSurface(surfaceId: RuntimeSurfaceId, handler: string, args: unknown, state: unknown): RuntimeAction[];
31
+ defineSurface(surfaceId: RuntimeSurfaceId, code: string, packId: string): RuntimeBundleMeta;
32
+ defineSurfaceRender(surfaceId: RuntimeSurfaceId, code: string): RuntimeBundleMeta;
33
+ defineSurfaceHandler(surfaceId: RuntimeSurfaceId, handler: string, code: string): RuntimeBundleMeta;
34
+ attachView(viewId: string): () => void;
35
+ dispose(): boolean;
36
+ }
37
+ export interface RuntimeSessionManager {
38
+ ensureSession(request: EnsureRuntimeSessionRequest): Promise<RuntimeSessionManagerHandle>;
39
+ getSession(sessionId: SessionId): RuntimeSessionManagerHandle | null;
40
+ getSummary(sessionId: SessionId): RuntimeSessionManagerSummary | null;
41
+ listSessions(): RuntimeSessionManagerSummary[];
42
+ disposeSession(sessionId: SessionId): boolean;
43
+ clear(): void;
44
+ subscribe(listener: () => void): () => void;
45
+ }
46
+ export declare function createRuntimeSessionManager(options?: QuickJSRuntimeServiceOptions): RuntimeSessionManager;
47
+ export declare const DEFAULT_RUNTIME_SESSION_MANAGER: RuntimeSessionManager;
@@ -0,0 +1,214 @@
1
+ import { QuickJSRuntimeService } from '../plugin-runtime/runtimeService';
2
+ import { WINDOW_OWNED_RUNTIME_SESSION, } from './runtimeOwnership';
3
+ function arraysEqual(left, right) {
4
+ return left.length === right.length && left.every((value, index) => value === right[index]);
5
+ }
6
+ function sameRequest(left, right) {
7
+ return (left.bundleId === right.bundleId &&
8
+ left.sessionId === right.sessionId &&
9
+ left.bundleCode === right.bundleCode &&
10
+ arraysEqual(left.packageIds, right.packageIds));
11
+ }
12
+ function cloneBundleMeta(bundle) {
13
+ return {
14
+ ...bundle,
15
+ packageIds: [...bundle.packageIds],
16
+ surfaces: [...bundle.surfaces],
17
+ surfaceTypes: bundle.surfaceTypes ? { ...bundle.surfaceTypes } : undefined,
18
+ };
19
+ }
20
+ function toSummary(record) {
21
+ return {
22
+ sessionId: record.request.sessionId,
23
+ bundleId: record.request.bundleId,
24
+ packageIds: record.bundle?.packageIds ? [...record.bundle.packageIds] : [...record.request.packageIds],
25
+ surfaces: record.bundle?.surfaces ? [...record.bundle.surfaces] : [],
26
+ surfaceTypes: record.bundle?.surfaceTypes ? { ...record.bundle.surfaceTypes } : undefined,
27
+ title: record.bundle?.title ?? record.request.bundleId,
28
+ description: record.bundle?.description,
29
+ status: record.status,
30
+ error: record.error,
31
+ attachedViewIds: Array.from(record.attachedViewIds).sort(),
32
+ ownership: WINDOW_OWNED_RUNTIME_SESSION,
33
+ };
34
+ }
35
+ export function createRuntimeSessionManager(options = {}) {
36
+ const runtimeService = new QuickJSRuntimeService(options);
37
+ const records = new Map();
38
+ const listeners = new Set();
39
+ function emit() {
40
+ listeners.forEach((listener) => listener());
41
+ }
42
+ function getRecordOrThrow(sessionId) {
43
+ const record = records.get(sessionId);
44
+ if (!record) {
45
+ throw new Error(`Unknown runtime session: ${sessionId}`);
46
+ }
47
+ return record;
48
+ }
49
+ function updateBundle(sessionId, bundle) {
50
+ const record = getRecordOrThrow(sessionId);
51
+ record.bundle = bundle;
52
+ record.status = 'ready';
53
+ record.error = undefined;
54
+ emit();
55
+ return bundle;
56
+ }
57
+ function attachView(sessionId, viewId) {
58
+ const record = getRecordOrThrow(sessionId);
59
+ let released = false;
60
+ record.attachedViewIds.add(viewId);
61
+ emit();
62
+ return () => {
63
+ if (released) {
64
+ return;
65
+ }
66
+ released = true;
67
+ const current = records.get(sessionId);
68
+ if (!current) {
69
+ return;
70
+ }
71
+ if (current.attachedViewIds.delete(viewId)) {
72
+ emit();
73
+ }
74
+ };
75
+ }
76
+ function createHandle(sessionId) {
77
+ return {
78
+ sessionId,
79
+ get bundleId() {
80
+ return getRecordOrThrow(sessionId).request.bundleId;
81
+ },
82
+ getBundleMeta() {
83
+ const record = getRecordOrThrow(sessionId);
84
+ if (!record.bundle) {
85
+ throw new Error(`Runtime session is not ready: ${sessionId}`);
86
+ }
87
+ return cloneBundleMeta(record.bundle);
88
+ },
89
+ evaluateSessionJs(code) {
90
+ return runtimeService.evaluateSessionJs(sessionId, code);
91
+ },
92
+ getSessionGlobalNames() {
93
+ return runtimeService.getSessionGlobalNames(sessionId);
94
+ },
95
+ renderSurface(surfaceId, state) {
96
+ return runtimeService.renderRuntimeSurface(sessionId, surfaceId, state);
97
+ },
98
+ eventSurface(surfaceId, handler, args, state) {
99
+ return runtimeService.eventRuntimeSurface(sessionId, surfaceId, handler, args, state);
100
+ },
101
+ defineSurface(surfaceId, code, packId) {
102
+ return updateBundle(sessionId, runtimeService.defineRuntimeSurface(sessionId, surfaceId, code, packId));
103
+ },
104
+ defineSurfaceRender(surfaceId, code) {
105
+ return updateBundle(sessionId, runtimeService.defineRuntimeSurfaceRender(sessionId, surfaceId, code));
106
+ },
107
+ defineSurfaceHandler(surfaceId, handler, code) {
108
+ return updateBundle(sessionId, runtimeService.defineRuntimeSurfaceHandler(sessionId, surfaceId, handler, code));
109
+ },
110
+ attachView(viewId) {
111
+ return attachView(sessionId, viewId);
112
+ },
113
+ dispose() {
114
+ return manager.disposeSession(sessionId);
115
+ },
116
+ };
117
+ }
118
+ const manager = {
119
+ async ensureSession(request) {
120
+ const existing = records.get(request.sessionId);
121
+ if (existing) {
122
+ if (!sameRequest(existing.request, request)) {
123
+ throw new Error(`Runtime session already exists with different configuration: ${request.sessionId}`);
124
+ }
125
+ if (existing.pendingLoad) {
126
+ await existing.pendingLoad;
127
+ }
128
+ if (existing.status === 'error') {
129
+ throw new Error(existing.error ?? `Runtime session failed to load: ${request.sessionId}`);
130
+ }
131
+ return createHandle(request.sessionId);
132
+ }
133
+ const record = {
134
+ request: {
135
+ ...request,
136
+ packageIds: [...request.packageIds],
137
+ },
138
+ status: 'loading',
139
+ bundle: null,
140
+ attachedViewIds: new Set(),
141
+ pendingLoad: null,
142
+ };
143
+ records.set(request.sessionId, record);
144
+ emit();
145
+ record.pendingLoad = runtimeService
146
+ .loadRuntimeBundle(request.bundleId, request.sessionId, request.packageIds, request.bundleCode)
147
+ .then((bundle) => {
148
+ if (records.get(request.sessionId) !== record) {
149
+ runtimeService.disposeSession(request.sessionId);
150
+ return bundle;
151
+ }
152
+ record.bundle = bundle;
153
+ record.status = 'ready';
154
+ record.error = undefined;
155
+ record.pendingLoad = null;
156
+ emit();
157
+ return bundle;
158
+ })
159
+ .catch((error) => {
160
+ if (records.get(request.sessionId) !== record) {
161
+ return Promise.reject(error);
162
+ }
163
+ record.status = 'error';
164
+ record.error = error instanceof Error ? error.message : String(error);
165
+ record.pendingLoad = null;
166
+ emit();
167
+ throw error;
168
+ });
169
+ await record.pendingLoad;
170
+ return createHandle(request.sessionId);
171
+ },
172
+ getSession(sessionId) {
173
+ const record = records.get(sessionId);
174
+ if (!record || record.status !== 'ready') {
175
+ return null;
176
+ }
177
+ return createHandle(sessionId);
178
+ },
179
+ getSummary(sessionId) {
180
+ const record = records.get(sessionId);
181
+ return record ? toSummary(record) : null;
182
+ },
183
+ listSessions() {
184
+ return Array.from(records.values())
185
+ .map((record) => toSummary(record))
186
+ .sort((left, right) => left.sessionId.localeCompare(right.sessionId));
187
+ },
188
+ disposeSession(sessionId) {
189
+ const record = records.get(sessionId);
190
+ if (!record) {
191
+ return false;
192
+ }
193
+ records.delete(sessionId);
194
+ const disposed = runtimeService.disposeSession(sessionId);
195
+ emit();
196
+ return disposed;
197
+ },
198
+ clear() {
199
+ for (const sessionId of Array.from(records.keys())) {
200
+ runtimeService.disposeSession(sessionId);
201
+ }
202
+ records.clear();
203
+ emit();
204
+ },
205
+ subscribe(listener) {
206
+ listeners.add(listener);
207
+ return () => {
208
+ listeners.delete(listener);
209
+ };
210
+ },
211
+ };
212
+ return manager;
213
+ }
214
+ export const DEFAULT_RUNTIME_SESSION_MANAGER = createRuntimeSessionManager();
@@ -0,0 +1,4 @@
1
+ import type { RuntimePackageDefinition } from './runtime-packages';
2
+ import type { RuntimeSurfaceTypeDefinition } from './runtime-packs';
3
+ export declare const TEST_UI_RUNTIME_PACKAGE: RuntimePackageDefinition;
4
+ export declare const TEST_UI_CARD_V1_RUNTIME_SURFACE_TYPE: RuntimeSurfaceTypeDefinition<unknown>;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ export const TEST_UI_RUNTIME_PACKAGE = {
3
+ packageId: 'ui',
4
+ version: 'test',
5
+ installPrelude: `
6
+ const __ui = {
7
+ text(content) { return { kind: 'text', text: String(content) }; },
8
+ button(label, props = {}) { return { kind: 'button', props: { label: String(label), ...props } }; },
9
+ column(children = []) { return { kind: 'column', children: Array.isArray(children) ? children : [] }; },
10
+ panel(children = []) { return { kind: 'panel', children: Array.isArray(children) ? children : [] }; },
11
+ };
12
+ globalThis.registerRuntimePackageApi('ui', { ui: __ui });
13
+ `.trim(),
14
+ surfaceTypes: ['ui.card.v1'],
15
+ };
16
+ function isRecord(value) {
17
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
18
+ }
19
+ function renderTestNode(node, keyHint) {
20
+ if (!isRecord(node) || typeof node.kind !== 'string') {
21
+ return null;
22
+ }
23
+ if (node.kind === 'panel' || node.kind === 'column') {
24
+ const children = Array.isArray(node.children) ? node.children : [];
25
+ return (_jsx("div", { children: children.map((child, index) => renderTestNode(child, `${keyHint}:${index}`)) }, keyHint));
26
+ }
27
+ if (node.kind === 'text' && typeof node.text === 'string') {
28
+ return _jsx("span", { children: node.text }, keyHint);
29
+ }
30
+ if (node.kind === 'button' && isRecord(node.props) && typeof node.props.label === 'string') {
31
+ return _jsx("button", { children: node.props.label }, keyHint);
32
+ }
33
+ return null;
34
+ }
35
+ export const TEST_UI_CARD_V1_RUNTIME_SURFACE_TYPE = {
36
+ packId: 'ui.card.v1',
37
+ validateTree: (value) => value,
38
+ render: ({ tree }) => _jsx(_Fragment, { children: renderTestNode(tree, 'test-ui-root') }),
39
+ };