arcane-os 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
- package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -146
- package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1050 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +40 -62
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +112 -779
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -187
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1252
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -677
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2960
- package/docs/reference/sdk-api.md +0 -6694
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -1,1126 +1,167 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {lstat,readFile,realpath} from 'node:fs/promises';
|
|
1
|
+
import {lstat,realpath} from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
3
|
+
import {pathToFileURL} from 'node:url';
|
|
5
4
|
import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
|
|
6
5
|
import {validateNativeBuilder} from './native-plan.mjs';
|
|
7
6
|
|
|
8
|
-
const PROVIDER_ROOT=
|
|
9
|
-
'machine_bundles',
|
|
10
|
-
'arcane-os-machine-bundle',
|
|
11
|
-
'tools'
|
|
12
|
-
]);
|
|
7
|
+
const PROVIDER_ROOT=['machine_bundles','arcane-os-machine-bundle','tools'];
|
|
13
8
|
|
|
14
9
|
function providerPath(fileName){
|
|
15
|
-
return
|
|
10
|
+
return [...PROVIDER_ROOT,fileName];
|
|
16
11
|
}
|
|
17
12
|
|
|
18
13
|
const linuxProviderPath=providerPath('linux-native-provider.mjs');
|
|
19
|
-
|
|
20
|
-
export const ARCANE_NATIVE_PROVIDER_PATHS=Object.freeze({
|
|
14
|
+
const FIXED_NATIVE_PROVIDER_PATHS={
|
|
21
15
|
portable:providerPath('portable-native-provider.mjs'),
|
|
22
16
|
'windows-x64':providerPath('windows-native-provider.mjs'),
|
|
23
17
|
'linux-x64':linuxProviderPath,
|
|
24
18
|
'linux-arm64':linuxProviderPath,
|
|
25
19
|
'android-arm64':providerPath('android-native-provider.mjs')
|
|
26
|
-
}
|
|
20
|
+
};
|
|
27
21
|
|
|
28
|
-
export const
|
|
22
|
+
export const ARCANE_NATIVE_PROVIDER_PATHS={
|
|
23
|
+
portable:[...FIXED_NATIVE_PROVIDER_PATHS.portable],
|
|
24
|
+
'windows-x64':[...FIXED_NATIVE_PROVIDER_PATHS['windows-x64']],
|
|
25
|
+
'linux-x64':[...FIXED_NATIVE_PROVIDER_PATHS['linux-x64']],
|
|
26
|
+
'linux-arm64':[...FIXED_NATIVE_PROVIDER_PATHS['linux-arm64']],
|
|
27
|
+
'android-arm64':[...FIXED_NATIVE_PROVIDER_PATHS['android-arm64']]
|
|
28
|
+
};
|
|
29
29
|
|
|
30
|
-
const
|
|
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
|
-
]);
|
|
30
|
+
export const ARCANE_PORTABLE_PROVIDER_PATH=[...ARCANE_NATIVE_PROVIDER_PATHS.portable];
|
|
65
31
|
|
|
66
32
|
function fail(message,details){
|
|
67
33
|
throw new ArcaneError(ERROR_CODES.targetUnavailable,message,{details});
|
|
68
34
|
}
|
|
69
35
|
|
|
70
|
-
function
|
|
71
|
-
|
|
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;
|
|
36
|
+
function importProviderModule(specifier){
|
|
37
|
+
return import(specifier);
|
|
80
38
|
}
|
|
81
39
|
|
|
82
40
|
function insideRoot(root,candidate){
|
|
83
41
|
const relative=path.relative(root,candidate);
|
|
84
|
-
return 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}
|
|
42
|
+
return relative===''||Boolean(
|
|
43
|
+
relative&&!relative.startsWith('..')&&!path.isAbsolute(relative)
|
|
114
44
|
);
|
|
115
45
|
}
|
|
116
46
|
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
47
|
+
async function resolveProviderLocation({
|
|
48
|
+
arcaneRoot,
|
|
49
|
+
target,
|
|
50
|
+
inspect=lstat,
|
|
51
|
+
canonicalize=realpath
|
|
52
|
+
}={}){
|
|
53
|
+
const relativeProviderPath=FIXED_NATIVE_PROVIDER_PATHS[target];
|
|
54
|
+
if(!relativeProviderPath){
|
|
55
|
+
fail(`No Arcane native provider is registered for target ${String(target)}.`,{
|
|
56
|
+
target,
|
|
57
|
+
supportedTargets:Object.keys(FIXED_NATIVE_PROVIDER_PATHS)
|
|
443
58
|
});
|
|
444
59
|
}
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
);
|
|
60
|
+
if(typeof arcaneRoot!=='string'||!arcaneRoot.trim()){
|
|
61
|
+
fail(`The ${target} native provider requires an Arcane OS checkout root.`);
|
|
489
62
|
}
|
|
490
|
-
if(
|
|
491
|
-
|
|
492
|
-
modulePath,
|
|
493
|
-
specifier
|
|
494
|
-
});
|
|
63
|
+
if(typeof inspect!=='function'||typeof canonicalize!=='function'){
|
|
64
|
+
fail('The Arcane native provider loader dependencies are invalid.');
|
|
495
65
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
66
|
+
const requestedRoot=path.resolve(arcaneRoot);
|
|
67
|
+
let rootInfo;
|
|
68
|
+
try{rootInfo=await inspect(requestedRoot);}
|
|
69
|
+
catch(error){
|
|
70
|
+
if(error?.code==='ENOENT')fail('The selected Arcane OS checkout does not exist.',{
|
|
71
|
+
arcaneRoot:requestedRoot
|
|
500
72
|
});
|
|
73
|
+
throw error;
|
|
501
74
|
}
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
|
75
|
+
if(rootInfo.isSymbolicLink()||!rootInfo.isDirectory()){
|
|
76
|
+
fail('The Arcane OS checkout root must be a real directory.',{arcaneRoot:requestedRoot});
|
|
77
|
+
}
|
|
78
|
+
const canonicalRoot=await canonicalize(requestedRoot);
|
|
79
|
+
let current=canonicalRoot;
|
|
80
|
+
for(const [index,segment] of relativeProviderPath.entries()){
|
|
81
|
+
current=path.join(current,segment);
|
|
82
|
+
let info;
|
|
83
|
+
try{info=await inspect(current);}
|
|
84
|
+
catch(error){
|
|
85
|
+
if(error?.code==='ENOENT')fail('The selected Arcane native provider does not exist.',{
|
|
86
|
+
providerPath:current
|
|
573
87
|
});
|
|
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
88
|
throw error;
|
|
585
89
|
}
|
|
586
|
-
if(
|
|
587
|
-
|
|
588
|
-
|
|
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
|
|
90
|
+
if(info.isSymbolicLink()){
|
|
91
|
+
fail('The Arcane native provider path must not contain links or junctions.',{
|
|
92
|
+
providerPath:current
|
|
597
93
|
});
|
|
598
94
|
}
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
|
95
|
+
const last=index===relativeProviderPath.length-1;
|
|
96
|
+
if(last?!info.isFile():!info.isDirectory()){
|
|
97
|
+
fail('The Arcane native provider path contains a non-file entry.',{
|
|
98
|
+
providerPath:current
|
|
612
99
|
});
|
|
613
100
|
}
|
|
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
101
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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
|
-
);
|
|
102
|
+
const canonicalProvider=await canonicalize(current);
|
|
103
|
+
if(!insideRoot(canonicalRoot,canonicalProvider)){
|
|
104
|
+
fail('The Arcane native provider resolves outside its checkout.',{
|
|
105
|
+
providerPath:canonicalProvider
|
|
106
|
+
});
|
|
822
107
|
}
|
|
823
|
-
return
|
|
108
|
+
return {canonicalRoot,providerPath:canonicalProvider};
|
|
824
109
|
}
|
|
825
110
|
|
|
826
|
-
function
|
|
827
|
-
|
|
828
|
-
|
|
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
|
-
};
|
|
111
|
+
export async function loadArcaneNativeProvider(options={}){
|
|
112
|
+
if(options===null||typeof options!=='object'||Array.isArray(options)){
|
|
113
|
+
fail('Arcane native provider options must be an object.');
|
|
853
114
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
115
|
+
const {
|
|
116
|
+
arcaneRoot,
|
|
117
|
+
target,
|
|
118
|
+
inspect=lstat,
|
|
119
|
+
canonicalize=realpath,
|
|
120
|
+
importModule=importProviderModule,
|
|
121
|
+
signal,
|
|
122
|
+
onEvent
|
|
123
|
+
}=options;
|
|
124
|
+
throwIfAborted(signal);
|
|
125
|
+
if(typeof importModule!=='function')fail('The Arcane native provider importer is invalid.');
|
|
126
|
+
await onEvent?.({
|
|
127
|
+
type:'native.provider.load.started',
|
|
128
|
+
target,
|
|
129
|
+
message:`Loading the Arcane ${String(target)} provider.`
|
|
865
130
|
});
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
record.importAttempted=true;
|
|
869
|
-
|
|
131
|
+
const location=await resolveProviderLocation({arcaneRoot,target,inspect,canonicalize});
|
|
132
|
+
throwIfAborted(signal);
|
|
870
133
|
let namespace;
|
|
871
|
-
let importError;
|
|
872
|
-
let rawBuilder;
|
|
873
|
-
let importCompleted=false;
|
|
874
134
|
try{
|
|
875
|
-
namespace=await
|
|
876
|
-
importCompleted=true;
|
|
877
|
-
rawBuilder=validateNativeBuilder(
|
|
878
|
-
namespace?.arcaneNativeBuilderProvider??namespace?.default
|
|
879
|
-
);
|
|
135
|
+
namespace=await importModule(pathToFileURL(location.providerPath).href);
|
|
880
136
|
}catch(error){
|
|
881
|
-
|
|
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;
|
|
137
|
+
if(error instanceof ArcaneError)throw error;
|
|
912
138
|
throw new ArcaneError(
|
|
913
139
|
ERROR_CODES.targetUnavailable,
|
|
914
|
-
`The Arcane ${
|
|
915
|
-
{cause:
|
|
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)}
|
|
140
|
+
`The Arcane ${target} provider could not be loaded: ${error.message}`,
|
|
141
|
+
{cause:error,details:{providerPath:location.providerPath}}
|
|
1034
142
|
);
|
|
1035
143
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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
|
-
}
|
|
144
|
+
const nativeBuilder=validateNativeBuilder(
|
|
145
|
+
namespace?.arcaneNativeBuilderProvider??namespace?.default
|
|
146
|
+
);
|
|
1057
147
|
throwIfAborted(signal);
|
|
1058
|
-
|
|
148
|
+
const pairing={
|
|
149
|
+
arcaneRoot:location.canonicalRoot,
|
|
150
|
+
toolchainRoot:location.canonicalRoot,
|
|
151
|
+
providerPath:location.providerPath,
|
|
152
|
+
nativeBuilder
|
|
153
|
+
};
|
|
154
|
+
await onEvent?.({
|
|
1059
155
|
type:'native.provider.load.completed',
|
|
1060
156
|
target,
|
|
1061
|
-
message:`The Arcane ${target} provider is
|
|
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,
|
|
157
|
+
message:`The Arcane ${String(target)} provider is ready.`
|
|
1121
158
|
});
|
|
159
|
+
return pairing;
|
|
1122
160
|
}
|
|
1123
161
|
|
|
1124
162
|
export function loadArcanePortableProvider(options={}){
|
|
163
|
+
if(options===null||typeof options!=='object'||Array.isArray(options)){
|
|
164
|
+
fail('Arcane portable provider options must be an object.');
|
|
165
|
+
}
|
|
1125
166
|
return loadArcaneNativeProvider({...options,target:'portable'});
|
|
1126
167
|
}
|