@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,477 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const { PACKAGE_VERSION, normalizeArtifact, normalizeDiagnostic } = loadContractsDiagnostics();
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
function loadContractsDiagnostics() {
|
|
8
|
+
try {
|
|
9
|
+
return require('@pulse-compute/wasm-contracts/diagnostics');
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) {
|
|
12
|
+
return require('../../../../wasm/packages/contracts/src/diagnostics.js');
|
|
13
|
+
}
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const PHASE = '13B';
|
|
19
|
+
const FASTLY_READINESS_VERSION = 'pulsewasm.fastly-readiness.v1';
|
|
20
|
+
const FASTLY_SDK_AUDIT_VERSION = 'pulsewasm.fastly-sdk-audit.v1';
|
|
21
|
+
const FASTLY_HOSTCALL_MAP_VERSION = 'pulsewasm.fastly-hostcall-map.v1';
|
|
22
|
+
const FASTLY_CAPABILITY_MAP_VERSION = 'pulsewasm.fastly-capability-map.v1';
|
|
23
|
+
const FASTLY_PACKAGING_PLAN_VERSION = 'pulsewasm.fastly-packaging-plan.v1';
|
|
24
|
+
const FASTLY_RISK_REPORT_VERSION = 'pulsewasm.fastly-risk-report.v1';
|
|
25
|
+
const FASTLY_ADAPTER_PLAN_VERSION = 'pulsewasm.fastly-adapter-plan.v1';
|
|
26
|
+
|
|
27
|
+
function sha256Text(text) {
|
|
28
|
+
return crypto.createHash('sha256').update(String(text || ''), 'utf8').digest('hex');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function makeFile(file, text) {
|
|
32
|
+
return { file, text, bytes: Buffer.byteLength(text, 'utf8'), sha256: sha256Text(text) };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function diag(code, message, hint, details) {
|
|
36
|
+
return normalizeDiagnostic({ phase: 'fastly-readiness', severity: 'error', code, message, hint, details, loc: { file: '<fastly-readiness>' } });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function source(url, title, notes) {
|
|
40
|
+
return { url, title, notes };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildSdkAudit() {
|
|
44
|
+
const sources = [
|
|
45
|
+
source('https://www.fastly.com/documentation/reference/compute/sdks/', 'Compute language SDKs', 'Official SDKs are Rust, JavaScript, and Go; current supported capability list includes Fanout, WebSockets passthrough, dynamic backends, config stores, secret stores, KV stores, cache APIs, and related features.'),
|
|
46
|
+
source('https://www.fastly.com/documentation/guides/compute/developer-guides/custom/', 'Unofficial SDKs on the Compute platform', 'Custom SDKs are possible because Compute is WASI-powered, but Fastly warns they are unsupported; hostcalls are defined in Viceroy .witx files and custom binaries can be packed with fastly compute pack.'),
|
|
47
|
+
source('https://www.fastly.com/documentation/reference/changes/2023/05/assemblyscript-sdk-deprecation/', 'AssemblyScript SDK deprecated', 'Fastly announced the AssemblyScript SDK is no longer actively developed or maintained.'),
|
|
48
|
+
source('https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/', 'Pub/Sub at the edge with Fanout', 'Fanout is a Compute service capability for pub/sub and handoff flows; real pulse.grip remains package-level work.'),
|
|
49
|
+
source('https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends', 'Developer guide: Backends', 'Dynamic backends are available via Compute SDKs and should not accept unvalidated user input as backend definitions.')
|
|
50
|
+
];
|
|
51
|
+
const sdks = [
|
|
52
|
+
{
|
|
53
|
+
name: 'rust',
|
|
54
|
+
support: 'official-sdk',
|
|
55
|
+
useInPulseWasm: 'reference-only',
|
|
56
|
+
baggageRisk: 'medium',
|
|
57
|
+
usefulForAudit: ['hostcall coverage', 'stream/body behavior', 'backend/fanout/config/secret store patterns', 'packaging expectations'],
|
|
58
|
+
dependencyDecision: 'do-not-depend'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'javascript',
|
|
62
|
+
support: 'official-sdk',
|
|
63
|
+
useInPulseWasm: 'reference-only',
|
|
64
|
+
baggageRisk: 'high-for-core',
|
|
65
|
+
usefulForAudit: ['web-platform request/response ergonomics', 'streams/fetch behavior', 'dynamic backend surface', 'local dev reference'],
|
|
66
|
+
dependencyDecision: 'do-not-depend'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'go',
|
|
70
|
+
support: 'official-sdk',
|
|
71
|
+
useInPulseWasm: 'reference-only',
|
|
72
|
+
baggageRisk: 'medium',
|
|
73
|
+
usefulForAudit: ['SDK capability coverage', 'hostcall mapping patterns', 'packaging expectations'],
|
|
74
|
+
dependencyDecision: 'do-not-depend'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'assemblyscript',
|
|
78
|
+
support: 'deprecated-unsupported',
|
|
79
|
+
useInPulseWasm: 'do-not-use-as-core-path',
|
|
80
|
+
baggageRisk: 'high',
|
|
81
|
+
usefulForAudit: ['historical caution only'],
|
|
82
|
+
dependencyDecision: 'do-not-depend'
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
return {
|
|
86
|
+
version: FASTLY_SDK_AUDIT_VERSION,
|
|
87
|
+
generatedBy: PACKAGE_VERSION,
|
|
88
|
+
phase: PHASE,
|
|
89
|
+
posture: {
|
|
90
|
+
officialSdksAsDependencies: false,
|
|
91
|
+
officialSdksAsReferences: true,
|
|
92
|
+
customHostcallPathStillPossible: true,
|
|
93
|
+
assemblyScriptSdkRevival: false,
|
|
94
|
+
reason: 'PulseWasm should consume Fastly platform capabilities through its own host/runtime contract; SDKs are audited for hostcall and packaging behavior only.'
|
|
95
|
+
},
|
|
96
|
+
sources,
|
|
97
|
+
sdks,
|
|
98
|
+
summary: {
|
|
99
|
+
sdksEvaluated: sdks.length,
|
|
100
|
+
officialSdksEvaluated: sdks.filter((item) => item.support === 'official-sdk').length,
|
|
101
|
+
dependenciesAdopted: 0,
|
|
102
|
+
usedAsReference: true,
|
|
103
|
+
assemblyScriptSdkUnsupported: true
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildHostcallMap() {
|
|
109
|
+
const capabilityMap = {
|
|
110
|
+
headers: {
|
|
111
|
+
pulseSurface: 'request-result-headers.json / ordered repeated header bags',
|
|
112
|
+
fastlySurface: 'HTTP request/response hostcalls via SDK/compute ABI',
|
|
113
|
+
readiness: 'ready-for-adapter-mapping',
|
|
114
|
+
adapterNeed: 'map ordered repeated headers without collapsing Grip-Channel/Set-Cookie-like repeats'
|
|
115
|
+
},
|
|
116
|
+
streamResult: {
|
|
117
|
+
pulseSurface: 'RESULT_STREAM / responseRef / streamRef',
|
|
118
|
+
fastlySurface: 'body/response streaming hostcalls or SDK response stream primitives',
|
|
119
|
+
readiness: 'critical-adapter-proof-needed',
|
|
120
|
+
adapterNeed: 'prove host-owned response/body passthrough without copying bytes into PulseWasm'
|
|
121
|
+
},
|
|
122
|
+
backendFetch: {
|
|
123
|
+
pulseSurface: 'backend-capabilities.json + ctx.fetch reserved effect',
|
|
124
|
+
fastlySurface: 'backend request hostcalls / dynamic backend support',
|
|
125
|
+
readiness: 'blocked-by-effect-runtime',
|
|
126
|
+
adapterNeed: 'wait for explicit effect/resume runtime; validate declared backends and secret/config lookups'
|
|
127
|
+
},
|
|
128
|
+
broadcaster: {
|
|
129
|
+
pulseSurface: 'channel-broadcaster.json + pulse.grip future contract',
|
|
130
|
+
fastlySurface: 'Fanout / WebSockets passthrough capability',
|
|
131
|
+
readiness: 'blocked-by-pulse-grip-package-analysis',
|
|
132
|
+
adapterNeed: 'do not implement Fastly Fanout until pulse.grip contract and sidecar are real'
|
|
133
|
+
},
|
|
134
|
+
config: {
|
|
135
|
+
pulseSurface: 'profile runtime config / env-resolved capabilities',
|
|
136
|
+
fastlySurface: 'Config stores',
|
|
137
|
+
readiness: 'mapping-needed',
|
|
138
|
+
adapterNeed: 'map profile/capability config keys to attached Fastly config stores'
|
|
139
|
+
},
|
|
140
|
+
secrets: {
|
|
141
|
+
pulseSurface: 'env secret refs in resolved config/backend capabilities',
|
|
142
|
+
fastlySurface: 'Secret stores',
|
|
143
|
+
readiness: 'mapping-needed',
|
|
144
|
+
adapterNeed: 'ensure secret refs are never compiled into Wasm binary'
|
|
145
|
+
},
|
|
146
|
+
clock: {
|
|
147
|
+
pulseSurface: 'host-capabilities clock/timer provider contract',
|
|
148
|
+
fastlySurface: 'WASI/host time provider candidate',
|
|
149
|
+
readiness: 'needs-provider-mapping',
|
|
150
|
+
adapterNeed: 'map monotonic deadline/timer needs surgically; no broad WASI leakage'
|
|
151
|
+
},
|
|
152
|
+
assets: {
|
|
153
|
+
pulseSurface: 'reserved host capability; streaming/passthrough primitive ready',
|
|
154
|
+
fastlySurface: 'backend/object/resource storage depending on future asset strategy',
|
|
155
|
+
readiness: 'reserved',
|
|
156
|
+
adapterNeed: 'wait for pulse.assets package contract and streaming/binary decisions'
|
|
157
|
+
},
|
|
158
|
+
body: {
|
|
159
|
+
pulseSurface: 'seeded body text / schema JSON; body-read effect reserved',
|
|
160
|
+
fastlySurface: 'request body hostcalls / SDK body APIs',
|
|
161
|
+
readiness: 'partial',
|
|
162
|
+
adapterNeed: 'seed body text only when explicitly requested; do not add implicit parse/read'
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
version: FASTLY_HOSTCALL_MAP_VERSION,
|
|
167
|
+
generatedBy: PACKAGE_VERSION,
|
|
168
|
+
phase: PHASE,
|
|
169
|
+
posture: {
|
|
170
|
+
useOfficialSdkHostcallsAsReference: true,
|
|
171
|
+
directBindingCandidate: true,
|
|
172
|
+
noWitGeneratedYet: true,
|
|
173
|
+
noFastlySemanticsInCore: true
|
|
174
|
+
},
|
|
175
|
+
pulseCapabilities: capabilityMap,
|
|
176
|
+
criticalPath: ['headers', 'streamResult', 'config', 'secrets'],
|
|
177
|
+
blockedCapabilities: ['backendFetch', 'broadcaster', 'assets', 'body-read-effect'],
|
|
178
|
+
summary: {
|
|
179
|
+
capabilitiesMapped: Object.keys(capabilityMap).length,
|
|
180
|
+
readyForAdapterMapping: Object.values(capabilityMap).filter((item) => item.readiness.includes('ready')).length,
|
|
181
|
+
blockedOrReserved: Object.values(capabilityMap).filter((item) => item.readiness.includes('blocked') || item.readiness === 'reserved').length,
|
|
182
|
+
noHostcallImplementation: true
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function buildCapabilityMap(hostCapabilities, sdkAudit, hostcallMap) {
|
|
188
|
+
const hostSummary = hostCapabilities?.artifact?.summary || hostCapabilities?.summary || {};
|
|
189
|
+
const capabilities = Object.entries(hostcallMap.pulseCapabilities).map(([key, value]) => ({
|
|
190
|
+
key,
|
|
191
|
+
pulseSurface: value.pulseSurface,
|
|
192
|
+
fastlySurface: value.fastlySurface,
|
|
193
|
+
readiness: value.readiness,
|
|
194
|
+
adapterNeed: value.adapterNeed,
|
|
195
|
+
source: 'fastly-hostcall-map.json'
|
|
196
|
+
}));
|
|
197
|
+
return {
|
|
198
|
+
version: FASTLY_CAPABILITY_MAP_VERSION,
|
|
199
|
+
generatedBy: PACKAGE_VERSION,
|
|
200
|
+
phase: PHASE,
|
|
201
|
+
capabilities,
|
|
202
|
+
pulseHostCapabilitySummary: hostSummary,
|
|
203
|
+
sdkAuditSummary: sdkAudit.summary,
|
|
204
|
+
summary: {
|
|
205
|
+
capabilities: capabilities.length,
|
|
206
|
+
readyOrPartial: capabilities.filter((item) => item.readiness.includes('ready') || item.readiness === 'partial').length,
|
|
207
|
+
blocked: capabilities.filter((item) => item.readiness.includes('blocked')).length,
|
|
208
|
+
reserved: capabilities.filter((item) => item.readiness === 'reserved').length
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function buildPackagingPlan() {
|
|
214
|
+
return {
|
|
215
|
+
version: FASTLY_PACKAGING_PLAN_VERSION,
|
|
216
|
+
generatedBy: PACKAGE_VERSION,
|
|
217
|
+
phase: PHASE,
|
|
218
|
+
strategies: [
|
|
219
|
+
{
|
|
220
|
+
id: 'official-sdk-wrapper',
|
|
221
|
+
status: 'not-preferred-for-core',
|
|
222
|
+
support: 'official',
|
|
223
|
+
benefit: 'lower platform integration risk',
|
|
224
|
+
cost: 'SDK/runtime baggage; risks letting SDK wrapper shape PulseWasm core semantics',
|
|
225
|
+
recommendation: 'audit only; do not adopt as 13B dependency'
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: 'custom-hostcall-adapter',
|
|
229
|
+
status: 'preferred-research-path',
|
|
230
|
+
support: 'unsupported-custom-sdk-risk',
|
|
231
|
+
benefit: 'preserves PulseWasm architecture and minimal runtime boundary',
|
|
232
|
+
cost: 'we own hostcall bindings, packaging, and platform drift',
|
|
233
|
+
recommendation: 'plan but do not implement until Fastly adapter phase'
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: 'hybrid-reference-first',
|
|
237
|
+
status: 'recommended-13b-posture',
|
|
238
|
+
support: 'uses-docs-and-sdk-source-as-reference',
|
|
239
|
+
benefit: 'learn from SDKs without importing baggage',
|
|
240
|
+
cost: 'requires later hostcall binding work',
|
|
241
|
+
recommendation: 'selected audit posture'
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
nearTerm: {
|
|
245
|
+
buildArtifact: 'PulseWasm compiled runtime .wasm remains platform-neutral',
|
|
246
|
+
packageCommandCandidate: 'fastly compute pack --wasm-binary <pulsewasm.wasm>',
|
|
247
|
+
requiresFastlyManifest: true,
|
|
248
|
+
manifestGeneration: 'deferred',
|
|
249
|
+
localTesting: 'candidate fastly compute serve / Viceroy after adapter exists',
|
|
250
|
+
noDeploymentPackageYet: true
|
|
251
|
+
},
|
|
252
|
+
summary: {
|
|
253
|
+
strategies: 3,
|
|
254
|
+
selected: 'hybrid-reference-first',
|
|
255
|
+
implementationReady: false,
|
|
256
|
+
packagingOnlyAfterAdapter: true
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function buildRiskReport() {
|
|
262
|
+
const risks = [
|
|
263
|
+
{
|
|
264
|
+
code: 'FASTLY_CUSTOM_SDK_UNSUPPORTED',
|
|
265
|
+
severity: 'high',
|
|
266
|
+
area: 'platform-support',
|
|
267
|
+
message: 'Direct/custom hostcall path preserves PulseWasm minimalism but is not officially supported by Fastly.',
|
|
268
|
+
mitigation: 'Use SDKs as references, keep an explicit adapter plan, and test through local Fastly tooling before deployment.'
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
code: 'FASTLY_AS_SDK_DEPRECATED',
|
|
272
|
+
severity: 'high',
|
|
273
|
+
area: 'tooling',
|
|
274
|
+
message: 'The prior Fastly AssemblyScript SDK is deprecated/unsupported and should not become the PulseWasm path.',
|
|
275
|
+
mitigation: 'Generate PulseWasm-owned AS and bind platform hostcalls deliberately.'
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
code: 'FASTLY_STREAM_PASSTHROUGH_CRITICAL',
|
|
279
|
+
severity: 'medium',
|
|
280
|
+
area: 'streaming',
|
|
281
|
+
message: 'RESULT_STREAM maps to host-owned body/response refs; Fastly adapter must prove passthrough without byte copies.',
|
|
282
|
+
mitigation: 'Make stream passthrough the first Fastly adapter proof after audit.'
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
code: 'FASTLY_FANOUT_GRIP_BLOCKED',
|
|
286
|
+
severity: 'medium',
|
|
287
|
+
area: 'realtime',
|
|
288
|
+
message: 'Fanout/GRIP readiness depends on the real pulse.grip package contract and sidecar analysis.',
|
|
289
|
+
mitigation: 'Do package analysis before implementing Fastly Fanout behavior.'
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
code: 'FASTLY_BACKEND_FETCH_EFFECT_BLOCKED',
|
|
293
|
+
severity: 'medium',
|
|
294
|
+
area: 'effects',
|
|
295
|
+
message: 'Backend fetch requires the explicit effect runtime/resume model, not direct async/Promise behavior.',
|
|
296
|
+
mitigation: 'Implement effect runtime proof before Fastly backend fetch support.'
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
code: 'FASTLY_DEBUG_ARTIFACT_STRIP_REQUIRED',
|
|
300
|
+
severity: 'low',
|
|
301
|
+
area: 'packaging',
|
|
302
|
+
message: 'Proof/debug artifacts, especially WAT, must not be part of deployment packages.',
|
|
303
|
+
mitigation: 'Keep .wat opt-in and emit production artifact filters before deployment packaging.'
|
|
304
|
+
}
|
|
305
|
+
];
|
|
306
|
+
return {
|
|
307
|
+
version: FASTLY_RISK_REPORT_VERSION,
|
|
308
|
+
generatedBy: PACKAGE_VERSION,
|
|
309
|
+
phase: PHASE,
|
|
310
|
+
risks,
|
|
311
|
+
summary: {
|
|
312
|
+
risks: risks.length,
|
|
313
|
+
high: risks.filter((item) => item.severity === 'high').length,
|
|
314
|
+
medium: risks.filter((item) => item.severity === 'medium').length,
|
|
315
|
+
low: risks.filter((item) => item.severity === 'low').length,
|
|
316
|
+
adapterBlocked: true
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function buildAdapterPlan() {
|
|
322
|
+
const stages = [
|
|
323
|
+
{
|
|
324
|
+
id: '13B',
|
|
325
|
+
name: 'Fastly readiness + SDK implementation audit',
|
|
326
|
+
status: 'this-phase',
|
|
327
|
+
output: ['fastly-readiness.json', 'fastly-sdk-audit.json', 'fastly-hostcall-map.json', 'fastly-capability-map.json', 'fastly-risk-report.json']
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
id: '13C-pre',
|
|
331
|
+
name: 'Fastly hostcall binding contract',
|
|
332
|
+
status: 'next-contract',
|
|
333
|
+
output: ['fastly-hostcall-abi.json', 'fastly-stream-binding-contract.json', 'fastly-packaging-contract.json']
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: '13C',
|
|
337
|
+
name: 'Fastly stream/header adapter proof',
|
|
338
|
+
status: 'future',
|
|
339
|
+
output: ['fastly-adapter-smoke.json'],
|
|
340
|
+
scope: 'headers + RESULT_STREAM passthrough only'
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: '13D',
|
|
344
|
+
name: 'Fastly config/secret mapping proof',
|
|
345
|
+
status: 'future',
|
|
346
|
+
scope: 'profile runtime capabilities → Fastly config/secret stores'
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: 'later',
|
|
350
|
+
name: 'Fanout/backend fetch/assets',
|
|
351
|
+
status: 'blocked-by-package-and-effect-work',
|
|
352
|
+
blockers: ['pulse.grip analysis', 'effect runtime', 'pulse.assets/package payload contract']
|
|
353
|
+
}
|
|
354
|
+
];
|
|
355
|
+
return {
|
|
356
|
+
version: FASTLY_ADAPTER_PLAN_VERSION,
|
|
357
|
+
generatedBy: PACKAGE_VERSION,
|
|
358
|
+
phase: PHASE,
|
|
359
|
+
recommendedPath: 'hybrid-reference-first-custom-hostcall-adapter-later',
|
|
360
|
+
stages,
|
|
361
|
+
doNotImplementYet: ['Fastly adapter', 'fastly.toml generation', 'WIT generation', 'Fanout/GRIP behavior', 'ctx.fetch effect runtime'],
|
|
362
|
+
summary: {
|
|
363
|
+
stages: stages.length,
|
|
364
|
+
next: '13C-pre Fastly hostcall binding contract',
|
|
365
|
+
readyForImplementation: false,
|
|
366
|
+
readyForAuditOnly: true
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function buildMarkdown({ artifact, sdkAudit, hostcallMap, capabilityMap, packagingPlan, riskReport, adapterPlan }) {
|
|
372
|
+
const lines = [];
|
|
373
|
+
lines.push('# PulseWasm Fastly Readiness + SDK Implementation Audit');
|
|
374
|
+
lines.push('');
|
|
375
|
+
lines.push('13B is audit-only. Fastly SDKs are references, not dependencies. No adapter, WIT, fastly.toml, or deployment package is generated here.');
|
|
376
|
+
lines.push('');
|
|
377
|
+
lines.push('## Verdict');
|
|
378
|
+
lines.push('');
|
|
379
|
+
lines.push(`- Preferred posture: \`${artifact.verdict.preferredStrategy}\``);
|
|
380
|
+
lines.push(`- Ready for Fastly adapter implementation: \`${artifact.verdict.readyForFastlyAdapter}\``);
|
|
381
|
+
lines.push(`- Ready for Fastly binding contract: \`${artifact.verdict.readyForFastlyBindingContract}\``);
|
|
382
|
+
lines.push('');
|
|
383
|
+
lines.push('## SDK audit posture');
|
|
384
|
+
lines.push('');
|
|
385
|
+
for (const sdk of sdkAudit.sdks) lines.push(`- ${sdk.name}: ${sdk.support}; dependency decision: ${sdk.dependencyDecision}`);
|
|
386
|
+
lines.push('');
|
|
387
|
+
lines.push('## Capability readiness');
|
|
388
|
+
lines.push('');
|
|
389
|
+
for (const cap of capabilityMap.capabilities) lines.push(`- ${cap.key}: ${cap.readiness}`);
|
|
390
|
+
lines.push('');
|
|
391
|
+
lines.push('## Risks');
|
|
392
|
+
lines.push('');
|
|
393
|
+
for (const risk of riskReport.risks) lines.push(`- ${risk.code} (${risk.severity}): ${risk.message}`);
|
|
394
|
+
lines.push('');
|
|
395
|
+
lines.push('## Next');
|
|
396
|
+
lines.push('');
|
|
397
|
+
lines.push(`- ${adapterPlan.summary.next}`);
|
|
398
|
+
return `${lines.join('\n')}\n`;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function buildFastlyReadiness(inputs = {}, options = {}) {
|
|
402
|
+
const diagnostics = [];
|
|
403
|
+
const sdkAudit = buildSdkAudit();
|
|
404
|
+
const hostcallMap = buildHostcallMap();
|
|
405
|
+
const capabilityMap = buildCapabilityMap(inputs.hostCapabilities, sdkAudit, hostcallMap);
|
|
406
|
+
const packagingPlan = buildPackagingPlan();
|
|
407
|
+
const riskReport = buildRiskReport();
|
|
408
|
+
const adapterPlan = buildAdapterPlan();
|
|
409
|
+
const compiledRuntimeReady = Boolean(inputs.compiledWasmRuntime?.artifact?.summary?.compiled || inputs.pulseWrapper?.artifact?.summary?.compiledWasmRuntime);
|
|
410
|
+
const streamReady = Boolean(inputs.streamingPassthrough?.artifact?.summary?.streamImplemented || inputs.compiledWasmRuntime?.artifact?.summary?.streamResult || inputs.pulseWrapper?.artifact?.summary?.nodeAdapterPath);
|
|
411
|
+
const artifact = normalizeArtifact({
|
|
412
|
+
version: FASTLY_READINESS_VERSION,
|
|
413
|
+
generatedBy: options.generatedBy || PACKAGE_VERSION,
|
|
414
|
+
phase: PHASE,
|
|
415
|
+
posture: {
|
|
416
|
+
auditOnly: true,
|
|
417
|
+
sdkDependency: false,
|
|
418
|
+
sdkReference: true,
|
|
419
|
+
adapterImplemented: false,
|
|
420
|
+
platformSemanticsAdded: false,
|
|
421
|
+
fastlyDefinesPulseSemantics: false,
|
|
422
|
+
officialSdkWrapperNotSelected: true,
|
|
423
|
+
customHostcallAdapterNotImplemented: true
|
|
424
|
+
},
|
|
425
|
+
currentPulseWasmState: {
|
|
426
|
+
compiledWasmRuntime: compiledRuntimeReady,
|
|
427
|
+
highLevelWrapper: Boolean(inputs.pulseWrapper),
|
|
428
|
+
hostRuntimeKernel: Boolean(inputs.compiledWasmRuntime || inputs.pulseWrapper),
|
|
429
|
+
streamPassthroughPrimitive: streamReady,
|
|
430
|
+
schemaJsonSidecars: Boolean(inputs.schemaJsonSidecar),
|
|
431
|
+
librarySidecarMechanism: Boolean(inputs.librarySidecars),
|
|
432
|
+
effectRuntimeContract: Boolean(inputs.effectRuntime || inputs.effectComposition),
|
|
433
|
+
backendFetchExecution: false,
|
|
434
|
+
realGripPackage: false,
|
|
435
|
+
realAssetsPackage: false
|
|
436
|
+
},
|
|
437
|
+
verdict: {
|
|
438
|
+
preferredStrategy: 'hybrid-reference-first-custom-hostcall-adapter-later',
|
|
439
|
+
readyForFastlyReadinessAudit: true,
|
|
440
|
+
readyForFastlyBindingContract: true,
|
|
441
|
+
readyForFastlyAdapter: false,
|
|
442
|
+
reason: 'PulseWasm has a platform-neutral compiled runtime and stream primitive, but Fastly adapter work still needs a hostcall binding contract. SDKs should be audited as references only.'
|
|
443
|
+
},
|
|
444
|
+
artifacts: {
|
|
445
|
+
sdkAudit: 'fastly-sdk-audit.json',
|
|
446
|
+
hostcallMap: 'fastly-hostcall-map.json',
|
|
447
|
+
capabilityMap: 'fastly-capability-map.json',
|
|
448
|
+
packagingPlan: 'fastly-packaging-plan.json',
|
|
449
|
+
riskReport: 'fastly-risk-report.json',
|
|
450
|
+
adapterPlan: 'fastly-adapter-plan.json'
|
|
451
|
+
},
|
|
452
|
+
sourceSummary: sdkAudit.sources,
|
|
453
|
+
summary: {
|
|
454
|
+
sdkReferences: sdkAudit.summary.sdksEvaluated,
|
|
455
|
+
capabilitiesMapped: capabilityMap.summary.capabilities,
|
|
456
|
+
risks: riskReport.summary.risks,
|
|
457
|
+
adapterImplemented: false,
|
|
458
|
+
readyForFastlyAdapter: false,
|
|
459
|
+
readyForFastlyBindingContract: true,
|
|
460
|
+
diagnostics: diagnostics.length
|
|
461
|
+
}
|
|
462
|
+
}, options.cwd || process.cwd());
|
|
463
|
+
const files = [makeFile('generated/host/fastly-readiness.md', buildMarkdown({ artifact, sdkAudit, hostcallMap, capabilityMap, packagingPlan, riskReport, adapterPlan }))];
|
|
464
|
+
return {
|
|
465
|
+
artifact,
|
|
466
|
+
sdkAudit: normalizeArtifact(sdkAudit, options.cwd || process.cwd()),
|
|
467
|
+
hostcallMap: normalizeArtifact(hostcallMap, options.cwd || process.cwd()),
|
|
468
|
+
capabilityMap: normalizeArtifact(capabilityMap, options.cwd || process.cwd()),
|
|
469
|
+
packagingPlan: normalizeArtifact(packagingPlan, options.cwd || process.cwd()),
|
|
470
|
+
riskReport: normalizeArtifact(riskReport, options.cwd || process.cwd()),
|
|
471
|
+
adapterPlan: normalizeArtifact(adapterPlan, options.cwd || process.cwd()),
|
|
472
|
+
files,
|
|
473
|
+
diagnostics
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
module.exports = { buildFastlyReadiness };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fastlyReadiness = require('./fastly-readiness.js');
|
|
4
|
+
const fastlyHostcallBinding = require('./fastly-hostcall-binding.js');
|
|
5
|
+
const fastlyAdapter = require('./fastly-adapter.js');
|
|
6
|
+
const fastlyCommandEntry = require('./fastly-command-entry.js');
|
|
7
|
+
const fastlyConfigSecretServe = require('./fastly-config-secret-serve.js');
|
|
8
|
+
const configProviderResolution = require('./config-provider-resolution.js');
|
|
9
|
+
const runtimeProviderKv = require('./runtime-provider-kv.js');
|
|
10
|
+
const assetsProviderProof = require('./assets-provider-proof.js');
|
|
11
|
+
const routeHandlerEffectProof = require('./route-handler-effect-proof.js');
|
|
12
|
+
const lifecycleParityProof = require('./lifecycle-parity-proof.js');
|
|
13
|
+
const assetsPackageOutParityProof = require('./assets-package-out-parity-proof.js');
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
fastlyReadiness,
|
|
17
|
+
fastlyHostcallBinding,
|
|
18
|
+
fastlyAdapter,
|
|
19
|
+
fastlyCommandEntry,
|
|
20
|
+
fastlyConfigSecretServe,
|
|
21
|
+
configProviderResolution,
|
|
22
|
+
runtimeProviderKv,
|
|
23
|
+
assetsProviderProof,
|
|
24
|
+
routeHandlerEffectProof,
|
|
25
|
+
lifecycleParityProof,
|
|
26
|
+
assetsPackageOutParityProof,
|
|
27
|
+
buildFastlyReadiness: fastlyReadiness.buildFastlyReadiness,
|
|
28
|
+
buildFastlyHostcallBinding: fastlyHostcallBinding.buildFastlyHostcallBinding,
|
|
29
|
+
buildFastlyStreamHeaderAdapter: fastlyAdapter.buildFastlyStreamHeaderAdapter,
|
|
30
|
+
buildFastlyCommandEntry: fastlyCommandEntry.buildFastlyCommandEntry,
|
|
31
|
+
buildFastlyConfigSecretServe: fastlyConfigSecretServe.buildFastlyConfigSecretServe,
|
|
32
|
+
buildConfigProviderResolution: configProviderResolution.buildConfigProviderResolution,
|
|
33
|
+
createPulseRuntimeConfigProvider: configProviderResolution.createPulseRuntimeConfigProvider,
|
|
34
|
+
buildRuntimeProviderKv: runtimeProviderKv.buildRuntimeProviderKv,
|
|
35
|
+
buildKvAdapterIntegration: runtimeProviderKv.buildKvAdapterIntegration,
|
|
36
|
+
buildFastlyAssetsProviderProof: assetsProviderProof.buildFastlyAssetsProviderProof,
|
|
37
|
+
buildFastlyRouteHandlerEffectProof: routeHandlerEffectProof.buildFastlyRouteHandlerEffectProof,
|
|
38
|
+
buildFastlyLifecycleParityProof: lifecycleParityProof.buildFastlyLifecycleParityProof,
|
|
39
|
+
buildFastlyAssetsPackageOutParityProof: assetsPackageOutParityProof.buildFastlyAssetsPackageOutParityProof
|
|
40
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const FASTLY_LIFECYCLE_PARITY_PROOF_VERSION = 'pulsewasm.fastly-lifecycle-parity-proof.v1';
|
|
4
|
+
const FASTLY_LIFECYCLE_PARITY_PROOF_PHASE = '44';
|
|
5
|
+
const FASTLY_LIFECYCLE_PARITY_PROOF_ARTIFACT = 'fastly-lifecycle-parity-proof.json';
|
|
6
|
+
const FASTLY_LIFECYCLE_PARITY_CONTRACT_ID = 'pulse.fastly-lifecycle-parity';
|
|
7
|
+
|
|
8
|
+
const FASTLY_LIFECYCLE_PARITY_SCOPE = Object.freeze({
|
|
9
|
+
provider: 'fastly',
|
|
10
|
+
compilerOrchestrationOnly: true,
|
|
11
|
+
compilerOwnsProviderBehavior: false,
|
|
12
|
+
providerOwnsOriginBehavior: true,
|
|
13
|
+
symbolicBackendConfig: true,
|
|
14
|
+
getHeadOriginMappingImplemented: true,
|
|
15
|
+
statusHeaderBodyPassthroughImplemented: true,
|
|
16
|
+
resolveContinuationLifecycleImplemented: true,
|
|
17
|
+
jsonTextBodyModePolicyImplemented: true,
|
|
18
|
+
actualNetworkFetch: false,
|
|
19
|
+
noExternalFastlyServiceRequired: true,
|
|
20
|
+
postJsonRequestBodyReadiness: 'plan-only',
|
|
21
|
+
binaryRequestBody: false,
|
|
22
|
+
streamRequestBody: false,
|
|
23
|
+
multipartRequestBody: false,
|
|
24
|
+
asyncAwait: false,
|
|
25
|
+
promises: false,
|
|
26
|
+
asyncify: false
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const FASTLY_LIFECYCLE_PARITY_POLICY = Object.freeze({
|
|
30
|
+
provider: 'fastly',
|
|
31
|
+
proofMode: 'symbolic-fastly-fixture-parity',
|
|
32
|
+
validates: [
|
|
33
|
+
'symbolic-backend-config',
|
|
34
|
+
'GET origin mapping',
|
|
35
|
+
'HEAD origin mapping',
|
|
36
|
+
'status/header/body passthrough',
|
|
37
|
+
'ctx.resolve continuation lifecycle metadata',
|
|
38
|
+
'json/text response body mode policy',
|
|
39
|
+
'explicit POST JSON plan-only classification'
|
|
40
|
+
],
|
|
41
|
+
doesNotRequire: [
|
|
42
|
+
'external Fastly service',
|
|
43
|
+
'actual network fetch',
|
|
44
|
+
'compiled-Wasm Fastly suspend/resume bridge',
|
|
45
|
+
'user-authored async',
|
|
46
|
+
'Promise lowering',
|
|
47
|
+
'Asyncify'
|
|
48
|
+
],
|
|
49
|
+
postJsonRequestBodyReadiness: 'plan-only',
|
|
50
|
+
reservedBodyModes: ['binary', 'stream', 'multipart/form-data']
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const FASTLY_LIFECYCLE_PARITY_DIAGNOSTICS = Object.freeze({
|
|
54
|
+
planRequired: 'PULSEWASM_FASTLY_LIFECYCLE_PARITY_PLAN_REQUIRED',
|
|
55
|
+
getRouteMissing: 'PULSEWASM_FASTLY_LIFECYCLE_PARITY_GET_ROUTE_MISSING',
|
|
56
|
+
headRouteMissing: 'PULSEWASM_FASTLY_LIFECYCLE_PARITY_HEAD_ROUTE_MISSING',
|
|
57
|
+
backendMissing: 'PULSEWASM_FASTLY_LIFECYCLE_PARITY_BACKEND_MISSING',
|
|
58
|
+
smokeFailed: 'PULSEWASM_FASTLY_LIFECYCLE_PARITY_SMOKE_FAILED',
|
|
59
|
+
postJsonBodyPlanOnly: 'PULSEWASM_FASTLY_POST_JSON_BODY_PLAN_ONLY'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
FASTLY_LIFECYCLE_PARITY_PROOF_VERSION,
|
|
64
|
+
FASTLY_LIFECYCLE_PARITY_PROOF_PHASE,
|
|
65
|
+
FASTLY_LIFECYCLE_PARITY_PROOF_ARTIFACT,
|
|
66
|
+
FASTLY_LIFECYCLE_PARITY_CONTRACT_ID,
|
|
67
|
+
FASTLY_LIFECYCLE_PARITY_SCOPE,
|
|
68
|
+
FASTLY_LIFECYCLE_PARITY_POLICY,
|
|
69
|
+
FASTLY_LIFECYCLE_PARITY_DIAGNOSTICS
|
|
70
|
+
};
|