arcane-os 0.42.0 → 0.42.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +1 -1
  3. package/docs/reference/runtime-components.md +7 -0
  4. package/docs/reference/runtime-modules.md +4 -3
  5. package/package.json +2 -2
  6. package/runtime/arcane/components/browser-ai-setup.html +1 -1
  7. package/runtime/arcane/components/calculator.html +1 -1
  8. package/runtime/arcane/components/chart.html +1 -1
  9. package/runtime/arcane/components/chat.html +34 -9
  10. package/runtime/arcane/components/conversation-view.html +1 -1
  11. package/runtime/arcane/components/dashboard-config.html +4 -4
  12. package/runtime/arcane/components/data-maintenance.html +3 -3
  13. package/runtime/arcane/components/data-view.html +3 -3
  14. package/runtime/arcane/components/directory-picker.html +2 -2
  15. package/runtime/arcane/components/file-drop.html +1 -1
  16. package/runtime/arcane/components/file-inspector.html +1 -1
  17. package/runtime/arcane/components/file-manager.html +3 -3
  18. package/runtime/arcane/components/integration-settings.html +1 -1
  19. package/runtime/arcane/components/local-ai-status.html +1 -1
  20. package/runtime/arcane/components/markdown-document.html +4 -4
  21. package/runtime/arcane/components/markdown-editor.html +2 -2
  22. package/runtime/arcane/components/media-embed.html +1 -1
  23. package/runtime/arcane/components/modal.html +1 -1
  24. package/runtime/arcane/components/output-panel.html +1 -1
  25. package/runtime/arcane/components/preferences-form.html +1 -1
  26. package/runtime/arcane/components/pwa-install.html +2 -2
  27. package/runtime/arcane/components/screen-capture.html +1 -1
  28. package/runtime/arcane/components/source-code-viewer.html +3 -3
  29. package/runtime/arcane/components/speech.html +1 -1
  30. package/runtime/arcane/components/summary-strip.html +1 -1
  31. package/runtime/arcane/components/table.html +1 -1
  32. package/runtime/arcane/components/task-progress.html +1 -1
  33. package/runtime/arcane/components/terminal-workspace.html +1 -1
  34. package/runtime/arcane/components/theme-editor.html +1 -1
  35. package/runtime/arcane/components/unified-inbox.html +1 -1
  36. package/runtime/arcane/components/voice-transcription.html +2 -2
  37. package/runtime/arcane/components/weather-widget.html +1 -1
  38. package/runtime/arcane/components/web-navigator.html +2 -2
  39. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +1 -1
  40. package/runtime/arcane/modules/CommunicationAppController.js +2 -2
  41. package/runtime/arcane/modules/CommunicationHub.js +1 -1
  42. package/runtime/arcane/modules/ConversationClosingReport.js +1 -1
  43. package/runtime/arcane/modules/Errors.js +1 -1
  44. package/runtime/arcane/modules/HTMLImport.js +19 -38
  45. package/runtime/arcane/modules/LocalAIReadinessController.js +1 -1
  46. package/runtime/arcane/modules/ScamRiskPolicy.js +1 -1
  47. package/src/dev-server.mjs +2 -2
  48. package/src/import-map.mjs +66 -145
  49. package/src/packager/core.mjs +3 -4
  50. package/src/templates/workspace-template.mjs +5 -5
  51. package/src/workspace-runtime.mjs +6 -9
@@ -17,15 +17,9 @@ globalThis[htmlImportHostRegistryKey]=htmlImportHostRegistry;
17
17
  let htmlImportScriptId=0;
18
18
 
19
19
  const htmlImportModuleURL=new URL(import.meta.url);
20
- const htmlImportAssetVersion=htmlImportModuleURL.searchParams.get('arcaneVersion');
21
20
 
