arcane-os 0.28.4 → 0.29.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 +12 -0
- package/README.md +1 -1
- package/docs/reference/cli.md +16 -0
- package/docs/reference/protocols.md +18 -0
- package/docs/reference/pwa.md +32 -0
- package/docs/reference/sdk-api.md +9 -0
- package/package.json +1 -1
- package/src/app-layout.mjs +6 -0
- package/src/dev-server.mjs +7 -6
- package/src/packager/core.mjs +12 -6
- package/src/toolchain.mjs +23 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.29.0
|
|
4
|
+
|
|
5
|
+
- Add `legacyAppPaths: false` to `arcane-packager.json` for root applications
|
|
6
|
+
using `appsRoot: "."`. Import-map refresh, source development, and packaging
|
|
7
|
+
omit SDK-generated `apps/<id>` navigation aliases and legacy PWA worker and
|
|
8
|
+
inventory files. Omission or `true` preserves the existing behavior.
|
|
9
|
+
- Preserve application storage identity, explicit PWA manifest identity, direct
|
|
10
|
+
installed npm resource URLs, and selected authored files. The SDK does not
|
|
11
|
+
delete existing historical files. Applications choosing root-only output own
|
|
12
|
+
removal of those files and the resulting loss of old bookmark, launch, and
|
|
13
|
+
service-worker update URLs that depended on them.
|
|
14
|
+
|
|
3
15
|
## 0.28.4
|
|
4
16
|
|
|
5
17
|
- Preserve installed component-relative browser-runtime and strong-type import
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ 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.29.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
|
|
package/docs/reference/cli.md
CHANGED
|
@@ -228,6 +228,16 @@ portable runtime subpaths such as `arcane-os/preference-store` and
|
|
|
228
228
|
modules. The result reports the complete map written to the selected
|
|
229
229
|
application; no fixed entry count is a release contract.
|
|
230
230
|
|
|
231
|
+
For `appsRoot: "."`, the artifact is `modules/arcane.importmap.json` at the
|
|
232
|
+
application root. Set `"legacyAppPaths": false` in `arcane-packager.json` to
|
|
233
|
+
omit SDK-generated `apps/<id>/` navigation/PWA compatibility files and aliases.
|
|
234
|
+
The same workspace choice applies to `arcane dev` and `arcane package`; restart
|
|
235
|
+
an already running dev server after changing it. The default remains `true`.
|
|
236
|
+
Root PWA files, app/installation identity and selected authored files are
|
|
237
|
+
preserved. Existing files are never deleted by this option. See
|
|
238
|
+
[root-only generated output](pwa.md#root-only-generated-output) before changing
|
|
239
|
+
the URLs needed by previously installed apps.
|
|
240
|
+
|
|
231
241
|
SDK `0.5.17` preserves the physical workspace route count and ordered include
|
|
232
242
|
list. External and modern integrated routes require `components`, `css`,
|
|
233
243
|
`dependencies`, `entities`, `img`, `modules`, and `sdk`; a physical workspace
|
|
@@ -532,6 +542,12 @@ selected SDK content directly from `node_modules`. Only the portable output
|
|
|
532
542
|
receives copies; no workspace `arcane/` projection is required. Its runtime URLs,
|
|
533
543
|
managed import-map targets, and PWA inventory destinations match source serving.
|
|
534
544
|
|
|
545
|
+
With `appsRoot: "."`, app files retain their root-relative layout. The optional
|
|
546
|
+
root-config `legacyAppPaths: false` omits SDK-generated compatibility files
|
|
547
|
+
beneath `apps/<id>/` from the planned and actual output. It does not omit
|
|
548
|
+
explicitly selected authored resources at those paths or change the default
|
|
549
|
+
packaged installation identity. Omission or `true` preserves existing behavior.
|
|
550
|
+
|
|
535
551
|
```text
|
|
536
552
|
arcane package [--app <id>] [--dry-run]
|
|
537
553
|
```
|
|
@@ -180,6 +180,24 @@ query strings and fragments. It preserves authored files at those destinations.
|
|
|
180
180
|
For a direct-installed root PWA it generates the static PWA records at the root,
|
|
181
181
|
so normal static hosting needs no SDK request handler or runtime copy.
|
|
182
182
|
|
|
183
|
+
To select root-only generated output, set `"legacyAppPaths": false` beside
|
|
184
|
+
`"appsRoot": "."` in `arcane-packager.json`. This optional boolean defaults to
|
|
185
|
+
`true`; it has no effect on the `appsRoot: "apps"` layout. The shared import-map
|
|
186
|
+
refresh, source dev server, package inspection, dry run and package output then
|
|
187
|
+
omit SDK-generated `apps/<id>/` navigation pages, navigation aliases, and the
|
|
188
|
+
legacy PWA worker/offline inventory. Root PWA files and normal managed imports
|
|
189
|
+
remain available. Edit the root configuration before starting `arcane dev`;
|
|
190
|
+
restart an existing server after changing this workspace-level choice.
|
|
191
|
+
|
|
192
|
+
This option does not change the application ID, stored data, the existing
|
|
193
|
+
default installation ID or an authored `pwa.manifest.id`. It neither deletes
|
|
194
|
+
existing files nor removes authored resources from the app's include/exclude
|
|
195
|
+
selection. Explicitly included files under `apps/<id>/` still serve and package
|
|
196
|
+
as authored resources. Previously installed launch URLs and worker update URLs
|
|
197
|
+
need their old resources to remain available; retain the default compatibility
|
|
198
|
+
output when those URLs still need SDK support. See the
|
|
199
|
+
[root PWA compatibility boundary](pwa.md#root-only-generated-output).
|
|
200
|
+
|
|
183
201
|
Direct-installed root maps expose `arcane-os/modules/<filename>` and
|
|
184
202
|
`arcane-os/entities/<filename>` (including extensions), plus the existing
|
|
185
203
|
focused lowercase package exports. They omit the old `arcane/*` and
|
package/docs/reference/pwa.md
CHANGED
|
@@ -71,6 +71,38 @@ introduced. Offline clients require a later successful connection to receive
|
|
|
71
71
|
updated files; fixture coverage does not establish a particular installed app's
|
|
72
72
|
actual browser lifecycle.
|
|
73
73
|
|
|
74
|
+
### Root-only generated output
|
|
75
|
+
|
|
76
|
+
For a root application, add `"legacyAppPaths": false` to the workspace's
|
|
77
|
+
`arcane-packager.json`, alongside `"appsRoot": "."`. The default is `true`.
|
|
78
|
+
This selects root-only SDK-generated navigation and PWA output in import-map
|
|
79
|
+
refresh, `arcane dev`, package inspection/dry run, and browser packaging:
|
|
80
|
+
|
|
81
|
+
- The four root PWA files remain generated normally.
|
|
82
|
+
- The SDK generates no `apps/<id>/` navigation pages, legacy worker or legacy
|
|
83
|
+
offline inventory, and adds no legacy navigation aliases or dev redirects.
|
|
84
|
+
- The default source installation ID remains `/apps/<id>/`, the default
|
|
85
|
+
packaged installation ID remains `./`, and an explicit `manifest.id` remains
|
|
86
|
+
authoritative. An ID is an installation identifier, not a request to generate
|
|
87
|
+
a directory. App identity and saved application data remain unchanged.
|
|
88
|
+
- Existing files are left on disk. Explicitly selected authored old-path
|
|
89
|
+
resources remain in the normal source/package/offline inventory; this setting
|
|
90
|
+
is not a deletion or migration command.
|
|
91
|
+
|
|
92
|
+
Restart `arcane dev` after changing this workspace configuration. Omission or
|
|
93
|
+
`true` retains the compatibility behavior described above. The option has no
|
|
94
|
+
effect on apps whose configured `appsRoot` is `"apps"`.
|
|
95
|
+
|
|
96
|
+
An installed app may still launch an old `/apps/<id>/` URL, and an existing
|
|
97
|
+
worker registration may still update its old script/inventory URL. Disabling
|
|
98
|
+
generation does not redirect those installed clients, unregister their worker,
|
|
99
|
+
clear caches or guarantee their next update. Keep compatibility output enabled
|
|
100
|
+
while old URLs still require SDK support, or supply the required resources
|
|
101
|
+
through the application's own declared files and hosting policy. This SDK
|
|
102
|
+
option alone makes no claim about any existing installation's adoption.
|
|
103
|
+
|
|
104
|
+
### Offline resource selection
|
|
105
|
+
|
|
74
106
|
`offline.include` and `offline.exclude` select literal paths or directory
|
|
75
107
|
prefixes from the selected emitted inventory. An omitted or empty include list
|
|
76
108
|
selects that inventory; exclusions subtract from it. App files use app-relative
|
|
@@ -1041,6 +1041,15 @@ async function usevalidateAppConfig(...arguments_) {
|
|
|
1041
1041
|
|
|
1042
1042
|
Validates one root packager mapping and its fixed shared-route boundaries.
|
|
1043
1043
|
|
|
1044
|
+
The schema-1 `arcane-packager.json` accepts the optional boolean
|
|
1045
|
+
`legacyAppPaths`, normalized to `true` when omitted. With `appsRoot: "."`,
|
|
1046
|
+
setting it to `false` omits generated legacy `apps/<id>/` navigation/PWA output
|
|
1047
|
+
across managed-map refresh, development serving, inspection and packaging.
|
|
1048
|
+
It leaves installation identity and authored file selection unchanged and has
|
|
1049
|
+
no effect with `appsRoot: "apps"`. See
|
|
1050
|
+
[standalone root applications](protocols.md#optional-standalone-root-application)
|
|
1051
|
+
for configuration, retained-file and existing-installation behavior.
|
|
1052
|
+
|
|
1044
1053
|
### Signature and result
|
|
1045
1054
|
|
|
1046
1055
|
```text
|
package/package.json
CHANGED
package/src/app-layout.mjs
CHANGED
|
@@ -5,6 +5,12 @@ export function appRelativeRoot(config, appId) {
|
|
|
5
5
|
return config.appsRoot === '.' ? '' : `apps/${appId}`;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
// Generated compatibility URLs are separate from installation and storage identity.
|
|
9
|
+
export function rootAppLegacyPath(config, appId) {
|
|
10
|
+
return config.appsRoot === '.' && config.legacyAppPaths !== false
|
|
11
|
+
? `apps/${appId}` : undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
export function resolveAppRoot(workspaceRoot, config, appId) {
|
|
9
15
|
return path.resolve(workspaceRoot, appRelativeRoot(config, appId));
|
|
10
16
|
}
|
package/src/dev-server.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import https from 'node:https';
|
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import {resolveWorkspace} from './workspace.mjs';
|
|
9
|
-
import {appRelativeRoot} from './app-layout.mjs';
|
|
9
|
+
import {appRelativeRoot,rootAppLegacyPath} from './app-layout.mjs';
|
|
10
10
|
import {readInstalledSdkLayout} from './sdk-runtime-layout.mjs';
|
|
11
11
|
import {APP_DESCRIPTOR_NAME, projectPackageManifest} from './app-descriptor.mjs';
|
|
12
12
|
import {APP_CONFIG_NAME, validateAppConfig} from './packager/core.mjs';
|
|
@@ -1047,10 +1047,11 @@ async function startOwnedDevServer({
|
|
|
1047
1047
|
const pwaResourceUrls = new Set();
|
|
1048
1048
|
function developmentPwaArtifacts(selectedRoutes, assets = [], version = assetVersion) {
|
|
1049
1049
|
const rootApp = selectedRoutes.config.appsRoot === '.';
|
|
1050
|
+
const legacyAppPath = rootAppLegacyPath(selectedRoutes.config, selectedRoutes.appId);
|
|
1050
1051
|
const appBase = applicationSourcePath(selectedRoutes.config,selectedRoutes.appId);
|
|
1051
1052
|
const navigationAliases = {'/': selectedRoutes.startPath};
|
|
1052
|
-
if (
|
|
1053
|
-
const legacyBase =
|
|
1053
|
+
if (legacyAppPath) {
|
|
1054
|
+
const legacyBase = `/${legacyAppPath}`;
|
|
1054
1055
|
navigationAliases[legacyBase] = selectedRoutes.startPath;
|
|
1055
1056
|
navigationAliases[`${legacyBase}/`] = selectedRoutes.startPath;
|
|
1056
1057
|
for (const asset of [selectedRoutes.startPath,...assets]) {
|
|
@@ -1082,9 +1083,9 @@ async function startOwnedDevServer({
|
|
|
1082
1083
|
basePath: '/',
|
|
1083
1084
|
appBase,
|
|
1084
1085
|
...(rootApp ? {
|
|
1085
|
-
installationId:`/apps/${routeSet.appId}
|
|
1086
|
-
legacyAppPath:`apps/${routeSet.appId}`
|
|
1086
|
+
installationId:`/apps/${routeSet.appId}/`
|
|
1087
1087
|
} : {}),
|
|
1088
|
+
legacyAppPath,
|
|
1088
1089
|
runtimeBase: selectedRoutes.browserRuntimeBase
|
|
1089
1090
|
?? selectedRoutes.installed?.browserRuntimeBase ?? '/arcane/sdk/',
|
|
1090
1091
|
mode: 'development'
|
|
@@ -1176,7 +1177,7 @@ async function startOwnedDevServer({
|
|
|
1176
1177
|
const target=parseRequestTarget(request.url);
|
|
1177
1178
|
if(!target){deny(response,400,'Invalid request path.');return;}
|
|
1178
1179
|
const {segments}=target;
|
|
1179
|
-
const legacyAppRequest = mode === 'source' && routeSet.config
|
|
1180
|
+
const legacyAppRequest = mode === 'source' && rootAppLegacyPath(routeSet.config, routeSet.appId)
|
|
1180
1181
|
&& segments[0] === 'apps' && segments[1] === routeSet.appId;
|
|
1181
1182
|
const legacyPwaRequest = legacyAppRequest && segments.length === 3
|
|
1182
1183
|
&& ['arcane-sw.js', 'arcane-offline.json'].includes(segments[2]);
|
package/src/packager/core.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from 'node:fs/promises';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import {pathToFileURL} from 'node:url';
|
|
15
|
-
import {appRelativeRoot,resolveAppRoot,rootAppNavigation} from '../app-layout.mjs';
|
|
15
|
+
import {appRelativeRoot,resolveAppRoot,rootAppLegacyPath,rootAppNavigation} from '../app-layout.mjs';
|
|
16
16
|
import {readInstalledSdkLayout} from '../sdk-runtime-layout.mjs';
|
|
17
17
|
import {withWorkspaceOperationLock} from '../workspace-operation-lock.mjs';
|
|
18
18
|
import {
|
|
@@ -275,11 +275,14 @@ function validateSharedRoute(route,label){
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
export function validateRootConfig(value,configPath=ROOT_CONFIG_NAME){
|
|
278
|
-
assertOnlyKeys(value,new Set(['schemaVersion','appsRoot','distRoot','sharedPayloads']),ROOT_CONFIG_NAME);
|
|
278
|
+
assertOnlyKeys(value,new Set(['schemaVersion','appsRoot','distRoot','sharedPayloads','legacyAppPaths']),ROOT_CONFIG_NAME);
|
|
279
279
|
if(value.schemaVersion!==1)fail(`${ROOT_CONFIG_NAME}.schemaVersion must be 1.`);
|
|
280
280
|
if(!['apps','.'].includes(value.appsRoot)||value.distRoot!=='dist'){
|
|
281
281
|
fail(`${ROOT_CONFIG_NAME} must bind appsRoot to "apps" or "." and distRoot to "dist".`);
|
|
282
282
|
}
|
|
283
|
+
if (value.legacyAppPaths !== undefined && !is.boolean(value.legacyAppPaths)) {
|
|
284
|
+
fail(`${ROOT_CONFIG_NAME}.legacyAppPaths must be a boolean.`);
|
|
285
|
+
}
|
|
283
286
|
if(!isPlainObject(value.sharedPayloads)){
|
|
284
287
|
fail(`${ROOT_CONFIG_NAME}.sharedPayloads must be an object.`);
|
|
285
288
|
}
|
|
@@ -293,7 +296,10 @@ export function validateRootConfig(value,configPath=ROOT_CONFIG_NAME){
|
|
|
293
296
|
validateSharedRoute(route,`sharedPayloads.${id}[${index}]`)
|
|
294
297
|
);
|
|
295
298
|
}
|
|
296
|
-
return {
|
|
299
|
+
return {
|
|
300
|
+
schemaVersion:1,appsRoot:value.appsRoot,distRoot:'dist',sharedPayloads,configPath,
|
|
301
|
+
legacyAppPaths:value.legacyAppPaths ?? true
|
|
302
|
+
};
|
|
297
303
|
}
|
|
298
304
|
|
|
299
305
|
function normalizeOptionalRecord(value,label){
|
|
@@ -575,7 +581,7 @@ async function optionalDescriptor(context){
|
|
|
575
581
|
async function inspectContext(context,{signal}={}){
|
|
576
582
|
const records=await collectPackageRecords(context,{signal});
|
|
577
583
|
const documents=await browserDocuments(records,context.config.entry);
|
|
578
|
-
const navigation=context.rootConfig.
|
|
584
|
+
const navigation=rootAppLegacyPath(context.rootConfig,context.appId)?rootAppNavigation(
|
|
579
585
|
context.appId,context.config.entry,documents.map(document=>document.path)
|
|
580
586
|
):[];
|
|
581
587
|
for(const redirect of navigation){
|
|
@@ -757,11 +763,11 @@ async function replaceDirectory(stagingRoot,outputRoot){
|
|
|
757
763
|
async function packageWithContext(context,options={}){
|
|
758
764
|
const {signal,onEvent,browserPwa=true}=options;
|
|
759
765
|
const pwaEnabled=browserPwa&&context.config.pwa?.enabled===true;
|
|
760
|
-
const legacyAppPath=context.rootConfig
|
|
766
|
+
const legacyAppPath=rootAppLegacyPath(context.rootConfig,context.appId);
|
|
761
767
|
const appPath=appRelativeRoot(context.rootConfig,context.appId);
|
|
762
768
|
const entryPath=appPackagePath(context,context.config.entry);
|
|
763
769
|
const inspected=await inspectContext(context,{signal});
|
|
764
|
-
const navigation=
|
|
770
|
+
const navigation=legacyAppPath?rootAppNavigation(
|
|
765
771
|
context.appId,context.config.entry,inspected.browserDocuments.map(document=>document.path)
|
|
766
772
|
):[];
|
|
767
773
|
if(options.dryRun){
|
package/src/toolchain.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {loadArcaneIntegratedProvider} from './integrated-provider-loader.mjs';
|
|
13
13
|
import {startDevServer} from './dev-server.mjs';
|
|
14
14
|
import {applyPwaEntryReferences,generateImportMap,readApplicationTestImportMapContext} from './import-map.mjs';
|
|
15
|
-
import {rootAppNavigation} from './app-layout.mjs';
|
|
15
|
+
import {rootAppLegacyPath,rootAppNavigation} from './app-layout.mjs';
|
|
16
16
|
import {createPwaArtifacts} from './pwa.mjs';
|
|
17
17
|
import {readInstalledSdkLayout} from './sdk-runtime-layout.mjs';
|
|
18
18
|
import {withWorkspaceOperationLock} from './workspace-operation-lock.mjs';
|
|
@@ -327,7 +327,16 @@ async function refreshPreparedImportMap(prepared,{signal,onEvent,workspaceOperat
|
|
|
327
327
|
async function refreshRootApplicationFiles(prepared,inspected,importMap,{signal,onEvent}){
|
|
328
328
|
const {workspaceRoot,appId}=prepared;
|
|
329
329
|
const manifest=prepared.validation.app.manifest;
|
|
330
|
-
const
|
|
330
|
+
const legacyAppPath = rootAppLegacyPath(prepared.validation.config, appId);
|
|
331
|
+
const navigation = legacyAppPath ? rootAppNavigation(
|
|
332
|
+
appId,
|
|
333
|
+
manifest.entry,
|
|
334
|
+
inspected.browserDocuments.map(
|
|
335
|
+
function rootBrowserDocumentPath(document) {
|
|
336
|
+
return document.path;
|
|
337
|
+
}
|
|
338
|
+
)
|
|
339
|
+
) : [];
|
|
331
340
|
// These aliases belong to the SDK only after generation; retained app files stay authored.
|
|
332
341
|
for(const redirect of navigation){
|
|
333
342
|
throwIfAborted(signal);
|
|
@@ -343,9 +352,17 @@ async function refreshRootApplicationFiles(prepared,inspected,importMap,{signal,
|
|
|
343
352
|
const entry=`/${manifest.entry.split('/').map(encodeURIComponent).join('/')}`;
|
|
344
353
|
const navigationAliases={
|
|
345
354
|
'/':entry,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
355
|
+
...(legacyAppPath ? {
|
|
356
|
+
[`/${legacyAppPath}`]:entry,
|
|
357
|
+
[`/${legacyAppPath}/`]:entry,
|
|
358
|
+
...Object.fromEntries(
|
|
359
|
+
navigation.map(
|
|
360
|
+
function rootNavigationAlias(redirect) {
|
|
361
|
+
return [`/${redirect.path}`,redirect.target];
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
)
|
|
365
|
+
} : {})
|
|
349
366
|
};
|
|
350
367
|
// The source host serves the installed files in place. There is no runtime projection.
|
|
351
368
|
const files=[...new Set([
|
|
@@ -361,7 +378,7 @@ async function refreshRootApplicationFiles(prepared,inspected,importMap,{signal,
|
|
|
361
378
|
basePath:'/',
|
|
362
379
|
appBase:'/',
|
|
363
380
|
installationId:`/apps/${appId}/`,
|
|
364
|
-
legacyAppPath
|
|
381
|
+
legacyAppPath,
|
|
365
382
|
runtimeBase:installed.browserRuntimeBase,
|
|
366
383
|
mode:'development',
|
|
367
384
|
navigationAliases
|