arcane-os 0.1.2 → 0.2.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/NOTICE +5 -3
  3. package/README.md +73 -24
  4. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +67 -18
  5. package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +16 -5
  6. package/browser-runtime/ai/browser-kokoro-worker.mjs +3 -0
  7. package/browser-runtime/ai/browser-speech-artifacts.mjs +1108 -0
  8. package/browser-runtime/ai/browser-speech-providers.mjs +780 -0
  9. package/browser-runtime/ai/browser-speech.mjs +9 -0
  10. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +1537 -167
  11. package/browser-runtime/ai/browser-wasm.mjs +46 -1
  12. package/browser-runtime/ai/browser-whisper-worker.mjs +3 -0
  13. package/browser-runtime/ai/browser-wllama-runtime.mjs +677 -132
  14. package/browser-runtime/ai/model-controller.mjs +138 -12
  15. package/browser-runtime/ai/speech-worker-client.mjs +207 -0
  16. package/browser-runtime/ai/speech-worker-runtime.mjs +516 -0
  17. package/browser-runtime/ai/wllama/index.mjs +389 -0
  18. package/docs/architecture.md +132 -22
  19. package/docs/reference/README.md +1 -1
  20. package/docs/reference/ai/browser-wasm.md +101 -42
  21. package/docs/reference/availability-and-normalization.md +19 -5
  22. package/docs/reference/behavioral-testing.md +18 -5
  23. package/docs/reference/cli.md +2 -2
  24. package/docs/reference/inventory/package-api.json +14 -14
  25. package/docs/reference/protocols.md +4 -4
  26. package/docs/reference/sdk-api.md +68 -38
  27. package/docs/work-amplification.md +8 -4
  28. package/package.json +7 -3
  29. package/runtime/ARCANE_RUNTIME_RELEASE.json +50 -20
  30. package/runtime/arcane/components/chat.html +551 -62
  31. package/runtime/arcane/components/speech.html +1113 -265
  32. package/runtime/arcane/entities/Chat.js +246 -43
  33. package/runtime/arcane/modules/AI.js +1394 -162
  34. package/runtime/arcane/modules/AIProviderRuntime.js +2289 -0
  35. package/runtime/arcane/modules/AIRuntimeState.js +872 -0
  36. package/runtime/arcane/modules/ConfiguredAIChatSession.js +382 -31
  37. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +1106 -0
  38. package/runtime/arcane/modules/DocumentLexicalSearch.js +292 -0
  39. package/runtime/arcane/modules/PersistentAIChatSession.js +268 -0
  40. package/runtime/arcane/modules/StaticDocumentCatalog.js +25 -206
  41. package/schemas/arcane-lock.schema.json +10 -6
  42. package/src/cli/main.mjs +14 -2
  43. package/src/constants.mjs +1 -1
  44. package/src/dev-server.mjs +273 -26
  45. package/src/doctor.mjs +1 -3
  46. package/src/import-map.mjs +193 -84
  47. package/src/packager/core.mjs +313 -41
  48. package/src/runtime.mjs +14 -4
  49. package/src/scaffold.mjs +45 -17
  50. package/src/sdk-browser-runtime.mjs +28 -75
  51. package/src/templates/workspace-template.mjs +27 -8
  52. package/src/toolchain.mjs +13 -2
  53. package/src/workspace-runtime.mjs +1 -1
  54. package/src/workspace.mjs +178 -25
@@ -14,15 +14,26 @@ export const MANAGED_IMPORT_MAP_ATTRIBUTE='data-arcane-import-map';
14
14
  const JAVASCRIPT_EXTENSION=/\.(?:js|mjs)$/u;
15
15
  const NODE_ONLY_MODULE='modules/CaseEvidenceIndexer.js';
16
16
  const RUNTIME_STRONG_TYPE_IMPORT='../../node_modules/strong-type/index.js';
