arcane-os 0.11.3 → 0.12.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/CHANGELOG.md +18 -0
- package/NOTICE +1 -0
- package/README.md +24 -13
- package/browser-runtime/pwa.mjs +129 -0
- package/docs/architecture.md +28 -12
- package/docs/reference/cli.md +42 -16
- package/docs/reference/inventory/package-api.json +1 -1
- package/docs/reference/pwa.md +87 -22
- package/docs/reference/sdk-api.md +61 -27
- package/package.json +2 -1
- package/src/app-descriptor.mjs +43 -1
- package/src/cli/main.mjs +29 -13
- package/src/dev-server.mjs +387 -136
- package/src/pwa-worker.mjs +362 -136
- package/src/pwa.mjs +4 -1
- package/src/targets/index.mjs +5 -1
- package/src/toolchain.mjs +62 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
- Refresh the selected authored app descriptor's package projection before
|
|
6
|
+
development startup, so file and PWA configuration changes take effect through
|
|
7
|
+
the ordinary dev command without packaging.
|
|
8
|
+
- Use published `node-http-server` 9.1.1 for development and packaged-preview HTTPS
|
|
9
|
+
serving, preserving source mounts and generated resource transformations.
|
|
10
|
+
Supply modification dates for conditional GET and HEAD requests.
|
|
11
|
+
Enforce HTTPS for every Arcane app, including localhost and packaged browser
|
|
12
|
+
previews, using the configured workspace certificate pair or public TLS options.
|
|
13
|
+
Redirect the paired HTTP listener with status 308 while preserving the request
|
|
14
|
+
path and query; select its port with `httpPort` or CLI `--http-port`.
|
|
15
|
+
- Keep complete PWA resource responses across app and SDK version changes.
|
|
16
|
+
Check on page load after 120 seconds in development or 15 minutes otherwise,
|
|
17
|
+
with one DBOPFS timestamp updated only after the whole check succeeds. Reuse
|
|
18
|
+
cached responses on `304`, replace them after a successful current response, and retain offline
|
|
19
|
+
copies on network failures. No SDK cache expiration or polling is added.
|
|
20
|
+
|
|
3
21
|
## 0.11.3
|
|
4
22
|
|
|
5
23
|
- Require Wllama's model-context load result to report success before publishing
|
package/NOTICE
CHANGED
|
@@ -8,6 +8,7 @@ informational notice and is not itself a grant of commercial rights.
|
|
|
8
8
|
Third-party material retains its own terms:
|
|
9
9
|
|
|
10
10
|
- event-pubsub 6.1.0: MIT License; see its installed `licence` file.
|
|
11
|
+
- node-http-server 9.1.1: MIT License; see its installed `licence` file.
|
|
11
12
|
- strong-type 2.0.1: MIT License; see its installed `licence` file.
|
|
12
13
|
- vanilla-test 2.1.3: MIT License; see its installed `licence` file.
|
|
13
14
|
- ansi-colors-es6 5.0.0: MIT License; see its installed `LICENSE` file.
|
package/README.md
CHANGED
|
@@ -19,15 +19,17 @@ 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.12.0` 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
26
|
Applications can enable [PWA installation and offline resources](docs/reference/pwa.md)
|
|
27
27
|
through their app descriptor. The SDK generates manifests, an independent
|
|
28
28
|
registration module and a service worker, with clean browser resource URLs,
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
page-load revalidation and persistent resource caches. The SDK records one
|
|
30
|
+
completed-check timestamp per app/cache in DBOPFS and checks after 120 seconds
|
|
31
|
+
in development or 15 minutes in packaged browser delivery. Apps retain ownership
|
|
32
|
+
of branding, offline page selection and network-dependent product behavior.
|
|
31
33
|
|
|
32
34
|
That registry query is a maintainer action, not an application behavior. Apps
|
|
33
35
|
never poll npm for SDK updates or replace their own SDK or synchronized runtime.
|
|
@@ -41,9 +43,15 @@ Create one browser application, install its pinned SDK, and start its source
|
|
|
41
43
|
server:
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
npx arcane-os@0.
|
|
46
|
+
npx arcane-os@0.12.0 new hello-speech --path ./hello-speech --target browser
|
|
45
47
|
cd hello-speech
|
|
46
48
|
npm install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Configure the workspace's HTTPS certificate pair as described in
|
|
52
|
+
[development HTTPS setup](docs/reference/cli.md#development-https-setup), then start:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
47
55
|
npm run dev
|
|
48
56
|
```
|
|
49
57
|
|
|
@@ -62,9 +70,12 @@ each device. Keep these local files ignored by Git. The same certificate pair
|
|
|
62
70
|
works for any selected app in that workspace. The command reports missing TLS
|
|
63
71
|
files instead of starting an HTTP listener.
|
|
64
72
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
Every Arcane app uses HTTPS for development and packaged browser previews.
|
|
74
|
+
Plain `npm run dev` binds to localhost with the same workspace certificate pair.
|
|
75
|
+
An explicit `--host` overrides the bind address, and `--port` selects the HTTPS port.
|
|
76
|
+
The paired HTTP listener returns 308 redirects and uses an OS-assigned port
|
|
77
|
+
unless selected with `--http-port`. The command prints its redirect URL.
|
|
78
|
+
Use `--cert <file> --key <file>` for an explicit PEM pair;
|
|
68
79
|
relative paths resolve from the workspace. The server prints HTTPS network URLs
|
|
69
80
|
for public mode. Network reachability depends on the machine's firewall and
|
|
70
81
|
network. See the [development HTTPS setup](docs/reference/cli.md#development-https-setup)
|
|
@@ -392,7 +403,7 @@ uses the same controller for automatic memory extraction.
|
|
|
392
403
|
Create a new repository-shaped Arcane application with the exact stable SDK:
|
|
393
404
|
|
|
394
405
|
```bash
|
|
395
|
-
npx arcane-os@0.
|
|
406
|
+
npx arcane-os@0.12.0 new my-app --path ./my-app --target portable --git
|
|
396
407
|
cd my-app
|
|
397
408
|
npm install
|
|
398
409
|
npm run dev
|
|
@@ -402,7 +413,7 @@ To enroll an existing repository, install the exact SDK and initialize only
|
|
|
402
413
|
missing Arcane files:
|
|
403
414
|
|
|
404
415
|
```bash
|
|
405
|
-
npm install --save-dev --save-exact arcane-os@0.
|
|
416
|
+
npm install --save-dev --save-exact arcane-os@0.12.0
|
|
406
417
|
npm exec -- arcane init my-app --target portable
|
|
407
418
|
```
|
|
408
419
|
|
|
@@ -418,7 +429,7 @@ npm exec -- arcane-os targets
|
|
|
418
429
|
No global SDK install or standalone Arcane CLI is required. The application
|
|
419
430
|
repository's exact npm dependency and lockfile own the CLI and toolchain version.
|
|
420
431
|
|
|
421
|
-
Use `npx arcane-os@0.
|
|
432
|
+
Use `npx arcane-os@0.12.0` for the initial bootstrap because it names this npm
|
|
422
433
|
package explicitly; bare `npx arcane` outside an installed project could resolve
|
|
423
434
|
a different package. Both installed commands invoke the same headless toolchain.
|
|
424
435
|
Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
|
|
@@ -438,7 +449,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
|
|
|
438
449
|
|
|
439
450
|
# From the generated app repository
|
|
440
451
|
cd ../local-app
|
|
441
|
-
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.
|
|
452
|
+
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.12.0.tgz
|
|
442
453
|
npm ci
|
|
443
454
|
```
|
|
444
455
|
|
|
@@ -447,7 +458,7 @@ same location. The lockfile retains the selected package dependency while
|
|
|
447
458
|
Arcane uses the installed package name and version. Local directory `file:` dependencies are not
|
|
448
459
|
accepted because npm may install them as links; use a packed `.tgz`. A GitHub
|
|
449
460
|
runner also needs that tarball at the locked path. After publication, replace
|
|
450
|
-
the local declaration with the exact `arcane-os@0.
|
|
461
|
+
the local declaration with the exact `arcane-os@0.12.0` registry package and
|
|
451
462
|
commit the regenerated lock.
|
|
452
463
|
|
|
453
464
|
Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
|
|
@@ -586,7 +597,7 @@ package installation, or assertions.
|
|
|
586
597
|
|
|
587
598
|
## Current target support
|
|
588
599
|
|
|
589
|
-
Version `0.
|
|
600
|
+
Version `0.12.0` exposes one browser target and five explicitly paired
|
|
590
601
|
native development targets: a non-runnable portable directory, a
|
|
591
602
|
Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
|
|
592
603
|
unsigned-local-test DEBs, and an Android development-signed APK. The
|
package/browser-runtime/pwa.mjs
CHANGED
|
@@ -24,6 +24,11 @@ export function registerPwa({workerUrl = './arcane-sw.js', scope} = {}) {
|
|
|
24
24
|
const container = globalThis.navigator?.serviceWorker;
|
|
25
25
|
const listeners = [];
|
|
26
26
|
const workers = new Map();
|
|
27
|
+
const queriedWorkers = new WeakSet();
|
|
28
|
+
const refreshedWorkers = new WeakSet();
|
|
29
|
+
const pendingRefreshes = new Set();
|
|
30
|
+
const refreshTasks = new Set();
|
|
31
|
+
let storageTask = null;
|
|
27
32
|
let registration = null;
|
|
28
33
|
let disposed = false;
|
|
29
34
|
let current = {
|
|
@@ -83,6 +88,13 @@ export function registerPwa({workerUrl = './arcane-sw.js', scope} = {}) {
|
|
|
83
88
|
function onWorkerStateChange(event) {
|
|
84
89
|
const previousState = workers.get(event.target);
|
|
85
90
|
workers.set(event.target, event.target.state);
|
|
91
|
+
if (event.target.state === 'redundant') {
|
|
92
|
+
for (const operation of pendingRefreshes) {
|
|
93
|
+
if (operation.worker === event.target) {
|
|
94
|
+
operation.cancel();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
86
98
|
// A newer installation normally supersedes an already waiting worker.
|
|
87
99
|
if (event.target.state === 'redundant'
|
|
88
100
|
&& previousState !== 'activated' && previousState !== 'installed') {
|
|
@@ -107,7 +119,118 @@ export function registerPwa({workerUrl = './arcane-sw.js', scope} = {}) {
|
|
|
107
119
|
watchWorker(registration.installing);
|
|
108
120
|
watchWorker(registration.waiting);
|
|
109
121
|
watchWorker(registration.active);
|
|
122
|
+
watchWorker(container.controller);
|
|
110
123
|
publish(currentStatus());
|
|
124
|
+
const worker = registration.active ?? container.controller;
|
|
125
|
+
if (worker?.state === 'activated' && !queriedWorkers.has(worker)) {
|
|
126
|
+
queriedWorkers.add(worker);
|
|
127
|
+
try {
|
|
128
|
+
worker.postMessage({type: 'arcane.pwa.capabilities'});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
publish('error', error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function startResourceRefresh(worker, cacheName) {
|
|
136
|
+
if (disposed || worker.state !== 'activated' || refreshedWorkers.has(worker)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
refreshedWorkers.add(worker);
|
|
140
|
+
const task = refreshCachedResources(worker, cacheName);
|
|
141
|
+
refreshTasks.add(task);
|
|
142
|
+
task.then(
|
|
143
|
+
function resourceRefreshComplete() {
|
|
144
|
+
refreshTasks.delete(task);
|
|
145
|
+
},
|
|
146
|
+
function resourceRefreshFailed(error) {
|
|
147
|
+
refreshTasks.delete(task);
|
|
148
|
+
if (error?.code !== 'ARCANE_PWA_REFRESH_CANCELLED') {
|
|
149
|
+
publish('error', error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function loadCheckStorage() {
|
|
156
|
+
if (!globalThis.dbopfs) {
|
|
157
|
+
await import('arcane/DBOPFS');
|
|
158
|
+
}
|
|
159
|
+
const storage = globalThis.dbopfs;
|
|
160
|
+
if (!storage) {
|
|
161
|
+
throw new Error('PWA check history could not open DBOPFS.');
|
|
162
|
+
}
|
|
163
|
+
await storage.readyPromise;
|
|
164
|
+
return storage;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function requestResourceRefresh(worker, lastChecked) {
|
|
168
|
+
const channel = new MessageChannel();
|
|
169
|
+
return new Promise(
|
|
170
|
+
function resourceRefreshReply(resolve, reject) {
|
|
171
|
+
function cleanup() {
|
|
172
|
+
pendingRefreshes.delete(operation);
|
|
173
|
+
channel.port1.onmessage = null;
|
|
174
|
+
channel.port1.onmessageerror = null;
|
|
175
|
+
channel.port1.close();
|
|
176
|
+
channel.port2.close();
|
|
177
|
+
}
|
|
178
|
+
function cancel() {
|
|
179
|
+
cleanup();
|
|
180
|
+
const error = new Error('The PWA resource refresh owner is no longer active.');
|
|
181
|
+
error.code = 'ARCANE_PWA_REFRESH_CANCELLED';
|
|
182
|
+
reject(error);
|
|
183
|
+
}
|
|
184
|
+
const operation = {worker, cancel};
|
|
185
|
+
pendingRefreshes.add(operation);
|
|
186
|
+
channel.port1.onmessage = function receiveResourceRefresh(event) {
|
|
187
|
+
if (event.data?.type === 'arcane.pwa.refreshed') {
|
|
188
|
+
cleanup();
|
|
189
|
+
resolve(event.data);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
channel.port1.onmessageerror = function unreadableResourceRefresh() {
|
|
193
|
+
cleanup();
|
|
194
|
+
reject(new Error('The PWA resource refresh reply could not be read.'));
|
|
195
|
+
};
|
|
196
|
+
channel.port1.start();
|
|
197
|
+
try {
|
|
198
|
+
worker.postMessage({type: 'arcane.pwa.refresh', lastChecked}, [channel.port2]);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
cleanup();
|
|
201
|
+
reject(error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function refreshCachedResources(worker, cacheName) {
|
|
208
|
+
storageTask ??= loadCheckStorage();
|
|
209
|
+
const storage = await storageTask;
|
|
210
|
+
const key = `${encodeURIComponent(cacheName)}.json`;
|
|
211
|
+
async function refreshStoredChecks() {
|
|
212
|
+
if (disposed || worker.state !== 'activated') {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// A fresh read under the shared lock preserves checks made by another tab.
|
|
216
|
+
const record = await storage.get('pwa', key, true);
|
|
217
|
+
if (disposed || worker.state !== 'activated') {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const result = await requestResourceRefresh(worker, record?.lastChecked ?? null);
|
|
221
|
+
if (!result.error && Number.isFinite(result.lastChecked) && result.lastChecked !== record?.lastChecked) {
|
|
222
|
+
await storage.set('pwa', key, {lastChecked: result.lastChecked});
|
|
223
|
+
}
|
|
224
|
+
if (result.error) {
|
|
225
|
+
publish('error', result.error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const locks = globalThis.navigator?.locks;
|
|
229
|
+
if (locks?.request) {
|
|
230
|
+
await locks.request(`arcane-pwa-checks|${cacheName}`, refreshStoredChecks);
|
|
231
|
+
} else {
|
|
232
|
+
await refreshStoredChecks();
|
|
233
|
+
}
|
|
111
234
|
}
|
|
112
235
|
|
|
113
236
|
function onControllerChange() {
|
|
@@ -115,6 +238,9 @@ export function registerPwa({workerUrl = './arcane-sw.js', scope} = {}) {
|
|
|
115
238
|
}
|
|
116
239
|
|
|
117
240
|
function onWorkerMessage(event) {
|
|
241
|
+
if (event.data?.type === 'arcane.pwa.capabilities' && event.data.refresh === true && workers.has(event.source)) {
|
|
242
|
+
startResourceRefresh(event.source, event.data.cacheName);
|
|
243
|
+
}
|
|
118
244
|
if (event.data?.type === 'arcane.pwa.error' && workers.has(event.source)) {
|
|
119
245
|
publish('error', event.data.error);
|
|
120
246
|
}
|
|
@@ -205,6 +331,9 @@ export function registerPwa({workerUrl = './arcane-sw.js', scope} = {}) {
|
|
|
205
331
|
for (const removeListener of listeners) {
|
|
206
332
|
removeListener();
|
|
207
333
|
}
|
|
334
|
+
for (const operation of pendingRefreshes) {
|
|
335
|
+
operation.cancel();
|
|
336
|
+
}
|
|
208
337
|
listeners.length = 0;
|
|
209
338
|
workers.clear();
|
|
210
339
|
source.dispose();
|
package/docs/architecture.md
CHANGED
|
@@ -110,21 +110,37 @@ behavior or claiming that the capability exists.
|
|
|
110
110
|
|
|
111
111
|
Rapid development uses `arcane dev`. The development server maps the selected
|
|
112
112
|
application's canonical source tree and the live installed SDK/runtime routes.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
At startup, the SDK refreshes only the selected authored app descriptor's
|
|
114
|
+
schema-1 package projection and managed import maps under the existing
|
|
115
|
+
development-refresh lock, then releases the lock before binding the listener.
|
|
116
|
+
The authored descriptor remains unchanged, and package-only apps retain their
|
|
117
|
+
existing path. An enabled PWA receives generated manifests directly from this
|
|
118
|
+
source server without creating `dist` output.
|
|
119
|
+
Changed resource requests return the complete current saved source without
|
|
120
|
+
packaging, copying files into `dist`, or restarting the server. Conditional
|
|
121
|
+
requests for unchanged resources return `304`. Enabled PWAs check on page load
|
|
122
|
+
when their single DBOPFS `lastChecked` value is older than 120 seconds in
|
|
123
|
+
development or 15 minutes in packaged browser delivery. Cached resource bodies have no SDK
|
|
124
|
+
expiration. The timestamp advances only after the whole resource check succeeds.
|
|
125
|
+
Restarting is not a content synchronization step; inspect the
|
|
126
|
+
selected source route and the last successful check when evaluating freshness.
|
|
127
|
+
|
|
128
|
+
The shared dev server uses RIAEvangelist's `node-http-server` public interface
|
|
129
|
+
for HTTPS and conditional responses on those selected routes. The SDK
|
|
130
|
+
owns source selection and generated representations. Every Arcane development
|
|
131
|
+
server and packaged browser preview serves content on HTTPS and redirects its
|
|
132
|
+
paired HTTP listener with status 308 through the public request hook.
|
|
133
|
+
`arcane dev --public` selects the IPv4 wildcard address;
|
|
134
|
+
explicit `--host` controls the bind address. CLI startup reads one workspace-local
|
|
135
|
+
PEM pair before binding, including ordinary localhost startup.
|
|
136
|
+
The certificate covers the device-facing address, and
|
|
124
137
|
each client trusts its issuing CA through that platform's certificate setup.
|
|
125
138
|
This supplies the secure origin required by OPFS/DBOPFS on LAN devices. The
|
|
126
139
|
server does not install trust, generate certificates, or expose private TLS
|
|
127
|
-
material through CLI events.
|
|
140
|
+
material through CLI events. `port` selects HTTPS; `httpPort` selects the HTTP
|
|
141
|
+
redirect listener, defaulting to an OS-assigned port. Existing raw `tls` inputs
|
|
142
|
+
retain their native HTTPS transport under the SDK, as described by the module's
|
|
143
|
+
advanced TLS extension guidance; all content uses its public serving methods.
|
|
128
144
|
|
|
129
145
|
Development is an intentionally fast feedback loop. Keep each increment small
|
|
130
146
|
and independently understandable so its effect has one clear cause and a
|
package/docs/reference/cli.md
CHANGED
|
@@ -48,10 +48,11 @@ meaning and cardinality rules:
|
|
|
48
48
|
| `--workspace` | directory | Commands that select an external or integrated workspace; defaults to `.`. |
|
|
49
49
|
| `--app` | app id | Workspace/app operations except shared scope and `verify-bundle`; also the exact `mail serve` caller id. |
|
|
50
50
|
| `--arcane-root` | directory | `doctor`, native `build`/`run`, `native-doctor`, `native-prepare` |
|
|
51
|
-
| `--host` / `--port` | host / integer 0–65535 | Browser `dev`/`run` default to `127.0.0.1:8000`; `mail serve` defaults to `127.0.0.1:8025` and admits numeric loopback only. |
|
|
51
|
+
| `--host` / `--port` | host / integer 0–65535 | Browser `dev`/`run` default to HTTPS at `127.0.0.1:8000`; `mail serve` defaults to HTTP at `127.0.0.1:8025` and admits numeric loopback only. |
|
|
52
|
+
| `--http-port` | integer 0–65535 | Browser `dev`/`run` HTTP redirect listener; defaults to `0`, which selects an available port. |
|
|
52
53
|
| `--public` | flag | `dev`; serves HTTPS and binds to `0.0.0.0` unless `--host` explicitly selects another address. |
|
|
53
|
-
| `--https` | flag | `dev`;
|
|
54
|
-
| `--cert` / `--key` | PEM file paths | `dev`; supply both
|
|
54
|
+
| `--https` | flag | Browser `dev`/`run`; retained explicitly, while HTTPS is always enabled. |
|
|
55
|
+
| `--cert` / `--key` | PEM file paths | Browser `dev`/`run`; supply both for an explicit certificate chain and private key. Relative paths resolve from the workspace. |
|
|
55
56
|
| `--target` | target id | `new`, `init`, native diagnostics, `build`, `run` |
|
|
56
57
|
| `--format` / `--signing` | target-supported values | Native diagnostics, `build`, `run` |
|
|
57
58
|
| `--output-root` | directory | Native `build` and `run` |
|
|
@@ -311,7 +312,7 @@ npm exec -- arcane upgrade --workspace . --app hello-world
|
|
|
311
312
|
### Overview
|
|
312
313
|
|
|
313
314
|
Starts one development server for one selected app and maps the exact
|
|
314
|
-
workspace/runtime routes. It defaults to localhost; `--public` enables access
|
|
315
|
+
workspace/runtime routes. It defaults to HTTPS on localhost; `--public` enables access
|
|
315
316
|
from other devices on the network over HTTPS.
|
|
316
317
|
|
|
317
318
|
For an external workspace, the server exposes the selected projected
|
|
@@ -321,11 +322,19 @@ The explicit live-source SDK mapping remains unchanged and does not replace the
|
|
|
321
322
|
installed projection.
|
|
322
323
|
|
|
323
324
|
```text
|
|
324
|
-
arcane dev [--app <id>] [--public] [--https] [--cert <file> --key <file>] [--host <address>] [--port 8000]
|
|
325
|
+
arcane dev [--app <id>] [--public] [--https] [--cert <file> --key <file>] [--host <address>] [--port 8000] [--http-port 0]
|
|
325
326
|
```
|
|
326
327
|
|
|
327
328
|
### Lifecycle
|
|
328
329
|
|
|
330
|
+
Startup refreshes the selected authored `arcane-app.json` projection into
|
|
331
|
+
`arcane-package.json`, then refreshes its managed import maps under one
|
|
332
|
+
development-refresh operation lock. The lock is released before the server
|
|
333
|
+
starts. Package-only apps retain their existing manifest. This operation does
|
|
334
|
+
not package the app or produce `dist` output. With PWA enabled, the server
|
|
335
|
+
generates the installation and offline manifests directly; see
|
|
336
|
+
[PWA development and versioning](pwa.md#development-and-hosting).
|
|
337
|
+
|
|
329
338
|
The command reports acceptance before bind/start work, emits the final URL,
|
|
330
339
|
owns the server until cancellation, and restores failure to the process exit.
|
|
331
340
|
The default host is `127.0.0.1`. `--public` selects `0.0.0.0` (all IPv4
|
|
@@ -335,25 +344,33 @@ device, since `localhost` refers to that device and `0.0.0.0` is a bind address.
|
|
|
335
344
|
Network URLs come from one interface snapshot at startup and do not establish
|
|
336
345
|
remote reachability through the machine's firewall or network.
|
|
337
346
|
|
|
338
|
-
|
|
339
|
-
`--https`
|
|
340
|
-
`--
|
|
341
|
-
|
|
347
|
+
Every Arcane app uses HTTPS for development and packaged browser previews.
|
|
348
|
+
`--https` remains accepted but is no longer needed to select the transport.
|
|
349
|
+
`--port` selects the HTTPS application port. A second HTTP listener returns
|
|
350
|
+
`308` redirects to that HTTPS port, preserving the requested path and query.
|
|
351
|
+
`--http-port` selects its port; the default `0` lets the operating system choose
|
|
352
|
+
an available port. Both listeners use the selected host and must be ready
|
|
353
|
+
before startup completes. Cancellation or a listener failure closes both.
|
|
354
|
+
Human output prints `HTTP redirect: <url>` alongside the HTTPS application URL;
|
|
355
|
+
JSON/NDJSON server results include `httpPort`, `httpOrigin`, and `httpUrl` for
|
|
356
|
+
the redirect endpoint.
|
|
357
|
+
Supplying both `--cert` and `--key` selects an explicit PEM pair. The command
|
|
358
|
+
does not configure a firewall, router forwarding, or an internet tunnel.
|
|
342
359
|
|
|
343
360
|
### Development HTTPS setup
|
|
344
361
|
|
|
345
|
-
Before starting
|
|
346
|
-
chain at `.arcane/dev/server-cert.pem` and its PEM private key at
|
|
362
|
+
Before starting `arcane dev` or a packaged browser preview, place the server's
|
|
363
|
+
PEM certificate chain at `.arcane/dev/server-cert.pem` and its PEM private key at
|
|
347
364
|
`.arcane/dev/server-key.pem`, relative to the workspace. Alternatively, pass
|
|
348
|
-
`--cert <file> --key <file>` together. The certificate must cover the LAN IP
|
|
365
|
+
`--cert <file> --key <file>` together. The certificate must cover localhost or the LAN IP
|
|
349
366
|
address or hostname opened by each device. Certificate creation and renewal
|
|
350
367
|
belong to the developer's certificate tooling; the server does not generate a
|
|
351
368
|
CA or alter device trust stores. Keep `.arcane/dev/` ignored by Git and keep the
|
|
352
369
|
private key on the development computer.
|
|
353
370
|
|
|
354
|
-
The server reads the selected pair
|
|
355
|
-
|
|
356
|
-
|
|
371
|
+
The server reads the selected pair during startup, before binding.
|
|
372
|
+
Missing files or certificate/key parse errors produce a startup error;
|
|
373
|
+
Arcane never silently falls back to HTTP. Certificate/key contents are not
|
|
357
374
|
included in operation events or JSON/NDJSON output. Restart the server after
|
|
358
375
|
replacing its certificate pair; ordinary app source edits still appear on
|
|
359
376
|
refresh without restarting.
|
|
@@ -383,6 +400,9 @@ npm run dev -- --app hello-world --public
|
|
|
383
400
|
|
|
384
401
|
# Equivalent direct CLI invocation, with an optional port.
|
|
385
402
|
npm exec -- arcane dev --app hello-world --public --port 8000
|
|
403
|
+
|
|
404
|
+
# Select a stable HTTP entry that redirects to HTTPS on port 8000.
|
|
405
|
+
npm exec -- arcane dev --app hello-world --port 8000 --http-port 8080
|
|
386
406
|
```
|
|
387
407
|
|
|
388
408
|
## `arcane test`
|
|
@@ -617,11 +637,17 @@ npm exec -- arcane build \
|
|
|
617
637
|
|
|
618
638
|
For `--target browser`, starts the existing current `dist/<app>` release; it
|
|
619
639
|
does not package, rebuild, test, check, or verify that release automatically.
|
|
640
|
+
The preview always uses HTTPS with the workspace certificate pair. Supply
|
|
641
|
+
`--cert <file> --key <file>` together to use another pair; see
|
|
642
|
+
[development HTTPS setup](#development-https-setup).
|
|
643
|
+
`--port` selects the HTTPS application port, and `--http-port` selects the
|
|
644
|
+
paired HTTP `308` redirect port. The HTTP port defaults to an available port;
|
|
645
|
+
the CLI prints its actual redirect URL and reports both listener endpoints.
|
|
620
646
|
For a paired native target, it performs package, prepare, plan, build, launch,
|
|
621
647
|
readiness, and owned cancellation in one process.
|
|
622
648
|
|
|
623
649
|
```text
|
|
624
|
-
arcane run [--target <target>] [--app <id>] [--arcane-root <directory>] [--output-root <directory>] [--format <format>] [--signing <mode>]
|
|
650
|
+
arcane run [--target <target>] [--app <id>] [--cert <file> --key <file>] [--port 8000] [--http-port 0] [--arcane-root <directory>] [--output-root <directory>] [--format <format>] [--signing <mode>]
|
|
625
651
|
```
|
|
626
652
|
|
|
627
653
|
### Availability
|
|
@@ -2991,7 +2991,7 @@
|
|
|
2991
2991
|
"entrypoints": ["arcane-os/pwa"],
|
|
2992
2992
|
"primaryImport": "arcane-os/pwa",
|
|
2993
2993
|
"group": "Progressive web applications",
|
|
2994
|
-
"summary": "Starts PWA registration without blocking
|
|
2994
|
+
"summary": "Starts PWA registration and DBOPFS-backed page-load resource checks without blocking rendering, returning an observable lifecycle owner.",
|
|
2995
2995
|
"availability": "Browser service workers; explicit unsupported state elsewhere",
|
|
2996
2996
|
"protocol": "Native registration, updatefound, statechange and controllerchange events",
|
|
2997
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."
|
package/docs/reference/pwa.md
CHANGED
|
@@ -83,31 +83,90 @@ the [existing asset version contract](asset-versioning.md).
|
|
|
83
83
|
|
|
84
84
|
## Development and hosting
|
|
85
85
|
|
|
86
|
+
Use the ordinary `arcane dev --app <id>` command after editing the selected
|
|
87
|
+
app's `arcane-app.json`. Startup refreshes that app's generated
|
|
88
|
+
`arcane-package.json` from the authored descriptor before refreshing its import
|
|
89
|
+
maps and starting the server. No packaging or `dist` output is required.
|
|
90
|
+
Package-only applications retain their existing descriptor workflow.
|
|
91
|
+
|
|
92
|
+
Add a file or directory to `package.include` to make it part of the app's
|
|
93
|
+
resources. A new file inside an already included directory needs no separate
|
|
94
|
+
entry. If `package.pwa.offline.include` is nonempty, the resource must also
|
|
95
|
+
match that offline selection and must not match `offline.exclude`. Adding a
|
|
96
|
+
path only to the offline selection does not add it to the app's resources.
|
|
97
|
+
Restart after changing descriptor settings. Edits to selected source files are
|
|
98
|
+
picked up by the next due page-load check while the server remains running.
|
|
99
|
+
|
|
86
100
|
For an enabled application, `arcane dev` serves the generated PWA files at the
|
|
87
101
|
origin root and starts at the selected app page under `/apps/<id>/`. Use one
|
|
88
|
-
selected app per development origin.
|
|
89
|
-
and
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
selected app per development origin. The SDK uses `node-http-server` for source
|
|
103
|
+
and packaged-preview serving, including conditional resource responses.
|
|
104
|
+
Every Arcane development server and packaged browser preview serves HTTPS,
|
|
105
|
+
including localhost. Configure the workspace certificate pair before starting
|
|
106
|
+
the ordinary command; see [development HTTPS setup](cli.md#development-https-setup).
|
|
107
|
+
|
|
108
|
+
Source inventory work begins when the browser requests the worker or current
|
|
109
|
+
offline manifest, after the page can start. It traverses the selected route
|
|
110
|
+
inventory once for that request, follows page and runtime resource references
|
|
111
|
+
to retain selected query variants, and shares an in-flight traversal with
|
|
112
|
+
concurrent requests. Each referenced source file is read once per traversal;
|
|
97
113
|
document corpus bodies remain under their existing owner. It does not rebuild
|
|
98
|
-
the application.
|
|
99
|
-
bundle.
|
|
114
|
+
the application. Installation metadata and bootstrap requests reuse the current
|
|
115
|
+
generated bundle.
|
|
116
|
+
|
|
117
|
+
The SDK owns version information in `arcane-offline.json`:
|
|
118
|
+
|
|
119
|
+
| Field | Owner and update rule |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `schemaVersion` | SDK offline-manifest format; currently `1`. |
|
|
122
|
+
| `appVersion` | The app descriptor's top-level `version`. |
|
|
123
|
+
| `sdkVersion` | The selected installed SDK or explicit live SDK source version. |
|
|
124
|
+
| `revision` | `development` for the source server; a fresh generated deployment ID for each packaged output. |
|
|
100
125
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
Do not hand-edit generated manifests or bump a version for every source edit.
|
|
127
|
+
`arcane.webmanifest` holds installation metadata and has no separate SDK-managed
|
|
128
|
+
release counter. Resource freshness uses each response's `Last-Modified` header.
|
|
129
|
+
|
|
130
|
+
On each page load, the SDK reads one `lastChecked` value for the app and worker
|
|
131
|
+
scope from DBOPFS in the background. When that value is missing or older than
|
|
132
|
+
the delivery mode's interval, it checks the current offline manifest and every
|
|
133
|
+
selected resource:
|
|
134
|
+
|
|
135
|
+
| Mode | Page-load check interval |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| Development | 120 seconds |
|
|
138
|
+
| Packaged browser delivery | 15 minutes |
|
|
139
|
+
|
|
140
|
+
These intervals schedule revalidation; they never expire a cached file. The
|
|
141
|
+
worker sends `GET` with `If-Modified-Since` using the cached response's
|
|
142
|
+
`Last-Modified`. A `304 Not Modified` retains the complete cached response. A
|
|
143
|
+
successful `200` replaces it after the new response is stored. Missing cached
|
|
144
|
+
resources are downloaded. Network and server failures retain an existing
|
|
145
|
+
offline copy and remain observable through SDK diagnostics. A host without
|
|
146
|
+
modification headers must send the current response because freshness cannot
|
|
147
|
+
be established from a missing header.
|
|
148
|
+
|
|
149
|
+
Complete resource responses remain in browser CacheStorage. DBOPFS stores one
|
|
150
|
+
successful whole-cycle timestamp, updated only after the manifest and every
|
|
151
|
+
selected resource have been checked successfully. A partial failure preserves
|
|
152
|
+
the previous timestamp so the next page load can retry. Each cached response
|
|
153
|
+
retains its own `Last-Modified` header, but there are no per-file check times.
|
|
154
|
+
The SDK imposes no age-based cache deletion and
|
|
155
|
+
retains resource bodies across app and SDK version changes. Requests for a page
|
|
156
|
+
do not wait for the complete resource inventory to finish checking. The SDK
|
|
157
|
+
uses at most four concurrent background resource requests and starts no timer
|
|
158
|
+
or polling loop between page loads.
|
|
159
|
+
|
|
160
|
+
During initial installation, selected resources are cached before the browser
|
|
161
|
+
activates the worker. Failures remain observable through native worker state
|
|
162
|
+
and SDK diagnostics. HTTP modification dates have second-level precision;
|
|
163
|
+
hosts must report changes to the served representation, including generated
|
|
164
|
+
output, rather than only the date of an unrelated source file.
|
|
107
165
|
|
|
108
166
|
The SDK development server sends `Cache-Control: no-cache` for PWA resources.
|
|
109
|
-
An independent static host
|
|
110
|
-
|
|
167
|
+
An independent static host should support `Last-Modified` and conditional GET
|
|
168
|
+
for stable HTML, module, style and import-map URLs, revalidate worker URLs, and
|
|
169
|
+
serve JavaScript with a JavaScript content type.
|
|
111
170
|
Keep the worker beside the deployment root it controls. The browser requires a
|
|
112
171
|
supported secure context, such as trusted HTTPS or localhost, to register it.
|
|
113
172
|
The SDK does not change certificates or browser permissions.
|
|
@@ -132,9 +191,15 @@ worker while they are open. Closing those pages allows activation; a refresh
|
|
|
132
191
|
can leave overlapping document clients and keep the update waiting.
|
|
133
192
|
|
|
134
193
|
The SDK does not call `skipWaiting`, claim the initial page, reload a page,
|
|
135
|
-
restart a model or poll for updates.
|
|
136
|
-
|
|
137
|
-
data and caches owned by other capabilities are untouched.
|
|
194
|
+
restart a model or poll for updates. Worker activation preserves cached
|
|
195
|
+
resources and the DBOPFS check history for the same app and registration scope.
|
|
196
|
+
Saved application data and caches owned by other capabilities are untouched.
|
|
197
|
+
|
|
198
|
+
When importing caches from an older SDK worker, the new worker fetches the
|
|
199
|
+
SDK-owned registration bootstrap and PWA client once so the page can use the
|
|
200
|
+
current cache-check protocol. Other cached resource bodies retain the normal
|
|
201
|
+
check cadence. This transition follows native worker installation and
|
|
202
|
+
activation without forcing a page reload.
|
|
138
203
|
|
|
139
204
|
Switching a server from a packaged release to live development does not replace
|
|
140
205
|
an already active release worker inside an open document. The same native
|