arcane-os 0.9.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.1
4
+
5
+ - Open developer error modals only for newly captured live incidents. Restoring
6
+ pending diagnostics after navigation or reload continues scheduled delivery
7
+ and retry without reopening old modals. Preserve complete stored incidents
8
+ and the existing behavior for new errors.
9
+
10
+ ## 0.10.0
11
+
12
+ - Add opt-in application PWA configuration, generated installation and offline
13
+ manifests, a stable service worker and asynchronous registration bootstrap.
14
+ Keep branding and offline resource selection application-owned.
15
+ - Expose `registerPwa()` and `PWA_STATE_EVENT` through `arcane-os/pwa`, with
16
+ current-state replay, native update lifecycle, complete errors and disposal.
17
+ Keep registration independent of rendering, preferences and model startup.
18
+ - Use clean local resource URLs for enabled PWA browser delivery. Remove `v`
19
+ and `arcaneVersion` from actual references and managed import-map aliases,
20
+ preserving meaningful queries, fragments and unrelated payloads. Dynamic
21
+ HTML imports follow the same document-owned selection.
22
+ - Revalidate PWA source resources for live development and retain selected
23
+ successful responses offline. Packaged output uses a separate cache generation
24
+ for each deployment revision, bounded concurrent installation and normal
25
+ browser activation. Preserve user data, model caches and native packaging.
26
+
3
27
  ## 0.9.0
4
28
 
5
29
  - Add `getBrowserDeviceClass()` through the dependency-free
package/README.md CHANGED
@@ -19,10 +19,16 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
19
19
  `arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
20
20
  event, cancellation, and browser run contracts.
21
21
 
22
- This checkout defines the `0.9.0` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.10.1` SDK contract. Applications pin one exact npm
23
23
  version and lockfile; registry state is deliberately not baked into application
24
24
  artifacts.
25
25
 
26
+ Applications can enable [PWA installation and offline resources](docs/reference/pwa.md)
27
+ through their app descriptor. The SDK generates manifests, an independent
28
+ registration module and a service worker, with clean browser resource URLs,
29
+ live-source revalidation and selected release caches. Apps retain ownership of
30
+ branding, offline page selection and network-dependent product behavior.
31
+
26
32
  That registry query is a maintainer action, not an application behavior. Apps
27
33
  never poll npm for SDK updates or replace their own SDK or synchronized runtime.
28
34
  The app repository's exact dependency and lockfile select the SDK; changing that
@@ -35,7 +41,7 @@ Create one browser application, install its pinned SDK, and start its source
35
41
  server:
36
42
 
37
43
  ```bash
38
- npx arcane-os@0.9.0 new hello-speech --path ./hello-speech --target browser
44
+ npx arcane-os@0.10.1 new hello-speech --path ./hello-speech --target browser
39
45
  cd hello-speech
40
46
  npm install
41
47
  npm run dev
@@ -386,7 +392,7 @@ uses the same controller for automatic memory extraction.
386
392
  Create a new repository-shaped Arcane application with the exact stable SDK:
387
393
 
388
394
  ```bash
389
- npx arcane-os@0.9.0 new my-app --path ./my-app --target portable --git
395
+ npx arcane-os@0.10.1 new my-app --path ./my-app --target portable --git
390
396
  cd my-app
391
397
  npm install
392
398
  npm run dev
@@ -396,7 +402,7 @@ To enroll an existing repository, install the exact SDK and initialize only
396
402
  missing Arcane files:
397
403
 
398
404
  ```bash
399
- npm install --save-dev --save-exact arcane-os@0.9.0
405
+ npm install --save-dev --save-exact arcane-os@0.10.1
400
406
  npm exec -- arcane init my-app --target portable
401
407
  ```
402
408
 
@@ -412,7 +418,7 @@ npm exec -- arcane-os targets
412
418
  No global SDK install or standalone Arcane CLI is required. The application
413
419
  repository's exact npm dependency and lockfile own the CLI and toolchain version.
414
420
 
415
- Use `npx arcane-os@0.9.0` for the initial bootstrap because it names this npm
421
+ Use `npx arcane-os@0.10.1` for the initial bootstrap because it names this npm
416
422
  package explicitly; bare `npx arcane` outside an installed project could resolve
417
423
  a different package. Both installed commands invoke the same headless toolchain.
418
424
  Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
@@ -432,7 +438,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
432
438
 