22
- function pwaResourceUrlsEnabled(){
23
- return Boolean(document.querySelector?.('script[data-arcane-pwa]'));
24
- }
25
-
26
- function versionComponentResource(value,baseHref){
27
- const clean=pwaResourceUrlsEnabled();
28
- if((!htmlImportAssetVersion&&!clean)||!is.string(value)||!value||value.startsWith('#')){
21
+ function cleanComponentResource(value,baseHref){
22
+ if(!is.string(value)||!value||value.startsWith('#')){
29
23
  return value;
30
24
  }
31
25
  let resolvedURL;
@@ -43,32 +37,20 @@ function versionComponentResource(value,baseHref){
43
37
  const fragment=fragmentIndex<0?'':value.slice(fragmentIndex);
44
38
  const resource=fragmentIndex<0?value:value.slice(0,fragmentIndex);
45
39
  const queryIndex=resource.indexOf('?');
46
- const pathname=queryIndex<0?resource:resource.slice(0,queryIndex);
47
- const query=queryIndex<0?'':resource.slice(queryIndex+1);
48
- const versionField=`arcaneVersion=${encodeURIComponent(htmlImportAssetVersion)}`;
49
- let replaced=false;
50
- const fields=query?query.split('&').map(function versionQueryField(field){
51
- const parameter=new URLSearchParams(field);
52
- if(parameter.has('v'))return null;
53
- if(!parameter.has('arcaneVersion'))return field;
54
- if(clean||replaced)return null;
55
- replaced=true;
40
+ if(queryIndex<0)return value;
41
+ const pathname=resource.slice(0,queryIndex);
42
+ const query=resource.slice(queryIndex+1);
43
+ const fields=query.split('&').filter(function retainQueryField(field){
56
44
  const equals=field.indexOf('=');
57
45
  const key=equals<0?field:field.slice(0,equals);
58
- return `${key}=${encodeURIComponent(htmlImportAssetVersion)}`;
59
- }).filter(function retainQueryField(field){
60
- return field!==null;
61
- }):[];
62
- if(clean){
63
- return fields.some(function hasRemainingField(field){return field!=='';})
64
- ?`${pathname}?${fields.join('&')}${fragment}`
65
- :`${pathname}${fragment}`;
66
- }
67
- if(!replaced){
68
- if(fields.at(-1)==='')fields[fields.length-1]=versionField;
69
- else fields.push(versionField);
70
- }
71
- return `${pathname}?${fields.join('&')}${fragment}`;
46
+ let decodedKey=key;
47
+ try{decodedKey=decodeURIComponent(key.replaceAll('+',' '));}
48
+ catch{decodedKey=key;}
49
+ return decodedKey!=='arcaneVersion';
50
+ });
51
+ return fields.length>0
52
+ ?`${pathname}?${fields.join('&')}${fragment}`
53
+ :`${pathname}${fragment}`;
72
54
  }
73
55
 
74
56
  function componentRuntimeRoot(resolvedHref){
@@ -91,7 +73,6 @@ function resolveComponentResource(value,runtimeRoot){
91
73
  }
92
74
 
93
75
  function resolveComponentStyleResources(styleText,runtimeRoot){
94
- if(!runtimeRoot&&!htmlImportAssetVersion&&!pwaResourceUrlsEnabled())return styleText;
95
76
  const parts=[];
96
77
  let copiedThrough=0;
97
78
  let index=0;
@@ -149,7 +130,7 @@ function resolveComponentStyleResources(styleText,runtimeRoot){
149
130
  const resourcePath=styleText.slice(start,end);
150
131
  // CSS escapes retain their original spelling until their URL syntax is decoded.
151
132
  if(resourcePath.includes('\\'))return;
152
- const resolved=versionComponentResource(
133
+ const resolved=cleanComponentResource(
153
134
  resolveComponentResource(resourcePath,runtimeRoot),
154
135
  document.baseURI
155
136
  );
@@ -237,7 +218,7 @@ function createComponentFragment(html,resolvedHref){
237
218
  const resolved=resolveComponentResource(value,runtimeRoot);
238
219
  const navigation=attribute==='href'
239
220
  &&['a','area','base'].includes(element.localName);
240
- const versioned=navigation?resolved:versionComponentResource(
221
+ const versioned=navigation?resolved:cleanComponentResource(
241
222
  resolved,
242
223
  element.localName==='script'?resolvedHref:document.baseURI
243
224
  );
@@ -257,7 +238,7 @@ function resolveRelativeDynamicImports(source,baseHref){
257
238
  return source.replace(
258
239
  /\bimport\s*\(\s*(['"])(\.{1,2}\/[^'"]+)\1\s*\)/gu,
259
240
  function resolveDynamicImport(_match,_quote,specifier){
260
- const resolved=versionComponentResource(new URL(specifier,baseHref).href,baseHref);
241
+ const resolved=cleanComponentResource(new URL(specifier,baseHref).href,baseHref);
261
242
  return `import(${JSON.stringify(resolved)})`;
262
243
  }
263
244
  );
@@ -327,7 +308,7 @@ class HTMLImport extends HTMLElement {
327
308
 
328
309
  try{
329
310
  const resolvedURL=new URL(href,document.baseURI);
330
- resolvedHref=versionComponentResource(resolvedURL.href,document.baseURI);
311
+ resolvedHref=cleanComponentResource(resolvedURL.href,document.baseURI);
331
312
  const response=await fetch(resolvedHref,{
332
313
  cache:'default',
333
314
  method:'GET',
@@ -497,7 +478,7 @@ class HTMLImport extends HTMLElement {
497
478
  let sourceBaseHref=contentBaseHref;
498
479
  const scriptSource=script.getAttribute('src');
499
480
  if(scriptSource!==null){
500
- const requestedScriptHref=versionComponentResource(
481
+ const requestedScriptHref=cleanComponentResource(
501
482
  new URL(scriptSource,contentBaseHref).href,
502
483
  contentBaseHref
503
484
  );
@@ -5,7 +5,7 @@ import { arcaneLogging } from 'arcane-os/logging';
5
5
  import {
6
6
  checkLocalAIReadiness,
7
7
  deriveLocalAIRequirements
8
- } from './LocalAIReadiness.js?v=4';
8
+ } from './LocalAIReadiness.js';
9
9
  import {
10
10
  createArcaneEventSource,
11
11
  projectArcaneDOMEvent
@@ -1,5 +1,5 @@
1
1
  import {analyzeRiskSignals} from './RiskSignalAnalyzer.js';
2
- import {canonicalNetworkHostname,emptyArcaneNetworkPolicy,findDeniedDomainRule,loadArcaneNetworkPolicy} from './ArcaneNetworkPolicy.js?v=3';
2
+ import {canonicalNetworkHostname,emptyArcaneNetworkPolicy,findDeniedDomainRule,loadArcaneNetworkPolicy} from './ArcaneNetworkPolicy.js';
3
3
 
4
4
  export const scamRiskSignals=[
5
5
  {id:'urgency',label:'Urgency or secrecy pressure',weight:18,pattern:/\b(urgent|immediately|act now|do not tell|keep (?:this|it) secret|stay on the line)\b/i,guidance:'Pause. A legitimate organization will let you verify independently.'},
@@ -1270,7 +1270,7 @@ async function startOwnedDevServer({
1270
1270
  }
1271
1271
  }catch{ /* Non-URL values remain under their existing owner. */ }
1272
1272
  };
1273
- const selectedVersion = rewrite && mode === 'source' ? (pwaEnabled ? null : assetVersion) : undefined;
1273
+ const selectedVersion = rewrite && mode === 'source' ? null : undefined;
1274
1274
  const pwaEntry = mode === 'source' && pwaEnabled && entryDocument;
1275
1275
  const transformed = pwaEntry || (selectedVersion !== undefined
1276
1276
  && /\.(?:m?js|html?|css|json)$/iu.test(extension));
@@ -1285,7 +1285,7 @@ async function startOwnedDevServer({
1285
1285
  pwaEntry,
1286
1286
  onReference,
1287
1287
  lastModified: transformed
1288
- ? sourceRepresentationModifiedAt(opened, selectedVersion, pwaEntry)
1288
+ ? sourceRepresentationModifiedAt(opened, assetVersion, pwaEntry)
1289
1289
  : undefined
1290
1290
  }
1291
1291
  );
@@ -820,12 +820,13 @@ function moduleImportsFromTokens(tokens,importer){
820
820
  }
821
821
 
822
822
  export function versionAssetUrl(value,version=SDK_VERSION){
823
+ // Keep the version argument accepted; SDK versions no longer identify resource URLs.
823
824
  if(!is.string(value))return value;
824
825
  return applyReferenceEdits(value,assetUrlVersionEdits(value,version));
825
826
  }
826
827
 
827
828
  function assetUrlVersionEdits(value,version){
828
- if(!is.string(value)||!value||(!version&&version!==null)||value.startsWith('#')
829
+ if(!is.string(value)||!value||value.startsWith('#')
829
830
  ||value.startsWith('//')||/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(value)
830
831
  ||/^\s/u.test(value))return [];
831
832
  const fragmentStart=value.indexOf('#');
@@ -833,14 +834,10 @@ function assetUrlVersionEdits(value,version){
833
834
  const queryStart=address.indexOf('?');
834
835
  const pathname=queryStart<0?address:address.slice(0,queryStart);
835
836
  if(!pathname)return [];
836
- const clean=version===null;
837
- if(clean&&queryStart<0)return [];
838
- const versionValue=clean?'':encodeURIComponent(String(version));
839
- if(queryStart<0)return [{start:address.length,end:address.length,value:`?arcaneVersion=${versionValue}`}];
837
+ if(queryStart<0)return [];
840
838
  const query=address.slice(queryStart+1);
841
839
  const edits=[];
842
840
  const parameters=[];
843
- let versionFound=false;
844
841
  let offset=queryStart+1;
845
842
  for(const parameter of query.split('&')){
846
843
  const equals=parameter.indexOf('=');
@@ -848,19 +845,11 @@ function assetUrlVersionEdits(value,version){
848
845
  let decodedKey=key;
849
846
  try{decodedKey=decodeURIComponent(key.replaceAll('+',' '));}
850
847
  catch{decodedKey=key;}
851
- const remove=decodedKey==='arcaneVersion'&&(clean||versionFound);
848
+ const remove=decodedKey==='arcaneVersion';
852
849
  parameters.push({start:offset,end:offset+parameter.length,remove});
853
- if(decodedKey==='arcaneVersion'&&!versionFound&&!clean){
854
- versionFound=true;
855
- edits.push({
856
- start:offset+(equals<0?parameter.length:equals+1),
857
- end:offset+parameter.length,
858
- value:`${equals<0?'=':''}${versionValue}`
859
- });
860
- }
861
850
  offset+=parameter.length+1;
862
851
  }
863
- if(clean&&parameters.every(function removesSdkField(parameter){
852
+ if(parameters.every(function removesSdkField(parameter){
864
853
  return parameter.remove;
865
854
  }))return [{start:queryStart,end:address.length,value:''}];
866
855
  // Remove adjacent SDK version fields together, including only their separator.
@@ -875,11 +864,6 @@ function assetUrlVersionEdits(value,version){
875
864
  value:''
876
865
  });
877
866
  }
878
- if(!clean&&!versionFound)edits.push({
879
- start:address.length,
880
- end:address.length,
881
- value:`&arcaneVersion=${versionValue}`
882
- });
883
867
  return edits;
884
868
  }
885
869
 
@@ -1126,7 +1110,7 @@ function cssReferenceEdits(source,version,onReference,versionReference=versionAs
1126
1110
  return edits;
1127
1111
  }
1128
1112
 
1129
- function importMapReferenceEdits(source,version,onReference){
1113
+ function importMapReferenceEdits(source,version,onReference,rewrite=true){
1130
1114
  const tokens=tokenize(source);
1131
1115
  const edits=[];
1132
1116
  function objectProperties(start){
@@ -1151,115 +1135,62 @@ function importMapReferenceEdits(source,version,onReference){
1151
1135
  }
1152
1136
  return properties;
1153
1137
  }
1154
- function addAlias(property,keys,value,aliases,alias=versionImportMapSpecifier(property.key,version)){
1155
- if(alias===property.key||keys.has(alias))return;
1156
- const keyEdit=stringReferenceEdit(source,property.keyToken,version,importMapUrlVersionEdits(property.key,version));
1157
- if(!keyEdit)return;
1158
- keys.add(alias);
1159
- aliases.push(`${keyEdit.value}: ${value}`);
1160
- }
1161
- function prependAliases(start,aliases,selectedEdits){
1162
- if(aliases.length===0)return;
1163
- // Authored keys retain priority even when different spellings normalize to one URL.
1164
- const offset=tokens[start].end;
1165
- selectedEdits.push({start:offset,end:offset,value:`${aliases.join(', ')}, `});
1166
- }
1167
- function addImports(start){
1168
- const selectedEdits=[];
1169
- const aliases=[];
1170
- const properties=objectProperties(start);
1171
- const keys=new Set(properties.map(function importKey(property){return property.key;}));
1138
+ function cleanProperties(properties,{scopes=false}={}){
1139
+ if(!rewrite)return properties;
1140
+ const groups=new Map();
1141
+ const removed=new Set();
1172
1142
  for(const property of properties){
1173
- if(property.value.value==='null'&&property.value.type==='identifier'){
1174
- addAlias(property,keys,'null',aliases);
1175
- continue;
1176
- }
1177
- if(property.value.type!=='string')continue;
1178
- if(property.key.endsWith('/'))continue;
1179
- reportAssetReference(onReference,property.value.value,'import');
1180
- const edit=property.value.value.split(/[?#]/u)[0].endsWith('/')
1181
- ?null:stringReferenceEdit(source,property.value,version);
1182
- if(edit)selectedEdits.push(edit);
1183
- addAlias(property,keys,edit?.value??source.slice(property.value.start,property.value.end),aliases);
1184
- }
1185
- prependAliases(start,aliases,selectedEdits);
1186
- return selectedEdits;
1187
- }
1188
- if(version===null){
1189
- function cleanProperties(properties,{scopes=false}={}){
1190
- const groups=new Map();
1191
- const removed=new Set();
1192
- for(const property of properties){
1193
- const key=scopes
1194
- ?versionImportMapUrl(property.key,null)
1195
- :versionImportMapSpecifier(property.key,null);
1196
- if(!groups.has(key))groups.set(key,[]);
1197
- groups.get(key).push(property);
1198
- }
1199
- for(const [key,group] of groups){
1200
- if(group.every(function unchangedKey(property){return property.key===key;}))continue;
1201
- // An authored clean key owns the target when old generated aliases converge.
1202
- const selected=group.findLast(function existingCleanKey(property){
1203
- return property.key===key;
1204
- })??group.at(-1);
1205
- for(const property of group){
1206
- if(property!==selected)removed.add(property);
1207
- }
1208
- const edit=stringReferenceEdit(
1209
- source,
1210
- selected.keyToken,
1211
- null,
1212
- importMapUrlVersionEdits(selected.key,null)
1213
- );
1214
- if(edit)edits.push(edit);
1215
- }
1216
- for(let index=0;index<properties.length;index+=1){
1217
- if(!removed.has(properties[index]))continue;
1218
- const first=index;
1219
- while(removed.has(properties[index+1]))index+=1;
1220
- edits.push({
1221
- start:first===0?properties[first].keyToken.start:properties[first-1].end,
1222
- end:first===0&&index<properties.length-1
1223
- ?properties[index+1].keyToken.start:properties[index].end,
1224
- value:''
1225
- });
1226
- }
1227
- return properties.filter(function retainedProperty(property){return !removed.has(property);});
1228
- }
1229
- function cleanImports(start){
1230
- const properties=cleanProperties(objectProperties(start));
1231
- for(const property of properties){
1232
- if(property.value.type!=='string'||property.key.endsWith('/'))continue;
1233
- reportAssetReference(onReference,property.value.value,'import');
1234
- if(property.value.value.split(/[?#]/u)[0].endsWith('/'))continue;
1235
- const edit=stringReferenceEdit(source,property.value,null);
1236
- if(edit)edits.push(edit);
1143
+ const key=scopes
1144
+ ?versionImportMapUrl(property.key,null)
1145
+ :versionImportMapSpecifier(property.key,null);
1146
+ if(!groups.has(key))groups.set(key,[]);
1147
+ groups.get(key).push(property);
1148
+ }
1149
+ for(const [key,group] of groups){
1150
+ if(group.every(function unchangedKey(property){return property.key===key;}))continue;
1151
+ // An authored clean key owns the target when old generated aliases converge.
1152
+ const selected=group.findLast(function existingCleanKey(property){
1153
+ return property.key===key;
1154
+ })??group.at(-1);
1155
+ for(const property of group){
1156
+ if(property!==selected)removed.add(property);
1237
1157
  }
1158
+ const edit=stringReferenceEdit(
1159
+ source,
1160
+ selected.keyToken,
1161
+ null,
1162
+ importMapUrlVersionEdits(selected.key,null)
1163
+ );
1164
+ if(edit)edits.push(edit);
1238
1165
  }
1239
- for(const property of objectProperties(0)){
1240
- if(property.key==='imports')cleanImports(property.valueIndex);
1241
- if(property.key==='scopes'){
1242
- const scopes=cleanProperties(objectProperties(property.valueIndex),{scopes:true});
1243
- for(const scope of scopes)cleanImports(scope.valueIndex);
1244
- }
1166
+ for(let index=0;index<properties.length;index+=1){
1167
+ if(!removed.has(properties[index]))continue;
1168
+ const first=index;
1169
+ while(removed.has(properties[index+1]))index+=1;
1170
+ edits.push({
1171
+ start:first===0?properties[first].keyToken.start:properties[first-1].end,
1172
+ end:first===0&&index<properties.length-1
1173
+ ?properties[index+1].keyToken.start:properties[index].end,
1174
+ value:''
1175
+ });
1176
+ }
1177
+ return properties.filter(function retainedProperty(property){return !removed.has(property);});
1178
+ }
1179
+ function cleanImports(start){
1180
+ const properties=cleanProperties(objectProperties(start));
1181
+ for(const property of properties){
1182
+ if(property.value.type!=='string'||property.key.endsWith('/'))continue;
1183
+ reportAssetReference(onReference,property.value.value,'import');
1184
+ if(!rewrite||property.value.value.split(/[?#]/u)[0].endsWith('/'))continue;
1185
+ const edit=stringReferenceEdit(source,property.value,null);
1186
+ if(edit)edits.push(edit);
1245
1187
  }
1246
- return edits;
1247
1188
  }
1248
1189
  for(const property of objectProperties(0)){
1249
- if(property.key==='imports')edits.push(...addImports(property.valueIndex));
1190
+ if(property.key==='imports')cleanImports(property.valueIndex);
1250
1191
  if(property.key==='scopes'){
1251
- const scopes=objectProperties(property.valueIndex);
1252
- const aliases=[];
1253
- const keys=new Set(scopes.map(function scopeKey(scope){return scope.key;}));
1254
- for(const scope of scopes){
1255
- const scopeEdits=addImports(scope.valueIndex);
1256
- edits.push(...scopeEdits);
1257
- const value=applyReferenceEdits(source.slice(scope.value.start,scope.end),scopeEdits.map(function scopeRelativeEdit(edit){
1258
- return {...edit,start:edit.start-scope.value.start,end:edit.end-scope.value.start};
1259
- }));
1260
- if(!scope.key.endsWith('/'))addAlias(scope,keys,value,aliases,versionImportMapUrl(scope.key,version));
1261
- }
1262
- prependAliases(property.valueIndex,aliases,edits);
1192
+ const scopes=cleanProperties(objectProperties(property.valueIndex),{scopes:true});
1193
+ for(const scope of scopes)cleanImports(scope.valueIndex);
1263
1194
  }
1264
1195
  }
1265
1196
  return edits;
@@ -1312,12 +1243,7 @@ function versionImportMapUrl(value,version){
1312
1243
  }
1313
1244
 
1314
1245
  function importMapUrlVersionEdits(value,version){
1315
- // Absolute map keys can match a relative import resolved against the document.
1316
- const authority=/^(?:[A-Za-z][A-Za-z0-9+.-]*:)?\/\/[^/?#]*/u.exec(value)?.[0];
1317
- const offset=authority?.length??0;
1318
- return assetUrlVersionEdits(value.slice(offset),version).map(function absoluteKeyEdit(edit){
1319
- return {...edit,start:edit.start+offset,end:edit.end+offset};
1320
- });
1246
+ return assetUrlVersionEdits(value,version);
1321
1247
  }
1322
1248
 
1323
1249
  function validateInventory(files){
@@ -1471,12 +1397,12 @@ async function physicalRuntime(workspaceRoot,signal){
1471
1397
  return {files};
1472
1398
  }
1473
1399
 
1474
- async function managedImportMapBuild(resolvedWorkspace,resolvedApp,signal,pwaEnabled=false){
1400
+ async function managedImportMapBuild(resolvedWorkspace,resolvedApp,signal){
1475
1401
  const installed=await readInstalledSdkLayout(resolvedWorkspace);
1476
- const [runtime,version]=await Promise.all([
1477
- installed?installedRuntimeFiles(resolvedWorkspace,installed,signal):physicalRuntime(resolvedWorkspace,signal),
1478
- pwaEnabled?null:installed?.version??readWorkspaceAssetVersion(resolvedWorkspace)
1479
- ]);
1402
+ const runtime=await (installed
1403
+ ?installedRuntimeFiles(resolvedWorkspace,installed,signal)
1404
+ :physicalRuntime(resolvedWorkspace,signal));
1405
+ const version=null;
1480
1406
  const built=await buildImportMap({files:runtime.files,signal,version});
1481
1407
  if(installed?.direct){
1482
1408
  const imports={};
@@ -2055,11 +1981,12 @@ function htmlReferenceEdits(source,version,onReference){
2055
1981
  const linkResources=new Set(['stylesheet','modulepreload','preload','icon','manifest']);
2056
1982
  const base=structure.bases[0];
2057
1983
  const baseHref=base?structuralAttribute(parseTagAttributes(base.open),'href','base'):null;
2058
- if(baseHref&&(/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(baseHref)||baseHref.startsWith('//')))version='';
1984
+ const externalBase=baseHref&&(/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(baseHref)||baseHref.startsWith('//'));
2059
1985
  function reportHtmlReference(reference){
2060
1986
  if(is.function(onReference))onReference({...reference,baseHref});
2061
1987
  }
2062
1988
  function addNestedEdits(offset,nested){
1989
+ if(externalBase)return;
2063
1990
  for(const edit of nested)edits.push({...edit,start:offset+edit.start,end:offset+edit.end});
2064
1991
  }
2065
1992
  for(const element of structure.elements){
@@ -2091,6 +2018,7 @@ function htmlReferenceEdits(source,version,onReference){
2091
2018
  if(view)reportAssetReference(reportHtmlReference,view.decoded,kind,
2092
2019
  (name==='href'||name==='src')&&view.decoded.startsWith('./arcane/')
2093
2020
  ?'component-runtime':undefined);
2021
+ if(externalBase)continue;
2094
2022
  const value=versionHtmlAttribute(original,version);
2095
2023
  if(value===original)continue;
2096
2024
  const position=attributes.positions.get(name);
@@ -2110,7 +2038,7 @@ function htmlReferenceEdits(source,version,onReference){
2110
2038
  if(attributes.has('src'))continue;
2111
2039
  const type=scriptType(attributes);
2112
2040
  const body=source.slice(script.openEnd,script.contentEnd);
2113
- if(type==='importmap')addNestedEdits(script.openEnd,importMapReferenceEdits(body,version,reportHtmlReference));
2041
+ if(type==='importmap')addNestedEdits(script.openEnd,importMapReferenceEdits(body,version,reportHtmlReference,!externalBase));
2114
2042
  else if(['','module','text/javascript','application/javascript'].includes(type)){
2115
2043
  addNestedEdits(script.openEnd,javascriptReferenceEdits(body,version,reportHtmlReference));
2116
2044
  }
@@ -2868,14 +2796,7 @@ async function generateImportMapUnlocked({
2868
2796
  renderManagedHtml(html,'{"imports":{}}\n',baseHref);
2869
2797
  documentStates.push({filePath:documentPath,html,label,baseHref});
2870
2798
  }
2871
- let pwaEnabled=false;
2872
- try{
2873
- const packageSource=await readFileFromDisk(path.join(resolvedApp,'arcane-package.json'),'utf8');
2874
- pwaEnabled=JSON.parse(packageSource)?.pwa?.enabled===true;
2875
- }catch(error){
2876
- if(error?.code!=='ENOENT')throw error;
2877
- }
2878
- const {built,json,version}=await managedImportMapBuild(resolvedWorkspace,resolvedApp,signal,pwaEnabled);
2799
+ const {built,json,version}=await managedImportMapBuild(resolvedWorkspace,resolvedApp,signal);
2879
2800
  const renderedDocuments=documentStates.map(item=>({
2880
2801
  ...item,
2881
2802
  content:rewriteAssetReferences(renderManagedHtml(item.html,json,item.baseHref),{
@@ -883,7 +883,6 @@ async function packageWithContext(context,options={}){
883
883
  // Traverse actual browser resources after the adapter finishes. Files
884
884
  // included only as application documents retain their original content.
885
885
  const assetVersion=await readWorkspaceAssetVersion(context.workspaceRoot);
886
- const version=pwaEnabled?null:assetVersion;
887
886
  const inventory=new Set(files);
888
887
  const offlineInventory=pwaEnabled?new Set(selectPwaFiles(files,context.config.pwa,appPath)):null;
889
888
  const offlineReferences=new Set();
@@ -929,7 +928,7 @@ async function packageWithContext(context,options={}){
929
928
  for(const file of files){
930
929
  if((sharedFiles.has(file)||/^arcane\/(?:modules|entities|components|css|sdk|dependencies)\//u.test(file))
931
930
  &&/\.(?:m?js|html?|css)$/iu.test(file))pending.push({file,documentUrl});
932
- if(pwaEnabled&&path.posix.basename(file)==='arcane.importmap.json'){
931
+ if(path.posix.basename(file)==='arcane.importmap.json'){
933
932
  pending.push({file,documentUrl});
934
933
  }
935
934
  }
@@ -954,7 +953,7 @@ async function packageWithContext(context,options={}){
954
953
  const contextKey=`${relative}\n${current.documentUrl.href}`;
955
954
  if(visited.has(contextKey)||!inventory.has(relative)
956
955
  ||(!/\.(?:m?js|html?|css)$/iu.test(relative)
957
- &&!(pwaEnabled&&path.posix.basename(relative)==='arcane.importmap.json')))continue;
956
+ &&path.posix.basename(relative)!=='arcane.importmap.json'))continue;
958
957
  visited.add(contextKey);
959
958
  const filePath=path.join(stagingRoot,...relative.split('/'));
960
959
  let resource=resources.get(relative);
@@ -962,7 +961,7 @@ async function packageWithContext(context,options={}){
962
961
  const original=pwaDocumentSources.get(relative)?.source??await readFile(filePath,'utf8');
963
962
  const references=[];
964
963
  const content=rewriteAssetReferences(original,{
965
- filePath:relative,version,onReference:reference=>references.push(reference)
964
+ filePath:relative,version:null,onReference:reference=>references.push(reference)
966
965
  });
967
966
  if(content!==original)await writeFile(filePath,content,'utf8');
968
967
  resource={references,...(pwaDocumentSources.has(relative)?{content}:{})};
@@ -133,7 +133,7 @@ appropriate.
133
133
  </script>
134
134
  `:'';
135
135
  const bootstrapMarkup=namedImports?'':
136
- ` <script type="module" src="${runtimePrefix}/modules/ThemeBootstrap.js?v=1"></script>\n`;
136
+ ` <script type="module" src="${runtimePrefix}/modules/ThemeBootstrap.js"></script>\n`;
137
137
  const files=new Map();
138
138
  files.set('.gitignore',[
139
139
  'node_modules/',
@@ -322,9 +322,9 @@ ${importMapMarkup} <meta name="viewport" content="width=device-width, initial
322
322
  <meta name="theme-color" content="rgb(23, 34, 56)">
323
323
  <title>${html(name)}</title>
324
324
  <link rel="manifest" href="./${html(appPrefix)}manifest.json">
325
- <link rel="stylesheet" href="${runtimePrefix}/css/theme.css?v=1">
326
- <link rel="stylesheet" href="${runtimePrefix}/css/primitives.css?v=1">
327
- <link rel="stylesheet" href="./${html(appPrefix)}${html(appId)}.css?v=1">
325
+ <link rel="stylesheet" href="${runtimePrefix}/css/theme.css">
326
+ <link rel="stylesheet" href="${runtimePrefix}/css/primitives.css">
327
+ <link rel="stylesheet" href="./${html(appPrefix)}${html(appId)}.css">
328
328
  ${bootstrapMarkup}</head>
329
329
  <body>
330
330
  <main class="app-shell">
@@ -341,7 +341,7 @@ ${bootstrapMarkup}</head>
341
341
  </div>
342
342
  </section>
343
343
  </main>
344
- <script type="module" src="./${html(appPrefix)}modules/App.js?v=1"></script>
344
+ <script type="module" src="./${html(appPrefix)}modules/App.js"></script>
345
345
  </body>
346
346
  </html>
347
347
  `);
@@ -52,14 +52,14 @@ async function realDirectory(location,label){
52
52
  return canonical;
53
53
  }
54
54
 
55
- async function copyCompleteEntry(source,destination,label,signal,version){
55
+ async function copyCompleteEntry(source,destination,label,signal){
56
56
  throwIfAborted(signal);
57
57
  const info=await lstat(source);
58
58
  if(info.isSymbolicLink())fail(`${label} must not contain a symbolic link or junction.`);
59
59
  if(info.isFile()){
60
60
  if(/\.(?:m?js|html?|css)$/iu.test(source)){
61
61
  const original=await readFile(source,'utf8');
62
- const content=rewriteAssetReferences(original,{filePath:source,version});
62
+ const content=rewriteAssetReferences(original,{filePath:source,version:null});
63
63
  await writeFile(destination,content,'utf8');
64
64
  }else{
65
65
  await copyFile(source,destination);
@@ -76,8 +76,7 @@ async function copyCompleteEntry(source,destination,label,signal,version){
76
76
  path.join(source,entry.name),
77
77
  path.join(destination,entry.name),
78
78
  `${label}/${entry.name}`,
79
- signal,
80
- version
79
+ signal
81
80
  );
82
81
  }
83
82
  }
@@ -113,14 +112,13 @@ export async function materializeWorkspaceRuntimeContent({
113
112
 
114
113
  await mkdir(stagingRoot);
115
114
  try{
116
- await copyCompleteEntry(runtimeArcane,stagingRoot,'SDK Arcane runtime',signal,sdkVersion);
115
+ await copyCompleteEntry(runtimeArcane,stagingRoot,'SDK Arcane runtime',signal);
117
116
  await mkdir(path.join(stagingRoot,'dependencies'),{recursive:true});
118
117
  await copyCompleteEntry(
119
118
  runtimeStrongType,
120
119
  path.join(stagingRoot,'dependencies','strong-type'),
121
120
  'SDK strong-type runtime',
122
- signal,
123
- sdkVersion
121
+ signal
124
122
  );
125
123
  const sdkDestination=path.join(stagingRoot,'sdk');
126
124
  await mkdir(sdkDestination,{recursive:true});
@@ -132,8 +130,7 @@ export async function materializeWorkspaceRuntimeContent({
132
130
  path.join(browserRuntime,entry.name),
133
131
  path.join(sdkDestination,entry.name),
134
132
  `SDK browser runtime/${entry.name}`,
135
- signal,
136
- sdkVersion
133
+ signal
137
134
  );
138
135
  }
139
136