arcane-os 0.8.0 → 0.8.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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1
4
+
5
+ - Use `arcaneVersion` as the sole local resource version field across import maps,
6
+ runtime materialization, source serving, packaged applications, and dynamic
7
+ components. Remove `v` and duplicate version fields while preserving unrelated
8
+ query parameters, fragments, and ordinary caching.
9
+ - Reuse the registered `html-import` constructor across module URL variants,
10
+ including overlapping application startup and developer error-dialog imports.
11
+ Prevent duplicate custom-element registration from raising `NotSupportedError`
12
+ while preserving component loading, readiness, teardown, and existing instances.
13
+
3
14
  ## 0.8.0
4
15
 
5
16
  - Add `removeBrowserSpeechModelCache()` to remove an explicitly retired Hugging
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.8.0` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.8.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
 
@@ -35,7 +35,7 @@ Create one browser application, install its pinned SDK, and start its source
35
35
  server:
36
36
 
37
37
  ```bash
38
- npx arcane-os@0.8.0 new hello-speech --path ./hello-speech --target browser
38
+ npx arcane-os@0.8.1 new hello-speech --path ./hello-speech --target browser
39
39
  cd hello-speech
40
40
  npm install
41
41
  npm run dev
@@ -386,7 +386,7 @@ uses the same controller for automatic memory extraction.
386
386
  Create a new repository-shaped Arcane application with the exact stable SDK:
387
387
 
388
388
  ```bash
389
- npx arcane-os@0.8.0 new my-app --path ./my-app --target portable --git
389
+ npx arcane-os@0.8.1 new my-app --path ./my-app --target portable --git
390
390
  cd my-app
391
391
  npm install
392
392
  npm run dev
@@ -396,7 +396,7 @@ To enroll an existing repository, install the exact SDK and initialize only
396
396
  missing Arcane files:
397
397
 
398
398
  ```bash
399
- npm install --save-dev --save-exact arcane-os@0.8.0
399
+ npm install --save-dev --save-exact arcane-os@0.8.1
400
400
  npm exec -- arcane init my-app --target portable
401
401
  ```
402
402
 
@@ -412,7 +412,7 @@ npm exec -- arcane-os targets
412
412
  No global SDK install or standalone Arcane CLI is required. The application
413
413
  repository's exact npm dependency and lockfile own the CLI and toolchain version.
414
414
 
415
- Use `npx arcane-os@0.8.0` for the initial bootstrap because it names this npm
415
+ Use `npx arcane-os@0.8.1` for the initial bootstrap because it names this npm
416
416
  package explicitly; bare `npx arcane` outside an installed project could resolve
417
417
  a different package. Both installed commands invoke the same headless toolchain.
418
418
  Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
@@ -432,7 +432,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
432
432
 
433
433
  # From the generated app repository
434
434
  cd ../local-app
435
- npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.8.0.tgz
435
+ npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.8.1.tgz
436
436
  npm ci
437
437
  ```
438
438
 
@@ -441,7 +441,7 @@ same location. The lockfile retains the selected package dependency while
441
441
  Arcane uses the installed package name and version. Local directory `file:` dependencies are not
442
442
  accepted because npm may install them as links; use a packed `.tgz`. A GitHub
443
443
  runner also needs that tarball at the locked path. After publication, replace
444
- the local declaration with the exact `arcane-os@0.8.0` registry package and
444
+ the local declaration with the exact `arcane-os@0.8.1` registry package and
445
445
  commit the regenerated lock.
446
446
 
447
447
  Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
@@ -580,7 +580,7 @@ package installation, or assertions.
580
580
 
581
581
  ## Current target support
582
582
 
583
- Version `0.8.0` exposes one browser target and five explicitly paired
583
+ Version `0.8.1` exposes one browser target and five explicitly paired
584
584
  native development targets: a non-runnable portable directory, a
585
585
  Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
586
586
  unsigned-local-test DEBs, and an Android development-signed APK. The
@@ -289,7 +289,7 @@ paths are withheld from the native provider. The provider copies the complete
289
289
  selected release rather than accepting an unrelated source path. Verification
