@ekrooh/bare 0.1.0 → 0.2.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.
@@ -1,5 +1,5 @@
1
1
  import { A as CapabilityDescriptor, G as PluginInvokeRequestHeader, K as PluginInvokeResponseHeader, Y as RuntimeTarget, q as PluginManifest } from "./index-DjulYFJL.mjs";
2
- import { t as LoopbackServer } from "./static-file-server-CxeI_mQr.mjs";
2
+ import { n as LoopbackServer } from "./static-file-server-DY9EKEVf.mjs";
3
3
  //#region plugins/discovery/plugin.d.ts
4
4
  type DiscoveryPluginDeps = {
5
5
  listBareCapabilities: () => Array<{
@@ -1,5 +1,5 @@
1
- import { A as CapabilityDescriptor, G as PluginInvokeRequestHeader, K as PluginInvokeResponseHeader, d as MessageProtocol, t as BareRuntimeContext, v as createPluginRegistry, y as createPluginRouter } from "./index-DjulYFJL.mjs";
2
- import { n as createLoopbackServer, t as LoopbackServer } from "./static-file-server-CxeI_mQr.mjs";
1
+ import { A as CapabilityDescriptor, G as PluginInvokeRequestHeader, K as PluginInvokeResponseHeader, V as MessageHeader, d as MessageProtocol, q as PluginManifest, t as BareRuntimeContext, v as createPluginRegistry, y as createPluginRouter } from "./index-DjulYFJL.mjs";
2
+ import { i as createLoopbackServer, n as LoopbackServer, r as collectRequestBody, t as LoopbackRouteHandler } from "./static-file-server-DY9EKEVf.mjs";
3
3
  //#region core/messages/host-ipc.d.ts
4
4
  type BareIpcLike = {
5
5
  write(data: Uint8Array | Buffer | string): boolean;
@@ -14,6 +14,13 @@ declare function createHostIpcBridge(config: {
14
14
  };
15
15
  //#endregion
16
16
  //#region core/server/websocket-server.d.ts
17
+ /** Envelope encoder bound to a loopback server's connected protocol socket —
18
+ * the server-initiated (backend → web) push seam. */
19
+ type LoopbackPush = (header: MessageHeader, payload?: Uint8Array | ArrayBuffer | null) => boolean;
20
+ /** Builds the push seam for the server's connected protocol socket: encodes a
21
+ * `DISPATCH`-style envelope and writes it, mirroring how `attachWebSocketProtocol`
22
+ * writes responses. Returns false when no socket is connected. */
23
+ declare function createLoopbackPush(server: LoopbackServer, protocol: MessageProtocol): LoopbackPush;
17
24
  /**
18
25
  * Attaches the framed-protocol socket to the unified loopback server. The
19
26
  * server owns the WebSocket upgrade (origin check, cookie/token auth, single
@@ -60,6 +67,9 @@ type WorkletRuntimeOptions = {
60
67
  port?: number;
61
68
  /** Require the per-session token/cookie. Defaults to on in device mode. */
62
69
  auth?: boolean;
70
+ /** Additional plugin manifests to register after the canonical defaults
71
+ * (consumer product plugins, e.g. `app.photos`). */
72
+ plugins?: PluginManifest[];
63
73
  /** Idle timeout (ms) after which an authenticated WebSocket is dropped. */
64
74
  wsIdleTimeoutMs?: number;
65
75
  };
@@ -93,4 +103,4 @@ interface WorkletRuntime {
93
103
  */
94
104
  declare function createWorkletRuntime(options?: WorkletRuntimeOptions): WorkletRuntime;
95
105
  //#endregion
96
- export { type LoopbackServer, WorkletRuntime, WorkletRuntimeConfig, WorkletRuntimeOptions, attachWebSocketProtocol, createLoopbackServer, createWorkletRuntime, getIPC, resolveWorkletConfig };
106
+ export { type LoopbackPush, type LoopbackRouteHandler, type LoopbackServer, WorkletRuntime, WorkletRuntimeConfig, WorkletRuntimeOptions, attachWebSocketProtocol, collectRequestBody, createLoopbackPush, createLoopbackServer, createWorkletRuntime, getIPC, resolveWorkletConfig };
package/dist/runtime.mjs CHANGED
@@ -13,7 +13,7 @@ import crypto from "bare-crypto";
13
13
  * In Bare (Sidecar), it uses Bare.IPC.
14
14
  */
15
15
  function getIPC() {
16
- const IPC = typeof BareKit !== "undefined" ? BareKit.IPC : Bare.IPC;
16
+ const IPC = typeof BareKit !== "undefined" ? BareKit.IPC : typeof Bare !== "undefined" ? Bare.IPC : void 0;
17
17
  if (IPC && typeof IPC.resume === "function") IPC.resume();
18
18
  return IPC;
19
19
  }
@@ -219,6 +219,7 @@ function createLoopbackServer(options = {}) {
219
219
  const wsIdleTimeoutMs = options.wsIdleTimeoutMs ?? 6e4;
220
220
  const fileMounts = /* @__PURE__ */ new Map();
221
221
  const dirMounts = /* @__PURE__ */ new Map();
222
+ const routeHandlers = /* @__PURE__ */ new Map();
222
223
  const connectionHandlers = [];
223
224
  let token = options.token ?? null;
224
225
  let boundOrigin = "";
@@ -375,6 +376,19 @@ function createLoopbackServer(options = {}) {
375
376
  writeError(res, 400, "Bad request");
376
377
  return;
377
378
  }
379
+ const route = routeHandlers.get(`${req.method} ${cleanPath}`);
380
+ if (route) {
381
+ if (!isAuthorized(headers, query)) {
382
+ writeError(res, 401, "Unauthorized");
383
+ return;
384
+ }
385
+ try {
386
+ route(req, res);
387
+ } catch (err) {
388
+ writeError(res, 500, err instanceof Error ? err.message : String(err));
389
+ }
390
+ return;
391
+ }
378
392
  const mount = resolveMount(cleanPath);
379
393
  if (!(mount !== null && mount.kind === "dir") && !isAuthorized(headers, query)) {
380
394
  writeError(res, 401, "Unauthorized");
@@ -471,6 +485,18 @@ function createLoopbackServer(options = {}) {
471
485
  onConnection(handler) {
472
486
  connectionHandlers.push(handler);
473
487
  },
488
+ registerRoute(method, path, handler) {
489
+ routeHandlers.set(`${method.toUpperCase()} ${path}`, handler);
490
+ },
491
+ push(frame) {
492
+ if (!activeSocket) return false;
493
+ try {
494
+ activeSocket.write(frame);
495
+ return true;
496
+ } catch {
497
+ return false;
498
+ }
499
+ },
474
500
  close(cb) {
475
501
  if (activeSocket) try {
476
502
  activeSocket.destroy();
@@ -486,6 +512,15 @@ function toUint8Array$1(data) {
486
512
  if (data instanceof ArrayBuffer) return new Uint8Array(data);
487
513
  throw new Error("Expected Uint8Array or ArrayBuffer from socket data");
488
514
  }
515
+ /** Builds the push seam for the server's connected protocol socket: encodes a
516
+ * `DISPATCH`-style envelope and writes it, mirroring how `attachWebSocketProtocol`
517
+ * writes responses. Returns false when no socket is connected. */
518
+ function createLoopbackPush(server, protocol) {
519
+ return (header, payload = null) => {
520
+ const frame = protocol.encode(MessageType.ENVELOPE, header, payload);
521
+ return server.push(frame);
522
+ };
523
+ }
489
524
  /**
490
525
  * Attaches the framed-protocol socket to the unified loopback server. The
491
526
  * server owns the WebSocket upgrade (origin check, cookie/token auth, single
@@ -582,6 +617,7 @@ function createWorkletRuntime(options = {}) {
582
617
  staticServer: server,
583
618
  invokeOnHost: (header, payload) => hostBridge?.invokeOnHost(header, payload) ?? Promise.resolve(null)
584
619
  })) pluginRegistry.register(plugin);
620
+ for (const plugin of options.plugins ?? []) pluginRegistry.register(plugin);
585
621
  const pluginRouter = createPluginRouter(pluginRegistry, "bare", { delegateToHost: (header, payload) => hostBridge?.invokeOnHost(header, payload) ?? Promise.resolve(null) });
586
622
  if (ipc) ipc.on("data", async (data) => {
587
623
  try {
@@ -607,7 +643,7 @@ function createWorkletRuntime(options = {}) {
607
643
  pluginRouter
608
644
  });
609
645
  const credentials = await server.credentials();
610
- if (deviceMode && options.storage) try {
646
+ if (auth && options.storage) try {
611
647
  fs.writeFileSync(path.join(options.storage, "handoff.json"), JSON.stringify(credentials));
612
648
  } catch (err) {
613
649
  const message = err instanceof Error ? err.message : String(err);
@@ -634,4 +670,4 @@ function createWorkletRuntime(options = {}) {
634
670
  };
635
671
  }
636
672
  //#endregion
637
- export { attachWebSocketProtocol, createLoopbackServer, createWorkletRuntime, getIPC, resolveWorkletConfig };
673
+ export { attachWebSocketProtocol, collectRequestBody, createLoopbackPush, createLoopbackServer, createWorkletRuntime, getIPC, resolveWorkletConfig };
@@ -1,5 +1,10 @@
1
- import "bare-http1";
1
+ import { HTTPIncomingMessage, HTTPServerResponse } from "bare-http1";
2
2
  //#region core/server/static-file-server.d.ts
3
+ /** Accumulates an HTTP request body into a UTF-8 string. Node http emits
4
+ * Buffers, bare-http1 emits Uint8Arrays, and a setEncoding'd stream emits
5
+ * strings — `String(uint8array)` would join bytes with commas, so decode
6
+ * explicitly (the `/login` token body must match byte-for-byte). */
7
+ declare function collectRequestBody(req: HTTPIncomingMessage): Promise<string>;
3
8
  type WebSocketLike = {
4
9
  on(event: 'data', listener: (chunk: unknown) => void): unknown;
5
10
  on(event: 'close', listener: () => void): unknown;
@@ -7,6 +12,8 @@ type WebSocketLike = {
7
12
  write(data: unknown): boolean;
8
13
  destroy(): void;
9
14
  };
15
+ /** Handler for a registered HTTP route on the loopback server. */
16
+ type LoopbackRouteHandler = (req: HTTPIncomingMessage, res: HTTPServerResponse) => void;
10
17
  /** Minimal shape of the HTTP request object handed to WS upgrade handlers. */
11
18
  type LoopbackRequest = {
12
19
  url?: string;
@@ -54,6 +61,13 @@ interface LoopbackServer {
54
61
  /** Registers a handler for authenticated, handshaken WebSocket connections
55
62
  * (the protocol socket). */
56
63
  onConnection(handler: LoopbackConnectionHandler): void;
64
+ /** Registers an HTTP route. `handler` runs before mount resolution, after
65
+ * the auth gate (device mode requires the session cookie/token). The route
66
+ * body is read with `collectRequestBody`. */
67
+ registerRoute(method: string, path: string, handler: LoopbackRouteHandler): void;
68
+ /** Writes a raw frame to the connected protocol socket (if any) — the
69
+ * server-initiated push seam (e.g. a `photos.changed` dispatch). */
70
+ push(frame: Uint8Array): boolean;
57
71
  /** Stops accepting connections and closes the server. */
58
72
  close(cb?: (err?: Error | null) => void): void;
59
73
  }
@@ -71,4 +85,4 @@ interface LoopbackServer {
71
85
  */
72
86
  declare function createLoopbackServer(options?: LoopbackServerOptions): LoopbackServer;
73
87
  //#endregion
74
- export { createLoopbackServer as n, LoopbackServer as t };
88
+ export { createLoopbackServer as i, LoopbackServer as n, collectRequestBody as r, LoopbackRouteHandler as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekrooh/bare",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "One soul, many platforms — 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",
@@ -10,7 +10,8 @@
10
10
  },
11
11
  "workspaces": [
12
12
  "examples/*",
13
- "e2e"
13
+ "e2e",
14
+ "apps/*"
14
15
  ],
15
16
  "exports": {
16
17
  ".": "./dist/index.mjs",
@@ -30,6 +31,10 @@
30
31
  "scripts": {
31
32
  "check": "vp check",
32
33
  "lint": "vp fmt . --fix",
34
+ "docs:dev": "vp run docs#dev",
35
+ "docs:build": "vp run docs#build",
36
+ "docs:preview": "vp run docs#preview",
37
+ "docs:check": "node scripts/docs-check.mjs",
33
38
  "typecheck": "tsc --noEmit",
34
39
  "test": "vp test && npm run test:e2e:web && npm run test:e2e:harness",
35
40
  "test:unit": "vp test",