@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.
- package/LICENSE +1 -1
- package/README.md +321 -51
- package/demo/demo-1.html +109 -194
- package/demo/demo-2.html +118 -299
- package/dist/core/data-calls.d.ts +5 -0
- package/dist/core/data-calls.d.ts.map +1 -0
- package/dist/core/data-channels.d.ts +17 -0
- package/dist/core/data-channels.d.ts.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/message-target.d.ts +16 -0
- package/dist/core/message-target.d.ts.map +1 -0
- package/dist/core/registry.d.ts +18 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/http/http-send-recieve.d.ts +23 -0
- package/dist/http/http-send-recieve.d.ts.map +1 -0
- package/dist/http/index.d.ts +4 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2541 -807
- package/dist/relay/index-sw.d.ts +7 -0
- package/dist/relay/index-sw.d.ts.map +1 -0
- package/dist/relay/index.d.ts +69 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/split-service-url.d.ts +13 -0
- package/dist/relay/split-service-url.d.ts.map +1 -0
- package/dist/relay-sw.d.ts +2 -0
- package/dist/relay-sw.d.ts.map +1 -0
- package/dist/relay-sw.js +878 -0
- package/dist/sw/http-sw-dispatcher.d.ts +28 -0
- package/dist/sw/http-sw-dispatcher.d.ts.map +1 -0
- package/dist/sw/index.d.ts +3 -0
- package/dist/sw/index.d.ts.map +1 -0
- package/dist/sw/sw-dispatcher.d.ts +74 -0
- package/dist/sw/sw-dispatcher.d.ts.map +1 -0
- package/dist/sw-worker.d.ts +2 -0
- package/dist/sw-worker.d.ts.map +1 -0
- package/dist/sw-worker.js +813 -0
- package/dist/sw.d.ts +2 -0
- package/dist/sw.d.ts.map +1 -0
- package/dist/sw.js +1076 -0
- package/package.json +50 -37
- package/public/index.html +61 -97
- package/public/index.js +7 -4
- package/public/sw-worker.js +4 -0
- package/public-relay/heartbeat.js +4 -3
- package/public-relay/relay-sw.js +4 -6
- package/public-relay/relay.html +16 -11
- package/src/core/data-calls.ts +46 -0
- package/src/core/data-channels.ts +224 -0
- package/src/core/index.ts +9 -0
- package/src/core/message-target.ts +18 -0
- package/src/core/registry.ts +54 -0
- package/src/http/http-send-recieve.ts +81 -0
- package/src/http/index.ts +7 -0
- package/src/index.ts +3 -0
- package/src/relay/index-sw.ts +145 -0
- package/src/relay/index.ts +261 -0
- package/src/relay/split-service-url.ts +30 -0
- package/src/relay-sw.ts +7 -0
- package/src/sw/http-sw-dispatcher.ts +122 -0
- package/src/sw/index.ts +2 -0
- package/src/sw/sw-dispatcher.ts +307 -0
- package/src/sw-worker.ts +7 -0
- package/src/sw.ts +1 -0
- package/CHANGELOG.md +0 -1
- package/demo/demo-2 copy.html +0 -598
- package/demo/demo-3.html +0 -167
- package/dist/index-sw-umd.js +0 -798
- package/dist/index-sw-umd.min.js +0 -2
- package/dist/index-sw.js +0 -791
- package/dist/index-sw.min.js +0 -2
- package/dist/index-umd.js +0 -853
- package/dist/index-umd.min.js +0 -2
- package/dist/index.min.js +0 -2
- package/index.js +0 -1
- package/src/core/data-calls.js +0 -36
- package/src/core/data-channels.js +0 -192
- package/src/core/data-send-recieve.js +0 -28
- package/src/core/errors.js +0 -13
- package/src/core/index.js +0 -4
- package/src/http/HttpError.js +0 -59
- package/src/http/http-send-recieve.js +0 -40
- package/src/http/http-stubs.js +0 -135
- package/src/http/index.js +0 -3
- package/src/http/readable-streams.js +0 -33
- package/src/index.js +0 -7
- package/src/relay/index-sw.js +0 -216
- package/src/relay/index.js +0 -263
- package/src/relay/splitServiceUrl.js +0 -24
- package/src/sw/http-sw-dispatcher.js +0 -128
- package/src/sw/sw-dispatcher.js +0 -264
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,73 +1,343 @@
|
|
|
1
1
|
# @statewalker/webrun-http-browser
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
8
|
+
Two modes, picked by how the SW is hosted:
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
##
|
|
16
|
+
## Why it exists
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
+
Your page registers its own SW, handlers are local to the page:
|
|
35
98
|
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
343
|
+
MIT © statewalker
|