@statewalker/webrun-http-browser 0.3.3 → 0.3.4

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.
Files changed (93) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +321 -51
  3. package/demo/demo-1.html +109 -194
  4. package/demo/demo-2.html +118 -299
  5. package/dist/core/data-calls.d.ts +5 -0
  6. package/dist/core/data-calls.d.ts.map +1 -0
  7. package/dist/core/data-channels.d.ts +17 -0
  8. package/dist/core/data-channels.d.ts.map +1 -0
  9. package/dist/core/index.d.ts +6 -0
  10. package/dist/core/index.d.ts.map +1 -0
  11. package/dist/core/message-target.d.ts +16 -0
  12. package/dist/core/message-target.d.ts.map +1 -0
  13. package/dist/core/registry.d.ts +18 -0
  14. package/dist/core/registry.d.ts.map +1 -0
  15. package/dist/http/http-send-recieve.d.ts +23 -0
  16. package/dist/http/http-send-recieve.d.ts.map +1 -0
  17. package/dist/http/index.d.ts +4 -0
  18. package/dist/http/index.d.ts.map +1 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +2541 -807
  22. package/dist/relay/index-sw.d.ts +7 -0
  23. package/dist/relay/index-sw.d.ts.map +1 -0
  24. package/dist/relay/index.d.ts +69 -0
  25. package/dist/relay/index.d.ts.map +1 -0
  26. package/dist/relay/split-service-url.d.ts +13 -0
  27. package/dist/relay/split-service-url.d.ts.map +1 -0
  28. package/dist/relay-sw.d.ts +2 -0
  29. package/dist/relay-sw.d.ts.map +1 -0
  30. package/dist/relay-sw.js +878 -0
  31. package/dist/sw/http-sw-dispatcher.d.ts +28 -0
  32. package/dist/sw/http-sw-dispatcher.d.ts.map +1 -0
  33. package/dist/sw/index.d.ts +3 -0
  34. package/dist/sw/index.d.ts.map +1 -0
  35. package/dist/sw/sw-dispatcher.d.ts +74 -0
  36. package/dist/sw/sw-dispatcher.d.ts.map +1 -0
  37. package/dist/sw-worker.d.ts +2 -0
  38. package/dist/sw-worker.d.ts.map +1 -0
  39. package/dist/sw-worker.js +813 -0
  40. package/dist/sw.d.ts +2 -0
  41. package/dist/sw.d.ts.map +1 -0
  42. package/dist/sw.js +1076 -0
  43. package/package.json +50 -37
  44. package/public/index.html +61 -97
  45. package/public/index.js +7 -4
  46. package/public/sw-worker.js +4 -0
  47. package/public-relay/heartbeat.js +4 -3
  48. package/public-relay/relay-sw.js +4 -6
  49. package/public-relay/relay.html +16 -11
  50. package/src/core/data-calls.ts +46 -0
  51. package/src/core/data-channels.ts +224 -0
  52. package/src/core/index.ts +9 -0
  53. package/src/core/message-target.ts +18 -0
  54. package/src/core/registry.ts +54 -0
  55. package/src/http/http-send-recieve.ts +81 -0
  56. package/src/http/index.ts +7 -0
  57. package/src/index.ts +3 -0
  58. package/src/relay/index-sw.ts +145 -0
  59. package/src/relay/index.ts +261 -0
  60. package/src/relay/split-service-url.ts +30 -0
  61. package/src/relay-sw.ts +7 -0
  62. package/src/sw/http-sw-dispatcher.ts +122 -0
  63. package/src/sw/index.ts +2 -0
  64. package/src/sw/sw-dispatcher.ts +307 -0
  65. package/src/sw-worker.ts +7 -0
  66. package/src/sw.ts +1 -0
  67. package/CHANGELOG.md +0 -1
  68. package/demo/demo-2 copy.html +0 -598
  69. package/demo/demo-3.html +0 -167
  70. package/dist/index-sw-umd.js +0 -798
  71. package/dist/index-sw-umd.min.js +0 -2
  72. package/dist/index-sw.js +0 -791
  73. package/dist/index-sw.min.js +0 -2
  74. package/dist/index-umd.js +0 -853
  75. package/dist/index-umd.min.js +0 -2
  76. package/dist/index.min.js +0 -2
  77. package/index.js +0 -1
  78. package/src/core/data-calls.js +0 -36
  79. package/src/core/data-channels.js +0 -192
  80. package/src/core/data-send-recieve.js +0 -28
  81. package/src/core/errors.js +0 -13
  82. package/src/core/index.js +0 -4
  83. package/src/http/HttpError.js +0 -59
  84. package/src/http/http-send-recieve.js +0 -40
  85. package/src/http/http-stubs.js +0 -135
  86. package/src/http/index.js +0 -3
  87. package/src/http/readable-streams.js +0 -33
  88. package/src/index.js +0 -7
  89. package/src/relay/index-sw.js +0 -216
  90. package/src/relay/index.js +0 -263
  91. package/src/relay/splitServiceUrl.js +0 -24
  92. package/src/sw/http-sw-dispatcher.js +0 -128
  93. package/src/sw/sw-dispatcher.js +0 -264
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 statewalker
3
+ Copyright (c) 2022-2026 statewalker
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,73 +1,343 @@
1
1
  # @statewalker/webrun-http-browser
