@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.
@@ -1,40 +1,47 @@
1
1
  import { HTTPIncomingMessage, HTTPServerResponse } from "bare-http1";
2
- /** Accumulates an HTTP request body into a UTF-8 string. Node http emits
3
- * Buffers, bare-http1 emits Uint8Arrays, and a setEncoding'd stream emits
4
- * strings — `String(uint8array)` would join bytes with commas, so decode
5
- * explicitly (the `/login` token body must match byte-for-byte). */
6
- declare function collectRequestBody(req: HTTPIncomingMessage, maxBytes?: number): Promise<string>;
2
+ import "bare-tcp";
3
+ /** The protocol socket as the routing layer sees it. */
7
4
  type WebSocketLike = {
8
5
  on(event: 'data', listener: (chunk: unknown) => void): unknown;
9
6
  on(event: 'close', listener: () => void): unknown;
10
7
  on(event: 'drain', listener: () => void): unknown;
11
8
  on(event: 'error', listener: (err: Error) => void): unknown;
12
- write(data: unknown): boolean;
9
+ write(data: Uint8Array): boolean;
13
10
  destroy(): void;
14
11
  /** Stops/restarts `data` events (inbound backpressure). */
15
12
  pause?(): void;
16
13
  resume?(): void;
17
14
  };
18
- /** Handler for a registered HTTP route on the loopback server. */
19
- type LoopbackRouteHandler = (req: HTTPIncomingMessage, res: HTTPServerResponse) => void;
20
- /** Minimal shape of the HTTP request object handed to WS upgrade handlers. */
15
+ /** Minimal shape of the HTTP request object handed to connection handlers. */
21
16
  type LoopbackRequest = {
22
17
  url?: string;
23
18
  method?: string;
24
- headers: Record<string, string | string[] | number>;
19
+ headers: Record<string, string | string[] | number | undefined>;
25
20
  };
26
21
  type LoopbackConnectionHandler = (socket: WebSocketLike, request: LoopbackRequest) => void;
