arcane-os 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +855 -895
- package/browser-runtime/ai/browser-speech-providers.mjs +80 -204
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -148
- package/browser-runtime/ai/speech-worker-runtime.mjs +642 -374
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1042 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +618 -359
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +27 -67
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +109 -1558
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -185
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1295
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -719
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2965
- package/docs/reference/sdk-api.md +0 -6698
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
package/src/native-plan.mjs
CHANGED
|
@@ -1,50 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {lstat,realpath} from 'node:fs/promises';
|
|
1
|
+
import {lstat,readFile,realpath} from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
projectNativeDescriptor,
|
|
7
|
-
projectPackageManifest,
|
|
8
|
-
validateAppDescriptor
|
|
9
|
-
} from './app-descriptor.mjs';
|
|
10
|
-
import {
|
|
11
|
-
authenticateAppReleaseAuthority,
|
|
12
|
-
parseSemver,
|
|
13
|
-
readVerifiedAppReleaseFile
|
|
14
|
-
} from './packager/core.mjs';
|
|
3
|
+
import {validateAppDescriptor} from './app-descriptor.mjs';
|
|
4
|
+
import {normalizeRelativePath} from './packager/core.mjs';
|
|
15
5
|
import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
|
|
16
6
|
|
|
17
7
|
export const NATIVE_BUILD_PLAN_PROTOCOL='arcane-native-build-plan/1';
|
|
18
8
|
export const NATIVE_BUILDER_PROTOCOL='arcane-native-builder/1';
|
|
19
9
|
|
|
20
|
-
const NATIVE_TARGETS=
|
|
21
|
-
portable:
|
|
10
|
+
const NATIVE_TARGETS={
|
|
11
|
+
portable:{
|
|
22
12
|
platforms:new Set(['windows','linux']),
|
|
23
13
|
architectures:new Set(['x64','arm64']),
|
|
24
14
|
format:'portable',
|
|
25
15
|
signingMode:'unsigned-local-test',
|
|
26
16
|
signingProfileId:null
|
|
27
|
-
}
|
|
28
|
-
'windows-x64':
|
|
17
|
+
},
|
|
18
|
+
'windows-x64':{
|
|
29
19
|
platforms:new Set(['windows']),architectures:new Set(['x64']),format:'exe',
|
|
30
20
|
signingMode:'unsigned-local-test',signingProfileId:null
|
|
31
|
-
}
|
|
32
|
-
'linux-x64':
|
|
21
|
+
},
|
|
22
|
+
'linux-x64':{
|
|
33
23
|
platforms:new Set(['linux']),architectures:new Set(['x64']),format:'deb',
|
|
34
24
|
signingMode:'unsigned-local-test',signingProfileId:null
|
|
35
|
-
}
|
|
36
|
-
'linux-arm64':
|
|
25
|
+
},
|
|
26
|
+
'linux-arm64':{
|
|
37
27
|
platforms:new Set(['linux']),architectures:new Set(['arm64']),format:'deb',
|
|
38
28
|
signingMode:'unsigned-local-test',signingProfileId:null
|
|
39
|
-
}
|
|
40
|
-
'android-arm64':
|
|
29
|
+
},
|
|
30
|
+
'android-arm64':{
|
|
41
31
|
platforms:new Set(['android']),architectures:new Set(['arm64']),format:'apk',
|
|
42
32
|
signingMode:'development',signingProfileId:'arcane-android-development-v1'
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const SHA256_PATTERN=/^[a-f0-9]{64}$/u;
|
|
46
|
-
const issuedPlans=new WeakMap();
|
|
47
|
-
const attemptedPlans=new WeakSet();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
48
35
|
|
|
49
36
|
function fail(message,code=ERROR_CODES.policyDenied,details){
|
|
50
37
|
throw new ArcaneError(code,message,{details});
|
|
@@ -54,469 +41,140 @@ function isObject(value){
|
|
|
54
41
|
return value!==null&&typeof value==='object'&&!Array.isArray(value);
|
|
55
42
|
}
|
|
56
43
|
|
|
57
|
-
function
|
|
58
|
-
if(
|
|
59
|
-
for(const key of Object.keys(value)){
|
|
60
|
-
if(!allowed.has(key))fail(`${label} contains an unsupported key: ${key}.`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function immutable(value){
|
|
65
|
-
if(Array.isArray(value))return Object.freeze(value.map(immutable));
|
|
66
|
-
if(isObject(value))return Object.freeze(Object.fromEntries(
|
|
67
|
-
Object.entries(value).map(([key,item])=>[key,immutable(item)])
|
|
68
|
-
));
|
|
69
|
-
return value;
|
|
44
|
+
async function emit(onEvent,event){
|
|
45
|
+
if(typeof onEvent==='function')await onEvent(event);
|
|
70
46
|
}
|
|
71
47
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
for(const key of ['major','minor','patch']){
|
|
76
|
-
if(left[key]!==right[key])return left[key]<right[key]?-1:1;
|
|
77
|
-
}
|
|
78
|
-
if(!left.prerelease.length&&!right.prerelease.length)return 0;
|
|
79
|
-
if(!left.prerelease.length)return 1;
|
|
80
|
-
if(!right.prerelease.length)return -1;
|
|
81
|
-
const length=Math.max(left.prerelease.length,right.prerelease.length);
|
|
82
|
-
for(let index=0;index<length;index+=1){
|
|
83
|
-
const leftPart=left.prerelease[index];
|
|
84
|
-
const rightPart=right.prerelease[index];
|
|
85
|
-
if(leftPart===undefined)return -1;
|
|
86
|
-
if(rightPart===undefined)return 1;
|
|
87
|
-
if(leftPart===rightPart)continue;
|
|
88
|
-
const leftNumeric=/^\d+$/u.test(leftPart);
|
|
89
|
-
const rightNumeric=/^\d+$/u.test(rightPart);
|
|
90
|
-
if(leftNumeric&&rightNumeric){
|
|
91
|
-
if(leftPart.length!==rightPart.length)return leftPart.length<rightPart.length?-1:1;
|
|
92
|
-
return leftPart<rightPart?-1:1;
|
|
93
|
-
}
|
|
94
|
-
if(leftNumeric!==rightNumeric)return leftNumeric?-1:1;
|
|
95
|
-
return leftPart<rightPart?-1:1;
|
|
96
|
-
}
|
|
97
|
-
return 0;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function canonicalContractList(value,label,{maximum=4096}={}){
|
|
101
|
-
if(!Array.isArray(value)||value.length>maximum||value.some(item=>typeof item!=='string'||!item)){
|
|
102
|
-
fail(`${label} must be a bounded, unique, sorted string array.`,ERROR_CODES.integrityFailed);
|
|
103
|
-
}
|
|
104
|
-
const sorted=[...value].sort((left,right)=>left.localeCompare(right,'en'));
|
|
105
|
-
if(new Set(value).size!==value.length||value.some((item,index)=>item!==sorted[index])){
|
|
106
|
-
fail(`${label} must be a bounded, unique, sorted string array.`,ERROR_CODES.integrityFailed);
|
|
107
|
-
}
|
|
108
|
-
return value;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function assertRequirementsAvailable(required,available,label){
|
|
112
|
-
if(!Array.isArray(available)){
|
|
113
|
-
fail(`The native toolchain receipt does not bind supported ${label}.`,ERROR_CODES.integrityFailed);
|
|
114
|
-
}
|
|
115
|
-
const missing=required.filter(value=>!available.includes(value));
|
|
116
|
-
if(missing.length){
|
|
117
|
-
fail(
|
|
118
|
-
`The native toolchain does not provide required ${label}: ${missing.join(', ')}.`,
|
|
119
|
-
ERROR_CODES.targetUnavailable
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function assertNativeToolchainCompatibility({
|
|
125
|
-
appDescriptor,
|
|
126
|
-
toolchainReceipt,
|
|
127
|
-
minimumCoreVersion
|
|
128
|
-
}={}){
|
|
129
|
-
const descriptor=validateAppDescriptor(appDescriptor,{appId:appDescriptor?.id});
|
|
130
|
-
const requirements=descriptor.requirements;
|
|
131
|
-
if(!isObject(toolchainReceipt)||typeof toolchainReceipt.version!=='string'){
|
|
132
|
-
fail('Native compatibility inputs are incomplete.',ERROR_CODES.integrityFailed);
|
|
133
|
-
}
|
|
134
|
-
if(toolchainReceipt.protocolVersion!==requirements.arcaneProtocol){
|
|
135
|
-
fail(
|
|
136
|
-
`The app requires ${requirements.arcaneProtocol}, but the native toolchain provides ${String(toolchainReceipt.protocolVersion??'unknown')}.`,
|
|
137
|
-
ERROR_CODES.targetUnavailable
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
const requiredVersions=[requirements.minimumCoreVersion];
|
|
141
|
-
if(minimumCoreVersion!==undefined)requiredVersions.push(minimumCoreVersion);
|
|
142
|
-
for(const requiredVersion of requiredVersions){
|
|
143
|
-
try{
|
|
144
|
-
if(typeof requiredVersion!=='string'||compareSemver(toolchainReceipt.version,requiredVersion)<0){
|
|
145
|
-
fail(
|
|
146
|
-
`Arcane Core ${toolchainReceipt.version} does not meet the required minimum ${String(requiredVersion??'unknown')}.`,
|
|
147
|
-
ERROR_CODES.targetUnavailable
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
}catch(error){
|
|
151
|
-
if(error instanceof ArcaneError)throw error;
|
|
152
|
-
fail('Native compatibility contains an invalid Arcane version.',ERROR_CODES.integrityFailed);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
assertRequirementsAvailable(requirements.features,toolchainReceipt.features,'features');
|
|
156
|
-
assertRequirementsAvailable(
|
|
157
|
-
descriptor.permissions.capabilities,
|
|
158
|
-
toolchainReceipt.supportedCapabilities,
|
|
159
|
-
'capabilities'
|
|
160
|
-
);
|
|
161
|
-
assertRequirementsAvailable(
|
|
162
|
-
descriptor.permissions.methods,
|
|
163
|
-
toolchainReceipt.supportedMethods,
|
|
164
|
-
'methods'
|
|
165
|
-
);
|
|
166
|
-
return descriptor;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function highestCoreMinimum(versions){
|
|
170
|
-
let highest=null;
|
|
171
|
-
for(const version of versions){
|
|
172
|
-
if(version===undefined)continue;
|
|
173
|
-
try{
|
|
174
|
-
parseSemver(version);
|
|
175
|
-
}catch(error){
|
|
176
|
-
fail('Native compatibility contains an invalid minimum Core version.',ERROR_CODES.integrityFailed);
|
|
177
|
-
}
|
|
178
|
-
if(highest===null||compareSemver(version,highest)>0)highest=version;
|
|
179
|
-
}
|
|
180
|
-
if(highest===null)fail('Native compatibility has no minimum Core version.',ERROR_CODES.integrityFailed);
|
|
181
|
-
return highest;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function sameOrDescendant(parent,candidate){
|
|
185
|
-
const relative=path.relative(parent,candidate);
|
|
186
|
-
return relative===''||Boolean(relative&&!relative.startsWith('..')&&!path.isAbsolute(relative));
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function overlaps(left,right){
|
|
190
|
-
return sameOrDescendant(left,right)||sameOrDescendant(right,left);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async function canonicalDirectory(value,label){
|
|
194
|
-
if(typeof value!=='string'||!value.trim())fail(`${label} is required.`,ERROR_CODES.usage);
|
|
195
|
-
const requested=path.resolve(value);
|
|
48
|
+
async function realDirectory(location,label){
|
|
49
|
+
if(typeof location!=='string'||!location.trim())fail(`${label} is required.`,ERROR_CODES.usage);
|
|
50
|
+
const requested=path.resolve(location);
|
|
196
51
|
let info;
|
|
197
52
|
try{info=await lstat(requested);}
|
|
198
53
|
catch(error){
|
|
199
|
-
if(error?.code==='ENOENT')fail(`${label} does not exist: ${requested}
|
|
54
|
+
if(error?.code==='ENOENT')fail(`${label} does not exist: ${requested}.`,ERROR_CODES.prerequisiteMissing);
|
|
200
55
|
throw error;
|
|
201
56
|
}
|
|
202
|
-
if(info.isSymbolicLink()||!info.isDirectory())
|
|
203
|
-
|
|
57
|
+
if(info.isSymbolicLink()||!info.isDirectory()){
|
|
58
|
+
fail(`${label} must be a real directory.`,ERROR_CODES.policyDenied);
|
|
59
|
+
}
|
|
60
|
+
const canonical=await realpath(requested);
|
|
61
|
+
const canonicalInfo=await lstat(canonical);
|
|
62
|
+
if(canonicalInfo.isSymbolicLink()||!canonicalInfo.isDirectory()){
|
|
63
|
+
fail(`${label} must be a real directory.`,ERROR_CODES.policyDenied);
|
|
64
|
+
}
|
|
65
|
+
return canonical;
|
|
204
66
|
}
|
|
205
67
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const missing=[];
|
|
211
|
-
while(true){
|
|
212
|
-
try{
|
|
213
|
-
const info=await lstat(cursor);
|
|
214
|
-
if(info.isSymbolicLink()||!info.isDirectory())fail(`${label} has a linked or non-directory ancestor.`);
|
|
215
|
-
const canonical=await realpath(cursor);
|
|
216
|
-
return path.join(canonical,...missing.reverse());
|
|
217
|
-
}catch(error){
|
|
218
|
-
if(error?.code!=='ENOENT')throw error;
|
|
219
|
-
const parent=path.dirname(cursor);
|
|
220
|
-
if(parent===cursor)throw error;
|
|
221
|
-
missing.push(path.basename(cursor));
|
|
222
|
-
cursor=parent;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
68
|
+
function inside(root,candidate,{allowEqual=false}={}){
|
|
69
|
+
const relative=path.relative(path.resolve(root),path.resolve(candidate));
|
|
70
|
+
return (allowEqual&&relative==='')
|
|
71
|
+
||Boolean(relative&&!relative.startsWith('..')&&!path.isAbsolute(relative));
|
|
225
72
|
}
|
|
226
73
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if(!isObject(provider)||provider.protocol!==NATIVE_BUILDER_PROTOCOL
|
|
230
|
-
||methods.some(method=>typeof provider[method]!=='function')){
|
|
231
|
-
fail(`Native builder must implement ${NATIVE_BUILDER_PROTOCOL} and its complete operation contract.`,ERROR_CODES.targetUnavailable);
|
|
232
|
-
}
|
|
233
|
-
return provider;
|
|
74
|
+
function overlap(left,right){
|
|
75
|
+
return inside(left,right,{allowEqual:true})||inside(right,left,{allowEqual:true});
|
|
234
76
|
}
|
|
235
77
|
|
|
236
|
-
function
|
|
237
|
-
|
|
78
|
+
function selectedTargetRequest(value){
|
|
79
|
+
if(!isObject(value))fail('targetRequest must be an object.',ERROR_CODES.usage);
|
|
238
80
|
const definition=NATIVE_TARGETS[value.target];
|
|
239
81
|
if(!definition)fail(`Unsupported native target: ${String(value.target)}.`,ERROR_CODES.targetUnavailable);
|
|
240
|
-
if(!definition.platforms.has(value.platform)
|
|
241
|
-
|
|
242
|
-
fail(`Target ${value.target} does not support ${value.platform}/${value.architecture}/${value.format}.`,ERROR_CODES.targetUnavailable);
|
|
82
|
+
if(!definition.platforms.has(value.platform)){
|
|
83
|
+
fail(`Target ${value.target} does not support platform ${String(value.platform)}.`,ERROR_CODES.targetUnavailable);
|
|
243
84
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
fail(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
);
|
|
85
|
+
if(!definition.architectures.has(value.architecture)){
|
|
86
|
+
fail(`Target ${value.target} does not support architecture ${String(value.architecture)}.`,ERROR_CODES.targetUnavailable);
|
|
87
|
+
}
|
|
88
|
+
if(value.format!==definition.format){
|
|
89
|
+
fail(`Target ${value.target} requires format ${definition.format}.`,ERROR_CODES.targetUnavailable);
|
|
90
|
+
}
|
|
91
|
+
if(value.signing!==definition.signingMode){
|
|
92
|
+
fail(`Target ${value.target} requires signing mode ${definition.signingMode}.`,ERROR_CODES.targetUnavailable);
|
|
93
|
+
}
|
|
94
|
+
if((value.signingProfileId??null)!==definition.signingProfileId){
|
|
95
|
+
fail(`Target ${value.target} has a different signing profile.`,ERROR_CODES.targetUnavailable);
|
|
255
96
|
}
|
|
256
|
-
return
|
|
97
|
+
return {
|
|
257
98
|
target:value.target,
|
|
258
99
|
platform:value.platform,
|
|
259
100
|
architecture:value.architecture,
|
|
260
101
|
format:value.format,
|
|
261
|
-
signing:
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function releaseApp(receipt){
|
|
266
|
-
return isObject(receipt?.app)?receipt.app:null;
|
|
102
|
+
signing:value.signing,
|
|
103
|
+
signingProfileId:value.signingProfileId??null
|
|
104
|
+
};
|
|
267
105
|
}
|
|
268
106
|
|
|
269
|
-
function
|
|
270
|
-
|
|
271
|
-
if(
|
|
272
|
-
|
|
273
|
-
||!SHA256_PATTERN.test(receipt.contentSha256)){
|
|
274
|
-
fail('The app release receipt is missing its verified identity.',ERROR_CODES.integrityFailed);
|
|
275
|
-
}
|
|
276
|
-
return immutable({
|
|
277
|
-
canonicalLocation:receipt.canonicalLocation,
|
|
278
|
-
policySha256:receipt.policySha256,
|
|
279
|
-
contentSha256:receipt.contentSha256,
|
|
280
|
-
fileCount:receipt.fileCount,
|
|
281
|
-
totalBytes:receipt.totalBytes
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
async function authenticateReleaseAuthority(
|
|
286
|
-
receipt,
|
|
287
|
-
releaseRoot,
|
|
288
|
-
signal,
|
|
289
|
-
expectedPackageConfig,
|
|
290
|
-
expectedDescriptor
|
|
291
|
-
){
|
|
292
|
-
try{
|
|
293
|
-
return await authenticateAppReleaseAuthority(receipt,{
|
|
294
|
-
releaseRoot,
|
|
295
|
-
expectedPackageConfig,
|
|
296
|
-
expectedDescriptor,
|
|
297
|
-
signal
|
|
298
|
-
});
|
|
299
|
-
}catch(error){
|
|
300
|
-
if(error?.code===ERROR_CODES.cancelled)throw error;
|
|
301
|
-
throw new ArcaneError(
|
|
302
|
-
ERROR_CODES.integrityFailed,
|
|
303
|
-
`App release authentication failed: ${error instanceof Error?error.message:String(error)}`,
|
|
304
|
-
{cause:error}
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
async function authenticatedRelease(item,targetRequest,{signal}){
|
|
310
|
-
assertOnlyKeys(
|
|
311
|
-
item,
|
|
312
|
-
new Set(['appDescriptor','appReleaseRoot','appReleaseReceipt']),
|
|
313
|
-
'application release input'
|
|
314
|
-
);
|
|
315
|
-
const presentedDescriptor=validateAppDescriptor(
|
|
316
|
-
item.appDescriptor,
|
|
317
|
-
{appId:item.appDescriptor?.id}
|
|
318
|
-
);
|
|
319
|
-
const root=await canonicalDirectory(
|
|
320
|
-
item.appReleaseRoot,
|
|
321
|
-
`Release root for ${presentedDescriptor.id}`
|
|
322
|
-
);
|
|
323
|
-
const authority=await authenticateReleaseAuthority(
|
|
324
|
-
item.appReleaseReceipt,
|
|
325
|
-
root,
|
|
326
|
-
signal,
|
|
327
|
-
projectPackageManifest(presentedDescriptor),
|
|
328
|
-
presentedDescriptor
|
|
329
|
-
);
|
|
330
|
-
const receipt=authority.receipt;
|
|
331
|
-
const descriptor=validateAppDescriptor(authority.descriptor,{
|
|
332
|
-
appId:presentedDescriptor.id
|
|
333
|
-
});
|
|
334
|
-
if(appDescriptorSha256(descriptor)!==authority.descriptorSha256){
|
|
335
|
-
fail('App release descriptor authority hash is invalid.',ERROR_CODES.integrityFailed);
|
|
336
|
-
}
|
|
337
|
-
projectNativeDescriptor(descriptor);
|
|
338
|
-
if(!descriptor.targets.includes(targetRequest.target)){
|
|
339
|
-
fail(`App ${descriptor.id} does not declare target ${targetRequest.target}.`);
|
|
340
|
-
}
|
|
341
|
-
const app=releaseApp(receipt);
|
|
342
|
-
if(app?.id!==descriptor.id||app?.version!==descriptor.version){
|
|
343
|
-
fail(`Release identity for ${descriptor.id} does not match its canonical descriptor.`,ERROR_CODES.integrityFailed);
|
|
107
|
+
export function validateNativeBuilder(provider){
|
|
108
|
+
if(!isObject(provider))fail('Native builder must be an object.',ERROR_CODES.usage);
|
|
109
|
+
if(provider.protocol!==NATIVE_BUILDER_PROTOCOL){
|
|
110
|
+
fail(`Native builder protocol must be ${NATIVE_BUILDER_PROTOCOL}.`,ERROR_CODES.targetUnavailable);
|
|
344
111
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
releaseRoot:root,
|
|
349
|
-
releaseReceipt:receipt,
|
|
350
|
-
readFile:(relativePath,{signal}={})=>readVerifiedAppReleaseFile(receipt,{
|
|
351
|
-
releaseRoot:root,
|
|
352
|
-
relativePath,
|
|
353
|
-
signal
|
|
354
|
-
}),
|
|
355
|
-
summary:immutable({
|
|
356
|
-
id:descriptor.id,
|
|
357
|
-
version:descriptor.version,
|
|
358
|
-
descriptorSha256:authority.descriptorSha256,
|
|
359
|
-
release:releaseSummary(receipt)
|
|
360
|
-
})
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function assertDependencyClosure(application,dependencies){
|
|
365
|
-
const byId=new Map(dependencies.map(item=>[item.descriptor.id,item]));
|
|
366
|
-
if(byId.size!==dependencies.length)fail('Dependency releases must have unique app ids.');
|
|
367
|
-
if(byId.has(application.descriptor.id))fail('The selected app must not also be supplied as a dependency.');
|
|
368
|
-
const reachable=new Set();
|
|
369
|
-
const visiting=new Set();
|
|
370
|
-
function visit(descriptor){
|
|
371
|
-
if(visiting.has(descriptor.id))fail(`Bundled app dependency cycle includes ${descriptor.id}.`);
|
|
372
|
-
visiting.add(descriptor.id);
|
|
373
|
-
for(const id of descriptor.native.bundledApps){
|
|
374
|
-
const dependency=byId.get(id);
|
|
375
|
-
if(!dependency)fail(`Bundled app dependency ${id} is missing its verified release.`);
|
|
376
|
-
if(visiting.has(id))fail(`Bundled app dependency cycle includes ${id}.`);
|
|
377
|
-
if(!reachable.has(id)){
|
|
378
|
-
reachable.add(id);
|
|
379
|
-
visit(dependency.descriptor);
|
|
380
|
-
}
|
|
112
|
+
for(const method of ['describe','doctor','prepare','build','verify','run']){
|
|
113
|
+
if(typeof provider[method]!=='function'){
|
|
114
|
+
fail(`Native builder must implement ${method}().`,ERROR_CODES.targetUnavailable);
|
|
381
115
|
}
|
|
382
|
-
visiting.delete(descriptor.id);
|
|
383
|
-
}
|
|
384
|
-
visit(application.descriptor);
|
|
385
|
-
const extra=[...byId.keys()].filter(id=>!reachable.has(id));
|
|
386
|
-
if(extra.length)fail(`Unrequested dependency releases were supplied: ${extra.sort().join(', ')}.`);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function toolchainSummary(receipt,toolchainRoot){
|
|
390
|
-
if(!isObject(receipt)||receipt.canonicalLocation!==toolchainRoot
|
|
391
|
-
||typeof receipt.kind!=='string'||!receipt.kind
|
|
392
|
-
||typeof receipt.version!=='string'||!receipt.version
|
|
393
|
-
||typeof receipt.protocolVersion!=='string'||!/^arcane\/[1-9][0-9]*$/u.test(receipt.protocolVersion)
|
|
394
|
-
||!SHA256_PATTERN.test(receipt.contentSha256)){
|
|
395
|
-
fail('The native builder returned an invalid authenticated toolchain receipt.',ERROR_CODES.integrityFailed);
|
|
396
|
-
}
|
|
397
|
-
const features=canonicalContractList(receipt.features,'toolchainReceipt.features',{maximum:256});
|
|
398
|
-
const supportedCapabilities=canonicalContractList(
|
|
399
|
-
receipt.supportedCapabilities,
|
|
400
|
-
'toolchainReceipt.supportedCapabilities'
|
|
401
|
-
);
|
|
402
|
-
const supportedMethods=canonicalContractList(receipt.supportedMethods,'toolchainReceipt.supportedMethods');
|
|
403
|
-
return immutable({
|
|
404
|
-
kind:receipt.kind,
|
|
405
|
-
version:receipt.version,
|
|
406
|
-
protocolVersion:receipt.protocolVersion,
|
|
407
|
-
features,
|
|
408
|
-
supportedCapabilities,
|
|
409
|
-
supportedMethods,
|
|
410
|
-
canonicalLocation:receipt.canonicalLocation,
|
|
411
|
-
contentSha256:receipt.contentSha256
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
function providerGenerationSummary(value,{optional=false}={}){
|
|
416
|
-
if(value===undefined&&optional)return null;
|
|
417
|
-
if(!isObject(value)
|
|
418
|
-
||value.kind!=='arcane-native-provider-generation'
|
|
419
|
-
||!SHA256_PATTERN.test(value.generationSha256)
|
|
420
|
-
||!SHA256_PATTERN.test(value.contentSha256)
|
|
421
|
-
||!Number.isInteger(value.moduleCount)
|
|
422
|
-
||value.moduleCount<1
|
|
423
|
-
||value.moduleCount>128){
|
|
424
|
-
fail(
|
|
425
|
-
'Native provider generation evidence is invalid.',
|
|
426
|
-
ERROR_CODES.integrityFailed
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
return immutable({
|
|
430
|
-
kind:'arcane-native-provider-generation',
|
|
431
|
-
generationSha256:value.generationSha256,
|
|
432
|
-
contentSha256:value.contentSha256,
|
|
433
|
-
moduleCount:value.moduleCount
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
function assertProviderGenerationBinding(summary,provider,label){
|
|
438
|
-
if(!summary)return;
|
|
439
|
-
const actual=providerGenerationSummary(provider?.providerGeneration,{optional:true});
|
|
440
|
-
if(!actual||JSON.stringify(actual)!==JSON.stringify(summary)){
|
|
441
|
-
fail(
|
|
442
|
-
`${label} belongs to a different native provider module generation.`,
|
|
443
|
-
ERROR_CODES.integrityFailed
|
|
444
|
-
);
|
|
445
116
|
}
|
|
117
|
+
return provider;
|
|
446
118
|
}
|
|
447
119
|
|
|
448
|
-
function
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
'Native build plan'
|
|
453
|
-
);
|
|
454
|
-
if(expectedNativeBuilder!==undefined){
|
|
455
|
-
assertProviderGenerationBinding(
|
|
456
|
-
state.providerGeneration,
|
|
457
|
-
expectedNativeBuilder,
|
|
458
|
-
'Expected native builder'
|
|
459
|
-
);
|
|
120
|
+
async function releaseSelection(releaseRoot,release,label){
|
|
121
|
+
const root=await realDirectory(releaseRoot,`${label} root`);
|
|
122
|
+
if(!isObject(release)||!Array.isArray(release.files)){
|
|
123
|
+
fail(`${label} must provide its structural file inventory.`,ERROR_CODES.usage);
|
|
460
124
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
125
|
+
const files=release.files.map((entry,index)=>normalizeRelativePath(
|
|
126
|
+
entry,
|
|
127
|
+
`${label}.files[${index}]`
|
|
128
|
+
));
|
|
129
|
+
if(new Set(files.map(file=>file.toLocaleLowerCase('en-US'))).size!==files.length){
|
|
130
|
+
fail(`${label} contains duplicate file paths.`,ERROR_CODES.usage);
|
|
131
|
+
}
|
|
132
|
+
async function readSelectedFile(relativePath,{signal}={}){
|
|
133
|
+
throwIfAborted(signal);
|
|
134
|
+
const normalized=normalizeRelativePath(relativePath,`${label} file`);
|
|
135
|
+
if(!files.some(file=>file.toLocaleLowerCase('en-US')===normalized.toLocaleLowerCase('en-US'))){
|
|
136
|
+
fail(`${label} does not contain ${normalized}.`,ERROR_CODES.usage);
|
|
137
|
+
}
|
|
138
|
+
const selected=path.resolve(root,...normalized.split('/'));
|
|
139
|
+
if(!inside(root,selected))fail(`${label} file leaves its root.`,ERROR_CODES.policyDenied);
|
|
140
|
+
const info=await lstat(selected);
|
|
141
|
+
if(info.isSymbolicLink()||!info.isFile()){
|
|
142
|
+
fail(`${label} file must be a real file: ${normalized}.`,ERROR_CODES.policyDenied);
|
|
143
|
+
}
|
|
144
|
+
const canonical=await realpath(selected);
|
|
145
|
+
if(!inside(root,canonical))fail(`${label} file resolves outside its root.`,ERROR_CODES.policyDenied);
|
|
146
|
+
return readFile(canonical);
|
|
469
147
|
}
|
|
148
|
+
return {root,release:{...release,files:[...files]},readFile:readSelectedFile};
|
|
470
149
|
}
|
|
471
150
|
|
|
472
|
-
async function
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
appDescriptor:item.descriptor,
|
|
486
|
-
toolchainReceipt:summary,
|
|
487
|
-
minimumCoreVersion:state.plan.minimumCoreVersion
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
await authenticateReleaseAuthority(
|
|
491
|
-
state.application.releaseReceipt,
|
|
492
|
-
state.application.releaseRoot,
|
|
493
|
-
signal,
|
|
494
|
-
projectPackageManifest(state.application.descriptor),
|
|
495
|
-
state.application.descriptor
|
|
496
|
-
);
|
|
497
|
-
for(const dependency of state.dependencies){
|
|
498
|
-
await authenticateReleaseAuthority(
|
|
499
|
-
dependency.releaseReceipt,
|
|
500
|
-
dependency.releaseRoot,
|
|
501
|
-
signal,
|
|
502
|
-
projectPackageManifest(dependency.descriptor),
|
|
503
|
-
dependency.descriptor
|
|
151
|
+
async function dependencySelections(value,{signal}={}){
|
|
152
|
+
if(value===undefined)return [];
|
|
153
|
+
if(!Array.isArray(value))fail('dependencyReleases must be an array.',ERROR_CODES.usage);
|
|
154
|
+
const dependencies=[];
|
|
155
|
+
for(const [index,item] of value.entries()){
|
|
156
|
+
throwIfAborted(signal);
|
|
157
|
+
if(!isObject(item)||typeof item.appId!=='string'){
|
|
158
|
+
fail(`dependencyReleases[${index}] is malformed.`,ERROR_CODES.usage);
|
|
159
|
+
}
|
|
160
|
+
const selected=await releaseSelection(
|
|
161
|
+
item.releaseRoot,
|
|
162
|
+
item.release,
|
|
163
|
+
`dependency release ${item.appId}`
|
|
504
164
|
);
|
|
165
|
+
dependencies.push({appId:item.appId,...selected});
|
|
505
166
|
}
|
|
506
|
-
|
|
507
|
-
if(outputRoot!==state.outputRoot)fail('The native output root identity changed after plan creation.',ERROR_CODES.integrityFailed);
|
|
508
|
-
return state.plan;
|
|
167
|
+
return dependencies;
|
|
509
168
|
}
|
|
510
169
|
|
|
511
170
|
export async function createNativeBuildPlan({
|
|
512
171
|
nativeBuilder,
|
|
513
172
|
toolchainRoot,
|
|
514
|
-
|
|
173
|
+
toolchain,
|
|
515
174
|
appReleaseRoot,
|
|
516
|
-
|
|
175
|
+
release,
|
|
517
176
|
appDescriptor,
|
|
518
|
-
dependencyReleases
|
|
519
|
-
providerGeneration,
|
|
177
|
+
dependencyReleases,
|
|
520
178
|
minimumCoreVersion,
|
|
521
179
|
protectedRoots=[],
|
|
522
180
|
outputRoot,
|
|
@@ -525,174 +183,81 @@ export async function createNativeBuildPlan({
|
|
|
525
183
|
onEvent
|
|
526
184
|
}={}){
|
|
527
185
|
throwIfAborted(signal);
|
|
528
|
-
await onEvent?.(Object.freeze({type:'native.plan.started'}));
|
|
529
|
-
const request=validateTargetRequest(targetRequest);
|
|
530
186
|
const provider=validateNativeBuilder(nativeBuilder);
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
);
|
|
541
|
-
const authenticatedToolchainSummary=toolchainSummary(authenticatedToolchain,canonicalToolchainRoot);
|
|
542
|
-
const application=await authenticatedRelease({
|
|
543
|
-
appDescriptor,
|
|
544
|
-
appReleaseRoot,
|
|
545
|
-
appReleaseReceipt
|
|
546
|
-
},request,{signal});
|
|
547
|
-
if(!Array.isArray(dependencyReleases)||dependencyReleases.length>64){
|
|
548
|
-
fail('dependencyReleases must be an array with at most 64 entries.');
|
|
187
|
+
const selectedToolchainRoot=await realDirectory(toolchainRoot,'Native toolchain root');
|
|
188
|
+
if(!isObject(toolchain))fail('Native toolchain preparation result is required.',ERROR_CODES.usage);
|
|
189
|
+
const descriptor=JSON.parse(JSON.stringify(
|
|
190
|
+
validateAppDescriptor(appDescriptor,{appId:appDescriptor?.id})
|
|
191
|
+
));
|
|
192
|
+
const application=await releaseSelection(appReleaseRoot,release,'application release');
|
|
193
|
+
const dependencies=await dependencySelections(dependencyReleases,{signal});
|
|
194
|
+
if(!Array.isArray(protectedRoots)){
|
|
195
|
+
fail('protectedRoots must be an array.',ERROR_CODES.usage);
|
|
549
196
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
dependencies.push(await authenticatedRelease(dependency,request,{signal}));
|
|
197
|
+
if(typeof outputRoot!=='string'||!outputRoot.trim()){
|
|
198
|
+
fail('outputRoot is required.',ERROR_CODES.usage);
|
|
553
199
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
minimumCoreVersion,
|
|
558
|
-
application.descriptor.requirements.minimumCoreVersion,
|
|
559
|
-
...dependencies.map(item=>item.descriptor.requirements.minimumCoreVersion)
|
|
560
|
-
]);
|
|
561
|
-
for(const item of [application,...dependencies]){
|
|
562
|
-
assertNativeToolchainCompatibility({
|
|
563
|
-
appDescriptor:item.descriptor,
|
|
564
|
-
toolchainReceipt:authenticatedToolchainSummary,
|
|
565
|
-
minimumCoreVersion:requiredCoreVersion
|
|
566
|
-
});
|
|
200
|
+
const selectedOutput=path.resolve(outputRoot);
|
|
201
|
+
if(protectedRoots.some(root=>overlap(path.resolve(root),selectedOutput))){
|
|
202
|
+
fail('Native output root overlaps a protected source directory.',ERROR_CODES.policyDenied);
|
|
567
203
|
}
|
|
568
|
-
|
|
569
|
-
const
|
|
570
|
-
const protectedPaths=[
|
|
571
|
-
canonicalToolchainRoot,
|
|
572
|
-
application.releaseRoot,
|
|
573
|
-
...dependencies.map(item=>item.releaseRoot)
|
|
574
|
-
];
|
|
575
|
-
if(!Array.isArray(protectedRoots))fail('protectedRoots must be an array.');
|
|
576
|
-
for(const [index,root] of protectedRoots.entries()){
|
|
577
|
-
protectedPaths.push(await canonicalDirectory(root,`Protected source root ${index}`));
|
|
578
|
-
}
|
|
579
|
-
const conflict=protectedPaths.find(root=>overlaps(root,canonicalOutputRoot));
|
|
580
|
-
if(conflict)fail('Native output root must not overlap a toolchain, source, or verified release root.');
|
|
581
|
-
|
|
582
|
-
const plan=immutable({
|
|
204
|
+
const request=selectedTargetRequest(targetRequest);
|
|
205
|
+
const plan={
|
|
583
206
|
protocol:NATIVE_BUILD_PLAN_PROTOCOL,
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
toolchain:
|
|
587
|
-
|
|
588
|
-
app:application.summary,
|
|
589
|
-
dependencies:dependencies.map(item=>item.summary),
|
|
590
|
-
targetRequest:request,
|
|
591
|
-
outputRoot:canonicalOutputRoot,
|
|
592
|
-
operations:[
|
|
593
|
-
'authenticate-toolchain',
|
|
594
|
-
'authenticate-app-release',
|
|
595
|
-
'authenticate-dependency-releases',
|
|
596
|
-
'build-selected-target',
|
|
597
|
-
'verify-native-artifact'
|
|
598
|
-
]
|
|
599
|
-
});
|
|
600
|
-
issuedPlans.set(plan,Object.freeze({
|
|
601
|
-
plan,
|
|
602
|
-
provider,
|
|
603
|
-
providerGeneration:generation,
|
|
604
|
-
toolchainRoot:canonicalToolchainRoot,
|
|
605
|
-
toolchainReceipt,
|
|
606
|
-
minimumCoreVersion:requiredCoreVersion,
|
|
207
|
+
nativeBuilder:provider,
|
|
208
|
+
toolchainRoot:selectedToolchainRoot,
|
|
209
|
+
toolchain:{...toolchain},
|
|
210
|
+
appDescriptor:descriptor,
|
|
607
211
|
application,
|
|
608
|
-
dependencies
|
|
609
|
-
|
|
212
|
+
dependencies,
|
|
213
|
+
minimumCoreVersion:minimumCoreVersion??null,
|
|
214
|
+
protectedRoots:protectedRoots.map(root=>path.resolve(root)),
|
|
215
|
+
outputRoot:selectedOutput,
|
|
610
216
|
targetRequest:request
|
|
611
|
-
}
|
|
612
|
-
await onEvent
|
|
217
|
+
};
|
|
218
|
+
await emit(onEvent,{type:'native.plan.created',target:request.target,outputRoot:selectedOutput});
|
|
613
219
|
return plan;
|
|
614
220
|
}
|
|
615
221
|
|
|
616
|
-
export async function authenticateNativeBuildPlan(plan,{
|
|
617
|
-
expectedNativeBuilder,
|
|
618
|
-
expectedTarget,
|
|
619
|
-
signal,
|
|
620
|
-
onEvent
|
|
621
|
-
}={}){
|
|
622
|
-
const state=issuedPlans.get(plan);
|
|
623
|
-
if(!state)fail('Native build plan was not issued by this SDK process.',ERROR_CODES.integrityFailed);
|
|
624
|
-
return authenticateState(state,{expectedNativeBuilder,expectedTarget,signal,onEvent});
|
|
625
|
-
}
|
|
626
|
-
|
|
627
222
|
export async function executeNativeBuildPlan(plan,{
|
|
628
223
|
expectedNativeBuilder,
|
|
629
224
|
expectedTarget,
|
|
630
225
|
signal,
|
|
631
226
|
onEvent
|
|
632
227
|
}={}){
|
|
633
|
-
const state=issuedPlans.get(plan);
|
|
634
|
-
if(!state)fail('Native build plan was not issued by this SDK process.',ERROR_CODES.integrityFailed);
|
|
635
228
|
throwIfAborted(signal);
|
|
636
|
-
if(
|
|
637
|
-
fail(
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
229
|
+
if(!isObject(plan)||plan.protocol!==NATIVE_BUILD_PLAN_PROTOCOL){
|
|
230
|
+
fail('Native build plan is malformed.',ERROR_CODES.usage);
|
|
231
|
+
}
|
|
232
|
+
const provider=validateNativeBuilder(plan.nativeBuilder);
|
|
233
|
+
if(expectedNativeBuilder!==undefined&&provider!==expectedNativeBuilder){
|
|
234
|
+
fail('Native build plan belongs to a different provider.',ERROR_CODES.usage);
|
|
235
|
+
}
|
|
236
|
+
if(expectedTarget!==undefined&&plan.targetRequest?.target!==expectedTarget){
|
|
237
|
+
fail('Native build plan targets a different platform.',ERROR_CODES.usage);
|
|
238
|
+
}
|
|
239
|
+
await emit(onEvent,{type:'native.build.started',target:plan.targetRequest.target});
|
|
240
|
+
const result=await provider.build({
|
|
241
|
+
toolchainRoot:plan.toolchainRoot,
|
|
242
|
+
toolchain:plan.toolchain,
|
|
243
|
+
appDescriptor:plan.appDescriptor,
|
|
244
|
+
appReleaseRoot:plan.application.root,
|
|
245
|
+
release:plan.application.release,
|
|
246
|
+
readReleaseFile:plan.application.readFile,
|
|
247
|
+
dependencies:plan.dependencies.map(dependency=>({
|
|
248
|
+
appId:dependency.appId,
|
|
249
|
+
releaseRoot:dependency.root,
|
|
250
|
+
release:dependency.release,
|
|
251
|
+
readReleaseFile:dependency.readFile
|
|
659
252
|
})),
|
|
660
|
-
|
|
661
|
-
|
|
253
|
+
minimumCoreVersion:plan.minimumCoreVersion,
|
|
254
|
+
protectedRoots:[...plan.protectedRoots],
|
|
255
|
+
outputRoot:plan.outputRoot,
|
|
256
|
+
targetRequest:{...plan.targetRequest},
|
|
662
257
|
signal,
|
|
663
258
|
onEvent
|
|
664
259
|
});
|
|
665
|
-
if(!isObject(result)||!result.artifactReceipt){
|
|
666
|
-
fail('Native builder did not return an artifact receipt.',ERROR_CODES.integrityFailed);
|
|
667
|
-
}
|
|
668
|
-
await onEvent?.(Object.freeze({
|
|
669
|
-
type:'native.build.committed',
|
|
670
|
-
appId:plan.app.id,
|
|
671
|
-
target:plan.targetRequest.target
|
|
672
|
-
}));
|
|
673
|
-
await onEvent?.(Object.freeze({
|
|
674
|
-
type:'native.verify.started',
|
|
675
|
-
appId:plan.app.id,
|
|
676
|
-
target:plan.targetRequest.target
|
|
677
|
-
}));
|
|
678
|
-
const artifactVerification=await state.provider.verify({
|
|
679
|
-
toolchainRoot:state.toolchainRoot,
|
|
680
|
-
toolchainReceipt:state.toolchainReceipt,
|
|
681
|
-
artifactReceipt:result.artifactReceipt,
|
|
682
|
-
outputRoot:state.outputRoot,
|
|
683
|
-
targetRequest:state.targetRequest,
|
|
684
|
-
signal,
|
|
685
|
-
onEvent
|
|
686
|
-
});
|
|
687
|
-
if(!isObject(artifactVerification)||artifactVerification.verified!==true){
|
|
688
|
-
fail('Native builder did not verify the built artifact.',ERROR_CODES.integrityFailed);
|
|
689
|
-
}
|
|
690
260
|
throwIfAborted(signal);
|
|
691
|
-
await onEvent
|
|
692
|
-
|
|
693
|
-
appId:plan.app.id,
|
|
694
|
-
target:plan.targetRequest.target
|
|
695
|
-
}));
|
|
696
|
-
await onEvent?.(Object.freeze({type:'native.build.completed',appId:plan.app.id,target:plan.targetRequest.target}));
|
|
697
|
-
return {...result,artifactVerification};
|
|
261
|
+
await emit(onEvent,{type:'native.build.completed',target:plan.targetRequest.target});
|
|
262
|
+
return result;
|
|
698
263
|
}
|