2
2
 
3
- This module simulates HTTP server using Service Workers.
4
- It allows to develop, test, run and debug server-side code directly in the browser.
5
- After that the same code can be deployed in Deno / Deno Deploy / Cloudflare / Node JS environments (with adapters).
3
+ ServiceWorker-based HTTP server for browsers. You write ordinary
4
+ `(Request) ⇒ Response` handlers in JavaScript; a ServiceWorker intercepts
5
+ same-origin `fetch()` calls and routes them to your handlers — no network
6
+ round-trip, no external server, no bundler tricks required.
6
7
 
7
- ## Demo
8
+ Two modes, picked by how the SW is hosted:
8
9
 
9
- * [https://observablehq.com/@kotelnikov/webrun-http-service](https://observablehq.com/@kotelnikov/webrun-http-service) - an Observable page demonstrating how it works. You can play with the code here.
10
- * [Demo 1](./demo/demo-1.html) - a dynamic web site with a server-side API, a HTML page and a CSS file
11
- * [Demo 2](./demo/demo-2.html) - a virtual file server exposing your local disk content
10
+ - **Same-origin** (`@statewalker/webrun-http-browser/sw`) — your app
11
+ registers its own SW and mounts handlers next to the page.
12
+ - **Relay** (default entry) — a SW running at a shared relay origin
13
+ (CDN / unpkg / your own host) serves requests for any page that embeds
14
+ a hidden relay iframe. Cross-origin friendly.
12
15
 
13
- ## Features
16
+ ## Why it exists
14
17
 
15
- This module provides a lightweight full-stack development environment in the browser:
16
- * Code, execute and debug the whole stack in the browser. Even without internet connection.
17
- * Your data and code don’t leave your browser
18
- * Instant reproducable environment without installation
19
- * Easily embeddable in your code
20
- * Client and server-side code in the browser based on standards:
21
- - Use module imports for scripts
22
- - Send requests with fetch
23
- - Handle HTTP queries with the Request/Response API
18
+ The browser already has everything needed to be an HTTP server: `Request`,
19
+ `Response`, `ReadableStream`, `ServiceWorker`. Two things are missing from
20
+ the raw platform APIs, and this package fills them:
24
21
 
22
+ 1. **Plumbing for same-origin SW dispatch.** Browsers let a SW intercept
23
+ `fetch` events, but you still have to build URL routing, MessageChannel
24
+ wiring between the page and the SW, and recovery after SW restarts.
25
+ 2. **A way to use a SW from a page that isn't on the SW's origin.** The
26
+ relay mode lets *any* page (Observable, notebooks, a `file://` demo,
27
+ unpkg, a third-party host) share a SW hosted somewhere else. The page
28
+ never registers a SW of its own — it just embeds a hidden iframe.
25
29
 
26
- ## UseCases
30
+ Combining both modes means the same handler code works in an app you
31
+ control *and* in an embed you don't.
27
32
 
28
- * Create rich documentations, tutorials, demos
29
- * Embed in your rich application - in the new generation Notion, Airtable or Figma
30
- * Deliver self-contained prototype environments to your clients
33
+ ## How to use
31
34
 
32
- ## How It Works
35
+ ```sh
36
+ npm install @statewalker/webrun-http-browser
37
+ ```
38
+
39
+ | Subpath | Purpose |
40
+ | --- | --- |
41
+ | `@statewalker/webrun-http-browser` | Page-side relay API: `newRemoteRelayChannel`, `initHttpService`, `callHttpService`, `splitServiceUrl`, `initServiceWorker`, `newServiceWorkerPort`, `getRelayWindowMessageHandler`; the MessagePort call primitives (`callChannel`, `handleChannelCalls`, `newInvokationChannel`, `sendStream`, `handleStreams`, `newRegistry`); plus everything re-exported from `@statewalker/webrun-http-streams` (`HttpError`, the client/server stubs) and `@statewalker/webrun-streams` (stream and error helpers) |
42
+ | `@statewalker/webrun-http-browser/sw` | Same-origin adapter classes: `SwHttpAdapter` (page), `SwHttpDispatcher` (SW), `startHttpDispatcher` bootstrap |
43
+ | `@statewalker/webrun-http-browser/relay-sw` | IIFE bundle of the relay SW runtime — load via `importScripts` from a loader script in your relay origin |
44
+ | `@statewalker/webrun-http-browser/sw-worker` | IIFE bundle of the same-origin SW runtime — ditto, for same-origin apps |
45
+
46
+ ## Examples
47
+
48
+ > Every example below needs a real browser: a ServiceWorker, and for relay
49
+ > mode an iframe on the relay origin. None of them run under Node, and
50
+ > ServiceWorkers only register over `http://localhost` or HTTPS. The runnable
51
+ > versions are in [`public/`](./public) and [`demo/`](./demo) — see
52
+ > [Running the bundled examples](#running-the-bundled-examples).
53
+
54
+ ### Relay mode — cross-origin
55
+
56
+ Your page ↔ hidden relay iframe ↔ relay ServiceWorker. The relay SW claims
57
+ URLs shaped `<relay-origin>/~<service-key>/…` and forwards each request to
58
+ whichever page registered that `key`.
59
+
60
+ ```ts
61
+ import {
62
+ newRemoteRelayChannel,
63
+ initHttpService,
64
+ callHttpService,
65
+ } from "@statewalker/webrun-http-browser";
66
+
67
+ // 1. Embed the relay iframe and open a MessagePort into its SW.
68
+ const connection = await newRemoteRelayChannel({
69
+ url: new URL("https://my-relay.example/public-relay/relay.html"),
70
+ });
71
+
72
+ // 2. Register a handler for service "FS".
73
+ const baseUrl = `${connection.baseUrl}~FS`;
74
+ await initHttpService(
75
+ async (request) =>
76
+ new Response(`Hello ${new URL(request.url).pathname}`),
77
+ { key: "FS", port: connection.port },
78
+ );
79
+
80
+ // 3a. Any browser tab loading the service URL now hits your handler:
81
+ await fetch(`${baseUrl}/anything`);
82
+
83
+ // 3b. …or call it directly through the same port, bypassing `fetch`
84
+ // (useful when the caller isn't on the relay origin):
85
+ const res = await callHttpService(
86
+ new Request(`${baseUrl}/anything`),
87
+ { key: "FS", port: connection.port },
88
+ );
89
+ ```
90
+
91
+ [`demo/demo-1.html`](./demo/demo-1.html) wires this to a Hono router
92
+ serving a mini site; [`demo/demo-2.html`](./demo/demo-2.html) pipes a
93
+ local-disk folder (File System Access API) through it.
94
+
95
+ ### Same-origin mode
33
96
 
34
- The core of this module is based on the following native browser technologies: ServiceWorker and MessageChannels.
97
+ Your page registers its own SW, handlers are local to the page:
35
98
 
36
- A ServiceWorker is used as the "server", intercepting HTTP calls and delegating their handling to registered modules via MessageChannels.
37
- So in the same browser-based application you can register a standard HTTP endpoint and call it.
99
+ ```ts
100
+ import { SwHttpAdapter } from "@statewalker/webrun-http-browser/sw";
101
+
102
+ const KEY = "demo"; // also the first URL segment the SW routes here
103
+ const adapter = new SwHttpAdapter({
104
+ key: KEY,
105
+ serviceWorkerUrl: new URL("./sw-worker.js", import.meta.url).toString(),
106
+ });
107
+ await adapter.start();
108
+
109
+ const { baseUrl } = await adapter.register(`${KEY}/api/`, async (request) => {
110
+ return new Response(JSON.stringify({ now: Date.now() }), {
111
+ headers: { "Content-Type": "application/json" },
112
+ });
113
+ });
114
+
115
+ // fetch(`${baseUrl}anything`) is intercepted by the SW.
116
+ ```
117
+
118
+ The SW script itself ships as a pre-built IIFE bundle. Put a tiny loader
119
+ next to your app pages so the SW's default scope covers them:
38
120
 
39
- Example:
40
121
  ```js
41
- import { httpService, endpointUrl } from "...";
122
+ // public/sw-worker.js — served next to your app pages.
123
+ importScripts(
124
+ "/path/to/node_modules/@statewalker/webrun-http-browser/dist/sw-worker.js",
125
+ );
126
+ ```
127
+
128
+ The working example lives in [`public/`](./public).
129
+
130
+ ### Running the bundled examples
42
131
 
43
- // Server-side code:
44
- httpService.register(async (request) => { // request: Request
45
- return new Response("Hello, world!", {
46
- headers: {
47
- "Content-Type": "text/plain"
48
- }
49
- })
50
- })
132
+ ```sh
133
+ pnpm run example:same-origin # public/index.html — same-origin SW demo
134
+ pnpm run example:relay-site # demo/demo-1.html — relay + Hono dynamic site
135
+ pnpm run example:relay-files # demo/demo-2.html — relay + local-disk file server
136
+ pnpm run serve # just a static server on :5173 (no auto-open)
137
+ ```
138
+
139
+ Each `example:*` script builds first, starts a static server on `:5173`,
140
+ then opens the target page in the default browser. ServiceWorkers only
141
+ register over `http://localhost` or HTTPS, so always visit through
142
+ `http://localhost:5173/…` — `file://` won't work.
143
+
144
+ #### [`public/index.html`](./public/index.html) — minimal same-origin SW
145
+
146
+ The smallest possible in-browser HTTP server. The page registers
147
+ `public/sw-worker.js` (which `importScripts`es the shipped
148
+ `dist/sw-worker.js`), constructs a `SwHttpAdapter` with key `"demo"`,
149
+ and registers a single handler at `demo/api/` that returns JSON. The
150
+ page then makes a standard `fetch(baseUrl + "anything")` and logs the
151
+ result.
152
+
153
+ Why it's interesting:
154
+
155
+ - **No framework, no glue, ~40 lines of inline JS.** This is the
156
+ unwrapped pattern — everything
157
+ [`@statewalker/webrun-site-host`](../webrun-site-host) and
158
+ [`@statewalker/webrun-site-builder`](../webrun-site-builder) build on
159
+ top of. Useful as a reference for exactly what the SW lifecycle
160
+ looks like at its lowest level.
161
+ - **Shows the SW-routing contract.** The adapter's `key: "demo"` is
162
+ the first URL segment the SW uses to find this page's registration;
163
+ `adapter.register(\`${KEY}/api/\`, ...)` mounts the handler prefix
164
+ under the same key. The mapping is visible and inspectable — great
165
+ for debugging your own SW-based code.
166
+
167
+ #### [`demo/demo-1.html`](./demo/demo-1.html) — relay + Hono dynamic site
168
+
169
+ A full-blown mini web site running in a single tab, behind the
170
+ **relay** ServiceWorker. The page spins up a Hono router with a
171
+ `/api/:name` endpoint and a static-file catch-all, registers it as
172
+ service `MY_SITE`, and embeds the service root in an iframe. Inside
173
+ the iframe, typing into an input fires `fetch("./api/" + name)` and
174
+ renders the JSON response — the whole back-end is the Hono app
175
+ running in the outer tab.
176
+
177
+ Why it's interesting:
178
+
179
+ - **An entire web framework running client-side.** Hono is a normal
180
+ Node/Deno/CF-Workers framework — here it's loaded from esm.sh and
181
+ mounted inside the browser with no server involvement. The
182
+ `(Request) ⇒ Response` contract makes this transparent.
183
+ - **Relay mode = cross-origin friendly.** Because the SW lives at the
184
+ relay origin (not the page's origin), this pattern also works when
185
+ your page is served from Observable, unpkg, a notebook, or a static
186
+ `file://` — places where registering your own SW isn't possible.
187
+ The hidden relay iframe does the SW registration on your behalf.
188
+ - **Two ways to call the service.** The iframe uses plain `fetch()`
189
+ through the SW; any other browser tab pointing at
190
+ `<relay-origin>/~MY_SITE/...` is also routed to this tab's Hono
191
+ app. Demonstrates that the page hosting the handler and the caller
192
+ don't have to share an origin.
193
+
194
+ #### [`demo/demo-2.html`](./demo/demo-2.html) — FS Access API folder as a site
51
195
 
52
- // Client-side code:
196
+ Click **Open folder**, grant read access, and any directory on your
197
+ local disk is exposed as an in-browser HTTP site under
198
+ `<relay-origin>/~FS/…`. The left panel shows a live file tree; clicking
199
+ a file loads it in the iframe preview. The service handler is a ~20-line
200
+ function that resolves paths via
201
+ [`FileSystemDirectoryHandle.getFileHandle`](https://developer.mozilla.org/docs/Web/API/FileSystemDirectoryHandle/getFileHandle)
202
+ and streams the file's bytes back through the SW.
53
203
 
54
- const res = await fetch(endpointUrl);
55
- const text = await res.text();
56
- console.log(text);
204
+ Why it's interesting:
205
+
206
+ - **Zero installs, real files.** Browse arbitrary directories as if
207
+ they were hosted — open a local project's `index.html` and it just
208
+ runs. Relative URLs inside the hosted files resolve correctly because
209
+ the SW serves every asset, CSS, and JS under the same origin.
210
+ - **Permissioned + sandboxed.** The browser's File System Access API
211
+ provides the "backend" (read permission granted per-folder by the
212
+ user); the relay SW provides the "network". You get the ergonomics
213
+ of a local HTTP dev server without running one.
214
+ - **Directory picker + request router in <100 lines.** No build step,
215
+ no tooling. Shows how small the glue between a platform API and a
216
+ `(Request) ⇒ Response` handler can be.
217
+
218
+ ## Internals
219
+
220
+ ### Source layout
57
221
 
58
222
  ```
223
+ src/
224
+ ├── core/ ┐
225
+ │ ├── data-calls.ts │ Transport primitives over a
226
+ │ ├── data-channels.ts │ `MessageTarget`: one-shot
227
+ │ ├── message-target.ts │ `callChannel` / `handleChannelCalls`,
228
+ │ └── registry.ts │ the request/response
229
+ │ │ `newInvokationChannel`, streaming
230
+ │ │ `sendStream` / `handleStreams` with
231
+ │ │ backpressure, and `newRegistry`.
232
+ │ │ Also re-exports
233
+ │ │ `@statewalker/webrun-streams`.
234
+ │ ┘
235
+ ├── http/ ┐
236
+ │ ├── http-send-recieve.ts │ Browser-specific HTTP transport:
237
+ │ │ │ `handleHttpRequests` /
238
+ │ │ │ `sendHttpRequest` over `MessageTarget`s,
239
+ │ │ │ built on the client/server stubs.
240
+ │ └── index.ts │ Re-exports
241
+ │ ┘ `@statewalker/webrun-http-streams`.
242
+ ├── sw/ ┐
243
+ │ ├── sw-dispatcher.ts │ Same-origin mode:
244
+ │ │ │ `SwPortHandler` (page) /
245
+ │ │ │ `SwPortDispatcher` (SW side,
246
+ │ │ │ IndexedDB-persisted client index).
247
+ │ ├── http-sw-dispatcher.ts │ `SwHttpAdapter` /
248
+ │ │ │ `SwHttpDispatcher` /
249
+ │ └── index.ts │ `startHttpDispatcher`.
250
+ │ ┘
251
+ ├── relay/ ┐
252
+ │ ├── index.ts │ Relay mode page-side:
253
+ │ │ │ `newRemoteRelayChannel`,
254
+ │ │ │ `initHttpService`,
255
+ │ │ │ `callHttpService`,
256
+ │ │ │ `getRelayWindowMessageHandler`.
257
+ │ ├── index-sw.ts │ `startRelayServiceWorker` — the SW
258
+ │ │ │ side (registry keyed by service key).
259
+ │ └── split-service-url.ts │ `<base>/~<key>/<path>` parser.
260
+ │ ┘
261
+ ├── index.ts — public entry: core + http + relay.
262
+ ├── sw.ts — `./sw` subpath entry.
263
+ ├── relay-sw.ts — relay SW bootstrap (IIFE target).
264
+ └── sw-worker.ts — same-origin SW bootstrap (IIFE target).
265
+ ```
266
+
267
+ ### Design notes
268
+
269
+ - **Two SW strategies**. Same-origin mode needs the SW to be served next
270
+ to the app (scope-rooted loader); relay mode puts the SW anywhere and
271
+ ferries messages through an iframe, at the cost of a `CONNECT`
272
+ round-trip per call. Pick the stricter mode when you own the origin.
273
+ - **Adapter key = URL segment**. For the same-origin path, the adapter's
274
+ `key` option **must match** the first URL segment the SW routes to it:
275
+ if `key: "demo"` and the SW scope is `/public/`, handlers answer at
276
+ `/public/demo/…`. The SW extracts the segment from the URL and looks up
277
+ `handlersIndex` by key. This is why
278
+ `adapter.register(\`${KEY}/api/\`, …)` prefixes the registration path
279
+ with the same key.
280
+ - **IIFE for SW bundles**. The SW runtime bundles (`relay-sw.js`,
281
+ `sw-worker.js`) are IIFE rather than ESM so a classic
282
+ `importScripts(...)` loader script can pull them in. Registering as
283
+ `{ type: "module" }` SWs would work but is subject to
284
+ `Service-Worker-Allowed` header games for a scope wider than the
285
+ bundle's directory.
286
+ - **ESM page-side bundles are self-contained**. `dist/index.js` and
287
+ `dist/sw.js` inline their dependencies (`idb-keyval`,
288
+ `@statewalker/webrun-http-streams`, `@statewalker/webrun-streams`) so a
289
+ page can load them straight from a static host without a bundler or
290
+ import map.
291
+ - **Streaming uses `newAsyncGenerator`** (from `@statewalker/webrun-streams`,
292
+ via `recieveIterator`). The queue-based async generator gives explicit
293
+ backpressure — each `next(value)` returns a `Promise<boolean>` that resolves
294
+ once the consumer has dequeued — and drains in-flight producers on consumer
295
+ exit.
296
+ - **SW client registry is IndexedDB-persisted**. Both `SwPortDispatcher`
297
+ (same-origin) and `relay/index-sw.ts` keep their client-lookup tables in
298
+ IndexedDB so a SW wake-up after idle doesn't lose its bindings.
299
+
300
+ ### Constraints
301
+
302
+ - **Request bodies are buffered on Firefox.** The stubs this package uses
303
+ (`newHttpClientStub` / `newHttpServerStub` from
304
+ [`@statewalker/webrun-http-streams`](../webrun-http-streams)) stream request
305
+ bodies wherever the runtime implements `Request.prototype.body`. Firefox
306
+ does not (checked against 146), so on that browser the whole request body is
307
+ buffered into memory on both the sending and the receiving side — a large
308
+ upload is a proportionally large allocation, and a handler that wanted to
309
+ stream its request body cannot. Response streaming is unaffected on every
310
+ browser. See that package's README for the details and for the Safari
311
+ caveat.
312
+ - **ServiceWorker scope rules apply.** A SW registered at `/public/sw-worker.js`
313
+ only controls pages and fetches under `/public/`. If you need a broader
314
+ scope, the SW script must be served with the
315
+ `Service-Worker-Allowed` HTTP header, *or* live higher in the origin.
316
+ - **`http://localhost` or HTTPS only.** Browsers refuse to register SWs
317
+ on other `http://` origins.
318
+ - **Relay mode needs an iframe-capable sandbox.** Pages with strict CSP
319
+ that blocks `frame-src` to the relay origin can't use the relay path.
320
+ - **Consumer-side `fetch()` only works from pages under the SW's scope.**
321
+ When your caller is on another origin, use `callHttpService(request,
322
+ …)` — it reaches the SW through the iframe's MessagePort and bypasses
323
+ the browser's fetch routing.
324
+
325
+ ### Dependencies
326
+
327
+ Runtime:
328
+
329
+ - `@statewalker/webrun-http-streams` — the `newHttpClientStub` /
330
+ `newHttpServerStub` pair this package's MessagePort transport is built
331
+ on, plus `HttpError`. Workspace-local.
332
+ - `@statewalker/webrun-streams` — iterator/stream primitives and
333
+ serialisable errors. Workspace-local.
334
+ - `idb-keyval` — tiny (<1 KB) IndexedDB KV used by both SW modes to keep
335
+ client/service registrations across SW restarts.
59
336
 
60
- ## They Play Well Together...
337
+ Dev: TypeScript, vitest, rolldown, rimraf, `http-server` (for the
338
+ `example:*` scripts), `@types/node` (catalog versions from the monorepo
339
+ root).
61
340
 
62
- This in-browser HTTP Server allows to implement the following functionalities:
63
- - Serve your content from your Local Disk:
64
- - using [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API)
65
- - using [Origin Private File System API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API)
66
- - Add version control with Git - using [IsomorphicGit](https://isomorphic-git.org/)
67
- - Provide direct P2P sharing with others – via [WebRTC](https://webrtc.org/)
68
- - Implement client/server applications using persistent SQLite on Origin Private File System - using [SQLite Wasm](https://developer.chrome.com/blog/sqlite-wasm-in-the-browser-backed-by-the-origin-private-file-system/)
69
- - Deploy your local site on Edge - via [Deno Deploy](https://deno.com/deploy)
70
- - Distribute your work in any browser via IPFS / [LibP2P](https://github.com/libp2p/js-libp2p)
71
- - Integration with existing powerful APIs like https://trpc.io/
341
+ ## License
72
342
 
73
- ...and everything in the browser!
343
+ MIT © statewalker