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
|
@@ -4,7 +4,7 @@ The npm package exposes a Node.js ESM control plane, the portable
|
|
|
4
4
|
`arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`,
|
|
5
5
|
`arcane-os/preference-store`, `arcane-os/speech-playback`,
|
|
6
6
|
`arcane-os/speech-text`, and `arcane-os/browser-device` entrypoints, and the browser-only
|
|
7
|
-
`arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints.
|
|
7
|
+
`arcane-os/pwa`, `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints.
|
|
8
8
|
Those package subpaths are distinct from application-facing projection modules
|
|
9
9
|
in the managed browser map, such as `arcane/AIProviderRuntime`,
|
|
10
10
|
`arcane/AIRuntimeState`, and `arcane/ThemeBootstrap`. Applications use those
|
|
@@ -39,6 +39,7 @@ for the installed-inventory-derived physical-runtime contract in SDK `0.5.18`.
|
|
|
39
39
|
| `arcane-os/speech-playback` | Portable speech preparation, playback state, and injected media adapters. |
|
|
40
40
|
| `arcane-os/speech-text` | Speech-input formatting cleanup for complete text and streamed chunks. |
|
|
41
41
|
| `arcane-os/browser-device` | Synchronous mobile or desktop identity hints for application-owned settings. |
|
|
42
|
+
| `arcane-os/pwa` | Nonblocking PWA registration, native worker updates and observable lifecycle state. |
|
|
42
43
|
| `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
|
|
43
44
|
| `arcane-os/ai/browser-speech` | Caller-selected browser-local Whisper STT and Kokoro TTS provider mechanisms, ordinary upstream assets, materialized/native routing, Workers, and cancellation. |
|
|
44
45
|
| `arcane-os/mail` | Portable Mail runtime, durable outbox, complete transport responses, and provider-neutral acceptance contracts. |
|
|
@@ -126,6 +127,8 @@ browser map are cataloged separately in [Runtime modules](runtime-modules.md).
|
|
|
126
127
|
| `createCanonicalUstarHeader()` | function | `arcane-os` | Packaging and release bundles | Node |
|
|
127
128
|
| `createDbopfsModelStore()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser with a ready DBOPFS instance and OPFS |
|
|
128
129
|
| `getBrowserDeviceClass()` | function | `arcane-os/browser-device` | Browser device settings | Node and Browser; synchronous identity hint with no model, storage, or GPU operation |
|
|
130
|
+
| `PWA_STATE_EVENT` | constant | `arcane-os/pwa` | Progressive web applications | Browser lifecycle event name; importable without registration |
|
|
131
|
+
| `registerPwa()` | function | `arcane-os/pwa` | Progressive web applications | Browser service workers; synchronous unsupported state when unavailable |
|
|
129
132
|
| `createDbopfsSpeechArtifactStore()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with ready DBOPFS, Web Locks, Fetch, File/Blob, and object URLs |
|
|
130
133
|
| `removeBrowserSpeechModelCache()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser CacheStorage; explicit removal of one selected upstream model |
|
|
131
134
|
| `createNativeBuildPlan()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
|
|
@@ -6046,6 +6049,47 @@ const deviceClass = getBrowserDeviceClass();
|
|
|
6046
6049
|
console.log(deviceClass); // mobile or desktop
|
|
6047
6050
|
```
|
|
6048
6051
|
|
|
6052
|
+
## PWA_STATE_EVENT
|
|
6053
|
+
|
|
6054
|
+
### Overview
|
|
6055
|
+
|
|
6056
|
+
The event name `arcane.pwa.state` identifies PWA registration and native worker
|
|
6057
|
+
lifecycle state published through the existing Arcane event owner.
|
|
6058
|
+
|
|
6059
|
+
### Example
|
|
6060
|
+
|
|
6061
|
+
```javascript
|
|
6062
|
+
import {PWA_STATE_EVENT} from 'arcane-os/pwa';
|
|
6063
|
+
console.log(PWA_STATE_EVENT);
|
|
6064
|
+
```
|
|
6065
|
+
|
|
6066
|
+
See the [PWA state contract](pwa.md#registerpwa) for complete payload fields and
|
|
6067
|
+
current-state subscriptions. Importing the constant does not register a worker.
|
|
6068
|
+
|
|
6069
|
+
## registerPwa()
|
|
6070
|
+
|
|
6071
|
+
### Overview
|
|
6072
|
+
|
|
6073
|
+
`registerPwa({workerUrl = './arcane-sw.js', scope} = {})` starts native browser
|
|
6074
|
+
registration and returns a synchronous owner with `ready`, `state`, `subscribe`,
|
|
6075
|
+
`update` and `dispose`. It does not block page rendering or load models.
|
|
6076
|
+
Unsupported environments receive an explicit unsupported state.
|
|
6077
|
+
|
|
6078
|
+
### Example
|
|
6079
|
+
|
|
6080
|
+
```javascript
|
|
6081
|
+
import {registerPwa} from 'arcane-os/pwa';
|
|
6082
|
+
const pwa = registerPwa();
|
|
6083
|
+
pwa.ready.catch(function reportRegistrationError(error) {
|
|
6084
|
+
console.error(error);
|
|
6085
|
+
});
|
|
6086
|
+
```
|
|
6087
|
+
|
|
6088
|
+
The [PWA guide](pwa.md) defines inputs, native registration results, error state,
|
|
6089
|
+
current-state replay, disposal, offline resource ownership and update behavior.
|
|
6090
|
+
The generated bootstrap registers automatically; a manual caller owns a separate
|
|
6091
|
+
entry point and must observe its `ready` rejection.
|
|
6092
|
+
|
|
6049
6093
|
## createBrowserWasmLlmProvider()
|
|
6050
6094
|
|
|
6051
6095
|
### Overview
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"./event-manager": "./src/event-manager.mjs",
|
|
16
16
|
"./logging": "./browser-runtime/logging.mjs",
|
|
17
17
|
"./browser-device": "./browser-runtime/browser-device.mjs",
|
|
18
|
+
"./pwa": "./browser-runtime/pwa.mjs",
|
|
18
19
|
"./preference-store": "./runtime/arcane/modules/PreferenceStore.js",
|
|
19
20
|
"./speech-playback": "./runtime/arcane/modules/SpeechPlayback.js",
|
|
20
21
|
"./speech-text": "./browser-runtime/speech-text.mjs",
|
|
@@ -66,7 +67,7 @@
|
|
|
66
67
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
67
68
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
68
69
|
"test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/contracts.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
|
|
69
|
-
"test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dev-server.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/packaging.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/update-check.test.mjs",
|
|
70
|
+
"test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/update-check.test.mjs",
|
|
70
71
|
"test:integration": "node ./bin/arcane-test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-plan.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/wllama-webgpu-runtime.test.mjs",
|
|
71
72
|
"test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
|
|
72
73
|
"check": "node tools/check-source.mjs && npm test",
|
|
@@ -477,6 +477,7 @@ class Errors {
|
|
|
477
477
|
return;
|
|
478
478
|
}
|
|
479
479
|
|
|
480
|
+
// Restored records resume delivery; only live capture offers developer modals.
|
|
480
481
|
const timestamp=this.now();
|
|
481
482
|
for(const storedRecord of records){
|
|
482
483
|
if(
|
|
@@ -519,7 +520,6 @@ class Errors {
|
|
|
519
520
|
if(!record.retryRequired){
|
|
520
521
|
this.scheduleRecord(record,dueAt-timestamp);
|
|
521
522
|
}
|
|
522
|
-
this.offerDeveloperIncident(record.incident,record.occurrenceId);
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
this.persistLedger();
|
|
@@ -19,8 +19,13 @@ let htmlImportScriptId=0;
|
|
|
19
19
|
const htmlImportModuleURL=new URL(import.meta.url);
|
|
20
20
|
const htmlImportAssetVersion=htmlImportModuleURL.searchParams.get('arcaneVersion');
|
|
21
21
|
|
|
22
|
+
function pwaResourceUrlsEnabled(){
|
|
23
|
+
return Boolean(document.querySelector?.('script[data-arcane-pwa]'));
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
function versionComponentResource(value,baseHref){
|
|
23
|
-
|
|
27
|
+
const clean=pwaResourceUrlsEnabled();
|
|
28
|
+
if((!htmlImportAssetVersion&&!clean)||!is.string(value)||!value||value.startsWith('#')){
|
|
24
29
|
return value;
|
|
25
30
|
}
|
|
26
31
|
let resolvedURL;
|
|
@@ -46,7 +51,7 @@ function versionComponentResource(value,baseHref){
|
|
|
46
51
|
const parameter=new URLSearchParams(field);
|
|
47
52
|
if(parameter.has('v'))return null;
|
|
48
53
|
if(!parameter.has('arcaneVersion'))return field;
|
|
49
|
-
if(replaced)return null;
|
|
54
|
+
if(clean||replaced)return null;
|
|
50
55
|
replaced=true;
|
|
51
56
|
const equals=field.indexOf('=');
|
|
52
57
|
const key=equals<0?field:field.slice(0,equals);
|
|
@@ -54,6 +59,11 @@ function versionComponentResource(value,baseHref){
|
|
|
54
59
|
}).filter(function retainQueryField(field){
|
|
55
60
|
return field!==null;
|
|
56
61
|
}):[];
|
|
62
|
+
if(clean){
|
|
63
|
+
return fields.some(function hasRemainingField(field){return field!=='';})
|
|
64
|
+
?`${pathname}?${fields.join('&')}${fragment}`
|
|
65
|
+
:`${pathname}${fragment}`;
|
|
66
|
+
}
|
|
57
67
|
if(!replaced){
|
|
58
68
|
if(fields.at(-1)==='')fields[fields.length-1]=versionField;
|
|
59
69
|
else fields.push(versionField);
|
|
@@ -81,7 +91,7 @@ function resolveComponentResource(value,runtimeRoot){
|
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
function resolveComponentStyleResources(styleText,runtimeRoot){
|
|
84
|
-
if(!runtimeRoot&&!htmlImportAssetVersion)return styleText;
|
|
94
|
+
if(!runtimeRoot&&!htmlImportAssetVersion&&!pwaResourceUrlsEnabled())return styleText;
|
|
85
95
|
const parts=[];
|
|
86
96
|
let copiedThrough=0;
|
|
87
97
|
let index=0;
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"entry": { "$ref": "#/$defs/relativePath" },
|
|
41
41
|
"strategy": { "enum": ["static", "adapter"] },
|
|
42
42
|
"localAIModelPolicy": { "$ref": "#/$defs/localAIModelPolicy" },
|
|
43
|
+
"pwa": { "$ref": "#/$defs/pwa" },
|
|
43
44
|
"include": { "$ref": "#/$defs/nonemptyPathList" },
|
|
44
45
|
"exclude": { "$ref": "#/$defs/pathList" },
|
|
45
46
|
"shared": {
|
|
@@ -224,6 +225,33 @@
|
|
|
224
225
|
}
|
|
225
226
|
],
|
|
226
227
|
"$defs": {
|
|
228
|
+
"pwa": {
|
|
229
|
+
"type": "object",
|
|
230
|
+
"additionalProperties": false,
|
|
231
|
+
"properties": {
|
|
232
|
+
"enabled": { "type": "boolean", "default": false },
|
|
233
|
+
"manifest": {
|
|
234
|
+
"type": "object",
|
|
235
|
+
"description": "Application-owned Web App Manifest members, including branding, icons, and presentation."
|
|
236
|
+
},
|
|
237
|
+
"offline": {
|
|
238
|
+
"type": "object",
|
|
239
|
+
"additionalProperties": false,
|
|
240
|
+
"properties": {
|
|
241
|
+
"include": {
|
|
242
|
+
"type": "array",
|
|
243
|
+
"description": "Literal package paths or directories to cache. Omitted or empty selects all emitted files.",
|
|
244
|
+
"items": { "type": "string", "minLength": 1 }
|
|
245
|
+
},
|
|
246
|
+
"exclude": {
|
|
247
|
+
"type": "array",
|
|
248
|
+
"description": "Literal package paths or directories excluded from offline assets. Generated shell records and the entry remain available.",
|
|
249
|
+
"items": { "type": "string", "minLength": 1 }
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
227
255
|
"appId": { "type": "string", "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$" },
|
|
228
256
|
"presentationText": {
|
|
229
257
|
"type": "string",
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
"localAIModelPolicy": {
|
|
44
44
|
"$ref": "#/$defs/localAIModelPolicy"
|
|
45
45
|
},
|
|
46
|
+
"pwa": {
|
|
47
|
+
"$ref": "#/$defs/pwa"
|
|
48
|
+
},
|
|
46
49
|
"security": {
|
|
47
50
|
"type": "object",
|
|
48
51
|
"additionalProperties": false,
|
|
@@ -117,6 +120,33 @@
|
|
|
117
120
|
}
|
|
118
121
|
],
|
|
119
122
|
"$defs": {
|
|
123
|
+
"pwa": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"additionalProperties": false,
|
|
126
|
+
"properties": {
|
|
127
|
+
"enabled": { "type": "boolean", "default": false },
|
|
128
|
+
"manifest": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"description": "Application-owned Web App Manifest members, including branding, icons, and presentation."
|
|
131
|
+
},
|
|
132
|
+
"offline": {
|
|
133
|
+
"type": "object",
|
|
134
|
+
"additionalProperties": false,
|
|
135
|
+
"properties": {
|
|
136
|
+
"include": {
|
|
137
|
+
"type": "array",
|
|
138
|
+
"description": "Literal package paths or directories to cache. Omitted or empty selects all emitted files.",
|
|
139
|
+
"items": { "type": "string", "minLength": 1 }
|
|
140
|
+
},
|
|
141
|
+
"exclude": {
|
|
142
|
+
"type": "array",
|
|
143
|
+
"description": "Literal package paths or directories excluded from offline assets. Generated shell records and the entry remain available.",
|
|
144
|
+
"items": { "type": "string", "minLength": 1 }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
120
150
|
"appId": {
|
|
121
151
|
"type": "string",
|
|
122
152
|
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
|
package/src/app-descriptor.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import Is from 'strong-type';
|
|
|
2
2
|
import {isDeepStrictEqual} from 'node:util';
|
|
3
3
|
import {readFile} from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import {normalizePwaConfig} from './pwa.mjs';
|
|
5
6
|
import {
|
|
6
7
|
ARCANE_MACHINE_BUNDLE_VERSION,
|
|
7
8
|
ARCANE_PROTOCOL,
|
|
@@ -107,7 +108,7 @@ function validateLocalAIModelPolicy(value,label){
|
|
|
107
108
|
|
|
108
109
|
function validatePackage(value,appId){
|
|
109
110
|
assertOnlyKeys(value,new Set([
|
|
110
|
-
'entry','strategy','include','exclude','shared','adapter','localAIModelPolicy'
|
|
111
|
+
'entry','strategy','include','exclude','shared','adapter','localAIModelPolicy','pwa'
|
|
111
112
|
]),'descriptor.package');
|
|
112
113
|
const entry=normalizeRelativePath(value.entry,'descriptor.package.entry');
|
|
113
114
|
if(!['static','adapter'].includes(value.strategy))fail('descriptor.package.strategy must be static or adapter.');
|
|
@@ -135,6 +136,7 @@ function validatePackage(value,appId){
|
|
|
135
136
|
include,
|
|
136
137
|
exclude,
|
|
137
138
|
shared,
|
|
139
|
+
...(value.pwa===undefined?{}:{pwa:normalizePwaConfig(value.pwa)}),
|
|
138
140
|
...(adapter?{adapter}:{}),
|
|
139
141
|
...(value.localAIModelPolicy===undefined?{}:{
|
|
140
142
|
localAIModelPolicy:validateLocalAIModelPolicy(
|
|
@@ -320,6 +322,7 @@ export function projectPackageManifest(descriptor){
|
|
|
320
322
|
mediaOrigins:[...value.security.mediaOrigins]
|
|
321
323
|
}}:{}),
|
|
322
324
|
...(value.package.localAIModelPolicy?{localAIModelPolicy:value.package.localAIModelPolicy}:{}),
|
|
325
|
+
...(value.package.pwa===undefined?{}:{pwa:value.package.pwa}),
|
|
323
326
|
include:[...value.package.include],
|
|
324
327
|
exclude:[...value.package.exclude],
|
|
325
328
|
shared:[...value.package.shared],
|
|
@@ -378,6 +381,7 @@ function synthesizedDescriptor(packageManifest,nativeDescriptor){
|
|
|
378
381
|
entry:packageManifest.entry,
|
|
379
382
|
strategy:packageManifest.strategy,
|
|
380
383
|
...(packageManifest.localAIModelPolicy?{localAIModelPolicy:packageManifest.localAIModelPolicy}:{}),
|
|
384
|
+
...(packageManifest.pwa===undefined?{}:{pwa:packageManifest.pwa}),
|
|
381
385
|
include:[...packageManifest.include],
|
|
382
386
|
exclude:[...(packageManifest.exclude??[])],
|
|
383
387
|
shared:[...packageManifest.shared],
|
|
@@ -426,7 +430,11 @@ export async function loadAppDescriptor({workspaceRoot,appRoot,appId,packageMani
|
|
|
426
430
|
if(authored){
|
|
427
431
|
const descriptor=validateAppDescriptor(authored,{appId});
|
|
428
432
|
const projection=projectPackageManifest(authored);
|
|
429
|
-
|
|
433
|
+
const packageProjection=packageManifest.pwa===undefined?packageManifest:{
|
|
434
|
+
...packageManifest,
|
|
435
|
+
pwa:normalizePwaConfig(packageManifest.pwa)
|
|
436
|
+
};
|
|
437
|
+
if(!isDeepStrictEqual(projection,packageProjection)){
|
|
430
438
|
fail(`${APP_DESCRIPTOR_NAME} does not project exactly to arcane-package.json.`);
|
|
431
439
|
}
|
|
432
440
|
return completeValue({descriptor,source:'authored',descriptorPath});
|