@pulse-compute/wasm-contracts 0.0.0 → 1.0.0-beta.1
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/README.md +34 -2
- package/package.json +123 -6
- package/src/assets/contracts.js +296 -0
- package/src/config/provider-resolution.js +54 -0
- package/src/config/runtime-provider-kv.js +73 -0
- package/src/config/symbolic-refs.js +192 -0
- package/src/crypto/contracts.js +637 -0
- package/src/definitions/config-schema.js +30 -0
- package/src/definitions/handler-roles.js +66 -0
- package/src/definitions/path-grammar.js +11 -0
- package/src/definitions/router-api.js +146 -0
- package/src/diagnostics/codes.js +42 -0
- package/src/diagnostics/reporter.js +20 -0
- package/src/diagnostics.js +139 -0
- package/src/dispatch-table.js +23 -0
- package/src/entities/catalog.d.ts +7 -0
- package/src/entities/catalog.js +87 -0
- package/src/entities/contracts.d.ts +95 -0
- package/src/entities/contracts.js +122 -0
- package/src/entities/json-rpc.d.ts +27 -0
- package/src/entities/json-rpc.js +69 -0
- package/src/entities/runtime.d.ts +67 -0
- package/src/entities/runtime.js +288 -0
- package/src/events/contracts.js +456 -0
- package/src/execution-plan.js +42 -0
- package/src/grip/contracts.js +106 -0
- package/src/handler/backend-json-request-body.js +58 -0
- package/src/handler/beta-lifecycle-golden.js +97 -0
- package/src/handler/canonical-native-plan.js +143 -0
- package/src/handler/canonical-native-runtime.js +168 -0
- package/src/handler/canonical-runtime.js +13 -0
- package/src/handler/context.js +131 -0
- package/src/handler/ctx-contract.js +71 -0
- package/src/handler/doctor-readiness-aggregator.js +67 -0
- package/src/handler/effects.js +446 -0
- package/src/handler/io-lifecycle.js +134 -0
- package/src/handler/lifecycle-authoring-alignment.js +52 -0
- package/src/handler/lifecycle-readiness-aggregate.js +93 -0
- package/src/handler/reference-node-lifecycle-golden.js +73 -0
- package/src/handler/request-json-body.js +147 -0
- package/src/handler/runtime-api.js +471 -0
- package/src/handler/schema-decode-result.js +135 -0
- package/src/handler/schema-response-codec.js +141 -0
- package/src/handler/surface-contract.js +594 -0
- package/src/handler/types-contract.js +51 -0
- package/src/handler-table.js +28 -0
- package/src/host/backend-capabilities.js +24 -0
- package/src/host/capabilities.js +273 -0
- package/src/host/channel-broadcaster.js +60 -0
- package/src/host/json-body.js +80 -0
- package/src/host/request-result-headers.js +38 -0
- package/src/host/streaming-passthrough.js +90 -0
- package/src/host/wasm-host-abi.js +154 -0
- package/src/index.js +75 -0
- package/src/jwt/contracts.js +427 -0
- package/src/library/api-surface.js +99 -0
- package/src/library/contracts.js +273 -0
- package/src/library/manifest.js +317 -0
- package/src/library/second-package-owned-lowering.js +39 -0
- package/src/library/sidecars.js +147 -0
- package/src/logging.js +89 -0
- package/src/package/package-contract.js +975 -0
- package/src/package/package-contract.schema.json +303 -0
- package/src/package/runtime-pulse-live.js +100 -0
- package/src/path-table.js +12 -0
- package/src/path.js +275 -0
- package/src/project/config-factory.js +48 -0
- package/src/project/config-plan.js +409 -0
- package/src/project/config-runtime.js +46 -0
- package/src/project/javascript-application.js +212 -0
- package/src/project/javascript-core-capabilities.js +44 -0
- package/src/project/native-optimization.js +9 -0
- package/src/project/package-ownership.js +242 -0
- package/src/project/package-product-projection.js +171 -0
- package/src/project/reachable-graph-identity.js +219 -0
- package/src/project/reachable-graph-projections.js +279 -0
- package/src/project/reachable-graph-resolver.js +303 -0
- package/src/project/reachable-graph-v2.js +430 -0
- package/src/project/reachable-graph-v2.schema.json +285 -0
- package/src/project/reachable-graph.js +199 -0
- package/src/project/reachable-graph.schema.json +222 -0
- package/src/project/target-support-evidence.js +559 -0
- package/src/provider/canonical-provider.js +235 -0
- package/src/provider/final-wasm-policy.js +87 -0
- package/src/provider/toolchain.js +771 -0
- package/src/route-plan.js +43 -0
- package/src/schema-json/compile.js +63 -0
- package/src/schema-json/contracts.js +10 -0
- package/src/schema-json/generic-parser.js +62 -0
- package/src/schema-json/registry.js +359 -0
- package/src/schema-json/semantic-trace.js +241 -0
- package/src/schema-json/v1.js +65 -0
- package/src/schema-json/v2.js +110 -0
- package/src/sha256.js +104 -0
- package/src/spine.js +381 -0
- package/src/stable-id.js +142 -0
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const {
|
|
5
|
+
CANONICAL_PROVIDER_CONTRACT_VERSION,
|
|
6
|
+
CANONICAL_PROVIDER_PLAN_VERSION,
|
|
7
|
+
normalizeDescriptor
|
|
8
|
+
} = require('./canonical-provider.js');
|
|
9
|
+
const { defineFinalWasmPolicy } = require('./final-wasm-policy.js');
|
|
10
|
+
const {
|
|
11
|
+
CANONICAL_NATIVE_PLAN_VERSION
|
|
12
|
+
} = require('../handler/canonical-native-plan.js');
|
|
13
|
+
const {
|
|
14
|
+
JAVASCRIPT_APPLICATION_PLAN_VERSION,
|
|
15
|
+
JAVASCRIPT_TARGET_DESCRIPTOR_VERSION
|
|
16
|
+
} = require('../project/javascript-application.js');
|
|
17
|
+
|
|
18
|
+
const PROVIDER_TOOLCHAIN_VERSION = 'pulse.provider-toolchain.v1';
|
|
19
|
+
const PROVIDER_DRIVER_VERSION = 'pulse.provider-driver.v1';
|
|
20
|
+
const PROVIDER_TARGET_DESCRIPTOR_VERSION = 'pulse.provider-target-descriptor.v1';
|
|
21
|
+
const PROVIDER_TARGET_INVOCATION_VERSION = 'pulse.provider-target-invocation.v1';
|
|
22
|
+
const PROVIDER_TARGET_RESULT_VERSION = 'pulse.provider-target-result.v1';
|
|
23
|
+
const PROVIDER_REQUIREMENTS_VERSION = 'pulse.provider-requirements.v1';
|
|
24
|
+
const PROVIDER_NATIVE_ARTIFACT_VERSION = 'pulse.provider-native-artifact.v1';
|
|
25
|
+
const PROVIDER_PACKAGING_AUDIT_VERSION = 'pulse.provider-packaging-audit.v1';
|
|
26
|
+
const PROVIDER_JAVASCRIPT_PACKAGE_RESULT_VERSION = 'pulse.provider-javascript-package-result.v1';
|
|
27
|
+
const PROVIDER_PLAN_INPUT_VERSION = 'pulse.provider-plan-input.v1';
|
|
28
|
+
|
|
29
|
+
const TOOLCHAIN_FIELDS = Object.freeze([
|
|
30
|
+
'version', 'id', 'packageName', 'packageVersion', 'createDriver'
|
|
31
|
+
]);
|
|
32
|
+
const DRIVER_FIELDS = Object.freeze([
|
|
33
|
+
'version',
|
|
34
|
+
'id',
|
|
35
|
+
'descriptor',
|
|
36
|
+
'executable',
|
|
37
|
+
'localExecution',
|
|
38
|
+
'deployable',
|
|
39
|
+
'deploymentValidated',
|
|
40
|
+
'sourcePackage',
|
|
41
|
+
'compiledWasm',
|
|
42
|
+
'defaultBuildMode',
|
|
43
|
+
'sourceOnlySupported',
|
|
44
|
+
'targetSupport',
|
|
45
|
+
'normalizeConfig',
|
|
46
|
+
'defaultLocalNetworkFetch',
|
|
47
|
+
'projectConfigDocument',
|
|
48
|
+
'configReference',
|
|
49
|
+
'initTemplate',
|
|
50
|
+
'javascript',
|
|
51
|
+
'events',
|
|
52
|
+
'targets',
|
|
53
|
+
'execute',
|
|
54
|
+
'createLoweringPlan',
|
|
55
|
+
'inspectRealization',
|
|
56
|
+
'writeTarget',
|
|
57
|
+
'executionOptions'
|
|
58
|
+
]);
|
|
59
|
+
const TARGET_FIELDS = Object.freeze([
|
|
60
|
+
'version',
|
|
61
|
+
'id',
|
|
62
|
+
'provider',
|
|
63
|
+
'mode',
|
|
64
|
+
'target',
|
|
65
|
+
'targetId',
|
|
66
|
+
'runtimeClass',
|
|
67
|
+
'status',
|
|
68
|
+
'sourceApplication',
|
|
69
|
+
'automaticFallback',
|
|
70
|
+
'hostBridge',
|
|
71
|
+
'applicationLoader',
|
|
72
|
+
'requestAdapter',
|
|
73
|
+
'lifecycle',
|
|
74
|
+
'capabilities',
|
|
75
|
+
'keyTypes',
|
|
76
|
+
'realizations',
|
|
77
|
+
'jwt',
|
|
78
|
+
'crypto',
|
|
79
|
+
'finalWasmPolicy',
|
|
80
|
+
'reporting',
|
|
81
|
+
'commands'
|
|
82
|
+
]);
|
|
83
|
+
const JAVASCRIPT_DRIVER_FIELDS = Object.freeze([
|
|
84
|
+
'kind',
|
|
85
|
+
'localEmulation',
|
|
86
|
+
'buildTimeLoader',
|
|
87
|
+
'localExecutionMode',
|
|
88
|
+
'realityRunner',
|
|
89
|
+
'targetSupportPolicy',
|
|
90
|
+
'validateApplication',
|
|
91
|
+
'loadApplication',
|
|
92
|
+
'writeSourcePackage',
|
|
93
|
+
'describeSourcePackage',
|
|
94
|
+
'executeTestCase',
|
|
95
|
+
'createServer',
|
|
96
|
+
'createFixtureFetch',
|
|
97
|
+
'createLocalEnvironment',
|
|
98
|
+
'createTestRequest',
|
|
99
|
+
'executeLocalRequest'
|
|
100
|
+
]);
|
|
101
|
+
const EVENT_DRIVER_FIELDS = Object.freeze(['targets', 'executeTestCase']);
|
|
102
|
+
const COMMAND_FIELDS = Object.freeze(['compile', 'inspect', 'doctor', 'build', 'test', 'dev']);
|
|
103
|
+
const INVOCATION_FIELDS = Object.freeze([
|
|
104
|
+
'version',
|
|
105
|
+
'action',
|
|
106
|
+
'provider',
|
|
107
|
+
'selectedTarget',
|
|
108
|
+
'applicationPlan',
|
|
109
|
+
'providerPlan',
|
|
110
|
+
'providerConfig',
|
|
111
|
+
'requirements',
|
|
112
|
+
'nativeArtifact',
|
|
113
|
+
'javascript',
|
|
114
|
+
'project',
|
|
115
|
+
'synchronizedPackages',
|
|
116
|
+
'optimization',
|
|
117
|
+
'timeoutMs'
|
|
118
|
+
]);
|
|
119
|
+
const RESULT_FIELDS = Object.freeze([
|
|
120
|
+
'version',
|
|
121
|
+
'action',
|
|
122
|
+
'provider',
|
|
123
|
+
'target',
|
|
124
|
+
'status',
|
|
125
|
+
'automaticFallback',
|
|
126
|
+
'files',
|
|
127
|
+
'build',
|
|
128
|
+
'realization',
|
|
129
|
+
'packaging'
|
|
130
|
+
]);
|
|
131
|
+
const JAVASCRIPT_PACKAGE_FIELDS = Object.freeze([
|
|
132
|
+
'version',
|
|
133
|
+
'outDir',
|
|
134
|
+
'entryFile',
|
|
135
|
+
'bundledApplicationFile',
|
|
136
|
+
'packageFile',
|
|
137
|
+
'applicationPlanFile',
|
|
138
|
+
'deploymentFile',
|
|
139
|
+
'deploymentCandidateFile',
|
|
140
|
+
'buildConfigFile',
|
|
141
|
+
'fastlyTomlFile',
|
|
142
|
+
'schemaRegistryFile',
|
|
143
|
+
'schemaCodecsFile',
|
|
144
|
+
'manifestFile',
|
|
145
|
+
'manifest',
|
|
146
|
+
'files'
|
|
147
|
+
]);
|
|
148
|
+
const NATIVE_ARTIFACT_FIELDS = Object.freeze([
|
|
149
|
+
'version',
|
|
150
|
+
'compilerVersion',
|
|
151
|
+
'wasm',
|
|
152
|
+
'wat',
|
|
153
|
+
'manifest',
|
|
154
|
+
'realizationArtifacts',
|
|
155
|
+
'guestUnits',
|
|
156
|
+
'guestAudit',
|
|
157
|
+
'finalArtifact'
|
|
158
|
+
]);
|
|
159
|
+
const PROVIDER_PLAN_FIELDS = Object.freeze([
|
|
160
|
+
'version',
|
|
161
|
+
'contractVersion',
|
|
162
|
+
'provider',
|
|
163
|
+
'providerVersion',
|
|
164
|
+
'package',
|
|
165
|
+
'runtime',
|
|
166
|
+
'buildTarget',
|
|
167
|
+
'deployable',
|
|
168
|
+
'localExecution',
|
|
169
|
+
'requirements',
|
|
170
|
+
'bindings',
|
|
171
|
+
'operations',
|
|
172
|
+
'providerSpecificUserland',
|
|
173
|
+
'providerSdkUserland',
|
|
174
|
+
'capabilityDiscoveryFromUserland'
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
function isRecord(value) {
|
|
178
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function nonEmpty(value, field) {
|
|
182
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
183
|
+
throw new TypeError(`Pulse provider toolchain ${field} must be a non-empty string.`);
|
|
184
|
+
}
|
|
185
|
+
return value.trim();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function assertExactFields(value, fields, label) {
|
|
189
|
+
if (!isRecord(value)) throw new TypeError(`${label} must be an object.`);
|
|
190
|
+
const allowed = new Set(fields);
|
|
191
|
+
const unknown = Object.keys(value).filter((field) => !allowed.has(field)).sort();
|
|
192
|
+
if (unknown.length > 0) throw new TypeError(`${label} contains unknown fields: ${unknown.join(', ')}.`);
|
|
193
|
+
return value;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function assertData(value, label, seen = new Set()) {
|
|
197
|
+
if (value === null || value === undefined) return;
|
|
198
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) return;
|
|
199
|
+
if (['string', 'number', 'boolean'].includes(typeof value)) return;
|
|
200
|
+
if (typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint') {
|
|
201
|
+
throw new TypeError(`${label} must contain data only.`);
|
|
202
|
+
}
|
|
203
|
+
if (typeof value !== 'object') throw new TypeError(`${label} contains an unsupported value.`);
|
|
204
|
+
if (seen.has(value)) throw new TypeError(`${label} must not contain cycles.`);
|
|
205
|
+
seen.add(value);
|
|
206
|
+
for (const [key, child] of Object.entries(value)) assertData(child, `${label}.${key}`, seen);
|
|
207
|
+
seen.delete(value);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function deepFreeze(value) {
|
|
211
|
+
if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
|
|
212
|
+
if (ArrayBuffer.isView(value)) return value;
|
|
213
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
214
|
+
return Object.freeze(value);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function assertDeepFrozen(value, label, seen = new Set()) {
|
|
218
|
+
if (!value || typeof value !== 'object' || ArrayBuffer.isView(value)) return;
|
|
219
|
+
if (seen.has(value)) throw new TypeError(`${label} must not contain cycles.`);
|
|
220
|
+
if (!Object.isFrozen(value)) throw new TypeError(`${label} must be immutable.`);
|
|
221
|
+
seen.add(value);
|
|
222
|
+
for (const [key, child] of Object.entries(value)) assertDeepFrozen(child, `${label}.${key}`, seen);
|
|
223
|
+
seen.delete(value);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function dataClone(value, label) {
|
|
227
|
+
assertData(value, label);
|
|
228
|
+
if (value === undefined || value === null) return value;
|
|
229
|
+
if (Buffer.isBuffer(value)) return Buffer.from(value);
|
|
230
|
+
if (value instanceof Uint8Array) return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
231
|
+
if (Array.isArray(value)) return Object.freeze(value.map((entry, index) => dataClone(entry, `${label}[${index}]`)));
|
|
232
|
+
if (typeof value === 'object') {
|
|
233
|
+
return Object.freeze(Object.fromEntries(Object.keys(value).sort()
|
|
234
|
+
.filter((key) => value[key] !== undefined)
|
|
235
|
+
.map((key) => [key, dataClone(value[key], `${label}.${key}`)])));
|
|
236
|
+
}
|
|
237
|
+
return value;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function sha256(value) {
|
|
241
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function commands(input, label) {
|
|
245
|
+
assertExactFields(input || {}, COMMAND_FIELDS, `${label} commands`);
|
|
246
|
+
return Object.freeze(Object.fromEntries(COMMAND_FIELDS.map((field) => [field, input && input[field] === true])));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function normalizeProviderTargetDescriptor(input, expected = {}) {
|
|
250
|
+
assertExactFields(input, TARGET_FIELDS, 'Pulse provider target descriptor');
|
|
251
|
+
const provider = nonEmpty(expected.provider || input.provider, 'target provider');
|
|
252
|
+
if (input.provider !== undefined && input.provider !== provider) {
|
|
253
|
+
throw new TypeError(`Pulse provider target descriptor provider mismatch: expected ${provider}, received ${String(input.provider)}.`);
|
|
254
|
+
}
|
|
255
|
+
const target = nonEmpty(expected.target || input.target || input.mode, 'target');
|
|
256
|
+
if (!['native', 'javascript'].includes(target)) throw new TypeError(`Pulse provider target ${target} is unsupported.`);
|
|
257
|
+
if (input.target !== undefined && input.target !== target) {
|
|
258
|
+
throw new TypeError(`Pulse provider target descriptor target mismatch: expected ${target}, received ${String(input.target)}.`);
|
|
259
|
+
}
|
|
260
|
+
const runtimeClass = nonEmpty(input.runtimeClass, 'target runtimeClass');
|
|
261
|
+
if (runtimeClass !== target) throw new TypeError(`Pulse provider target ${target} runtimeClass must be ${target}.`);
|
|
262
|
+
const expectedVersion = target === 'javascript'
|
|
263
|
+
? JAVASCRIPT_TARGET_DESCRIPTOR_VERSION
|
|
264
|
+
: PROVIDER_TARGET_DESCRIPTOR_VERSION;
|
|
265
|
+
if (input.version !== expectedVersion) {
|
|
266
|
+
throw new TypeError(`Pulse provider target ${provider}/${target} version must be ${expectedVersion}.`);
|
|
267
|
+
}
|
|
268
|
+
if (input.automaticFallback !== false) throw new TypeError(`Pulse provider target ${provider}/${target} must disable automatic fallback.`);
|
|
269
|
+
const normalizedCommands = commands(input.commands, `Pulse provider target ${provider}/${target}`);
|
|
270
|
+
const finalWasmPolicy = target === 'native' && input.status === 'supported'
|
|
271
|
+
? defineFinalWasmPolicy(input.finalWasmPolicy)
|
|
272
|
+
: (input.finalWasmPolicy === undefined ? undefined : defineFinalWasmPolicy(input.finalWasmPolicy));
|
|
273
|
+
return deepFreeze({
|
|
274
|
+
version: expectedVersion,
|
|
275
|
+
provider,
|
|
276
|
+
target,
|
|
277
|
+
targetId: nonEmpty(input.targetId || input.id, 'targetId'),
|
|
278
|
+
runtimeClass,
|
|
279
|
+
status: nonEmpty(input.status, 'target status'),
|
|
280
|
+
sourceApplication: target === 'javascript',
|
|
281
|
+
automaticFallback: false,
|
|
282
|
+
...(target === 'javascript' ? {
|
|
283
|
+
hostBridge: input.hostBridge === true,
|
|
284
|
+
applicationLoader: input.applicationLoader === true,
|
|
285
|
+
requestAdapter: input.requestAdapter === true,
|
|
286
|
+
lifecycle: input.lifecycle === true
|
|
287
|
+
} : {}),
|
|
288
|
+
...(input.capabilities !== undefined ? { capabilities: dataClone(input.capabilities, 'target capabilities') } : {}),
|
|
289
|
+
...(input.keyTypes !== undefined ? { keyTypes: dataClone(input.keyTypes, 'target keyTypes') } : {}),
|
|
290
|
+
...(input.realizations !== undefined ? { realizations: dataClone(input.realizations, 'target realizations') } : {}),
|
|
291
|
+
...(input.jwt !== undefined ? { jwt: dataClone(input.jwt, 'target jwt') } : {}),
|
|
292
|
+
...(input.crypto !== undefined ? { crypto: dataClone(input.crypto, 'target crypto') } : {}),
|
|
293
|
+
...(finalWasmPolicy ? { finalWasmPolicy } : {}),
|
|
294
|
+
...(input.reporting !== undefined ? { reporting: dataClone(input.reporting, 'target reporting') } : {}),
|
|
295
|
+
commands: normalizedCommands
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function normalizeJavascriptDriver(input, descriptor) {
|
|
300
|
+
if (input === undefined) {
|
|
301
|
+
if (descriptor && descriptor.commands.build) throw new TypeError('Pulse JavaScript target requires a provider JavaScript driver.');
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
assertExactFields(input, JAVASCRIPT_DRIVER_FIELDS, 'Pulse provider JavaScript driver');
|
|
305
|
+
if (descriptor && descriptor.commands.build && typeof input.writeSourcePackage !== 'function') {
|
|
306
|
+
throw new TypeError('Pulse provider JavaScript driver is missing writeSourcePackage().');
|
|
307
|
+
}
|
|
308
|
+
if (descriptor && descriptor.commands.build && typeof input.describeSourcePackage !== 'function') {
|
|
309
|
+
throw new TypeError('Pulse provider JavaScript driver is missing describeSourcePackage().');
|
|
310
|
+
}
|
|
311
|
+
if (input.targetSupportPolicy !== undefined && !isRecord(input.targetSupportPolicy)) {
|
|
312
|
+
throw new TypeError('Pulse provider JavaScript targetSupportPolicy must be an object.');
|
|
313
|
+
}
|
|
314
|
+
return Object.freeze({ ...input });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function normalizeEventDriver(input, targets) {
|
|
318
|
+
if (input === undefined) return undefined;
|
|
319
|
+
assertExactFields(input, EVENT_DRIVER_FIELDS, 'Pulse provider event driver');
|
|
320
|
+
if (!Array.isArray(input.targets) || input.targets.length === 0) {
|
|
321
|
+
throw new TypeError('Pulse provider event driver targets must be a non-empty array.');
|
|
322
|
+
}
|
|
323
|
+
const normalizedTargets = [...new Set(input.targets.map((target) => nonEmpty(target, 'event driver target')))].sort();
|
|
324
|
+
const unknownTargets = normalizedTargets.filter((target) => !['native', 'javascript'].includes(target));
|
|
325
|
+
if (unknownTargets.length > 0) throw new TypeError(`Pulse provider event driver declares unknown targets: ${unknownTargets.join(', ')}.`);
|
|
326
|
+
for (const target of normalizedTargets) {
|
|
327
|
+
if (!targets[target] || !targets[target].commands.test) {
|
|
328
|
+
throw new TypeError(`Pulse provider event driver target ${target} requires an implemented test command.`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (typeof input.executeTestCase !== 'function') {
|
|
332
|
+
throw new TypeError('Pulse provider event driver is missing executeTestCase().');
|
|
333
|
+
}
|
|
334
|
+
return Object.freeze({ targets: Object.freeze(normalizedTargets), executeTestCase: input.executeTestCase });
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function defineProviderDriver(input, expected = {}) {
|
|
338
|
+
assertExactFields(input, DRIVER_FIELDS, 'Pulse provider driver');
|
|
339
|
+
if (input.version !== PROVIDER_DRIVER_VERSION) {
|
|
340
|
+
throw new TypeError(`Pulse provider driver version must be ${PROVIDER_DRIVER_VERSION}.`);
|
|
341
|
+
}
|
|
342
|
+
const id = nonEmpty(input.id, 'driver id');
|
|
343
|
+
if (expected.id && id !== expected.id) {
|
|
344
|
+
throw new TypeError(`Pulse provider driver id mismatch: expected ${expected.id}, received ${id}.`);
|
|
345
|
+
}
|
|
346
|
+
for (const field of ['normalizeConfig', 'projectConfigDocument', 'initTemplate']) {
|
|
347
|
+
if (typeof input[field] !== 'function') throw new TypeError(`Pulse provider driver ${id} is missing ${field}().`);
|
|
348
|
+
}
|
|
349
|
+
if (!isRecord(input.targets)) throw new TypeError(`Pulse provider driver ${id} is missing target descriptors.`);
|
|
350
|
+
const unknownTargets = Object.keys(input.targets).filter((target) => !['native', 'javascript'].includes(target));
|
|
351
|
+
if (unknownTargets.length > 0) throw new TypeError(`Pulse provider driver ${id} declares unknown targets: ${unknownTargets.join(', ')}.`);
|
|
352
|
+
const targets = Object.freeze(Object.fromEntries(Object.entries(input.targets).map(([target, descriptor]) => [
|
|
353
|
+
target,
|
|
354
|
+
normalizeProviderTargetDescriptor(descriptor, { provider: id, target })
|
|
355
|
+
])));
|
|
356
|
+
const executable = input.executable === true;
|
|
357
|
+
if (executable && typeof input.createLoweringPlan !== 'function') {
|
|
358
|
+
throw new TypeError(`Pulse provider driver ${id} is missing createLoweringPlan().`);
|
|
359
|
+
}
|
|
360
|
+
if (targets.native && targets.native.commands.inspect && typeof input.inspectRealization !== 'function') {
|
|
361
|
+
throw new TypeError(`Pulse provider driver ${id} is missing inspectRealization().`);
|
|
362
|
+
}
|
|
363
|
+
if (targets.native && targets.native.commands.build && typeof input.writeTarget !== 'function') {
|
|
364
|
+
throw new TypeError(`Pulse provider driver ${id} is missing writeTarget().`);
|
|
365
|
+
}
|
|
366
|
+
if ((targets.native && (targets.native.commands.test || targets.native.commands.dev)) && typeof input.execute !== 'function') {
|
|
367
|
+
throw new TypeError(`Pulse provider driver ${id} is missing execute().`);
|
|
368
|
+
}
|
|
369
|
+
const descriptor = input.descriptor === undefined ? undefined : normalizeDescriptor(input.descriptor);
|
|
370
|
+
if (descriptor && descriptor.id !== id) throw new TypeError(`Pulse provider driver ${id} descriptor identity does not match.`);
|
|
371
|
+
const javascript = normalizeJavascriptDriver(input.javascript, targets.javascript);
|
|
372
|
+
const events = normalizeEventDriver(input.events, targets);
|
|
373
|
+
return Object.freeze({
|
|
374
|
+
version: PROVIDER_DRIVER_VERSION,
|
|
375
|
+
id,
|
|
376
|
+
descriptor,
|
|
377
|
+
executable,
|
|
378
|
+
localExecution: input.localExecution === true,
|
|
379
|
+
deployable: input.deployable === true,
|
|
380
|
+
deploymentValidated: input.deploymentValidated === true,
|
|
381
|
+
sourcePackage: input.sourcePackage === true,
|
|
382
|
+
compiledWasm: input.compiledWasm === true,
|
|
383
|
+
defaultBuildMode: nonEmpty(input.defaultBuildMode || 'compile-only', 'driver defaultBuildMode'),
|
|
384
|
+
sourceOnlySupported: input.sourceOnlySupported === true,
|
|
385
|
+
targetSupport: input.targetSupport === undefined ? undefined : dataClone(input.targetSupport, 'driver targetSupport'),
|
|
386
|
+
normalizeConfig: input.normalizeConfig,
|
|
387
|
+
defaultLocalNetworkFetch: input.defaultLocalNetworkFetch === true,
|
|
388
|
+
projectConfigDocument: input.projectConfigDocument,
|
|
389
|
+
configReference: input.configReference === undefined ? undefined : dataClone(input.configReference, 'driver configReference'),
|
|
390
|
+
initTemplate: input.initTemplate,
|
|
391
|
+
javascript,
|
|
392
|
+
events,
|
|
393
|
+
targets,
|
|
394
|
+
execute: input.execute,
|
|
395
|
+
createLoweringPlan: input.createLoweringPlan,
|
|
396
|
+
inspectRealization: input.inspectRealization,
|
|
397
|
+
writeTarget: input.writeTarget,
|
|
398
|
+
executionOptions: input.executionOptions
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function assertProviderDriver(value, expected = {}) {
|
|
403
|
+
return defineProviderDriver(value, expected);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function defineProviderToolchain(input) {
|
|
407
|
+
assertExactFields(input, TOOLCHAIN_FIELDS, 'Pulse provider toolchain');
|
|
408
|
+
if (input.version !== PROVIDER_TOOLCHAIN_VERSION) {
|
|
409
|
+
throw new TypeError(`Pulse provider toolchain version must be ${PROVIDER_TOOLCHAIN_VERSION}.`);
|
|
410
|
+
}
|
|
411
|
+
if (typeof input.createDriver !== 'function') {
|
|
412
|
+
throw new TypeError('Pulse provider toolchain createDriver must be a function.');
|
|
413
|
+
}
|
|
414
|
+
if (input.createDriver.length !== 0) {
|
|
415
|
+
throw new TypeError('Pulse provider toolchain createDriver must receive no compiler service or caller option bag.');
|
|
416
|
+
}
|
|
417
|
+
return Object.freeze({
|
|
418
|
+
version: PROVIDER_TOOLCHAIN_VERSION,
|
|
419
|
+
id: nonEmpty(input.id, 'id'),
|
|
420
|
+
packageName: nonEmpty(input.packageName, 'packageName'),
|
|
421
|
+
packageVersion: nonEmpty(input.packageVersion, 'packageVersion'),
|
|
422
|
+
createDriver: input.createDriver
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function assertProviderToolchain(value, expected = {}) {
|
|
427
|
+
const toolchain = defineProviderToolchain(value);
|
|
428
|
+
if (expected.packageName && toolchain.packageName !== expected.packageName) {
|
|
429
|
+
throw new TypeError(`Pulse provider toolchain package mismatch: expected ${expected.packageName}, received ${toolchain.packageName}.`);
|
|
430
|
+
}
|
|
431
|
+
if (expected.id && toolchain.id !== expected.id) {
|
|
432
|
+
throw new TypeError(`Pulse provider toolchain id mismatch: expected ${expected.id}, received ${toolchain.id}.`);
|
|
433
|
+
}
|
|
434
|
+
return toolchain;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function packagesForPlan(plan) {
|
|
438
|
+
const entries = plan && plan.target === 'javascript'
|
|
439
|
+
? plan.packages || []
|
|
440
|
+
: plan && plan.packages && plan.packages.effects || [];
|
|
441
|
+
const records = new Map();
|
|
442
|
+
for (const entry of entries) {
|
|
443
|
+
const packageName = entry.packageName || entry.package;
|
|
444
|
+
const contractId = entry.contractId;
|
|
445
|
+
if (!packageName && !contractId) continue;
|
|
446
|
+
const record = Object.freeze({
|
|
447
|
+
packageName: packageName === undefined ? null : String(packageName),
|
|
448
|
+
contractId: contractId === undefined ? null : String(contractId)
|
|
449
|
+
});
|
|
450
|
+
records.set(`${record.packageName || ''}\0${record.contractId || ''}`, record);
|
|
451
|
+
}
|
|
452
|
+
return Object.freeze([...records.values()].sort((left, right) => (
|
|
453
|
+
String(left.packageName).localeCompare(String(right.packageName))
|
|
454
|
+
|| String(left.contractId).localeCompare(String(right.contractId))
|
|
455
|
+
)));
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function createProviderRequirements(input) {
|
|
459
|
+
assertExactFields(input, ['version', 'capabilities', 'bindings', 'packages'], 'Pulse provider requirements');
|
|
460
|
+
if (input.version !== PROVIDER_REQUIREMENTS_VERSION) {
|
|
461
|
+
throw new TypeError(`Pulse provider requirements version must be ${PROVIDER_REQUIREMENTS_VERSION}.`);
|
|
462
|
+
}
|
|
463
|
+
if (!Array.isArray(input.capabilities)) throw new TypeError('Pulse provider requirements capabilities must be an array.');
|
|
464
|
+
if (!Array.isArray(input.packages)) throw new TypeError('Pulse provider requirements packages must be an array.');
|
|
465
|
+
return deepFreeze({
|
|
466
|
+
version: PROVIDER_REQUIREMENTS_VERSION,
|
|
467
|
+
capabilities: Object.freeze([...new Set(input.capabilities.map(String))].sort()),
|
|
468
|
+
bindings: dataClone(input.bindings || {}, 'provider requirement bindings'),
|
|
469
|
+
packages: dataClone(input.packages, 'provider package requirements')
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function createProviderPlanInput(input) {
|
|
474
|
+
assertExactFields(input, ['version', 'capabilities', 'providerOperations', 'opaqueReturnCount'], 'Pulse provider plan input');
|
|
475
|
+
if (input.version !== PROVIDER_PLAN_INPUT_VERSION) {
|
|
476
|
+
throw new TypeError(`Pulse provider plan input version must be ${PROVIDER_PLAN_INPUT_VERSION}.`);
|
|
477
|
+
}
|
|
478
|
+
if (!Array.isArray(input.capabilities)) throw new TypeError('Pulse provider plan input capabilities must be an array.');
|
|
479
|
+
if (!Array.isArray(input.providerOperations)) throw new TypeError('Pulse provider plan input providerOperations must be an array.');
|
|
480
|
+
const opaqueReturnCount = Number(input.opaqueReturnCount || 0);
|
|
481
|
+
if (!Number.isSafeInteger(opaqueReturnCount) || opaqueReturnCount < 0) {
|
|
482
|
+
throw new TypeError('Pulse provider plan input opaqueReturnCount must be a non-negative integer.');
|
|
483
|
+
}
|
|
484
|
+
return deepFreeze({
|
|
485
|
+
version: PROVIDER_PLAN_INPUT_VERSION,
|
|
486
|
+
capabilities: Object.freeze([...new Set(input.capabilities.map(String))].sort()),
|
|
487
|
+
providerOperations: dataClone(input.providerOperations, 'provider plan operations'),
|
|
488
|
+
opaqueReturnCount
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function normalizeCanonicalProviderPlan(input, expected = {}) {
|
|
493
|
+
assertExactFields(input, PROVIDER_PLAN_FIELDS, 'Pulse canonical provider plan');
|
|
494
|
+
if (input.version !== CANONICAL_PROVIDER_PLAN_VERSION) {
|
|
495
|
+
throw new TypeError(`Pulse canonical provider plan version must be ${CANONICAL_PROVIDER_PLAN_VERSION}.`);
|
|
496
|
+
}
|
|
497
|
+
if (input.contractVersion !== CANONICAL_PROVIDER_CONTRACT_VERSION) {
|
|
498
|
+
throw new TypeError(`Pulse canonical provider plan contractVersion must be ${CANONICAL_PROVIDER_CONTRACT_VERSION}.`);
|
|
499
|
+
}
|
|
500
|
+
const provider = nonEmpty(input.provider, 'canonical provider plan provider');
|
|
501
|
+
if (expected.provider && provider !== expected.provider) {
|
|
502
|
+
throw new TypeError(`Pulse canonical provider plan provider mismatch: expected ${expected.provider}, received ${provider}.`);
|
|
503
|
+
}
|
|
504
|
+
if (!Array.isArray(input.requirements)) throw new TypeError('Pulse canonical provider plan requirements must be an array.');
|
|
505
|
+
if (!isRecord(input.bindings)) throw new TypeError('Pulse canonical provider plan bindings must be an object.');
|
|
506
|
+
if (!Array.isArray(input.operations)) throw new TypeError('Pulse canonical provider plan operations must be an array.');
|
|
507
|
+
return dataClone(input, 'canonical provider plan');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function projectProviderRequirements(applicationPlan, providerPlan) {
|
|
511
|
+
const plan = normalizeCanonicalProviderPlan(providerPlan);
|
|
512
|
+
return createProviderRequirements({
|
|
513
|
+
version: PROVIDER_REQUIREMENTS_VERSION,
|
|
514
|
+
capabilities: plan.requirements,
|
|
515
|
+
bindings: plan.bindings,
|
|
516
|
+
packages: packagesForPlan(applicationPlan)
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function createProviderNativeArtifact(native) {
|
|
521
|
+
if (!isRecord(native) || !Buffer.isBuffer(native.wasm) || !isRecord(native.manifest)) {
|
|
522
|
+
throw new TypeError('Pulse provider Native artifact projection requires compiled Wasm and its manifest.');
|
|
523
|
+
}
|
|
524
|
+
const observedSha256 = sha256(native.wasm);
|
|
525
|
+
if (!native.manifest.wasm || native.manifest.wasm.sha256 !== observedSha256) {
|
|
526
|
+
throw new TypeError('Pulse provider Native artifact bytes do not match their manifest.');
|
|
527
|
+
}
|
|
528
|
+
let guestAudit = null;
|
|
529
|
+
if (native.guestLink) {
|
|
530
|
+
const audit = native.guestLink.audit;
|
|
531
|
+
const packaging = native.guestLink.providerPackaging;
|
|
532
|
+
const finalArtifact = native.guestLink.finalArtifact;
|
|
533
|
+
if (!audit || audit.status !== 'passed' || !audit.artifact || audit.artifact.sha256 !== observedSha256) {
|
|
534
|
+
throw new TypeError('Pulse provider Native artifact does not match the final guest-link audit.');
|
|
535
|
+
}
|
|
536
|
+
if (!packaging || packaging.authorized !== true || !finalArtifact || finalArtifact.sha256 !== observedSha256) {
|
|
537
|
+
throw new TypeError('Pulse provider Native artifact is not authorized for provider packaging.');
|
|
538
|
+
}
|
|
539
|
+
guestAudit = dataClone({ audit, finalArtifact, providerPackaging: packaging }, 'provider Native guest audit');
|
|
540
|
+
}
|
|
541
|
+
return deepFreeze({
|
|
542
|
+
version: PROVIDER_NATIVE_ARTIFACT_VERSION,
|
|
543
|
+
compilerVersion: nonEmpty(native.compilerVersion, 'Native artifact compilerVersion'),
|
|
544
|
+
wasm: Buffer.from(native.wasm),
|
|
545
|
+
wat: String(native.wat || ''),
|
|
546
|
+
manifest: dataClone({
|
|
547
|
+
compilerVersion: native.manifest.compilerVersion,
|
|
548
|
+
assemblyScript: native.manifest.assemblyScript,
|
|
549
|
+
optimization: native.manifest.optimization,
|
|
550
|
+
wasm: native.manifest.wasm,
|
|
551
|
+
wat: native.manifest.wat,
|
|
552
|
+
importModules: native.manifest.importModules,
|
|
553
|
+
imports: native.manifest.imports,
|
|
554
|
+
exports: native.manifest.exports,
|
|
555
|
+
packageRealizationArtifacts: native.manifest.packageRealizationArtifacts,
|
|
556
|
+
crypto: native.manifest.crypto,
|
|
557
|
+
policy: native.manifest.policy
|
|
558
|
+
}, 'provider Native manifest'),
|
|
559
|
+
realizationArtifacts: dataClone(native.realizationArtifacts || [], 'provider Native realization artifacts'),
|
|
560
|
+
guestUnits: dataClone(native.guestUnits || [], 'provider Native guest units'),
|
|
561
|
+
guestAudit,
|
|
562
|
+
finalArtifact: Object.freeze({
|
|
563
|
+
bytes: native.wasm.byteLength,
|
|
564
|
+
sha256: observedSha256,
|
|
565
|
+
magic: native.manifest.wasm.magic
|
|
566
|
+
})
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function assertProviderNativeArtifact(input) {
|
|
571
|
+
assertExactFields(input, NATIVE_ARTIFACT_FIELDS, 'Pulse provider Native artifact');
|
|
572
|
+
if (input.version !== PROVIDER_NATIVE_ARTIFACT_VERSION) {
|
|
573
|
+
throw new TypeError(`Pulse provider Native artifact version must be ${PROVIDER_NATIVE_ARTIFACT_VERSION}.`);
|
|
574
|
+
}
|
|
575
|
+
assertDeepFrozen(input, 'Pulse provider Native artifact');
|
|
576
|
+
if (!Buffer.isBuffer(input.wasm) || !isRecord(input.manifest) || !isRecord(input.finalArtifact)) {
|
|
577
|
+
throw new TypeError('Pulse provider Native artifact requires exact Wasm, manifest, and finalArtifact values.');
|
|
578
|
+
}
|
|
579
|
+
const observedSha256 = sha256(input.wasm);
|
|
580
|
+
if (
|
|
581
|
+
!input.manifest.wasm
|
|
582
|
+
|| input.manifest.wasm.sha256 !== observedSha256
|
|
583
|
+
|| input.finalArtifact.sha256 !== observedSha256
|
|
584
|
+
|| input.finalArtifact.bytes !== input.wasm.byteLength
|
|
585
|
+
) {
|
|
586
|
+
throw new TypeError('Pulse provider Native artifact identity does not match its exact bytes.');
|
|
587
|
+
}
|
|
588
|
+
if (input.guestAudit) {
|
|
589
|
+
const audit = input.guestAudit.audit;
|
|
590
|
+
const finalArtifact = input.guestAudit.finalArtifact;
|
|
591
|
+
const packaging = input.guestAudit.providerPackaging;
|
|
592
|
+
if (
|
|
593
|
+
!audit
|
|
594
|
+
|| audit.status !== 'passed'
|
|
595
|
+
|| !audit.artifact
|
|
596
|
+
|| audit.artifact.sha256 !== observedSha256
|
|
597
|
+
|| !finalArtifact
|
|
598
|
+
|| finalArtifact.sha256 !== observedSha256
|
|
599
|
+
|| !packaging
|
|
600
|
+
|| packaging.authorized !== true
|
|
601
|
+
) {
|
|
602
|
+
throw new TypeError('Pulse provider Native artifact is not the exact guest-audited packaging artifact.');
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return input;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function normalizeProject(input, action) {
|
|
609
|
+
assertExactFields(input, ['root', 'outDir', 'profile'], 'Pulse provider invocation project');
|
|
610
|
+
return Object.freeze({
|
|
611
|
+
root: nonEmpty(input.root, 'invocation project root'),
|
|
612
|
+
outDir: action === 'write-native' || action === 'write-javascript'
|
|
613
|
+
? nonEmpty(input.outDir, 'invocation output directory')
|
|
614
|
+
: null,
|
|
615
|
+
profile: nonEmpty(input.profile, 'invocation profile')
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function createProviderTargetInvocation(input) {
|
|
620
|
+
assertExactFields(input, INVOCATION_FIELDS, 'Pulse provider target invocation');
|
|
621
|
+
if (input.version !== PROVIDER_TARGET_INVOCATION_VERSION) {
|
|
622
|
+
throw new TypeError(`Pulse provider target invocation version must be ${PROVIDER_TARGET_INVOCATION_VERSION}.`);
|
|
623
|
+
}
|
|
624
|
+
const action = nonEmpty(input.action, 'invocation action');
|
|
625
|
+
if (!['inspect-native', 'write-native', 'write-javascript'].includes(action)) {
|
|
626
|
+
throw new TypeError(`Pulse provider target invocation action ${action} is unsupported.`);
|
|
627
|
+
}
|
|
628
|
+
const target = action.endsWith('javascript') ? 'javascript' : 'native';
|
|
629
|
+
const provider = nonEmpty(input.provider, 'invocation provider');
|
|
630
|
+
const selectedTarget = normalizeProviderTargetDescriptor(input.selectedTarget, { provider, target });
|
|
631
|
+
const expectedPlanVersion = target === 'native' ? CANONICAL_NATIVE_PLAN_VERSION : JAVASCRIPT_APPLICATION_PLAN_VERSION;
|
|
632
|
+
if (!input.applicationPlan || input.applicationPlan.version !== expectedPlanVersion) {
|
|
633
|
+
throw new TypeError(`Pulse provider ${target} invocation requires ${expectedPlanVersion}.`);
|
|
634
|
+
}
|
|
635
|
+
const applicationPlan = dataClone(input.applicationPlan, `canonical ${target} application plan`);
|
|
636
|
+
const providerPlan = normalizeCanonicalProviderPlan(input.providerPlan, { provider });
|
|
637
|
+
const requirements = createProviderRequirements(input.requirements);
|
|
638
|
+
const projectedRequirements = projectProviderRequirements(applicationPlan, providerPlan);
|
|
639
|
+
if (JSON.stringify(requirements) !== JSON.stringify(projectedRequirements)) {
|
|
640
|
+
throw new TypeError('Pulse provider invocation requirements do not match its canonical plans.');
|
|
641
|
+
}
|
|
642
|
+
const projected = {
|
|
643
|
+
version: PROVIDER_TARGET_INVOCATION_VERSION,
|
|
644
|
+
action,
|
|
645
|
+
provider,
|
|
646
|
+
selectedTarget,
|
|
647
|
+
applicationPlan,
|
|
648
|
+
providerPlan,
|
|
649
|
+
providerConfig: dataClone(input.providerConfig || {}, 'normalized provider config'),
|
|
650
|
+
requirements,
|
|
651
|
+
nativeArtifact: target === 'native' ? input.nativeArtifact : null,
|
|
652
|
+
javascript: target === 'javascript' ? dataClone(input.javascript || {}, 'provider JavaScript package facts') : null,
|
|
653
|
+
project: normalizeProject(input.project, action),
|
|
654
|
+
synchronizedPackages: dataClone(input.synchronizedPackages || [], 'synchronized provider packages'),
|
|
655
|
+
optimization: input.optimization === undefined ? null : dataClone(input.optimization, 'provider optimization posture'),
|
|
656
|
+
timeoutMs: input.timeoutMs === undefined ? null : Number(input.timeoutMs)
|
|
657
|
+
};
|
|
658
|
+
if (target === 'native') {
|
|
659
|
+
projected.nativeArtifact = assertProviderNativeArtifact(input.nativeArtifact);
|
|
660
|
+
}
|
|
661
|
+
return deepFreeze(projected);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function fileRecord(input) {
|
|
665
|
+
if (input === undefined || input === null) return Object.freeze({});
|
|
666
|
+
if (!isRecord(input)) throw new TypeError('Pulse provider target result files must be an object.');
|
|
667
|
+
return Object.freeze(Object.fromEntries(Object.keys(input).sort()
|
|
668
|
+
.filter((key) => input[key] !== undefined && input[key] !== null)
|
|
669
|
+
.map((key) => [key, nonEmpty(input[key], `target result file ${key}`)])));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function packagingAudit(input) {
|
|
673
|
+
if (input === undefined || input === null) return null;
|
|
674
|
+
assertExactFields(input, ['version', 'artifactSha256', 'auditedSha256', 'authorized'], 'Pulse provider packaging audit');
|
|
675
|
+
if (input.version !== PROVIDER_PACKAGING_AUDIT_VERSION) {
|
|
676
|
+
throw new TypeError(`Pulse provider packaging audit version must be ${PROVIDER_PACKAGING_AUDIT_VERSION}.`);
|
|
677
|
+
}
|
|
678
|
+
const artifactSha256 = nonEmpty(input.artifactSha256, 'packaging artifactSha256');
|
|
679
|
+
const auditedSha256 = nonEmpty(input.auditedSha256, 'packaging auditedSha256');
|
|
680
|
+
if (input.authorized !== true || artifactSha256 !== auditedSha256) {
|
|
681
|
+
throw new TypeError('Pulse provider packaged bytes do not match the authorized final guest audit.');
|
|
682
|
+
}
|
|
683
|
+
return Object.freeze({ version: PROVIDER_PACKAGING_AUDIT_VERSION, artifactSha256, auditedSha256, authorized: true });
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function normalizeProviderTargetResult(input, expected = {}) {
|
|
687
|
+
assertExactFields(input, RESULT_FIELDS, 'Pulse provider target result');
|
|
688
|
+
if (input.version !== PROVIDER_TARGET_RESULT_VERSION) {
|
|
689
|
+
throw new TypeError(`Pulse provider target result version must be ${PROVIDER_TARGET_RESULT_VERSION}.`);
|
|
690
|
+
}
|
|
691
|
+
const action = nonEmpty(input.action, 'target result action');
|
|
692
|
+
const provider = nonEmpty(input.provider, 'target result provider');
|
|
693
|
+
const target = nonEmpty(input.target, 'target result target');
|
|
694
|
+
if (expected.action && action !== expected.action) throw new TypeError(`Pulse provider target result action mismatch: expected ${expected.action}.`);
|
|
695
|
+
if (expected.provider && provider !== expected.provider) throw new TypeError(`Pulse provider target result provider mismatch: expected ${expected.provider}.`);
|
|
696
|
+
if (expected.target && target !== expected.target) throw new TypeError(`Pulse provider target result target mismatch: expected ${expected.target}.`);
|
|
697
|
+
if (input.automaticFallback !== false) throw new TypeError('Pulse provider target result must disable automatic fallback.');
|
|
698
|
+
if (input.build !== null && input.build !== undefined && !isRecord(input.build)) throw new TypeError('Pulse provider target result build must be an object or null.');
|
|
699
|
+
if (input.realization !== null && input.realization !== undefined && !isRecord(input.realization)) throw new TypeError('Pulse provider target result realization must be an object or null.');
|
|
700
|
+
return deepFreeze({
|
|
701
|
+
version: PROVIDER_TARGET_RESULT_VERSION,
|
|
702
|
+
action,
|
|
703
|
+
provider,
|
|
704
|
+
target,
|
|
705
|
+
status: nonEmpty(input.status, 'target result status'),
|
|
706
|
+
automaticFallback: false,
|
|
707
|
+
files: fileRecord(input.files),
|
|
708
|
+
build: input.build == null ? null : dataClone(input.build, 'provider build result'),
|
|
709
|
+
realization: input.realization == null ? null : dataClone(input.realization, 'provider realization result'),
|
|
710
|
+
packaging: packagingAudit(input.packaging)
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function normalizeProviderJavascriptPackageResult(input, expected = {}) {
|
|
715
|
+
assertExactFields(input, JAVASCRIPT_PACKAGE_FIELDS, 'Pulse provider JavaScript package result');
|
|
716
|
+
const sourcePackageVersion = nonEmpty(input.version, 'JavaScript source package version');
|
|
717
|
+
if (!isRecord(input.manifest) || input.manifest.version !== sourcePackageVersion) {
|
|
718
|
+
throw new TypeError('Pulse provider JavaScript package manifest must match its versioned result.');
|
|
719
|
+
}
|
|
720
|
+
if (!Array.isArray(input.files)) throw new TypeError('Pulse provider JavaScript package result files must be an array.');
|
|
721
|
+
const paths = Object.freeze({
|
|
722
|
+
entry: nonEmpty(input.entryFile, 'JavaScript package entryFile'),
|
|
723
|
+
package: nonEmpty(input.packageFile, 'JavaScript package packageFile'),
|
|
724
|
+
applicationPlan: nonEmpty(input.applicationPlanFile, 'JavaScript package applicationPlanFile'),
|
|
725
|
+
manifest: nonEmpty(input.manifestFile, 'JavaScript package manifestFile'),
|
|
726
|
+
...(input.bundledApplicationFile ? { bundledApplication: nonEmpty(input.bundledApplicationFile, 'JavaScript package bundledApplicationFile') } : {}),
|
|
727
|
+
...(input.deploymentFile ? { deployment: nonEmpty(input.deploymentFile, 'JavaScript package deploymentFile') } : {}),
|
|
728
|
+
...(input.deploymentCandidateFile ? { deploymentCandidate: nonEmpty(input.deploymentCandidateFile, 'JavaScript package deploymentCandidateFile') } : {}),
|
|
729
|
+
...(input.buildConfigFile ? { buildConfig: nonEmpty(input.buildConfigFile, 'JavaScript package buildConfigFile') } : {}),
|
|
730
|
+
...(input.fastlyTomlFile ? { fastlyToml: nonEmpty(input.fastlyTomlFile, 'JavaScript package fastlyTomlFile') } : {}),
|
|
731
|
+
...(input.schemaRegistryFile ? { schemaRegistry: nonEmpty(input.schemaRegistryFile, 'JavaScript package schemaRegistryFile') } : {}),
|
|
732
|
+
...(input.schemaCodecsFile ? { schemaCodecs: nonEmpty(input.schemaCodecsFile, 'JavaScript package schemaCodecsFile') } : {})
|
|
733
|
+
});
|
|
734
|
+
return deepFreeze({
|
|
735
|
+
version: PROVIDER_JAVASCRIPT_PACKAGE_RESULT_VERSION,
|
|
736
|
+
provider: nonEmpty(expected.provider, 'JavaScript package provider'),
|
|
737
|
+
target: 'javascript',
|
|
738
|
+
sourcePackageVersion,
|
|
739
|
+
outDir: nonEmpty(input.outDir, 'JavaScript package outDir'),
|
|
740
|
+
paths,
|
|
741
|
+
manifest: dataClone(input.manifest, 'provider JavaScript package manifest'),
|
|
742
|
+
fileRecords: dataClone(input.files, 'provider JavaScript package file records'),
|
|
743
|
+
automaticFallback: false
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
module.exports = Object.freeze({
|
|
748
|
+
PROVIDER_TOOLCHAIN_VERSION,
|
|
749
|
+
PROVIDER_DRIVER_VERSION,
|
|
750
|
+
PROVIDER_TARGET_DESCRIPTOR_VERSION,
|
|
751
|
+
PROVIDER_TARGET_INVOCATION_VERSION,
|
|
752
|
+
PROVIDER_TARGET_RESULT_VERSION,
|
|
753
|
+
PROVIDER_REQUIREMENTS_VERSION,
|
|
754
|
+
PROVIDER_NATIVE_ARTIFACT_VERSION,
|
|
755
|
+
PROVIDER_PACKAGING_AUDIT_VERSION,
|
|
756
|
+
PROVIDER_JAVASCRIPT_PACKAGE_RESULT_VERSION,
|
|
757
|
+
PROVIDER_PLAN_INPUT_VERSION,
|
|
758
|
+
defineProviderToolchain,
|
|
759
|
+
assertProviderToolchain,
|
|
760
|
+
defineProviderDriver,
|
|
761
|
+
assertProviderDriver,
|
|
762
|
+
normalizeProviderTargetDescriptor,
|
|
763
|
+
createProviderRequirements,
|
|
764
|
+
createProviderPlanInput,
|
|
765
|
+
projectProviderRequirements,
|
|
766
|
+
createProviderNativeArtifact,
|
|
767
|
+
assertProviderNativeArtifact,
|
|
768
|
+
createProviderTargetInvocation,
|
|
769
|
+
normalizeProviderTargetResult,
|
|
770
|
+
normalizeProviderJavascriptPackageResult
|
|
771
|
+
});
|