arcane-os 0.1.0-dev.5

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 (248) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/COMMERCIAL-LICENSE.md +13 -0
  3. package/LICENSE +661 -0
  4. package/NOTICE +70 -0
  5. package/README.md +448 -0
  6. package/bin/arcane-test.mjs +741 -0
  7. package/bin/arcane.mjs +5 -0
  8. package/docs/architecture.md +234 -0
  9. package/docs/compatibility.md +36 -0
  10. package/docs/event-manager.md +166 -0
  11. package/docs/platform-targets.md +108 -0
  12. package/docs/publishing.md +203 -0
  13. package/docs/reference/README.md +117 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +123 -0
  16. package/docs/reference/behavioral-testing.md +86 -0
  17. package/docs/reference/cli.md +569 -0
  18. package/docs/reference/core/README.md +62 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +873 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +65 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +957 -0
  32. package/docs/reference/inventory/package-api.json +2632 -0
  33. package/docs/reference/inventory/runtime-components.json +934 -0
  34. package/docs/reference/inventory/runtime-entities.json +26 -0
  35. package/docs/reference/inventory/runtime-modules.json +1249 -0
  36. package/docs/reference/protocols.md +242 -0
  37. package/docs/reference/runtime-components.md +1098 -0
  38. package/docs/reference/runtime-entities.md +303 -0
  39. package/docs/reference/runtime-modules.md +2010 -0
  40. package/docs/reference/sdk-api.md +4901 -0
  41. package/docs/roadmap.md +79 -0
  42. package/docs/work-amplification.md +124 -0
  43. package/node_modules/event-pubsub/CHANGELOG.md +55 -0
  44. package/node_modules/event-pubsub/MIGRATION.md +70 -0
  45. package/node_modules/event-pubsub/README.md +363 -0
  46. package/node_modules/event-pubsub/SECURITY.md +37 -0
  47. package/node_modules/event-pubsub/index.js +141 -0
  48. package/node_modules/event-pubsub/licence +21 -0
  49. package/node_modules/event-pubsub/package.json +59 -0
  50. package/node_modules/strong-type/README.md +408 -0
  51. package/node_modules/strong-type/assets/strong-type-header.png +0 -0
  52. package/node_modules/strong-type/index.js +1151 -0
  53. package/node_modules/strong-type/licence +21 -0
  54. package/node_modules/strong-type/node.js +125 -0
  55. package/node_modules/strong-type/package.json +61 -0
  56. package/package.json +95 -0
  57. package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
  58. package/runtime/arcane/components/app-bar.html +468 -0
  59. package/runtime/arcane/components/assistant-panel.html +715 -0
  60. package/runtime/arcane/components/calculator.html +8 -0
  61. package/runtime/arcane/components/chart.html +655 -0
  62. package/runtime/arcane/components/chat.html +1225 -0
  63. package/runtime/arcane/components/conversation-view.html +13 -0
  64. package/runtime/arcane/components/dashboard-config.html +341 -0
  65. package/runtime/arcane/components/data-maintenance.html +112 -0
  66. package/runtime/arcane/components/data-view.html +92 -0
  67. package/runtime/arcane/components/directory-picker.html +197 -0
  68. package/runtime/arcane/components/document-inspector.html +252 -0
  69. package/runtime/arcane/components/file-drop.html +264 -0
  70. package/runtime/arcane/components/file-inspector.html +293 -0
  71. package/runtime/arcane/components/file-manager.html +1715 -0
  72. package/runtime/arcane/components/header.html +142 -0
  73. package/runtime/arcane/components/integration-settings.html +14 -0
  74. package/runtime/arcane/components/local-ai-status.html +360 -0
  75. package/runtime/arcane/components/markdown-document.html +1048 -0
  76. package/runtime/arcane/components/markdown-editor.html +360 -0
  77. package/runtime/arcane/components/media-embed.html +8 -0
  78. package/runtime/arcane/components/modal.html +402 -0
  79. package/runtime/arcane/components/output-panel.html +259 -0
  80. package/runtime/arcane/components/preferences-form.html +135 -0
  81. package/runtime/arcane/components/record-timeline.html +105 -0
  82. package/runtime/arcane/components/relationship-board.html +116 -0
  83. package/runtime/arcane/components/screen-capture.html +8 -0
  84. package/runtime/arcane/components/source-code-viewer.html +441 -0
  85. package/runtime/arcane/components/source-explanation.html +124 -0
  86. package/runtime/arcane/components/speech.html +365 -0
  87. package/runtime/arcane/components/summary-strip.html +177 -0
  88. package/runtime/arcane/components/table.html +77 -0
  89. package/runtime/arcane/components/task-progress.html +282 -0
  90. package/runtime/arcane/components/terminal-workspace.html +65 -0
  91. package/runtime/arcane/components/theme-editor.html +41 -0
  92. package/runtime/arcane/components/theme-switcher.html +46 -0
  93. package/runtime/arcane/components/unified-inbox.html +20 -0
  94. package/runtime/arcane/components/voice-transcription.html +476 -0
  95. package/runtime/arcane/components/weather-widget.html +8 -0
  96. package/runtime/arcane/components/web-navigator.html +239 -0
  97. package/runtime/arcane/css/communications.css +1 -0
  98. package/runtime/arcane/css/dashboard-config.css +45 -0
  99. package/runtime/arcane/css/document-site.css +981 -0
  100. package/runtime/arcane/css/layout.css +438 -0
  101. package/runtime/arcane/css/primitives.css +321 -0
  102. package/runtime/arcane/css/theme.css +112 -0
  103. package/runtime/arcane/css/utility-workspace.css +1 -0
  104. package/runtime/arcane/entities/ApiModelRecord.js +20 -0
  105. package/runtime/arcane/entities/Calculation.js +13 -0
  106. package/runtime/arcane/entities/Chat.js +581 -0
  107. package/runtime/arcane/entities/CommunicationMessage.js +29 -0
  108. package/runtime/arcane/entities/CommunicationThread.js +21 -0
  109. package/runtime/arcane/entities/Document.js +10 -0
  110. package/runtime/arcane/entities/File.js +143 -0
  111. package/runtime/arcane/entities/Image.js +135 -0
  112. package/runtime/arcane/entities/IntentEnvelope.js +834 -0
  113. package/runtime/arcane/entities/Preference.js +83 -0
  114. package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
  115. package/runtime/arcane/entities/TerminalSession.js +46 -0
  116. package/runtime/arcane/entities/Theme.js +107 -0
  117. package/runtime/arcane/entities/User.js +1046 -0
  118. package/runtime/arcane/entities/Weather.js +23 -0
  119. package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
  120. package/runtime/arcane/img/arrow-left.png +0 -0
  121. package/runtime/arcane/img/arrow-right.png +0 -0
  122. package/runtime/arcane/img/doc.svg +5 -0
  123. package/runtime/arcane/img/folder.svg +4 -0
  124. package/runtime/arcane/img/image.svg +5 -0
  125. package/runtime/arcane/img/refresh.png +0 -0
  126. package/runtime/arcane/img/send.svg +9 -0
  127. package/runtime/arcane/img/trash.svg +5 -0
  128. package/runtime/arcane/img/upload.svg +5 -0
  129. package/runtime/arcane/modules/AI.js +2048 -0
  130. package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
  131. package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
  132. package/runtime/arcane/modules/AIResponseLength.js +42 -0
  133. package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
  134. package/runtime/arcane/modules/AnsiText.js +53 -0
  135. package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
  136. package/runtime/arcane/modules/AppDataScope.js +245 -0
  137. package/runtime/arcane/modules/AppearancePreferences.js +28 -0
  138. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
  139. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
  140. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
  141. package/runtime/arcane/modules/AsyncBoundary.js +161 -0
  142. package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
  143. package/runtime/arcane/modules/CalculatorEngine.js +20 -0
  144. package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
  145. package/runtime/arcane/modules/ChartLibrary.js +35 -0
  146. package/runtime/arcane/modules/ChatRecords.js +13 -0
  147. package/runtime/arcane/modules/CommunicationAppController.js +43 -0
  148. package/runtime/arcane/modules/CommunicationHub.js +16 -0
  149. package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
  150. package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
  151. package/runtime/arcane/modules/ComponentContracts.js +586 -0
  152. package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
  153. package/runtime/arcane/modules/ConversationActionItems.js +488 -0
  154. package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
  155. package/runtime/arcane/modules/ConversationTimebox.js +527 -0
  156. package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
  157. package/runtime/arcane/modules/DBLS.js +171 -0
  158. package/runtime/arcane/modules/DBOPFS.js +1154 -0
  159. package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
  160. package/runtime/arcane/modules/DataMaintenance.js +91 -0
  161. package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
  162. package/runtime/arcane/modules/DirectoryPicker.js +109 -0
  163. package/runtime/arcane/modules/DocumentNavigation.js +223 -0
  164. package/runtime/arcane/modules/Errors.js +1025 -0
  165. package/runtime/arcane/modules/GifEncoder.js +29 -0
  166. package/runtime/arcane/modules/HTMLImport.js +114 -0
  167. package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
  168. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
  169. package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
  170. package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
  171. package/runtime/arcane/modules/MD.js +111 -0
  172. package/runtime/arcane/modules/Mail.js +352 -0
  173. package/runtime/arcane/modules/MailTransport.mjs +180 -0
  174. package/runtime/arcane/modules/Marked.min.js +71 -0
  175. package/runtime/arcane/modules/MemoryRecords.js +44 -0
  176. package/runtime/arcane/modules/MessageAdvisory.js +38 -0
  177. package/runtime/arcane/modules/ModelDefinition.js +189 -0
  178. package/runtime/arcane/modules/Ollama.js +74 -0
  179. package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
  180. package/runtime/arcane/modules/OllamaSettings.js +24 -0
  181. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
  182. package/runtime/arcane/modules/PreferenceStore.js +109 -0
  183. package/runtime/arcane/modules/QRCode.min.js +1 -0
  184. package/runtime/arcane/modules/Questionnaire.js +61 -0
  185. package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
  186. package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
  187. package/runtime/arcane/modules/RecordReviewStore.js +94 -0
  188. package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
  189. package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
  190. package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
  191. package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
  192. package/runtime/arcane/modules/ScreenCapture.js +20 -0
  193. package/runtime/arcane/modules/SpeechPlayback.js +581 -0
  194. package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
  195. package/runtime/arcane/modules/SystemAppearance.js +20 -0
  196. package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
  197. package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
  198. package/runtime/arcane/modules/TerminalClient.js +52 -0
  199. package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
  200. package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
  201. package/runtime/arcane/modules/ThemeManager.js +131 -0
  202. package/runtime/arcane/modules/TimeGuard.js +149 -0
  203. package/runtime/arcane/modules/ToolCallRouter.js +83 -0
  204. package/runtime/arcane/modules/WaitForComponent.js +102 -0
  205. package/runtime/arcane/modules/YouTubeMedia.js +16 -0
  206. package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
  207. package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
  208. package/runtime/arcane/modules/uPlot.min.css +1 -0
  209. package/runtime/arcane/security/arcane-network-policy.json +6 -0
  210. package/runtime/strong-type/index.js +352 -0
  211. package/runtime/strong-type/licence +21 -0
  212. package/runtime/strong-type/package.json +45 -0
  213. package/schemas/arcane-app-bundle.schema.json +125 -0
  214. package/schemas/arcane-app.schema.json +329 -0
  215. package/schemas/arcane-lock.schema.json +86 -0
  216. package/schemas/arcane-package.schema.json +224 -0
  217. package/schemas/cli-event.schema.json +122 -0
  218. package/schemas/event-stack.schema.json +152 -0
  219. package/schemas/native-build-plan.schema.json +176 -0
  220. package/schemas/target-adapter.schema.json +117 -0
  221. package/src/app-descriptor.mjs +500 -0
  222. package/src/cli/main.mjs +561 -0
  223. package/src/constants.mjs +31 -0
  224. package/src/dev-server.mjs +718 -0
  225. package/src/doctor.mjs +315 -0
  226. package/src/dom-event-instrumentation.mjs +594 -0
  227. package/src/errors.mjs +75 -0
  228. package/src/event-manager.mjs +1342 -0
  229. package/src/event-queue.mjs +138 -0
  230. package/src/events.mjs +219 -0
  231. package/src/index.mjs +177 -0
  232. package/src/integrated-provider-loader.mjs +432 -0
  233. package/src/native-plan.mjs +698 -0
  234. package/src/native-provider-loader.mjs +1126 -0
  235. package/src/packager/core.mjs +2691 -0
  236. package/src/process.mjs +353 -0
  237. package/src/release-bundle.mjs +2523 -0
  238. package/src/repository.mjs +90 -0
  239. package/src/runtime.mjs +452 -0
  240. package/src/scaffold.mjs +380 -0
  241. package/src/targets/index.mjs +436 -0
  242. package/src/templates/assets/app-icon.png +0 -0
  243. package/src/templates/workspace-template.mjs +388 -0
  244. package/src/testing-loader.mjs +9 -0
  245. package/src/testing.mjs +427 -0
  246. package/src/toolchain.mjs +1335 -0
  247. package/src/update-check.mjs +307 -0
  248. package/src/workspace.mjs +449 -0