290
290
  is a separate explicit operation for a selected release artifact.
291
291
 
292
- The SDK `0.8.0` runtime requires Arcane `0.8.12` or newer. Compatibility
292
+ The SDK `0.8.1` runtime requires Arcane `0.8.12` or newer. Compatibility
293
293
  is contractual rather than exact-version pinning: the prepared Core must meet
294
294
  the highest minimum declared by the runtime, selected app, and bundled app
295
295
  dependencies; keep each app's Arcane protocol generation; and provide every
@@ -7,10 +7,11 @@ SDK package metadata, not a timestamp, content measurement, or application
7
7
  constant.
8
8
 
9
9
  For example, an existing `./arcane/modules/HTMLImport.js?v=6#module` reference
10
- retains its application revision and fragment and gains
11
- `&arcaneVersion=${version}` before the fragment. Regenerating for another SDK
12
- release replaces only the `arcaneVersion` value. Existing query spelling and
13
- application parameters remain intact.
10
+ becomes `./arcane/modules/HTMLImport.js?arcaneVersion=${version}#module`.
11
+ `arcaneVersion` is the sole resource version field: transformation removes `v`,
12
+ updates the first existing `arcaneVersion`, and removes duplicate version fields.
13
+ Regenerating for another SDK release replaces that version value. Unrelated
14
+ query fields, their spelling, and fragments remain intact.
14
15
 
15
16
  ## Public tooling
16
17
 
@@ -57,6 +58,12 @@ take effect on ordinary navigation or refresh; they do not replace live module
57
58
  instances, force a reload, restart a model, or erase a conversation. The SDK does
58
59
  not retain earlier installed runtime trees after materialization.
59
60
 
61
+ `HTMLImport` registers its element once per browser custom-element registry.
62
+ Previously authored revision URLs and the developer error dialog's module URL
63
+ can be different; each import exports the constructor already registered in that
64
+ document. Repeated imports do not replace existing component instances or
65
+ register a second `html-import` definition.
66
+
60
67
  ## Scope and work
61
68
 
62
69
  One invocation acts on the selected workspace/application. Runtime
@@ -2052,6 +2052,15 @@ Defines the same-origin `<html-import>` loader with open shadow root, inline scr
2052
2052
 
2053
2053
  default `HTMLImport`; registers `html-import`; `connectedCallback()` and `ready`.
2054
2054
 
2055
+ Registration occurs once per custom-element registry. Imports through different
2056
+ module URLs reuse and export the registered constructor, including overlapping
2057
+ application startup and developer error-dialog imports. Existing component
2058
+ instances, loading, readiness events, and teardown keep that same constructor.
2059
+
2060
+ Local component resources use the loader's `arcaneVersion` as their sole version
2061
+ field. Existing `v` fields are removed; unrelated query fields and fragments
2062
+ remain intact.
2063
+
2055
2064
  Exact exports: `default`.
2056
2065
 
2057
2066
  ### Availability and normalization
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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
  "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
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",
69
69
  "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
- "test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.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",
70
+ "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
71
  "check": "node tools/check-source.mjs && npm test",
72
72
  "check:release": "node tools/check-source.mjs --package-only && npm run test:release",
73
73
  "pack:local": "npm pack --ignore-scripts",
@@ -43,14 +43,21 @@ function versionComponentResource(value,baseHref){
43
43
  const versionField=`arcaneVersion=${encodeURIComponent(htmlImportAssetVersion)}`;
44
44
  let replaced=false;
45
45
  const fields=query?query.split('&').map(function versionQueryField(field){
46
- if(!new URLSearchParams(field).has('arcaneVersion'))return field;
46
+ const parameter=new URLSearchParams(field);
47
+ if(parameter.has('v'))return null;
48
+ if(!parameter.has('arcaneVersion'))return field;
47
49
  if(replaced)return null;
48
50
  replaced=true;
49
- return versionField;
51
+ const equals=field.indexOf('=');
52
+ const key=equals<0?field:field.slice(0,equals);
53
+ return `${key}=${encodeURIComponent(htmlImportAssetVersion)}`;
50
54
  }).filter(function retainQueryField(field){
51
55
  return field!==null;
52
56
  }):[];
53
- if(!replaced)fields.push(versionField);
57
+ if(!replaced){
58
+ if(fields.at(-1)==='')fields[fields.length-1]=versionField;
59
+ else fields.push(versionField);
60
+ }
54
61
  return `${pathname}?${fields.join('&')}${fragment}`;
55
62
  }