22
+ /** Accumulates an HTTP request body into a UTF-8 string. Node http emits
23
+ * Buffers, bare-http1 emits Uint8Arrays, and a setEncoding'd stream emits
24
+ * strings — `String(uint8array)` would join bytes with commas, so decode
25
+ * explicitly (the `/login` token body must match byte-for-byte). */
26
+ declare function collectRequestBody(req: HTTPIncomingMessage, maxBytes?: number): Promise<string>;
27
+ /** Handler for a registered HTTP route on the loopback server. */
28
+ type LoopbackRouteHandler = (req: HTTPIncomingMessage, res: HTTPServerResponse) => void;
27
29
  interface LoopbackServerOptions {
28
- /** Bind address. Defaults to `127.0.0.1`. */
30
+ /** Bind address. Defaults to `127.0.0.1`. A non-loopback address throws
31
+ * unless `auth` is on. */
29
32
  host?: string;
30
33
  /** Port to bind; `0` picks an ephemeral port (on-device). */
31
34
  port?: number;
32
- /** Require the per-session token/cookie on every request and WS upgrade.
33
- * The dev backend turns this off (the browser cannot know the token). */
35
+ /** Require the per-session token/cookie on every gated request and WS
36
+ * upgrade. Defaults to on; only the dev backend turns it off. The origin
37
+ * check applies either way. */
34
38
  auth?: boolean;
35
- /** Fixed session token (dev/tests); otherwise a random 32-byte token is
36
- * generated on first bind. */
39
+ /** Fixed session token (tests); otherwise a random 32-byte token. */
37
40
  token?: string;
41
+ /** Extra page origins allowed to call the server, e.g. the Vite dev server
42
+ * (`http://localhost:5173`). The bound origin and its loopback aliases
43
+ * (`127.0.0.1`, `localhost`, `[::1]` on the bound port) are always allowed. */
44
+ allowedOrigins?: readonly string[];
38
45
  /** Idle timeout (ms) after which an authenticated WebSocket connection is
39
46
  * dropped. Defaults to 60s. */
40
47
  wsIdleTimeoutMs?: number;
@@ -45,8 +52,8 @@ interface LoopbackServerOptions {
45
52
  * application message. Defaults to 10s. */
46
53
  wsDeadPeerTimeoutMs?: number;
47
54
  /** Minimum gap, in ms, between an accepted protocol socket and the next one
48
- * that may replace it. Bounds eviction churn if two live clients compete.
49
- * Defaults to 1000. */
55
+ * that may replace it; an upgrade that arrives sooner is held until then.
56
+ * Bounds eviction churn if two live clients compete. Defaults to 1000. */
50
57
  wsReplaceMinIntervalMs?: number;
51
58
  /** Most bytes queued behind a protocol socket whose reader has stopped reading
52
59
  * before server pushes are dropped, newest first. Responses are queued past
@@ -59,15 +66,15 @@ interface LoopbackServer {
59
66
  * first use. */
60
67
  origin(): Promise<string>;
61
68
  /** Resolves to a URL for `path` (`<origin><path>`). The per-session cookie
62
- * authorizes media loads; no token is embedded in the URL anymore. */
69
+ * authorizes media loads; no token is embedded in the URL. */
63
70
  url(path: string): Promise<string>;
64
- /** The per-session token (generated on first bind). The host reads it from
65
- * `credentials()` for its own IPC needs — never injects it into the page. */
71
+ /** The per-session token. The host reads it from `credentials()` for its own
72
+ * `X-Bare-Token` calls and never injects it into the page. */
66
73
  token(): string;
67
- /** Resolves to the bound origin, port, token and a one-time bootstrap nonce —
68
- * written to the handoff file for the host to read before loading the page.
69
- * The host injects `bootstrap` into the page (not the token); the page
70
- * exchanges it once via `POST /login` for the HttpOnly session cookie. */
74
+ /** Resolves to the bound origin, port, token and the one-time bootstrap
75
+ * nonce (empty once spent). The host injects `bootstrap` into the page (not
76
+ * the token); the page exchanges it once via `POST /login` for the HttpOnly
77
+ * session cookie. */
71
78
  credentials(): Promise<{
72
79
  origin: string;
73
80
  port: number;
@@ -78,10 +85,12 @@ interface LoopbackServer {
78
85
  mount(path: string, filePath: string): void;
79
86
  unmount(path: string): void;
80
87
  /** Serves every file under `dirPath` below `prefix` (longest-prefix match).
81
- * The SPA fallback to `dirPath/index.html` for unknown nave GETs applies
82
- * only to `public` mounts (the bootstrap web app). Pass `{ public: true }`
83
- * to mark a mount as world-readable bootstrap content served before auth; a
84
- * non-public directory mount gates every request behind the session. */
88
+ * The SPA fallback to `dirPath/index.html` for unknown navigation GETs
89
+ * applies only to `public` mounts (the bootstrap web app). Pass
90
+ * `{ public: true }` to mark a mount as world-readable bootstrap content
91
+ * served before auth; a non-public directory mount gates every request
92
+ * behind the session. A request never resolves outside `dirPath`, symlinks
93
+ * included. */
85
94
  mountDir(prefix: string, dirPath: string, opts?: {
86
95
  public?: boolean;
87
96
  }): void;
@@ -102,14 +111,10 @@ interface LoopbackServer {
102
111
  * The single loopback HTTP+WS server for the worklet. One origin serves the
103
112
  * web app (`/`), the media files mounted by plugins, and the framed-protocol
104
113
  * WebSocket socket the page connects to. On device it binds `127.0.0.1` on an
105
- * ephemeral port. Only the explicit **bootstrap** directory mount (`/`, marked
106
- * `{ public: true }`) is world-readable before auth — a fresh WebView must load
107
- * the page before it can `POST /login`. Mounted session files (media) and the
108
- * WS upgrade are gated by a per-session token, accepted as a `bare_session`
109
- * cookie (set via `POST /login` with the one-time bootstrap nonce or the token)
110
- * or the `X-Bare-Token` header (non-browser clients). The URL `?token=` query
111
- * param is **not** accepted — a token must never appear in a URL an observer
112
- * could read. All responses send `Referrer-Policy: no-referrer`.
114
+ * ephemeral port. Admission (origin rule, session credential, bootstrap login)
115
+ * is decided by the loopback gate (`loopback-gate.ts`); the protocol socket's
116
+ * lifecycle by the protocol socket slot (`protocol-socket.ts`). All responses
117
+ * send `Referrer-Policy: no-referrer`.
113
118
  */
114
119
  declare function createLoopbackServer(options?: LoopbackServerOptions): LoopbackServer;
115
120
  export { createLoopbackServer as i, LoopbackServer as n, collectRequestBody as r, LoopbackRouteHandler as t };
@@ -1,16 +1,14 @@
1
- import { K as MessageHeader, ot as MessageTypeValue, tt as WireMessage } from "./index-BkwUzwVt.mjs";
1
+ import { nt as WireMessage, q as MessageHeader, st as MessageTypeValue } from "./index-Do-kTNXw.mjs";
2
2
  declare global {
3
3
  interface Window {
4
4
  /** Per-session bridge state injected by the embedding shell on-device.
5
5
  * `bootstrap` is a one-time nonce the page exchanges for a session cookie
6
6
  * via `POST /login` (single-use — the raw session token is never exposed
7
- * to page JS). `token` remains as a legacy login fallback for consumers
8
- * that still inject it. The cookie then rides every same-origin request —
7
+ * to page JS). The cookie then rides every same-origin request —
9
8
  * including the WebSocket upgrade. Browser dev has none (the dev backend
10
9
  * runs without auth). */
11
10
  __ekrooh?: {
12
11
  bootstrap?: string;
13
- token?: string;
14
12
  [key: string]: unknown;
15
13
  };
16
14
  /** Set by the embedding shell on-device. */
@@ -21,8 +19,8 @@ interface CreateWebSocketTransportOptions {
21
19
  /** WebSocket URL. Defaults to the page's same-origin (`ws://location.host`).
22
20
  * Browser dev passes `VITE_BARE_WS_URL` explicitly. */
23
21
  url?: string;
24
- /** Whether to bootstrap the session cookie via `POST /login` when a token
25
- * is present. Defaults to `true`. */
22
+ /** Whether to bootstrap the session cookie via `POST /login` when a
23
+ * bootstrap nonce is present. Defaults to `true`. */
26
24
  login?: boolean;
27
25
  /** Reconnect attempts after an unexpected close (boot-window). Default 5. */
28
26
  maxRetries?: number;
@@ -1,4 +1,4 @@
1
- import { g as ErrorCode, o as MessageProtocol, y as MessageType } from "./messages-BbpF3wNH.mjs";
1
+ import { g as ErrorCode, o as MessageProtocol, y as MessageType } from "./messages-CMNIFMFY.mjs";
2
2
  import { t as createLogRingBuffer } from "./store-DTg1jN5E.mjs";
3
3
  import { Actor, RealClock, event, state } from "@mantaq/core";
4
4
  // web/connection-machine.ts
@@ -163,8 +163,7 @@ function createWebSocketTransport(urlOrOptions = {}) {
163
163
  const maxRetries = options.maxRetries ?? DEFAULT_RETRIES;
164
164
  const initialBackoff = options.backoffMs ?? DEFAULT_BACKOFF_MS;
165
165
  const loginTimeoutMs = options.loginTimeoutMs ?? LOGIN_TIMEOUT_MS;
166
- const bridge = typeof window !== "undefined" ? window.__ekrooh : void 0;
167
- const credential = bridge?.bootstrap ?? bridge?.token;
166
+ const credential = (typeof window !== "undefined" ? window.__ekrooh : void 0)?.bootstrap;
168
167
  const shouldLogin = options.login !== false && typeof credential === "string" && credential.length > 0;
169
168
  const listeners = /* @__PURE__ */ new Set();
170
169
  const queued = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekrooh/bare",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "One soul, many platforms \u2014 the boring bootstrap for cross-platform apps on the Bare runtime: wire protocol, plugin kernel, RPC messenger, transports, and native host bridges.",
6
6
  "license": "MIT",
@@ -93,7 +93,6 @@
93
93
  },
94
94
  "dependencies": {
95
95
  "@mantaq/core": "^0.2.0",
96
- "@mantaq/sugar": "^0.3.0",
97
96
  "bare-crypto": "^1.15.3",
98
97
  "bare-encoding": "^1.0.3",
99
98
  "bare-env": "^3.0.0",