433
439
  # From the generated app repository
434
440
  cd ../local-app
435
- npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.9.0.tgz
441
+ npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.10.1.tgz
436
442
  npm ci
437
443
  ```
438
444
 
@@ -441,7 +447,7 @@ same location. The lockfile retains the selected package dependency while
441
447
  Arcane uses the installed package name and version. Local directory `file:` dependencies are not
442
448
  accepted because npm may install them as links; use a packed `.tgz`. A GitHub
443
449
  runner also needs that tarball at the locked path. After publication, replace
444
- the local declaration with the exact `arcane-os@0.9.0` registry package and
450
+ the local declaration with the exact `arcane-os@0.10.1` registry package and
445
451
  commit the regenerated lock.
446
452
 
447
453
  Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
@@ -580,7 +586,7 @@ package installation, or assertions.
580
586
 
581
587
  ## Current target support
582
588
 
583
- Version `0.9.0` exposes one browser target and five explicitly paired
589
+ Version `0.10.1` exposes one browser target and five explicitly paired
584
590
  native development targets: a non-runnable portable directory, a
585
591
  Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
586
592
  unsigned-local-test DEBs, and an Android development-signed APK. The
@@ -0,0 +1,215 @@
1
+ import {createArcaneEventSource} from './event-manager.mjs';
2
+
3
+ export const PWA_STATE_EVENT = 'arcane.pwa.state';
4
+
5
+ export function registerPwa({workerUrl = './arcane-sw.js', scope} = {}) {
6
+ const owner = {
7
+ get ready() {
8
+ return ready;
9
+ },
10
+ get state() {
11
+ return snapshot();
12
+ },
13
+ subscribe,
14
+ dispose,
15
+ update
16
+ };
17
+ const source = createArcaneEventSource(
18
+ owner,
19
+ {
20
+ source: 'arcane.pwa',
21
+ eventTypes: [PWA_STATE_EVENT]
22
+ }
23
+ );
24
+ const container = globalThis.navigator?.serviceWorker;
25
+ const listeners = [];
26
+ const workers = new Map();
27
+ let registration = null;
28
+ let disposed = false;
29
+ let current = {
30
+ status: 'registering',
31
+ workerUrl: String(workerUrl),
32
+ scope: scope === undefined ? null : String(scope),
33
+ controller: null,
34
+ installing: null,
35
+ waiting: null,
36
+ active: null,
37
+ error: null
38
+ };
39
+
40
+ function snapshot() {
41
+ return {...current};
42
+ }
43
+
44
+ function publish(status, error = null) {
45
+ if (disposed) {
46
+ return;
47
+ }
48
+ current = {
49
+ status,
50
+ workerUrl: current.workerUrl,
51
+ scope: registration?.scope ?? current.scope,
52
+ controller: container?.controller?.state ?? null,
53
+ installing: registration?.installing?.state ?? null,
54
+ waiting: registration?.waiting?.state ?? null,
55
+ active: registration?.active?.state ?? null,
56
+ error
57
+ };
58
+ source.dispatch(
59
+ PWA_STATE_EVENT,
60
+ snapshot()
61
+ );
62
+ }
63
+
64
+ function observe(target, eventName, listener) {
65
+ target.addEventListener(eventName, listener);
66
+ listeners.push(
67
+ function removeNativeListener() {
68
+ target.removeEventListener(eventName, listener);
69
+ }
70
+ );
71
+ }
72
+
73
+ function currentStatus() {
74
+ if (registration.waiting) {
75
+ return 'waiting';
76
+ }
77
+ if (registration.installing) {
78
+ return 'installing';
79
+ }
80
+ return registration.active ? 'active' : 'registered';
81
+ }
82
+
83
+ function onWorkerStateChange(event) {
84
+ const previousState = workers.get(event.target);
85
+ workers.set(event.target, event.target.state);
86
+ // A newer installation normally supersedes an already waiting worker.
87
+ if (event.target.state === 'redundant'
88
+ && previousState !== 'activated' && previousState !== 'installed') {
89
+ publish('error', current.error ?? new Error('The PWA worker became redundant before activation.'));
90
+ return;
91
+ }
92
+ refreshRegistration();
93
+ }
94
+
95
+ function watchWorker(worker) {
96
+ if (!worker || workers.has(worker)) {
97
+ return;
98
+ }
99
+ workers.set(worker, worker.state);
100
+ observe(worker, 'statechange', onWorkerStateChange);
101
+ }
102
+
103
+ function refreshRegistration() {
104
+ if (disposed) {
105
+ return;
106
+ }
107
+ watchWorker(registration.installing);
108
+ watchWorker(registration.waiting);
109
+ watchWorker(registration.active);
110
+ publish(currentStatus());
111
+ }
112
+
113
+ function onControllerChange() {
114
+ refreshRegistration();
115
+ }
116
+
117
+ function onWorkerMessage(event) {
118
+ if (event.data?.type === 'arcane.pwa.error' && workers.has(event.source)) {
119
+ publish('error', event.data.error);
120
+ }
121
+ }
122
+
123
+ function onRegistered(value) {
124
+ registration = value;
125
+ if (!disposed) {
126
+ observe(registration, 'updatefound', refreshRegistration);
127
+ observe(container, 'controllerchange', onControllerChange);
128
+ observe(container, 'message', onWorkerMessage);
129
+ refreshRegistration();
130
+ }
131
+ return registration;
132
+ }
133
+
134
+ function onRegistrationError(error) {
135
+ publish('error', error);
136
+ throw error;
137
+ }
138
+
139
+ function startRegistration() {
140
+ if (!container) {
141
+ publish('unsupported');
142
+ return Promise.resolve(null);
143
+ }
144
+ try {
145
+ const url = new URL(workerUrl, globalThis.document?.baseURI ?? globalThis.location?.href);
146
+ const registrationScope = new URL(scope ?? './', url);
147
+ current.workerUrl = url.href;
148
+ current.scope = registrationScope.href;
149
+ const registered = container.register(
150
+ url.href,
151
+ {
152
+ scope: registrationScope.href,
153
+ updateViaCache: 'none'
154
+ }
155
+ );
156
+ return Promise.resolve(registered).then(onRegistered).catch(onRegistrationError);
157
+ } catch (error) {
158
+ publish('error', error);
159
+ return Promise.reject(error);
160
+ }
161
+ }
162
+
163
+ function subscribe(listener, {emitCurrent = true, signal} = {}) {
164
+ function forwardPwaState(event) {
165
+ listener(event.detail);
166
+ }
167
+ const unsubscribe = source.on(PWA_STATE_EVENT, forwardPwaState, signal ? {signal} : undefined);
168
+ try {
169
+ if (emitCurrent && !signal?.aborted) {
170
+ listener(snapshot());
171
+ }
172
+ } catch (error) {
173
+ unsubscribe();
174
+ throw error;
175
+ }
176
+ return unsubscribe;
177
+ }
178
+
179
+ async function update() {
180
+ const value = await ready;
181
+ if (disposed) {
182
+ throw new Error('The PWA registration owner has been disposed.');
183
+ }
184
+ if (!value) {
185
+ return null;
186
+ }
187
+ try {
188
+ return await value.update();
189
+ } catch (error) {
190
+ publish('error', error);
191
+ throw error;
192
+ }
193
+ }
194
+
195
+ function dispose() {
196
+ if (disposed) {
197
+ return;
198
+ }
199
+ current = {...current, status: 'disposed'};
200
+ disposed = true;
201
+ source.dispatch(
202
+ PWA_STATE_EVENT,
203
+ snapshot()
204
+ );
205
+ for (const removeListener of listeners) {
206
+ removeListener();
207
+ }
208
+ listeners.length = 0;
209
+ workers.clear();
210
+ source.dispose();
211
+ }
212
+
213
+ const ready = startRegistration();
214
+ return owner;
215
+ }
@@ -36,6 +36,7 @@ alone does not make bare module names resolve in a browser.
36
36
  | Publish central events, capture complete time-travel history, or observe the DOM | [EventManager and event-stack reference](event-manager.md) |
37
37
  | Inspect complete AI and speech calls using the shared developer-mode preference | [Shared logger](sdk-api.md#arcanelogging) and [speech developer diagnostics](ai/browser-speech.md#developer-diagnostics) |
38
38
  | Use the `arcane` command | [CLI reference](cli.md) |
39
+ | Install an app and cache selected resources offline | [Progressive web applications](pwa.md) |
39
40
  | Generate named browser imports or inspect the selected physical runtime | [`arcane import-map`](cli.md#arcane-import-map) and [browser runtime delivery](protocols.md#browser-runtime-delivery) |
40
41
  | Choose browser, native, cloud, or cross-host behavior | [Availability and normalization](availability-and-normalization.md) |
41
42
  | Import a shipped renderer module | [Runtime module catalog](runtime-modules.md) |
@@ -124,13 +125,13 @@ Public reference entries follow the established Arcane documentation model:
124
125
 
125
126
  ## Public runtime inventory
126
127
 
127
- The package exposes 203 semantic JavaScript records across 19 JavaScript
128
+ The package exposes 205 semantic JavaScript records across 20 JavaScript
128
129
  entrypoints, plus eight JSON Schemas and package metadata. Ten entrypoints are
129
130
  Node.js control-plane surfaces,
130
131
  `arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`, `arcane-os/preference-store`, and
131
132
  `arcane-os/speech-playback`, `arcane-os/speech-text`, and `arcane-os/browser-device`
132
133
  run in Node and browsers, and
133
- `arcane-os/ai/browser-wasm` plus `arcane-os/ai/browser-speech` are browser-only.
134
+ `arcane-os/pwa`, `arcane-os/ai/browser-wasm` plus `arcane-os/ai/browser-speech` are browser-only.
134
135
  The [machine-readable package
135
136
  inventory](inventory/package-api.json) and [SDK member reference](sdk-api.md)
136
137
  are checked bidirectionally against every declared JavaScript export.
@@ -1,5 +1,13 @@
1
1
  # Release-derived browser asset URLs
2
2
 
3
+ Applications that enable [PWA delivery](pwa.md) use clean local resource URLs.
4
+ Their generated offline manifest and service worker own the selected application
5
+ and SDK release information. In that mode, the delivery transformer removes
6
+ both `v` and `arcaneVersion`, preserving other query fields and fragments.
7
+ The behavior below continues to apply when PWA delivery is disabled and to native
8
+ packages. Workspace runtime materialization remains usable by either target;
9
+ the selected browser delivery applies its PWA URL policy.
10
+
3
11
  The SDK's public import-map generator, runtime materializer, source server and
4
12
  application packager use the selected SDK package version for local browser
5
13
  resource references. The query field is `arcaneVersion`. Its value comes from
@@ -49,6 +57,11 @@ HTML and managed import-map JSON. This allows storage but requests revalidation,
49
57
  or refresh obtains the current entry document and release URLs. Other assets
50
58
  retain ordinary caching; no cache or user storage is cleared.
51
59
 
60
+ For PWA delivery the SDK server revalidates all served resources, including
61
+ the stable worker script. A static host serving a PWA package must likewise
62
+ revalidate stable resource URLs. The service worker maintains its own selected
63
+ offline resource cache independently of the HTTP cache.
64
+
52
65
  An independently configured static host must likewise revalidate entry HTML
53
66
  and managed import-map JSON. The generated package supplies versioned resource
54
67
  references; it cannot configure another server's HTTP headers.
@@ -6,7 +6,7 @@
6
6
  "minimumVersion": "22.23.2 for Node entrypoints",
7
7
  "moduleSystem": "ESM"
8
8
  },
9
- "memberCount": 203,
9
+ "memberCount": 205,
10
10
  "members": [
11
11
  {
12
12
  "id": "root:APP_BUNDLE_DESCRIPTOR_NAME",
@@ -2968,6 +2968,34 @@
2968
2968
  "protocol": "Synchronous optional navigator-like identity input and mobile or desktop string result",
2969
2969
  "normalization": "Positive mobile hints, Android or iOS identity, and iPadOS Mac-platform touch identity select mobile; missing or unrecognized identity selects desktop. Importing the dependency-free entrypoint loads no AI runtime, and classification performs no model, GPU, storage, network, or listener operation."
2970
2970
  },
2971
+ {
2972
+ "id": "pwa:PWA_STATE_EVENT",
2973
+ "name": "PWA_STATE_EVENT",
2974
+ "displayName": "PWA_STATE_EVENT",
2975
+ "kind": "constant",
2976
+ "signature": "const PWA_STATE_EVENT",
2977
+ "entrypoints": ["arcane-os/pwa"],
2978
+ "primaryImport": "arcane-os/pwa",
2979
+ "group": "Progressive web applications",
2980
+ "summary": "Names the shared PWA lifecycle state event.",
2981
+ "availability": "Browser lifecycle; importable without registration",
2982
+ "protocol": "Existing Arcane event owner and native service worker lifecycle",
2983
+ "normalization": "The exact event name is arcane.pwa.state."
2984
+ },
2985
+ {
2986
+ "id": "pwa:registerPwa",
2987
+ "name": "registerPwa",
2988
+ "displayName": "registerPwa()",
2989
+ "kind": "function",
2990
+ "signature": "registerPwa({workerUrl = './arcane-sw.js', scope} = {})",
2991
+ "entrypoints": ["arcane-os/pwa"],
2992
+ "primaryImport": "arcane-os/pwa",
2993
+ "group": "Progressive web applications",
2994
+ "summary": "Starts PWA registration without blocking page rendering and returns an observable lifecycle owner.",
2995
+ "availability": "Browser service workers; explicit unsupported state elsewhere",
2996
+ "protocol": "Native registration, updatefound, statechange and controllerchange events",
2997
+ "normalization": "Synchronous owner exposes ready, state, subscribe, update and dispose; current state replays by default, failures remain observable, disposal does not unregister the worker or delete saved data."
2998
+ },
2971
2999
  {
2972
3000
  "id": "browser-speech:BROWSER_SPEECH_ARTIFACT_GRAPH_PROTOCOL",
2973
3001
  "name": "BROWSER_SPEECH_ARTIFACT_GRAPH_PROTOCOL",
@@ -0,0 +1,183 @@
1
+ # Progressive web applications
2
+
3
+ An application supplies its installation identity and offline resource selection.
4
+ The SDK generates the Web App Manifest, offline inventory, service worker and
5
+ nonblocking registration module. Native packages keep their existing lifecycle.
6
+
7
+ ## Application configuration
8
+
9
+ Set `package.pwa` in the application's `arcane-app.json`. Its ordinary package
10
+ projection carries the same `pwa` record in `arcane-package.json`:
11
+
12
+ ```json
13
+ {
14
+ "enabled": true,
15
+ "manifest": {
16
+ "name": "Example Library",
17
+ "short_name": "Library",
18
+ "display": "standalone",
19
+ "icons": [
20
+ {
21
+ "src": "img/library.png",
22
+ "sizes": "512x512",
23
+ "type": "image/png"
24
+ }
25
+ ]
26
+ },
27
+ "offline": {
28
+ "exclude": ["documents"]
29
+ }
30
+ }
31
+ ```
32
+
33
+ `enabled` defaults to `false`. `manifest` contains ordinary Web App Manifest
34
+ metadata. Its default name comes from `displayName`; its default start page
35
+ comes from the selected application entry. The app owns names, icons, colors,
36
+ display preference, routes and descriptions. Use real app icons; the SDK does
37
+ not invent branding or claim that a browser has installed the app.
38
+
39
+ Manifest URL fields are relative to the application directory. Source delivery
40
+ and packaged delivery resolve those fields into their respective layouts.
41
+ Absolute URL fields retain their authored destination.
42
+
43
+ `offline.include` and `offline.exclude` select literal paths or directory
44
+ prefixes from the selected emitted inventory. An omitted or empty include list
45
+ selects that inventory; exclusions subtract from it. App files use app-relative
46
+ paths and shared runtime files use paths such as `arcane/sdk/pwa.mjs`.
47
+ The application entry and generated PWA shell records are retained. Select the
48
+ resources needed by every offline page, including its shared modules and styles.
49
+ The worker script itself is never an application cache entry.
50
+
51
+ These settings describe published application resources. They do not select
52
+ user uploads, provider responses, live API requests, saved conversations,
53
+ preferences, IndexedDB, OPFS, or a model owner's download/cache lifecycle.
54
+ Offline page availability does not imply that cloud inference or other network
55
+ services work offline. Applications own the corresponding visible behavior.
56
+
57
+ ## Generated output
58
+
59
+ Browser packaging emits these files at the selected deployment root:
60
+
61
+ | File | Purpose |
62
+ | --- | --- |
63
+ | `arcane.webmanifest` | Browser installation metadata. |
64
+ | `arcane-offline.json` | App ID/version, SDK version, deployment revision, resource URLs and explicit navigation aliases. |
65
+ | `arcane-sw.js` | Stable worker URL with the selected offline manifest embedded in its source. |
66
+ | `arcane-pwa.mjs` | Independent registration module importing the SDK client. |
67
+
68
+ Each packaged output gets one deployment revision shared by its offline
69
+ manifest and worker. It distinguishes separately generated outputs even when
70
+ their app and SDK versions match. It is not a content measurement.
71
+
72
+ The package owns its selected inventory once, after any app adapter finishes.
73
+ It follows actual resource references to include meaningful query variants.
74
+ Generated application pages receive a manifest link and an `async` module
75
+ marked `data-arcane-pwa`. Existing application scripts retain their order.
76
+ PWA registration does not wait for models, storage, preferences or page rendering.
77
+
78
+ The selected PWA browser delivery removes `v` and `arcaneVersion` from actual
79
+ local resource references, including the managed import map. Other query fields,
80
+ fragments, source spelling and unrelated payloads are preserved. The offline
81
+ manifest now carries release information. Non-PWA and native delivery retain
82
+ the [existing asset version contract](asset-versioning.md).
83
+
84
+ ## Development and hosting
85
+
86
+ For an enabled application, `arcane dev` serves the generated PWA files at the
87
+ origin root and starts at the selected app page under `/apps/<id>/`. Use one
88
+ selected app per development origin. Its worker uses network revalidation first
89
+ and retains successful selected resource responses for offline use, so saved
90
+ source edits remain visible on an ordinary online refresh.
91
+
92
+ Source inventory work begins when the browser requests the worker update, after
93
+ the page can start. It traverses the selected route inventory once for that
94
+ request, follows application page and runtime resource references to retain
95
+ selected query variants, and shares an in-flight traversal with concurrent
96
+ manifest requests. Each referenced source file is read once per traversal;
97
+ document corpus bodies remain under their existing owner. It does not rebuild
98
+ the application. Generated metadata and bootstrap requests reuse the current
99
+ bundle.
100
+
101
+ Packaged workers use their selected cache generation first. During installation,
102
+ at most four resource requests run together; each response is fetched from the
103
+ network with the browser's reload cache mode before it enters the candidate
104
+ cache. The candidate must finish
105
+ installation before the browser activates it. Failures retain the prior active
106
+ worker and remain observable through native worker state and SDK diagnostics.
107
+
108
+ The SDK development server sends `Cache-Control: no-cache` for PWA resources.
109
+ An independent static host must also revalidate stable HTML, module, style,
110
+ import-map and worker URLs, and serve JavaScript with a JavaScript content type.
111
+ Keep the worker beside the deployment root it controls. The browser requires a
112
+ supported secure context, such as trusted HTTPS or localhost, to register it.
113
+ The SDK does not change certificates or browser permissions.
114
+
115
+ An initial visit can load before a worker controls the document. Revalidation
116
+ therefore matters even when an offline cache exists. Updating files in place
117
+ while installation fetches them is not an atomic release snapshot; the host owns
118
+ consistent deployment of the selected output.
119
+
120
+ An offline navigation alias redirects to its selected entry page, preserving the
121
+ document URL used to resolve relative modules and styles.
122
+
123
+ Browser storage eviction can remove an offline cache. A missing release cache
124
+ entry falls back to the network, so offline availability still depends on the
125
+ browser retaining the selected resources.
126
+
127
+ ## Updates and stored data
128
+
129
+ An updated worker installs alongside the current worker, then waits for the
130
+ browser's normal activation boundary. Existing controlled pages retain their
131
+ worker while they are open. Closing those pages allows activation; a refresh
132
+ can leave overlapping document clients and keep the update waiting.
133
+
134
+ The SDK does not call `skipWaiting`, claim the initial page, reload a page,
135
+ restart a model or poll for updates. Activation retires only obsolete resource
136
+ caches belonging to that exact app and registration scope. Saved application
137
+ data and caches owned by other capabilities are untouched.
138
+
139
+ Switching a server from a packaged release to live development does not replace
140
+ an already active release worker inside an open document. The same native
141
+ worker lifecycle applies.
142
+
143
+ ## registerPwa()
144
+
145
+ Import `registerPwa` and `PWA_STATE_EVENT` from `arcane-os/pwa` through the
146
+ managed browser import map. The generated bootstrap already calls this API;
147
+ manual callers use it when they own a separate registration entry point.
148
+
149
+ ```javascript
150
+ import {registerPwa} from 'arcane-os/pwa';
151
+
152
+ const pwa = registerPwa(
153
+ {
154
+ workerUrl: new URL('./arcane-sw.js', import.meta.url).href,
155
+ scope: new URL('./', import.meta.url).href
156
+ }
157
+ );
158
+
159
+ pwa.subscribe(function showPwaState(state) {
160
+ console.log(state);
161
+ });
162
+ pwa.ready.catch(function reportRegistrationFailure(error) {
163
+ console.error(error);
164
+ });
165
+ ```
166
+
167
+ The function returns synchronously with `{ready, state, subscribe, update,
168
+ dispose}`. `ready` settles with the native registration, or `null` when service
169
+ workers are unavailable. A registration failure rejects it and publishes error
170
+ state. Do not await it before rendering or confuse it with a controlled page.
171
+
172
+ `state` reports `status`, `workerUrl`, `scope`, `controller`, `installing`,
173
+ `waiting`, `active` and the complete `error` when present. Status is one of
174
+ `registering`, `unsupported`, `registered`, `installing`, `waiting`, `active`,
175
+ `error` or `disposed`. Native worker state fields are strings or `null`.
176
+
177
+ `subscribe(listener, {emitCurrent: true, signal} = {})` immediately replays the
178
+ current state by default and returns an unsubscribe function. Subsequent state
179
+ uses the existing Arcane event owner and `PWA_STATE_EVENT` (`arcane.pwa.state`).
180
+ `update()` requests the browser's normal update check on demand; failure is
181
+ observable in state and through its returned promise. `dispose()` removes
182
+ page-owned listeners and subscriptions without unregistering the persistent
183
+ worker or deleting stored data.
@@ -84,7 +84,7 @@ own asynchronous work, cancellation, and backpressure.
84
84
  | [`DirectoryPicker.js`](#directorypickerjs) | esm | Wraps the provider-owned native directory chooser and normalizes selected/cancelled/error results. | Native bridge | Complete mutable caller options and provider result fields; coded cancellation and malformed-result errors. |
85
85
  | [`DocumentLexicalSearch.js`](#documentlexicalsearchjs) | esm | Provides dependency-free deterministic metadata/body ranking and complete excerpts. | Cross-host | Mutable complete results with no storage, provider, or network side effects. |
86
86
  | [`DocumentNavigation.js`](#documentnavigationjs) | esm | Binds document navigation, filtering, history, current-item reveal, and load initialization. | Browser / native WebView | Normalized filter/navigation state; DOM effects preserved. |
87
- | [`Errors.js`](#errorsjs) | esm | Normalizes global errors/rejections, fingerprints and deduplicates incidents, persists a complete ledger, and performs complete delivery. | Browser / native WebView hybrid | Incident records normalized; storage/mail failures isolated. |
87
+ | [`Errors.js`](#errorsjs) | esm | Normalizes global errors/rejections, assigns occurrence identifiers, persists a complete ledger, and performs complete delivery. | Browser / native WebView hybrid | Incident records normalized; storage/mail failures isolated. |
88
88
  | [`GifEncoder.js`](#gifencoderjs) | esm | Encodes indexed frames into a complete animated GIF using palette mapping and LZW. | Cross-host | Normalized complete binary output. |
89
89
  | [`HTMLImport.js`](#htmlimportjs) | esm | Defines the same-origin `<html-import>` loader with open shadow root, inline script execution, and readiness/error events. | Browser / native WebView | Public error detail normalized; fetch/DOM failure preserved. |
90
90
  | [`InMemoryCommunicationProvider.js`](#inmemorycommunicationproviderjs) | esm | Implements deterministic in-memory thread/message/send behavior for demos and tests. | Cross-host | Normalized communication entities. |
@@ -1998,6 +1998,12 @@ console.log(Object.keys(module));
1998
1998
 
1999
1999
  Normalizes global errors/rejections, assigns occurrence identifiers, persists a complete ledger, and performs complete delivery.
2000
2000
 
2001
+ Developer modals are offered only for newly captured live incidents. Loading
2002
+ pending records from a prior page preserves their complete diagnostics and
2003
+ delivery/retry state without reopening their modals. This remains true when
2004
+ developer preferences become ready after restoration. A newly captured error
2005
+ still follows the ordinary developer-mode presentation path.
2006
+
2001
2007
  ### Public surface
2002
2008
 
2003
2009
  default `Errors`; event normalizers plus lifecycle, capture, delivery and teardown methods.