@pulse-compute/provider-fastly 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 +52 -1
- package/dist/config-schema.json +73 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/package.json +87 -5
- package/src/build/canonical-target.js +436 -0
- package/src/build/entities-native.js +564 -0
- package/src/build/fixed-memory-runtime.as.ts +148 -0
- package/src/build/native-http-effects.js +871 -0
- package/src/build/native-http-shell.js +703 -0
- package/src/build/native-platform-capabilities.js +2109 -0
- package/src/compiler/assets-package-out-parity-proof.js +338 -0
- package/src/compiler/assets-provider-proof.js +499 -0
- package/src/compiler/config-provider-resolution.js +573 -0
- package/src/compiler/fastly-adapter.js +564 -0
- package/src/compiler/fastly-command-entry.js +390 -0
- package/src/compiler/fastly-config-secret-serve.js +323 -0
- package/src/compiler/fastly-hostcall-binding.js +598 -0
- package/src/compiler/fastly-readiness.js +477 -0
- package/src/compiler/index.js +40 -0
- package/src/compiler/lifecycle-parity-contract.js +70 -0
- package/src/compiler/lifecycle-parity-proof.js +260 -0
- package/src/compiler/route-handler-effect-proof.js +255 -0
- package/src/compiler/runtime-provider-kv.js +237 -0
- package/src/compiler/runtime-provider-shape.js +109 -0
- package/src/config-api.js +63 -0
- package/src/config-schema.d.ts +13 -0
- package/src/config-schema.js +27 -0
- package/src/config-schema.json +73 -0
- package/src/index.js +3 -0
- package/src/index.ts +118 -0
- package/src/javascript/assets-adapter.js +122 -0
- package/src/javascript/bindings-adapter.js +237 -0
- package/src/javascript/errors.js +26 -0
- package/src/javascript/fetch-adapter.js +93 -0
- package/src/javascript/grip-broadcast.js +261 -0
- package/src/javascript/jwt-verifier.js +137 -0
- package/src/javascript/lifecycle.js +55 -0
- package/src/javascript/local-loader.js +138 -0
- package/src/javascript/logging.js +26 -0
- package/src/javascript/package-effects.js +68 -0
- package/src/javascript/runtime-host.js +108 -0
- package/src/javascript/sha256.js +128 -0
- package/src/javascript/source-package.js +776 -0
- package/src/javascript/support.js +142 -0
- package/src/javascript/target-support-policy.js +260 -0
- package/src/javascript/target.js +73 -0
- package/src/javascript/test-runtime.js +407 -0
- package/src/provider-contract.js +72 -0
- package/src/runtime/assets-provider.js +464 -0
- package/src/runtime/canonical-api-runtime.js +597 -0
- package/src/runtime/index.js +28 -0
- package/src/runtime/provider-primitives-contract.js +272 -0
- package/src/runtime/provider-primitives.js +82 -0
- package/src/runtime/route-handler-effects.js +507 -0
- package/src/testing/fastly-cli.js +538 -0
- package/src/testing/native-http-effects-host.js +390 -0
- package/src/testing/native-http-shell-host.js +190 -0
- package/src/testing/native-platform-capabilities-host.js +599 -0
- package/src/toolchain/config.js +128 -0
- package/src/toolchain/index.js +343 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function loadAssetsContracts() {
|
|
4
|
+
try {
|
|
5
|
+
return require('@pulse-compute/wasm-contracts/assets/contracts');
|
|
6
|
+
} catch (error) {
|
|
7
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
|
|
8
|
+
return require('../../../../wasm/packages/contracts/src/assets/contracts.js');
|
|
9
|
+
}
|
|
10
|
+
throw error;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function loadDiagnostics() {
|
|
15
|
+
try {
|
|
16
|
+
return require('@pulse-compute/wasm-contracts/diagnostics');
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
|
|
19
|
+
return require('../../../../wasm/packages/contracts/src/diagnostics.js');
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const assetsContracts = loadAssetsContracts();
|
|
26
|
+
const {
|
|
27
|
+
PACKAGE_VERSION,
|
|
28
|
+
normalizeArtifact,
|
|
29
|
+
normalizeDiagnostic
|
|
30
|
+
} = loadDiagnostics();
|
|
31
|
+
const {
|
|
32
|
+
createFastlyAssetsProvider,
|
|
33
|
+
createDefaultStoresFromPlan,
|
|
34
|
+
buildFastlyAssetsProviderConfig,
|
|
35
|
+
normalizeHeaders
|
|
36
|
+
} = require('../runtime/assets-provider.js');
|
|
37
|
+
|
|
38
|
+
const phaseName = 'fastly-assets-provider-proof';
|
|
39
|
+
|
|
40
|
+
function clone(value) {
|
|
41
|
+
if (Array.isArray(value)) return value.map(clone);
|
|
42
|
+
if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, clone(entry)]));
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizePlan(input) {
|
|
47
|
+
const plan = input && input.artifact ? input.artifact : input;
|
|
48
|
+
if (!plan || typeof plan !== 'object') return undefined;
|
|
49
|
+
return {
|
|
50
|
+
...plan,
|
|
51
|
+
entries: Array.isArray(plan.entries) ? plan.entries.map(clone) : [],
|
|
52
|
+
payloadModes: plan.payloadModes ? clone(plan.payloadModes) : undefined
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function plannedEntries(plan) {
|
|
57
|
+
return (plan && Array.isArray(plan.entries) ? plan.entries : [])
|
|
58
|
+
.filter((entry) => entry && entry.status !== 'unsupported' && entry.lookup && entry.lookup.store && entry.lookup.key)
|
|
59
|
+
.map(clone);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeKey(key) {
|
|
63
|
+
const text = String(key || '');
|
|
64
|
+
return text.startsWith('/') ? text : `/${text}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function makeDiagnostic(code, message, severity, details) {
|
|
68
|
+
return normalizeDiagnostic({
|
|
69
|
+
phase: phaseName,
|
|
70
|
+
severity: severity || 'error',
|
|
71
|
+
code,
|
|
72
|
+
message,
|
|
73
|
+
hint: details && details.hint,
|
|
74
|
+
details,
|
|
75
|
+
loc: { file: '<fastly-assets-provider-proof>' }
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function headerValue(headers, name) {
|
|
80
|
+
const lower = String(name).toLowerCase();
|
|
81
|
+
const found = normalizeHeaders(headers).find(([key]) => String(key).toLowerCase() === lower);
|
|
82
|
+
return found ? found[1] : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resultBody(result) {
|
|
86
|
+
if (!result) return undefined;
|
|
87
|
+
if (result.body !== undefined) return String(result.body);
|
|
88
|
+
if (result.bodyStream && Array.isArray(result.bodyStream.chunks)) {
|
|
89
|
+
return result.bodyStream.chunks.map((chunk) => Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk)).join('');
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function check(name, passed, details, failures, diagnostics) {
|
|
95
|
+
const out = { name, status: passed ? 'ok' : 'error', details: details || {} };
|
|
96
|
+
if (!passed) {
|
|
97
|
+
failures.push(out);
|
|
98
|
+
diagnostics.push(makeDiagnostic(
|
|
99
|
+
assetsContracts.ASSETS_DIAGNOSTIC_CODES.FASTLY_PROVIDER_SMOKE_FAILED,
|
|
100
|
+
`Fastly assets provider proof check failed: ${name}.`,
|
|
101
|
+
'error',
|
|
102
|
+
{ check: name, details: out.details }
|
|
103
|
+
));
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function cloneEntryForPayload(entry, payloadMode, idSuffix) {
|
|
109
|
+
const next = clone(entry);
|
|
110
|
+
next.id = `${entry.id || 'asset'}-${idSuffix}`;
|
|
111
|
+
next.lookup = { ...(next.lookup || {}), payloadMode };
|
|
112
|
+
next.response = {
|
|
113
|
+
...(next.response || {}),
|
|
114
|
+
lookupId: next.id,
|
|
115
|
+
payloadMode,
|
|
116
|
+
payloadStatus: payloadMode === 'binary-buffer' ? 'reserved-with-diagnostic' : 'lowering-plan-only'
|
|
117
|
+
};
|
|
118
|
+
return next;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function selectEntry(entries, method) {
|
|
122
|
+
const upper = String(method || 'GET').toUpperCase();
|
|
123
|
+
if (upper === 'HEAD') {
|
|
124
|
+
return entries.find((entry) => String(entry.lookup.method || 'GET').toUpperCase() === 'HEAD') || entries[0];
|
|
125
|
+
}
|
|
126
|
+
return entries.find((entry) => String(entry.lookup.method || 'GET').toUpperCase() === upper) || entries[0];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function bodyFixtureForEntry(entry, label) {
|
|
130
|
+
return `${label}:${entry.lookup.store}:${normalizeKey(entry.lookup.key)}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function storesWithBody(entry, body, options = {}) {
|
|
134
|
+
return {
|
|
135
|
+
[entry.lookup.store]: {
|
|
136
|
+
[normalizeKey(entry.lookup.key)]: {
|
|
137
|
+
status: options.status || (entry.response && entry.response.status) || 200,
|
|
138
|
+
body,
|
|
139
|
+
payloadMode: options.payloadMode,
|
|
140
|
+
cacheControl: entry.lookup.cacheControl,
|
|
141
|
+
headers: [
|
|
142
|
+
['Content-Type', options.contentType || 'text/plain; charset=utf-8'],
|
|
143
|
+
['X-PulseWasm-Fastly-Proof', options.label || '1']
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function sourceArtifactVersion(value) {
|
|
151
|
+
if (!value) return undefined;
|
|
152
|
+
if (value.artifact) return value.artifact.version;
|
|
153
|
+
return value.version;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function normalizeSourceArtifacts(inputs) {
|
|
157
|
+
const fastlyStreamBindingPlan = inputs.fastlyStreamBindingPlan || (inputs.fastlyHostcallBinding && inputs.fastlyHostcallBinding.streamBindingPlan);
|
|
158
|
+
const fastlyStreamHeaderAdapter = inputs.fastlyStreamHeaderAdapter || (inputs.fastlyAdapter && inputs.fastlyAdapter.streamHeaderAdapter);
|
|
159
|
+
return {
|
|
160
|
+
fastlyStreamBindingPlan: sourceArtifactVersion(fastlyStreamBindingPlan),
|
|
161
|
+
fastlyStreamHeaderAdapter: sourceArtifactVersion(fastlyStreamHeaderAdapter),
|
|
162
|
+
requestResultHeaders: sourceArtifactVersion(inputs.requestResultHeaders),
|
|
163
|
+
streamingPassthrough: sourceArtifactVersion(inputs.streamingPassthrough),
|
|
164
|
+
wasmHostAbi: sourceArtifactVersion(inputs.wasmHostAbi)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function buildFastlyAssetsProviderProof(inputs = {}) {
|
|
169
|
+
const cwd = inputs.cwd || process.cwd();
|
|
170
|
+
const generatedBy = inputs.generatedBy || PACKAGE_VERSION;
|
|
171
|
+
const plan = normalizePlan(inputs.assetsLoweringPlan || inputs.plan);
|
|
172
|
+
const diagnostics = [];
|
|
173
|
+
if (!plan) {
|
|
174
|
+
diagnostics.push(makeDiagnostic(
|
|
175
|
+
assetsContracts.ASSETS_DIAGNOSTIC_CODES.FASTLY_PROVIDER_PLAN_REQUIRED,
|
|
176
|
+
'Fastly assets provider proof requires an assets-lowering-plan artifact.',
|
|
177
|
+
'error',
|
|
178
|
+
{ hint: 'Emit --assets-lowering-plan before requesting the Fastly assets provider proof.' }
|
|
179
|
+
));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const entries = plannedEntries(plan);
|
|
183
|
+
if (plan && entries.length === 0) {
|
|
184
|
+
diagnostics.push(makeDiagnostic(
|
|
185
|
+
assetsContracts.ASSETS_DIAGNOSTIC_CODES.FASTLY_PROVIDER_PLAN_REQUIRED,
|
|
186
|
+
'Fastly assets provider proof requires at least one static assets lookup/respond entry from the lowering plan.',
|
|
187
|
+
'error',
|
|
188
|
+
{ entries: 0, hint: 'Add assets.lookup("public", "/file.txt") and assets.respond(handle) to the entry source.' }
|
|
189
|
+
));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const failures = [];
|
|
193
|
+
const checks = [];
|
|
194
|
+
const results = {};
|
|
195
|
+
const sourceArtifacts = normalizeSourceArtifacts(inputs);
|
|
196
|
+
let proofStores = inputs.stores;
|
|
197
|
+
const providerConfig = inputs.providerConfig || buildFastlyAssetsProviderConfig({ plan, resolvedConfig: inputs.resolvedConfig });
|
|
198
|
+
let provider;
|
|
199
|
+
|
|
200
|
+
checks.push(check(
|
|
201
|
+
'Fastly assets provider config maps plan stores to symbolic config/secret refs',
|
|
202
|
+
providerConfig && providerConfig.provider === 'fastly' && providerConfig.symbolicRefsOnly === true && Array.isArray(providerConfig.stores) && providerConfig.stores.length === entries.length ? true : Boolean(providerConfig && providerConfig.stores && providerConfig.stores.length > 0 && providerConfig.configStore && providerConfig.secretStore),
|
|
203
|
+
{
|
|
204
|
+
stores: providerConfig && providerConfig.stores ? providerConfig.stores.length : 0,
|
|
205
|
+
configStore: providerConfig && providerConfig.configStore,
|
|
206
|
+
secretStore: providerConfig && providerConfig.secretStore,
|
|
207
|
+
symbolicRefsOnly: providerConfig && providerConfig.symbolicRefsOnly
|
|
208
|
+
},
|
|
209
|
+
failures,
|
|
210
|
+
diagnostics
|
|
211
|
+
));
|
|
212
|
+
|
|
213
|
+
if (entries.length > 0) {
|
|
214
|
+
const getEntry = selectEntry(entries, 'GET');
|
|
215
|
+
const headEntry = selectEntry(entries, 'HEAD') || getEntry;
|
|
216
|
+
if (!proofStores) {
|
|
217
|
+
proofStores = createDefaultStoresFromPlan(plan);
|
|
218
|
+
proofStores[getEntry.lookup.store] = {
|
|
219
|
+
...(proofStores[getEntry.lookup.store] || {}),
|
|
220
|
+
[normalizeKey(getEntry.lookup.key)]: {
|
|
221
|
+
status: (getEntry.response && getEntry.response.status) || 200,
|
|
222
|
+
body: bodyFixtureForEntry(getEntry, 'fastly-proof'),
|
|
223
|
+
cacheControl: getEntry.lookup.cacheControl,
|
|
224
|
+
headers: [['Content-Type', 'text/plain; charset=utf-8']]
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
proofStores[headEntry.lookup.store] = {
|
|
228
|
+
...(proofStores[headEntry.lookup.store] || {}),
|
|
229
|
+
[normalizeKey(headEntry.lookup.key)]: {
|
|
230
|
+
status: (headEntry.response && headEntry.response.status) || 200,
|
|
231
|
+
body: bodyFixtureForEntry(headEntry, 'fastly-proof-head'),
|
|
232
|
+
cacheControl: headEntry.lookup.cacheControl,
|
|
233
|
+
headers: [['Content-Type', 'text/plain; charset=utf-8']]
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
provider = createFastlyAssetsProvider({
|
|
239
|
+
plan,
|
|
240
|
+
stores: proofStores,
|
|
241
|
+
mode: inputs.mode || 'provider-object-store',
|
|
242
|
+
providerConfig,
|
|
243
|
+
resolvedConfig: inputs.resolvedConfig,
|
|
244
|
+
sourceArtifacts
|
|
245
|
+
});
|
|
246
|
+
diagnostics.push(...(provider.diagnostics || []));
|
|
247
|
+
|
|
248
|
+
const getResult = provider.executeRequest({ method: 'GET', path: getEntry.lookup.key });
|
|
249
|
+
results.getText = {
|
|
250
|
+
status: getResult.status,
|
|
251
|
+
kind: getResult.kind,
|
|
252
|
+
body: resultBody(getResult),
|
|
253
|
+
headers: normalizeHeaders(getResult.headers),
|
|
254
|
+
diagnostics: getResult.diagnostics || []
|
|
255
|
+
};
|
|
256
|
+
checks.push(check(
|
|
257
|
+
'GET maps to Fastly response path with status/headers/body',
|
|
258
|
+
getResult.status === ((getEntry.response && getEntry.response.status) || 200) && getResult.kind === 'fastly-response' && typeof getResult.body === 'string' && getResult.body.length > 0 && getResult.fastlyResponsePath === true,
|
|
259
|
+
{ status: getResult.status, kind: getResult.kind, bodyLength: getResult.body ? String(getResult.body).length : 0, fastlyResponsePath: getResult.fastlyResponsePath },
|
|
260
|
+
failures,
|
|
261
|
+
diagnostics
|
|
262
|
+
));
|
|
263
|
+
checks.push(check(
|
|
264
|
+
'GET headers/status pass through into Fastly response shape',
|
|
265
|
+
Boolean(headerValue(getResult.headers, 'Content-Type')) && headerValue(getResult.headers, 'X-PulseWasm-Fastly-Asset-Key') === normalizeKey(getEntry.lookup.key),
|
|
266
|
+
{ contentType: headerValue(getResult.headers, 'Content-Type'), assetKey: headerValue(getResult.headers, 'X-PulseWasm-Fastly-Asset-Key') },
|
|
267
|
+
failures,
|
|
268
|
+
diagnostics
|
|
269
|
+
));
|
|
270
|
+
if (getEntry.lookup.cacheControl) {
|
|
271
|
+
checks.push(check(
|
|
272
|
+
'GET cache-control policy is preserved in Fastly response headers when declared',
|
|
273
|
+
headerValue(getResult.headers, 'Cache-Control') === getEntry.lookup.cacheControl,
|
|
274
|
+
{ expected: getEntry.lookup.cacheControl, actual: headerValue(getResult.headers, 'Cache-Control') },
|
|
275
|
+
failures,
|
|
276
|
+
diagnostics
|
|
277
|
+
));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const headResult = provider.executeRequest({ method: 'HEAD', path: headEntry.lookup.key });
|
|
281
|
+
results.head = {
|
|
282
|
+
status: headResult.status,
|
|
283
|
+
kind: headResult.kind,
|
|
284
|
+
body: resultBody(headResult),
|
|
285
|
+
headers: normalizeHeaders(headResult.headers),
|
|
286
|
+
diagnostics: headResult.diagnostics || []
|
|
287
|
+
};
|
|
288
|
+
checks.push(check(
|
|
289
|
+
'HEAD maps to Fastly response headers/status with no body',
|
|
290
|
+
headResult.status === ((headEntry.response && headEntry.response.status) || 200) && headResult.kind === 'fastly-empty' && headResult.body === undefined,
|
|
291
|
+
{ status: headResult.status, kind: headResult.kind, body: headResult.body },
|
|
292
|
+
failures,
|
|
293
|
+
diagnostics
|
|
294
|
+
));
|
|
295
|
+
|
|
296
|
+
const missingPath = '/__pulsewasm-fastly-missing-asset__';
|
|
297
|
+
const missingResult = provider.executeRequest({ method: 'GET', path: missingPath });
|
|
298
|
+
results.missing = {
|
|
299
|
+
status: missingResult.status,
|
|
300
|
+
kind: missingResult.kind,
|
|
301
|
+
passThroughOn404: missingResult.passThroughOn404,
|
|
302
|
+
diagnostics: missingResult.diagnostics || []
|
|
303
|
+
};
|
|
304
|
+
checks.push(check(
|
|
305
|
+
'missing asset follows Fastly 404/pass-through policy',
|
|
306
|
+
missingResult.status === 404 && missingResult.kind === 'fastly-empty' && missingResult.passThroughOn404 === true,
|
|
307
|
+
{ status: missingResult.status, kind: missingResult.kind, passThroughOn404: missingResult.passThroughOn404 },
|
|
308
|
+
failures,
|
|
309
|
+
diagnostics
|
|
310
|
+
));
|
|
311
|
+
|
|
312
|
+
const methodResult = provider.executeRequest({ method: 'POST', path: getEntry.lookup.key });
|
|
313
|
+
results.unsupportedMethod = {
|
|
314
|
+
status: methodResult.status,
|
|
315
|
+
kind: methodResult.kind,
|
|
316
|
+
headers: normalizeHeaders(methodResult.headers),
|
|
317
|
+
diagnostics: methodResult.diagnostics || []
|
|
318
|
+
};
|
|
319
|
+
checks.push(check(
|
|
320
|
+
'unsupported methods are diagnosed and rejected by Fastly proof',
|
|
321
|
+
methodResult.status === 405 && (methodResult.diagnostics || []).some((diag) => diag.code === assetsContracts.ASSETS_DIAGNOSTIC_CODES.UNSUPPORTED_METHOD),
|
|
322
|
+
{ status: methodResult.status, diagnosticCodes: (methodResult.diagnostics || []).map((diag) => diag.code) },
|
|
323
|
+
failures,
|
|
324
|
+
diagnostics
|
|
325
|
+
));
|
|
326
|
+
|
|
327
|
+
const streamEntry = cloneEntryForPayload(getEntry, 'stream-pass-through-response', 'stream');
|
|
328
|
+
const streamPlan = { ...plan, entries: [streamEntry] };
|
|
329
|
+
const streamProvider = createFastlyAssetsProvider({
|
|
330
|
+
plan: streamPlan,
|
|
331
|
+
stores: storesWithBody(streamEntry, bodyFixtureForEntry(streamEntry, 'fastly-proof-stream'), { payloadMode: 'stream-pass-through-response', label: 'stream' }),
|
|
332
|
+
providerConfig,
|
|
333
|
+
sourceArtifacts
|
|
334
|
+
});
|
|
335
|
+
const streamResult = streamProvider.executeRequest({ method: 'GET', path: streamEntry.lookup.key });
|
|
336
|
+
results.stream = {
|
|
337
|
+
status: streamResult.status,
|
|
338
|
+
kind: streamResult.kind,
|
|
339
|
+
fastlyStreamBindingPlanConnected: streamResult.fastlyStreamBindingPlanConnected,
|
|
340
|
+
responseRef: streamResult.responseRef,
|
|
341
|
+
streamRef: streamResult.streamRef,
|
|
342
|
+
body: resultBody(streamResult),
|
|
343
|
+
diagnostics: streamResult.diagnostics || []
|
|
344
|
+
};
|
|
345
|
+
checks.push(check(
|
|
346
|
+
'stream pass-through response maps to Fastly stream binding plan path',
|
|
347
|
+
streamResult.status === 200 && streamResult.kind === 'fastly-stream' && streamResult.bodyStream && streamResult.bodyStream.hostOwnedBytes === true && resultBody(streamResult).length > 0,
|
|
348
|
+
{ status: streamResult.status, kind: streamResult.kind, fastlyStreamBindingPlanConnected: streamResult.fastlyStreamBindingPlanConnected, responseRef: streamResult.responseRef, streamRef: streamResult.streamRef, bodyLength: resultBody(streamResult).length },
|
|
349
|
+
failures,
|
|
350
|
+
diagnostics
|
|
351
|
+
));
|
|
352
|
+
|
|
353
|
+
const binaryEntry = cloneEntryForPayload(getEntry, 'binary-buffer', 'binary');
|
|
354
|
+
const binaryPlan = { ...plan, entries: [binaryEntry] };
|
|
355
|
+
const binaryProvider = createFastlyAssetsProvider({
|
|
356
|
+
plan: binaryPlan,
|
|
357
|
+
stores: storesWithBody(binaryEntry, Buffer.from('binary-proof'), { payloadMode: 'binary-buffer', label: 'binary' }),
|
|
358
|
+
providerConfig,
|
|
359
|
+
sourceArtifacts
|
|
360
|
+
});
|
|
361
|
+
const binaryResult = binaryProvider.executeRequest({ method: 'GET', path: binaryEntry.lookup.key });
|
|
362
|
+
results.binary = {
|
|
363
|
+
status: binaryResult.status,
|
|
364
|
+
kind: binaryResult.kind,
|
|
365
|
+
diagnostics: binaryResult.diagnostics || []
|
|
366
|
+
};
|
|
367
|
+
checks.push(check(
|
|
368
|
+
'binary body/buffer mode remains reserved with diagnostic for Fastly',
|
|
369
|
+
binaryResult.status === 501 && (binaryResult.diagnostics || []).some((diag) => diag.code === assetsContracts.ASSETS_DIAGNOSTIC_CODES.BINARY_BODY_RESERVED),
|
|
370
|
+
{ status: binaryResult.status, diagnosticCodes: (binaryResult.diagnostics || []).map((diag) => diag.code) },
|
|
371
|
+
failures,
|
|
372
|
+
diagnostics
|
|
373
|
+
));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const errorDiagnostics = diagnostics.filter((entry) => (entry.severity || 'error') === 'error');
|
|
377
|
+
const artifact = normalizeArtifact({
|
|
378
|
+
version: assetsContracts.ASSETS_FASTLY_PROVIDER_PROOF_VERSION,
|
|
379
|
+
generatedBy,
|
|
380
|
+
phase: phaseName,
|
|
381
|
+
status: errorDiagnostics.length === 0 ? 'ok' : 'error',
|
|
382
|
+
provider: 'fastly',
|
|
383
|
+
providerPackage: '@pulse-compute/provider-fastly',
|
|
384
|
+
contractId: assetsContracts.ASSETS_CONTRACT_ID,
|
|
385
|
+
sourceAssetsLoweringPlanVersion: plan && plan.version,
|
|
386
|
+
loweringPlanConnected: Boolean(plan && entries.length > 0),
|
|
387
|
+
providerBehaviorImplemented: true,
|
|
388
|
+
compiledWasmLowering: false,
|
|
389
|
+
proofMode: 'direct-fastly-provider-smoke-connected-to-lowering-plan',
|
|
390
|
+
scope: {
|
|
391
|
+
mode: inputs.mode || 'provider-object-store',
|
|
392
|
+
methods: clone(assetsContracts.ASSETS_ALLOWED_METHODS),
|
|
393
|
+
staticStoreKeyFromLoweringPlan: true,
|
|
394
|
+
uploads: false,
|
|
395
|
+
invalidation: false,
|
|
396
|
+
mutationApis: false,
|
|
397
|
+
fullObjectStoreSemantics: false,
|
|
398
|
+
cacheControlPolicy: 'pass-through-when-declared',
|
|
399
|
+
notFoundPolicy: '404-pass-through',
|
|
400
|
+
configSecretRefs: 'symbolic-only'
|
|
401
|
+
},
|
|
402
|
+
providerConfig,
|
|
403
|
+
fastlyArtifacts: {
|
|
404
|
+
fastlyStreamBindingPlan: sourceArtifacts.fastlyStreamBindingPlan,
|
|
405
|
+
fastlyStreamHeaderAdapter: sourceArtifacts.fastlyStreamHeaderAdapter,
|
|
406
|
+
requestResultHeaders: sourceArtifacts.requestResultHeaders,
|
|
407
|
+
streamingPassthrough: sourceArtifacts.streamingPassthrough,
|
|
408
|
+
wasmHostAbi: sourceArtifacts.wasmHostAbi
|
|
409
|
+
},
|
|
410
|
+
payloadModes: {
|
|
411
|
+
textResponseBody: {
|
|
412
|
+
mode: 'text-response-body',
|
|
413
|
+
status: 'implemented-now',
|
|
414
|
+
proof: 'GET response body via Fastly-like response path'
|
|
415
|
+
},
|
|
416
|
+
headerPassThrough: {
|
|
417
|
+
mode: 'header-pass-through',
|
|
418
|
+
status: 'implemented-now',
|
|
419
|
+
proof: 'Content-Type, X-PulseWasm-Fastly-Asset-Key, and Cache-Control header checks'
|
|
420
|
+
},
|
|
421
|
+
statusResultPassThrough: {
|
|
422
|
+
mode: 'status-result-pass-through',
|
|
423
|
+
status: 'implemented-now',
|
|
424
|
+
proof: 'GET and HEAD status checks'
|
|
425
|
+
},
|
|
426
|
+
streamPassThroughResponse: {
|
|
427
|
+
mode: 'stream-pass-through-response',
|
|
428
|
+
contractStatus: assetsContracts.ASSETS_PAYLOAD_MODE_CLASSIFICATION.streamPassThroughResponse.status,
|
|
429
|
+
planStatus: plan && plan.payloadModes && plan.payloadModes.stream ? plan.payloadModes.stream.status : undefined,
|
|
430
|
+
hostSurfaceStatus: assetsContracts.ASSETS_PAYLOAD_MODE_CLASSIFICATION.streamPassThroughResponse.hostSurfaceStatus,
|
|
431
|
+
status: entries.length > 0 ? 'implemented-fastly-proof' : 'not-exercised',
|
|
432
|
+
providerWired: entries.length > 0,
|
|
433
|
+
fastlyStreamBindingPlanConnected: Boolean(sourceArtifacts.fastlyStreamBindingPlan),
|
|
434
|
+
binaryStaticPayloadDecision: 'stream-pass-through-for-stream-mode; binary-buffer-reserved',
|
|
435
|
+
proof: 'direct Fastly provider returns kind=fastly-stream with responseRef/streamRef-style binding metadata'
|
|
436
|
+
},
|
|
437
|
+
binaryBodyBufferResponse: {
|
|
438
|
+
mode: 'binary-buffer',
|
|
439
|
+
contractStatus: assetsContracts.ASSETS_PAYLOAD_MODE_CLASSIFICATION.binaryBodyBufferResponse.status,
|
|
440
|
+
planStatus: plan && plan.payloadModes && plan.payloadModes.binary ? plan.payloadModes.binary.status : undefined,
|
|
441
|
+
status: 'reserved-with-diagnostic',
|
|
442
|
+
providerWired: false,
|
|
443
|
+
diagnosticCode: assetsContracts.ASSETS_DIAGNOSTIC_CODES.BINARY_BODY_RESERVED
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
entriesConsumed: entries.map((entry) => ({
|
|
447
|
+
id: entry.id,
|
|
448
|
+
store: entry.lookup.store,
|
|
449
|
+
key: entry.lookup.key,
|
|
450
|
+
method: entry.lookup.method || 'GET',
|
|
451
|
+
payloadMode: (entry.response && entry.response.payloadMode) || entry.lookup.payloadMode || 'text-response-body',
|
|
452
|
+
response: entry.response ? { status: entry.response.status, payloadMode: entry.response.payloadMode } : undefined
|
|
453
|
+
})),
|
|
454
|
+
smoke: {
|
|
455
|
+
checks,
|
|
456
|
+
failedChecks: failures,
|
|
457
|
+
results
|
|
458
|
+
},
|
|
459
|
+
policy: {
|
|
460
|
+
compilerOwnsProviderBehavior: false,
|
|
461
|
+
providerOwnsImplementation: true,
|
|
462
|
+
planBuilderOwner: plan && plan.policy && plan.policy.builderOwner ? plan.policy.builderOwner : '@pulse-compute/assets',
|
|
463
|
+
providerOwner: '@pulse-compute/provider-fastly',
|
|
464
|
+
consumesLoweringPlan: true,
|
|
465
|
+
fastlyBehaviorInCompiler: false,
|
|
466
|
+
officialSdkDependency: false
|
|
467
|
+
},
|
|
468
|
+
summary: {
|
|
469
|
+
entries: entries.length,
|
|
470
|
+
checks: checks.length,
|
|
471
|
+
failedChecks: failures.length,
|
|
472
|
+
providerConfigMapped: checks.some((entry) => entry.name.startsWith('Fastly assets provider config') && entry.status === 'ok'),
|
|
473
|
+
getTextValidated: checks.some((entry) => entry.name.startsWith('GET maps') && entry.status === 'ok'),
|
|
474
|
+
headValidated: checks.some((entry) => entry.name.startsWith('HEAD maps') && entry.status === 'ok'),
|
|
475
|
+
notFoundValidated: checks.some((entry) => entry.name.startsWith('missing asset') && entry.status === 'ok'),
|
|
476
|
+
unsupportedMethodValidated: checks.some((entry) => entry.name.startsWith('unsupported methods') && entry.status === 'ok'),
|
|
477
|
+
streamValidated: checks.some((entry) => entry.name.startsWith('stream pass-through') && entry.status === 'ok'),
|
|
478
|
+
streamBindingPlanConnected: Boolean(sourceArtifacts.fastlyStreamBindingPlan),
|
|
479
|
+
binaryReservedValidated: checks.some((entry) => entry.name.startsWith('binary body') && entry.status === 'ok'),
|
|
480
|
+
diagnostics: diagnostics.length,
|
|
481
|
+
errorDiagnostics: errorDiagnostics.length
|
|
482
|
+
},
|
|
483
|
+
diagnostics
|
|
484
|
+
}, cwd);
|
|
485
|
+
|
|
486
|
+
return {
|
|
487
|
+
artifact,
|
|
488
|
+
diagnostics,
|
|
489
|
+
entries: artifact.entriesConsumed,
|
|
490
|
+
smoke: artifact.smoke,
|
|
491
|
+
providerConfig: artifact.providerConfig,
|
|
492
|
+
hasErrors: errorDiagnostics.length > 0
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
module.exports = {
|
|
497
|
+
buildFastlyAssetsProviderProof,
|
|
498
|
+
phaseName
|
|
499
|
+
};
|