@ekrooh/bare 0.5.0 → 0.6.1

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 CHANGED
@@ -3,11 +3,14 @@
3
3
  **One soul, many platforms.** One codebase and identity across Android, iOS,
4
4
  web, and desktop.
5
5
 
6
+ **Docs: <https://anderscan.github.io/ekrooh>**
7
+
6
8
  [![CI](https://github.com/AndersCan/ekrooh/actions/workflows/test.yml/badge.svg)](https://github.com/AndersCan/ekrooh/actions/workflows/test.yml)
9
+ [![Release](https://img.shields.io/github/v/release/AndersCan/ekrooh)](https://github.com/AndersCan/ekrooh/releases/latest)
7
10
 
8
11
  The boring bootstrap for cross-platform apps on the Bare runtime (holepunch): a
9
12
  binary wire protocol, plugin kernel, RPC messenger, transports, and native host
10
- bridges, plus a reference app. Public docs: <https://anderscan.github.io/ekrooh>.
13
+ bridges, plus a reference app.
11
14
 
12
15
  - [`vision.md`](vision.md): what this project is and is not.
13
16
  - [`AGENTS.md`](AGENTS.md): how to develop it and the canonical commands.
@@ -99,16 +102,23 @@ ephemeral port. Every request is gated by a per-session token:
99
102
 
100
103
  1. The shell injects `window.__ekrooh = { bootstrap }`, a single-use nonce (never the raw token).
101
104
  2. The page calls `POST /login` with the nonce and receives a
102
- `bare_session` cookie (`HttpOnly; SameSite=Lax; Path=/`).
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.
103
108
  3. The cookie authorizes `<img>`, `<video>`, `fetch`, and the WS upgrade.
104
109
  Non-browser clients send `X-Bare-Token`. There is no `?token=` fallback
105
110
  (see `docs/adr/0003-loopback-auth-surface.md`).
106
111
 
107
112
  Only the bootstrap mount at `/` is public pre-auth. The worklet writes
108
- `{ origin, port, token, bootstrap }` to `handoff.json` in the host's sandbox dir;
109
- the host keeps `token` and loads `http://127.0.0.1:<port>/index.html`.
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.
110
119
 
111
- The dev backend runs the same bundle with auth off on `ws://localhost:8080`.
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).
112
122
 
113
123
  ## Testing
114
124
 
@@ -154,6 +154,18 @@ declare function coerceErrorCode(code: string | undefined): ErrorCode;
154
154
  type Ok<T> = [null, T];
155
155
  type Err = [CoreError, null];
156
156
  type PluginResult<T> = Either<CoreError, T>;
157
+ /**
158
+ * Environment capabilities the message layer needs but must not read from
159
+ * globals: Bare has no Web Crypto global, so reaching for
160
+ * `globalThis.crypto` broke host invokes in consumer worklets (ekrooh#268).
161
+ * Each entry point injects its own implementation; tests override fields.
162
+ */
163
+ interface Platform {
164
+ /** Fill `bytes` with CSPRNG output and return it. Never Math.random. */
165
+ randomBytes(bytes: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>;
166
+ /** Milliseconds since the epoch. */
167
+ now(): number;
168
+ }
157
169
  type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
158
170
  type InternalDispatchRequest = (PluginDispatchHeader & {
159
171
  requestId: string;
@@ -167,7 +179,9 @@ interface ProtocolMessenger {
167
179
  invoke(request: InvokeRequest, payload?: Uint8Array | ArrayBuffer | string | null, timeoutMs?: number): Promise<MessageHeader>;
168
180
  handleIncoming(header: MessageHeader): void;
169
181
  }
170
- declare function createProtocolMessenger(send: (request: InternalDispatchRequest, payload?: Uint8Array | ArrayBuffer | string | null) => void): ProtocolMessenger;
182
+ declare function createProtocolMessenger(send: (request: InternalDispatchRequest, payload?: Uint8Array | ArrayBuffer | string | null) => void, options?: {
183
+ platform?: Partial<Platform>;
184
+ }): ProtocolMessenger;
171
185
  interface PluginRegistry {
172
186
  register(plugin: PluginManifest): void;
173
187
  resolve(pluginId: string, runtime: RuntimeTarget): PluginManifest['runtimes'][RuntimeTarget] | undefined;
@@ -332,4 +346,4 @@ type WebConsoleCapture = {
332
346
  * where there is no loopback backend to ingest into.
333
347
  */
334
348
  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 };
349
+ export { PluginManifest as $, Err as A, Either as B, createPluginRegistry as C, ProtocolMessenger as D, InvokeRequest as E, ok as F, HostInvokeResponseHeader as G, HostCapabilitiesQueryHeader as H, CapabilityDescriptor as I, PluginContext as J, InvokeEnvelope as K, CoreError as L, PluginResult as M, coerceErrorCode as N, createProtocolMessenger as O, err as P, PluginInvokeResponseHeader as Q, CoreErrorWire as R, createPluginBus as S, DispatchRequest as T, HostCapabilitiesResponseHeader as U, ErrorCodeOrString as V, HostInvokeRequestHeader as W, PluginHeader as X, PluginDispatchHeader as Y, PluginInvokeRequestHeader as Z, ProtocolOptions as _, LogStore as a, MAX_HEADER_BYTES as at, PluginRouter as b, DispatchHandler as c, VERSION as ct, definePlugin as d, PluginRuntimeAdapter as et, dispatchEvent as f, MessageProtocol as g, Encoder as h, LogSource as i, MAX_FRAME_BYTES as it, Ok as j, Platform as k, EventSpec as l, Decoder as m, LogEntry as n, WireMessage as nt, BareRuntimeContext as o, MessageType as ot, invokeEvent as p, MessageHeader as q, LogLevel as r, ErrorCode as rt, createBareRuntimeContext as s, MessageTypeValue as st, installWebConsoleCapture as t, RuntimeTarget as tt, InvokeHandler as u, PluginBus as v, createPluginRouter as w, PluginRouterOptions as x, PluginRegistry as y, DispatchEnvelope as z };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
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 };
1
+ import { $ as PluginManifest, A as Err, B as Either, C as createPluginRegistry, D as ProtocolMessenger, E as InvokeRequest, F as ok, G as HostInvokeResponseHeader, H as HostCapabilitiesQueryHeader, I as CapabilityDescriptor, J as PluginContext, K as InvokeEnvelope, L as CoreError, M as PluginResult, N as coerceErrorCode, O as createProtocolMessenger, P as err, Q as PluginInvokeResponseHeader, R as CoreErrorWire, S as createPluginBus, T as DispatchRequest, U as HostCapabilitiesResponseHeader, V as ErrorCodeOrString, W as HostInvokeRequestHeader, X as PluginHeader, Y as PluginDispatchHeader, Z as PluginInvokeRequestHeader, _ as ProtocolOptions, at as MAX_HEADER_BYTES, b as PluginRouter, c as DispatchHandler, ct as VERSION, d as definePlugin, et as PluginRuntimeAdapter, f as dispatchEvent, g as MessageProtocol, h as Encoder, it as MAX_FRAME_BYTES, j as Ok, k as Platform, l as EventSpec, m as Decoder, nt as WireMessage, o as BareRuntimeContext, ot as MessageType, p as invokeEvent, q as MessageHeader, rt as ErrorCode, s as createBareRuntimeContext, st as MessageTypeValue, t as installWebConsoleCapture, tt as RuntimeTarget, u as InvokeHandler, v as PluginBus, w as createPluginRouter, x as PluginRouterOptions, y as PluginRegistry, z as DispatchEnvelope } from "./index-Do-kTNXw.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, type Platform, 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 };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { h as CoreError, g as ErrorCode, _ as MAX_FRAME_BYTES, v as MAX_HEADER_BYTES, o as MessageProtocol, y as MessageType, b as VERSION, f as coerceErrorCode, n as createBareRuntimeContext, l as createPluginBus, u as createPluginRegistry, d as createPluginRouter, s as createProtocolMessenger, r as definePlugin, i as dispatchEvent, p as err, t as installWebConsoleCapture, a as invokeEvent, m as ok } from "./messages-BbpF3wNH.mjs";
1
+ export { h as CoreError, g as ErrorCode, _ as MAX_FRAME_BYTES, v as MAX_HEADER_BYTES, o as MessageProtocol, y as MessageType, b as VERSION, f as coerceErrorCode, n as createBareRuntimeContext, l as createPluginBus, u as createPluginRegistry, d as createPluginRouter, s as createProtocolMessenger, r as definePlugin, i as dispatchEvent, p as err, t as installWebConsoleCapture, a as invokeEvent, m as ok } from "./messages-CMNIFMFY.mjs";
@@ -204,8 +204,8 @@ function createPluginRouter(registry, runtime, options) {
204
204
  logger.error(`invoke ${header.pluginId}.${header.event} failed: ${error.message}`);
205
205
  return invokeErrorResponse(header, error);
206
206
  }
207
- if (header.requestId && options?.delegateToHost) {
208
- if (await hostAnnounces(header.pluginId, header.event)) try {
207
+ if (header.requestId && options?.delegateToHost) try {
208
+ if (await hostAnnounces(header.pluginId, header.event)) {
209
209
  const delegated = await options.delegateToHost(header, payload);
210
210
  if (delegated) {
211
211
  const code = delegated.error?.code;
@@ -213,10 +213,10 @@ function createPluginRouter(registry, runtime, options) {
213
213
  return delegated;
214
214
  }
215
215
  capsCache = null;
216
- } catch (e) {
217
- capsCache = null;
218
- return invokeErrorResponse(header, new CoreError(ErrorCode.HOST_ERROR, e instanceof Error ? e.message : String(e)));
219
216
  }
217
+ } catch (e) {
218
+ capsCache = null;
219
+ return invokeErrorResponse(header, new CoreError(ErrorCode.HOST_ERROR, e instanceof Error ? e.message : String(e)));
220
220
  }
221
221
  return invokeErrorResponse(header, new CoreError(ErrorCode.UNSUPPORTED_CAPABILITY, `Unsupported capability ${header.pluginId}.${header.event} on ${runtime}`));
222
222
  }
@@ -296,25 +296,46 @@ function isPluginInvokeResponseHeader(header) {
296
296
  /**
297
297
  * CSPRNG-backed correlation id for RPC requests.
298
298
  *
299
- * Shared by the production messenger (`rpc-messenger.ts`) and the mantaq
300
- * prototype (`rpc-messenger.mantaq.ts`) so the unguessable,
301
- * collision-resistant guarantee can never regress in a single copy. Request
299
+ * Shared by the RPC messenger (`rpc-messenger.ts`) and the host IPC bridge
300
+ * (`host-ipc.ts`) so the unguessable, collision-resistant guarantee can never
301
+ * regress in a single copy. Request
302
302
  * IDs must never be derived from `Math.random()` — a guessable id lets a peer
303
303
  * spoof or collide with an in-flight request and corrupt correlation. See
304
- * ekrooh#141.
304
+ * ekrooh#141. The random source is injected, never read from a global
305
+ * (ekrooh#268).
305
306
  */
306
- function createRequestId() {
307
- const bytes = /* @__PURE__ */ new Uint8Array(16);
308
- globalThis.crypto.getRandomValues(bytes);
309
- const rand = bytes.reduce((acc, b) => acc + b.toString(36), "").slice(0, 22);
310
- return `${Date.now().toString(36)}-${rand}`;
307
+ function createRequestId(platform) {
308
+ const rand = platform.randomBytes(/* @__PURE__ */ new Uint8Array(16)).reduce((acc, b) => acc + b.toString(36), "").slice(0, 22);
309
+ return `${platform.now().toString(36)}-${rand}`;
310
+ }
311
+ // core/messages/platform.ts
312
+ /**
313
+ * Fill in any field `override` leaves out from the Web platform (browser,
314
+ * Node). Throws when a CSPRNG is needed but the environment has none, rather
315
+ * than falling back to a guessable source (ekrooh#141).
316
+ */
317
+ function resolvePlatform(override = {}) {
318
+ return {
319
+ randomBytes: override.randomBytes ?? webRandomBytes(),
320
+ now: override.now ?? Date.now
321
+ };
322
+ }
323
+ function webRandomBytes() {
324
+ const c = globalThis.crypto;
325
+ if (typeof c?.getRandomValues !== "function") throw new Error("ekrooh: no CSPRNG in this environment; pass `platform.randomBytes`");
326
+ return (bytes) => c.getRandomValues(bytes);
311
327
  }
312
328
  // core/messages/rpc-messenger.ts
313
329
  /** Hard ceiling on concurrently in-flight invokes. Above this, the oldest call
314
330
  * is dropped (its timer cleared and promise rejected) so a flood of requests
315
331
  * cannot grow the pending map without bound. */
316
332
  const MAX_CONCURRENT_INVOKES = 1024;
317
- function createProtocolMessenger(send) {
333
+ function createProtocolMessenger(send, options = {}) {
334
+ const platform = resolvePlatform(options.platform);
335
+ const withRequestId = (request) => ({
336
+ ...request,
337
+ requestId: createRequestId(platform)
338
+ });
318
339
  const pending = /* @__PURE__ */ new Map();
319
340
  const sharedLogTail = createSharedLogTail();
320
341
  return {
@@ -383,12 +404,6 @@ function createProtocolMessenger(send) {
383
404
  }
384
405
  };
385
406
  }
386
- function withRequestId(request) {
387
- return {
388
- ...request,
389
- requestId: createRequestId()
390
- };
391
- }
392
407
  const INVOKE_TAIL_TIMEOUT_MS = 2e3;
393
408
  /** A tail fetched within this window is reused for later timeouts instead of
394
409
  * fetching again. Timeouts cluster (a stalled peer times out every pending
@@ -747,9 +762,11 @@ function dispatchEvent(spec, args, payload) {
747
762
  // core/messages/create-bare-runtime-context.ts
748
763
  function createBareRuntimeContext(plugins = [], routerOptions) {
749
764
  const runtime = "bare";
765
+ const textEncoder = new TextEncoder$1();
766
+ const textDecoder = new TextDecoder$1();
750
767
  const protocol = new MessageProtocol({
751
- encode: (str) => new TextEncoder$1().encode(str),
752
- decode: (bytes) => new TextDecoder$1().decode(bytes)
768
+ encode: (str) => textEncoder.encode(str),
769
+ decode: (bytes) => textDecoder.decode(bytes)
753
770
  });
754
771
  const pluginRegistry = createPluginRegistry();
755
772
  for (const plugin of plugins) pluginRegistry.register(plugin);
@@ -1,4 +1,4 @@
1
- import { g as ErrorCode, m as ok, p as err, r as definePlugin } from "./messages-BbpF3wNH.mjs";
1
+ import { g as ErrorCode, m as ok, p as err, r as definePlugin } from "./messages-CMNIFMFY.mjs";
2
2
  import { discoverySpecs } from "./plugins-discovery-events.mjs";
3
3
  import { healthSpecs } from "./plugins-health-events.mjs";
4
4
  import { logsSpecs } from "./plugins-logs-events.mjs";
@@ -84,6 +84,7 @@ function createLogsPlugin(deps) {
84
84
  * mounted at once and evict the least-recently-used so picked/captured files
85
85
  * do not accumulate on the loopback server for the whole session. */
86
86
  const MEDIA_MOUNT_CAP = 4;
87
+ const MEDIA_KINDS = ["image", "video"];
87
88
  function newRequestId() {
88
89
  const rand = crypto.randomBytes(16).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
89
90
  return `${Date.now().toString(36)}-${rand}`;
@@ -108,18 +109,20 @@ function createMediaPlugin(deps) {
108
109
  }
109
110
  };
110
111
  const serve = async (event, args) => {
112
+ const kind = args?.kind;
113
+ if (kind !== void 0 && !MEDIA_KINDS.includes(kind)) return err(ErrorCode.PLUGIN_ERROR, `Unknown media kind for ${event}`);
111
114
  const host = await deps.invokeOnHost({
112
115
  type: "INVOKE_REQUEST",
113
116
  pluginId: "vendor.media",
114
117
  event,
115
118
  requestId: newRequestId(),
116
- args
119
+ args: kind === void 0 ? {} : { kind }
117
120
  }, /* @__PURE__ */ new Uint8Array(0));
118
121
  if (!host) return err(ErrorCode.HOST_ERROR, `Host did not answer ${event}`);
119
122
  if (host.error) return err(host.error.code || ErrorCode.HOST_ERROR, host.error.message ?? `Host failed ${event}`);
120
123
  const path = host.result?.path;
121
124
  if (!path) return err(ErrorCode.HOST_ERROR, `Host returned no path for ${event}`);
122
- const id = `${args?.kind ?? "media"}-${newRequestId()}`;
125
+ const id = `${kind ?? "media"}-${newRequestId()}`;
123
126
  mountMedia(id, path);
124
127
  const url = await deps.staticServer.url(`/media/${id}`);
125
128
  return ok({ url });
@@ -1,4 +1,4 @@
1
- import { F as CapabilityDescriptor, G as InvokeEnvelope } from "./index-BkwUzwVt.mjs";
1
+ import { I as CapabilityDescriptor, K as InvokeEnvelope } from "./index-Do-kTNXw.mjs";
2
2
  export type DiscoveryListResult = {
3
3
  schemaVersion: 1;
4
4
  capabilities: CapabilityDescriptor[];
@@ -1,4 +1,4 @@
1
- import { a as invokeEvent } from "./messages-BbpF3wNH.mjs";
1
+ import { a as invokeEvent } from "./messages-CMNIFMFY.mjs";
2
2
  // plugins/discovery/events.ts
3
3
  const discoverySpecs = { list: {
4
4
  pluginId: "core.discovery",
@@ -1,4 +1,4 @@
1
- import { G as InvokeEnvelope } from "./index-BkwUzwVt.mjs";
1
+ import { K as InvokeEnvelope } from "./index-Do-kTNXw.mjs";
2
2
  export declare const healthSpecs: {
3
3
  readonly ping: {
4
4
  readonly pluginId: 'core.health';
@@ -1,4 +1,4 @@
1
- import { a as invokeEvent } from "./messages-BbpF3wNH.mjs";
1
+ import { a as invokeEvent } from "./messages-CMNIFMFY.mjs";
2
2
  // plugins/health/events.ts
3
3
  const healthSpecs = {
4
4
  ping: {
@@ -1,4 +1,4 @@
1
- import { G as InvokeEnvelope, i as LogSource, n as LogEntry, r as LogLevel } from "./index-BkwUzwVt.mjs";
1
+ import { K as InvokeEnvelope, i as LogSource, n as LogEntry, r as LogLevel } from "./index-Do-kTNXw.mjs";
2
2
  export type LogsViewArgs = {
3
3
  /** Most-recent N matching entries. Defaults to a small window. */
4
4
  tail?: number;
@@ -1,4 +1,4 @@
1
- import { a as invokeEvent } from "./messages-BbpF3wNH.mjs";
1
+ import { a as invokeEvent } from "./messages-CMNIFMFY.mjs";
2
2
  // plugins/logs/events.ts
3
3
  const logsSpecs = {
4
4
  view: {
@@ -1,4 +1,4 @@
1
- import { G as InvokeEnvelope } from "./index-BkwUzwVt.mjs";
1
+ import { K as InvokeEnvelope } from "./index-Do-kTNXw.mjs";
2
2
  export type MediaKind = 'image' | 'video';
3
3
  export type MediaResult = {
4
4
  /** URL served by the worklet's loopback HTTP server (no bytes on the wire). */
@@ -1,4 +1,4 @@
1
- import { a as invokeEvent } from "./messages-BbpF3wNH.mjs";
1
+ import { a as invokeEvent } from "./messages-CMNIFMFY.mjs";
2
2
  // plugins/media/events.ts
3
3
  const MEDIA_INVOKE_TIMEOUT_MS = 3e5;
4
4
  const mediaSpecs = {
@@ -1,4 +1,4 @@
1
- import { G as InvokeEnvelope } from "./index-BkwUzwVt.mjs";
1
+ import { K as InvokeEnvelope } from "./index-Do-kTNXw.mjs";
2
2
  /** Canonical permission ids. Hosts map each to their platform equivalent. */
3
3
  export type PermissionId = 'storage' | 'camera';
4
4
  export type PermissionStatus = 'granted' | 'denied' | 'notDetermined' | 'unsupported';
@@ -1,4 +1,4 @@
1
- import { a as invokeEvent } from "./messages-BbpF3wNH.mjs";
1
+ import { a as invokeEvent } from "./messages-CMNIFMFY.mjs";
2
2
  // plugins/permissions/events.ts
3
3
  const permissionSpecs = {
4
4
  request: {
@@ -1,5 +1,5 @@
1
- import { F as CapabilityDescriptor, Q as PluginManifest, X as PluginInvokeRequestHeader, Z as PluginInvokeResponseHeader, a as LogStore, et as RuntimeTarget } from "./index-BkwUzwVt.mjs";
2
- import { n as LoopbackServer } from "./static-file-server-C8pIX1JD.mjs";
1
+ import { $ as PluginManifest, I as CapabilityDescriptor, Q as PluginInvokeResponseHeader, Z as PluginInvokeRequestHeader, a as LogStore, tt as RuntimeTarget } from "./index-Do-kTNXw.mjs";
2
+ import { n as LoopbackServer } from "./static-file-server-COxd-PFR.mjs";
3
3
  type DiscoveryPluginDeps = {
4
4
  listBareCapabilities: () => Array<{
5
5
  pluginId: string;
package/dist/plugins.mjs CHANGED
@@ -1 +1 @@
1
- export { t as createDefaultPlugins } from "./plugins-DZu_NWVp.mjs";
1
+ export { t as createDefaultPlugins } from "./plugins-BnpdCi3A.mjs";
@@ -1,17 +1,21 @@
1
- import { C as createPluginRegistry, F as CapabilityDescriptor, K as MessageHeader, Q as PluginManifest, X as PluginInvokeRequestHeader, Z as PluginInvokeResponseHeader, g as MessageProtocol, o as BareRuntimeContext, tt as WireMessage, w as createPluginRouter } from "./index-BkwUzwVt.mjs";
2
- import { i as createLoopbackServer, n as LoopbackServer, r as collectRequestBody, t as LoopbackRouteHandler } from "./static-file-server-C8pIX1JD.mjs";
1
+ import { $ as PluginManifest, C as createPluginRegistry, I as CapabilityDescriptor, Q as PluginInvokeResponseHeader, Z as PluginInvokeRequestHeader, g as MessageProtocol, k as Platform, nt as WireMessage, o as BareRuntimeContext, q as MessageHeader, w as createPluginRouter } from "./index-Do-kTNXw.mjs";
2
+ import { i as createLoopbackServer, n as LoopbackServer, r as collectRequestBody, t as LoopbackRouteHandler } from "./static-file-server-COxd-PFR.mjs";
3
3
  type BareIpcLike = {
4
- write(data: Uint8Array | Buffer | string): boolean;
4
+ write(data: Uint8Array | string): boolean;
5
5
  };
6
6
  declare function createHostIpcBridge(config: {
7
7
  ipc: BareIpcLike;
8
8
  protocol: MessageProtocol;
9
+ /** Worklet entry points pass `barePlatform`; Bare has no Web Crypto global. */
10
+ platform: Platform;
9
11
  }): {
10
12
  tryConsumeDownstream: (parsed: WireMessage) => boolean;
11
- tryConsumeDownstreamFromHost(raw: Uint8Array | ArrayBuffer | Buffer): boolean;
13
+ tryConsumeDownstreamFromHost(raw: Uint8Array | ArrayBuffer): boolean;
12
14
  queryCapabilities(timeoutMs?: number): Promise<CapabilityDescriptor[]>;
13
15
  invokeOnHost(header: PluginInvokeRequestHeader, payload: Uint8Array, timeoutMs?: number): Promise<PluginInvokeResponseHeader | null>;
14
16
  };
17
+ /** Envelope encoder bound to a loopback server's connected protocol socket —
18
+ * the server-initiated (backend → web) push seam. */
15
19
  type LoopbackPush = (header: MessageHeader, payload?: Uint8Array | ArrayBuffer | null) => boolean;
16
20
  /** Builds the push seam for the server's connected protocol socket: encodes a
17
21
  * `DISPATCH`-style envelope and writes it, mirroring how `attachWebSocketProtocol`
@@ -20,8 +24,9 @@ export declare function createLoopbackPush(server: LoopbackServer, protocol: Mes
20
24
  /**
21
25
  * Attaches the framed-protocol socket to the unified loopback server. The
22
26
  * server owns the WebSocket upgrade (origin check, cookie/token auth, single
23
- * client, idle timeout); this layer only routes decoded envelopes through the
24
- * plugin router and writes responses back as raw frames.
27
+ * client, idle timeout); this layer runs one frame channel per socket. A peer
28
+ * that sends a malformed frame is not recoverable on that socket, so it is
29
+ * torn down and the client reconnects.
25
30
  */
26
31
  export declare function attachWebSocketProtocol(server: LoopbackServer, context: BareRuntimeContext): void;
27
32
  /**
@@ -29,36 +34,36 @@ export declare function attachWebSocketProtocol(server: LoopbackServer, context:
29
34
  */
30
35
  interface BareIPC extends NodeJS.EventEmitter {
31
36
  read(n?: number): Buffer | null;
32
- write(data: Buffer | string): boolean;
37
+ write(data: Uint8Array | string): boolean;
33
38
  resume?(): void;
34
39
  pause?(): void;
35
40
  }
36
41
  /**
37
- * Returns the active IPC channel.
38
- * In BareKit (Android), it uses BareKit.IPC.
42
+ * Returns the active IPC channel, or null when there is no host.
43
+ * In BareKit (Android, iOS), it uses BareKit.IPC.
39
44
  * In Bare (Sidecar), it uses Bare.IPC.
40
45
  */
41
- export declare function getIPC(): BareIPC;
46
+ export declare function getIPC(): BareIPC | null;
42
47
  /** Resolves the host-provided worklet configuration from `start(...)`
43
48
  * arguments. On-device hosts pass `["<webAssets>", "<storage>", "<cache>"]`
44
- * (three existing directories, in that order), landing at `Bare.argv[0..2]`;
45
- * the Bare CLI (dev) passes the binary and the script path instead, so a
46
- * missing/absent config means dev mode (auth off, fixed port, no handoff
47
- * file). Falls back to the labeled CLI token syntax (see `resolveCliConfig`)
48
- * when the positional shape doesn't match, so the same entry works for both
49
- * device and dev runs. */
49
+ * (three existing directories, in that order), landing at `Bare.argv[0..2]`.
50
+ * Falls back to the labeled CLI token syntax (see `resolveCliConfig`) when the
51
+ * positional shape doesn't match, so the same entry works for device and dev
52
+ * runs. Nothing here turns auth off: the dev backend is selected only by an
53
+ * explicit `mode=dev` token. */
50
54
  export declare function resolveWorkletConfig(): WorkletRuntimeOptions;
51
55
  /**
52
- * Parses the dev/CLI labeled-token configuration from bare CLI arguments:
53
- * `webassets=<dir>`, `storage=<dir>`, `cache=<dir>`, `host=<addr>`,
54
- * `port=<n>`, `auth=<on|off|true|false|1|0>` — a single `key=value` separator.
55
- * Unknown labels (e.g. `bootstrap=127.0.0.1:49737`) are ignored and stay
56
- * consumer-owned.
56
+ * Parses labeled-token configuration from bare CLI arguments, one `key=value`
57
+ * per token: `webassets=<dir>`, `storage=<dir>`, `cache=<dir>`,
58
+ * `host=<addr>`, `port=<n>`, `auth=<on|off|true|false|1|0>`,
59
+ * `mode=<device|dev>` and `origin=<scheme://host[:port]>` (repeatable; an
60
+ * extra allowed page origin, e.g. the Vite dev server). Unknown labels (e.g.
61
+ * `bootstrap=127.0.0.1:49737`) are ignored and stay consumer-owned.
57
62
  *
58
- * Returns the equivalent `WorkletRuntimeOptions` in **dev mode**: `deviceMode:
59
- * false` (so a `storage=` token does not flip auth on or make the port
60
- * ephemeral), `auth: false`, `port: 8080`, each overridable by a token. When
61
- * no labeled tokens are present it returns `{}` (plain dev defaults).
63
+ * Without `mode=dev` the result keeps the secure device defaults (auth on, an
64
+ * ephemeral port), and `auth=off` is ignored with a warning. `mode=dev` selects the dev backend: `deviceMode: false`,
65
+ * `auth: false`, `port: 8080`, each overridable by a token. Returns `{}` when
66
+ * no labeled tokens are present.
62
67
  */
63
68
  export declare function resolveCliConfig(argv?: readonly string[]): WorkletRuntimeOptions;
64
69
  export type WorkletRuntimeOptions = {
@@ -73,16 +78,21 @@ export type WorkletRuntimeOptions = {
73
78
  /** Port to bind; `0` picks an ephemeral port (on-device). Defaults to an
74
79
  * ephemeral port in device mode, 8080 in dev mode. */
75
80
  port?: number;
76
- /** Require the per-session token/cookie. Defaults to on in device mode. */
81
+ /** Require the per-session token/cookie. Defaults to on in device mode and
82
+ * off in dev mode. The origin check applies either way. */
77
83
  auth?: boolean;
78
- /** Write the credentials to `<storage>/handoff.json` for the host to read
79
- * (auth on only). Defaults to true; a host that receives them over its own
80
- * channel (the desktop host reads them from a pipe) sets false so the
81
- * session token never touches disk. */
84
+ /** Extra page origins allowed to call the loopback server, e.g. the Vite dev
85
+ * server (`http://localhost:5173`). The bound origin and its loopback
86
+ * aliases are always allowed. */
87
+ allowedOrigins?: readonly string[];
88
+ /** Write `{ origin, port, bootstrap }` to `<storage>/handoff.json` for the
89
+ * host to read (auth on only; the session token is never written). Defaults
90
+ * to true; a host that receives them over its own channel (the desktop host
91
+ * reads them from a pipe) sets false so nothing touches disk. */
82
92
  handoffFile?: boolean;
83
- /** Explicit device-mode override. Defaults to on whenever `storage` is set
84
- * (the on-device 3-arg contract); `resolveCliConfig` resolves it to false so
85
- * a dev backend keeps auth off + fixed port even with a storage dir. */
93
+ /** Device mode (the default): auth on, an ephemeral port, and the handoff
94
+ * file when `storage` is set. Pass `false` (or the `mode=dev` CLI token) for
95
+ * the dev backend: auth off and port 8080 unless set explicitly. */
86
96
  deviceMode?: boolean;
87
97
  /** Additional plugin manifests to register after the canonical defaults
88
98
  * (consumer product plugins, e.g. `app.photos`). */
@@ -93,7 +103,8 @@ export type WorkletRuntimeOptions = {
93
103
  * is destroyed, freeing the slot. Defaults to 10s. */
94
104
  wsDeadPeerTimeoutMs?: number;
95
105
  /** Minimum gap (ms) before an accepted protocol socket may be replaced by a
96
- * new authenticated one. Defaults to 1s. */
106
+ * new authenticated one; an upgrade that arrives sooner is held until then.
107
+ * Defaults to 1s. */
97
108
  wsReplaceMinIntervalMs?: number;
98
109
  /** Bytes queued behind a protocol socket whose reader stopped reading before
99
110
  * server pushes are dropped (newest first). Defaults to 2 MiB. */