arcane-os 0.8.1 → 0.10.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 +28 -0
- package/README.md +14 -8
- package/browser-runtime/browser-device.mjs +13 -0
- package/browser-runtime/pwa.mjs +215 -0
- package/docs/architecture.md +1 -1
- package/docs/reference/README.md +5 -3
- package/docs/reference/ai/browser-wasm.md +19 -0
- package/docs/reference/asset-versioning.md +13 -0
- package/docs/reference/inventory/package-api.json +45 -1
- package/docs/reference/pwa.md +183 -0
- package/docs/reference/sdk-api.md +86 -3
- package/package.json +4 -2
- 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 +187 -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
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
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
|
-
`arcane-os/preference-store`, `arcane-os/speech-playback`,
|
|
6
|
-
`arcane-os/speech-text` entrypoints, and the browser-only
|
|
7
|
-
`arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints.
|
|
5
|
+
`arcane-os/preference-store`, `arcane-os/speech-playback`,
|
|
6
|
+
`arcane-os/speech-text`, and `arcane-os/browser-device` entrypoints, and the browser-only
|
|
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
|
|
@@ -38,6 +38,8 @@ for the installed-inventory-derived physical-runtime contract in SDK `0.5.18`.
|
|
|
38
38
|
| `arcane-os/preference-store` | Portable preference records and injected storage adapters. |
|
|
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
|
+
| `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. |
|
|
41
43
|
| `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
|
|
42
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. |
|
|
43
45
|
| `arcane-os/mail` | Portable Mail runtime, durable outbox, complete transport responses, and provider-neutral acceptance contracts. |
|
|
@@ -124,6 +126,9 @@ browser map are cataloged separately in [Runtime modules](runtime-modules.md).
|
|
|
124
126
|
| `createBrowserWhisperProvider()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with Workers, object URLs, caller-selected Whisper runtime/model artifacts, and an SDK DBOPFS speech store |
|
|
125
127
|
| `createCanonicalUstarHeader()` | function | `arcane-os` | Packaging and release bundles | Node |
|
|
126
128
|
| `createDbopfsModelStore()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser with a ready DBOPFS instance and OPFS |
|
|
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 |
|
|
127
132
|
| `createDbopfsSpeechArtifactStore()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with ready DBOPFS, Web Locks, Fetch, File/Blob, and object URLs |
|
|
128
133
|
| `removeBrowserSpeechModelCache()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser CacheStorage; explicit removal of one selected upstream model |
|
|
129
134
|
| `createNativeBuildPlan()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
|
|
@@ -6007,6 +6012,84 @@ const source = createBrowserModelSource({
|
|
|
6007
6012
|
});
|
|
6008
6013
|
```
|
|
6009
6014
|
|
|
6015
|
+
## getBrowserDeviceClass()
|
|
6016
|
+
|
|
6017
|
+
### Overview
|
|
6018
|
+
|
|
6019
|
+
Returns `mobile` or `desktop` to select application-owned settings. Phones and
|
|
6020
|
+
tablets use `mobile` when browser client hints, platform, user-agent information,
|
|
6021
|
+
or the iPadOS Mac-platform/touch combination identifies them. Missing or
|
|
6022
|
+
unrecognized identity uses `desktop`.
|
|
6023
|
+
|
|
6024
|
+
### Signature and result
|
|
6025
|
+
|
|
6026
|
+
```text
|
|
6027
|
+
getBrowserDeviceClass(navigatorObject = globalThis.navigator)
|
|
6028
|
+
```
|
|
6029
|
+
|
|
6030
|
+
The optional navigator-like object supplies `userAgentData`, `userAgent`,
|
|
6031
|
+
`platform`, and `maxTouchPoints`. The synchronous string result reports a
|
|
6032
|
+
settings class, not hardware capability or readiness.
|
|
6033
|
+
|
|
6034
|
+
### Availability and normalization
|
|
6035
|
+
|
|
6036
|
+
Import from `arcane-os/browser-device` in Node or a browser. The dependency-free
|
|
6037
|
+
entrypoint reads only the supplied or global browser identity and does not load
|
|
6038
|
+
an AI runtime. No model, GPU, storage, network, or listener operation runs.
|
|
6039
|
+
Missing or unrecognized identity returns `desktop`; browser identity masking
|
|
6040
|
+
can affect classification. Existing provider defaults remain unchanged unless
|
|
6041
|
+
the caller selects and supplies its own profile.
|
|
6042
|
+
|
|
6043
|
+
### Example
|
|
6044
|
+
|
|
6045
|
+
```javascript
|
|
6046
|
+
import {getBrowserDeviceClass} from 'arcane-os/browser-device';
|
|
6047
|
+
|
|
6048
|
+
const deviceClass = getBrowserDeviceClass();
|
|
6049
|
+
console.log(deviceClass); // mobile or desktop
|
|
6050
|
+
```
|
|
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
|
+
|
|
6010
6093
|
## createBrowserWasmLlmProvider()
|
|
6011
6094
|
|
|
6012
6095
|
### Overview
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
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",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"./events": "./src/events.mjs",
|
|
15
15
|
"./event-manager": "./src/event-manager.mjs",
|
|
16
16
|
"./logging": "./browser-runtime/logging.mjs",
|
|
17
|
+
"./browser-device": "./browser-runtime/browser-device.mjs",
|
|
18
|
+
"./pwa": "./browser-runtime/pwa.mjs",
|
|
17
19
|
"./preference-store": "./runtime/arcane/modules/PreferenceStore.js",
|
|
18
20
|
"./speech-playback": "./runtime/arcane/modules/SpeechPlayback.js",
|
|
19
21
|
"./speech-text": "./browser-runtime/speech-text.mjs",
|
|
@@ -65,7 +67,7 @@
|
|
|
65
67
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
66
68
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
67
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",
|
|
68
|
-
"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",
|
|
69
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",
|
|
70
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",
|
|
71
73
|
"check": "node tools/check-source.mjs && npm test",
|
|
@@ -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});
|