@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.
- package/README.md +147 -0
- package/app/createAppStore.d.ts +89 -0
- package/app/createAppStore.js +90 -0
- package/app/index.d.ts +2 -0
- package/app/index.js +2 -0
- package/app/runtimeSessionLifecycleMiddleware.d.ts +6 -0
- package/app/runtimeSessionLifecycleMiddleware.js +42 -0
- package/features/runtimeSessions/capabilityPolicy.d.ts +12 -0
- package/features/runtimeSessions/capabilityPolicy.js +36 -0
- package/features/runtimeSessions/index.d.ts +3 -0
- package/features/runtimeSessions/index.js +3 -0
- package/features/runtimeSessions/runtimeSessionsSlice.d.ts +90 -0
- package/features/runtimeSessions/runtimeSessionsSlice.js +210 -0
- package/features/runtimeSessions/selectors.d.ts +16 -0
- package/features/runtimeSessions/selectors.js +40 -0
- package/hypercard/artifacts/artifactProjectionMiddleware.d.ts +1 -0
- package/hypercard/artifacts/artifactProjectionMiddleware.js +66 -0
- package/hypercard/artifacts/artifactRuntime.d.ts +22 -0
- package/hypercard/artifacts/artifactRuntime.js +137 -0
- package/hypercard/artifacts/artifactsSelectors.d.ts +10 -0
- package/hypercard/artifacts/artifactsSelectors.js +2 -0
- package/hypercard/artifacts/artifactsSlice.d.ts +38 -0
- package/hypercard/artifacts/artifactsSlice.js +94 -0
- package/hypercard/debug/RuntimeSurfaceDebugWindow.d.ts +7 -0
- package/hypercard/debug/RuntimeSurfaceDebugWindow.js +181 -0
- package/hypercard/debug/jsSessionDebugRegistry.d.ts +11 -0
- package/hypercard/debug/jsSessionDebugRegistry.js +43 -0
- package/hypercard/debug/runtimeDebugApp.d.ts +19 -0
- package/hypercard/debug/runtimeDebugApp.js +25 -0
- package/hypercard/debug/runtimeDebugRegistry.d.ts +6 -0
- package/hypercard/debug/runtimeDebugRegistry.js +49 -0
- package/hypercard/editor/CodeEditorWindow.d.ts +7 -0
- package/hypercard/editor/CodeEditorWindow.js +128 -0
- package/hypercard/editor/editorLaunch.d.ts +16 -0
- package/hypercard/editor/editorLaunch.js +58 -0
- package/hypercard/editor/runtimeSurfaceRef.d.ts +8 -0
- package/hypercard/editor/runtimeSurfaceRef.js +60 -0
- package/hypercard/index.d.ts +12 -0
- package/hypercard/index.js +12 -0
- package/hypercard/task-manager/TaskManagerWindow.d.ts +1 -0
- package/hypercard/task-manager/TaskManagerWindow.js +102 -0
- package/hypercard/task-manager/index.d.ts +6 -0
- package/hypercard/task-manager/index.js +6 -0
- package/hypercard/task-manager/jsSessionSource.d.ts +10 -0
- package/hypercard/task-manager/jsSessionSource.js +49 -0
- package/hypercard/task-manager/runtimeSessionSource.d.ts +26 -0
- package/hypercard/task-manager/runtimeSessionSource.js +123 -0
- package/hypercard/task-manager/taskManagerApp.d.ts +16 -0
- package/hypercard/task-manager/taskManagerApp.js +25 -0
- package/hypercard/task-manager/taskManagerRegistry.d.ts +10 -0
- package/hypercard/task-manager/taskManagerRegistry.js +75 -0
- package/hypercard/task-manager/types.d.ts +25 -0
- package/hypercard/task-manager/types.js +1 -0
- package/hypercard/timeline/hypercardCard.d.ts +5 -0
- package/hypercard/timeline/hypercardCard.js +104 -0
- package/index.d.ts +18 -0
- package/index.js +18 -0
- package/package.json +72 -0
- package/plugin-runtime/contracts.d.ts +116 -0
- package/plugin-runtime/contracts.js +32 -0
- package/plugin-runtime/fixtures/column-stack.vm.js +19 -0
- package/plugin-runtime/fixtures/dynamic-card.vm.js +13 -0
- package/plugin-runtime/fixtures/inventory-stack.vm.js +29 -0
- package/plugin-runtime/fixtures/kanban-card.vm.js +55 -0
- package/plugin-runtime/fixtures/loop-stack.vm.js +16 -0
- package/plugin-runtime/fixtures/patched-low-stock-handler.vm.js +3 -0
- package/plugin-runtime/fixtures/patched-low-stock-render.vm.js +5 -0
- package/plugin-runtime/index.d.ts +6 -0
- package/plugin-runtime/index.js +6 -0
- package/plugin-runtime/intentSchema.d.ts +3 -0
- package/plugin-runtime/intentSchema.js +25 -0
- package/plugin-runtime/jsEvalSupport.d.ts +6 -0
- package/plugin-runtime/jsEvalSupport.js +93 -0
- package/plugin-runtime/jsSessionService.d.ts +55 -0
- package/plugin-runtime/jsSessionService.js +136 -0
- package/plugin-runtime/quickJsSessionCore.d.ts +24 -0
- package/plugin-runtime/quickJsSessionCore.js +92 -0
- package/plugin-runtime/runtimeService.d.ts +34 -0
- package/plugin-runtime/runtimeService.js +248 -0
- package/plugin-runtime/runtimeSurfaceRegistry.d.ts +45 -0
- package/plugin-runtime/runtimeSurfaceRegistry.js +73 -0
- package/plugin-runtime/stack-bootstrap.vm.js +236 -0
- package/repl/attachedJsSessionRegistry.d.ts +25 -0
- package/repl/attachedJsSessionRegistry.js +81 -0
- package/repl/attachedRuntimeSessionRegistry.d.ts +11 -0
- package/repl/attachedRuntimeSessionRegistry.js +107 -0
- package/repl/hypercardReplDriver.d.ts +54 -0
- package/repl/hypercardReplDriver.js +600 -0
- package/repl/jsReplDriver.d.ts +8 -0
- package/repl/jsReplDriver.js +348 -0
- package/repl/jsSessionBroker.d.ts +21 -0
- package/repl/jsSessionBroker.js +75 -0
- package/repl/runtimeBroker.d.ts +43 -0
- package/repl/runtimeBroker.js +117 -0
- package/runtime-host/RuntimeSurfaceSessionHost.d.ts +8 -0
- package/runtime-host/RuntimeSurfaceSessionHost.js +384 -0
- package/runtime-host/fixtures/CardSessionHost.chat.vm.js +61 -0
- package/runtime-host/fixtures/CardSessionHost.list.vm.js +29 -0
- package/runtime-host/fixtures/CardSessionHost.nav.vm.js +101 -0
- package/runtime-host/fixtures/CardSessionHost.report.vm.js +34 -0
- package/runtime-host/pluginIntentRouting.d.ts +13 -0
- package/runtime-host/pluginIntentRouting.js +83 -0
- package/runtime-packages/index.d.ts +1 -0
- package/runtime-packages/index.js +1 -0
- package/runtime-packages/runtimePackageRegistry.d.ts +14 -0
- package/runtime-packages/runtimePackageRegistry.js +42 -0
- package/runtime-packages/ui.package.vm.js +84 -0
- package/runtime-packs/index.d.ts +1 -0
- package/runtime-packs/index.js +1 -0
- package/runtime-packs/runtimeSurfaceTypeRegistry.d.ts +20 -0
- package/runtime-packs/runtimeSurfaceTypeRegistry.js +37 -0
- package/runtime-session-manager/index.d.ts +2 -0
- package/runtime-session-manager/index.js +2 -0
- package/runtime-session-manager/runtimeOwnership.d.ts +10 -0
- package/runtime-session-manager/runtimeOwnership.js +19 -0
- package/runtime-session-manager/runtimeSessionManager.d.ts +47 -0
- package/runtime-session-manager/runtimeSessionManager.js +214 -0
- package/testRuntimeUi.d.ts +4 -0
- package/testRuntimeUi.js +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# @go-go-golems/os-scripting
|
|
2
|
+
|
|
3
|
+
QuickJS-backed scripting runtime for go-go-os React hosts.
|
|
4
|
+
|
|
5
|
+
Use this package when a standalone app needs to load JavaScript runtime bundles as data, execute them inside a sandbox, render runtime surfaces, and route surface events back into host state.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @go-go-golems/os-scripting @go-go-golems/os-ui-cards @go-go-golems/os-shell @go-go-golems/os-core
|
|
11
|
+
npm install react react-dom react-redux @reduxjs/toolkit
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`react`, `react-dom`, `react-redux`, and `@reduxjs/toolkit` are peer dependencies.
|
|
15
|
+
|
|
16
|
+
## Main exports
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import {
|
|
20
|
+
createAppStore,
|
|
21
|
+
RuntimeSurfaceSessionHost,
|
|
22
|
+
registerRuntimePackage,
|
|
23
|
+
registerRuntimeSurfaceType,
|
|
24
|
+
} from '@go-go-golems/os-scripting';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Consumer-facing APIs:
|
|
28
|
+
|
|
29
|
+
- `createAppStore(domainReducers, options)` — creates a Redux store with runtime session reducers and middleware.
|
|
30
|
+
- `RuntimeSurfaceSessionHost` — React bridge that loads a runtime bundle and renders the current surface.
|
|
31
|
+
- `registerRuntimePackage(definition)` — registers a VM-side package prelude such as `ui` or `kanban`.
|
|
32
|
+
- `registerRuntimeSurfaceType(definition)` — registers a host-side validator/renderer such as `ui.card.v1`.
|
|
33
|
+
|
|
34
|
+
Advanced APIs include the QuickJS runtime service, runtime session manager, attached REPL drivers, debug windows, and dynamic runtime surface registry.
|
|
35
|
+
|
|
36
|
+
## Minimal runtime registration
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { registerRuntimePackage, registerRuntimeSurfaceType } from '@go-go-golems/os-scripting';
|
|
40
|
+
import { UI_RUNTIME_PACKAGE, UI_CARD_V1_RUNTIME_SURFACE_TYPE } from '@go-go-golems/os-ui-cards';
|
|
41
|
+
|
|
42
|
+
let registered = false;
|
|
43
|
+
|
|
44
|
+
export function registerRuntimePackages() {
|
|
45
|
+
if (registered) return;
|
|
46
|
+
registered = true;
|
|
47
|
+
|
|
48
|
+
registerRuntimePackage(UI_RUNTIME_PACKAGE);
|
|
49
|
+
registerRuntimeSurfaceType(UI_CARD_V1_RUNTIME_SURFACE_TYPE);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Call this once at app startup before rendering runtime surfaces.
|
|
54
|
+
|
|
55
|
+
## Minimal store
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { Provider } from 'react-redux';
|
|
59
|
+
import { createAppStore } from '@go-go-golems/os-scripting';
|
|
60
|
+
|
|
61
|
+
const { store } = createAppStore({});
|
|
62
|
+
|
|
63
|
+
export function AppProviders({ children }: { children: React.ReactNode }) {
|
|
64
|
+
return <Provider store={store}>{children}</Provider>;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `createAppStore` when rendering VM surfaces. It includes `runtimeSessions`, runtime lifecycle middleware, and artifact projection middleware. For shell-only apps that never render VM surfaces, `createLauncherStore` from `@go-go-golems/os-shell` is lighter.
|
|
69
|
+
|
|
70
|
+
## Minimal surface host
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { RuntimeSurfaceSessionHost } from '@go-go-golems/os-scripting';
|
|
74
|
+
import type { RuntimeBundleDefinition } from '@go-go-golems/os-shell';
|
|
75
|
+
|
|
76
|
+
export function RuntimeWindow({ bundle }: { bundle: RuntimeBundleDefinition }) {
|
|
77
|
+
return (
|
|
78
|
+
<RuntimeSurfaceSessionHost
|
|
79
|
+
windowId="window:demo"
|
|
80
|
+
sessionId="session:demo"
|
|
81
|
+
bundle={bundle}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Runtime bundle shape
|
|
88
|
+
|
|
89
|
+
Host TypeScript:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import type { RuntimeBundleDefinition } from '@go-go-golems/os-shell';
|
|
93
|
+
import bundleCode from './bundle.vm.js?raw';
|
|
94
|
+
|
|
95
|
+
export const BUNDLE: RuntimeBundleDefinition = {
|
|
96
|
+
id: 'hello-vm',
|
|
97
|
+
name: 'Hello VM',
|
|
98
|
+
icon: '🧪',
|
|
99
|
+
homeSurface: 'home',
|
|
100
|
+
plugin: {
|
|
101
|
+
packageIds: ['ui'],
|
|
102
|
+
bundleCode,
|
|
103
|
+
capabilities: { domain: [], system: [] },
|
|
104
|
+
},
|
|
105
|
+
surfaces: {
|
|
106
|
+
home: {
|
|
107
|
+
id: 'home',
|
|
108
|
+
type: 'ui.card.v1',
|
|
109
|
+
title: 'Hello VM',
|
|
110
|
+
icon: '🧪',
|
|
111
|
+
ui: {},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
VM JavaScript:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
defineRuntimeBundle(({ ui }) => ({
|
|
121
|
+
id: 'hello-vm',
|
|
122
|
+
title: 'Hello VM',
|
|
123
|
+
packageIds: ['ui'],
|
|
124
|
+
surfaces: {
|
|
125
|
+
home: {
|
|
126
|
+
packId: 'ui.card.v1',
|
|
127
|
+
render({ state }) {
|
|
128
|
+
return ui.panel([
|
|
129
|
+
ui.text('Hello from QuickJS'),
|
|
130
|
+
ui.text('Session: ' + state.self.sessionId),
|
|
131
|
+
]);
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
}));
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Security model
|
|
139
|
+
|
|
140
|
+
Runtime bundle code runs inside QuickJS, not in the browser global environment. It does not receive direct DOM, network, local storage, filesystem, or Node.js access. It communicates with the host by returning structured UI trees and dispatching validated runtime actions from event handlers.
|
|
141
|
+
|
|
142
|
+
## Related packages
|
|
143
|
+
|
|
144
|
+
- `@go-go-golems/os-ui-cards` — base `ui` VM package and `ui.card.v1` renderer.
|
|
145
|
+
- `@go-go-golems/os-kanban` — higher-level Kanban VM package and renderer.
|
|
146
|
+
- `@go-go-golems/os-shell` — desktop shell, app launcher, and window content adapter types.
|
|
147
|
+
- `@go-go-golems/os-repl` — terminal UI used by scripting REPL integrations.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type Reducer } from '@reduxjs/toolkit';
|
|
2
|
+
export declare const CORE_APP_REDUCER_KEYS: readonly ["pluginCardRuntime", "runtimeSessions", "windowing", "notifications", "debug", "hypercardArtifacts"];
|
|
3
|
+
/** Options for `createAppStore`. */
|
|
4
|
+
export interface CreateAppStoreOptions {
|
|
5
|
+
/** Enable Redux throughput/FPS diagnostics middleware. Default: false. */
|
|
6
|
+
enableReduxDiagnostics?: boolean;
|
|
7
|
+
/** Rolling window duration in ms for diagnostics aggregation. Default: 5000. */
|
|
8
|
+
diagnosticsWindowMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a Redux store factory pre-wired with all HyperCard engine reducers
|
|
12
|
+
* (runtimeSessions, windowing, notifications, debug).
|
|
13
|
+
*
|
|
14
|
+
* Optionally enables Redux throughput/FPS diagnostics when
|
|
15
|
+
* `options.enableReduxDiagnostics` is true (intended for dev-mode only).
|
|
16
|
+
* Diagnostics data is stored outside of Redux in a module-level store
|
|
17
|
+
* to avoid polluting the dispatch/render cycle it measures.
|
|
18
|
+
*
|
|
19
|
+
* Returns both a singleton store and a createStore() factory for Storybook.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const { store, createStore } = createAppStore({
|
|
24
|
+
* contacts: contactsReducer,
|
|
25
|
+
* companies: companiesReducer,
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* // With diagnostics enabled (dev mode):
|
|
32
|
+
* const { store, createStore } = createAppStore(
|
|
33
|
+
* { inventory: inventoryReducer },
|
|
34
|
+
* { enableReduxDiagnostics: import.meta.env.DEV },
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function createAppStore<T extends Record<string, Reducer>>(domainReducers: T, options?: CreateAppStoreOptions): {
|
|
39
|
+
store: import("@reduxjs/toolkit").EnhancedStore<{
|
|
40
|
+
[x: string]: /*elided*/ any;
|
|
41
|
+
runtimeSessions: import("..").RuntimeSessionsState;
|
|
42
|
+
windowing: import("@go-go-golems/os-core/desktop-core").WindowingState;
|
|
43
|
+
notifications: import("@go-go-golems/os-core/features/notifications/notificationsSlice").NotificationsState;
|
|
44
|
+
debug: import("@go-go-golems/os-core").DebugState;
|
|
45
|
+
hypercardArtifacts: import("..").ArtifactsState;
|
|
46
|
+
}, import("@reduxjs/toolkit").UnknownAction, import("@reduxjs/toolkit").Tuple<[import("@reduxjs/toolkit").StoreEnhancer<{
|
|
47
|
+
dispatch: import("@reduxjs/toolkit").ThunkDispatch<{
|
|
48
|
+
[x: string]: /*elided*/ any;
|
|
49
|
+
runtimeSessions: import("..").RuntimeSessionsState;
|
|
50
|
+
windowing: import("@go-go-golems/os-core/desktop-core").WindowingState;
|
|
51
|
+
notifications: import("@go-go-golems/os-core/features/notifications/notificationsSlice").NotificationsState;
|
|
52
|
+
debug: import("@go-go-golems/os-core").DebugState;
|
|
53
|
+
hypercardArtifacts: import("..").ArtifactsState;
|
|
54
|
+
}, undefined, import("@reduxjs/toolkit").UnknownAction> & ((action: import("@reduxjs/toolkit").Action<"listenerMiddleware/add">) => import("@reduxjs/toolkit").UnsubscribeListener);
|
|
55
|
+
}>, import("@reduxjs/toolkit").StoreEnhancer]>>;
|
|
56
|
+
createStore: () => import("@reduxjs/toolkit").EnhancedStore<{
|
|
57
|
+
[x: string]: /*elided*/ any;
|
|
58
|
+
runtimeSessions: import("..").RuntimeSessionsState;
|
|
59
|
+
windowing: import("@go-go-golems/os-core/desktop-core").WindowingState;
|
|
60
|
+
notifications: import("@go-go-golems/os-core/features/notifications/notificationsSlice").NotificationsState;
|
|
61
|
+
debug: import("@go-go-golems/os-core").DebugState;
|
|
62
|
+
hypercardArtifacts: import("..").ArtifactsState;
|
|
63
|
+
}, import("@reduxjs/toolkit").UnknownAction, import("@reduxjs/toolkit").Tuple<[import("@reduxjs/toolkit").StoreEnhancer<{
|
|
64
|
+
dispatch: import("@reduxjs/toolkit").ThunkDispatch<{
|
|
65
|
+
[x: string]: /*elided*/ any;
|
|
66
|
+
runtimeSessions: import("..").RuntimeSessionsState;
|
|
67
|
+
windowing: import("@go-go-golems/os-core/desktop-core").WindowingState;
|
|
68
|
+
notifications: import("@go-go-golems/os-core/features/notifications/notificationsSlice").NotificationsState;
|
|
69
|
+
debug: import("@go-go-golems/os-core").DebugState;
|
|
70
|
+
hypercardArtifacts: import("..").ArtifactsState;
|
|
71
|
+
}, undefined, import("@reduxjs/toolkit").UnknownAction> & ((action: import("@reduxjs/toolkit").Action<"listenerMiddleware/add">) => import("@reduxjs/toolkit").UnsubscribeListener);
|
|
72
|
+
}>, import("@reduxjs/toolkit").StoreEnhancer]>>;
|
|
73
|
+
RootState: {
|
|
74
|
+
[x: string]: /*elided*/ any;
|
|
75
|
+
runtimeSessions: import("..").RuntimeSessionsState;
|
|
76
|
+
windowing: import("@go-go-golems/os-core/desktop-core").WindowingState;
|
|
77
|
+
notifications: import("@go-go-golems/os-core/features/notifications/notificationsSlice").NotificationsState;
|
|
78
|
+
debug: import("@go-go-golems/os-core").DebugState;
|
|
79
|
+
hypercardArtifacts: import("..").ArtifactsState;
|
|
80
|
+
};
|
|
81
|
+
AppDispatch: import("@reduxjs/toolkit").ThunkDispatch<{
|
|
82
|
+
[x: string]: /*elided*/ any;
|
|
83
|
+
runtimeSessions: import("..").RuntimeSessionsState;
|
|
84
|
+
windowing: import("@go-go-golems/os-core/desktop-core").WindowingState;
|
|
85
|
+
notifications: import("@go-go-golems/os-core/features/notifications/notificationsSlice").NotificationsState;
|
|
86
|
+
debug: import("@go-go-golems/os-core").DebugState;
|
|
87
|
+
hypercardArtifacts: import("..").ArtifactsState;
|
|
88
|
+
}, undefined, import("@reduxjs/toolkit").UnknownAction> & ((action: import("@reduxjs/toolkit").Action<"listenerMiddleware/add">) => import("@reduxjs/toolkit").UnsubscribeListener) & import("@reduxjs/toolkit").Dispatch<import("@reduxjs/toolkit").UnknownAction>;
|
|
89
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
2
|
+
import { createReduxPerfMiddleware, debugReducer, initDiagnostics, notificationsReducer, startFrameMonitor, } from '@go-go-golems/os-core';
|
|
3
|
+
import { windowingReducer } from '@go-go-golems/os-core/desktop-core';
|
|
4
|
+
import { runtimeSessionsReducer } from '../features/runtimeSessions/runtimeSessionsSlice';
|
|
5
|
+
import { createArtifactProjectionMiddleware } from '../hypercard/artifacts/artifactProjectionMiddleware';
|
|
6
|
+
import { hypercardArtifactsReducer } from '../hypercard/artifacts/artifactsSlice';
|
|
7
|
+
import { createRuntimeSessionLifecycleMiddleware } from './runtimeSessionLifecycleMiddleware';
|
|
8
|
+
export const CORE_APP_REDUCER_KEYS = [
|
|
9
|
+
'pluginCardRuntime',
|
|
10
|
+
'runtimeSessions',
|
|
11
|
+
'windowing',
|
|
12
|
+
'notifications',
|
|
13
|
+
'debug',
|
|
14
|
+
'hypercardArtifacts',
|
|
15
|
+
];
|
|
16
|
+
const CORE_APP_REDUCER_KEY_SET = new Set(CORE_APP_REDUCER_KEYS);
|
|
17
|
+
function assertNoReservedDomainReducerKeys(domainReducers) {
|
|
18
|
+
const reservedKeys = Object.keys(domainReducers).filter((key) => CORE_APP_REDUCER_KEY_SET.has(key));
|
|
19
|
+
if (reservedKeys.length === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`createAppStore domain reducer keys are reserved by engine core reducers: ${reservedKeys.join(', ')}`);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a Redux store factory pre-wired with all HyperCard engine reducers
|
|
26
|
+
* (runtimeSessions, windowing, notifications, debug).
|
|
27
|
+
*
|
|
28
|
+
* Optionally enables Redux throughput/FPS diagnostics when
|
|
29
|
+
* `options.enableReduxDiagnostics` is true (intended for dev-mode only).
|
|
30
|
+
* Diagnostics data is stored outside of Redux in a module-level store
|
|
31
|
+
* to avoid polluting the dispatch/render cycle it measures.
|
|
32
|
+
*
|
|
33
|
+
* Returns both a singleton store and a createStore() factory for Storybook.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const { store, createStore } = createAppStore({
|
|
38
|
+
* contacts: contactsReducer,
|
|
39
|
+
* companies: companiesReducer,
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* // With diagnostics enabled (dev mode):
|
|
46
|
+
* const { store, createStore } = createAppStore(
|
|
47
|
+
* { inventory: inventoryReducer },
|
|
48
|
+
* { enableReduxDiagnostics: import.meta.env.DEV },
|
|
49
|
+
* );
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function createAppStore(domainReducers, options = {}) {
|
|
53
|
+
const enableDiag = options.enableReduxDiagnostics === true;
|
|
54
|
+
assertNoReservedDomainReducerKeys(domainReducers);
|
|
55
|
+
const reducer = {
|
|
56
|
+
runtimeSessions: runtimeSessionsReducer,
|
|
57
|
+
windowing: windowingReducer,
|
|
58
|
+
notifications: notificationsReducer,
|
|
59
|
+
debug: debugReducer,
|
|
60
|
+
hypercardArtifacts: hypercardArtifactsReducer,
|
|
61
|
+
...domainReducers,
|
|
62
|
+
};
|
|
63
|
+
// Initialise module-level diagnostics storage when enabled
|
|
64
|
+
if (enableDiag) {
|
|
65
|
+
initDiagnostics({ windowMs: options.diagnosticsWindowMs ?? 5000 });
|
|
66
|
+
}
|
|
67
|
+
const perfMiddleware = enableDiag
|
|
68
|
+
? createReduxPerfMiddleware({ windowMs: options.diagnosticsWindowMs ?? 5000 })
|
|
69
|
+
: null;
|
|
70
|
+
function createStore() {
|
|
71
|
+
const artifactProjectionMiddleware = createArtifactProjectionMiddleware();
|
|
72
|
+
const runtimeSessionLifecycleMiddleware = createRuntimeSessionLifecycleMiddleware();
|
|
73
|
+
const store = configureStore({
|
|
74
|
+
reducer,
|
|
75
|
+
middleware: (getDefault) => perfMiddleware
|
|
76
|
+
? getDefault().concat(artifactProjectionMiddleware.middleware, runtimeSessionLifecycleMiddleware.middleware, perfMiddleware)
|
|
77
|
+
: getDefault().concat(artifactProjectionMiddleware.middleware, runtimeSessionLifecycleMiddleware.middleware),
|
|
78
|
+
});
|
|
79
|
+
// Start frame monitor when diagnostics are enabled
|
|
80
|
+
if (enableDiag && typeof requestAnimationFrame !== 'undefined') {
|
|
81
|
+
startFrameMonitor();
|
|
82
|
+
}
|
|
83
|
+
return store;
|
|
84
|
+
}
|
|
85
|
+
const store = createStore();
|
|
86
|
+
return {
|
|
87
|
+
store,
|
|
88
|
+
createStore,
|
|
89
|
+
};
|
|
90
|
+
}
|
package/app/index.d.ts
ADDED
package/app/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type RuntimeSessionManager } from '../runtime-session-manager';
|
|
2
|
+
interface CreateRuntimeSessionLifecycleMiddlewareOptions {
|
|
3
|
+
manager?: RuntimeSessionManager;
|
|
4
|
+
}
|
|
5
|
+
export declare function createRuntimeSessionLifecycleMiddleware(options?: CreateRuntimeSessionLifecycleMiddlewareOptions): import("@reduxjs/toolkit").ListenerMiddlewareInstance<unknown, import("@reduxjs/toolkit").ThunkDispatch<unknown, unknown, import("@reduxjs/toolkit").UnknownAction>, unknown>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createListenerMiddleware } from '@reduxjs/toolkit';
|
|
2
|
+
import { closeWindow } from '@go-go-golems/os-core/desktop-core';
|
|
3
|
+
import { removeRuntimeSession } from '../features/runtimeSessions';
|
|
4
|
+
import { DEFAULT_RUNTIME_SESSION_MANAGER, } from '../runtime-session-manager';
|
|
5
|
+
import { shouldDisposeOnLastSurfaceWindowClose } from '../runtime-session-manager';
|
|
6
|
+
function getClosedSurfaceSessionId(state, windowId) {
|
|
7
|
+
const window = state.windowing?.windows?.[windowId];
|
|
8
|
+
if (!window || window.content.kind !== 'surface') {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return window.content.surface?.surfaceSessionId ?? null;
|
|
12
|
+
}
|
|
13
|
+
function stillHasSurfaceWindow(state, sessionId) {
|
|
14
|
+
const windows = Object.values(state.windowing?.windows ?? {});
|
|
15
|
+
return windows.some((window) => window.content.kind === 'surface' &&
|
|
16
|
+
window.content.surface?.surfaceSessionId === sessionId);
|
|
17
|
+
}
|
|
18
|
+
export function createRuntimeSessionLifecycleMiddleware(options = {}) {
|
|
19
|
+
const listener = createListenerMiddleware();
|
|
20
|
+
const manager = options.manager ?? DEFAULT_RUNTIME_SESSION_MANAGER;
|
|
21
|
+
listener.startListening({
|
|
22
|
+
actionCreator: closeWindow,
|
|
23
|
+
effect: async (action, api) => {
|
|
24
|
+
const windowId = action.payload;
|
|
25
|
+
const sessionId = getClosedSurfaceSessionId(api.getOriginalState(), windowId);
|
|
26
|
+
if (!sessionId) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const nextState = api.getState();
|
|
30
|
+
if (stillHasSurfaceWindow(nextState, sessionId)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const summary = manager.getSummary(sessionId);
|
|
34
|
+
if (!summary || !shouldDisposeOnLastSurfaceWindowClose(summary.ownership)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
manager.disposeSession(sessionId);
|
|
38
|
+
api.dispatch(removeRuntimeSession({ sessionId }));
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return listener;
|
|
42
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type CapabilitySet = 'all' | string[];
|
|
2
|
+
export interface CapabilityPolicy {
|
|
3
|
+
domain: CapabilitySet;
|
|
4
|
+
system: CapabilitySet;
|
|
5
|
+
}
|
|
6
|
+
export interface CapabilityDecision {
|
|
7
|
+
allowed: boolean;
|
|
8
|
+
reason: string | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveCapabilityPolicy(input?: Partial<CapabilityPolicy>): CapabilityPolicy;
|
|
11
|
+
export declare function authorizeDomainIntent(policy: CapabilityPolicy, domain: string): CapabilityDecision;
|
|
12
|
+
export declare function authorizeSystemIntent(policy: CapabilityPolicy, command: string): CapabilityDecision;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const DEFAULT_POLICY = {
|
|
2
|
+
domain: 'all',
|
|
3
|
+
system: 'all',
|
|
4
|
+
};
|
|
5
|
+
function normalizeCapabilitySet(value, fallback) {
|
|
6
|
+
if (value === 'all') {
|
|
7
|
+
return 'all';
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
return value.filter((entry) => typeof entry === 'string' && entry.length > 0);
|
|
11
|
+
}
|
|
12
|
+
return fallback;
|
|
13
|
+
}
|
|
14
|
+
export function resolveCapabilityPolicy(input) {
|
|
15
|
+
return {
|
|
16
|
+
domain: normalizeCapabilitySet(input?.domain, DEFAULT_POLICY.domain),
|
|
17
|
+
system: normalizeCapabilitySet(input?.system, DEFAULT_POLICY.system),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function includesCapability(set, value) {
|
|
21
|
+
return set === 'all' || set.includes(value);
|
|
22
|
+
}
|
|
23
|
+
export function authorizeDomainIntent(policy, domain) {
|
|
24
|
+
const allowed = includesCapability(policy.domain, domain);
|
|
25
|
+
return {
|
|
26
|
+
allowed,
|
|
27
|
+
reason: allowed ? null : `domain_not_allowed:${domain}`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function authorizeSystemIntent(policy, command) {
|
|
31
|
+
const allowed = includesCapability(policy.system, command);
|
|
32
|
+
return {
|
|
33
|
+
allowed,
|
|
34
|
+
reason: allowed ? null : `system_command_not_allowed:${command}`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { authorizeDomainIntent, authorizeSystemIntent, resolveCapabilityPolicy, type CapabilityDecision, type CapabilityPolicy, type CapabilitySet, } from './capabilityPolicy';
|
|
2
|
+
export { clearRuntimeTimeline, dequeuePendingDomainIntent, dequeuePendingNavIntent, dequeuePendingSystemIntent, ingestRuntimeAction, runtimeSessionsReducer, registerRuntimeSession, removeRuntimeSession, setRuntimeSessionStatus, type DispatchOutcome, type DomainIntentEnvelope, type RuntimeSessionsState, type RuntimeSessionsStateSlice, type RuntimeSessionRecord, type RuntimeSessionStatus, type RuntimeTimelineEntry, type SystemIntentEnvelope, } from './runtimeSessionsSlice';
|
|
3
|
+
export { selectPendingDomainIntents, selectPendingNavIntents, selectPendingSystemIntents, selectRuntimeSessionsState, selectProjectedRuntimeDomains, selectRuntimeSurfaceState, selectRuntimeSession, selectRuntimeSessionState, selectRuntimeTimeline, } from './selectors';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { authorizeDomainIntent, authorizeSystemIntent, resolveCapabilityPolicy, } from './capabilityPolicy';
|
|
2
|
+
export { clearRuntimeTimeline, dequeuePendingDomainIntent, dequeuePendingNavIntent, dequeuePendingSystemIntent, ingestRuntimeAction, runtimeSessionsReducer, registerRuntimeSession, removeRuntimeSession, setRuntimeSessionStatus, } from './runtimeSessionsSlice';
|
|
3
|
+
export { selectPendingDomainIntents, selectPendingNavIntents, selectPendingSystemIntents, selectRuntimeSessionsState, selectProjectedRuntimeDomains, selectRuntimeSurfaceState, selectRuntimeSession, selectRuntimeSessionState, selectRuntimeTimeline, } from './selectors';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { RuntimeAction, RuntimeActionKind } from '../../plugin-runtime/contracts';
|
|
2
|
+
import { type CapabilityPolicy } from './capabilityPolicy';
|
|
3
|
+
export type DispatchOutcome = 'applied' | 'denied' | 'ignored';
|
|
4
|
+
export type RuntimeSessionStatus = 'loading' | 'ready' | 'error';
|
|
5
|
+
export interface RuntimeTimelineEntry {
|
|
6
|
+
id: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
sessionId: string;
|
|
9
|
+
surfaceId: string;
|
|
10
|
+
kind: RuntimeActionKind;
|
|
11
|
+
actionType: string;
|
|
12
|
+
payload?: unknown;
|
|
13
|
+
outcome: DispatchOutcome;
|
|
14
|
+
reason: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface DomainIntentEnvelope {
|
|
17
|
+
id: string;
|
|
18
|
+
timestamp: string;
|
|
19
|
+
sessionId: string;
|
|
20
|
+
surfaceId: string;
|
|
21
|
+
domain: string;
|
|
22
|
+
type: string;
|
|
23
|
+
payload?: unknown;
|
|
24
|
+
}
|
|
25
|
+
export interface SystemIntentEnvelope {
|
|
26
|
+
id: string;
|
|
27
|
+
timestamp: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
surfaceId: string;
|
|
30
|
+
type: string;
|
|
31
|
+
payload?: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface RuntimeSessionRecord {
|
|
34
|
+
bundleId: string;
|
|
35
|
+
status: RuntimeSessionStatus;
|
|
36
|
+
error: string | null;
|
|
37
|
+
sessionState: Record<string, unknown>;
|
|
38
|
+
surfaceState: Record<string, Record<string, unknown>>;
|
|
39
|
+
capabilities: CapabilityPolicy;
|
|
40
|
+
}
|
|
41
|
+
export interface RuntimeSessionsState {
|
|
42
|
+
sessions: Record<string, RuntimeSessionRecord>;
|
|
43
|
+
timeline: RuntimeTimelineEntry[];
|
|
44
|
+
pendingDomainIntents: DomainIntentEnvelope[];
|
|
45
|
+
pendingSystemIntents: SystemIntentEnvelope[];
|
|
46
|
+
pendingNavIntents: SystemIntentEnvelope[];
|
|
47
|
+
}
|
|
48
|
+
export interface RuntimeSessionsStateSlice {
|
|
49
|
+
runtimeSessions: RuntimeSessionsState;
|
|
50
|
+
}
|
|
51
|
+
interface RegisterSessionPayload {
|
|
52
|
+
sessionId: string;
|
|
53
|
+
bundleId: string;
|
|
54
|
+
initialSessionState?: Record<string, unknown>;
|
|
55
|
+
initialSurfaceState?: Record<string, Record<string, unknown>>;
|
|
56
|
+
capabilities?: Partial<CapabilityPolicy>;
|
|
57
|
+
status?: RuntimeSessionStatus;
|
|
58
|
+
}
|
|
59
|
+
interface RemoveSessionPayload {
|
|
60
|
+
sessionId: string;
|
|
61
|
+
}
|
|
62
|
+
interface SetSessionStatusPayload {
|
|
63
|
+
sessionId: string;
|
|
64
|
+
status: RuntimeSessionStatus;
|
|
65
|
+
error?: string | null;
|
|
66
|
+
}
|
|
67
|
+
interface IngestActionPayload {
|
|
68
|
+
id: string;
|
|
69
|
+
timestamp: string;
|
|
70
|
+
sessionId: string;
|
|
71
|
+
surfaceId: string;
|
|
72
|
+
action: RuntimeAction;
|
|
73
|
+
}
|
|
74
|
+
export declare const clearRuntimeTimeline: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"runtimeSessions/clearRuntimeTimeline">, dequeuePendingDomainIntent: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<{
|
|
75
|
+
id?: string;
|
|
76
|
+
} | undefined, "runtimeSessions/dequeuePendingDomainIntent">, dequeuePendingNavIntent: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<{
|
|
77
|
+
id?: string;
|
|
78
|
+
} | undefined, "runtimeSessions/dequeuePendingNavIntent">, dequeuePendingSystemIntent: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<{
|
|
79
|
+
id?: string;
|
|
80
|
+
} | undefined, "runtimeSessions/dequeuePendingSystemIntent">, ingestRuntimeAction: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: Omit<IngestActionPayload, "id" | "timestamp"> & {
|
|
81
|
+
timestamp?: string;
|
|
82
|
+
}], {
|
|
83
|
+
id: string;
|
|
84
|
+
timestamp: string;
|
|
85
|
+
sessionId: string;
|
|
86
|
+
surfaceId: string;
|
|
87
|
+
action: RuntimeAction;
|
|
88
|
+
}, "runtimeSessions/ingestRuntimeAction", never, never>, registerRuntimeSession: import("@reduxjs/toolkit").ActionCreatorWithPayload<RegisterSessionPayload, "runtimeSessions/registerRuntimeSession">, removeRuntimeSession: import("@reduxjs/toolkit").ActionCreatorWithPayload<RemoveSessionPayload, "runtimeSessions/removeRuntimeSession">, setRuntimeSessionStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<SetSessionStatusPayload, "runtimeSessions/setRuntimeSessionStatus">;
|
|
89
|
+
export declare const runtimeSessionsReducer: import("@reduxjs/toolkit").Reducer<RuntimeSessionsState>;
|
|
90
|
+
export {};
|