arcane-os 0.3.2 → 0.3.3
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 +13 -0
- package/README.md +7 -7
- package/package.json +1 -1
- package/runtime/arcane/components/speech.html +1 -1
- package/schemas/arcane-lock.schema.json +4 -1
- package/src/dev-server.mjs +13 -4
- package/src/installed-sdk-runtime.mjs +24 -2
- package/src/packager/core.mjs +12 -5
- package/src/scaffold.mjs +19 -0
- package/src/templates/workspace-template.mjs +37 -1
- package/src/toolchain.mjs +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.3
|
|
4
|
+
|
|
5
|
+
- Corrected installed-workspace import-map refresh to preserve rich browser
|
|
6
|
+
document inspection records while passing only navigable document paths to
|
|
7
|
+
the public generator, including `.html` and `.htm` pages without modifying
|
|
8
|
+
HTML fragments.
|
|
9
|
+
- Restored one semantic `arcane.lock.json` contract across scaffold, init, and
|
|
10
|
+
installed runtime materialization, replacing stale versions with the exact
|
|
11
|
+
installed package version and npm-alias roots without byte or security
|
|
12
|
+
metadata.
|
|
13
|
+
- Added selected installed-package coverage for stale-lock replacement and the
|
|
14
|
+
public multi-document import-map command through the packed npm artifact.
|
|
15
|
+
|
|
3
16
|
## 0.3.2
|
|
4
17
|
|
|
5
18
|
- Preserved complete application, document, model, message, event, process,
|
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.3.
|
|
22
|
+
This checkout defines the `0.3.3` 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
|
|
|
@@ -126,7 +126,7 @@ uses the same controller for automatic memory extraction.
|
|
|
126
126
|
Create a new repository-shaped Arcane application with the exact stable SDK:
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
npx arcane-os@0.3.
|
|
129
|
+
npx arcane-os@0.3.3 new my-app --path ./my-app --target portable --git
|
|
130
130
|
cd my-app
|
|
131
131
|
npm install
|
|
132
132
|
npm run check
|
|
@@ -137,7 +137,7 @@ To enroll an existing repository, install the exact SDK and initialize only
|
|
|
137
137
|
missing Arcane files:
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
npm install --save-dev --save-exact arcane-os@0.3.
|
|
140
|
+
npm install --save-dev --save-exact arcane-os@0.3.3
|
|
141
141
|
npm exec -- arcane init my-app --target portable
|
|
142
142
|
```
|
|
143
143
|
|
|
@@ -153,7 +153,7 @@ npm exec -- arcane-os targets
|
|
|
153
153
|
No global SDK install or standalone Arcane CLI is required. The application
|
|
154
154
|
repository's exact npm dependency and lockfile own the CLI and toolchain version.
|
|
155
155
|
|
|
156
|
-
Use `npx arcane-os@0.3.
|
|
156
|
+
Use `npx arcane-os@0.3.3` for the initial bootstrap because it names this npm
|
|
157
157
|
package explicitly; bare `npx arcane` outside an installed project could resolve
|
|
158
158
|
a different package. Both installed commands invoke the same headless toolchain.
|
|
159
159
|
Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
|
|
@@ -174,7 +174,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
|
|
|
174
174
|
|
|
175
175
|
# From the generated app repository
|
|
176
176
|
cd ../local-app
|
|
177
|
-
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.3.
|
|
177
|
+
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.3.3.tgz
|
|
178
178
|
npm run check
|
|
179
179
|
npm ci
|
|
180
180
|
```
|
|
@@ -184,7 +184,7 @@ same location. The lockfile retains the selected package dependency while
|
|
|
184
184
|
Arcane uses the installed package name and version. Local directory `file:` dependencies are not
|
|
185
185
|
accepted because npm may install them as links; use a packed `.tgz`. A GitHub
|
|
186
186
|
runner also needs that tarball at the locked path. After publication, replace
|
|
187
|
-
the local declaration with the exact `arcane-os@0.3.
|
|
187
|
+
the local declaration with the exact `arcane-os@0.3.3` registry package and
|
|
188
188
|
commit the regenerated lock.
|
|
189
189
|
|
|
190
190
|
Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
|
|
@@ -322,7 +322,7 @@ package installation, or assertions.
|
|
|
322
322
|
|
|
323
323
|
## Current target support
|
|
324
324
|
|
|
325
|
-
Version `0.3.
|
|
325
|
+
Version `0.3.3` exposes one browser target and five explicitly paired
|
|
326
326
|
native development targets: a non-runnable portable directory, a
|
|
327
327
|
Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
|
|
328
328
|
unsigned-local-test DEBs, and an Android development-signed APK. The
|
package/package.json
CHANGED
|
@@ -1380,7 +1380,7 @@
|
|
|
1380
1380
|
function stop() {
|
|
1381
1381
|
recordingRequested = false;
|
|
1382
1382
|
clearSilenceTimer();
|
|
1383
|
-
if (captureSession
|
|
1383
|
+
if (captureSession && captureSession.recorder.state !== 'inactive') {
|
|
1384
1384
|
captureSession.recorder.stop();
|
|
1385
1385
|
}
|
|
1386
1386
|
return true;
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
"required": ["name", "version"],
|
|
15
15
|
"properties": {
|
|
16
16
|
"name": {"const": "arcane-os"},
|
|
17
|
-
"version": {
|
|
17
|
+
"version": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?$"
|
|
20
|
+
}
|
|
18
21
|
}
|
|
19
22
|
},
|
|
20
23
|
"runtime": {
|
package/src/dev-server.mjs
CHANGED
|
@@ -31,6 +31,11 @@ const SDK_RUNTIME_SOURCE_PROTOCOL='arcane-sdk-runtime-source/1';
|
|
|
31
31
|
const SDK_RUNTIME_SOURCE_ARCANE_ROOTS=new Set([
|
|
32
32
|
'components','css','entities','img','modules','security'
|
|
33
33
|
]);
|
|
34
|
+
const SDK_INSTALLED_ARCANE_ROOTS=new Set([
|
|
35
|
+
...SDK_RUNTIME_SOURCE_ARCANE_ROOTS,
|
|
36
|
+
'dependencies',
|
|
37
|
+
'sdk'
|
|
38
|
+
]);
|
|
34
39
|
const SDK_RUNTIME_SOURCE_PRIVATE_SEGMENTS=new Set([
|
|
35
40
|
'node_modules','.git','.hg','.svn','cvs'
|
|
36
41
|
]);
|
|
@@ -126,7 +131,7 @@ async function canonicalRealDirectory(requested,label){
|
|
|
126
131
|
return canonical;
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
function sdkRuntimeSourcePathAllowed(relative,{
|
|
134
|
+
function sdkRuntimeSourcePathAllowed(relative,{arcaneRoots=null}={}){
|
|
130
135
|
if(relative.length===0)return false;
|
|
131
136
|
const normalized=relative.map(function normalizeRuntimeSourceSegment(segment){
|
|
132
137
|
return segment.normalize('NFC').toLowerCase();
|
|
@@ -135,11 +140,15 @@ function sdkRuntimeSourcePathAllowed(relative,{arcaneRoot=false}={}){
|
|
|
135
140
|
return segment.startsWith('.')||SDK_RUNTIME_SOURCE_PRIVATE_SEGMENTS.has(segment)
|
|
136
141
|
||SDK_RUNTIME_SOURCE_PRIVATE_MANIFESTS.has(segment);
|
|
137
142
|
}))return false;
|
|
138
|
-
return
|
|
143
|
+
return arcaneRoots===null||arcaneRoots.has(normalized[0]);
|
|
139
144
|
}
|
|
140
145
|
|
|
141
146
|
function sdkArcaneSourcePathAllowed(relative){
|
|
142
|
-
return sdkRuntimeSourcePathAllowed(relative,{
|
|
147
|
+
return sdkRuntimeSourcePathAllowed(relative,{arcaneRoots:SDK_RUNTIME_SOURCE_ARCANE_ROOTS});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function sdkInstalledArcanePathAllowed(relative){
|
|
151
|
+
return sdkRuntimeSourcePathAllowed(relative,{arcaneRoots:SDK_INSTALLED_ARCANE_ROOTS});
|
|
143
152
|
}
|
|
144
153
|
|
|
145
154
|
function sdkDependencySourcePathAllowed(relative){
|
|
@@ -418,7 +427,7 @@ async function sourceRoutes(workspaceRoot,appId,{
|
|
|
418
427
|
{
|
|
419
428
|
prefix:['arcane'],
|
|
420
429
|
root:runtimeRoot,
|
|
421
|
-
allow:
|
|
430
|
+
allow:sdkInstalledArcanePathAllowed
|
|
422
431
|
}
|
|
423
432
|
]
|
|
424
433
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {readFile,realpath,stat} from 'node:fs/promises';
|
|
1
|
+
import {lstat,readFile,realpath,stat,writeFile} from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import {materializeWorkspaceRuntimeContent} from './workspace-runtime.mjs';
|
|
4
4
|
import {withWorkspaceOperationLock} from './workspace-operation-lock.mjs';
|
|
5
|
+
import {createWorkspaceLockDocument} from './templates/workspace-template.mjs';
|
|
5
6
|
import {
|
|
6
7
|
resolveInstalledSdkInstallation,
|
|
7
8
|
resolveSdkPackageDeclaration
|
|
@@ -55,6 +56,18 @@ async function installedSdkAuthority(canonicalRoot,{sdkPackageSource}={}){
|
|
|
55
56
|
return {declaration,installation};
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
async function writeWorkspaceLock(lockPath,lock){
|
|
60
|
+
try{
|
|
61
|
+
const info=await lstat(lockPath);
|
|
62
|
+
if(info.isSymbolicLink()||!info.isFile()){
|
|
63
|
+
fail(`Workspace SDK lock must be a real file: ${lockPath}.`);
|
|
64
|
+
}
|
|
65
|
+
}catch(error){
|
|
66
|
+
if(error?.code!=='ENOENT')throw error;
|
|
67
|
+
}
|
|
68
|
+
await writeFile(lockPath,`${JSON.stringify(lock,null,2)}\n`,'utf8');
|
|
69
|
+
}
|
|
70
|
+
|
|
58
71
|
export async function materializeInstalledSdkRuntime({
|
|
59
72
|
workspaceRoot,
|
|
60
73
|
sdkPackageSource,
|
|
@@ -79,12 +92,21 @@ export async function materializeInstalledSdkRuntime({
|
|
|
79
92
|
signal,
|
|
80
93
|
onEvent
|
|
81
94
|
});
|
|
95
|
+
const lockPath=path.join(canonicalRoot,'arcane.lock.json');
|
|
96
|
+
const lock=createWorkspaceLockDocument({
|
|
97
|
+
dependencyName:authority.installation.dependencyName,
|
|
98
|
+
packageName:authority.installation.packageName,
|
|
99
|
+
packageVersion:authority.installation.packageVersion,
|
|
100
|
+
packageSource:authority.installation.packageSource
|
|
101
|
+
});
|
|
102
|
+
await writeWorkspaceLock(lockPath,lock);
|
|
82
103
|
return {
|
|
83
104
|
schemaVersion:1,
|
|
84
105
|
kind:'arcane-installed-sdk-runtime-materialization',
|
|
85
106
|
status:'materialized',
|
|
86
107
|
installation:authority.installation,
|
|
87
|
-
workspaceRuntime
|
|
108
|
+
workspaceRuntime,
|
|
109
|
+
workspaceLock:{path:lockPath,document:lock}
|
|
88
110
|
};
|
|
89
111
|
});
|
|
90
112
|
}
|
package/src/packager/core.mjs
CHANGED
|
@@ -499,16 +499,23 @@ async function collectPackageRecords(context,{signal}={}){
|
|
|
499
499
|
return records;
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
-
async function browserDocuments(records){
|
|
502
|
+
async function browserDocuments(records,entry){
|
|
503
|
+
let entryDocument=null;
|
|
503
504
|
const documents=[];
|
|
504
505
|
for(const record of records){
|
|
505
|
-
|
|
506
|
+
const extension=path.posix.extname(record.destination).toLocaleLowerCase('en-US');
|
|
507
|
+
if(extension!=='.html'&&extension!=='.htm')continue;
|
|
506
508
|
const inspected=inspectImportMapHtml(await readFile(record.source,'utf8'),{
|
|
507
509
|
documentPath:record.destination
|
|
508
510
|
});
|
|
509
|
-
|
|
511
|
+
const document={path:record.destination,...copyJson(inspected)};
|
|
512
|
+
if(record.destination===entry){
|
|
513
|
+
entryDocument=document;
|
|
514
|
+
}else if(inspected.bases.length>0){
|
|
515
|
+
documents.push(document);
|
|
516
|
+
}
|
|
510
517
|
}
|
|
511
|
-
return documents;
|
|
518
|
+
return entryDocument===null?documents:[entryDocument,...documents];
|
|
512
519
|
}
|
|
513
520
|
|
|
514
521
|
async function optionalDescriptor(context){
|
|
@@ -540,7 +547,7 @@ async function inspectContext(context,{signal}={}){
|
|
|
540
547
|
}),
|
|
541
548
|
...(context.config.adapter===undefined?{}:{adapter:context.config.adapter}),
|
|
542
549
|
descriptor:await optionalDescriptor(context),
|
|
543
|
-
browserDocuments:await browserDocuments(records),
|
|
550
|
+
browserDocuments:await browserDocuments(records,context.config.entry),
|
|
544
551
|
files:records.map(record=>record.destination),
|
|
545
552
|
output:path.relative(context.workspaceRoot,context.outputRoot).split(path.sep).join('/')
|
|
546
553
|
};
|
package/src/scaffold.mjs
CHANGED
|
@@ -430,6 +430,25 @@ export async function initWorkspace({
|
|
|
430
430
|
sdkDeclaration
|
|
431
431
|
);
|
|
432
432
|
const writtenFiles=await writeMissingFiles(resolvedRoot,template.files,{signal,onEvent});
|
|
433
|
+
if(workspaceMode==='external'&&writtenFiles.skippedFiles.includes('arcane.lock.json')){
|
|
434
|
+
throwIfAborted(signal);
|
|
435
|
+
await assertNoLinkedAncestors(resolvedRoot,'arcane.lock.json');
|
|
436
|
+
const lockPath=path.join(resolvedRoot,'arcane.lock.json');
|
|
437
|
+
const lockInfo=await lstat(lockPath);
|
|
438
|
+
if(lockInfo.isSymbolicLink()||!lockInfo.isFile()){
|
|
439
|
+
fail('Existing arcane.lock.json must be a real file.');
|
|
440
|
+
}
|
|
441
|
+
await writeFile(
|
|
442
|
+
lockPath,
|
|
443
|
+
template.files.get('arcane.lock.json'),
|
|
444
|
+
'utf8'
|
|
445
|
+
);
|
|
446
|
+
writtenFiles.skippedFiles=writtenFiles.skippedFiles.filter(
|
|
447
|
+
relative=>relative!=='arcane.lock.json'
|
|
448
|
+
);
|
|
449
|
+
writtenFiles.replacedFiles=['arcane.lock.json'];
|
|
450
|
+
await emit(onEvent,{type:'scaffold.file.replaced',path:'arcane.lock.json'});
|
|
451
|
+
}
|
|
433
452
|
const packageUpdated=await applyPackageMerge(resolvedRoot,packagePlan,{signal,onEvent});
|
|
434
453
|
const workspaceRuntime=workspaceMode==='external'
|
|
435
454
|
?await materializeWorkspaceRuntimeContent({
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ARCANE_PROTOCOL,
|
|
3
|
+
CLI_EVENT_PROTOCOL,
|
|
4
|
+
SDK_NAME,
|
|
5
|
+
SDK_VERSION,
|
|
6
|
+
TARGET_ADAPTER_PROTOCOL
|
|
7
|
+
} from '../constants.mjs';
|
|
2
8
|
|
|
3
9
|
const NPM_PACKAGE_NAME_PATTERN=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
|
|
4
10
|
const LOCAL_TARBALL_PATTERN=/^file:.+\.tgz$/iu;
|
|
@@ -22,6 +28,32 @@ function titleCase(appId){
|
|
|
22
28
|
.join(' ');
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
export function createWorkspaceLockDocument({
|
|
32
|
+
dependencyName=SDK_NAME,
|
|
33
|
+
packageName=SDK_NAME,
|
|
34
|
+
packageVersion=SDK_VERSION,
|
|
35
|
+
packageSource=`node_modules/${dependencyName}`
|
|
36
|
+
}={}){
|
|
37
|
+
if(packageName!==SDK_NAME||typeof packageVersion!=='string'
|
|
38
|
+
||!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/u.test(packageVersion)
|
|
39
|
+
||typeof dependencyName!=='string'||dependencyName.length>214
|
|
40
|
+
||!NPM_PACKAGE_NAME_PATTERN.test(dependencyName)
|
|
41
|
+
||packageSource!==`node_modules/${dependencyName}`){
|
|
42
|
+
throw new Error('Invalid workspace SDK lock authority.');
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
schemaVersion:1,
|
|
46
|
+
sdk:{name:packageName,version:packageVersion},
|
|
47
|
+
runtime:{root:`${packageSource}/runtime`},
|
|
48
|
+
sdkBrowserRuntime:{root:`${packageSource}/browser-runtime`},
|
|
49
|
+
protocols:{
|
|
50
|
+
arcane:ARCANE_PROTOCOL,
|
|
51
|
+
cliEvents:CLI_EVENT_PROTOCOL,
|
|
52
|
+
targetAdapter:TARGET_ADAPTER_PROTOCOL
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
25
57
|
export function workspaceTemplate({
|
|
26
58
|
appId,
|
|
27
59
|
displayName,
|
|
@@ -212,6 +244,10 @@ Every browser release also carries Arcane OS licensing material under \`licenses
|
|
|
212
244
|
]
|
|
213
245
|
}
|
|
214
246
|
}));
|
|
247
|
+
files.set('arcane.lock.json',json(createWorkspaceLockDocument({
|
|
248
|
+
dependencyName:sdkDependencyName,
|
|
249
|
+
packageSource:sdkPackageSource
|
|
250
|
+
})));
|
|
215
251
|
files.set(`apps/${appId}/arcane-app.json`,json({
|
|
216
252
|
schemaVersion:2,
|
|
217
253
|
id:appId,
|
package/src/toolchain.mjs
CHANGED
|
@@ -314,7 +314,9 @@ async function refreshPreparedImportMap(prepared,{signal,onEvent,workspaceOperat
|
|
|
314
314
|
appId:prepared.appId,
|
|
315
315
|
appRoot:prepared.appRoot,
|
|
316
316
|
entry:manifest.entry,
|
|
317
|
-
documents:inspected.browserDocuments
|
|
317
|
+
documents:inspected.browserDocuments.map(function selectBrowserDocumentPath(document){
|
|
318
|
+
return document.path;
|
|
319
|
+
}),
|
|
318
320
|
workspaceOperationLease,
|
|
319
321
|
signal,
|
|
320
322
|
onEvent
|