@statewalker/webrun-http-browser 0.3.4 → 0.5.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.
- package/README.md +164 -6
- package/dist/core/data-channels.d.ts.map +1 -1
- package/dist/core/deadline.d.ts +7 -0
- package/dist/core/deadline.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/message-target.d.ts +1 -15
- package/dist/core/message-target.d.ts.map +1 -1
- package/dist/core/service-worker-control.d.ts +72 -0
- package/dist/core/service-worker-control.d.ts.map +1 -0
- package/dist/http/http-send-recieve.d.ts +27 -14
- package/dist/http/http-send-recieve.d.ts.map +1 -1
- package/dist/index.js +485 -167
- package/dist/relay/index-sw.d.ts.map +1 -1
- package/dist/relay/index.d.ts +23 -6
- package/dist/relay/index.d.ts.map +1 -1
- package/dist/relay-sw.js +864 -65
- package/dist/sw/sw-dispatcher.d.ts +22 -0
- package/dist/sw/sw-dispatcher.d.ts.map +1 -1
- package/dist/sw-worker.js +869 -64
- package/dist/sw.js +1096 -106
- package/package.json +8 -3
- package/src/core/data-channels.ts +42 -4
- package/src/core/deadline.ts +31 -0
- package/src/core/index.ts +1 -0
- package/src/core/message-target.ts +8 -18
- package/src/core/service-worker-control.ts +243 -0
- package/src/http/http-send-recieve.ts +27 -14
- package/src/relay/index-sw.ts +5 -0
- package/src/relay/index.ts +65 -47
- package/src/sw/sw-dispatcher.ts +98 -46
package/README.md
CHANGED
|
@@ -30,6 +30,15 @@ the raw platform APIs, and this package fills them:
|
|
|
30
30
|
Combining both modes means the same handler code works in an app you
|
|
31
31
|
control *and* in an embed you don't.
|
|
32
32
|
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install @statewalker/webrun-http-browser
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Browser-only — it needs `navigator.serviceWorker`, so a secure context
|
|
40
|
+
(`https://` or `localhost`) is required. No peer dependencies.
|
|
41
|
+
|
|
33
42
|
## How to use
|
|
34
43
|
|
|
35
44
|
```sh
|
|
@@ -38,8 +47,8 @@ npm install @statewalker/webrun-http-browser
|
|
|
38
47
|
|
|
39
48
|
| Subpath | Purpose |
|
|
40
49
|
| --- | --- |
|
|
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)
|
|
42
|
-
| `@statewalker/webrun-http-browser/sw` | Same-origin adapter classes: `SwHttpAdapter` (page), `SwHttpDispatcher` (SW), `startHttpDispatcher` bootstrap |
|
|
50
|
+
| `@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), `@statewalker/webrun-streams` (stream and error helpers) and the `MessageTarget` family from `@statewalker/webrun-rpc` |
|
|
51
|
+
| `@statewalker/webrun-http-browser/sw` | Same-origin adapter classes: `SwHttpAdapter` (page), `SwHttpDispatcher` (SW), `startHttpDispatcher` bootstrap; `start()` options `timeout` and `reloadIfUncontrolled` |
|
|
43
52
|
| `@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
53
|
| `@statewalker/webrun-http-browser/sw-worker` | IIFE bundle of the same-origin SW runtime — ditto, for same-origin apps |
|
|
45
54
|
|
|
@@ -115,6 +124,45 @@ const { baseUrl } = await adapter.register(`${KEY}/api/`, async (request) => {
|
|
|
115
124
|
// fetch(`${baseUrl}anything`) is intercepted by the SW.
|
|
116
125
|
```
|
|
117
126
|
|
|
127
|
+
`start()` resolves once the worker is activated **and controls the page** —
|
|
128
|
+
only a controlled page's `fetch()` reaches the worker. Two options bound it:
|
|
129
|
+
|
|
130
|
+
| Option | Default | Meaning |
|
|
131
|
+
| --- | --- | --- |
|
|
132
|
+
| `timeout` | `30_000` | Upper bound, in ms, for the wait for the worker to activate, take control and answer the adapter's handshake. Past it `start()` rejects with a `ServiceWorkerControlError` (see `reason`) instead of waiting forever. |
|
|
133
|
+
| `reloadIfUncontrolled` | `false` | If the page is still uncontrolled once the worker is active, reload it once instead of rejecting (see below). |
|
|
134
|
+
|
|
135
|
+
#### When the page is not controlled
|
|
136
|
+
|
|
137
|
+
A page can load **uncontrolled although its worker is active**: a hard reload
|
|
138
|
+
(Ctrl+Shift+R / Cmd+Shift+R) bypasses ServiceWorkers for that load, and the
|
|
139
|
+
worker's `clients.claim()` already ran when it activated, so nothing ever
|
|
140
|
+
hands the page to it. (Firefox has also been seen leaving a second page of a
|
|
141
|
+
running worker uncontrolled.) `start()` then asks the worker to claim the page
|
|
142
|
+
again — a `CLAIM` call this package's workers answer with `clients.claim()` —
|
|
143
|
+
and waits for `controllerchange`. That takes the page over in Chromium and
|
|
144
|
+
Firefox, both verified by the browser tests.
|
|
145
|
+
|
|
146
|
+
If it still is not controlled (a worker that does not answer `CLAIM`, a page
|
|
147
|
+
outside the worker's scope), `start()` rejects with a
|
|
148
|
+
`ServiceWorkerControlError` whose `reason` is `"uncontrolled"` and whose message
|
|
149
|
+
says what happened and what to do. With `reloadIfUncontrolled: true` it reloads
|
|
150
|
+
the page instead — a normal reload is a controlled navigation — at most once:
|
|
151
|
+
a `sessionStorage` marker makes a second uncontrolled load reject rather than
|
|
152
|
+
loop. After a failure, calling `start()` again retries.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
try {
|
|
156
|
+
await adapter.start();
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if ((error as Error).name === "ServiceWorkerControlError") showReloadPrompt();
|
|
159
|
+
else throw error;
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Check `name` or `reason`, not `instanceof`: each bundle of this package carries
|
|
164
|
+
its own copy of the class.
|
|
165
|
+
|
|
118
166
|
The SW script itself ships as a pre-built IIFE bundle. Put a tiny loader
|
|
119
167
|
next to your app pages so the SW's default scope covers them:
|
|
120
168
|
|
|
@@ -215,6 +263,87 @@ Why it's interesting:
|
|
|
215
263
|
no tooling. Shows how small the glue between a platform API and a
|
|
216
264
|
`(Request) ⇒ Response` handler can be.
|
|
217
265
|
|
|
266
|
+
## Exports
|
|
267
|
+
|
|
268
|
+
The package root re-exports everything from
|
|
269
|
+
[`@statewalker/webrun-streams`](../webrun-streams) and
|
|
270
|
+
[`@statewalker/webrun-http-streams`](../webrun-http-streams), so existing
|
|
271
|
+
imports keep working after those extractions. Its own surface is below.
|
|
272
|
+
|
|
273
|
+
### Relay mode
|
|
274
|
+
|
|
275
|
+
| Export | Kind | Purpose |
|
|
276
|
+
| --- | --- | --- |
|
|
277
|
+
| `newRemoteRelayChannel(opts?)` | function | Embeds the hidden relay iframe, handshakes a `MessageChannel`, resolves a `RemoteRelayChannel`. |
|
|
278
|
+
| `RemoteRelayChannelOptions` | interface | `baseUrl`, `url`, `container` — where the relay lives and what to append the iframe to. |
|
|
279
|
+
| `RemoteRelayChannel` | interface | `{ baseUrl, port, close() }`. |
|
|
280
|
+
| `initHttpService(handler, opts)` | function | Registers `handler` as the server for a service `key` on the relay. Returns a cleanup. |
|
|
281
|
+
| `callHttpService(request, opts)` | function | Sends a `Request` to the service under `key`; resolves its `Response`. |
|
|
282
|
+
| `ServiceOptions` | interface | `{ key: string; port: MessageTarget }` — shared by the two above. |
|
|
283
|
+
| `getRelayWindowMessageHandler(opts?)` | function | The `window.onmessage` handler that runs *inside* the relay iframe. |
|
|
284
|
+
| `RelayWindowHandlerOptions` | interface | `swUrl`, `scopeUrl` for that handler. |
|
|
285
|
+
| `splitServiceUrl(url, separator?)` | function | Splits a relay URL into service key + remaining path (default separator `~`). |
|
|
286
|
+
| `SplitServiceUrl` | interface | Its result shape. |
|
|
287
|
+
|
|
288
|
+
### ServiceWorker lifecycle
|
|
289
|
+
|
|
290
|
+
| Export | Kind | Purpose |
|
|
291
|
+
| --- | --- | --- |
|
|
292
|
+
| `initServiceWorker(opts)` | function | Registers a SW and resolves with it once it is activated: the controller, or the registration's active worker when the page is not controlled (the relay only needs to message it). Rejects with a `ServiceWorkerControlError` past `timeout`. |
|
|
293
|
+
| `InitServiceWorkerOptions` | interface | `{ swUrl, scopeUrl?, type?, timeout? }`. |
|
|
294
|
+
| `newServiceWorkerPort(registration?)` | function | A `MessagePort` that transparently bridges to the controlling SW, or to `registration.active` while the page is not controlled. |
|
|
295
|
+
| `awaitActiveServiceWorker(registration, opts?)` | function | Resolves with the registration's worker once `activated`; rejects (`reason: "activation-timeout"`) past `timeout`. |
|
|
296
|
+
| `awaitServiceWorkerControl(registration, opts?)` | function | Resolves with the controller once the page is controlled, asking the worker to claim an uncontrolled page; bounded by `timeout`, optional `reloadIfUncontrolled`. What `SwHttpAdapter.start()` uses. |
|
|
297
|
+
| `handleClaimRequests(self)` | function | Worker side: answers the page's `CLAIM` call with `clients.claim()`. Both of this package's workers install it; use it in a worker of your own. |
|
|
298
|
+
| `ServiceWorkerControlError` | class | `name: "ServiceWorkerControlError"`, `reason`: `"activation-timeout"` (worker did not activate in time), `"uncontrolled"` (active, but the page is not controlled), `"unresponsive"` (controls the page, did not answer `SwHttpAdapter`'s handshake). |
|
|
299
|
+
| `DEFAULT_SERVICE_WORKER_TIMEOUT` / `CLAIM_CALL` | const | `30_000` ms / `"CLAIM"`. |
|
|
300
|
+
|
|
301
|
+
### Connection registry
|
|
302
|
+
|
|
303
|
+
| Export | Kind | Purpose |
|
|
304
|
+
| --- | --- | --- |
|
|
305
|
+
| `initializeConnection(opts)` | function | Sends `CONNECT` for a service `key`; resolves a `MessagePort`, or `null` if no such service. |
|
|
306
|
+
| `InitializeConnectionOptions` | interface | `{ key, communicationPort, ...extra }` — extra fields ride along in the CONNECT payload. |
|
|
307
|
+
| `registerConnectionsHandler(opts)` | function | Registers a `key` and answers inbound `CONNECT`s. Returns a cleanup that unregisters. |
|
|
308
|
+
| `RegisterConnectionsHandlerOptions` | interface | `{ key, handler, communicationPort }`. |
|
|
309
|
+
|
|
310
|
+
### Messaging primitives
|
|
311
|
+
|
|
312
|
+
| Export | Kind | Purpose |
|
|
313
|
+
| --- | --- | --- |
|
|
314
|
+
| `callChannel(target, type, data, port?)` | function | One typed request/response over a `MessageTarget`. |
|
|
315
|
+
| `handleChannelCalls(target, type, handler)` | function | Answer those calls. Returns an unsubscribe. |
|
|
316
|
+
| `ChannelCallHandler` | type | The handler signature the two above exchange. |
|
|
317
|
+
| `newInvokationChannel(opts)` | function | Multiplexed invocations over one target. |
|
|
318
|
+
| `InvocationChannel` / `NewInvocationChannelOptions` | interface | Its result and options. |
|
|
319
|
+
| `handleStreams(...)` / `StreamHandler<T>` | function / type | Stream-shaped invocations over the same channel. |
|
|
320
|
+
|
|
321
|
+
> **These stream primitives have no backpressure.** `sendStream`'s chunk sender
|
|
322
|
+
> discards the promise it is handed, so a fast producer over a slow consumer
|
|
323
|
+
> accumulates without bound; there is also no per-stream timeout and no chunking
|
|
324
|
+
> to a transport's message ceiling. `@statewalker/webrun-rpc`'s `duplexOverPort`
|
|
325
|
+
> is the replacement — one `Duplex` over one port, with the confirmation
|
|
326
|
+
> withheld until the consumer has pulled. Migrating this package onto it is
|
|
327
|
+
> planned, not done.
|
|
328
|
+
| `MessageTarget` / `MessageSource` / `MessageSink` / `MessageListener` | interface / type | The structural port view everything above accepts — a `MessagePort`, a `Worker`, or a SW bridge. Defined in [`@statewalker/webrun-streams`](../webrun-streams) and re-exported here. |
|
|
329
|
+
| `newRegistry(onError?)` | function | Small cleanup registry used for teardown. |
|
|
330
|
+
| `Registry` / `NewRegistryResult` / `CleanupAction` | interface / type | Its shapes. |
|
|
331
|
+
|
|
332
|
+
### HTTP over a port
|
|
333
|
+
|
|
334
|
+
| Export | Kind | Purpose |
|
|
335
|
+
| --- | --- | --- |
|
|
336
|
+
| `sendHttpRequest(port, request)` | function | **Deprecated.** Ship a `Request` over a `MessageTarget`, await the `Response`. |
|
|
337
|
+
| `handleHttpRequests(port, handler)` | function | **Deprecated.** Serve an `HttpHandler` on the other end of one. |
|
|
338
|
+
|
|
339
|
+
### Subpath entry points
|
|
340
|
+
|
|
341
|
+
| Entry | Purpose |
|
|
342
|
+
| --- | --- |
|
|
343
|
+
| `@statewalker/webrun-http-browser/sw` | `SwHttpAdapter` — the same-origin ServiceWorker adapter. |
|
|
344
|
+
| `@statewalker/webrun-http-browser/relay-sw` | IIFE relay SW runtime, loadable via `importScripts(...)`. |
|
|
345
|
+
| `@statewalker/webrun-http-browser/sw-worker` | IIFE same-origin SW runtime, loadable via `importScripts(...)`. |
|
|
346
|
+
|
|
218
347
|
## Internals
|
|
219
348
|
|
|
220
349
|
### Source layout
|
|
@@ -225,10 +354,14 @@ src/
|
|
|
225
354
|
│ ├── data-calls.ts │ Transport primitives over a
|
|
226
355
|
│ ├── data-channels.ts │ `MessageTarget`: one-shot
|
|
227
356
|
│ ├── message-target.ts │ `callChannel` / `handleChannelCalls`,
|
|
228
|
-
│
|
|
357
|
+
│ ├── registry.ts │ the request/response
|
|
229
358
|
│ │ `newInvokationChannel`, streaming
|
|
230
|
-
│
|
|
359
|
+
│ └── service-worker-control.ts │ `sendStream` / `handleStreams` with
|
|
231
360
|
│ │ backpressure, and `newRegistry`.
|
|
361
|
+
│ │ Bounded waits for a worker to
|
|
362
|
+
│ │ activate / take control, and the
|
|
363
|
+
│ │ `CLAIM` request that takes over an
|
|
364
|
+
│ │ uncontrolled page.
|
|
232
365
|
│ │ Also re-exports
|
|
233
366
|
│ │ `@statewalker/webrun-streams`.
|
|
234
367
|
│ ┘
|
|
@@ -293,6 +426,17 @@ src/
|
|
|
293
426
|
backpressure — each `next(value)` returns a `Promise<boolean>` that resolves
|
|
294
427
|
once the consumer has dequeued — and drains in-flight producers on consumer
|
|
295
428
|
exit.
|
|
429
|
+
- **No literal `new URL("…", import.meta.url)` in page-side code**.
|
|
430
|
+
Bundlers (Vite among them) turn that pattern into an emitted asset at build
|
|
431
|
+
time, before tree-shaking; the relay defaults resolved `"../"` that way,
|
|
432
|
+
which is the package itself, so every Vite consumer shipped a dead copy of
|
|
433
|
+
`dist/index.js`. They resolve against a `moduleUrl` variable instead, and
|
|
434
|
+
`tests/dist/vite-consumer.dist.ts` builds a Vite app to prove nothing is
|
|
435
|
+
emitted.
|
|
436
|
+
- **Uncontrolled pages are handled per mode**. Same-origin mode needs
|
|
437
|
+
control — an uncontrolled page's `fetch()` never reaches the worker — so it
|
|
438
|
+
asks the worker to claim the page. Relay mode needs only a worker to message,
|
|
439
|
+
so an uncontrolled relay page bridges to `registration.active`.
|
|
296
440
|
- **SW client registry is IndexedDB-persisted**. Both `SwPortDispatcher`
|
|
297
441
|
(same-origin) and `relay/index-sw.ts` keep their client-lookup tables in
|
|
298
442
|
IndexedDB so a SW wake-up after idle doesn't lose its bindings.
|
|
@@ -313,6 +457,11 @@ src/
|
|
|
313
457
|
only controls pages and fetches under `/public/`. If you need a broader
|
|
314
458
|
scope, the SW script must be served with the
|
|
315
459
|
`Service-Worker-Allowed` HTTP header, *or* live higher in the origin.
|
|
460
|
+
- **A hard reload loads the page without its worker.** Handled — see
|
|
461
|
+
[When the page is not controlled](#when-the-page-is-not-controlled) — as long
|
|
462
|
+
as the worker answers `CLAIM`. A worker script of your own that
|
|
463
|
+
`importScripts` this package's `sw-worker.js` or `relay-sw.js` does; one
|
|
464
|
+
that does not should call `handleClaimRequests(self)`.
|
|
316
465
|
- **`http://localhost` or HTTPS only.** Browsers refuse to register SWs
|
|
317
466
|
on other `http://` origins.
|
|
318
467
|
- **Relay mode needs an iframe-capable sandbox.** Pages with strict CSP
|
|
@@ -336,8 +485,17 @@ Runtime:
|
|
|
336
485
|
|
|
337
486
|
Dev: TypeScript, vitest, rolldown, rimraf, `http-server` (for the
|
|
338
487
|
`example:*` scripts), `@types/node` (catalog versions from the monorepo
|
|
339
|
-
root).
|
|
488
|
+
root), `playwright` and `vite` (for `test:browser`).
|
|
489
|
+
|
|
490
|
+
## Tests
|
|
491
|
+
|
|
492
|
+
- `pnpm test` — unit tests under Node, against the source.
|
|
493
|
+
- `pnpm test:browser` — builds, then runs `tests/browser/` in real Chromium and
|
|
494
|
+
Firefox through Playwright against the built bundles (first visit, normal
|
|
495
|
+
reload, hard reload, second tab, the relay page, and the timeout and
|
|
496
|
+
uncontrolled errors), plus `tests/dist/`, which builds a Vite consumer of
|
|
497
|
+
the bundles. Needs the browsers: `npx playwright install chromium firefox`.
|
|
340
498
|
|
|
341
499
|
## License
|
|
342
500
|
|
|
343
|
-
MIT © statewalker
|
|
501
|
+
MIT © statewalker — see [LICENSE](../../LICENSE).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-channels.d.ts","sourceRoot":"","sources":["../../src/core/data-channels.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAqBzD,MAAM,WAAW,iBAAiB;IAChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClF;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC;IACjE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;CAC1B;AAED,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,OAEC,EACD,OAAuB,EACvB,SAAuC,GACxC,EAAE,2BAA2B,GAAG,iBAAiB,CAoEjD;AAED,wBAAuB,UAAU,CAAC,CAAC,EACjC,iBAAiB,EAAE,aAAa,EAChC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"data-channels.d.ts","sourceRoot":"","sources":["../../src/core/data-channels.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAqBzD,MAAM,WAAW,iBAAiB;IAChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClF;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC;IACjE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;CAC1B;AAED,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,OAEC,EACD,OAAuB,EACvB,SAAuC,GACxC,EAAE,2BAA2B,GAAG,iBAAiB,CAoEjD;AAED,wBAAuB,UAAU,CAAC,CAAC,EACjC,iBAAiB,EAAE,aAAa,EAChC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAmBlC;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAC7B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AAElD,wBAAgB,aAAa,CAAC,CAAC,EAC7B,iBAAiB,EAAE,aAAa,EAChC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,MAAM,IAAI,CAqBZ"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settles like `promise` if it settles before `deadline` (a `Date.now()`
|
|
3
|
+
* value). Otherwise calls `onTimeout`: an `Error` it returns rejects, any
|
|
4
|
+
* other value resolves. Internal; not re-exported.
|
|
5
|
+
*/
|
|
6
|
+
export declare function withDeadline<T, F>(deadline: number, promise: Promise<T>, onTimeout: () => F): Promise<T | (F extends Error ? never : F)>;
|
|
7
|
+
//# sourceMappingURL=deadline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadline.d.ts","sourceRoot":"","sources":["../../src/core/deadline.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,CAAC,GACjB,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAqB5C"}
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAGA,cAAc,6BAA6B,CAAC;AAE5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAGA,cAAc,6BAA6B,CAAC;AAE5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,6BAA6B,CAAC"}
|
|
@@ -1,16 +1,2 @@
|
|
|
1
|
-
export type MessageListener
|
|
2
|
-
/** An object we can listen for `"message"` events on. */
|
|
3
|
-
export interface MessageSource {
|
|
4
|
-
addEventListener(type: "message", listener: MessageListener): void;
|
|
5
|
-
removeEventListener(type: "message", listener: MessageListener): void;
|
|
6
|
-
start?(): void | Promise<void>;
|
|
7
|
-
}
|
|
8
|
-
/** An object we can post messages to (with optional transferable list). */
|
|
9
|
-
export interface MessageSink {
|
|
10
|
-
postMessage(message: unknown, transfer?: Transferable[]): void;
|
|
11
|
-
}
|
|
12
|
-
/** Full-duplex message target: both sends and receives. */
|
|
13
|
-
export interface MessageTarget extends MessageSource, MessageSink {
|
|
14
|
-
close?(): void | Promise<void>;
|
|
15
|
-
}
|
|
1
|
+
export type { MessageListener, MessageSink, MessageSource, MessageTarget, } from "@statewalker/webrun-rpc";
|
|
16
2
|
//# sourceMappingURL=message-target.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-target.d.ts","sourceRoot":"","sources":["../../src/core/message-target.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"message-target.d.ts","sourceRoot":"","sources":["../../src/core/message-target.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,aAAa,GACd,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long, by default, the page waits for its ServiceWorker to activate and
|
|
3
|
+
* to take control before giving up. Generous: a first install downloads and
|
|
4
|
+
* evaluates the worker script, which on a slow link takes seconds.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_SERVICE_WORKER_TIMEOUT = 30000;
|
|
7
|
+
/** Channel call a page sends to ask its ServiceWorker to `clients.claim()` it. */
|
|
8
|
+
export declare const CLAIM_CALL = "CLAIM";
|
|
9
|
+
/**
|
|
10
|
+
* - `activation-timeout`: the worker did not activate in time.
|
|
11
|
+
* - `uncontrolled`: the worker is active but the page is not controlled by it.
|
|
12
|
+
* - `unresponsive`: the page is controlled, but the worker did not answer the
|
|
13
|
+
* adapter's handshake in time.
|
|
14
|
+
*/
|
|
15
|
+
export type ServiceWorkerControlFailure = "activation-timeout" | "uncontrolled" | "unresponsive";
|
|
16
|
+
/**
|
|
17
|
+
* Why a page could not get a working ServiceWorker. `reason` says which wait
|
|
18
|
+
* failed; check it (or `name`) rather than `instanceof`, because each of this
|
|
19
|
+
* package's bundles carries its own copy of this class.
|
|
20
|
+
*/
|
|
21
|
+
export declare class ServiceWorkerControlError extends Error {
|
|
22
|
+
readonly reason: ServiceWorkerControlFailure;
|
|
23
|
+
constructor(reason: ServiceWorkerControlFailure, message: string);
|
|
24
|
+
}
|
|
25
|
+
export interface AwaitServiceWorkerOptions {
|
|
26
|
+
/** Upper bound for the whole wait, in ms. Default `DEFAULT_SERVICE_WORKER_TIMEOUT`. */
|
|
27
|
+
timeout?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface AwaitServiceWorkerControlOptions extends AwaitServiceWorkerOptions {
|
|
30
|
+
/**
|
|
31
|
+
* When the page is still uncontrolled after asking the worker to claim it,
|
|
32
|
+
* reload the page once instead of rejecting. A normal reload is a
|
|
33
|
+
* navigation, and navigations are controlled. Guarded by `sessionStorage`
|
|
34
|
+
* so it never loops: if the reloaded page is uncontrolled too, it rejects.
|
|
35
|
+
* Default `false`.
|
|
36
|
+
*/
|
|
37
|
+
reloadIfUncontrolled?: boolean;
|
|
38
|
+
/** For tests. Default `navigator.serviceWorker`. */
|
|
39
|
+
container?: ServiceWorkerContainer;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolves with the registration's worker once it is `activated`. Rejects
|
|
43
|
+
* with a `ServiceWorkerControlError` (`reason: "activation-timeout"`) if that
|
|
44
|
+
* takes longer than `timeout` — an install that throws or never finishes
|
|
45
|
+
* would otherwise leave the caller waiting forever.
|
|
46
|
+
*/
|
|
47
|
+
export declare function awaitActiveServiceWorker(registration: ServiceWorkerRegistration, { timeout }?: AwaitServiceWorkerOptions): Promise<ServiceWorker>;
|
|
48
|
+
/**
|
|
49
|
+
* Resolves with the ServiceWorker that controls this page, once `registration`
|
|
50
|
+
* has an activated worker and the page is controlled by it.
|
|
51
|
+
*
|
|
52
|
+
* A page can stay uncontrolled while its worker is active, and then no
|
|
53
|
+
* `controllerchange` ever fires on its own:
|
|
54
|
+
* - a hard reload (Ctrl+Shift+R) bypasses the worker for that load, and the
|
|
55
|
+
* worker's `clients.claim()` already ran when it activated;
|
|
56
|
+
* - Firefox can leave a page loaded while the worker is running uncontrolled.
|
|
57
|
+
*
|
|
58
|
+
* So when the page is uncontrolled, this asks the active worker to claim it
|
|
59
|
+
* again (a `CLAIM` channel call — this package's workers answer it) and waits
|
|
60
|
+
* for `controllerchange`. Every wait is bounded by `timeout`. If control never
|
|
61
|
+
* comes it reloads once (`reloadIfUncontrolled`) or rejects with a
|
|
62
|
+
* `ServiceWorkerControlError` (`reason: "uncontrolled"`) that says what
|
|
63
|
+
* happened and what to do.
|
|
64
|
+
*/
|
|
65
|
+
export declare function awaitServiceWorkerControl(registration: ServiceWorkerRegistration, { timeout, reloadIfUncontrolled, container, }?: AwaitServiceWorkerControlOptions): Promise<ServiceWorker>;
|
|
66
|
+
/**
|
|
67
|
+
* ServiceWorker side: answers the page's `CLAIM` request with
|
|
68
|
+
* `clients.claim()`, which takes over every uncontrolled client in scope.
|
|
69
|
+
* Returns a function that stops answering.
|
|
70
|
+
*/
|
|
71
|
+
export declare function handleClaimRequests(self: ServiceWorkerGlobalScope): () => void;
|
|
72
|
+
//# sourceMappingURL=service-worker-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-worker-control.d.ts","sourceRoot":"","sources":["../../src/core/service-worker-control.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAAS,CAAC;AAUrD,kFAAkF;AAClF,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,oBAAoB,GAAG,cAAc,GAAG,cAAc,CAAC;AAEjG;;;;GAIG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAC;IAC7C,YAAY,MAAM,EAAE,2BAA2B,EAAE,OAAO,EAAE,MAAM,EAI/D;CACF;AAED,MAAM,WAAW,yBAAyB;IACxC,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gCAAiC,SAAQ,yBAAyB;IACjF;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,oDAAoD;IACpD,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,yBAAyB,EACvC,EAAE,OAAwC,EAAE,GAAE,yBAA8B,GAC3E,OAAO,CAAC,aAAa,CAAC,CAaxB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,yBAAyB,EACvC,EACE,OAAwC,EACxC,oBAA4B,EAC5B,SAAmC,GACpC,GAAE,gCAAqC,GACvC,OAAO,CAAC,aAAa,CAAC,CA6CxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM,IAAI,CAM9E"}
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { type HttpHandler } from "@statewalker/webrun-http-streams";
|
|
2
2
|
import type { MessageTarget } from "../core/message-target.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
* `
|
|
4
|
+
* Serve an `HttpHandler` over a `MessageTarget`, using this package's own
|
|
5
|
+
* `handleStreams` transport.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated Prefer the port stack in `@statewalker/webrun-rpc`: open a port
|
|
8
|
+
* (`multiplexPort` over one pipe, or `transferPortMux` where the platform can
|
|
9
|
+
* transfer a real `MessagePort`), turn it into a `Duplex` with
|
|
10
|
+
* `serveDuplexOverPort`, and serve HTTP on that with
|
|
11
|
+
* `httpServe(handler, options)` from `@statewalker/webrun-http-streams`.
|
|
12
|
+
*
|
|
13
|
+
* That path has backpressure; **this one does not.** `sendStream`'s chunk
|
|
14
|
+
* sender discards the promise it is given, so a fast producer over a slow
|
|
15
|
+
* consumer accumulates without bound. It also has no per-stream timeout and no
|
|
16
|
+
* chunking to a transport's message ceiling.
|
|
17
|
+
*
|
|
18
|
+
* Kept for existing ServiceWorker setups built on the `MessageTarget` surface.
|
|
11
19
|
*/
|
|
12
20
|
export declare function handleHttpRequests(communicationPort: MessageTarget, handler: HttpHandler): () => void;
|
|
13
21
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `
|
|
22
|
+
* Ship a `Request` over a `MessageTarget` and await the `Response`, using this
|
|
23
|
+
* package's own `sendStream` transport.
|
|
24
|
+
*
|
|
25
|
+
* @deprecated Prefer the port stack in `@statewalker/webrun-rpc`: open a port
|
|
26
|
+
* (`multiplexPort` over one pipe, or `transferPortMux` where the platform can
|
|
27
|
+
* transfer a real `MessagePort`), turn it into a `Duplex` with
|
|
28
|
+
* `duplexOverPort`, and drive HTTP over it with `httpFetch` from
|
|
29
|
+
* `@statewalker/webrun-http-streams`.
|
|
30
|
+
*
|
|
31
|
+
* Same caveat as {@link handleHttpRequests}: the transport underneath this
|
|
32
|
+
* helper has no backpressure, no per-stream timeout, and no chunking to a
|
|
33
|
+
* transport's message ceiling. Kept for existing ServiceWorker setups.
|
|
21
34
|
*/
|
|
22
35
|
export declare function sendHttpRequest(communicationPort: MessageTarget, request: Request): Promise<Response>;
|
|
23
36
|
//# sourceMappingURL=http-send-recieve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-send-recieve.d.ts","sourceRoot":"","sources":["../../src/http/http-send-recieve.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAMjB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA+B/D
|
|
1
|
+
{"version":3,"file":"http-send-recieve.d.ts","sourceRoot":"","sources":["../../src/http/http-send-recieve.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAMjB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA+B/D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,iBAAiB,EAAE,aAAa,EAChC,OAAO,EAAE,WAAW,GACnB,MAAM,IAAI,CAOZ;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,iBAAiB,EAAE,aAAa,EAChC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,QAAQ,CAAC,CAOnB"}
|