arcane-os 0.3.1 → 0.3.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 +14 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +855 -895
- package/browser-runtime/ai/browser-speech-providers.mjs +80 -204
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -148
- package/browser-runtime/ai/speech-worker-runtime.mjs +642 -374
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1042 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +618 -359
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +27 -67
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +109 -1558
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -185
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1295
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -719
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2965
- package/docs/reference/sdk-api.md +0 -6698
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
package/bin/arcane-test.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {spawn} from 'node:child_process';
|
|
3
|
-
import {randomUUID} from 'node:crypto';
|
|
4
|
-
import {writeSync} from 'node:fs';
|
|
5
3
|
import {lstat,readdir,realpath} from 'node:fs/promises';
|
|
6
4
|
import {register} from 'node:module';
|
|
7
5
|
import path from 'node:path';
|
|
@@ -14,17 +12,97 @@ const TEST_FILE_PATTERN=/\.test\.(?:mjs|cjs|js)$/u;
|
|
|
14
12
|
const CANCELLATION_FORCE_WAIT_MS=1500;
|
|
15
13
|
const WATCHDOG_REPORT_GRACE_MS=500;
|
|
16
14
|
const FILE_HEARTBEAT_MS=30_000;
|
|
17
|
-
const OUTPUT_FLUSH_TIMEOUT_MS=5_000;
|
|
18
15
|
const BETWEEN_PHASE_TIMEOUT_MS=30_000;
|
|
19
|
-
const TREE_DRAIN_DIAGNOSTIC_BYTES=4_096;
|
|
20
16
|
const IPC_PROTOCOL=1;
|
|
21
|
-
const
|
|
17
|
+
const MANAGED_IMPORT_MAP_ENV='ARCANE_TEST_IMPORT_MAP_CONTEXT';
|
|
18
|
+
const MANAGED_IMPORT_MAP_PROTOCOL='arcane-test-import-map/1';
|
|
19
|
+
const APPLICATION_MAP_BOUNDARIES=new Set(['source','dist','test']);
|
|
22
20
|
const ISOLATED_MODE=process.argv[2]===SINGLE_FILE_ARGUMENT;
|
|
23
|
-
const
|
|
21
|
+
const MANAGED_IMPORT_MAP_SOURCE=process.env[MANAGED_IMPORT_MAP_ENV];
|
|
22
|
+
const ISOLATED_MANAGED_IMPORT_MAP=ISOLATED_MODE
|
|
23
|
+
&&MANAGED_IMPORT_MAP_SOURCE!==''
|
|
24
|
+
?MANAGED_IMPORT_MAP_SOURCE
|
|
25
|
+
:undefined;
|
|
26
|
+
const COORDINATOR_MANAGED_IMPORT_MAP=!ISOLATED_MODE
|
|
27
|
+
&&MANAGED_IMPORT_MAP_SOURCE!==''
|
|
28
|
+
?MANAGED_IMPORT_MAP_SOURCE
|
|
29
|
+
:undefined;
|
|
24
30
|
const ISOLATED_SEND=ISOLATED_MODE&&typeof process.send==='function'
|
|
25
31
|
?process.send.bind(process)
|
|
26
32
|
:null;
|
|
27
|
-
|
|
33
|
+
delete process.env[MANAGED_IMPORT_MAP_ENV];
|
|
34
|
+
|
|
35
|
+
function importMapFailure(message,code='ARCANE_IMPORT_MAP_INVALID'){
|
|
36
|
+
const error=new Error(message);
|
|
37
|
+
error.code=code;
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function samePath(left,right){
|
|
42
|
+
const a=path.resolve(left);
|
|
43
|
+
const b=path.resolve(right);
|
|
44
|
+
return process.platform==='win32'?a.toLowerCase()===b.toLowerCase():a===b;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function pathInside(root,candidate){
|
|
48
|
+
const relative=path.relative(root,candidate);
|
|
49
|
+
return relative===''||(!relative.startsWith(`..${path.sep}`)&&relative!=='..'&&!path.isAbsolute(relative));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function readManagedImportMapContext(source,signal,testFile){
|
|
53
|
+
if(source===undefined)return null;
|
|
54
|
+
throwIfCancelled(signal);
|
|
55
|
+
let context;
|
|
56
|
+
try{context=JSON.parse(source);}
|
|
57
|
+
catch(error){importMapFailure(`Managed import-map context is not valid JSON: ${error.message}`);}
|
|
58
|
+
if(context===null||typeof context!=='object'||Array.isArray(context)
|
|
59
|
+
||context.protocol!==MANAGED_IMPORT_MAP_PROTOCOL
|
|
60
|
+
||!APPLICATION_MAP_BOUNDARIES.has(context.boundary)
|
|
61
|
+
||typeof context.baseURL!=='string'
|
|
62
|
+
||context.imports===null||typeof context.imports!=='object'
|
|
63
|
+
||Array.isArray(context.imports)){
|
|
64
|
+
importMapFailure('Managed import-map context is malformed.');
|
|
65
|
+
}
|
|
66
|
+
let suppliedBase;
|
|
67
|
+
try{
|
|
68
|
+
const parsed=new URL(context.baseURL);
|
|
69
|
+
if(parsed.protocol!=='file:'||parsed.username||parsed.password||parsed.search||parsed.hash){
|
|
70
|
+
importMapFailure('Managed import-map base URL must be a physical application directory.');
|
|
71
|
+
}
|
|
72
|
+
suppliedBase=path.resolve(fileURLToPath(parsed));
|
|
73
|
+
}catch(error){
|
|
74
|
+
if(error?.code)throw error;
|
|
75
|
+
importMapFailure(`Managed import-map base URL is invalid: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
const testCodeRoot=await physicalTestCodeRoot(testFile);
|
|
78
|
+
const applicationRoot=path.dirname(testCodeRoot);
|
|
79
|
+
const expectedBase=context.boundary==='source'
|
|
80
|
+
?applicationRoot
|
|
81
|
+
:context.boundary==='test'
|
|
82
|
+
?testCodeRoot
|
|
83
|
+
:path.join(applicationRoot,'dist');
|
|
84
|
+
let baseInfo;
|
|
85
|
+
let canonicalBase;
|
|
86
|
+
try{
|
|
87
|
+
baseInfo=await lstat(expectedBase);
|
|
88
|
+
canonicalBase=await realpath(expectedBase);
|
|
89
|
+
}catch(error){
|
|
90
|
+
importMapFailure(`Selected ${context.boundary} application directory is unavailable: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
if(baseInfo.isSymbolicLink()||!baseInfo.isDirectory()
|
|
93
|
+
||!samePath(expectedBase,canonicalBase)||!samePath(suppliedBase,canonicalBase)){
|
|
94
|
+
importMapFailure(
|
|
95
|
+
`Managed import-map base URL must select this application's physical ${context.boundary} directory.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
throwIfCancelled(signal);
|
|
99
|
+
return {
|
|
100
|
+
protocol:MANAGED_IMPORT_MAP_PROTOCOL,
|
|
101
|
+
boundary:context.boundary,
|
|
102
|
+
baseURL:pathToFileURL(`${canonicalBase}${path.sep}`).href,
|
|
103
|
+
imports:{...context.imports}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
28
106
|
|
|
29
107
|
function comparePaths(left,right){
|
|
30
108
|
return left.localeCompare(right,'en');
|
|
@@ -36,8 +114,37 @@ function throwIfCancelled(signal){
|
|
|
36
114
|
}
|
|
37
115
|
}
|
|
38
116
|
|
|
39
|
-
|
|
117
|
+
function enclosingTestCodeRoot(candidate){
|
|
118
|
+
let current=path.resolve(candidate);
|
|
119
|
+
while(true){
|
|
120
|
+
if(path.basename(current).toLowerCase()==='test')return current;
|
|
121
|
+
const parent=path.dirname(current);
|
|
122
|
+
if(samePath(parent,current))return null;
|
|
123
|
+
current=parent;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function physicalTestCodeRoot(candidate){
|
|
128
|
+
const selected=enclosingTestCodeRoot(candidate);
|
|
129
|
+
if(selected===null){
|
|
130
|
+
throw new Error(`Selected test path is not inside an application test-code directory: ${candidate}`);
|
|
131
|
+
}
|
|
132
|
+
const info=await lstat(selected);
|
|
133
|
+
const canonical=await realpath(selected);
|
|
134
|
+
if(info.isSymbolicLink()||!info.isDirectory()||!samePath(selected,canonical)){
|
|
135
|
+
throw new Error(`Application test-code directory must be one physical directory: ${selected}`);
|
|
136
|
+
}
|
|
137
|
+
return canonical;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function collectDirectory(root,testCodeRoot,files,signal){
|
|
40
141
|
throwIfCancelled(signal);
|
|
142
|
+
const rootInfo=await lstat(root);
|
|
143
|
+
const canonicalRoot=await realpath(root);
|
|
144
|
+
if(rootInfo.isSymbolicLink()||!rootInfo.isDirectory()||!samePath(root,canonicalRoot)
|
|
145
|
+
||!pathInside(testCodeRoot,canonicalRoot)){
|
|
146
|
+
throw new Error(`Test discovery must stay inside its physical test-code directory: ${root}`);
|
|
147
|
+
}
|
|
41
148
|
const entries=await readdir(root,{withFileTypes:true});
|
|
42
149
|
throwIfCancelled(signal);
|
|
43
150
|
for(const entry of entries.sort((left,right)=>comparePaths(left.name,right.name))){
|
|
@@ -47,9 +154,13 @@ async function collectDirectory(root,files,signal){
|
|
|
47
154
|
throw new Error(`Test discovery refuses symbolic links: ${candidate}`);
|
|
48
155
|
}
|
|
49
156
|
if(entry.isDirectory()){
|
|
50
|
-
await collectDirectory(candidate,files,signal);
|
|
157
|
+
await collectDirectory(candidate,testCodeRoot,files,signal);
|
|
51
158
|
}else if(entry.isFile()&&TEST_FILE_PATTERN.test(entry.name)){
|
|
52
|
-
|
|
159
|
+
const canonical=await realpath(candidate);
|
|
160
|
+
if(!samePath(candidate,canonical)||!pathInside(testCodeRoot,canonical)){
|
|
161
|
+
throw new Error(`Selected test file leaves its physical test-code directory: ${candidate}`);
|
|
162
|
+
}
|
|
163
|
+
files.add(canonical);
|
|
53
164
|
}
|
|
54
165
|
}
|
|
55
166
|
}
|
|
@@ -72,10 +183,18 @@ async function collectTestFiles(arguments_,signal){
|
|
|
72
183
|
if(info.isSymbolicLink()){
|
|
73
184
|
throw new Error(`Test paths must not be symbolic links: ${candidate}`);
|
|
74
185
|
}
|
|
186
|
+
const testCodeRoot=await physicalTestCodeRoot(candidate);
|
|
187
|
+
if(!pathInside(testCodeRoot,candidate)){
|
|
188
|
+
throw new Error(`Selected test path leaves its application test-code directory: ${candidate}`);
|
|
189
|
+
}
|
|
75
190
|
if(info.isDirectory()){
|
|
76
|
-
await collectDirectory(candidate,files,signal);
|
|
191
|
+
await collectDirectory(candidate,testCodeRoot,files,signal);
|
|
77
192
|
}else if(info.isFile()&&TEST_FILE_PATTERN.test(path.basename(candidate))){
|
|
78
|
-
|
|
193
|
+
const canonical=await realpath(candidate);
|
|
194
|
+
if(!samePath(candidate,canonical)||!pathInside(testCodeRoot,canonical)){
|
|
195
|
+
throw new Error(`Selected test file leaves its physical test-code directory: ${candidate}`);
|
|
196
|
+
}
|
|
197
|
+
files.add(canonical);
|
|
79
198
|
}else{
|
|
80
199
|
throw new Error(`Expected a .test.js, .test.cjs, or .test.mjs file: ${candidate}`);
|
|
81
200
|
}
|
|
@@ -88,14 +207,14 @@ function formatError(error){
|
|
|
88
207
|
}
|
|
89
208
|
|
|
90
209
|
function sendIsolatedMessage(message){
|
|
91
|
-
if(
|
|
210
|
+
if(ISOLATED_SEND===null){
|
|
92
211
|
return Promise.reject(
|
|
93
|
-
new Error('Internal single-file mode requires its
|
|
212
|
+
new Error('Internal single-file mode requires its coordinator channel.')
|
|
94
213
|
);
|
|
95
214
|
}
|
|
96
215
|
return new Promise((resolve,reject)=>{
|
|
97
216
|
ISOLATED_SEND(
|
|
98
|
-
{...message,protocol:IPC_PROTOCOL
|
|
217
|
+
{...message,protocol:IPC_PROTOCOL},
|
|
99
218
|
error=>error?reject(error):resolve()
|
|
100
219
|
);
|
|
101
220
|
});
|
|
@@ -209,12 +328,9 @@ function forceDrainWindowsTree(child){
|
|
|
209
328
|
let stdoutDiagnostic='';
|
|
210
329
|
let stderrDiagnostic='';
|
|
211
330
|
const deadline=performance.now()+CANCELLATION_FORCE_WAIT_MS;
|
|
212
|
-
const capture=(stream,chunk)
|
|
213
|
-
const value=`${stream}${chunk.toString()}`;
|
|
214
|
-
return value.slice(-TREE_DRAIN_DIAGNOSTIC_BYTES);
|
|
215
|
-
};
|
|
331
|
+
const capture=(stream,chunk)=>`${stream}${chunk.toString()}`;
|
|
216
332
|
const diagnostic=()=>[stdoutDiagnostic,stderrDiagnostic]
|
|
217
|
-
.
|
|
333
|
+
.filter(value=>value!=='').join('\n');
|
|
218
334
|
const reportFailure=reason=>{
|
|
219
335
|
const detail=diagnostic();
|
|
220
336
|
console.error(
|
|
@@ -321,7 +437,6 @@ function forceDrainProcessTree(child){
|
|
|
321
437
|
function runIsolatedFile(file,signal){
|
|
322
438
|
if(signal.aborted)return Promise.reject(signal.reason);
|
|
323
439
|
return new Promise((resolve,reject)=>{
|
|
324
|
-
const token=randomUUID();
|
|
325
440
|
let settled=false;
|
|
326
441
|
let escalation=null;
|
|
327
442
|
let forceSettlement=null;
|
|
@@ -353,7 +468,7 @@ function runIsolatedFile(file,signal){
|
|
|
353
468
|
if(!child?.connected)return;
|
|
354
469
|
try{
|
|
355
470
|
child.send(
|
|
356
|
-
{protocol:IPC_PROTOCOL,
|
|
471
|
+
{protocol:IPC_PROTOCOL,type:'abort',reason},
|
|
357
472
|
()=>{}
|
|
358
473
|
);
|
|
359
474
|
}catch{}
|
|
@@ -390,7 +505,7 @@ function runIsolatedFile(file,signal){
|
|
|
390
505
|
return;
|
|
391
506
|
}
|
|
392
507
|
console.error(
|
|
393
|
-
`[arcane-test] ${displayFile(file)} closed without
|
|
508
|
+
`[arcane-test] ${displayFile(file)} closed without a harness report.`
|
|
394
509
|
);
|
|
395
510
|
finish(resolve,{
|
|
396
511
|
code:2,
|
|
@@ -465,7 +580,6 @@ function runIsolatedFile(file,signal){
|
|
|
465
580
|
const onMessage=message=>{
|
|
466
581
|
if(
|
|
467
582
|
message?.protocol!==IPC_PROTOCOL
|
|
468
|
-
||message?.token!==token
|
|
469
583
|
||typeof message.type!=='string'
|
|
470
584
|
)return;
|
|
471
585
|
if(message.type==='phase'){
|
|
@@ -473,24 +587,28 @@ function runIsolatedFile(file,signal){
|
|
|
473
587
|
completion!==null
|
|
474
588
|
||watchdogFailure!==null
|
|
475
589
|
||typeof message.id!=='string'
|
|
476
|
-
||message.id===''
|
|
590
|
+
||message.id===''
|
|
477
591
|
)return;
|
|
478
592
|
if(message.status==='started'){
|
|
479
593
|
if(
|
|
480
594
|
typeof message.kind!=='string'
|
|
481
595
|
||typeof message.name!=='string'
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
596
|
+
||(message.timeoutMs!==null&&(
|
|
597
|
+
!Number.isSafeInteger(message.timeoutMs)
|
|
598
|
+
||message.timeoutMs<1
|
|
599
|
+
||message.timeoutMs>3_600_000
|
|
600
|
+
))
|
|
485
601
|
||phaseTimers.has(message.id)
|
|
486
602
|
)return;
|
|
487
603
|
clearTimeout(idleTimer);
|
|
488
|
-
const timer=
|
|
604
|
+
const timer=message.timeoutMs===null
|
|
605
|
+
?null
|
|
606
|
+
:setTimeout(()=>watchdog(message),message.timeoutMs);
|
|
489
607
|
phaseTimers.set(message.id,timer);
|
|
490
608
|
}else if(message.status==='completed'){
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
clearTimeout(timer);
|
|
609
|
+
if(phaseTimers.has(message.id)){
|
|
610
|
+
const timer=phaseTimers.get(message.id);
|
|
611
|
+
if(timer!==null)clearTimeout(timer);
|
|
494
612
|
phaseTimers.delete(message.id);
|
|
495
613
|
scheduleIdle();
|
|
496
614
|
}
|
|
@@ -513,7 +631,10 @@ function runIsolatedFile(file,signal){
|
|
|
513
631
|
};
|
|
514
632
|
try{
|
|
515
633
|
child=spawn(process.execPath,[RUNNER_PATH,SINGLE_FILE_ARGUMENT,file],{
|
|
516
|
-
env:{
|
|
634
|
+
env:{
|
|
635
|
+
...process.env,
|
|
636
|
+
[MANAGED_IMPORT_MAP_ENV]:COORDINATOR_MANAGED_IMPORT_MAP??''
|
|
637
|
+
},
|
|
517
638
|
shell:false,
|
|
518
639
|
windowsHide:true,
|
|
519
640
|
detached:process.platform!=='win32',
|
|
@@ -537,7 +658,17 @@ function runIsolatedFile(file,signal){
|
|
|
537
658
|
|
|
538
659
|
async function runSingleFile(file,signal){
|
|
539
660
|
console.log(`[arcane-test] IMPORT ${displayFile(file)}`);
|
|
540
|
-
|
|
661
|
+
const managedImportMap=await readManagedImportMapContext(
|
|
662
|
+
ISOLATED_MANAGED_IMPORT_MAP,
|
|
663
|
+
signal,
|
|
664
|
+
file
|
|
665
|
+
);
|
|
666
|
+
throwIfCancelled(signal);
|
|
667
|
+
register(new URL('../src/testing-loader.mjs',import.meta.url),{
|
|
668
|
+
parentURL:import.meta.url,
|
|
669
|
+
data:{managedImportMap}
|
|
670
|
+
});
|
|
671
|
+
throwIfCancelled(signal);
|
|
541
672
|
const importPhase={
|
|
542
673
|
id:'module-import',
|
|
543
674
|
kind:'import',
|
|
@@ -551,6 +682,7 @@ async function runSingleFile(file,signal){
|
|
|
551
682
|
let importError=null;
|
|
552
683
|
try{
|
|
553
684
|
try{
|
|
685
|
+
throwIfCancelled(signal);
|
|
554
686
|
await import(pathToFileURL(file).href);
|
|
555
687
|
}catch(error){
|
|
556
688
|
importError=error;
|
|
@@ -617,12 +749,10 @@ function flushStream(stream){
|
|
|
617
749
|
const finish=flushed=>{
|
|
618
750
|
if(settled)return;
|
|
619
751
|
settled=true;
|
|
620
|
-
clearTimeout(timeout);
|
|
621
752
|
stream.removeListener('error',failed);
|
|
622
753
|
resolve(flushed);
|
|
623
754
|
};
|
|
624
755
|
const failed=()=>finish(false);
|
|
625
|
-
const timeout=setTimeout(failed,OUTPUT_FLUSH_TIMEOUT_MS);
|
|
626
756
|
stream.once('error',failed);
|
|
627
757
|
try{
|
|
628
758
|
stream.write('',()=>finish(true));
|
|
@@ -635,12 +765,7 @@ function flushStream(stream){
|
|
|
635
765
|
async function flushOutput(){
|
|
636
766
|
const flushed=await Promise.all([flushStream(process.stdout),flushStream(process.stderr)]);
|
|
637
767
|
if(flushed.every(Boolean))return;
|
|
638
|
-
|
|
639
|
-
writeSync(
|
|
640
|
-
2,
|
|
641
|
-
'[arcane-test] Output flush exceeded its bounded drain; diagnostics may be truncated.\n'
|
|
642
|
-
);
|
|
643
|
-
}catch{}
|
|
768
|
+
throw new Error('One or more test output streams failed before reporting all diagnostics.');
|
|
644
769
|
}
|
|
645
770
|
|
|
646
771
|
async function main(){
|
|
@@ -656,7 +781,6 @@ async function main(){
|
|
|
656
781
|
const parentAbort=message=>{
|
|
657
782
|
if(
|
|
658
783
|
message?.protocol!==IPC_PROTOCOL
|
|
659
|
-
||message?.token!==ISOLATED_TOKEN
|
|
660
784
|
||message?.type!=='abort'
|
|
661
785
|
)return;
|
|
662
786
|
const error=new Error(
|
|
@@ -673,9 +797,9 @@ async function main(){
|
|
|
673
797
|
if(process.argv.length!==4){
|
|
674
798
|
throw new Error(`${SINGLE_FILE_ARGUMENT} requires one exact test file.`);
|
|
675
799
|
}
|
|
676
|
-
if(
|
|
800
|
+
if(ISOLATED_SEND===null){
|
|
677
801
|
throw new Error(
|
|
678
|
-
'Internal single-file mode requires its
|
|
802
|
+
'Internal single-file mode requires its coordinator channel.'
|
|
679
803
|
);
|
|
680
804
|
}
|
|
681
805
|
interceptIsolatedExit();
|
|
@@ -718,7 +842,7 @@ if(ISOLATED_MODE){
|
|
|
718
842
|
kind:'flush',
|
|
719
843
|
name:'test report output flush',
|
|
720
844
|
status:'started',
|
|
721
|
-
timeoutMs:
|
|
845
|
+
timeoutMs:null,
|
|
722
846
|
type:'phase'
|
|
723
847
|
};
|
|
724
848
|
await sendIsolatedMessage(flushPhase);
|