@@ -0,0 +1,427 @@
1
+ import VanillaTest from 'vanilla-test';
2
+
3
+ export const DEFAULT_TEST_TIMEOUT_MS=30_000;
4
+
5
+ const MAX_TEST_TIMEOUT_MS=3_600_000;
6
+ const CLEANUP_TIMEOUT_MS=5_000;
7
+ const REPORT_TIMEOUT_MS=5_000;
8
+ const registrations=[];
9
+ let lifecycle='collecting';
10
+ let phaseSequence=0;
11
+
12
+ class TestTimeoutError extends Error{
13
+ constructor(name,timeoutMs,{cleanup=false}={}){
14
+ super(`${cleanup?'Cleanup for':'Test'} "${name}" exceeded ${String(timeoutMs)} ms.`);
15
+ this.name='TestTimeoutError';
16
+ this.code='ARCANE_TEST_TIMEOUT';
17
+ }
18
+ }
19
+
20
+ class TestCancellationError extends Error{
21
+ constructor(reason){
22
+ super('The test run was cancelled.',{cause:reason});
23
+ this.name='AbortError';
24
+ this.code='ARCANE_TEST_CANCELLED';
25
+ }
26
+ }
27
+
28
+ function assertName(name){
29
+ if(typeof name!=='string'||name.trim()===''){
30
+ throw new TypeError('A test name must be a nonempty string.');
31
+ }
32
+ }
33
+
34
+ function testTimeout(options){
35
+ const timeout=options.timeout??DEFAULT_TEST_TIMEOUT_MS;
36
+ if(!Number.isSafeInteger(timeout)||timeout<1||timeout>MAX_TEST_TIMEOUT_MS){
37
+ throw new TypeError(
38
+ `Test timeout must be an integer from 1 through ${String(MAX_TEST_TIMEOUT_MS)} milliseconds.`
39
+ );
40
+ }
41
+ return timeout;
42
+ }
43
+
44
+ function definition(name,optionsOrCallback,maybeCallback){
45
+ assertName(name);
46
+ const hasOptions=typeof optionsOrCallback!=='function';
47
+ const options=hasOptions?optionsOrCallback:{};
48
+ const callback=hasOptions?maybeCallback:optionsOrCallback;
49
+ if(options===null||typeof options!=='object'||Array.isArray(options)){
50
+ throw new TypeError('Test options must be an object.');
51
+ }
52
+ if(typeof callback!=='function'){
53
+ throw new TypeError(`Test "${name}" requires a callback function.`);
54
+ }
55
+ return Object.freeze({name,timeoutMs:testTimeout(options),callback});
56
+ }
57
+
58
+ export function test(name,optionsOrCallback,maybeCallback){
59
+ if(lifecycle!=='collecting'){
60
+ throw new ReferenceError('Tests must be registered before the test run starts.');
61
+ }
62
+ registrations.push(definition(name,optionsOrCallback,maybeCallback));
63
+ }
64
+
65
+ export default test;
66
+
67
+ export function registeredTestCount(){
68
+ return registrations.length;
69
+ }
70
+
71
+ function cancellationError(signal){
72
+ return new TestCancellationError(signal?.reason);
73
+ }
74
+
75
+ function waitFor(promise,{timeoutMs,signal,timeoutError,onTimeout}={}){
76
+ if(signal?.aborted){
77
+ return Promise.reject(cancellationError(signal));
78
+ }
79
+ return new Promise((resolve,reject)=>{
80
+ let settled=false;
81
+ let timer=null;
82
+ const finish=(callback,value)=>{
83
+ if(settled)return;
84
+ settled=true;
85
+ clearTimeout(timer);
86
+ signal?.removeEventListener('abort',abort);
87
+ callback(value);
88
+ };
89
+ const abort=()=>finish(reject,cancellationError(signal));
90
+ signal?.addEventListener('abort',abort,{once:true});
91
+ timer=setTimeout(()=>{
92
+ const error=timeoutError();
93
+ finish(reject,error);
94
+ onTimeout?.(error);
95
+ },timeoutMs);
96
+ Promise.resolve(promise).then(
97
+ value=>finish(resolve,value),
98
+ error=>finish(reject,error)
99
+ );
100
+ });
101
+ }
102
+
103
+ function linkedController(parentSignal){
104
+ const controller=new AbortController();
105
+ const abort=()=>controller.abort(parentSignal?.reason);
106
+ parentSignal?.addEventListener('abort',abort,{once:true});
107
+ if(parentSignal?.aborted)abort();
108
+ return {
109
+ controller,
110
+ dispose:()=>parentSignal?.removeEventListener('abort',abort)
111
+ };
112
+ }
113
+
114
+ function runState(parentSignal){
115
+ const controller=new AbortController();
116
+ let cancelled=false;
117
+ let fatalError=null;
118
+ const cancel=()=>{
119
+ cancelled=true;
120
+ if(!controller.signal.aborted){
121
+ controller.abort(parentSignal?.reason);
122
+ }
123
+ };
124
+ parentSignal?.addEventListener('abort',cancel,{once:true});
125
+ if(parentSignal?.aborted)cancel();
126
+ return {
127
+ signal:controller.signal,
128
+ get cancelled(){
129
+ return cancelled;
130
+ },
131
+ get fatalError(){
132
+ return fatalError;
133
+ },
134
+ markFatal(error){
135
+ fatalError??=error;
136
+ if(!controller.signal.aborted){
137
+ controller.abort(fatalError);
138
+ }
139
+ },
140
+ dispose:()=>parentSignal?.removeEventListener('abort',cancel)
141
+ };
142
+ }
143
+
144
+ function displayName(path){
145
+ return path.join(' > ');
146
+ }
147
+
148
+ function errorText(error){
149
+ if(error instanceof Error){
150
+ return error.stack??`${error.name}: ${error.message}`;
151
+ }
152
+ return String(error);
153
+ }
154
+
155
+ async function ownedPhase(onPhase,{kind,name,timeoutMs,cleanup=false},work){
156
+ const id=`${kind}-${String(++phaseSequence)}`;
157
+ await onPhase?.({id,kind,name,status:'started',timeoutMs});
158
+ const started=performance.now();
159
+ let value;
160
+ let workError=null;
161
+ try{
162
+ value=await work();
163
+ }catch(error){
164
+ workError=error;
165
+ }
166
+ const elapsedMs=performance.now()-started;
167
+ let phaseError=null;
168
+ try{
169
+ await onPhase?.({id,kind,name,status:'completed',timeoutMs,elapsedMs});
170
+ }catch(error){
171
+ phaseError=error;
172
+ }
173
+ if(elapsedMs>timeoutMs){
174
+ const timeoutError=new TestTimeoutError(name,timeoutMs,{cleanup});
175
+ if(workError!==null)timeoutError.cause=workError;
176
+ throw timeoutError;
177
+ }
178
+ if(workError!==null)throw workError;
179
+ if(phaseError!==null)throw phaseError;
180
+ return value;
181
+ }
182
+
183
+ async function runCleanup(cleanup,context,name,signal,onPhase,index){
184
+ return waitFor(
185
+ ownedPhase(
186
+ onPhase,
187
+ {
188
+ kind:'cleanup',
189
+ name:`${name} cleanup ${String(index+1)}`,
190
+ timeoutMs:CLEANUP_TIMEOUT_MS,
191
+ cleanup:true
192
+ },
193
+ ()=>cleanup(context)
194
+ ),
195
+ {
196
+ timeoutMs:CLEANUP_TIMEOUT_MS,
197
+ signal,
198
+ timeoutError:()=>new TestTimeoutError(name,CLEANUP_TIMEOUT_MS,{cleanup:true})
199
+ }
200
+ );
201
+ }
202
+
203
+ async function executeDefinition(current,path,outcomes,{parentSignal,state,onPhase}){
204
+ if(state.cancelled)throw cancellationError(state.signal);
205
+ if(state.fatalError)throw state.fatalError;
206
+ if(parentSignal?.aborted)throw cancellationError(parentSignal);
207
+
208
+ const outcome={name:displayName(path),ok:false,errors:[]};
209
+ outcomes.push(outcome);
210
+
211
+ const {controller,dispose}=linkedController(parentSignal);
212
+ const cleanups=[];
213
+ let accepting=true;
214
+ let childChain=Promise.resolve();
215
+ let childFailure=false;
216
+ let childFatal=null;
217
+
218
+ const context={
219
+ get signal(){
220
+ return controller.signal;
221
+ },
222
+ after(callback){
223
+ if(!accepting){
224
+ throw new ReferenceError(`Test "${outcome.name}" is no longer accepting cleanup hooks.`);
225
+ }
226
+ if(state.cancelled||state.fatalError||controller.signal.aborted){
227
+ throw state.fatalError??cancellationError(controller.signal);
228
+ }
229
+ if(typeof callback!=='function'){
230
+ throw new TypeError('t.after() requires a callback function.');
231
+ }
232
+ cleanups.push(callback);
233
+ },
234
+ test(name,optionsOrCallback,maybeCallback){
235
+ if(!accepting){
236
+ return Promise.reject(
237
+ new ReferenceError(`Test "${outcome.name}" is no longer accepting nested tests.`)
238
+ );
239
+ }
240
+ if(state.cancelled||state.fatalError||controller.signal.aborted){
241
+ return Promise.reject(state.fatalError??cancellationError(controller.signal));
242
+ }
243
+ const child=definition(name,optionsOrCallback,maybeCallback);
244
+ const execution=childChain.then(()=>{
245
+ if(state.cancelled)throw cancellationError(state.signal);
246
+ if(state.fatalError)throw state.fatalError;
247
+ if(controller.signal.aborted)throw cancellationError(controller.signal);
248
+ if(childFatal)throw childFatal;
249
+ return executeDefinition(
250
+ child,
251
+ [...path,child.name],
252
+ outcomes,
253
+ {parentSignal:controller.signal,state,onPhase}
254
+ );
255
+ });
256
+ const result=execution.then(childOutcome=>{
257
+ if(!childOutcome.ok)childFailure=true;
258
+ return undefined;
259
+ });
260
+ childChain=result.catch(error=>{
261
+ childFatal??=error;
262
+ });
263
+ return result;
264
+ }
265
+ };
266
+
267
+ let bodyError=null;
268
+ try{
269
+ const body=ownedPhase(
270
+ onPhase,
271
+ {kind:'test',name:outcome.name,timeoutMs:current.timeoutMs},
272
+ async()=>{
273
+ if(state.cancelled)throw cancellationError(state.signal);
274
+ if(state.fatalError)throw state.fatalError;
275
+ if(controller.signal.aborted)throw cancellationError(controller.signal);
276
+ let callbackError=null;
277
+ try{
278
+ await current.callback(context);
279
+ }catch(error){
280
+ callbackError=error;
281
+ }finally{
282
+ // Once the callback settles it can no longer add work to the
283
+ // parent's owned child queue, even while existing children drain.
284
+ accepting=false;
285
+ }
286
+ await childChain;
287
+ if(callbackError)throw callbackError;
288
+ if(childFatal)throw childFatal;
289
+ }
290
+ );
291
+ await waitFor(body,{
292
+ timeoutMs:current.timeoutMs,
293
+ signal:controller.signal,
294
+ timeoutError:()=>new TestTimeoutError(outcome.name,current.timeoutMs),
295
+ onTimeout:error=>{
296
+ controller.abort(error);
297
+ state.markFatal(error);
298
+ }
299
+ });
300
+ }catch(error){
301
+ bodyError=error;
302
+ if(error?.code==='ARCANE_TEST_TIMEOUT')state.markFatal(error);
303
+ }finally{
304
+ accepting=false;
305
+ }
306
+
307
+ // The body wait can stop on a parent or child timeout before the body promise
308
+ // settles. Drain only the framework-owned child chain (whose callbacks all
309
+ // share the aborted signal) so every already-created outcome is final before
310
+ // the report snapshot is rendered. Never await the potentially live body.
311
+ await childChain;
312
+
313
+ const cleanupErrors=[];
314
+ // A timed-out or cancelled callback can still be executing JavaScript because
315
+ // promises are not preemptible. Cleanup is therefore safe only after an
316
+ // ordinarily settled body. The isolated file process is terminated after
317
+ // reporting a fatal timeout, so skipped hooks and late work cannot overlap a
318
+ // later test.
319
+ if(!state.fatalError&&!state.cancelled){
320
+ for(let index=0;index<cleanups.length;index+=1){
321
+ if(state.cancelled)break;
322
+ try{
323
+ await runCleanup(
324
+ cleanups[index],
325
+ context,
326
+ outcome.name,
327
+ state.signal,
328
+ onPhase,
329
+ index
330
+ );
331
+ }catch(error){
332
+ cleanupErrors.push(error);
333
+ if(state.cancelled)break;
334
+ if(error?.code==='ARCANE_TEST_TIMEOUT'){
335
+ state.markFatal(error);
336
+ break;
337
+ }
338
+ }
339
+ }
340
+ }
341
+ dispose();
342
+
343
+ if(state.cancelled){
344
+ throw cancellationError(state.signal);
345
+ }
346
+ if(bodyError)outcome.errors.push(bodyError);
347
+ if(childFailure){
348
+ outcome.errors.push(new Error(`One or more nested tests failed in "${outcome.name}".`));
349
+ }
350
+ outcome.errors.push(...cleanupErrors);
351
+ outcome.ok=outcome.errors.length===0;
352
+ return outcome;
353
+ }
354
+
355
+ function uniqueDescriptions(outcomes){
356
+ const occurrences=new Map();
357
+ return outcomes.map(outcome=>{
358
+ const count=(occurrences.get(outcome.name)??0)+1;
359
+ occurrences.set(outcome.name,count);
360
+ return count===1?outcome.name:`${outcome.name} [${String(count)}]`;
361
+ });
362
+ }
363
+
364
+ export async function runRegisteredTests({signal,requireTests=true,onPhase}={}){
365
+ if(lifecycle!=='collecting'){
366
+ throw new ReferenceError('The registered test suite can run only once.');
367
+ }
368
+ if(onPhase!==undefined&&typeof onPhase!=='function'){
369
+ throw new TypeError('onPhase must be a function when provided.');
370
+ }
371
+ lifecycle='running';
372
+ const outcomes=[];
373
+ const state=runState(signal);
374
+ try{
375
+ if(state.cancelled)throw cancellationError(state.signal);
376
+ if(requireTests&&registrations.length===0){
377
+ outcomes.push({
378
+ name:'test module registers at least one test',
379
+ ok:false,
380
+ errors:[new Error('The test module did not register any tests.')]
381
+ });
382
+ }else{
383
+ for(const current of registrations){
384
+ if(state.cancelled)throw cancellationError(state.signal);
385
+ if(state.fatalError)break;
386
+ await executeDefinition(
387
+ current,
388
+ [current.name],
389
+ outcomes,
390
+ {parentSignal:state.signal,state,onPhase}
391
+ );
392
+ }
393
+ }
394
+
395
+ const result=await ownedPhase(
396
+ onPhase,
397
+ {kind:'report',name:'Vanilla Test report',timeoutMs:REPORT_TIMEOUT_MS},
398
+ ()=>{
399
+ const descriptions=uniqueDescriptions(outcomes);
400
+ const runner=new VanillaTest();
401
+ for(let index=0;index<outcomes.length;index+=1){
402
+ const outcome=outcomes[index];
403
+ if(!outcome.ok){
404
+ for(const error of outcome.errors){
405
+ console.error(`\n[${outcome.name}]\n${errorText(error)}`);
406
+ }
407
+ }
408
+ runner.expects(descriptions[index]);
409
+ if(outcome.ok){
410
+ runner.pass();
411
+ }else{
412
+ runner.fail();
413
+ }
414
+ runner.done();
415
+ }
416
+ return runner.report();
417
+ }
418
+ );
419
+ lifecycle='reported';
420
+ return result;
421
+ }catch(error){
422
+ lifecycle='failed';
423
+ throw error;
424
+ }finally{
425
+ state.dispose();
426
+ }
427
+ }