arcane-os 0.16.0 → 0.16.2

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.2
4
+
5
+ - Scope explicit application selection and workspace resolution to the named
6
+ app before reading its descriptor. An unrelated invalid descriptor no longer
7
+ blocks that operation. Selected descriptors and unscoped discovery retain
8
+ their existing validation. Named resolution reports only the selected app in
9
+ `appIds`; unknown selections report the requested missing identifier without
10
+ validating or listing unrelated apps.
11
+
12
+ ## 0.16.1
13
+
14
+ - Remove raw script elements and inline event-handler attributes at the
15
+ `markdown-document` insertion boundary, including nested template contents.
16
+ Preserve literal fenced and inline code examples, formatting, navigation,
17
+ and asynchronous document loading. This targeted reader correction does not
18
+ provide a general-purpose HTML sanitizer or alter other Markdown consumers.
19
+
3
20
  ## 0.16.0
4
21
 
5
22
  - Make mail hosting domain-based: use the current browser origin by default,
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.16.0` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.16.2` 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
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",
@@ -67,7 +67,7 @@
67
67
  "scripts": {
68
68
  "test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
69
69
  "test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
70
- "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",
70
+ "test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.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",
71
71
  "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-install.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",
72
72
  "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/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
73
73
  "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",
@@ -616,8 +616,8 @@
616
616
  return true;
617
617
  }
618
618
 
619
- // MD.safeRendered owns Markdown rendering. The detached template keeps
620
- // source-relative links and images coherent before insertion.
619
+ // MD renders Markdown; the detached fragment removes active script
620
+ // markup and resolves source-relative assets before insertion.
621
621
  const safeRendered=new MD(markdown).safeRendered;
622
622
  const fragment=renderedMarkdownFragment(safeRendered,sourceURL);
623
623
  const meaningful=Boolean(
@@ -648,6 +648,27 @@
648
648
  const template=document.createElement('template');
649
649
  template.innerHTML=html;
650
650
 
651
+ // Code examples are text nodes, so their literal markup stays intact.
652
+ // Visit nested template contents too, without reparsing cleaned HTML.
653
+ const pendingFragments=[template.content];
654
+ while(pendingFragments.length){
655
+ const fragment=pendingFragments.pop();
656
+ for(const element of fragment.querySelectorAll('*')){
657
+ if(element.localName.toLowerCase()==='script'){
658
+ element.remove();
659
+ continue;
660
+ }
661
+ for(const attribute of Array.from(element.attributes)){
662
+ if(attribute.name.toLowerCase().startsWith('on')){
663
+ element.removeAttributeNode(attribute);
664
+ }
665
+ }
666
+ if(element.localName==='template'&&element.content){
667
+ pendingFragments.push(element.content);
668
+ }
669
+ }
670
+ }
671
+
651
672
  for(const link of template.content.querySelectorAll('a[href]')){
652
673
  const originalHref=link.getAttribute('href')||'';
653
674
  const policy=markdownDocumentLinkPolicy(originalHref,sourceURL,pageOrigin);
package/src/workspace.mjs CHANGED
@@ -216,12 +216,17 @@ function classifyRootConfig(config){
216
216
  });
217
217
  }
218
218
 
219
- async function discoverAppsInRoot(root,config){
219
+ async function discoverAppsInRoot(root,config,appId){
220
+ if(appId!==undefined&&(!is.string(appId)||!APP_ID_PATTERN.test(appId))){
221
+ fail(`Invalid app id: ${String(appId)}.`,'ARCANE_USAGE');
222
+ }
220
223
  const appsRoot=path.join(root,'apps');
221
224
  await assertRealDirectory(appsRoot,'Workspace apps root');
222
225
  const entries=await readdir(appsRoot,{withFileTypes:true});
223
226
  const apps=[];
224
227
  for(const entry of entries.sort((left,right)=>ordinal(left.name,right.name))){
228
+ // A named operation validates that app, not unrelated application descriptors.
229
+ if(appId!==undefined&&entry.name!==appId)continue;
225
230
  if(!APP_ID_PATTERN.test(entry.name))continue;
226
231
  if(entry.isSymbolicLink())fail(`apps/${entry.name} must not be a symbolic link or junction.`);
227
232
  if(!entry.isDirectory())continue;
@@ -274,13 +279,11 @@ export async function inspectWorkspaceProfile(workspaceRoot=process.cwd()){
274
279
  }
275
280
 
276
281
  export async function selectApp(workspaceRoot=process.cwd(),appId){
277
- const apps=await discoverApps(workspaceRoot);
278
- if(appId!==undefined&&(!is.string(appId)||!APP_ID_PATTERN.test(appId))){
279
- fail(`Invalid app id: ${String(appId)}.`,'ARCANE_USAGE');
280
- }
282
+ const profile=await inspectWorkspaceProfile(workspaceRoot);
283
+ const apps=await discoverAppsInRoot(profile.workspaceRoot,profile.config,appId);
281
284
  if(appId){
282
285
  const selected=apps.find(app=>app.appId===appId);
283
- if(!selected)fail(`Unknown app "${appId}". Available apps: ${apps.map(app=>app.appId).join(', ')||'[none]'}.`);
286
+ if(!selected)fail(`Unknown app "${appId}".`);
284
287
  return selected;
285
288
  }
286
289
  if(apps.length===0)fail('No Arcane applications were found under apps/.');
@@ -294,14 +297,11 @@ export async function resolveWorkspace({workspaceRoot=process.cwd(),appId}={}){
294
297
  const profile=await inspectWorkspaceProfile(workspaceRoot);
295
298
  const canonicalRoot=profile.workspaceRoot;
296
299
  const config=profile.config;
297
- const apps=await discoverAppsInRoot(canonicalRoot,config);
298
- if(appId!==undefined&&(!is.string(appId)||!APP_ID_PATTERN.test(appId))){
299
- fail(`Invalid app id: ${String(appId)}.`,'ARCANE_USAGE');
300
- }
300
+ const apps=await discoverAppsInRoot(canonicalRoot,config,appId);
301
301
  let app;
302
302
  if(appId){
303
303
  app=apps.find(item=>item.appId===appId);
304
- if(!app)fail(`Unknown app "${appId}". Available apps: ${apps.map(item=>item.appId).join(', ')||'[none]'}.`);
304
+ if(!app)fail(`Unknown app "${appId}".`);
305
305
  }else if(apps.length===1){
306
306
  [app]=apps;
307
307
  }else if(apps.length===0){