arcane-os 0.28.4 → 0.29.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 +25 -0
- package/README.md +8 -4
- package/docs/architecture.md +20 -5
- package/docs/reference/asset-versioning.md +7 -6
- package/docs/reference/cli.md +16 -0
- package/docs/reference/inventory/package-api.json +3 -3
- package/docs/reference/protocols.md +18 -0
- package/docs/reference/pwa.md +36 -3
- package/docs/reference/sdk-api.md +27 -3
- package/package.json +1 -1
- package/schemas/arcane-app-bundle.schema.json +1 -1
- package/src/app-layout.mjs +6 -0
- package/src/dev-server.mjs +7 -6
- package/src/import-map.mjs +5 -8
- package/src/packager/core.mjs +12 -6
- package/src/release-bundle.mjs +100 -12
- package/src/toolchain.mjs +23 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.29.1
|
|
4
|
+
|
|
5
|
+
- Preserve complete long and non-ASCII filenames in application release bundles
|
|
6
|
+
through standard per-file PAX path extensions. Public bundle creation and
|
|
7
|
+
verification retain logical payload paths and complete file content without
|
|
8
|
+
application-side renaming. Ordinary USTAR representation remains supported,
|
|
9
|
+
and the existing SDK-version metadata contract is unchanged. Bundles using
|
|
10
|
+
extended paths require the updated SDK reader or another PAX-capable reader.
|
|
11
|
+
- Preserve application-authored resource URL queries, including `v`, encoded
|
|
12
|
+
and repeated fields, empty query segments, and fragments. Import-map refresh,
|
|
13
|
+
serving, packaging, and PWA delivery now change only SDK-owned `arcaneVersion`
|
|
14
|
+
fields through the existing shared transformer.
|
|
15
|
+
|
|
16
|
+
## 0.29.0
|
|
17
|
+
|
|
18
|
+
- Add `legacyAppPaths: false` to `arcane-packager.json` for root applications
|
|
19
|
+
using `appsRoot: "."`. Import-map refresh, source development, and packaging
|
|
20
|
+
omit SDK-generated `apps/<id>` navigation aliases and legacy PWA worker and
|
|
21
|
+
inventory files. Omission or `true` preserves the existing behavior.
|
|
22
|
+
- Preserve application storage identity, explicit PWA manifest identity, direct
|
|
23
|
+
installed npm resource URLs, and selected authored files. The SDK does not
|
|
24
|
+
delete existing historical files. Applications choosing root-only output own
|
|
25
|
+
removal of those files and the resulting loss of old bookmark, launch, and
|
|
26
|
+
service-worker update URLs that depended on them.
|
|
27
|
+
|
|
3
28
|
## 0.28.4
|
|
4
29
|
|
|
5
30
|
- 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.1` SDK contract. Applications pin one exact npm
|
|
23
23
|
version and lockfile; registry state is deliberately not baked into application
|
|
24
24
|
artifacts.
|
|
25
25
|
|
|
@@ -604,9 +604,13 @@ prior file until the replacement is complete. Cancellation or failure before
|
|
|
604
604
|
commit restores the prior output when that can be done without overwriting a
|
|
605
605
|
concurrent change. A conflicting or uncertain path is preserved for inspection.
|
|
606
606
|
|
|
607
|
-
The archive uses
|
|
608
|
-
|
|
609
|
-
|
|
607
|
+
The archive uses USTAR+gzip with per-file PAX path extensions for long or
|
|
608
|
+
non-ASCII filenames and publishes its v1 JSON contract at
|
|
609
|
+
`arcane-os/schemas/arcane-app-bundle.json`. Logical filenames and content remain
|
|
610
|
+
complete; ordinary USTAR paths retain their existing representation, and bundle
|
|
611
|
+
metadata keeps its existing SDK-version matching contract. Readers must support
|
|
612
|
+
PAX path extensions to consume bundles that need them. When the
|
|
613
|
+
user explicitly selects bundle verification, it rejects malformed archives, links, devices,
|
|
610
614
|
unsafe or colliding paths, unsupported archive members, trailing data, and
|
|
611
615
|
inconsistent descriptor or inventory structure. These checks reject corrupt
|
|
612
616
|
selected artifacts; they do not impose byte-count, hash, provenance, or
|
package/docs/architecture.md
CHANGED
|
@@ -273,16 +273,31 @@ External repository delivery adds a distinct schema-1
|
|
|
273
273
|
`arcane-app-release-bundle` envelope. Bundle creation uses an authored schema-2
|
|
274
274
|
`arcane-app.json`; a synthesized package or registry projection remains valid for integrated
|
|
275
275
|
packaging but is not used for an external bundle. The
|
|
276
|
-
|
|
276
|
+
logical file inventory contains exactly `ARCANE_APP_BUNDLE.json`, canonical `arcane-app.json`,
|
|
277
277
|
`payload/ARCANE_APP_RELEASE.json`, and the release inventory beneath `payload/`
|
|
278
278
|
in that order. The envelope adds no repository-only source or build tooling
|
|
279
279
|
beyond that selected release inventory. Individual apps remain responsible for
|
|
280
280
|
their authored source policy.
|
|
281
281
|
|
|
282
|
-
The bundle contract
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
282
|
+
The bundle contract retains schema 1 and the `ustar+gzip` format identifier.
|
|
283
|
+
Ordinary representable ASCII paths use the existing USTAR headers. Long paths
|
|
284
|
+
and non-ASCII names use a [POSIX PAX](https://docs.oracle.com/cd/E86824_01/html/E54763/pax-1.html)
|
|
285
|
+
per-file `x` header containing the complete
|
|
286
|
+
UTF-8 `path`, immediately before that file's regular header and content. This
|
|
287
|
+
transport framing is not a payload member and never appears in the logical
|
|
288
|
+
inventory or `readFile()` results. Path spelling and file content remain unchanged;
|
|
289
|
+
the existing portable-path rules still apply. The public
|
|
290
|
+
`createCanonicalUstarHeader()` helper remains a single USTAR header and retains
|
|
291
|
+
its format-local field limits; complete bundles use `createAppReleaseBundle()`.
|
|
292
|
+
|
|
293
|
+
The bundle reader consumes these `path` extensions for the following file only,
|
|
294
|
+
as well as ordinary USTAR entries. Other PAX attributes and archive member
|
|
295
|
+
types remain outside this bundle profile. Older SDK readers without PAX support
|
|
296
|
+
cannot read bundles that require these extensions; update the consuming SDK
|
|
297
|
+
before importing one. The existing same-SDK-version bundle condition is unchanged.
|
|
298
|
+
Explicit bundle verification parses the selected archive without extraction and
|
|
299
|
+
rejects genuinely malformed structures, unsafe or colliding paths, unsupported
|
|
300
|
+
members, and incompatible bundle generations. These corrupt-artifact
|
|
286
301
|
checks do not create byte-count, content-hash, provenance, or admission gates
|
|
287
302
|
for ordinary development, packaging, serving, or running.
|
|
288
303
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Applications that enable [PWA delivery](pwa.md) use clean local resource URLs.
|
|
4
4
|
Their generated offline manifest and service worker own the selected application
|
|
5
5
|
and SDK release information. In that mode, the delivery transformer removes
|
|
6
|
-
|
|
6
|
+
only the SDK-owned `arcaneVersion` field, preserving authored query fields and fragments.
|
|
7
7
|
The behavior below continues to apply when PWA delivery is disabled and to native
|
|
8
8
|
packages. Workspace runtime materialization remains usable by either target;
|
|
9
9
|
the selected browser delivery applies its PWA URL policy.
|
|
@@ -15,11 +15,12 @@ SDK package metadata, not a timestamp, content measurement, or application
|
|
|
15
15
|
constant.
|
|
16
16
|
|
|
17
17
|
For example, an existing `./arcane/modules/HTMLImport.js?v=6#module` reference
|
|
18
|
-
becomes `./arcane/modules/HTMLImport.js?arcaneVersion=${version}#module`.
|
|
19
|
-
`arcaneVersion` is the
|
|
20
|
-
|
|
21
|
-
Regenerating for another SDK release replaces that
|
|
22
|
-
|
|
18
|
+
becomes `./arcane/modules/HTMLImport.js?v=6&arcaneVersion=${version}#module`.
|
|
19
|
+
`arcaneVersion` is the SDK's resource version field: transformation updates its
|
|
20
|
+
first existing value and removes duplicate `arcaneVersion` fields, or appends it
|
|
21
|
+
when absent. Regenerating for another SDK release replaces only that SDK value.
|
|
22
|
+
Authored fields, including `v`, encoded keys and values, repeated or empty query
|
|
23
|
+
segments, their source spelling, and fragments remain intact.
|
|
23
24
|
|
|
24
25
|
## Public tooling
|
|
25
26
|
|
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
|
```
|
|
@@ -464,14 +464,14 @@
|
|
|
464
464
|
"name": "createAppReleaseBundle",
|
|
465
465
|
"displayName": "createAppReleaseBundle()",
|
|
466
466
|
"kind": "function",
|
|
467
|
-
"signature": "async createAppReleaseBundle({
|
|
467
|
+
"signature": "async createAppReleaseBundle({releaseRoot, appDescriptor, outputPath, overwrite=false, signal, onEvent}={})",
|
|
468
468
|
"entrypoints": [
|
|
469
469
|
"arcane-os",
|
|
470
470
|
"arcane-os/release-bundle"
|
|
471
471
|
],
|
|
472
472
|
"primaryImport": "arcane-os",
|
|
473
473
|
"group": "Packaging and release bundles",
|
|
474
|
-
"summary": "Writes one deterministic USTAR+gzip external application bundle
|
|
474
|
+
"summary": "Writes one deterministic USTAR+gzip external application bundle, using per-file POSIX PAX path extensions for complete long or non-ASCII filenames.",
|
|
475
475
|
"availability": "Node",
|
|
476
476
|
"protocol": "SDK packager and deterministic bundle contract",
|
|
477
477
|
"normalization": "Normalized SDK validation with complete canonical archive and release content"
|
|
@@ -2164,7 +2164,7 @@
|
|
|
2164
2164
|
],
|
|
2165
2165
|
"primaryImport": "arcane-os",
|
|
2166
2166
|
"group": "Packaging and release bundles",
|
|
2167
|
-
"summary": "Parses
|
|
2167
|
+
"summary": "Parses one deterministic app bundle without extraction, supporting ordinary USTAR entries and complete per-file POSIX PAX paths within the existing same-SDK-version contract.",
|
|
2168
2168
|
"availability": "Node",
|
|
2169
2169
|
"protocol": "SDK packager and deterministic bundle contract",
|
|
2170
2170
|
"normalization": "Normalized SDK validation with complete canonical archive and release content"
|
|
@@ -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
|
|
@@ -118,9 +150,10 @@ The same bootstrap starts one initially hidden `pwa-install.html` component with
|
|
|
118
150
|
the generated manifest's app name. Component loading and worker registration
|
|
119
151
|
proceed independently.
|
|
120
152
|
|
|
121
|
-
The selected PWA browser delivery removes
|
|
122
|
-
local resource references, including the managed import map.
|
|
123
|
-
|
|
153
|
+
The selected PWA browser delivery removes only SDK-owned `arcaneVersion` fields
|
|
154
|
+
from actual local resource references, including the managed import map. Authored
|
|
155
|
+
fields such as `v`, encoded and repeated fields, empty query segments, fragments,
|
|
156
|
+
source spelling and unrelated payloads are preserved. The offline
|
|
124
157
|
manifest now carries release information. Non-PWA and native delivery retain
|
|
125
158
|
the [existing asset version contract](asset-versioning.md).
|
|
126
159
|
|
|
@@ -629,12 +629,17 @@ async function previewNextPrerelease(workspaceRoot) {
|
|
|
629
629
|
|
|
630
630
|
### Overview
|
|
631
631
|
|
|
632
|
-
Writes one deterministic USTAR+gzip external application bundle from the selected
|
|
632
|
+
Writes one deterministic USTAR+gzip external application bundle from the selected
|
|
633
|
+
authored release state. Long or non-ASCII filenames use per-file POSIX PAX `path`
|
|
634
|
+
extensions, preserving their complete UTF-8 spelling and file content. Ordinary
|
|
635
|
+
USTAR entries remain unchanged. Metadata framing is excluded from the manifest's
|
|
636
|
+
logical file inventory. Existing portable-path rules, schema 1, and the
|
|
637
|
+
`ustar+gzip` format identifier remain unchanged.
|
|
633
638
|
|
|
634
639
|
### Signature and result
|
|
635
640
|
|
|
636
641
|
```text
|
|
637
|
-
async createAppReleaseBundle({
|
|
642
|
+
async createAppReleaseBundle({releaseRoot, appDescriptor, outputPath, overwrite=false, signal, onEvent}={})
|
|
638
643
|
```
|
|
639
644
|
|
|
640
645
|
Import it from `arcane-os` or `arcane-os/release-bundle`. The signature above states whether settlement is synchronous or promise-based. The overview and owning group define result authority, side effects, callbacks, events, cancellation, and lifecycle.
|
|
@@ -658,6 +663,9 @@ async function usecreateAppReleaseBundle(...arguments_) {
|
|
|
658
663
|
### Overview
|
|
659
664
|
|
|
660
665
|
Builds the exact 512-byte canonical USTAR header for one validated bundle entry.
|
|
666
|
+
This low-level helper remains a single regular-file header with the USTAR field
|
|
667
|
+
limits. Use `createAppReleaseBundle()` to encode complete long or Unicode paths;
|
|
668
|
+
the bundle writer supplies PAX framing where needed.
|
|
661
669
|
|
|
662
670
|
### Signature and result
|
|
663
671
|
|
|
@@ -1041,6 +1049,15 @@ async function usevalidateAppConfig(...arguments_) {
|
|
|
1041
1049
|
|
|
1042
1050
|
Validates one root packager mapping and its fixed shared-route boundaries.
|
|
1043
1051
|
|
|
1052
|
+
The schema-1 `arcane-packager.json` accepts the optional boolean
|
|
1053
|
+
`legacyAppPaths`, normalized to `true` when omitted. With `appsRoot: "."`,
|
|
1054
|
+
setting it to `false` omits generated legacy `apps/<id>/` navigation/PWA output
|
|
1055
|
+
across managed-map refresh, development serving, inspection and packaging.
|
|
1056
|
+
It leaves installation identity and authored file selection unchanged and has
|
|
1057
|
+
no effect with `appsRoot: "apps"`. See
|
|
1058
|
+
[standalone root applications](protocols.md#optional-standalone-root-application)
|
|
1059
|
+
for configuration, retained-file and existing-installation behavior.
|
|
1060
|
+
|
|
1044
1061
|
### Signature and result
|
|
1045
1062
|
|
|
1046
1063
|
```text
|
|
@@ -1095,7 +1112,14 @@ async function useverifyApp(...arguments_) {
|
|
|
1095
1112
|
|
|
1096
1113
|
### Overview
|
|
1097
1114
|
|
|
1098
|
-
Parses
|
|
1115
|
+
Parses one deterministic app bundle without extraction and checks its structural
|
|
1116
|
+
manifest and complete logical file inventory. It supports ordinary USTAR entries
|
|
1117
|
+
and per-file POSIX PAX `path` extensions. The complete extended path is used by
|
|
1118
|
+
`files` and `readFile()`; the metadata and regular-header placeholder are not
|
|
1119
|
+
payload files. Malformed or orphaned PAX records produce `ARCANE_BUNDLE_INVALID`.
|
|
1120
|
+
Other PAX attributes and archive member types remain unsupported. The existing
|
|
1121
|
+
same-SDK-version condition remains unchanged. An older reader without PAX support
|
|
1122
|
+
must be updated before importing a bundle that requires these path extensions.
|
|
1099
1123
|
|
|
1100
1124
|
### Signature and result
|
|
1101
1125
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://github.com/TheWizardNexus/arcane-os-sdk/schemas/arcane-app-bundle.schema.json",
|
|
4
4
|
"title": "Arcane external application release bundle",
|
|
5
|
-
"description": "Structural manifest for one complete USTAR+gzip application bundle.",
|
|
5
|
+
"description": "Structural manifest for one complete USTAR+gzip application bundle, with per-file POSIX PAX path extensions for long or non-ASCII filenames. PAX framing is excluded from the logical file inventory.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
8
8
|
"schemaVersion",
|
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/import-map.mjs
CHANGED
|
@@ -847,7 +847,7 @@ function assetUrlVersionEdits(value,version){
|
|
|
847
847
|
let decodedKey=key;
|
|
848
848
|
try{decodedKey=decodeURIComponent(key.replaceAll('+',' '));}
|
|
849
849
|
catch{decodedKey=key;}
|
|
850
|
-
const remove=decodedKey==='
|
|
850
|
+
const remove=decodedKey==='arcaneVersion'&&(clean||versionFound);
|
|
851
851
|
parameters.push({start:offset,end:offset+parameter.length,remove});
|
|
852
852
|
if(decodedKey==='arcaneVersion'&&!versionFound&&!clean){
|
|
853
853
|
versionFound=true;
|
|
@@ -859,10 +859,10 @@ function assetUrlVersionEdits(value,version){
|
|
|
859
859
|
}
|
|
860
860
|
offset+=parameter.length+1;
|
|
861
861
|
}
|
|
862
|
-
if(clean
|
|
863
|
-
return
|
|
862
|
+
if(clean&¶meters.every(function removesSdkField(parameter){
|
|
863
|
+
return parameter.remove;
|
|
864
864
|
}))return [{start:queryStart,end:address.length,value:''}];
|
|
865
|
-
// Remove adjacent
|
|
865
|
+
// Remove adjacent SDK version fields together, including only their separator.
|
|
866
866
|
// Other field spelling and source-level escapes remain untouched.
|
|
867
867
|
for(let index=0;index<parameters.length;index+=1){
|
|
868
868
|
if(!parameters[index].remove)continue;
|
|
@@ -874,13 +874,10 @@ function assetUrlVersionEdits(value,version){
|
|
|
874
874
|
value:''
|
|
875
875
|
});
|
|
876
876
|
}
|
|
877
|
-
const lastRetained=parameters.findLast(function retainedParameter(parameter){
|
|
878
|
-
return !parameter.remove;
|
|
879
|
-
});
|
|
880
877
|
if(!clean&&!versionFound)edits.push({
|
|
881
878
|
start:address.length,
|
|
882
879
|
end:address.length,
|
|
883
|
-
value
|
|
880
|
+
value:`&arcaneVersion=${versionValue}`
|
|
884
881
|
});
|
|
885
882
|
return edits;
|
|
886
883
|
}
|
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/release-bundle.mjs
CHANGED
|
@@ -93,7 +93,7 @@ function splitUstarPath(archivePath){
|
|
|
93
93
|
return {name,prefix};
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
|
|
96
|
+
return null;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function writeTextField(header,offset,length,value,label){
|
|
@@ -112,8 +112,10 @@ function writeOctalField(header,offset,length,value,label,{trailingSpace=false}=
|
|
|
112
112
|
header.write(rendered,offset,length,'ascii');
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
const
|
|
115
|
+
function createTarHeader(archivePath,size,type){
|
|
116
|
+
const fields=splitUstarPath(archivePath);
|
|
117
|
+
if(!fields)fail(`Archive path cannot be represented by ustar: ${archivePath}.`);
|
|
118
|
+
const {name,prefix}=fields;
|
|
117
119
|
const header=Buffer.alloc(TAR_BLOCK_SIZE);
|
|
118
120
|
writeTextField(header,0,100,name,'ustar name');
|
|
119
121
|
writeOctalField(header,100,8,ARCHIVE_MODE,'ustar mode');
|
|
@@ -122,7 +124,7 @@ export function createCanonicalUstarHeader(archivePath,size){
|
|
|
122
124
|
writeOctalField(header,124,12,size,'ustar size');
|
|
123
125
|
writeOctalField(header,136,12,0,'ustar mtime');
|
|
124
126
|
header.fill(0x20,148,156);
|
|
125
|
-
header[156]=
|
|
127
|
+
header[156]=type;
|
|
126
128
|
header.write('ustar\0',257,6,'ascii');
|
|
127
129
|
header.write('00',263,2,'ascii');
|
|
128
130
|
writeTextField(header,265,32,'root','ustar owner');
|
|
@@ -134,13 +136,45 @@ export function createCanonicalUstarHeader(archivePath,size){
|
|
|
134
136
|
return header;
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
+
export function createCanonicalUstarHeader(archivePath,size){
|
|
140
|
+
return createTarHeader(archivePath,size,0x30);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function tarRecord(archivePath,content,type=0x30){
|
|
144
|
+
const header=createTarHeader(archivePath,content.length,type);
|
|
139
145
|
const remainder=content.length%TAR_BLOCK_SIZE;
|
|
140
146
|
const padding=remainder===0?Buffer.alloc(0):Buffer.alloc(TAR_BLOCK_SIZE-remainder);
|
|
141
147
|
return Buffer.concat([header,content,padding]);
|
|
142
148
|
}
|
|
143
149
|
|
|
150
|
+
function paxPathRecord(archivePath) {
|
|
151
|
+
const value = Buffer.from(` path=${archivePath}\n`, 'utf8');
|
|
152
|
+
// POSIX PAX framing includes the decimal prefix itself in the record length.
|
|
153
|
+
let length = value.length + 1;
|
|
154
|
+
while(String(length).length + value.length !== length) {
|
|
155
|
+
length = String(length).length + value.length;
|
|
156
|
+
}
|
|
157
|
+
const prefix = Buffer.from(String(length), 'ascii');
|
|
158
|
+
return Buffer.concat(
|
|
159
|
+
[prefix, value]
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function tarEntry(archivePath, content) {
|
|
164
|
+
const fields = splitUstarPath(archivePath);
|
|
165
|
+
if(fields && !/[^\u0000-\u007f]/u.test(archivePath)) {
|
|
166
|
+
return tarRecord(archivePath, content);
|
|
167
|
+
}
|
|
168
|
+
// The following regular header is only a placeholder; PAX owns the complete path.
|
|
169
|
+
const extendedPath = paxPathRecord(archivePath);
|
|
170
|
+
return Buffer.concat(
|
|
171
|
+
[
|
|
172
|
+
tarRecord('PaxHeaders/entry', extendedPath, 0x78),
|
|
173
|
+
tarRecord('PaxPayload/entry', content)
|
|
174
|
+
]
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
144
178
|
async function realDirectory(location,label){
|
|
145
179
|
const requested=path.resolve(location);
|
|
146
180
|
let info;
|
|
@@ -308,9 +342,53 @@ function readOctalField(header,offset,length,label){
|
|
|
308
342
|
return value;
|
|
309
343
|
}
|
|
310
344
|
|
|
345
|
+
function readPaxPath(content) {
|
|
346
|
+
let offset = 0;
|
|
347
|
+
let archivePath;
|
|
348
|
+
while(offset < content.length) {
|
|
349
|
+
const separator = content.indexOf(0x20, offset);
|
|
350
|
+
if(separator < 0) {
|
|
351
|
+
fail('Bundle contains malformed PAX record framing.');
|
|
352
|
+
}
|
|
353
|
+
const lengthText = content.subarray(offset, separator).toString('latin1');
|
|
354
|
+
if(!/^[1-9][0-9]*$/u.test(lengthText)) {
|
|
355
|
+
fail('Bundle contains malformed PAX record framing.');
|
|
356
|
+
}
|
|
357
|
+
const length = Number(lengthText);
|
|
358
|
+
const end = offset + length;
|
|
359
|
+
if(!is.safeInteger(length) || end > content.length || end <= separator + 1
|
|
360
|
+
|| content[end - 1] !== 0x0a) {
|
|
361
|
+
fail('Bundle contains an incomplete or malformed PAX record.');
|
|
362
|
+
}
|
|
363
|
+
const assignment = content.indexOf(0x3d, separator + 1);
|
|
364
|
+
if(assignment <= separator + 1 || assignment >= end - 1) {
|
|
365
|
+
fail('Bundle contains a malformed PAX assignment.');
|
|
366
|
+
}
|
|
367
|
+
const keyword = content.subarray(separator + 1, assignment).toString('utf8');
|
|
368
|
+
if(keyword !== 'path') {
|
|
369
|
+
fail(`Bundle contains an unsupported PAX field: ${keyword}.`);
|
|
370
|
+
}
|
|
371
|
+
try {
|
|
372
|
+
const decoder = new TextDecoder(
|
|
373
|
+
'utf-8',
|
|
374
|
+
{fatal: true, ignoreBOM: true}
|
|
375
|
+
);
|
|
376
|
+
archivePath = decoder.decode(content.subarray(assignment + 1, end - 1));
|
|
377
|
+
} catch (error) {
|
|
378
|
+
fail(`Bundle PAX path is not valid UTF-8: ${error.message}.`);
|
|
379
|
+
}
|
|
380
|
+
offset = end;
|
|
381
|
+
}
|
|
382
|
+
if(archivePath === undefined) {
|
|
383
|
+
fail('Bundle PAX header is missing its path.');
|
|
384
|
+
}
|
|
385
|
+
return archivePath;
|
|
386
|
+
}
|
|
387
|
+
|
|
311
388
|
function readTarEntries(archive){
|
|
312
389
|
const entries=new Map();
|
|
313
390
|
let offset=0;
|
|
391
|
+
let pendingPath;
|
|
314
392
|
while(offset+TAR_BLOCK_SIZE<=archive.length){
|
|
315
393
|
const header=archive.subarray(offset,offset+TAR_BLOCK_SIZE);
|
|
316
394
|
if(header.every(value=>value===0))break;
|
|
@@ -321,21 +399,31 @@ function readTarEntries(archive){
|
|
|
321
399
|
let actualChecksum=0;
|
|
322
400
|
for(const value of checksumHeader)actualChecksum+=value;
|
|
323
401
|
if(actualChecksum!==expectedChecksum)fail('Bundle contains a malformed ustar header.');
|
|
324
|
-
|
|
402
|
+
const type=header[156];
|
|
403
|
+
if(type!==0&&type!==0x30&&type!==0x78)fail('Bundle contains a non-file archive entry.');
|
|
325
404
|
const name=readStringField(header,0,100);
|
|
326
405
|
const prefix=readStringField(header,345,155);
|
|
327
|
-
const
|
|
328
|
-
if(entries.has(pathKey(archivePath)))fail(`Bundle contains a duplicate path: ${archivePath}.`);
|
|
406
|
+
const headerPath=prefix?`${prefix}/${name}`:name;
|
|
329
407
|
const size=readOctalField(header,124,12,'ustar size');
|
|
330
408
|
const contentStart=offset+TAR_BLOCK_SIZE;
|
|
331
409
|
const contentEnd=contentStart+size;
|
|
332
|
-
if(contentEnd>archive.length)fail(`Bundle entry is incomplete: ${
|
|
410
|
+
if(contentEnd>archive.length)fail(`Bundle entry is incomplete: ${pendingPath??headerPath}.`);
|
|
411
|
+
const content=archive.subarray(contentStart,contentEnd);
|
|
412
|
+
offset=contentStart+Math.ceil(size/TAR_BLOCK_SIZE)*TAR_BLOCK_SIZE;
|
|
413
|
+
if(type===0x78){
|
|
414
|
+
if(pendingPath!==undefined)fail('Bundle PAX path is missing its following file.');
|
|
415
|
+
pendingPath=readPaxPath(content);
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
const archivePath=validateAppBundlePath(pendingPath??headerPath,'archive path');
|
|
419
|
+
pendingPath=undefined;
|
|
420
|
+
if(entries.has(pathKey(archivePath)))fail(`Bundle contains a duplicate path: ${archivePath}.`);
|
|
333
421
|
entries.set(pathKey(archivePath),{
|
|
334
422
|
path:archivePath,
|
|
335
|
-
content:Buffer.from(
|
|
423
|
+
content:Buffer.from(content)
|
|
336
424
|
});
|
|
337
|
-
offset=contentStart+Math.ceil(size/TAR_BLOCK_SIZE)*TAR_BLOCK_SIZE;
|
|
338
425
|
}
|
|
426
|
+
if(pendingPath!==undefined)fail('Bundle PAX path is missing its following file.');
|
|
339
427
|
return entries;
|
|
340
428
|
}
|
|
341
429
|
|
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
|