56
63
 
@@ -538,6 +545,12 @@ class HTMLImport extends HTMLElement {
538
545
  }
539
546
  }
540
547
 
541
- customElements.define('html-import', HTMLImport);
548
+ // Application startup and error dialogs can import different URL variants.
549
+ // Each variant must use the constructor already owned by this registry.
550
+ const RegisteredHTMLImport = customElements.get('html-import') || HTMLImport;
551
+
552
+ if(RegisteredHTMLImport === HTMLImport){
553
+ customElements.define('html-import', HTMLImport);
554
+ }
542
555
 
543
- export default HTMLImport;
556
+ export default RegisteredHTMLImport;
@@ -814,6 +814,8 @@ function assetUrlVersionEdits(value,version){
814
814
  if(queryStart<0)return [{start:address.length,end:address.length,value:`?arcaneVersion=${versionValue}`}];
815
815
  const query=address.slice(queryStart+1);
816
816
  const edits=[];
817
+ const parameters=[];
818
+ let versionFound=false;
817
819
  let offset=queryStart+1;
818
820
  for(const parameter of query.split('&')){
819
821
  const equals=parameter.indexOf('=');
@@ -821,7 +823,10 @@ function assetUrlVersionEdits(value,version){
821
823
  let decodedKey=key;
822
824
  try{decodedKey=decodeURIComponent(key.replaceAll('+',' '));}
823
825
  catch{decodedKey=key;}
824
- if(decodedKey==='arcaneVersion'){
826
+ const remove=decodedKey==='v'||(decodedKey==='arcaneVersion'&&versionFound);
827
+ parameters.push({start:offset,end:offset+parameter.length,remove});
828
+ if(decodedKey==='arcaneVersion'&&!versionFound){
829
+ versionFound=true;
825
830
  edits.push({
826
831
  start:offset+(equals<0?parameter.length:equals+1),
827
832
  end:offset+parameter.length,
@@ -830,10 +835,25 @@ function assetUrlVersionEdits(value,version){
830
835
  }
831
836
  offset+=parameter.length+1;
832
837
  }
833
- if(edits.length===0)edits.push({
838
+ // Remove adjacent obsolete fields together, including only their separator.
839
+ // Other field spelling and source-level escapes remain untouched.
840
+ for(let index=0;index<parameters.length;index+=1){
841
+ if(!parameters[index].remove)continue;
842
+ const first=index;
843
+ while(parameters[index+1]?.remove)index+=1;
844
+ edits.push({
845
+ start:parameters[first].start-(first>0?1:0),
846
+ end:parameters[index].end+(first===0&&index<parameters.length-1?1:0),
847
+ value:''
848
+ });
849
+ }
850
+ const lastRetained=parameters.findLast(function retainedParameter(parameter){
851
+ return !parameter.remove;
852
+ });
853
+ if(!versionFound)edits.push({
834
854
  start:address.length,
835
855
  end:address.length,
836
- value:`${query&&!query.endsWith('&')?'&':''}arcaneVersion=${versionValue}`
856
+ value:`${lastRetained&&lastRetained.end>lastRetained.start?'&':''}arcaneVersion=${versionValue}`
837
857
  });
838
858
  return edits;
839
859
  }
@@ -841,7 +861,7 @@ function assetUrlVersionEdits(value,version){
841
861
  function applyReferenceEdits(source,edits){
842
862
  let result=source;
843
863
  for(const edit of edits.sort(function descendingReferenceOffset(left,right){
844
- return right.start-left.start;
864
+ return right.start-left.start||right.end-left.end;
845
865
  })){
846
866
  result=result.slice(0,edit.start)+edit.value+result.slice(edit.end);
847
867
  }