17
+ const PERSISTENT_CHAT_IMPORT='#arcane/persistent-ai-chat-session';
18
+ const PERSISTENT_CHAT_MODULE='modules/PersistentAIChatSession.js';
17
19
  const SDK_BROWSER_ENTRY='sdk/event-manager.mjs';
18
20
  const SDK_BROWSER_AI_ENTRY='sdk/ai/browser-wasm.mjs';
21
+ const SDK_BROWSER_SPEECH_ENTRY='sdk/ai/browser-speech.mjs';
22
+ const SDK_BROWSER_SPEECH_WORKER_RUNTIME='sdk/ai/speech-worker-runtime.mjs';
19
23
  const SDK_BROWSER_FILES=Object.freeze([
20
24
  'sdk/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json',
25
+ 'sdk/ai/browser-kokoro-worker.mjs',
26
+ 'sdk/ai/browser-speech-artifacts.mjs',
27
+ 'sdk/ai/browser-speech-providers.mjs',
28
+ SDK_BROWSER_SPEECH_ENTRY,
21
29
  'sdk/ai/browser-wasm-llm-provider.mjs',
22
30
  SDK_BROWSER_AI_ENTRY,
31
+ 'sdk/ai/browser-whisper-worker.mjs',
23
32
  'sdk/ai/browser-wllama-runtime.mjs',
24
33
  'sdk/ai/internal/sha256.mjs',
25
34
  'sdk/ai/model-controller.mjs',
35
+ 'sdk/ai/speech-worker-client.mjs',
36
+ SDK_BROWSER_SPEECH_WORKER_RUNTIME,
26
37
  'sdk/ai/wllama/LICENCE',
27
38
  'sdk/ai/wllama/index.mjs',
28
39
  'sdk/ai/wllama/llama.cpp-LICENSE',
@@ -75,6 +86,35 @@ function safeRelativePath(value,label='path'){
75
86
  return value;
76
87
  }
77
88
 
89
+ function normalizedDocumentPaths(entry,documents){
90
+ if(documents===undefined)return Object.freeze([entry]);
91
+ if(!Array.isArray(documents)||documents.length===0){
92
+ fail('Import-map documents must be a non-empty array of application-relative paths.');
93
+ }
94
+ const normalized=[];
95
+ const identities=new Map();
96
+ for(const [index,value] of documents.entries()){
97
+ const relative=safeRelativePath(value,`documents[${String(index)}]`);
98
+ const key=collisionKey(relative);
99
+ const prior=identities.get(key);
100
+ if(prior!==undefined){
101
+ fail(
102
+ `Import-map documents contain a duplicate or portable path collision: `
103
+ +`${prior} and ${relative}.`
104
+ );
105
+ }
106
+ identities.set(key,relative);
107
+ normalized.push(relative);
108
+ }
109
+ if(!normalized.includes(entry)){
110
+ fail(`Import-map documents must include the configured application entry: ${entry}.`);
111
+ }
112
+ return Object.freeze([
113
+ entry,
114
+ ...normalized.filter(relative=>relative!==entry).sort(compareUtf8)
115
+ ]);
116
+ }
117
+
78
118
  function decodedEscape(source,index){
79
119
  const character=source[index];
80
120
  if(/[1-9]/u.test(character)||(character==='0'&&/[0-9]/u.test(source[index+1]??''))){
@@ -719,6 +759,14 @@ function nonliteralDynamic(importer,offset){
719
759
  );
720
760
  }
721
761
 
762
+ function isAuthorizedSpeechRuntimeImport(tokens,index,close,importer){
763
+ if(importer!==SDK_BROWSER_SPEECH_WORKER_RUNTIME||close!==index+5)return false;
764
+ const [entry,dot,moduleUrl]=tokens.slice(index+2,close);
765
+ return entry?.type==='identifier'&&entry.value==='entry'
766
+ &&dot?.value==='.'
767
+ &&moduleUrl?.type==='identifier'&&moduleUrl.value==='moduleUrl';
768
+ }
769
+
722
770
  export function scanModuleImports(source,{importer='<module>'}={}){
723
771
  if(typeof source!=='string')throw new TypeError('scanModuleImports source must be a string.');
724
772
  const tokens=tokenize(source);
@@ -745,6 +793,11 @@ export function scanModuleImports(source,{importer='<module>'}={}){
745
793
  ||commas.length>2
746
794
  ||(commas.length===2
747
795
  &&(commas[1]!==close-1||commas[1]===commas[0]+1))){
796
+ if(isAuthorizedSpeechRuntimeImport(tokens,index,close,importer)){
797
+ hasModuleSyntax=true;
798
+ index=close;
799
+ continue;
800
+ }
748
801
  nonliteralDynamic(importer,current.start);
749
802
  }
750
803
  hasModuleSyntax=true;
@@ -811,6 +864,20 @@ function unresolved(importer,specifier,normalizedTarget,reason='is not in the sh
811
864
  }
812
865
 
813
866
  function resolveImport(importer,specifier,files){
867
+ if(specifier===PERSISTENT_CHAT_IMPORT){
868
+ if(!files.has(PERSISTENT_CHAT_MODULE)){
869
+ unresolved(importer,specifier,PERSISTENT_CHAT_MODULE);
870
+ }
871
+ return {target:PERSISTENT_CHAT_MODULE,persistentChat:true};
872
+ }
873
+ if(specifier.startsWith(PERSISTENT_CHAT_IMPORT)){
874
+ unresolved(
875
+ importer,
876
+ specifier,
877
+ PERSISTENT_CHAT_MODULE,
878
+ 'does not match the exact browser import-map key'
879
+ );
880
+ }
814
881
  const reachableSpecifier=stripQueryAndHash(specifier);
815
882
  if(!reachableSpecifier)unresolved(importer,specifier,'<empty>');
816
883
  if(reachableSpecifier.includes('%')){
@@ -966,9 +1033,13 @@ export async function buildImportMap({files,readFile,signal}={}){
966
1033
  }
967
1034
  const hasSdkBrowserGraph=inventory.has(SDK_BROWSER_ENTRY);
968
1035
  const hasSdkAiGraph=inventory.has(SDK_BROWSER_AI_ENTRY);
1036
+ const hasSdkSpeechGraph=inventory.has(SDK_BROWSER_SPEECH_ENTRY);
969
1037
  if(hasSdkAiGraph&&!hasSdkBrowserGraph){
970
1038
  unresolved(SDK_BROWSER_AI_ENTRY,'<authenticated SDK browser closure>',SDK_BROWSER_ENTRY);
971
1039
  }
1040
+ if(hasSdkSpeechGraph&&!hasSdkAiGraph){
1041
+ unresolved(SDK_BROWSER_SPEECH_ENTRY,'<authenticated SDK browser closure>',SDK_BROWSER_AI_ENTRY);
1042
+ }
972
1043
  if(hasSdkBrowserGraph){
973
1044
  for(const required of SDK_BROWSER_FILES){
974
1045
  if(!inventory.has(required)){
@@ -1016,11 +1087,20 @@ export async function buildImportMap({files,readFile,signal}={}){
1016
1087
  'arcane-os/ai/browser-wasm',
1017
1088
  './arcane/sdk/ai/browser-wasm.mjs'
1018
1089
  );
1090
+ registerSpecifier(
1091
+ namedRegistry,
1092
+ 'arcane-os/ai/browser-speech',
1093
+ './arcane/sdk/ai/browser-speech.mjs'
1094
+ );
1019
1095
  }
1020
1096
  }
1021
1097
 
1022
1098
  const sdkRoots=hasSdkBrowserGraph
1023
- ?[SDK_BROWSER_ENTRY,...(hasSdkAiGraph?[SDK_BROWSER_AI_ENTRY]:[])]:[];
1099
+ ?[
1100
+ SDK_BROWSER_ENTRY,
1101
+ ...(hasSdkAiGraph?[SDK_BROWSER_AI_ENTRY]:[]),
1102
+ ...(hasSdkSpeechGraph?[SDK_BROWSER_SPEECH_ENTRY]:[]),
1103
+ ]:[];
1024
1104
  const queue=[...roots,...sdkRoots];
1025
1105
  const reached=new Set();
1026
1106
  const entities=new Set();
@@ -1036,6 +1116,13 @@ export async function buildImportMap({files,readFile,signal}={}){
1036
1116
  const resolution=resolveImport(importer,imported.specifier,inventory);
1037
1117
  if(resolution.runtimeStrongType)usesRuntimeStrongType=true;
1038
1118
  if(resolution.eventPubSub)usesEventPubSub=true;
1119
+ if(resolution.persistentChat){
1120
+ registerSpecifier(
1121
+ namedRegistry,
1122
+ PERSISTENT_CHAT_IMPORT,
1123
+ './arcane/modules/PersistentAIChatSession.js'
1124
+ );
1125
+ }
1039
1126
  if(resolution.target.startsWith('entities/'))entities.add(resolution.target);
1040
1127
  if(!reached.has(resolution.target))queue.push(resolution.target);
1041
1128
  }
@@ -1702,14 +1789,20 @@ export function inspectImportMapHtml(html){
1702
1789
  });
1703
1790
  }
1704
1791
 
1705
- function renderManagedHtml(html,json){
1792
+ function documentBaseHref(relative){
1793
+ const directory=path.posix.dirname(relative);
1794
+ const depth=directory==='.'?0:directory.split('/').length;
1795
+ return '../'.repeat(depth+2);
1796
+ }
1797
+
1798
+ function renderManagedHtml(html,json,baseHref='../../'){
1706
1799
  const structure=scanHtmlStructure(html);
1707
1800
  const activeBases=structure.bases.map(base=>({
1708
1801
  ...base,
1709
1802
  href:structuralAttribute(parseTagAttributes(base.open),'href','base')
1710
1803
  }));
1711
- if(activeBases.length!==1||activeBases[0].href!=='../../'){
1712
- fail('Application HTML must contain exactly one active <base href="../../"> element.');
1804
+ if(activeBases.length!==1||activeBases[0].href!==baseHref){
1805
+ fail(`Application HTML must contain exactly one active <base href="${baseHref}"> element.`);
1713
1806
  }
1714
1807
  const complete=[];
1715
1808
  for(const script of structure.scripts){
@@ -1738,8 +1831,8 @@ function renderManagedHtml(html,json){
1738
1831
  ...base,
1739
1832
  href:structuralAttribute(parseTagAttributes(base.open),'href','base')
1740
1833
  }));
1741
- if(cleanedBases.length!==1||cleanedBases[0].href!=='../../'){
1742
- fail('Application HTML must retain exactly one active <base href="../../"> element.');
1834
+ if(cleanedBases.length!==1||cleanedBases[0].href!==baseHref){
1835
+ fail(`Application HTML must retain exactly one active <base href="${baseHref}"> element.`);
1743
1836
  }
1744
1837
  const firstBlocking=firstBlockingLoadPosition(withoutManaged);
1745
1838
  if(firstBlocking>=0&&cleanedBases[0].start>firstBlocking){
@@ -2097,73 +2190,53 @@ async function installStagedFile(state,staged,label){
2097
2190
  };
2098
2191
  }
2099
2192
 
2100
- async function commitGeneratedPair({
2101
- artifactState,
2102
- entryState,
2103
- artifactBytes,
2104
- entryBytes,
2193
+ async function commitGeneratedFiles({
2194
+ files,
2105
2195
  signal,
2106
2196
  onEvent
2107
2197
  }){
2108
- const artifactStage=await stageSibling(
2109
- artifactState.filePath,
2110
- artifactBytes,
2111
- artifactState.directoryState
2112
- );
2113
- let entryStage;
2114
- let artifactInstall;
2115
- let entryInstall;
2198
+ const staged=[];
2199
+ const installed=[];
2116
2200
  let failure;
2117
2201
  try{
2118
- entryStage=await stageSibling(
2119
- entryState.filePath,
2120
- entryBytes,
2121
- entryState.directoryState
2122
- );
2202
+ for(const file of files){
2203
+ throwIfAborted(signal);
2204
+ staged.push(await stageSibling(
2205
+ file.state.filePath,
2206
+ file.bytes,
2207
+ file.state.directoryState
2208
+ ));
2209
+ }
2123
2210
  await emit(onEvent,{type:'import-map.commit.staged'});
2124
2211
  throwIfAborted(signal);
2125
- artifactInstall=await installStagedFile(
2126
- artifactState,
2127
- artifactStage,
2128
- 'Import-map artifact'
2129
- );
2130
- entryInstall=await installStagedFile(entryState,entryStage,'Import-map application entry');
2131
- await artifactInstall.verify();
2132
- await entryInstall.verify();
2212
+ for(const [index,file] of files.entries()){
2213
+ installed.push(await installStagedFile(file.state,staged[index],file.label));
2214
+ throwIfAborted(signal);
2215
+ }
2216
+ for(const transaction of installed)await transaction.verify();
2133
2217
  await emit(onEvent,{
2134
2218
  type:'import-map.commit.progress',
2135
- paths:Object.freeze([artifactState.filePath,entryState.filePath])
2219
+ paths:Object.freeze(files.map(file=>file.state.filePath))
2136
2220
  });
2137
2221
  throwIfAborted(signal);
2138
- await artifactInstall.verify();
2139
- await entryInstall.verify();
2222
+ for(const transaction of installed)await transaction.verify();
2140
2223
  }catch(error){
2141
- if(entryInstall)await entryInstall.rollback().catch(rollback=>{error.rollbackError??=rollback;});
2142
- if(artifactInstall){
2143
- await artifactInstall.rollback().catch(rollback=>{error.rollbackError??=rollback;});
2224
+ for(const transaction of [...installed].reverse()){
2225
+ await transaction.rollback().catch(rollback=>{error.rollbackError??=rollback;});
2144
2226
  }
2145
2227
  failure=error;
2146
2228
  }
2147
2229
  const cleanupErrors=[];
2148
- if(!artifactInstall){
2230
+ for(const [index,stage] of staged.entries()){
2231
+ if(index<installed.length)continue;
2149
2232
  try{
2150
2233
  const removed=await removeOwnedPath(
2151
- artifactStage.path,
2152
- artifactStage.identity,
2153
- artifactStage.directoryState
2154
- );
2155
- if(!removed)fail(`Import-map artifact stage could not be safely cleaned: ${artifactStage.path}.`);
2156
- }catch(error){cleanupErrors.push(error);}
2157
- }
2158
- if(entryStage&&!entryInstall){
2159
- try{
2160
- const removed=await removeOwnedPath(
2161
- entryStage.path,
2162
- entryStage.identity,
2163
- entryStage.directoryState
2234
+ stage.path,
2235
+ stage.identity,
2236
+ stage.directoryState
2164
2237
  );
2165
2238
  if(!removed){
2166
- fail(`Import-map application-entry stage could not be safely cleaned: ${entryStage.path}.`);
2239
+ fail(`${files[index].label} stage could not be safely cleaned: ${stage.path}.`);
2167
2240
  }
2168
2241
  }catch(error){cleanupErrors.push(error);}
2169
2242
  }
@@ -2180,12 +2253,11 @@ async function commitGeneratedPair({
2180
2253
  }
2181
2254
 
2182
2255
  const cleanupWarnings=[];
2183
- for(const installed of [entryInstall,artifactInstall]){
2184
- try{await installed.commit();}
2256
+ for(const transaction of [...installed].reverse()){
2257
+ try{await transaction.commit();}
2185
2258
  catch(error){cleanupWarnings.push(error);}
2186
2259
  }
2187
- await artifactInstall.verify();
2188
- await entryInstall.verify();
2260
+ for(const transaction of installed)await transaction.verify();
2189
2261
  if(cleanupWarnings.length>0){
2190
2262
  return Object.freeze(cleanupWarnings.map(error=>String(error?.message??error)));
2191
2263
  }
@@ -2206,6 +2278,7 @@ async function generateImportMapUnlocked({
2206
2278
  appId,
2207
2279
  appRoot,
2208
2280
  entry='index.html',
2281
+ documents,
2209
2282
  workspaceRuntimeReceipt,
2210
2283
  signal,
2211
2284
  onEvent
@@ -2217,21 +2290,45 @@ async function generateImportMapUnlocked({
2217
2290
  const resolvedWorkspace=path.resolve(workspaceRoot);
2218
2291
  const resolvedApp=resolvedAppRoot(resolvedWorkspace,appId,appRoot);
2219
2292
  const safeEntry=safeRelativePath(entry,'application entry');
2220
- const entryPath=path.resolve(resolvedApp,...safeEntry.split('/'));
2221
- if(!pathInside(resolvedApp,entryPath))fail('Import-map application entry escapes its app root.');
2293
+ const safeDocuments=normalizedDocumentPaths(safeEntry,documents);
2294
+ const documentPaths=Object.freeze(safeDocuments.map(relative=>{
2295
+ const documentPath=path.resolve(resolvedApp,...relative.split('/'));
2296
+ if(!pathInside(resolvedApp,documentPath)){
2297
+ fail(`Import-map application document escapes its app root: ${relative}.`);
2298
+ }
2299
+ return documentPath;
2300
+ }));
2301
+ const entryPath=documentPaths[0];
2222
2302
  const artifactPath=path.join(resolvedApp,...IMPORT_MAP_RELATIVE_PATH.split('/'));
2223
- await emit(onEvent,{type:'import-map.started',appId,artifactPath,entryPath});
2303
+ await emit(onEvent,{
2304
+ type:'import-map.started',
2305
+ appId,
2306
+ artifactPath,
2307
+ entryPath,
2308
+ documentPaths,
2309
+ documentCount:documentPaths.length
2310
+ });
2224
2311
 
2225
- const entryDirectoryState=await captureDirectoryState(
2226
- resolvedWorkspace,
2227
- path.dirname(entryPath)
2228
- );
2229
- const entryState=await readRealFileState(entryPath,'Import-map application entry');
2230
- entryState.directoryState=entryDirectoryState;
2231
- const html=entryState.bytes.toString('utf8');
2232
- // Reject malformed application structure before traversing the substantially larger runtime
2233
- // graph. The real generated map is rendered and revalidated again before commit.
2234
- renderManagedHtml(html,'{"imports":{}}\n');
2312
+ const documentStates=[];
2313
+ for(const [index,documentPath] of documentPaths.entries()){
2314
+ throwIfAborted(signal);
2315
+ const label=index===0
2316
+ ?'Import-map application entry'
2317
+ :`Import-map application document ${safeDocuments[index]}`;
2318
+ const directoryState=await captureDirectoryState(
2319
+ resolvedWorkspace,
2320
+ path.dirname(documentPath)
2321
+ );
2322
+ const state=await readRealFileState(documentPath,label);
2323
+ throwIfAborted(signal);
2324
+ state.directoryState=directoryState;
2325
+ const html=state.bytes.toString('utf8');
2326
+ // Reject malformed application structure before traversing the substantially larger
2327
+ // runtime graph. The real generated map is rendered and revalidated before commit.
2328
+ const baseHref=documentBaseHref(safeDocuments[index]);
2329
+ renderManagedHtml(html,'{"imports":{}}\n',baseHref);
2330
+ documentStates.push({state,html,label,baseHref});
2331
+ }
2235
2332
  let runtime;
2236
2333
  if(workspaceRuntimeReceipt){
2237
2334
  await authenticateWorkspaceRuntimeReceipt(workspaceRuntimeReceipt,{
@@ -2261,7 +2358,10 @@ async function generateImportMapUnlocked({
2261
2358
  const built=await buildImportMap({files:runtime.files,readFile:runtime.readFile,signal});
2262
2359
  const document={imports:built.imports};
2263
2360
  const json=`${JSON.stringify(document,null,2).replaceAll('<','\\u003c')}\n`;
2264
- const renderedHtml=renderManagedHtml(html,json);
2361
+ const renderedDocuments=documentStates.map(item=>Object.freeze({
2362
+ ...item,
2363
+ bytes:Buffer.from(renderManagedHtml(item.html,json,item.baseHref),'utf8')
2364
+ }));
2265
2365
 
2266
2366
  throwIfAborted(signal);
2267
2367
  const artifactDirectoryState=await captureDirectoryState(
@@ -2275,11 +2375,16 @@ async function generateImportMapUnlocked({
2275
2375
  {optional:true}
2276
2376
  );
2277
2377
  artifactState.directoryState=artifactDirectoryState;
2278
- const cleanupWarnings=await commitGeneratedPair({
2279
- artifactState,
2280
- entryState,
2281
- artifactBytes:Buffer.from(json,'utf8'),
2282
- entryBytes:Buffer.from(renderedHtml,'utf8'),
2378
+ const artifactBytes=Buffer.from(json,'utf8');
2379
+ const cleanupWarnings=await commitGeneratedFiles({
2380
+ files:[
2381
+ {state:artifactState,bytes:artifactBytes,label:'Import-map artifact'},
2382
+ ...renderedDocuments.map(item=>({
2383
+ state:item.state,
2384
+ bytes:item.bytes,
2385
+ label:item.label
2386
+ }))
2387
+ ],
2283
2388
  signal,
2284
2389
  onEvent
2285
2390
  });
@@ -2287,21 +2392,23 @@ async function generateImportMapUnlocked({
2287
2392
  Object.freeze({
2288
2393
  role:'artifact',
2289
2394
  path:path.relative(resolvedWorkspace,artifactPath).split(path.sep).join('/'),
2290
- bytes:Buffer.byteLength(json,'utf8'),
2291
- sha256:sha256(Buffer.from(json,'utf8'))
2395
+ bytes:artifactBytes.length,
2396
+ sha256:sha256(artifactBytes)
2292
2397
  }),
2293
- Object.freeze({
2294
- role:'entry',
2295
- path:path.relative(resolvedWorkspace,entryPath).split(path.sep).join('/'),
2296
- bytes:Buffer.byteLength(renderedHtml,'utf8'),
2297
- sha256:sha256(Buffer.from(renderedHtml,'utf8'))
2298
- })
2398
+ ...renderedDocuments.map((item,index)=>Object.freeze({
2399
+ role:index===0?'entry':'document',
2400
+ path:path.relative(resolvedWorkspace,item.state.filePath).split(path.sep).join('/'),
2401
+ bytes:item.bytes.length,
2402
+ sha256:sha256(item.bytes)
2403
+ }))
2299
2404
  ]);
2300
2405
  const receipt=Object.freeze({
2301
2406
  appId,
2302
2407
  artifactPath,
2303
2408
  artifactRelativePath:path.relative(resolvedWorkspace,artifactPath).split(path.sep).join('/'),
2304
2409
  entryPath,
2410
+ documentPaths,
2411
+ documentCount:documentPaths.length,
2305
2412
  imports:built.imports,
2306
2413
  entryCount:built.entryCount,
2307
2414
  excludedModules:built.excludedModules,
@@ -2315,6 +2422,8 @@ async function generateImportMapUnlocked({
2315
2422
  appId,
2316
2423
  artifactPath,
2317
2424
  entryPath,
2425
+ documentPaths,
2426
+ documentCount:receipt.documentCount,
2318
2427
  entryCount:receipt.entryCount,
2319
2428
  cleanupWarnings:receipt.cleanupWarnings,
2320
2429
  committed:true