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.
- package/CHANGELOG.md +154 -0
- package/COMMERCIAL-LICENSE.md +13 -0
- package/LICENSE +661 -0
- package/NOTICE +70 -0
- package/README.md +448 -0
- package/bin/arcane-test.mjs +741 -0
- package/bin/arcane.mjs +5 -0
- package/docs/architecture.md +234 -0
- package/docs/compatibility.md +36 -0
- package/docs/event-manager.md +166 -0
- package/docs/platform-targets.md +108 -0
- package/docs/publishing.md +203 -0
- package/docs/reference/README.md +117 -0
- package/docs/reference/arcane-ollama.md +288 -0
- package/docs/reference/availability-and-normalization.md +123 -0
- package/docs/reference/behavioral-testing.md +86 -0
- package/docs/reference/cli.md +569 -0
- package/docs/reference/core/README.md +62 -0
- package/docs/reference/core/arcane-ai-contracts.md +873 -0
- package/docs/reference/core/arcane-api.md +601 -0
- package/docs/reference/core/arcane-entities.md +65 -0
- package/docs/reference/core/arcane-events.md +134 -0
- package/docs/reference/core/ollama-module.md +181 -0
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
- package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
- package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
- package/docs/reference/event-manager.md +957 -0
- package/docs/reference/inventory/package-api.json +2632 -0
- package/docs/reference/inventory/runtime-components.json +934 -0
- package/docs/reference/inventory/runtime-entities.json +26 -0
- package/docs/reference/inventory/runtime-modules.json +1249 -0
- package/docs/reference/protocols.md +242 -0
- package/docs/reference/runtime-components.md +1098 -0
- package/docs/reference/runtime-entities.md +303 -0
- package/docs/reference/runtime-modules.md +2010 -0
- package/docs/reference/sdk-api.md +4901 -0
- package/docs/roadmap.md +79 -0
- package/docs/work-amplification.md +124 -0
- package/node_modules/event-pubsub/CHANGELOG.md +55 -0
- package/node_modules/event-pubsub/MIGRATION.md +70 -0
- package/node_modules/event-pubsub/README.md +363 -0
- package/node_modules/event-pubsub/SECURITY.md +37 -0
- package/node_modules/event-pubsub/index.js +141 -0
- package/node_modules/event-pubsub/licence +21 -0
- package/node_modules/event-pubsub/package.json +59 -0
- package/node_modules/strong-type/README.md +408 -0
- package/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/strong-type/index.js +1151 -0
- package/node_modules/strong-type/licence +21 -0
- package/node_modules/strong-type/node.js +125 -0
- package/node_modules/strong-type/package.json +61 -0
- package/package.json +95 -0
- package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
- package/runtime/arcane/components/app-bar.html +468 -0
- package/runtime/arcane/components/assistant-panel.html +715 -0
- package/runtime/arcane/components/calculator.html +8 -0
- package/runtime/arcane/components/chart.html +655 -0
- package/runtime/arcane/components/chat.html +1225 -0
- package/runtime/arcane/components/conversation-view.html +13 -0
- package/runtime/arcane/components/dashboard-config.html +341 -0
- package/runtime/arcane/components/data-maintenance.html +112 -0
- package/runtime/arcane/components/data-view.html +92 -0
- package/runtime/arcane/components/directory-picker.html +197 -0
- package/runtime/arcane/components/document-inspector.html +252 -0
- package/runtime/arcane/components/file-drop.html +264 -0
- package/runtime/arcane/components/file-inspector.html +293 -0
- package/runtime/arcane/components/file-manager.html +1715 -0
- package/runtime/arcane/components/header.html +142 -0
- package/runtime/arcane/components/integration-settings.html +14 -0
- package/runtime/arcane/components/local-ai-status.html +360 -0
- package/runtime/arcane/components/markdown-document.html +1048 -0
- package/runtime/arcane/components/markdown-editor.html +360 -0
- package/runtime/arcane/components/media-embed.html +8 -0
- package/runtime/arcane/components/modal.html +402 -0
- package/runtime/arcane/components/output-panel.html +259 -0
- package/runtime/arcane/components/preferences-form.html +135 -0
- package/runtime/arcane/components/record-timeline.html +105 -0
- package/runtime/arcane/components/relationship-board.html +116 -0
- package/runtime/arcane/components/screen-capture.html +8 -0
- package/runtime/arcane/components/source-code-viewer.html +441 -0
- package/runtime/arcane/components/source-explanation.html +124 -0
- package/runtime/arcane/components/speech.html +365 -0
- package/runtime/arcane/components/summary-strip.html +177 -0
- package/runtime/arcane/components/table.html +77 -0
- package/runtime/arcane/components/task-progress.html +282 -0
- package/runtime/arcane/components/terminal-workspace.html +65 -0
- package/runtime/arcane/components/theme-editor.html +41 -0
- package/runtime/arcane/components/theme-switcher.html +46 -0
- package/runtime/arcane/components/unified-inbox.html +20 -0
- package/runtime/arcane/components/voice-transcription.html +476 -0
- package/runtime/arcane/components/weather-widget.html +8 -0
- package/runtime/arcane/components/web-navigator.html +239 -0
- package/runtime/arcane/css/communications.css +1 -0
- package/runtime/arcane/css/dashboard-config.css +45 -0
- package/runtime/arcane/css/document-site.css +981 -0
- package/runtime/arcane/css/layout.css +438 -0
- package/runtime/arcane/css/primitives.css +321 -0
- package/runtime/arcane/css/theme.css +112 -0
- package/runtime/arcane/css/utility-workspace.css +1 -0
- package/runtime/arcane/entities/ApiModelRecord.js +20 -0
- package/runtime/arcane/entities/Calculation.js +13 -0
- package/runtime/arcane/entities/Chat.js +581 -0
- package/runtime/arcane/entities/CommunicationMessage.js +29 -0
- package/runtime/arcane/entities/CommunicationThread.js +21 -0
- package/runtime/arcane/entities/Document.js +10 -0
- package/runtime/arcane/entities/File.js +143 -0
- package/runtime/arcane/entities/Image.js +135 -0
- package/runtime/arcane/entities/IntentEnvelope.js +834 -0
- package/runtime/arcane/entities/Preference.js +83 -0
- package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
- package/runtime/arcane/entities/TerminalSession.js +46 -0
- package/runtime/arcane/entities/Theme.js +107 -0
- package/runtime/arcane/entities/User.js +1046 -0
- package/runtime/arcane/entities/Weather.js +23 -0
- package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
- package/runtime/arcane/img/arrow-left.png +0 -0
- package/runtime/arcane/img/arrow-right.png +0 -0
- package/runtime/arcane/img/doc.svg +5 -0
- package/runtime/arcane/img/folder.svg +4 -0
- package/runtime/arcane/img/image.svg +5 -0
- package/runtime/arcane/img/refresh.png +0 -0
- package/runtime/arcane/img/send.svg +9 -0
- package/runtime/arcane/img/trash.svg +5 -0
- package/runtime/arcane/img/upload.svg +5 -0
- package/runtime/arcane/modules/AI.js +2048 -0
- package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
- package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
- package/runtime/arcane/modules/AIResponseLength.js +42 -0
- package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
- package/runtime/arcane/modules/AnsiText.js +53 -0
- package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
- package/runtime/arcane/modules/AppDataScope.js +245 -0
- package/runtime/arcane/modules/AppearancePreferences.js +28 -0
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
- package/runtime/arcane/modules/AsyncBoundary.js +161 -0
- package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
- package/runtime/arcane/modules/CalculatorEngine.js +20 -0
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
- package/runtime/arcane/modules/ChartLibrary.js +35 -0
- package/runtime/arcane/modules/ChatRecords.js +13 -0
- package/runtime/arcane/modules/CommunicationAppController.js +43 -0
- package/runtime/arcane/modules/CommunicationHub.js +16 -0
- package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
- package/runtime/arcane/modules/ComponentContracts.js +586 -0
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
- package/runtime/arcane/modules/ConversationActionItems.js +488 -0
- package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
- package/runtime/arcane/modules/ConversationTimebox.js +527 -0
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
- package/runtime/arcane/modules/DBLS.js +171 -0
- package/runtime/arcane/modules/DBOPFS.js +1154 -0
- package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
- package/runtime/arcane/modules/DataMaintenance.js +91 -0
- package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
- package/runtime/arcane/modules/DirectoryPicker.js +109 -0
- package/runtime/arcane/modules/DocumentNavigation.js +223 -0
- package/runtime/arcane/modules/Errors.js +1025 -0
- package/runtime/arcane/modules/GifEncoder.js +29 -0
- package/runtime/arcane/modules/HTMLImport.js +114 -0
- package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
- package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
- package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
- package/runtime/arcane/modules/MD.js +111 -0
- package/runtime/arcane/modules/Mail.js +352 -0
- package/runtime/arcane/modules/MailTransport.mjs +180 -0
- package/runtime/arcane/modules/Marked.min.js +71 -0
- package/runtime/arcane/modules/MemoryRecords.js +44 -0
- package/runtime/arcane/modules/MessageAdvisory.js +38 -0
- package/runtime/arcane/modules/ModelDefinition.js +189 -0
- package/runtime/arcane/modules/Ollama.js +74 -0
- package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
- package/runtime/arcane/modules/OllamaSettings.js +24 -0
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
- package/runtime/arcane/modules/PreferenceStore.js +109 -0
- package/runtime/arcane/modules/QRCode.min.js +1 -0
- package/runtime/arcane/modules/Questionnaire.js +61 -0
- package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
- package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
- package/runtime/arcane/modules/RecordReviewStore.js +94 -0
- package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
- package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
- package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
- package/runtime/arcane/modules/ScreenCapture.js +20 -0
- package/runtime/arcane/modules/SpeechPlayback.js +581 -0
- package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
- package/runtime/arcane/modules/SystemAppearance.js +20 -0
- package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
- package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
- package/runtime/arcane/modules/TerminalClient.js +52 -0
- package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
- package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
- package/runtime/arcane/modules/ThemeManager.js +131 -0
- package/runtime/arcane/modules/TimeGuard.js +149 -0
- package/runtime/arcane/modules/ToolCallRouter.js +83 -0
- package/runtime/arcane/modules/WaitForComponent.js +102 -0
- package/runtime/arcane/modules/YouTubeMedia.js +16 -0
- package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
- package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
- package/runtime/arcane/modules/uPlot.min.css +1 -0
- package/runtime/arcane/security/arcane-network-policy.json +6 -0
- package/runtime/strong-type/index.js +352 -0
- package/runtime/strong-type/licence +21 -0
- package/runtime/strong-type/package.json +45 -0
- package/schemas/arcane-app-bundle.schema.json +125 -0
- package/schemas/arcane-app.schema.json +329 -0
- package/schemas/arcane-lock.schema.json +86 -0
- package/schemas/arcane-package.schema.json +224 -0
- package/schemas/cli-event.schema.json +122 -0
- package/schemas/event-stack.schema.json +152 -0
- package/schemas/native-build-plan.schema.json +176 -0
- package/schemas/target-adapter.schema.json +117 -0
- package/src/app-descriptor.mjs +500 -0
- package/src/cli/main.mjs +561 -0
- package/src/constants.mjs +31 -0
- package/src/dev-server.mjs +718 -0
- package/src/doctor.mjs +315 -0
- package/src/dom-event-instrumentation.mjs +594 -0
- package/src/errors.mjs +75 -0
- package/src/event-manager.mjs +1342 -0
- package/src/event-queue.mjs +138 -0
- package/src/events.mjs +219 -0
- package/src/index.mjs +177 -0
- package/src/integrated-provider-loader.mjs +432 -0
- package/src/native-plan.mjs +698 -0
- package/src/native-provider-loader.mjs +1126 -0
- package/src/packager/core.mjs +2691 -0
- package/src/process.mjs +353 -0
- package/src/release-bundle.mjs +2523 -0
- package/src/repository.mjs +90 -0
- package/src/runtime.mjs +452 -0
- package/src/scaffold.mjs +380 -0
- package/src/targets/index.mjs +436 -0
- package/src/templates/assets/app-icon.png +0 -0
- package/src/templates/workspace-template.mjs +388 -0
- package/src/testing-loader.mjs +9 -0
- package/src/testing.mjs +427 -0
- package/src/toolchain.mjs +1335 -0
- package/src/update-check.mjs +307 -0
- 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'&®EX_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==='/'&®exMayStart(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
|
+
}
|