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,1126 @@
1
+ import crypto from 'node:crypto';
2
+ import {lstat,readFile,realpath} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import {fileURLToPath,pathToFileURL} from 'node:url';
5
+ import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
6
+ import {validateNativeBuilder} from './native-plan.mjs';
7
+
8
+ const PROVIDER_ROOT=Object.freeze([
9
+ 'machine_bundles',
10
+ 'arcane-os-machine-bundle',
11
+ 'tools'
12
+ ]);
13
+
14
+ function providerPath(fileName){
15
+ return Object.freeze([...PROVIDER_ROOT,fileName]);
16
+ }
17
+
18
+ const linuxProviderPath=providerPath('linux-native-provider.mjs');
19
+
20
+ export const ARCANE_NATIVE_PROVIDER_PATHS=Object.freeze({
21
+ portable:providerPath('portable-native-provider.mjs'),
22
+ 'windows-x64':providerPath('windows-native-provider.mjs'),
23
+ 'linux-x64':linuxProviderPath,
24
+ 'linux-arm64':linuxProviderPath,
25
+ 'android-arm64':providerPath('android-native-provider.mjs')
26
+ });
27
+
28
+ export const ARCANE_PORTABLE_PROVIDER_PATH=ARCANE_NATIVE_PROVIDER_PATHS.portable;
29
+
30
+ const PROVIDER_GENERATION_CODE='ARCANE_NATIVE_PROVIDER_RESTART_REQUIRED';
31
+ const PROVIDER_CLOSURE_CODE='ARCANE_NATIVE_PROVIDER_CLOSURE_INVALID';
32
+ const MAX_PROVIDER_MODULES=128;
33
+ const MAX_PROVIDER_DEPTH=32;
34
+ const MAX_PROVIDER_IMPORTS=1024;
35
+ const MAX_PROVIDER_MODULE_BYTES=4*1024*1024;
36
+ const MAX_PROVIDER_CLOSURE_BYTES=32*1024*1024;
37
+ const PROCESS_PROVIDER_STATE=Symbol.for('arcane-os-sdk.native-provider-state.v1');
38
+ if(!globalThis[PROCESS_PROVIDER_STATE]){
39
+ Object.defineProperty(globalThis,PROCESS_PROVIDER_STATE,{
40
+ value:Object.freeze({
41
+ providerGenerations:new Map(),
42
+ moduleGenerations:new Map()
43
+ }),
44
+ configurable:false,
45
+ enumerable:false,
46
+ writable:false
47
+ });
48
+ }
49
+ const providerGenerationCache=globalThis[PROCESS_PROVIDER_STATE].providerGenerations;
50
+ const processModuleGenerations=globalThis[PROCESS_PROVIDER_STATE].moduleGenerations;
51
+ const utf8Decoder=new TextDecoder('utf-8',{fatal:true});
52
+ const REGEX_PREFIX_IDENTIFIERS=new Set([
53
+ 'await','case','delete','do','else','in','instanceof','new','of','return',
54
+ 'throw','typeof','void','yield'
55
+ ]);
56
+ const REGEX_PREFIX_PUNCTUATORS=new Set([
57
+ '(','[','{',',',';',':','=','==','===','!=','!==','!','?','&&','||','??',
58
+ '+','-','*','%','&','|','^','~','<','>','<=','>=','=>'
59
+ ]);
60
+ const MULTI_PUNCTUATORS=new Set([
61
+ '>>>=','===','!==','>>>','**=','&&=','||=','??=','=>','==','!=','<=','>=',
62
+ '++','--','&&','||','??','**','<<','>>','+=','-=','*=','/=','%=','&=','|=','^=',
63
+ '?.','...'
64
+ ]);
65
+
66
+ function fail(message,details){
67
+ throw new ArcaneError(ERROR_CODES.targetUnavailable,message,{details});
68
+ }
69
+
70
+ function samePath(left,right){
71
+ const normalize=value=>process.platform==='win32'
72
+ ?path.normalize(value).toLowerCase()
73
+ :path.normalize(value);
74
+ return normalize(left)===normalize(right);
75
+ }
76
+
77
+ function pathKey(value){
78
+ const normalized=path.normalize(value);
79
+ return process.platform==='win32'?normalized.toLowerCase():normalized;
80
+ }
81
+
82
+ function insideRoot(root,candidate){
83
+ const relative=path.relative(root,candidate);
84
+ return relative===''||(!relative.startsWith(`..${path.sep}`)&&relative!=='..'&&!path.isAbsolute(relative));
85
+ }
86
+
87
+ async function assertUnlinkedDirectoryAncestors(location,inspect){
88
+ const resolved=path.resolve(location);
89
+ const parsed=path.parse(resolved);
90
+ const relative=resolved.slice(parsed.root.length);
91
+ let current=parsed.root;
92
+ const components=relative.split(path.sep).filter(Boolean);
93
+ for(let index=0;index<components.length-1;index+=1){
94
+ current=path.join(current,components[index]);
95
+ const info=await inspect(current);
96
+ if(info.isSymbolicLink()||!info.isDirectory()){
97
+ fail('The Arcane OS checkout root must not use a linked or non-directory ancestor.',{
98
+ arcaneRoot:resolved,
99
+ ancestor:current
100
+ });
101
+ }
102
+ }
103
+ }
104
+
105
+ function closureFailure(message,details){
106
+ throw new ArcaneError(PROVIDER_CLOSURE_CODE,message,{details});
107
+ }
108
+
109
+ function restartRequired(target,details){
110
+ throw new ArcaneError(
111
+ PROVIDER_GENERATION_CODE,
112
+ `The Arcane ${target} provider module generation changed in this process. Restart the Arcane SDK process before pairing this provider again.`,
113
+ {details}
114
+ );
115
+ }
116
+
117
+ function sha256(value){
118
+ return crypto.createHash('sha256').update(value).digest('hex');
119
+ }
120
+
121
+ function identityValue(value){
122
+ if(typeof value==='bigint')return value.toString(10);
123
+ if(typeof value==='number'&&Number.isFinite(value))return String(value);
124
+ return null;
125
+ }
126
+
127
+ function filesystemIdentity(info){
128
+ return Object.freeze({
129
+ device:identityValue(info.dev),
130
+ inode:identityValue(info.ino),
131
+ mode:identityValue(info.mode),
132
+ links:identityValue(info.nlink),
133
+ user:identityValue(info.uid),
134
+ group:identityValue(info.gid),
135
+ size:identityValue(info.size),
136
+ modified:identityValue(info.mtimeNs??info.mtimeMs),
137
+ changed:identityValue(info.ctimeNs??info.ctimeMs),
138
+ created:identityValue(info.birthtimeNs??info.birthtimeMs)
139
+ });
140
+ }
141
+
142
+ function sameIdentity(left,right){
143
+ return JSON.stringify(left)===JSON.stringify(right);
144
+ }
145
+
146
+ function moduleToken(type,value,start,depth){
147
+ return Object.freeze({type,value,start,depth});
148
+ }
149
+
150
+ function identifierStart(character){
151
+ return /[A-Za-z_$]/u.test(character);
152
+ }
153
+
154
+ function identifierPart(character){
155
+ return /[A-Za-z0-9_$]/u.test(character);
156
+ }
157
+
158
+ function regexMayStart(previous){
159
+ if(!previous)return true;
160
+ if(previous.type==='identifier'){
161
+ return REGEX_PREFIX_IDENTIFIERS.has(previous.value);
162
+ }
163
+ return previous.type==='punctuator'&&REGEX_PREFIX_PUNCTUATORS.has(previous.value);
164
+ }
165
+
166
+ function scanQuoted(source,index,quote){
167
+ let cursor=index+1;
168
+ let escaped=false;
169
+ while(cursor<source.length){
170
+ const character=source[cursor];
171
+ if(!escaped&&character===quote){
172
+ return {end:cursor+1,raw:source.slice(index+1,cursor)};
173
+ }
174
+ if(!escaped&&(character==='\n'||character==='\r')){
175
+ closureFailure('Arcane provider source contains an unterminated string literal.',{offset:index});
176
+ }
177
+ if(!escaped&&character==='\\')escaped=true;
178
+ else escaped=false;
179
+ cursor+=1;
180
+ }
181
+ closureFailure('Arcane provider source contains an unterminated string literal.',{offset:index});
182
+ }
183
+
184
+ function skipLineComment(source,index){
185
+ let cursor=index+2;
186
+ while(cursor<source.length&&source[cursor]!=='\n'&&source[cursor]!=='\r')cursor+=1;
187
+ return cursor;
188
+ }
189
+
190
+ function skipBlockComment(source,index){
191
+ const end=source.indexOf('*/',index+2);
192
+ if(end<0)closureFailure('Arcane provider source contains an unterminated block comment.');
193
+ return end+2;
194
+ }
195
+
196
+ function skipRegex(source,index){
197
+ let cursor=index+1;
198
+ let escaped=false;
199
+ let characterClass=false;
200
+ while(cursor<source.length){
201
+ const character=source[cursor];
202
+ if(!escaped){
203
+ if(character==='[')characterClass=true;
204
+ else if(character===']')characterClass=false;
205
+ else if(character==='/'&&!characterClass){
206
+ cursor+=1;
207
+ while(cursor<source.length&&/[A-Za-z]/u.test(source[cursor]))cursor+=1;
208
+ return cursor;
209
+ }else if(character==='\n'||character==='\r'){
210
+ closureFailure('Arcane provider source contains an unterminated regular expression.');
211
+ }
212
+ }
213
+ if(!escaped&&character==='\\')escaped=true;
214
+ else escaped=false;
215
+ cursor+=1;
216
+ }
217
+ closureFailure('Arcane provider source contains an unterminated regular expression.');
218
+ }
219
+
220
+ function punctuatorAt(source,index){
221
+ for(const width of [4,3,2]){
222
+ const candidate=source.slice(index,index+width);
223
+ if(MULTI_PUNCTUATORS.has(candidate))return candidate;
224
+ }
225
+ return source[index];
226
+ }
227
+
228
+ function tokenizeProviderModule(source){
229
+ const tokens=[];
230
+ const depths={brace:0,bracket:0,parenthesis:0,template:0};
231
+ const templateExpressionBaselines=[];
232
+ let index=0;
233
+ let previous=null;
234
+
235
+ function depth(){
236
+ return depths.brace+depths.bracket+depths.parenthesis+depths.template;
237
+ }
238
+
239
+ function push(type,value,start){
240
+ const token=moduleToken(type,value,start,depth());
241
+ tokens.push(token);
242
+ previous=token;
243
+ }
244
+
245
+ function scanTemplate(opening){
246
+ if(opening){
247
+ depths.template+=1;
248
+ index+=1;
249
+ }
250
+ let escaped=false;
251
+ while(index<source.length){
252
+ const character=source[index];
253
+ if(!escaped&&character==='`'){
254
+ index+=1;
255
+ depths.template-=1;
256
+ return;
257
+ }
258
+ if(!escaped&&character==='$'&&source[index+1]==='{'){
259
+ templateExpressionBaselines.push(depths.brace);
260
+ depths.brace+=1;
261
+ index+=2;
262
+ return;
263
+ }
264
+ if(!escaped&&character==='\\')escaped=true;
265
+ else escaped=false;
266
+ index+=1;
267
+ }
268
+ closureFailure('Arcane provider source contains an unterminated template literal.');
269
+ }
270
+
271
+ while(index<source.length){
272
+ const character=source[index];
273
+ if(/\s/u.test(character)){
274
+ index+=1;
275
+ continue;
276
+ }
277
+ if(character==='/'&&source[index+1]==='/'){
278
+ index=skipLineComment(source,index);
279
+ continue;
280
+ }
281
+ if(character==='/'&&source[index+1]==='*'){
282
+ index=skipBlockComment(source,index);
283
+ continue;
284
+ }
285
+ if(character==='\''||character==='"'){
286
+ const scanned=scanQuoted(source,index,character);
287
+ push('string',scanned.raw,index);
288
+ index=scanned.end;
289
+ continue;
290
+ }
291
+ if(character==='`'){
292
+ scanTemplate(true);
293
+ continue;
294
+ }
295
+ if(character==='/'&&regexMayStart(previous)){
296
+ index=skipRegex(source,index);
297
+ push('regex','/',index);
298
+ continue;
299
+ }
300
+ if(identifierStart(character)){
301
+ const start=index;
302
+ index+=1;
303
+ while(index<source.length&&identifierPart(source[index]))index+=1;
304
+ push('identifier',source.slice(start,index),start);
305
+ continue;
306
+ }
307
+ if(/[0-9]/u.test(character)){
308
+ const start=index;
309
+ index+=1;
310
+ while(index<source.length&&/[A-Za-z0-9_.]/u.test(source[index]))index+=1;
311
+ push('number',source.slice(start,index),start);
312
+ continue;
313
+ }
314
+ const punctuator=punctuatorAt(source,index);
315
+ const tokenDepth=depth();
316
+ if(character==='}'&&depths.brace>0){
317
+ const templateBaseline=templateExpressionBaselines.at(-1);
318
+ const closesTemplateExpression=templateBaseline!==undefined
319
+ &&depths.brace===templateBaseline+1;
320
+ depths.brace-=1;
321
+ if(closesTemplateExpression){
322
+ templateExpressionBaselines.pop();
323
+ index+=1;
324
+ scanTemplate(false);
325
+ continue;
326
+ }
327
+ }else if(character===')'&&depths.parenthesis>0)depths.parenthesis-=1;
328
+ else if(character===']'&&depths.bracket>0)depths.bracket-=1;
329
+ const token=moduleToken('punctuator',punctuator,index,tokenDepth);
330
+ tokens.push(token);
331
+ previous=token;
332
+ if(character==='{')depths.brace+=1;
333
+ else if(character==='(')depths.parenthesis+=1;
334
+ else if(character==='[')depths.bracket+=1;
335
+ index+=punctuator.length;
336
+ }
337
+ return tokens;
338
+ }
339
+
340
+ function decodeModuleSpecifier(token){
341
+ if(token.type!=='string')closureFailure('Arcane provider import source must be a string literal.');
342
+ if(token.value.includes('\\')){
343
+ closureFailure('Arcane provider module specifiers must not use escaped characters.');
344
+ }
345
+ return token.value;
346
+ }
347
+
348
+ function staticModuleSpecifiers(source,modulePath){
349
+ let tokens;
350
+ try{
351
+ tokens=tokenizeProviderModule(source);
352
+ }catch(error){
353
+ if(error instanceof ArcaneError&&error.code===PROVIDER_CLOSURE_CODE){
354
+ throw new ArcaneError(error.code,error.message,{
355
+ cause:error,
356
+ details:{modulePath,...error.details}
357
+ });
358
+ }
359
+ throw error;
360
+ }
361
+ const specifiers=[];
362
+ for(let index=0;index<tokens.length;index+=1){
363
+ const token=tokens[index];
364
+ if(token.type!=='identifier'||token.value!=='import')continue;
365
+ const next=tokens[index+1];
366
+ if(next?.type==='punctuator'&&next.value==='.')continue;
367
+ if(next?.type==='punctuator'&&next.value==='('){
368
+ closureFailure('Arcane native providers must not use dynamic import().');
369
+ }
370
+ if(token.depth!==0)continue;
371
+ if(next?.type==='string'){
372
+ specifiers.push(decodeModuleSpecifier(next));
373
+ continue;
374
+ }
375
+ const from=tokens.slice(index+1).find(candidate=>
376
+ candidate.depth===0&&candidate.type==='identifier'&&candidate.value==='from'
377
+ );
378
+ if(!from)closureFailure('Arcane provider import declaration has no static source.');
379
+ specifiers.push(decodeModuleSpecifier(tokens[tokens.indexOf(from)+1]));
380
+ }
381
+ for(let index=0;index<tokens.length;index+=1){
382
+ const token=tokens[index];
383
+ if(token.depth!==0||token.type!=='identifier'||token.value!=='export')continue;
384
+ const next=tokens[index+1];
385
+ if(next?.value!=='*'&&next?.value!=='{')continue;
386
+ let cursor=index+1;
387
+ while(cursor<tokens.length&&!(
388
+ tokens[cursor].depth===0
389
+ &&tokens[cursor].type==='identifier'
390
+ &&tokens[cursor].value==='from'
391
+ )){
392
+ if(tokens[cursor].depth===0&&tokens[cursor].value===';')break;
393
+ cursor+=1;
394
+ }
395
+ if(tokens[cursor]?.value==='from'){
396
+ specifiers.push(decodeModuleSpecifier(tokens[cursor+1]));
397
+ }
398
+ }
399
+ return specifiers;
400
+ }
401
+
402
+ async function regularFile(filePath,inspect){
403
+ try{
404
+ const info=await inspect(filePath);
405
+ return info.isFile()&&!info.isSymbolicLink();
406
+ }catch(error){
407
+ if(error?.code==='ENOENT')return false;
408
+ throw error;
409
+ }
410
+ }
411
+
412
+ async function stableModule({modulePath,inspect,readModule}){
413
+ const before=await inspect(modulePath);
414
+ if(before.isSymbolicLink()||!before.isFile()){
415
+ closureFailure('Every Arcane native provider module must be a real regular file.',{
416
+ modulePath
417
+ });
418
+ }
419
+ const beforeIdentity=filesystemIdentity(before);
420
+ const declaredSize=Number(before.size);
421
+ if(Number.isFinite(declaredSize)&&declaredSize>MAX_PROVIDER_MODULE_BYTES){
422
+ closureFailure('An Arcane native provider module exceeds the fixed size limit.',{
423
+ modulePath,
424
+ maximumBytes:MAX_PROVIDER_MODULE_BYTES
425
+ });
426
+ }
427
+ const bytes=await readModule(modulePath);
428
+ if(!Buffer.isBuffer(bytes)&&!(bytes instanceof Uint8Array)){
429
+ closureFailure('The Arcane native provider module reader must return bytes.',{modulePath});
430
+ }
431
+ const normalizedBytes=Buffer.from(bytes);
432
+ if(normalizedBytes.byteLength>MAX_PROVIDER_MODULE_BYTES){
433
+ closureFailure('An Arcane native provider module exceeds the fixed size limit.',{
434
+ modulePath,
435
+ maximumBytes:MAX_PROVIDER_MODULE_BYTES
436
+ });
437
+ }
438
+ const after=await inspect(modulePath);
439
+ const afterIdentity=filesystemIdentity(after);
440
+ if(after.isSymbolicLink()||!after.isFile()||!sameIdentity(beforeIdentity,afterIdentity)){
441
+ closureFailure('An Arcane native provider module changed while its generation was read.',{
442
+ modulePath
443
+ });
444
+ }
445
+ let source;
446
+ try{
447
+ source=utf8Decoder.decode(normalizedBytes);
448
+ }catch(error){
449
+ throw new ArcaneError(
450
+ PROVIDER_CLOSURE_CODE,
451
+ 'Arcane native provider modules must contain valid UTF-8 source.',
452
+ {cause:error,details:{modulePath}}
453
+ );
454
+ }
455
+ return Object.freeze({
456
+ bytes:normalizedBytes.byteLength,
457
+ contentSha256:sha256(normalizedBytes),
458
+ filesystemIdentity:afterIdentity,
459
+ source
460
+ });
461
+ }
462
+
463
+ function resolveRelativeModule({canonicalRoot,modulePath,specifier}){
464
+ if(specifier.startsWith('node:'))return null;
465
+ if(!specifier.startsWith('./')&&!specifier.startsWith('../')){
466
+ closureFailure(
467
+ 'Arcane native providers may import only node: built-ins and static relative modules.',
468
+ {modulePath,specifier}
469
+ );
470
+ }
471
+ let resolvedUrl;
472
+ let resolvedPath;
473
+ try{
474
+ resolvedUrl=new URL(specifier,pathToFileURL(modulePath));
475
+ if(resolvedUrl.protocol!=='file:'||resolvedUrl.search||resolvedUrl.hash){
476
+ closureFailure('Arcane native provider relative imports must resolve to plain files.',{
477
+ modulePath,
478
+ specifier
479
+ });
480
+ }
481
+ resolvedPath=path.resolve(fileURLToPath(resolvedUrl));
482
+ }catch(error){
483
+ if(error instanceof ArcaneError)throw error;
484
+ throw new ArcaneError(
485
+ PROVIDER_CLOSURE_CODE,
486
+ 'Arcane native provider module specifier is invalid.',
487
+ {cause:error,details:{modulePath,specifier}}
488
+ );
489
+ }
490
+ if(!insideRoot(canonicalRoot,resolvedPath)){
491
+ closureFailure('An Arcane native provider relative import escapes the Arcane checkout.',{
492
+ modulePath,
493
+ specifier
494
+ });
495
+ }
496
+ if(!['.js','.mjs'].includes(path.extname(resolvedPath).toLowerCase())){
497
+ closureFailure('Arcane native provider relative imports must name .js or .mjs modules.',{
498
+ modulePath,
499
+ specifier
500
+ });
501
+ }
502
+ return resolvedPath;
503
+ }
504
+
505
+ function freezeGenerationEvidence({canonicalRoot,target,providerPath,modules,totalBytes}){
506
+ const sorted=[...modules.values()].sort((left,right)=>
507
+ left.relativePath.localeCompare(right.relativePath,'en')
508
+ );
509
+ const contentRecords=sorted.map(module=>Object.freeze({
510
+ path:module.relativePath,
511
+ bytes:module.bytes,
512
+ contentSha256:module.contentSha256
513
+ }));
514
+ const identityRecords=sorted.map(module=>Object.freeze({
515
+ path:module.relativePath,
516
+ filesystemIdentity:module.filesystemIdentity
517
+ }));
518
+ const contentSha256=sha256(JSON.stringify(contentRecords));
519
+ const filesystemIdentitySha256=sha256(JSON.stringify(identityRecords));
520
+ const generationSha256=sha256(`${contentSha256}\0${filesystemIdentitySha256}`);
521
+ return Object.freeze({
522
+ schemaVersion:1,
523
+ kind:'arcane-native-provider-generation',
524
+ target,
525
+ canonicalArcaneRoot:canonicalRoot,
526
+ providerPath,
527
+ entryPath:path.relative(canonicalRoot,providerPath).split(path.sep).join('/'),
528
+ moduleCount:sorted.length,
529
+ totalBytes,
530
+ contentSha256,
531
+ filesystemIdentitySha256,
532
+ generationSha256,
533
+ modules:Object.freeze(sorted.map(module=>Object.freeze({
534
+ path:module.relativePath,
535
+ canonicalLocation:module.canonicalLocation,
536
+ bytes:module.bytes,
537
+ contentSha256:module.contentSha256,
538
+ filesystemIdentity:module.filesystemIdentity
539
+ })))
540
+ });
541
+ }
542
+
543
+ async function providerGeneration({
544
+ canonicalRoot,
545
+ providerPath,
546
+ target,
547
+ inspect,
548
+ canonicalize,
549
+ readModule,
550
+ signal
551
+ }){
552
+ const modules=new Map();
553
+ let importCount=0;
554
+ let totalBytes=0;
555
+
556
+ async function visit(requestedPath,depth){
557
+ throwIfAborted(signal);
558
+ if(depth>MAX_PROVIDER_DEPTH){
559
+ closureFailure('Arcane native provider module closure exceeds the fixed depth limit.',{
560
+ maximumDepth:MAX_PROVIDER_DEPTH
561
+ });
562
+ }
563
+ const resolvedPath=path.resolve(requestedPath);
564
+ if(!insideRoot(canonicalRoot,resolvedPath)){
565
+ closureFailure('An Arcane native provider module escapes the Arcane checkout.',{
566
+ modulePath:resolvedPath
567
+ });
568
+ }
569
+ if(modules.has(pathKey(resolvedPath)))return;
570
+ if(modules.size>=MAX_PROVIDER_MODULES){
571
+ closureFailure('Arcane native provider module closure exceeds the fixed module limit.',{
572
+ maximumModules:MAX_PROVIDER_MODULES
573
+ });
574
+ }
575
+ let canonicalModule;
576
+ try{
577
+ canonicalModule=await canonicalize(resolvedPath);
578
+ }catch(error){
579
+ if(error?.code==='ENOENT'){
580
+ closureFailure('An Arcane native provider static relative module does not exist.',{
581
+ modulePath:resolvedPath
582
+ });
583
+ }
584
+ throw error;
585
+ }
586
+ if(!samePath(canonicalModule,resolvedPath)||!insideRoot(canonicalRoot,canonicalModule)){
587
+ closureFailure('Arcane native provider modules must not resolve through linked or escaped locations.',{
588
+ modulePath:resolvedPath,
589
+ canonicalModule
590
+ });
591
+ }
592
+ const snapshot=await stableModule({modulePath:canonicalModule,inspect,readModule});
593
+ totalBytes+=snapshot.bytes;
594
+ if(totalBytes>MAX_PROVIDER_CLOSURE_BYTES){
595
+ closureFailure('Arcane native provider module closure exceeds the fixed byte limit.',{
596
+ maximumBytes:MAX_PROVIDER_CLOSURE_BYTES
597
+ });
598
+ }
599
+ const relativePath=path.relative(canonicalRoot,canonicalModule).split(path.sep).join('/');
600
+ modules.set(pathKey(canonicalModule),Object.freeze({
601
+ relativePath,
602
+ canonicalLocation:canonicalModule,
603
+ bytes:snapshot.bytes,
604
+ contentSha256:snapshot.contentSha256,
605
+ filesystemIdentity:snapshot.filesystemIdentity
606
+ }));
607
+ const specifiers=staticModuleSpecifiers(snapshot.source,canonicalModule);
608
+ importCount+=specifiers.length;
609
+ if(importCount>MAX_PROVIDER_IMPORTS){
610
+ closureFailure('Arcane native provider module closure exceeds the fixed import limit.',{
611
+ maximumImports:MAX_PROVIDER_IMPORTS
612
+ });
613
+ }
614
+ for(const specifier of specifiers){
615
+ const dependency=resolveRelativeModule({
616
+ canonicalRoot,
617
+ modulePath:canonicalModule,
618
+ specifier
619
+ });
620
+ if(dependency)await visit(dependency,depth+1);
621
+ }
622
+ }
623
+
624
+ await visit(providerPath,0);
625
+ return freezeGenerationEvidence({canonicalRoot,target,providerPath,modules,totalBytes});
626
+ }
627
+
628
+ function sameGeneration(left,right){
629
+ return left.generationSha256===right.generationSha256
630
+ &&left.moduleCount===right.moduleCount
631
+ &&left.totalBytes===right.totalBytes;
632
+ }
633
+
634
+ function generationCacheKey({arcaneRoot,target,providerPath}){
635
+ return `${pathKey(arcaneRoot)}\0${target}\0${pathKey(providerPath)}`;
636
+ }
637
+
638
+ function generationDetails(expected,current){
639
+ return Object.freeze({
640
+ expectedGenerationSha256:expected.generationSha256,
641
+ actualGenerationSha256:current.generationSha256,
642
+ expectedContentSha256:expected.contentSha256,
643
+ actualContentSha256:current.contentSha256,
644
+ expectedFilesystemIdentitySha256:expected.filesystemIdentitySha256,
645
+ actualFilesystemIdentitySha256:current.filesystemIdentitySha256
646
+ });
647
+ }
648
+
649
+ function unavailableGenerationDetails(expected,error){
650
+ return Object.freeze({
651
+ expectedGenerationSha256:expected.generationSha256,
652
+ actualGenerationSha256:null,
653
+ validationCode:typeof error?.code==='string'?error.code:null,
654
+ validationMessage:String(error?.message??error)
655
+ });
656
+ }
657
+
658
+ function sameModuleGeneration(left,right){
659
+ return samePath(left.canonicalLocation,right.canonicalLocation)
660
+ &&left.bytes===right.bytes
661
+ &&left.contentSha256===right.contentSha256
662
+ &&sameIdentity(left.filesystemIdentity,right.filesystemIdentity);
663
+ }
664
+
665
+ function moduleGenerationDetails(expected,current){
666
+ return Object.freeze({
667
+ modulePath:expected.canonicalLocation,
668
+ expectedContentSha256:expected.contentSha256,
669
+ actualContentSha256:current?.contentSha256??null,
670
+ expectedFilesystemIdentity:expected.filesystemIdentity,
671
+ actualFilesystemIdentity:current?.filesystemIdentity??null
672
+ });
673
+ }
674
+
675
+ function poisonChangedModuleBindings(record,current){
676
+ const currentModules=new Map(current.modules.map(module=>[
677
+ pathKey(module.canonicalLocation),
678
+ module
679
+ ]));
680
+ for(const binding of record.moduleBindings??[]){
681
+ const observed=currentModules.get(binding.key);
682
+ if(!observed||!sameModuleGeneration(binding.module,observed)){
683
+ binding.registryRecord.poisoned=true;
684
+ }
685
+ }
686
+ record.state='poisoned';
687
+ }
688
+
689
+ function reserveProcessModuleGenerations(record,evidence){
690
+ const bindings=[];
691
+ const created=[];
692
+ try{
693
+ for(const module of evidence.modules){
694
+ const key=pathKey(module.canonicalLocation);
695
+ let registryRecord=processModuleGenerations.get(key);
696
+ if(registryRecord){
697
+ if(registryRecord.poisoned
698
+ ||!sameModuleGeneration(registryRecord.module,module)){
699
+ registryRecord.poisoned=true;
700
+ record.state='poisoned';
701
+ restartRequired(record.target,moduleGenerationDetails(
702
+ registryRecord.module,
703
+ module
704
+ ));
705
+ }
706
+ }else{
707
+ registryRecord={module,loaded:false,poisoned:false};
708
+ processModuleGenerations.set(key,registryRecord);
709
+ created.push({key,registryRecord});
710
+ }
711
+ bindings.push(Object.freeze({key,module,registryRecord}));
712
+ }
713
+ }catch(error){
714
+ for(const createdBinding of created){
715
+ if(!createdBinding.registryRecord.loaded
716
+ &&processModuleGenerations.get(createdBinding.key)===createdBinding.registryRecord){
717
+ processModuleGenerations.delete(createdBinding.key);
718
+ }
719
+ }
720
+ throw error;
721
+ }
722
+ record.moduleBindings=Object.freeze(bindings);
723
+ }
724
+
725
+ function markProcessModulesLoaded(record){
726
+ for(const binding of record.moduleBindings){
727
+ if(binding.registryRecord.poisoned){
728
+ record.state='poisoned';
729
+ restartRequired(record.target,moduleGenerationDetails(
730
+ binding.registryRecord.module,
731
+ binding.module
732
+ ));
733
+ }
734
+ binding.registryRecord.loaded=true;
735
+ }
736
+ }
737
+
738
+ function poisonUnloadedProcessModules(record){
739
+ for(const binding of record.moduleBindings??[]){
740
+ if(!binding.registryRecord.loaded)binding.registryRecord.poisoned=true;
741
+ }
742
+ }
743
+
744
+ function assertLoadedProcessModules(record,evidence=record.evidence){
745
+ if(evidence.modules.length!==(record.moduleBindings?.length??0)){
746
+ record.state='poisoned';
747
+ restartRequired(record.target,unavailableGenerationDetails(
748
+ record.evidence,
749
+ new Error('The process-global provider module inventory changed.')
750
+ ));
751
+ }
752
+ for(const module of evidence.modules){
753
+ const key=pathKey(module.canonicalLocation);
754
+ const registryRecord=processModuleGenerations.get(key);
755
+ if(!registryRecord||!registryRecord.loaded||registryRecord.poisoned
756
+ ||!sameModuleGeneration(registryRecord.module,module)){
757
+ if(registryRecord)registryRecord.poisoned=true;
758
+ record.state='poisoned';
759
+ restartRequired(record.target,moduleGenerationDetails(
760
+ registryRecord?.module??module,
761
+ registryRecord?module:null
762
+ ));
763
+ }
764
+ }
765
+ }
766
+
767
+ async function authenticateProviderGeneration({
768
+ record,
769
+ canonicalRoot,
770
+ providerPath,
771
+ target,
772
+ inspect,
773
+ canonicalize
774
+ }){
775
+ if(record.state==='poisoned'){
776
+ restartRequired(target,unavailableGenerationDetails(
777
+ record.evidence,
778
+ new Error('The cached provider generation is no longer valid.')
779
+ ));
780
+ }
781
+ try{
782
+ const rootInfo=await inspect(canonicalRoot);
783
+ const authenticatedRoot=await canonicalize(canonicalRoot);
784
+ if(rootInfo.isSymbolicLink()||!rootInfo.isDirectory()
785
+ ||!samePath(authenticatedRoot,canonicalRoot)
786
+ ||record.evidence.moduleCount!==record.evidence.modules.length
787
+ ||record.evidence.moduleCount>MAX_PROVIDER_MODULES
788
+ ||!record.evidence.modules.some(module=>samePath(module.canonicalLocation,providerPath))){
789
+ closureFailure('The loaded Arcane provider module inventory is no longer valid.');
790
+ }
791
+ assertLoadedProcessModules(record);
792
+ const observedPaths=new Set();
793
+ for(const module of record.evidence.modules){
794
+ if(observedPaths.has(pathKey(module.canonicalLocation))
795
+ ||!insideRoot(canonicalRoot,module.canonicalLocation)){
796
+ closureFailure('The loaded Arcane provider module inventory is no longer canonical.');
797
+ }
798
+ observedPaths.add(pathKey(module.canonicalLocation));
799
+ const before=await inspect(module.canonicalLocation);
800
+ const canonicalModule=await canonicalize(module.canonicalLocation);
801
+ const after=await inspect(module.canonicalLocation);
802
+ const registryRecord=processModuleGenerations.get(pathKey(module.canonicalLocation));
803
+ if(before.isSymbolicLink()||!before.isFile()
804
+ ||after.isSymbolicLink()||!after.isFile()
805
+ ||!samePath(canonicalModule,module.canonicalLocation)
806
+ ||!sameIdentity(filesystemIdentity(before),module.filesystemIdentity)
807
+ ||!sameIdentity(filesystemIdentity(after),module.filesystemIdentity)){
808
+ if(registryRecord)registryRecord.poisoned=true;
809
+ closureFailure('A loaded Arcane provider module identity is no longer valid.',{
810
+ modulePath:module.canonicalLocation
811
+ });
812
+ }
813
+ }
814
+ }catch(error){
815
+ record.state='poisoned';
816
+ const details=unavailableGenerationDetails(record.evidence,error);
817
+ throw new ArcaneError(
818
+ PROVIDER_GENERATION_CODE,
819
+ `The Arcane ${target} provider module generation can no longer be authenticated. Restart the Arcane SDK process before pairing this provider again.`,
820
+ {cause:error,details}
821
+ );
822
+ }
823
+ return record.evidence;
824
+ }
825
+
826
+ function guardedNativeBuilder({
827
+ rawBuilder,
828
+ record,
829
+ canonicalRoot,
830
+ providerPath,
831
+ target,
832
+ inspect,
833
+ canonicalize
834
+ }){
835
+ const guarded={
836
+ protocol:rawBuilder.protocol,
837
+ providerGeneration:record.evidence
838
+ };
839
+ for(const method of [
840
+ 'describe','doctor','prepare','authenticateToolchainReceipt','build','verify','run'
841
+ ]){
842
+ guarded[method]=async function guardedProviderOperation(...args){
843
+ await authenticateProviderGeneration({
844
+ record,
845
+ canonicalRoot,
846
+ providerPath,
847
+ target,
848
+ inspect,
849
+ canonicalize
850
+ });
851
+ return rawBuilder[method].apply(rawBuilder,args);
852
+ };
853
+ }
854
+ return Object.freeze(guarded);
855
+ }
856
+
857
+ async function importReservedProvider(record){
858
+ const requested=await providerGeneration({
859
+ canonicalRoot:record.canonicalRoot,
860
+ providerPath:record.providerPath,
861
+ target:record.target,
862
+ inspect:record.inspect,
863
+ canonicalize:record.canonicalize,
864
+ readModule:record.readModule
865
+ });
866
+ record.evidence=requested;
867
+ reserveProcessModuleGenerations(record,requested);
868
+ record.importAttempted=true;
869
+
870
+ let namespace;
871
+ let importError;
872
+ let rawBuilder;
873
+ let importCompleted=false;
874
+ try{
875
+ namespace=await record.importModule(pathToFileURL(record.providerPath).href);
876
+ importCompleted=true;
877
+ rawBuilder=validateNativeBuilder(
878
+ namespace?.arcaneNativeBuilderProvider??namespace?.default
879
+ );
880
+ }catch(error){
881
+ importError=error;
882
+ }
883
+
884
+ let afterImport;
885
+ try{
886
+ afterImport=await providerGeneration({
887
+ canonicalRoot:record.canonicalRoot,
888
+ providerPath:record.providerPath,
889
+ target:record.target,
890
+ inspect:record.inspect,
891
+ canonicalize:record.canonicalize,
892
+ readModule:record.readModule
893
+ });
894
+ }catch(error){
895
+ record.state='poisoned';
896
+ for(const binding of record.moduleBindings)binding.registryRecord.poisoned=true;
897
+ throw new ArcaneError(
898
+ PROVIDER_GENERATION_CODE,
899
+ `The Arcane ${record.target} provider module generation changed while it was imported. Restart the Arcane SDK process before pairing this provider again.`,
900
+ {cause:error,details:unavailableGenerationDetails(requested,error)}
901
+ );
902
+ }
903
+ if(!sameGeneration(requested,afterImport)){
904
+ poisonChangedModuleBindings(record,afterImport);
905
+ restartRequired(record.target,generationDetails(requested,afterImport));
906
+ }
907
+ if(importCompleted)markProcessModulesLoaded(record);
908
+ if(importError){
909
+ if(!importCompleted)poisonUnloadedProcessModules(record);
910
+ record.state=importCompleted?'invalid':'poisoned';
911
+ if(importError instanceof ArcaneError)throw importError;
912
+ throw new ArcaneError(
913
+ ERROR_CODES.targetUnavailable,
914
+ `The Arcane ${record.target} provider could not be loaded: ${importError.message}`,
915
+ {cause:importError,details:{providerPath:record.providerPath}}
916
+ );
917
+ }
918
+
919
+ const guardedBuilder=guardedNativeBuilder({
920
+ rawBuilder,
921
+ record,
922
+ canonicalRoot:record.canonicalRoot,
923
+ providerPath:record.providerPath,
924
+ target:record.target,
925
+ inspect:record.inspect,
926
+ canonicalize:record.canonicalize
927
+ });
928
+ record.namespace=namespace;
929
+ record.pairing=Object.freeze({
930
+ arcaneRoot:record.canonicalRoot,
931
+ toolchainRoot:record.canonicalRoot,
932
+ providerPath:record.providerPath,
933
+ providerGeneration:requested,
934
+ nativeBuilder:guardedBuilder
935
+ });
936
+ record.state='ready';
937
+ return record.pairing;
938
+ }
939
+
940
+ async function initializeProviderReservation(record){
941
+ record.state='loading';
942
+ try{
943
+ const rootInfo=await record.inspect(record.requestedRoot);
944
+ if(rootInfo.isSymbolicLink()||!rootInfo.isDirectory()){
945
+ fail('The Arcane OS checkout root must be a real directory.',{
946
+ arcaneRoot:record.requestedRoot
947
+ });
948
+ }
949
+ await assertUnlinkedDirectoryAncestors(record.requestedRoot,record.inspect);
950
+ record.canonicalRoot=await record.canonicalize(record.requestedRoot);
951
+ if(!await regularFile(record.requestedProvider,record.inspect)){
952
+ fail(`The selected Arcane OS checkout does not contain the ${record.target} native provider.`,{
953
+ target:record.target,
954
+ arcaneRoot:record.canonicalRoot,
955
+ providerPath:record.requestedProvider
956
+ });
957
+ }
958
+ record.providerPath=await record.canonicalize(record.requestedProvider);
959
+ const expectedProvider=path.join(
960
+ record.canonicalRoot,
961
+ ...ARCANE_NATIVE_PROVIDER_PATHS[record.target]
962
+ );
963
+ if(!samePath(record.providerPath,expectedProvider)){
964
+ fail(`The Arcane ${record.target} provider path must not resolve through a linked location.`,{
965
+ target:record.target,
966
+ providerPath:record.requestedProvider
967
+ });
968
+ }
969
+ return await importReservedProvider(record);
970
+ }catch(error){
971
+ if(!record.importAttempted){
972
+ record.state='invalid';
973
+ if(providerGenerationCache.get(record.cacheKey)===record){
974
+ providerGenerationCache.delete(record.cacheKey);
975
+ }
976
+ }
977
+ if(error?.code==='ENOENT'){
978
+ fail('The selected Arcane OS checkout root or provider module does not exist.',{
979
+ arcaneRoot:record.requestedRoot
980
+ });
981
+ }
982
+ throw error;
983
+ }
984
+ }
985
+
986
+ function createProviderReservation(options){
987
+ let releaseStart;
988
+ const startGate=new Promise(resolve=>{releaseStart=resolve;});
989
+ const record={
990
+ ...options,
991
+ state:'reserved',
992
+ started:false,
993
+ importAttempted:false,
994
+ evidence:null,
995
+ moduleBindings:null,
996
+ namespace:null,
997
+ pairing:null,
998
+ promise:null,
999
+ start:null
1000
+ };
1001
+ record.start=()=>{
1002
+ if(record.started)return;
1003
+ record.started=true;
1004
+ releaseStart();
1005
+ };
1006
+ record.promise=startGate.then(()=>initializeProviderReservation(record));
1007
+ record.promise.catch(()=>{});
1008
+ return record;
1009
+ }
1010
+
1011
+ async function authenticateReadyPairing(record){
1012
+ if(record.state==='poisoned'){
1013
+ restartRequired(record.target,unavailableGenerationDetails(
1014
+ record.evidence,
1015
+ new Error('The cached provider generation is no longer valid.')
1016
+ ));
1017
+ }
1018
+ let current;
1019
+ try{
1020
+ current=await providerGeneration({
1021
+ canonicalRoot:record.canonicalRoot,
1022
+ providerPath:record.providerPath,
1023
+ target:record.target,
1024
+ inspect:record.inspect,
1025
+ canonicalize:record.canonicalize,
1026
+ readModule:record.readModule
1027
+ });
1028
+ }catch(error){
1029
+ record.state='poisoned';
1030
+ throw new ArcaneError(
1031
+ PROVIDER_GENERATION_CODE,
1032
+ `The Arcane ${record.target} provider module generation can no longer be authenticated. Restart the Arcane SDK process before pairing this provider again.`,
1033
+ {cause:error,details:unavailableGenerationDetails(record.evidence,error)}
1034
+ );
1035
+ }
1036
+ if(!sameGeneration(record.evidence,current)){
1037
+ poisonChangedModuleBindings(record,current);
1038
+ restartRequired(record.target,generationDetails(record.evidence,current));
1039
+ }
1040
+ assertLoadedProcessModules(record,current);
1041
+ return record.pairing;
1042
+ }
1043
+
1044
+ async function observeProviderReservation({record,stateAtInvocation,onEvent,signal,target}){
1045
+ throwIfAborted(signal);
1046
+ await onEvent?.(Object.freeze({
1047
+ type:'native.provider.load.started',
1048
+ target,
1049
+ message:`Loading the explicitly selected Arcane ${String(target)} provider.`
1050
+ }));
1051
+ record.start();
1052
+ throwIfAborted(signal);
1053
+ const pairing=await record.promise;
1054
+ if(stateAtInvocation==='ready'||stateAtInvocation==='poisoned'){
1055
+ await authenticateReadyPairing(record);
1056
+ }
1057
+ throwIfAborted(signal);
1058
+ await onEvent?.(Object.freeze({
1059
+ type:'native.provider.load.completed',
1060
+ target,
1061
+ message:`The Arcane ${target} provider is paired for this process.`
1062
+ }));
1063
+ return pairing;
1064
+ }
1065
+
1066
+ export function loadArcaneNativeProvider({
1067
+ arcaneRoot,
1068
+ target,
1069
+ inspect=lstat,
1070
+ canonicalize=realpath,
1071
+ readModule=readFile,
1072
+ importModule=specifier=>import(specifier),
1073
+ generationCache=providerGenerationCache,
1074
+ signal,
1075
+ onEvent
1076
+ }={}){
1077
+ throwIfAborted(signal);
1078
+ const relativeProviderPath=ARCANE_NATIVE_PROVIDER_PATHS[target];
1079
+ if(!relativeProviderPath){
1080
+ fail(`No fixed Arcane native provider is registered for target ${String(target)}.`,{
1081
+ target,
1082
+ supportedTargets:Object.keys(ARCANE_NATIVE_PROVIDER_PATHS)
1083
+ });
1084
+ }
1085
+ if(typeof arcaneRoot!=='string'||!arcaneRoot.trim()){
1086
+ fail(`The ${target} native provider requires an explicit Arcane OS checkout root.`);
1087
+ }
1088
+ if(typeof readModule!=='function'||typeof importModule!=='function'
1089
+ ||typeof generationCache?.get!=='function'||typeof generationCache?.set!=='function'){
1090
+ fail('The Arcane native provider loader dependencies are invalid.');
1091
+ }
1092
+ const requestedRoot=path.resolve(arcaneRoot);
1093
+ const requestedProvider=path.join(requestedRoot,...relativeProviderPath);
1094
+ const cacheKey=generationCacheKey({
1095
+ arcaneRoot:requestedRoot,
1096
+ target,
1097
+ providerPath:requestedProvider
1098
+ });
1099
+ let record=providerGenerationCache.get(cacheKey);
1100
+ const stateAtInvocation=record?.state??'created';
1101
+ if(!record){
1102
+ record=createProviderReservation({
1103
+ cacheKey,
1104
+ requestedRoot,
1105
+ requestedProvider,
1106
+ target,
1107
+ inspect,
1108
+ canonicalize,
1109
+ readModule,
1110
+ importModule
1111
+ });
1112
+ providerGenerationCache.set(cacheKey,record);
1113
+ }
1114
+ generationCache.set(cacheKey,record);
1115
+ return observeProviderReservation({
1116
+ record,
1117
+ stateAtInvocation,
1118
+ onEvent,
1119
+ signal,
1120
+ target,
1121
+ });
1122
+ }
1123
+
1124
+ export function loadArcanePortableProvider(options={}){
1125
+ return loadArcaneNativeProvider({...options,target:'portable'});
1126
+ }