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 +24 -0
- package/README.md +14 -8
- package/browser-runtime/pwa.mjs +215 -0
- package/docs/reference/README.md +3 -2
- package/docs/reference/asset-versioning.md +13 -0
- package/docs/reference/inventory/package-api.json +29 -1
- package/docs/reference/pwa.md +183 -0
- package/docs/reference/runtime-modules.md +7 -1
- package/docs/reference/sdk-api.md +45 -1
- package/package.json +3 -2
- package/runtime/arcane/modules/Errors.js +1 -1
- package/runtime/arcane/modules/HTMLImport.js +13 -3
- package/schemas/arcane-app.schema.json +28 -0
- package/schemas/arcane-package.schema.json +30 -0
- package/src/app-descriptor.mjs +10 -2
- package/src/dev-server.mjs +252 -13
- package/src/import-map.mjs +186 -11
- package/src/packager/core.mjs +140 -17
- package/src/pwa-worker.mjs +227 -0
- package/src/pwa.mjs +311 -0
- package/src/targets/index.mjs +1 -0
- package/src/toolchain.mjs +1 -0
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
}
|
package/docs/reference/README.md
CHANGED
|
@@ -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
|
|
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":
|
|
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,
|
|
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.
|