@ekrooh/bare 0.4.0 → 0.6.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 +139 -195
- package/dist/{index-DjulYFJL.d.mts → index-BkwUzwVt.d.mts} +94 -20
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -512
- package/dist/messages-CevD8qkV.mjs +851 -0
- package/dist/{plugins-1KweEBct.mjs → plugins-Ddp2AgOe.mjs} +51 -20
- package/dist/plugins-discovery-events.d.mts +5 -8
- package/dist/plugins-discovery-events.mjs +2 -3
- package/dist/plugins-health-events.d.mts +4 -7
- package/dist/plugins-health-events.mjs +2 -3
- package/dist/plugins-logs-events.d.mts +34 -0
- package/dist/plugins-logs-events.mjs +25 -0
- package/dist/plugins-media-events.d.mts +6 -11
- package/dist/plugins-media-events.mjs +2 -3
- package/dist/plugins-permissions-events.d.mts +7 -10
- package/dist/plugins-permissions-events.mjs +2 -3
- package/dist/plugins.d.mts +9 -11
- package/dist/plugins.mjs +1 -2
- package/dist/runtime.d.mts +75 -46
- package/dist/runtime.mjs +1314 -324
- package/dist/static-file-server-COxd-PFR.d.mts +120 -0
- package/dist/store-DTg1jN5E.mjs +49 -0
- package/dist/transports.d.mts +15 -14
- package/dist/transports.mjs +169 -82
- package/package.json +12 -8
- package/dist/static-file-server-DY9EKEVf.d.mts +0 -88
package/README.md
CHANGED
|
@@ -1,246 +1,190 @@
|
|
|
1
1
|
# @ekrooh/bare
|
|
2
2
|
|
|
3
|
-
**One soul, many platforms.**
|
|
4
|
-
across every platform at once — the same app, state, and soul on Android, iOS,
|
|
3
|
+
**One soul, many platforms.** One codebase and identity across Android, iOS,
|
|
5
4
|
web, and desktop.
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
**Docs: <https://anderscan.github.io/ekrooh>**
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
(
|
|
11
|
-
and native host bridges — plus a reference app that demonstrates all of it.
|
|
8
|
+
[](https://github.com/AndersCan/ekrooh/actions/workflows/test.yml)
|
|
9
|
+
[](https://github.com/AndersCan/ekrooh/releases/latest)
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
The boring bootstrap for cross-platform apps on the Bare runtime (holepunch): a
|
|
12
|
+
binary wire protocol, plugin kernel, RPC messenger, transports, and native host
|
|
13
|
+
bridges, plus a reference app.
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
- [`vision.md`](vision.md): what this project is and is not.
|
|
16
|
+
- [`AGENTS.md`](AGENTS.md): how to develop it and the canonical commands.
|
|
17
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md): how to contribute.
|
|
18
|
+
- [`RELEASING.md`](RELEASING.md): how to cut a release.
|
|
19
|
+
- `docs/`: internal ADRs (`docs/adr/`) and agent operations; not the public site.
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
**backend** reachable over a **WebSocket** (binary framed messages). It picks a
|
|
21
|
-
transport automatically:
|
|
21
|
+
## Model
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
so the page connects to the same origin it was served from (`ws://location.host`).
|
|
26
|
-
- **Mock transport** when `VITE_TRANSPORT_MODE=mock` (tests).
|
|
27
|
-
- Browser dev points the WebSocket at the dev backend explicitly
|
|
28
|
-
(`VITE_BARE_WS_URL`).
|
|
23
|
+
The frontend (browser or WebView) is written as if only a backend is reachable
|
|
24
|
+
over a WebSocket carrying binary framed messages.
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
- On device, the worklet runs one loopback HTTP+WS server that serves the web
|
|
27
|
+
app, media files, and the protocol socket. The page connects to
|
|
28
|
+
`ws://location.host`.
|
|
29
|
+
- `VITE_TRANSPORT_MODE=mock` selects the mock transport (tests).
|
|
30
|
+
- Browser dev points the WebSocket at the dev backend via `VITE_BARE_WS_URL`.
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
The UI never branches on worklets, Bare, or host IPC. `@ekrooh/bare/core`
|
|
33
|
+
describes the wire protocol, not the runtime that implements it.
|
|
35
34
|
|
|
36
|
-
| Term | Meaning
|
|
37
|
-
| ------------ |
|
|
38
|
-
| **Frontend** | Web UI: Vite bundle in the browser
|
|
39
|
-
| **Backend** |
|
|
40
|
-
| **Host** | Native shell (Android/iOS): starts the backend, owns system APIs, injects the session
|
|
35
|
+
| Term | Meaning |
|
|
36
|
+
| ------------ | ------------------------------------------------------------------------------------------------ |
|
|
37
|
+
| **Frontend** | Web UI: Vite bundle in the browser, or the same bundle in a WebView. |
|
|
38
|
+
| **Backend** | Runs the core bundle and speaks the framed protocol with the frontend. |
|
|
39
|
+
| **Host** | Native shell (Android/iOS): starts the backend, owns system APIs, injects the session bootstrap. |
|
|
41
40
|
|
|
42
|
-
## Repository
|
|
41
|
+
## Repository layout
|
|
43
42
|
|
|
44
|
-
- `core
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- `plugins/` — framework: canonical plugins (`core.health`, `core.discovery`,
|
|
43
|
+
- `core/`: wire codec, plugin router, RPC messenger, host IPC (`core/messages`),
|
|
44
|
+
loopback server (`core/server`), worklet entry (`core/main.core.ts`).
|
|
45
|
+
- `plugins/`: canonical plugins (`core.health`, `core.discovery`,
|
|
48
46
|
`core.permissions`, `vendor.media`) and typed event builders.
|
|
49
|
-
- `web/transports
|
|
50
|
-
|
|
51
|
-
- `
|
|
52
|
-
|
|
53
|
-
- `
|
|
54
|
-
|
|
55
|
-
- `
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
The framework's public surface is the `exports` map of the root `package.json`
|
|
64
|
-
(`@ekrooh/bare/core`, `/runtime`, `/plugins`, `/plugins/*/events`, `/transports`).
|
|
65
|
-
The package ships **compiled ESM JavaScript + type declarations** (`dist/`,
|
|
66
|
-
built with `vp pack`) — consumers never see TypeScript source.
|
|
47
|
+
- `web/transports/`: `MessageTransport`, WebSocket and mock transports.
|
|
48
|
+
- `android/`: host library `:bare-host` (IPC coordinator, plugin registry, WebView client).
|
|
49
|
+
- `ios/`: host Swift package `BareHost` (root `Package.swift`).
|
|
50
|
+
- `examples/`: `web/`, `android-app/`, `ios-app/`, `consumer-basic/`.
|
|
51
|
+
- `e2e/`: Playwright specs on the mock transport. `scripts/`: dev runner, Playwright wrapper, prebuild fetcher.
|
|
52
|
+
- `apps/docs/`: public docs site (Astro Starlight, GitHub Pages).
|
|
53
|
+
- `templates/`, `research/`: shared deploy templates; design notes.
|
|
54
|
+
- `prebuilds/`: Bare Kit prebuilds (gitignored build output).
|
|
55
|
+
|
|
56
|
+
The public surface is the `exports` map in `package.json` (`@ekrooh/bare/core`,
|
|
57
|
+
`/runtime`, `/plugins`, `/plugins/*/events`, `/transports`). It points at
|
|
58
|
+
compiled ESM and type declarations in `dist/` (built with `vp pack`). Consumers
|
|
59
|
+
never see TypeScript source.
|
|
67
60
|
|
|
68
61
|
## Support matrix
|
|
69
62
|
|
|
70
|
-
| Runtime | Status | Transport | Notes
|
|
71
|
-
| -------------- | ------------------------------ | --------- |
|
|
72
|
-
| Browser (dev) | first-class | WebSocket | Vite + local backend
|
|
73
|
-
| Browser (test) | first-class | Mock | Deterministic Playwright runs
|
|
74
|
-
| Android | first-class | WebSocket | Same-origin loopback socket
|
|
75
|
-
| iOS | first-class (reference parity) | WebSocket | Same-origin loopback socket
|
|
76
|
-
| Desktop |
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- Plugins report unsupported behavior with deterministic errors.
|
|
83
|
-
- Feature code calls plugin events, not raw platform APIs from shared UI code.
|
|
84
|
-
|
|
85
|
-
## Architecture
|
|
86
|
-
|
|
87
|
-
```mermaid
|
|
88
|
-
flowchart LR
|
|
89
|
-
subgraph frontend [Frontend]
|
|
90
|
-
webUi[WebUI]
|
|
91
|
-
end
|
|
92
|
-
subgraph backend [Backend]
|
|
93
|
-
coreProtocol[Message protocol]
|
|
94
|
-
coreRuntime[Core + plugins]
|
|
95
|
-
end
|
|
96
|
-
subgraph host [Host native]
|
|
97
|
-
hostIpc[Host IPC]
|
|
98
|
-
end
|
|
99
|
-
webUi -->|WebSocket framed messages| coreProtocol
|
|
100
|
-
coreProtocol --> coreRuntime
|
|
101
|
-
coreRuntime <-->|delegation| hostIpc
|
|
102
|
-
```
|
|
63
|
+
| Runtime | Status | Transport | Notes |
|
|
64
|
+
| -------------- | ------------------------------ | --------- | ----------------------------------------------------------------------------------------------- |
|
|
65
|
+
| Browser (dev) | first-class | WebSocket | Vite + local backend (`VITE_BARE_WS_URL`) |
|
|
66
|
+
| Browser (test) | first-class | Mock | Deterministic Playwright runs |
|
|
67
|
+
| Android | first-class | WebSocket | Same-origin loopback socket, cookie auth |
|
|
68
|
+
| iOS | first-class (reference parity) | WebSocket | Same-origin loopback socket, cookie auth; host ships as source (`ios/`, SPM) on the release tag |
|
|
69
|
+
| Desktop | reference (macOS) | WebSocket | Zig host + system webview + sidecar `bare`; see `examples/desktop-app` |
|
|
70
|
+
|
|
71
|
+
Parity policy: core protocol and plugin contracts are runtime-agnostic.
|
|
72
|
+
Capabilities are opt-in plugins and may be runtime-specific. Plugins report
|
|
73
|
+
unsupported behavior with deterministic errors. Feature code calls plugin
|
|
74
|
+
events, never raw platform APIs.
|
|
103
75
|
|
|
104
76
|
## Message protocol
|
|
105
77
|
|
|
106
|
-
Binary frame:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
(`
|
|
149
|
-
|
|
150
|
-
### Media (out-of-band bytes)
|
|
151
|
-
|
|
152
|
-
Images/videos never cross the wire protocol (the frame cap is 16 MiB).
|
|
153
|
-
`vendor.media` demonstrates the intended pattern: the host picks/captures a
|
|
154
|
-
file natively and returns its path; the worklet mounts it on the loopback
|
|
155
|
-
server and returns a URL. The web layer loads the URL directly (same-origin,
|
|
156
|
-
cookie-authenticated) — one serving implementation for every runtime. The
|
|
157
|
-
Android reference host wires a real native picker/camera; the iOS reference
|
|
158
|
-
host's real picker/camera is part of the iOS parity worklist.
|
|
159
|
-
|
|
160
|
-
## Scripts
|
|
161
|
-
|
|
162
|
-
| Script | Purpose |
|
|
163
|
-
| ---------------------------- | ------------------------------------------------------ |
|
|
164
|
-
| `npm run check` | Format + lint + type-check (`vp check`) |
|
|
165
|
-
| `npm run typecheck` | `tsc --noEmit` only |
|
|
166
|
-
| `npm run test` | Unit tests + Playwright e2e |
|
|
167
|
-
| `npm run test:unit` | Unit tests (vitest) |
|
|
168
|
-
| `npm run test:e2e:web` | Playwright e2e |
|
|
169
|
-
| `npm run dev` | Vite dev server + Bare backend (watch restart) |
|
|
170
|
-
| `npm run dev:mock` | Vite with mock transport |
|
|
171
|
-
| `npm run dev:ws` | Vite only (backend started separately) |
|
|
172
|
-
| `npm run build` | Core bundle + web assets for the Android app |
|
|
173
|
-
| `npm run build:core` | Bundle `core/main.core.ts` with esbuild |
|
|
174
|
-
| `npm run build:web` | Build `examples/web` into the Android app's assets |
|
|
175
|
-
| `npm run build:ios` | iOS app inputs (addons, bundle, web assets) + xcodegen |
|
|
176
|
-
| `npm run prebuilds` | Fetch Bare Kit prebuilds (requires `gh`) |
|
|
177
|
-
| `npm run test:ios` | XCTest + UI test on the iOS simulator |
|
|
178
|
-
| `npm run playwright:install` | Download Chromium into `.playwright-browsers/` |
|
|
179
|
-
| `npm run smoke:loopback` | Smoke-test the unified loopback server under `bare` |
|
|
78
|
+
Binary frame: `version` (1 byte), `type` (1 byte), `headerLen` (2 bytes, BE),
|
|
79
|
+
`payloadLen` (3 bytes, BE), `header` (UTF-8 JSON), `payload` (optional).
|
|
80
|
+
Implemented by `MessageProtocol` in `core/messages` (`wire-codec.ts`).
|
|
81
|
+
|
|
82
|
+
Header types: `DISPATCH`, `INVOKE_REQUEST`, `INVOKE_RESPONSE` (responses echo
|
|
83
|
+
`requestId`). `dispatch` is fire-and-forget; `invoke` is request/response and
|
|
84
|
+
timeout-bound.
|
|
85
|
+
|
|
86
|
+
## Plugins
|
|
87
|
+
|
|
88
|
+
Core ships no product features. Plugin IDs are `vendor.plugin`; events are
|
|
89
|
+
plugin-local (`health.ping`). Runtime adapters: `web`, `android`, `ios`, `bare`.
|
|
90
|
+
Canonical plugins are listed under Repository layout.
|
|
91
|
+
|
|
92
|
+
Media never crosses the wire (frame cap 16 MiB). `vendor.media` is the reference
|
|
93
|
+
for out-of-band bytes: the host picks or captures a file natively, the worklet
|
|
94
|
+
mounts it on the loopback server and returns a URL, and the web layer loads that
|
|
95
|
+
URL with cookie auth.
|
|
96
|
+
|
|
97
|
+
## Loopback server and auth
|
|
98
|
+
|
|
99
|
+
One server (`core/server/static-file-server.ts`) serves the web app at `/`, plugin
|
|
100
|
+
media mounts, and the WebSocket upgrade. On device it binds `127.0.0.1` on an
|
|
101
|
+
ephemeral port. Every request is gated by a per-session token:
|
|
102
|
+
|
|
103
|
+
1. The shell injects `window.__ekrooh = { bootstrap }`, a single-use nonce (never the raw token).
|
|
104
|
+
2. The page calls `POST /login` with the nonce and receives a
|
|
105
|
+
`bare_session_<port>` cookie (`HttpOnly; SameSite=Strict; Path=/`). The port
|
|
106
|
+
in the name keeps two loopback servers in one cookie store apart. The raw
|
|
107
|
+
token is never a login credential.
|
|
108
|
+
3. The cookie authorizes `<img>`, `<video>`, `fetch`, and the WS upgrade.
|
|
109
|
+
Non-browser clients send `X-Bare-Token`. There is no `?token=` fallback
|
|
110
|
+
(see `docs/adr/0003-loopback-auth-surface.md`).
|
|
111
|
+
|
|
112
|
+
Only the bootstrap mount at `/` is public pre-auth. The worklet writes
|
|
113
|
+
`{ origin, port, bootstrap }` to `handoff.json` in the host's sandbox dir (never
|
|
114
|
+
the session token); the host loads `http://127.0.0.1:<port>/index.html` and
|
|
115
|
+
deletes the file.
|
|
116
|
+
|
|
117
|
+
Every request must also target the bound loopback server (`Host`), and a page
|
|
118
|
+
`Origin` must be that server or an explicitly allowed one, in every mode.
|
|
119
|
+
|
|
120
|
+
The dev backend (`mode=dev`) runs the same bundle with auth off on
|
|
121
|
+
`ws://localhost:8080`, allowing the Vite dev origin (`origin=` tokens).
|
|
180
122
|
|
|
181
123
|
## Testing
|
|
182
124
|
|
|
183
|
-
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
125
|
+
- Unit: vitest (`vp test`) for the codec, router, RPC messenger, transports;
|
|
126
|
+
JUnit for the Kotlin host; XCTest for the Swift host.
|
|
127
|
+
- Type check: `tsc --noEmit`, part of `vp check`.
|
|
128
|
+
- Integration: Playwright e2e on the mock transport (`npm run test:e2e:web`).
|
|
129
|
+
Browsers resolve through `scripts/playwright-local-browsers.mjs` into `.playwright-browsers/`.
|
|
130
|
+
- Native: `npm run test:ios` runs the Swift XCTest suite and a UI test on the simulator.
|
|
131
|
+
|
|
132
|
+
## Commands
|
|
133
|
+
|
|
134
|
+
Canonical list: [`AGENTS.md`](AGENTS.md#canonical-commands). Common ones:
|
|
135
|
+
|
|
136
|
+
| Command | Purpose |
|
|
137
|
+
| ---------------------------- | -------------------------------------------------- |
|
|
138
|
+
| `vp check` | Format, lint, type-check |
|
|
139
|
+
| `vp test` | Unit tests |
|
|
140
|
+
| `npm run test` | Unit + Playwright e2e |
|
|
141
|
+
| `npm run dev` | Vite dev server + Bare backend (watch restart) |
|
|
142
|
+
| `npm run dev:mock` | Vite with mock transport |
|
|
143
|
+
| `npm run build:core` | Bundle `core/main.core.ts` (esbuild) |
|
|
144
|
+
| `npm run build:web` | Build `examples/web` into the Android app's assets |
|
|
145
|
+
| `npm run build:ios` | iOS inputs (addons, bundle, web assets, xcodegen) |
|
|
146
|
+
| `npm run prebuilds` | Fetch Bare Kit prebuilds (requires `gh`) |
|
|
147
|
+
| `npm run playwright:install` | Download Chromium into `.playwright-browsers/` |
|
|
148
|
+
| `npm run smoke:loopback` | Smoke-test the loopback server under `bare` |
|
|
194
149
|
|
|
195
150
|
## Android build
|
|
196
151
|
|
|
197
|
-
|
|
198
|
-
prebuilds.
|
|
152
|
+
Needs the Android SDK (`local.properties` with `sdk.dir=...`) and Bare Kit prebuilds.
|
|
199
153
|
|
|
200
154
|
```bash
|
|
201
|
-
|
|
202
|
-
npm run prebuilds
|
|
155
|
+
vp install
|
|
156
|
+
npm run prebuilds
|
|
203
157
|
npm run build # core bundle + web assets
|
|
204
158
|
./gradlew :examples:android-app:assembleDebug
|
|
205
159
|
```
|
|
206
160
|
|
|
207
|
-
`./gradlew build`
|
|
161
|
+
`./gradlew build` also runs the host library's JUnit tests.
|
|
208
162
|
|
|
209
163
|
### Consuming the published AAR
|
|
210
164
|
|
|
211
|
-
`io.github.anderscan.ekrooh:bare-host` is
|
|
212
|
-
|
|
165
|
+
`io.github.anderscan.ekrooh:bare-host` is on Maven Central (no credentials).
|
|
166
|
+
Pin it to the same version as `@ekrooh/bare`:
|
|
213
167
|
|
|
214
168
|
```gradle
|
|
215
|
-
repositories {
|
|
216
|
-
mavenCentral() // default in modern Gradle; no credentials needed
|
|
217
|
-
}
|
|
218
|
-
|
|
219
169
|
dependencies {
|
|
220
170
|
implementation "io.github.anderscan.ekrooh:bare-host:<version>"
|
|
221
171
|
}
|
|
222
172
|
```
|
|
223
173
|
|
|
224
|
-
GitHub Packages
|
|
225
|
-
but requires a `read:packages` PAT — prefer Maven Central.
|
|
174
|
+
GitHub Packages is a fallback that requires a `read:packages` token. Prefer Maven Central.
|
|
226
175
|
|
|
227
176
|
## iOS build
|
|
228
177
|
|
|
229
|
-
|
|
178
|
+
Needs Xcode, Bare Kit prebuilds, and `xcodegen`.
|
|
230
179
|
|
|
231
180
|
```bash
|
|
232
|
-
|
|
233
|
-
npm run prebuilds # fetches prebuilds/ios/BareKit.xcframework
|
|
234
|
-
npm run build:ios
|
|
235
|
-
npm run test:ios
|
|
181
|
+
vp install
|
|
182
|
+
npm run prebuilds # also fetches prebuilds/ios/BareKit.xcframework
|
|
183
|
+
npm run build:ios
|
|
184
|
+
npm run test:ios
|
|
236
185
|
```
|
|
237
186
|
|
|
238
|
-
|
|
239
|
-
app) plus a UI test that verifies the health-checks page end to end.
|
|
240
|
-
|
|
241
|
-
## Asset hygiene
|
|
187
|
+
## Notes
|
|
242
188
|
|
|
243
|
-
-
|
|
244
|
-
before rebuilding
|
|
245
|
-
- Treat files under `examples/android-app/src/main/assets/` as build output,
|
|
246
|
-
not source.
|
|
189
|
+
- Treat `examples/android-app/src/main/assets/` as build output. Vite clears
|
|
190
|
+
hashed `assets/main-*.{js,css}` before rebuilding.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region core/messages/constants.d.ts
|
|
2
1
|
declare const VERSION = 1;
|
|
3
2
|
declare const MessageType: {
|
|
4
3
|
readonly ENVELOPE: 1;
|
|
@@ -25,8 +24,6 @@ declare const MAX_HEADER_BYTES = 65535;
|
|
|
25
24
|
/** Upper bound on one encoded frame. Large binary transfers are a plugin-level
|
|
26
25
|
* concern (out-of-band handles), not an in-band payload. */
|
|
27
26
|
declare const MAX_FRAME_BYTES: number;
|
|
28
|
-
//#endregion
|
|
29
|
-
//#region core/messages/types.d.ts
|
|
30
27
|
/** Decoded binary frame (version byte stripped); used by WebSocket and other transports. */
|
|
31
28
|
interface WireMessage {
|
|
32
29
|
type: MessageTypeValue;
|
|
@@ -126,6 +123,12 @@ type MessageHeader = PluginHeader | HostCapabilitiesQueryHeader | HostCapabiliti
|
|
|
126
123
|
interface PluginContext {
|
|
127
124
|
runtime: RuntimeTarget;
|
|
128
125
|
payload: Uint8Array;
|
|
126
|
+
/** The registry-validated sender of the envelope. Always equals the
|
|
127
|
+
* wire `pluginId` once the router has matched a manifest — adapters can trust
|
|
128
|
+
* it without re-validating against the wire. */
|
|
129
|
+
sender?: {
|
|
130
|
+
pluginId: string;
|
|
131
|
+
};
|
|
129
132
|
}
|
|
130
133
|
interface PluginRuntimeAdapter {
|
|
131
134
|
dispatch?: (event: string, args: Record<string, unknown> | undefined, context: PluginContext) => void | Promise<void>;
|
|
@@ -138,8 +141,6 @@ interface PluginManifest {
|
|
|
138
141
|
events?: string[];
|
|
139
142
|
runtimes: Partial<Record<RuntimeTarget, PluginRuntimeAdapter>>;
|
|
140
143
|
}
|
|
141
|
-
//#endregion
|
|
142
|
-
//#region core/messages/errors.d.ts
|
|
143
144
|
/** Success half of an `Either` tuple. */
|
|
144
145
|
declare function ok<T>(result: T): [null, T];
|
|
145
146
|
/** Failure half of an `Either` tuple. Accepts a canonical error code or any
|
|
@@ -153,8 +154,6 @@ declare function coerceErrorCode(code: string | undefined): ErrorCode;
|
|
|
153
154
|
type Ok<T> = [null, T];
|
|
154
155
|
type Err = [CoreError, null];
|
|
155
156
|
type PluginResult<T> = Either<CoreError, T>;
|
|
156
|
-
//#endregion
|
|
157
|
-
//#region core/messages/rpc-messenger.d.ts
|
|
158
157
|
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
|
|
159
158
|
type InternalDispatchRequest = (PluginDispatchHeader & {
|
|
160
159
|
requestId: string;
|
|
@@ -169,8 +168,6 @@ interface ProtocolMessenger {
|
|
|
169
168
|
handleIncoming(header: MessageHeader): void;
|
|
170
169
|
}
|
|
171
170
|
declare function createProtocolMessenger(send: (request: InternalDispatchRequest, payload?: Uint8Array | ArrayBuffer | string | null) => void): ProtocolMessenger;
|
|
172
|
-
//#endregion
|
|
173
|
-
//#region core/messages/plugin-kernel.d.ts
|
|
174
171
|
interface PluginRegistry {
|
|
175
172
|
register(plugin: PluginManifest): void;
|
|
176
173
|
resolve(pluginId: string, runtime: RuntimeTarget): PluginManifest['runtimes'][RuntimeTarget] | undefined;
|
|
@@ -188,7 +185,27 @@ interface PluginRouter {
|
|
|
188
185
|
}
|
|
189
186
|
type PluginRouterOptions = {
|
|
190
187
|
delegateToHost?: (header: PluginInvokeRequestHeader, payload: Uint8Array) => Promise<PluginInvokeResponseHeader | null>;
|
|
191
|
-
|
|
188
|
+
/** Host-registered capabilities (from `HOST_CAPABILITIES_RESPONSE`).
|
|
189
|
+
* Delegation to the host is only permitted for events the host announces
|
|
190
|
+
* here — a deny-by-default allowlist. When omitted or empty, no
|
|
191
|
+
* host-delegated invokes are allowed. */
|
|
192
|
+
getHostCapabilities?: () => Promise<CapabilityDescriptor[]>;
|
|
193
|
+
logger?: Pick<Console, 'warn' | 'error' | 'debug'>;
|
|
194
|
+
/** Global cap on calls routed at once (running or queued behind a serial
|
|
195
|
+
* plugin). A call over the cap is answered with a `TRANSPORT_ERROR` instead
|
|
196
|
+
* of being queued, so a flood cannot grow memory without bound. Defaults to
|
|
197
|
+
* `DEFAULT_MAX_INFLIGHT`. */
|
|
198
|
+
maxInflight?: number;
|
|
199
|
+
/** Plugin ids whose calls must run one at a time, in arrival order. Every
|
|
200
|
+
* other plugin's calls run concurrently, so a slow call never delays them. */
|
|
201
|
+
serialPlugins?: readonly string[];
|
|
202
|
+
/** How long (ms) the host's announced capabilities are reused before the
|
|
203
|
+
* host is asked again. Delegating an invoke otherwise costs an IPC round
|
|
204
|
+
* trip per call. Capabilities the host adds are still picked up within
|
|
205
|
+
* `NEGATIVE_REFRESH_MS`, and a failed delegation drops the cache at once.
|
|
206
|
+
* `0` disables caching (ask the host on every delegation). Defaults to
|
|
207
|
+
* `DEFAULT_HOST_CAPABILITIES_TTL_MS`. */
|
|
208
|
+
hostCapabilitiesTtlMs?: number;
|
|
192
209
|
};
|
|
193
210
|
declare function createPluginRouter(registry: PluginRegistry, runtime: RuntimeTarget, options?: PluginRouterOptions): PluginRouter;
|
|
194
211
|
interface PluginBus {
|
|
@@ -196,28 +213,29 @@ interface PluginBus {
|
|
|
196
213
|
invoke<TResult>(envelope: InvokeEnvelope<string, Record<string, unknown>, TResult>): Promise<Either<CoreError, TResult>>;
|
|
197
214
|
}
|
|
198
215
|
declare function createPluginBus(messenger: ProtocolMessenger): PluginBus;
|
|
199
|
-
//#endregion
|
|
200
|
-
//#region core/messages/wire-codec.d.ts
|
|
201
216
|
type Encoder = (str: string) => Uint8Array;
|
|
202
217
|
type Decoder = (bytes: Uint8Array) => string;
|
|
203
218
|
interface ProtocolOptions {
|
|
204
219
|
encode?: Encoder;
|
|
205
220
|
decode?: Decoder;
|
|
206
221
|
allowUnknownTypes?: boolean;
|
|
207
|
-
/** Override the default {@link MAX_FRAME_BYTES} cap.
|
|
222
|
+
/** Override the default {@link MAX_FRAME_BYTES} cap. Clamped to a sane
|
|
223
|
+
* range: it must be large enough to hold the largest legal header, and never
|
|
224
|
+
* larger than {@link MAX_FRAME_BYTES}. `Infinity`/`NaN`/negative inputs
|
|
225
|
+
* collapse to the defaults so a caller cannot disable the decode-side cap. */
|
|
208
226
|
maxFrameBytes?: number;
|
|
209
227
|
}
|
|
210
228
|
declare class MessageProtocol {
|
|
211
229
|
private encodeStr;
|
|
212
230
|
private decodeStr;
|
|
213
231
|
private allowUnknownTypes;
|
|
214
|
-
|
|
232
|
+
/** Resolved, clamped frame cap. Public so callers/tests can inspect the
|
|
233
|
+
* effective limit after clamping. */
|
|
234
|
+
readonly maxFrameBytes: number;
|
|
215
235
|
constructor(options?: ProtocolOptions);
|
|
216
236
|
encode(type: MessageTypeValue, header: MessageHeader, payload?: Uint8Array | ArrayBuffer | string | null): Uint8Array;
|
|
217
237
|
decode(data: ArrayBuffer | Uint8Array): WireMessage;
|
|
218
238
|
}
|
|
219
|
-
//#endregion
|
|
220
|
-
//#region core/messages/define-plugin.d.ts
|
|
221
239
|
/**
|
|
222
240
|
* One event's wire identity and type shape — the single source of truth for
|
|
223
241
|
* the manifest `events` list, the generated handler table, and the typed
|
|
@@ -249,13 +267,69 @@ declare function definePlugin<const TSpecs extends Record<string, EventSpec<any,
|
|
|
249
267
|
declare function invokeEvent<TSpec extends EventSpec<any, any>>(spec: TSpec, args: TSpec['args'], payload?: Uint8Array | ArrayBuffer | string | null, timeoutMs?: number): InvokeEnvelope<TSpec['name'], TSpec['args'], TSpec['result']>;
|
|
250
268
|
/** Typed builder: a dispatch envelope for a spec's event. */
|
|
251
269
|
declare function dispatchEvent<TSpec extends EventSpec<any, any>>(spec: TSpec, args: TSpec['args'], payload?: Uint8Array | ArrayBuffer | string | null): DispatchEnvelope<TSpec['name'], TSpec['args']>;
|
|
252
|
-
//#endregion
|
|
253
|
-
//#region core/messages/create-bare-runtime-context.d.ts
|
|
254
270
|
interface BareRuntimeContext {
|
|
255
271
|
protocol: MessageProtocol;
|
|
256
272
|
pluginRegistry: PluginRegistry;
|
|
257
273
|
pluginRouter: PluginRouter;
|
|
258
274
|
}
|
|
259
275
|
declare function createBareRuntimeContext(plugins?: PluginManifest[], routerOptions?: PluginRouterOptions): BareRuntimeContext;
|
|
260
|
-
|
|
261
|
-
|
|
276
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
277
|
+
type LogSource = 'backend' | 'web';
|
|
278
|
+
interface LogEntry {
|
|
279
|
+
/** Monotonic sequence assigned by the store on append. */
|
|
280
|
+
seq: number;
|
|
281
|
+
/** Epoch millisecond timestamp. */
|
|
282
|
+
ts: number;
|
|
283
|
+
level: LogLevel;
|
|
284
|
+
source: LogSource;
|
|
285
|
+
tag?: string;
|
|
286
|
+
message: string;
|
|
287
|
+
}
|
|
288
|
+
interface LogViewOptions {
|
|
289
|
+
/** Most-recent N matching entries. Omit for all. */
|
|
290
|
+
tail?: number;
|
|
291
|
+
level?: LogLevel;
|
|
292
|
+
source?: LogSource;
|
|
293
|
+
}
|
|
294
|
+
interface LogStore {
|
|
295
|
+
/** Maximum number of entries retained (oldest dropped first). */
|
|
296
|
+
readonly capacity: number;
|
|
297
|
+
/** Appends an entry, assigning its `seq`. */
|
|
298
|
+
append(entry: Omit<LogEntry, 'seq'>): void;
|
|
299
|
+
/** Returns a snapshot of matching entries, newest-last, bounded by `tail`. */
|
|
300
|
+
view(options?: LogViewOptions): LogEntry[];
|
|
301
|
+
/** Empties the buffer. Returns the number of entries dropped. */
|
|
302
|
+
clear(): number;
|
|
303
|
+
}
|
|
304
|
+
type WebConsoleCaptureOptions = {
|
|
305
|
+
/** Ingest endpoint. Same-origin `/logs` on the loopback server. */
|
|
306
|
+
endpoint?: string;
|
|
307
|
+
/** Max entries buffered before a flush is issued. */
|
|
308
|
+
flushThreshold?: number;
|
|
309
|
+
/** Flush interval (ms); pass 0 to disable the interval. */
|
|
310
|
+
flushIntervalMs?: number;
|
|
311
|
+
/** Injectable ingest function (defaults to a same-origin POST fetch). */
|
|
312
|
+
ingest?: (payload: {
|
|
313
|
+
source: 'web';
|
|
314
|
+
entries: Array<{
|
|
315
|
+
level: LogLevel;
|
|
316
|
+
tag?: string;
|
|
317
|
+
message: string;
|
|
318
|
+
}>;
|
|
319
|
+
}) => void | Promise<void>;
|
|
320
|
+
};
|
|
321
|
+
type WebConsoleCapture = {
|
|
322
|
+
/** Flushes any buffered entries immediately (fire-and-forget). */
|
|
323
|
+
flush(): void;
|
|
324
|
+
/** Restores the original console methods captured at install time. */
|
|
325
|
+
restore(): void;
|
|
326
|
+
};
|
|
327
|
+
/**
|
|
328
|
+
* Web-side console capture: wraps the console methods, buffers formatted
|
|
329
|
+
* entries, and flushes them as a batch to the loopback `POST /logs` ingest
|
|
330
|
+
* route with `source: 'web'` (same-origin fetch). Every step is try/catch-
|
|
331
|
+
* wrapped so the seam can never break the app. Skip installing in mock mode,
|
|
332
|
+
* where there is no loopback backend to ingest into.
|
|
333
|
+
*/
|
|
334
|
+
declare function installWebConsoleCapture(options?: WebConsoleCaptureOptions): WebConsoleCapture;
|
|
335
|
+
export { PluginRuntimeAdapter as $, Ok as A, ErrorCodeOrString as B, createPluginRegistry as C, ProtocolMessenger as D, InvokeRequest as E, CapabilityDescriptor as F, InvokeEnvelope as G, HostCapabilitiesResponseHeader as H, CoreError as I, PluginDispatchHeader as J, MessageHeader as K, CoreErrorWire as L, coerceErrorCode as M, err as N, createProtocolMessenger as O, ok as P, PluginManifest as Q, DispatchEnvelope as R, createPluginBus as S, DispatchRequest as T, HostInvokeRequestHeader as U, HostCapabilitiesQueryHeader as V, HostInvokeResponseHeader as W, PluginInvokeRequestHeader as X, PluginHeader as Y, PluginInvokeResponseHeader as Z, ProtocolOptions as _, LogStore as a, MessageType as at, PluginRouter as b, DispatchHandler as c, definePlugin as d, RuntimeTarget as et, dispatchEvent as f, MessageProtocol as g, Encoder as h, LogSource as i, MAX_HEADER_BYTES as it, PluginResult as j, Err as k, EventSpec as l, Decoder as m, LogEntry as n, ErrorCode as nt, BareRuntimeContext as o, MessageTypeValue as ot, invokeEvent as p, PluginContext as q, LogLevel as r, MAX_FRAME_BYTES as rt, createBareRuntimeContext as s, VERSION as st, installWebConsoleCapture as t, WireMessage as tt, InvokeHandler as u, PluginBus as v, createPluginRouter as w, PluginRouterOptions as x, PluginRegistry as y, Either as z };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
export { BareRuntimeContext, CapabilityDescriptor, CoreError, CoreErrorWire, Decoder, DispatchEnvelope, DispatchHandler, DispatchRequest, Either, Encoder, Err, ErrorCode, ErrorCodeOrString, EventSpec, HostCapabilitiesQueryHeader, HostCapabilitiesResponseHeader, HostInvokeRequestHeader, HostInvokeResponseHeader, InvokeEnvelope, InvokeHandler, InvokeRequest, MAX_FRAME_BYTES, MAX_HEADER_BYTES, MessageHeader, MessageProtocol, MessageType, MessageTypeValue, Ok, PluginBus, PluginContext, PluginDispatchHeader, PluginHeader, PluginInvokeRequestHeader, PluginInvokeResponseHeader, PluginManifest, PluginRegistry, PluginResult, PluginRouter, PluginRouterOptions, PluginRuntimeAdapter, ProtocolMessenger, ProtocolOptions, RuntimeTarget, VERSION, WireMessage, coerceErrorCode, createBareRuntimeContext, createPluginBus, createPluginRegistry, createPluginRouter, createProtocolMessenger, definePlugin, dispatchEvent, err, invokeEvent, ok };
|
|
1
|
+
import { $ as PluginRuntimeAdapter, A as Ok, B as ErrorCodeOrString, C as createPluginRegistry, D as ProtocolMessenger, E as InvokeRequest, F as CapabilityDescriptor, G as InvokeEnvelope, H as HostCapabilitiesResponseHeader, I as CoreError, J as PluginDispatchHeader, K as MessageHeader, L as CoreErrorWire, M as coerceErrorCode, N as err, O as createProtocolMessenger, P as ok, Q as PluginManifest, R as DispatchEnvelope, S as createPluginBus, T as DispatchRequest, U as HostInvokeRequestHeader, V as HostCapabilitiesQueryHeader, W as HostInvokeResponseHeader, X as PluginInvokeRequestHeader, Y as PluginHeader, Z as PluginInvokeResponseHeader, _ as ProtocolOptions, at as MessageType, b as PluginRouter, c as DispatchHandler, d as definePlugin, et as RuntimeTarget, f as dispatchEvent, g as MessageProtocol, h as Encoder, it as MAX_HEADER_BYTES, j as PluginResult, k as Err, l as EventSpec, m as Decoder, nt as ErrorCode, o as BareRuntimeContext, ot as MessageTypeValue, p as invokeEvent, q as PluginContext, rt as MAX_FRAME_BYTES, s as createBareRuntimeContext, st as VERSION, t as installWebConsoleCapture, tt as WireMessage, u as InvokeHandler, v as PluginBus, w as createPluginRouter, x as PluginRouterOptions, y as PluginRegistry, z as Either } from "./index-BkwUzwVt.mjs";
|
|
2
|
+
export { BareRuntimeContext, CapabilityDescriptor, CoreError, CoreErrorWire, Decoder, DispatchEnvelope, DispatchHandler, DispatchRequest, Either, Encoder, Err, ErrorCode, ErrorCodeOrString, EventSpec, HostCapabilitiesQueryHeader, HostCapabilitiesResponseHeader, HostInvokeRequestHeader, HostInvokeResponseHeader, InvokeEnvelope, InvokeHandler, InvokeRequest, MAX_FRAME_BYTES, MAX_HEADER_BYTES, MessageHeader, MessageProtocol, MessageType, MessageTypeValue, Ok, PluginBus, PluginContext, PluginDispatchHeader, PluginHeader, PluginInvokeRequestHeader, PluginInvokeResponseHeader, PluginManifest, PluginRegistry, PluginResult, PluginRouter, PluginRouterOptions, PluginRuntimeAdapter, ProtocolMessenger, ProtocolOptions, RuntimeTarget, VERSION, WireMessage, coerceErrorCode, createBareRuntimeContext, createPluginBus, createPluginRegistry, createPluginRouter, createProtocolMessenger, definePlugin, dispatchEvent, err, installWebConsoleCapture, invokeEvent, ok };
|