@pulse-compute/provider-fastly 1.0.0-beta.1 → 1.0.0-beta.11
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 +7 -6
- package/dist/config-schema.json +30 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +11 -10
- package/src/build/canonical-target.js +41 -2
- package/src/build/digest-native.js +25 -0
- package/src/build/effect-invocations.js +51 -0
- package/src/build/embedded-assets.as.ts +32 -0
- package/src/build/entities-native.js +6 -11
- package/src/build/host-extensions.js +254 -0
- package/src/build/kv-native.as.ts +331 -0
- package/src/build/kv-native.js +27 -0
- package/src/build/lifecycle.js +50 -0
- package/src/build/native-application-errors.js +159 -0
- package/src/build/native-binary-resources.js +30 -0
- package/src/build/native-body-forwarding.as.ts +325 -0
- package/src/build/native-body-forwarding.js +73 -0
- package/src/build/native-dynamic-backends.as.ts +115 -0
- package/src/build/native-dynamic-backends.js +52 -0
- package/src/build/native-fetch-accounting.as.ts +133 -0
- package/src/build/native-fetch-accounting.js +84 -0
- package/src/build/native-fetch-bounds.js +47 -0
- package/src/build/native-fetch-cohorts.as.ts +126 -0
- package/src/build/native-fetch-cohorts.js +65 -0
- package/src/build/native-fetch-deadlines.as.ts +169 -0
- package/src/build/native-fetch-deadlines.js +36 -0
- package/src/build/native-fetch-errors.js +38 -0
- package/src/build/native-fetch-headers.js +52 -0
- package/src/build/native-fetch-outcomes.js +87 -0
- package/src/build/native-fetch-timing.as.ts +74 -0
- package/src/build/native-fetch-timing.js +50 -0
- package/src/build/native-forwarding-accounting.as.ts +153 -0
- package/src/build/native-forwarding-accounting.js +47 -0
- package/src/build/native-handle-ownership.js +127 -0
- package/src/build/native-header-lookup.js +50 -0
- package/src/build/native-http-effects.js +164 -35
- package/src/build/native-platform-capabilities.js +815 -220
- package/src/build/native-request-body.js +45 -0
- package/src/build/native-request-headers.js +93 -0
- package/src/build/native-send-error-detail.as.ts +12 -0
- package/src/build/native-stdio.js +45 -0
- package/src/build/native-string-values.js +143 -0
- package/src/build/native-transport-details.as.ts +84 -0
- package/src/build/native-value-budget.js +90 -0
- package/src/build/package-native-application.js +114 -0
- package/src/build/request-budget-source.js +58 -0
- package/src/build/request-budget.js +46 -0
- package/src/build/response-authority.js +56 -0
- package/src/build/s3-body.as.ts +97 -0
- package/src/build/s3-native.as.ts +228 -0
- package/src/build/s3-native.js +30 -0
- package/src/build/schema-admission.js +83 -0
- package/src/build/schema-nested-json.js +93 -0
- package/src/build/schema-scalar-record.js +41 -0
- package/src/build/time-native.js +49 -0
- package/src/build/wasm-budget.js +28 -0
- package/src/config-api.js +6 -0
- package/src/config-schema.json +30 -0
- package/src/index.ts +25 -0
- package/src/javascript/fetch-adapter.js +3 -0
- package/src/javascript/jwt-verifier.js +14 -4
- package/src/javascript/logging.js +11 -2
- package/src/javascript/package-effects.js +5 -2
- package/src/javascript/runtime-host.js +8 -3
- package/src/javascript/source-package.js +4 -2
- package/src/javascript/target-support-policy.js +20 -3
- package/src/javascript/target.js +17 -1
- package/src/javascript/test-runtime.js +1 -0
- package/src/provider-contract.js +23 -6
- package/src/runtime/canonical-api-runtime.js +6 -0
- package/src/testing/conditional-kv-host.js +155 -0
- package/src/testing/native-dynamic-backends-host.js +39 -0
- package/src/testing/native-http-effects-host.js +18 -1
- package/src/testing/native-platform-capabilities-host.js +359 -24
- package/src/toolchain/body-forwarding.js +25 -0
- package/src/toolchain/config.js +16 -0
- package/src/toolchain/index.js +49 -5
- package/src/toolchain/logging.js +18 -0
- package/src/toolchain/native-execution.js +83 -0
- package/src/toolchain/s3.js +26 -0
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { fetchHeadersSource } = require('./native-fetch-headers.js');
|
|
4
|
+
|
|
5
|
+
const dynamicBackends = require('./native-dynamic-backends.js');
|
|
6
|
+
|
|
7
|
+
const { scalarRecordProjectionLines } = require('./schema-scalar-record.js');
|
|
8
|
+
const { hasJsonAdmission, tracksJsonDuplicates, schemaParserDepth, nestedJsonProjectionLines, schemaJsonAdmissionSource, schemaFetchJsonSource } = require('./schema-nested-json.js');
|
|
9
|
+
const { nativeLoggingSource } = require('./native-stdio.js');
|
|
10
|
+
const applicationErrors = require('./native-application-errors.js');
|
|
11
|
+
const effectInvocations = require('./effect-invocations.js');
|
|
12
|
+
const lifecycleBindings = require('./lifecycle.js');
|
|
13
|
+
const hostExtensions = require('./host-extensions.js');
|
|
14
|
+
const packageApplication = require('./package-native-application.js');
|
|
15
|
+
const nativeValueBudget = require('./native-value-budget.js');
|
|
16
|
+
const { schemaNeedsValueProjection } = require('@pulse-compute/wasm-contracts/schema-json/registry');
|
|
17
|
+
|
|
3
18
|
const crypto = require('node:crypto');
|
|
19
|
+
const { nativeStringAssembly, nativeStringFields, nativeStringConcat, nativeStringTrim, nativeStringIndex, needsNativeValueFailureGuard } = require('./native-string-values.js');
|
|
20
|
+
const conditionalKv = require('./kv-native.js');
|
|
4
21
|
const fs = require('node:fs');
|
|
5
22
|
const os = require('node:os');
|
|
6
23
|
const path = require('node:path');
|
|
@@ -12,6 +29,7 @@ const {
|
|
|
12
29
|
} = require('@pulse-compute/wasm-contracts/package/package-contract');
|
|
13
30
|
const {
|
|
14
31
|
EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION,
|
|
32
|
+
prepareReportCapture,
|
|
15
33
|
appendAssemblyScriptOptimizationArgs
|
|
16
34
|
} = require('@pulse-compute/wasm-build-support/native-optimization');
|
|
17
35
|
|
|
@@ -77,6 +95,7 @@ function loadGuestToolchain() {
|
|
|
77
95
|
|
|
78
96
|
const { resolveAsc } = loadBuildSupport();
|
|
79
97
|
const { generateCanonicalNativeAssemblyScript } = loadNativeGenerator();
|
|
98
|
+
const nativeHandleOwnership = require('./native-handle-ownership.js');
|
|
80
99
|
const { pulseJwtAssemblyScriptSource } = loadJwtNativeSource();
|
|
81
100
|
const {
|
|
82
101
|
GUEST_LINK_STAGE_INVOCATION_VERSION,
|
|
@@ -89,25 +108,35 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION = 'pulse.fastly-native-platfor
|
|
|
89
108
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_GENERATOR_VERSION = 'pulse.fastly-native-platform-capabilities-generator.v1';
|
|
90
109
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION = 'pulse.fastly-native-platform-capabilities-compiler.v1';
|
|
91
110
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION = 1;
|
|
92
|
-
|
|
111
|
+
// Internal compilation budget, not a Fastly service limit. Keep a finite guard
|
|
112
|
+
// while allowing composed applications beyond the original 1 MiB proof fixture.
|
|
113
|
+
const { DEFAULT_FASTLY_MAX_WASM_BYTES, normalizeFastlyMaxWasmBytes, assertFastlyWasmBudget } = require('./wasm-budget.js');
|
|
114
|
+
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_MAX_WASM_BYTES = DEFAULT_FASTLY_MAX_WASM_BYTES;
|
|
93
115
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES = 65536;
|
|
94
116
|
const FASTLY_NATIVE_SIZE_OPTIMIZATION = EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION;
|
|
95
117
|
|
|
96
118
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
|
|
119
|
+
...require('./native-body-forwarding.js').IMPORTS.map(key => Object.freeze(key.split(':'))),
|
|
120
|
+
...conditionalKv.KV_IMPORTS.map((key) => Object.freeze(key.split(':'))),
|
|
121
|
+
...require('./s3-native.js').S3_IMPORTS.map((key) => Object.freeze(key.split(':'))),
|
|
97
122
|
Object.freeze(['fastly_abi', 'init']),
|
|
98
123
|
Object.freeze(['fastly_http_req', 'body_downstream_get']),
|
|
99
124
|
Object.freeze(['fastly_http_req', 'method_get']),
|
|
100
125
|
Object.freeze(['fastly_http_req', 'uri_get']),
|
|
101
126
|
Object.freeze(['fastly_http_req', 'header_value_get']),
|
|
127
|
+
Object.freeze(['fastly_http_req', 'header_names_get']),
|
|
128
|
+
Object.freeze(['fastly_http_req', 'header_values_get']),
|
|
102
129
|
Object.freeze(['fastly_http_req', 'new']),
|
|
103
130
|
Object.freeze(['fastly_http_req', 'method_set']),
|
|
104
131
|
Object.freeze(['fastly_http_req', 'uri_set']),
|
|
105
132
|
Object.freeze(['fastly_http_req', 'header_insert']),
|
|
133
|
+
Object.freeze(['fastly_http_req', 'header_append']),
|
|
106
134
|
Object.freeze(['fastly_http_req', 'send_async']),
|
|
107
|
-
Object.freeze(['fastly_http_req', '
|
|
135
|
+
Object.freeze(['fastly_http_req', 'pending_req_wait_v2']),
|
|
108
136
|
Object.freeze(['fastly_http_resp', 'new']),
|
|
109
137
|
Object.freeze(['fastly_http_resp', 'header_append']),
|
|
110
138
|
Object.freeze(['fastly_http_resp', 'header_value_get']),
|
|
139
|
+
Object.freeze(['fastly_http_resp', 'header_values_get']),
|
|
111
140
|
Object.freeze(['fastly_http_resp', 'status_get']),
|
|
112
141
|
Object.freeze(['fastly_http_resp', 'status_set']),
|
|
113
142
|
Object.freeze(['fastly_http_resp', 'send_downstream']),
|
|
@@ -124,10 +153,15 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
|
|
|
124
153
|
Object.freeze(['fastly_kv_store', 'lookup_wait_v2']),
|
|
125
154
|
Object.freeze(['fastly_kv_store', 'insert']),
|
|
126
155
|
Object.freeze(['fastly_kv_store', 'insert_wait']),
|
|
156
|
+
Object.freeze(['fastly_kv_store', 'list']),
|
|
157
|
+
Object.freeze(['fastly_kv_store', 'list_wait']),
|
|
158
|
+
Object.freeze(['fastly_kv_store', 'delete']),
|
|
159
|
+
Object.freeze(['fastly_kv_store', 'delete_wait']),
|
|
127
160
|
Object.freeze(['fastly_log', 'endpoint_get']),
|
|
128
161
|
Object.freeze(['fastly_log', 'write']),
|
|
162
|
+
Object.freeze(['wasi_snapshot_preview1', 'fd_write']),
|
|
129
163
|
Object.freeze(['wasi_snapshot_preview1', 'clock_time_get'])
|
|
130
|
-
]);
|
|
164
|
+
].filter(([module, name], index, entries) => entries.findIndex((item) => item[0] === module && item[1] === name) === index));
|
|
131
165
|
|
|
132
166
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_REQUIRED_IMPORTS = Object.freeze([
|
|
133
167
|
Object.freeze(['fastly_abi', 'init']),
|
|
@@ -148,38 +182,70 @@ const FASTLY_NATIVE_PLATFORM_EFFECT_KIND = Object.freeze({
|
|
|
148
182
|
'grip.publish': 8,
|
|
149
183
|
'grip.broadcast': 9,
|
|
150
184
|
'assets.lookup': 10,
|
|
151
|
-
'jwt.verify': 11
|
|
185
|
+
'jwt.verify': 11,
|
|
186
|
+
'jwt.sign': 20,
|
|
187
|
+
's3.head': 12,
|
|
188
|
+
's3.getText': 13,
|
|
189
|
+
's3.putText': 14,
|
|
190
|
+
's3.getBody': 21,
|
|
191
|
+
'kv.delete': 22,
|
|
192
|
+
'kv.list': 23,
|
|
193
|
+
'kv.getVersioned': 15,
|
|
194
|
+
'kv.insertIfAbsent': 16,
|
|
195
|
+
'kv.compareAndSwap': 17,
|
|
196
|
+
'time.now': 18,
|
|
197
|
+
'crypto.digestText': 19,
|
|
198
|
+
'host.extension': 24
|
|
152
199
|
});
|
|
153
200
|
|
|
154
201
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS = new Set([
|
|
202
|
+
'fastly_http_req:register_dynamic_backend',
|
|
155
203
|
...FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS.map(([moduleName, name]) => `${moduleName}:${name}`)
|
|
156
204
|
]);
|
|
157
205
|
|
|
158
206
|
const FASTLY_NATIVE_PLATFORM_EFFECT_KINDS = Object.freeze([
|
|
207
|
+
'host.extension',
|
|
208
|
+
'time.now',
|
|
209
|
+
'crypto.digestText',
|
|
159
210
|
'fetch',
|
|
160
211
|
'config.get',
|
|
161
212
|
'secret.get',
|
|
162
213
|
'kv.get',
|
|
163
214
|
'kv.put',
|
|
215
|
+
...conditionalKv.KV_CONDITIONAL_KINDS,
|
|
164
216
|
'grip.channel',
|
|
165
217
|
'grip.hold',
|
|
166
218
|
'grip.publish',
|
|
167
219
|
'grip.broadcast',
|
|
168
220
|
'assets.lookup',
|
|
169
|
-
'jwt.verify'
|
|
221
|
+
'jwt.verify',
|
|
222
|
+
'jwt.sign',
|
|
223
|
+
's3.head',
|
|
224
|
+
's3.getText',
|
|
225
|
+
's3.putText',
|
|
226
|
+
's3.getBody'
|
|
170
227
|
]);
|
|
171
228
|
|
|
172
229
|
const FASTLY_NATIVE_PLATFORM_CAPABILITY_KINDS = new Set([
|
|
230
|
+
'host.extension',
|
|
231
|
+
'time.now',
|
|
232
|
+
'crypto.digestText',
|
|
173
233
|
'config.get',
|
|
174
234
|
'secret.get',
|
|
175
235
|
'kv.get',
|
|
176
236
|
'kv.put',
|
|
237
|
+
...conditionalKv.KV_CONDITIONAL_KINDS,
|
|
177
238
|
'assets.lookup',
|
|
178
239
|
'grip.channel',
|
|
179
240
|
'grip.hold',
|
|
180
241
|
'grip.publish',
|
|
181
242
|
'grip.broadcast',
|
|
182
|
-
'jwt.verify'
|
|
243
|
+
'jwt.verify',
|
|
244
|
+
'jwt.sign',
|
|
245
|
+
's3.head',
|
|
246
|
+
's3.getText',
|
|
247
|
+
's3.putText',
|
|
248
|
+
's3.getBody'
|
|
183
249
|
]);
|
|
184
250
|
|
|
185
251
|
class FastlyNativePlatformCapabilitiesError extends Error {
|
|
@@ -241,45 +307,38 @@ function packageFacts(_plan, options = {}) {
|
|
|
241
307
|
});
|
|
242
308
|
}
|
|
243
309
|
|
|
310
|
+
function jwtVerificationAlgorithm(effect) {
|
|
311
|
+
return effect.resource.keyType === 'secret' ? 'HS256' : String(effect.resource.keyArtifactId).startsWith('pulse.jwt.rs256-key.') ? 'RS256' : 'ES256';
|
|
312
|
+
}
|
|
313
|
+
|
|
244
314
|
function selectedJwtCrypto(plan) {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
&& selected.kind === 'guest-linked'
|
|
267
|
-
&& selected.targetImplemented === true;
|
|
268
|
-
if ((!hs256 && !es256) || selected.automaticFallback !== false) {
|
|
269
|
-
fail(
|
|
270
|
-
'Fastly Native JWT selected an unavailable or non-exact crypto realization.',
|
|
271
|
-
'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID',
|
|
272
|
-
{ selected, automaticFallback: false }
|
|
273
|
-
);
|
|
315
|
+
const effects = plan.effects || [];
|
|
316
|
+
const signing = effects.filter(effect => effect.kind === 'jwt.sign');
|
|
317
|
+
const verifies = effects.filter(effect => effect.kind === 'jwt.verify');
|
|
318
|
+
const required = new Set([
|
|
319
|
+
...signing.map(effect => literalObjectField(inputExpression(effect, 'payload'), 'algorithm') === 'HS256' ? 'HMAC-SHA256' : literalObjectField(inputExpression(effect, 'payload'), 'algorithm')),
|
|
320
|
+
...verifies.map(effect => jwtVerificationAlgorithm(effect))
|
|
321
|
+
]);
|
|
322
|
+
if (!required.size) return undefined;
|
|
323
|
+
const definitions = {
|
|
324
|
+
'HMAC-SHA256': ['guest-source:pulse-hmac-as', 'pulse-hmac-as.v1', 'guest-source'],
|
|
325
|
+
HS256: ['guest-source:pulse-hmac-as', 'pulse-hmac-as.v1', 'guest-source'],
|
|
326
|
+
RS256: ['guest-linked:pulse-rs256-bearssl-i31', 'bearssl.0.6.rsa-i31.sha256.v1', 'guest-linked'],
|
|
327
|
+
ES256: ['guest-linked:pulse-es256-rustcrypto-p256', 'rustcrypto.p256-0.13.2.ecdsa-0.16.9.sha2-0.10.9.v1', 'guest-linked']
|
|
328
|
+
};
|
|
329
|
+
for (const algorithm of required) {
|
|
330
|
+
const selected = plan.crypto?.algorithms.find(entry => entry.algorithm === algorithm);
|
|
331
|
+
const expected = definitions[algorithm];
|
|
332
|
+
if (!expected || !selected || selected.realization !== expected[0] || selected.implementation !== expected[1]
|
|
333
|
+
|| selected.kind !== expected[2] || selected.targetImplemented !== true || selected.automaticFallback !== false) {
|
|
334
|
+
fail('JWT requires its explicitly selected Crypto realization.', 'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID');
|
|
335
|
+
}
|
|
274
336
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
guestUnitRequired: es256,
|
|
281
|
-
automaticFallback: false
|
|
282
|
-
});
|
|
337
|
+
const verificationAlgorithms = [...new Set(verifies.map(effect => jwtVerificationAlgorithm(effect)))];
|
|
338
|
+
if (verificationAlgorithms.length > 1) fail('Fastly Native JWT verification requires one exact algorithm.', 'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID');
|
|
339
|
+
const algorithm = verificationAlgorithms[0] || (required.has('RS256') ? 'RS256' : required.has('ES256') ? 'ES256' : 'HMAC-SHA256');
|
|
340
|
+
return Object.freeze({ ...plan.crypto.algorithms.find(entry => entry.algorithm === algorithm),
|
|
341
|
+
guestUnitRequired: required.has('ES256') || required.has('RS256'), signingAlgorithms: signing.map(effect => literalObjectField(inputExpression(effect, 'payload'), 'algorithm')) });
|
|
283
342
|
}
|
|
284
343
|
|
|
285
344
|
function decodeP256Coordinate(value, artifactId, field) {
|
|
@@ -301,6 +360,26 @@ function decodeP256Coordinate(value, artifactId, field) {
|
|
|
301
360
|
return bytes;
|
|
302
361
|
}
|
|
303
362
|
|
|
363
|
+
function normalizedRs256Jwk(value, artifactId) {
|
|
364
|
+
const invalid = () => fail('Invalid bounded RSA public key artifact.', 'PULSE_FASTLY_NATIVE_JWT_KEY_ARTIFACT_INVALID', { artifactId });
|
|
365
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
366
|
+
|| Object.keys(value).some(name => !['kty', 'n', 'e', 'alg', 'use', 'key_ops', 'kid'].includes(name))
|
|
367
|
+
|| value.kty !== 'RSA' || value.alg !== undefined && value.alg !== 'RS256'
|
|
368
|
+
|| value.use !== undefined && value.use !== 'sig'
|
|
369
|
+
|| value.key_ops !== undefined && (!Array.isArray(value.key_ops) || value.key_ops.length !== 1 || value.key_ops[0] !== 'verify')
|
|
370
|
+
|| value.kid !== undefined && (typeof value.kid !== 'string' || !value.kid.length || value.kid.includes('\0') || Buffer.byteLength(value.kid) > 256)) invalid();
|
|
371
|
+
const decode = (text, maximum) => {
|
|
372
|
+
if (typeof text !== 'string' || text.length > Math.ceil(maximum * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(text)) invalid();
|
|
373
|
+
const bytes = Buffer.from(text, 'base64url');
|
|
374
|
+
if (!bytes.length || bytes.length > maximum || bytes[0] === 0 || bytes.toString('base64url') !== text) invalid();
|
|
375
|
+
return bytes;
|
|
376
|
+
};
|
|
377
|
+
const n = decode(value.n, 512), e = decode(value.e, 4), k = n.length;
|
|
378
|
+
if (![256, 384, 512].includes(k) || n[0] < 128 || !(n[k-1] & 1) || e.readUIntBE(0, e.length) < 3 || !(e.at(-1) & 1)) invalid();
|
|
379
|
+
const bytes = Buffer.alloc(8+k); bytes.writeUInt32LE(k); n.copy(bytes, 4); e.copy(bytes, 8+k-e.length);
|
|
380
|
+
return Object.freeze({ kid: value.kid, bytes });
|
|
381
|
+
}
|
|
382
|
+
|
|
304
383
|
function normalizedEs256Jwk(value, artifactId) {
|
|
305
384
|
if (
|
|
306
385
|
!value
|
|
@@ -347,13 +426,14 @@ function resolveEs256KeyArtifacts(plan, facts) {
|
|
|
347
426
|
}
|
|
348
427
|
const artifactId = effect.resource.keyArtifactId;
|
|
349
428
|
const artifact = artifacts.get(artifactId);
|
|
429
|
+
const algorithm = jwtVerificationAlgorithm(effect).toLowerCase();
|
|
350
430
|
if (
|
|
351
431
|
!artifact
|
|
352
|
-
|| artifact.version !==
|
|
432
|
+
|| artifact.version !== `pulse.jwt-${algorithm}-key-artifact.v1`
|
|
353
433
|
|| artifact.contractId !== 'pulse.jwt'
|
|
354
434
|
|| artifact.package !== '@pulse-compute/jwt'
|
|
355
|
-
|| artifact.kind !==
|
|
356
|
-
|| artifact.mediaType !==
|
|
435
|
+
|| artifact.kind !== `jwt-${algorithm}-static-public-key`
|
|
436
|
+
|| artifact.mediaType !== `application/vnd.pulse.jwt-${algorithm}-key+json`
|
|
357
437
|
|| artifact.id !== artifactId
|
|
358
438
|
|| artifact.materialHash !== sha256(stableStringify(artifact.data))
|
|
359
439
|
|| !artifact.data
|
|
@@ -379,7 +459,7 @@ function resolveEs256KeyArtifacts(plan, facts) {
|
|
|
379
459
|
{ effectId: effect.id, artifactId, keyCount: keys && keys.length }
|
|
380
460
|
);
|
|
381
461
|
}
|
|
382
|
-
const normalized = keys.map((key) => normalizedEs256Jwk(key, artifactId));
|
|
462
|
+
const normalized = keys.map((key) => algorithm === 'rs256' ? normalizedRs256Jwk(key, artifactId) : normalizedEs256Jwk(key, artifactId));
|
|
383
463
|
const kids = normalized.map((key) => key.kid).filter((kid) => kid !== undefined);
|
|
384
464
|
if (new Set(kids).size !== kids.length) {
|
|
385
465
|
fail(
|
|
@@ -455,8 +535,71 @@ function es256KeyArtifactSource(records) {
|
|
|
455
535
|
return lines.join('\n');
|
|
456
536
|
}
|
|
457
537
|
|
|
538
|
+
function jwtSigningCryptoCompositionSource(selected) {
|
|
539
|
+
const algorithms = selected?.signingAlgorithms || [];
|
|
540
|
+
const hs = algorithms.includes('HS256');
|
|
541
|
+
const es = algorithms.includes('ES256');
|
|
542
|
+
const rs = algorithms.includes('RS256');
|
|
543
|
+
return `function __pulse_fastly_jwt_crypto_sign(algorithm: string, key: Uint8Array, data: Uint8Array, output: Uint8Array): i32 {
|
|
544
|
+
${hs ? `if (algorithm == "HS256") return pulse_crypto_hmac_sha256_bytes_v1(key.dataStart, key.length, data.dataStart, data.length, output.dataStart, output.length)` : ''}
|
|
545
|
+
${rs ? `if (algorithm == "RS256") return __pulse_fastly_rs256(true, key, data, output)` : ''}
|
|
546
|
+
${es ? `if (algorithm == "ES256") {
|
|
547
|
+
if (key.length != 96 || output.length != 64 || data.length > 16340) return -2
|
|
548
|
+
const pointer: usize = 524288
|
|
549
|
+
memory.fill(pointer, 0, 16640)
|
|
550
|
+
store<u32>(pointer, 0x32534550); store<u32>(pointer + 4, 2); store<u32>(pointer + 8, 64)
|
|
551
|
+
store<u32>(pointer + 12, (224 + data.length + 15) & ~15); store<u32>(pointer + 16, 1)
|
|
552
|
+
store<u32>(pointer + 24, 224); store<u32>(pointer + 28, data.length)
|
|
553
|
+
store<u32>(pointer + 32, 64); store<u32>(pointer + 36, 96)
|
|
554
|
+
store<u32>(pointer + 40, 160); store<u32>(pointer + 44, 64)
|
|
555
|
+
memory.copy(pointer + 64, key.dataStart, 96); memory.copy(pointer + 224, data.dataStart, data.length)
|
|
556
|
+
const status = __pulse_crypto_es256_sign_anchor(i32(pointer), 16640)
|
|
557
|
+
if (status == 1) memory.copy(output.dataStart, pointer + 160, 64)
|
|
558
|
+
memory.fill(pointer, 0, 16640); memory.fill(0, 0, 65536)
|
|
559
|
+
return status
|
|
560
|
+
}` : ''}
|
|
561
|
+
return -3
|
|
562
|
+
}`;
|
|
563
|
+
}
|
|
564
|
+
|
|
458
565
|
function jwtCryptoCompositionSource(selected) {
|
|
459
|
-
|
|
566
|
+
const rsa = selected && (selected.algorithm === 'RS256' || selected.signingAlgorithms?.includes('RS256'));
|
|
567
|
+
const helper = rsa ? rs256CompositionSource() : '';
|
|
568
|
+
return helper + jwtVerificationCompositionSource(selected);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function rs256CompositionSource() {
|
|
572
|
+
return `function __pulse_fastly_rs256(signing: bool, key: Uint8Array, data: Uint8Array, signature: Uint8Array): i32 {
|
|
573
|
+
if (key.length < 8 || data.length > 12288) return -2
|
|
574
|
+
const k = load<u32>(key.dataStart)
|
|
575
|
+
if ((k != 256 && k != 384 && k != 512) || signature.length != i32(k) || key.length != i32(signing ? 8 + k * 9 / 2 : 8 + k)) return -2
|
|
576
|
+
const pointer: usize = 524288
|
|
577
|
+
const output = (64 + key.length + 15) & ~15
|
|
578
|
+
const input = output + signature.length
|
|
579
|
+
memory.fill(pointer, 0, 16640)
|
|
580
|
+
store<u32>(pointer, 0x32534550); store<u32>(pointer + 4, 2); store<u32>(pointer + 8, 64)
|
|
581
|
+
store<u32>(pointer + 12, (input + data.length + 15) & ~15); store<u32>(pointer + 16, 2)
|
|
582
|
+
store<u32>(pointer + 24, input); store<u32>(pointer + 28, data.length)
|
|
583
|
+
store<u32>(pointer + 32, 64); store<u32>(pointer + 36, key.length)
|
|
584
|
+
store<u32>(pointer + 40, output); store<u32>(pointer + 44, k)
|
|
585
|
+
memory.copy(pointer + 64, key.dataStart, key.length); memory.copy(pointer + input, data.dataStart, data.length)
|
|
586
|
+
if (!signing) memory.copy(pointer + output, signature.dataStart, signature.length)
|
|
587
|
+
const status = signing ? __pulse_crypto_rs256_sign_anchor(i32(pointer), 16640) : __pulse_crypto_rs256_verify_anchor(i32(pointer), 16640)
|
|
588
|
+
if (signing && status == 1) memory.copy(signature.dataStart, pointer + output, signature.length)
|
|
589
|
+
memory.fill(pointer, 0, 16640); memory.fill(0, 0, 65536)
|
|
590
|
+
return status
|
|
591
|
+
}\n`;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function jwtVerificationCompositionSource(selected) {
|
|
595
|
+
if (selected?.algorithm === 'RS256') return `
|
|
596
|
+
function __pulse_fastly_jwt_capture_signing_input(input: Uint8Array): void { __pulse_fastly_jwt_record_signing_input(input.byteLength, "") }
|
|
597
|
+
function __pulse_fastly_jwt_crypto_verify(algorithm: string, key: Uint8Array, data: Uint8Array, signature: Uint8Array): i32 {
|
|
598
|
+
if (algorithm != "RS256") return -2
|
|
599
|
+
return __pulse_fastly_rs256(false, key, data, signature)
|
|
600
|
+
}`;
|
|
601
|
+
|
|
602
|
+
if (!selected || selected.algorithm === 'HMAC-SHA256') {
|
|
460
603
|
return [
|
|
461
604
|
'function __pulse_fastly_jwt_capture_signing_input(input: Uint8Array): void { void input }',
|
|
462
605
|
'function __pulse_fastly_jwt_crypto_verify(algorithm: string, key: Uint8Array, data: Uint8Array, signature: Uint8Array): i32 { void algorithm; void key; void data; void signature; return -3 }'
|
|
@@ -541,6 +684,7 @@ function fastlyGuestLinkedStartWrapperWat() {
|
|
|
541
684
|
}
|
|
542
685
|
|
|
543
686
|
function validatePlanBoundary(plan, options = {}) {
|
|
687
|
+
require('./native-body-forwarding.js').validate(plan, options);
|
|
544
688
|
if (!plan || typeof plan !== 'object') throw new TypeError('Fastly native platform capability realization requires a canonical native plan.');
|
|
545
689
|
if (plan.version !== CANONICAL_NATIVE_PLAN_VERSION) {
|
|
546
690
|
fail(`Fastly native platform capability realization requires ${CANONICAL_NATIVE_PLAN_VERSION}.`, 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_PLAN_VERSION_UNSUPPORTED', { version: plan.version });
|
|
@@ -548,6 +692,7 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
548
692
|
if (!plan.ownership || plan.ownership.providerNeutral !== true || plan.ownership.javascriptRuntime !== false) {
|
|
549
693
|
fail('Fastly native platform capability realization requires a provider-neutral plan without JavaScript runtime ownership.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_PLAN_OWNERSHIP_INVALID', { ownership: plan.ownership });
|
|
550
694
|
}
|
|
695
|
+
const selectedExtensions = hostExtensions.select(plan, options);
|
|
551
696
|
const effects = plan.effects || [];
|
|
552
697
|
const unsupported = effects.filter((effect) => !FASTLY_NATIVE_PLATFORM_EFFECT_KINDS.includes(effect.kind));
|
|
553
698
|
if (unsupported.length > 0) {
|
|
@@ -569,13 +714,17 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
569
714
|
&& effect.kind === 'assets.lookup';
|
|
570
715
|
const jwt = effect.package === '@pulse-compute/jwt'
|
|
571
716
|
&& effect.contractId === 'pulse.jwt'
|
|
572
|
-
&&
|
|
573
|
-
&& effect.operation ===
|
|
574
|
-
|
|
575
|
-
|
|
717
|
+
&& ['verify', 'sign'].includes(effect.operation)
|
|
718
|
+
&& effect.kind === `jwt.${effect.operation}` && effect.capability === effect.kind;
|
|
719
|
+
const s3 = effect.package === '@pulse-compute/s3' && effect.contractId === 'pulse.s3'
|
|
720
|
+
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
721
|
+
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
722
|
+
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
723
|
+
const extension = effect.kind === 'host.extension' && selectedExtensions.some(item => hostExtensions.matches(effect, item.artifact));
|
|
724
|
+
return !grip && !assets && !jwt && !s3 && !digest && !extension;
|
|
576
725
|
});
|
|
577
726
|
if (unsupportedPackages.length > 0) {
|
|
578
|
-
fail('Fastly native realization only accepts the trusted first-party Assets, GRIP, and
|
|
727
|
+
fail('Fastly native realization only accepts the trusted first-party Assets, GRIP, JWT, S3, and Crypto digest package contracts.', 'PULSE_FASTLY_NATIVE_PLATFORM_PACKAGE_UNSUPPORTED', {
|
|
579
728
|
effects: unsupportedPackages.map((entry) => ({ id: entry.id, kind: entry.kind, package: entry.package, contractId: entry.contractId }))
|
|
580
729
|
});
|
|
581
730
|
}
|
|
@@ -589,15 +738,19 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
589
738
|
&& effect.kind === 'assets.lookup';
|
|
590
739
|
const jwt = effect.package === '@pulse-compute/jwt'
|
|
591
740
|
&& effect.contractId === 'pulse.jwt'
|
|
592
|
-
&&
|
|
593
|
-
&& effect.operation ===
|
|
594
|
-
|
|
595
|
-
|
|
741
|
+
&& ['verify', 'sign'].includes(effect.operation)
|
|
742
|
+
&& effect.kind === `jwt.${effect.operation}` && effect.capability === effect.kind;
|
|
743
|
+
const s3 = effect.package === '@pulse-compute/s3' && effect.contractId === 'pulse.s3'
|
|
744
|
+
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
745
|
+
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
746
|
+
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
747
|
+
const extension = effect.kind === 'host.extension' && selectedExtensions.some(item => hostExtensions.matches(effect, item.artifact));
|
|
748
|
+
return !grip && !assets && !jwt && !s3 && !digest && !extension;
|
|
596
749
|
});
|
|
597
750
|
if (unsupportedDeclarations.length > 0) {
|
|
598
|
-
fail('Fastly native package realization is restricted to the Assets, GRIP, and
|
|
751
|
+
fail('Fastly native package realization is restricted to the Assets, GRIP, JWT, S3, and Crypto digest contracts.', 'PULSE_FASTLY_NATIVE_PLATFORM_PACKAGE_UNSUPPORTED', { effects: unsupportedDeclarations });
|
|
599
752
|
}
|
|
600
|
-
for (const effect of effects.filter((entry) =>
|
|
753
|
+
for (const effect of effects.filter((entry) => ['jwt.verify', 'jwt.sign'].includes(entry.kind))) {
|
|
601
754
|
const resource = effect.resource;
|
|
602
755
|
const knownShape = resource
|
|
603
756
|
&& !Object.keys(resource)
|
|
@@ -612,7 +765,7 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
612
765
|
&& typeof resource.keyArtifactId === 'string'
|
|
613
766
|
&& resource.keyArtifactId.length > 0
|
|
614
767
|
&& resource.secretBinding === null;
|
|
615
|
-
if (!namedSecret && !staticPublicKey) {
|
|
768
|
+
if (!namedSecret && (!staticPublicKey || effect.kind === 'jwt.sign')) {
|
|
616
769
|
fail('Fastly Native JWT requires one exact named-secret or bounded static public-key descriptor.', 'PULSE_FASTLY_NATIVE_JWT_KEY_DESCRIPTOR_INVALID', {
|
|
617
770
|
effectId: effect.id,
|
|
618
771
|
resource,
|
|
@@ -678,8 +831,8 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
678
831
|
const effects = plan.effects || [];
|
|
679
832
|
const hasConfig = effects.some((effect) => effect.kind === 'config.get');
|
|
680
833
|
const hasSecret = effects.some((effect) => (
|
|
681
|
-
effect.kind === 'secret.get'
|
|
682
|
-
|| (
|
|
834
|
+
effect.kind === 'secret.get' || ['s3.head', 's3.getText', 's3.putText', 's3.getBody'].includes(effect.kind)
|
|
835
|
+
|| (['jwt.verify', 'jwt.sign'].includes(effect.kind) && effect.resource && effect.resource.keyType === 'secret')
|
|
683
836
|
));
|
|
684
837
|
const hasGripHold = effects.some((effect) => effect.kind === 'grip.hold');
|
|
685
838
|
const hasGripBroadcast = effects.some((effect) => effect.kind === 'grip.broadcast');
|
|
@@ -703,8 +856,9 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
703
856
|
|
|
704
857
|
const kv = [];
|
|
705
858
|
for (const [index, effect] of effects.entries()) {
|
|
706
|
-
if (effect.kind !== 'kv.get' && effect.kind !== 'kv.put' && effect.kind !== 'assets.lookup') continue;
|
|
859
|
+
if (effect.kind !== 'kv.get' && effect.kind !== 'kv.put' && !conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind) && effect.kind !== 'assets.lookup') continue;
|
|
707
860
|
const packagePayload = inputExpression(effect, 'payload');
|
|
861
|
+
if (effect.kind === 'assets.lookup' && literalObjectField(packagePayload, 'embeddedId')) continue;
|
|
708
862
|
const logical = effect.resource && effect.resource.kind === 'literal'
|
|
709
863
|
&& effect.kind !== 'assets.lookup'
|
|
710
864
|
? String(effect.resource.value)
|
|
@@ -715,6 +869,7 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
715
869
|
resource: effect.resource
|
|
716
870
|
});
|
|
717
871
|
}
|
|
872
|
+
if (conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind) && Buffer.byteLength(logical, 'utf8') > conditionalKv.KV_CONDITIONAL_LIMITS.namespaceBytes) fail('Conditional KV namespace exceeds its portable bound.', 'PULSE_FASTLY_NATIVE_KV_NAMESPACE_DYNAMIC');
|
|
718
873
|
const physical = stringBinding(
|
|
719
874
|
kvMap[logical],
|
|
720
875
|
`Fastly KV binding for ${logical}`,
|
|
@@ -770,11 +925,11 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
770
925
|
backend = byEffect[effect.id]
|
|
771
926
|
|| (origin && byOrigin[origin])
|
|
772
927
|
|| (effect.resource && effect.resource.value && byOrigin[effect.resource.value]);
|
|
773
|
-
if ((typeof backend !== 'string' || backend.trim() === '') && !origin) {
|
|
928
|
+
if ((typeof backend !== 'string' || backend.trim() === '') && !origin && source.dynamicBackends !== true) {
|
|
774
929
|
const configured = [...new Set(Object.values(byOrigin).filter((value) => typeof value === 'string' && value.trim()).map((value) => value.trim()))];
|
|
775
930
|
if (configured.length === 1) backend = configured[0];
|
|
776
931
|
}
|
|
777
|
-
if (typeof backend !== 'string' || backend.trim() === '') {
|
|
932
|
+
if ((typeof backend !== 'string' || backend.trim() === '') && !(source.dynamicBackends === true && !(plan.capabilities || []).includes('request.body.forward'))) {
|
|
778
933
|
fail(`Fastly native effect ${effect.id} has no static backend binding.`, 'PULSE_FASTLY_BACKEND_REQUIRED', {
|
|
779
934
|
effectId: effect.id,
|
|
780
935
|
effectIndex: index,
|
|
@@ -782,7 +937,7 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
782
937
|
hint: `Provide bindings.backends[${JSON.stringify(origin || '<origin>')}] or an explicit effect backend.`
|
|
783
938
|
});
|
|
784
939
|
}
|
|
785
|
-
backend = backend.trim();
|
|
940
|
+
backend = typeof backend === 'string' ? backend.trim() : '';
|
|
786
941
|
}
|
|
787
942
|
const binding = Object.freeze({
|
|
788
943
|
index,
|
|
@@ -791,6 +946,7 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
791
946
|
origin,
|
|
792
947
|
url,
|
|
793
948
|
backend,
|
|
949
|
+
...(!backend && effect.kind === 'fetch' ? { dynamic: true } : {}),
|
|
794
950
|
...(effect.kind === 'grip.broadcast' && gripSecretRef ? { authSecretRef: gripSecretRef } : {})
|
|
795
951
|
});
|
|
796
952
|
backends.push(binding);
|
|
@@ -799,8 +955,12 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
799
955
|
}
|
|
800
956
|
|
|
801
957
|
return Object.freeze({
|
|
958
|
+
maxDurationMs: require('@pulse-compute/runtime/host').normalizeRequestDuration(options.maxDurationMs),
|
|
959
|
+
bodyForwarding: require('../toolchain/body-forwarding.js').normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs),
|
|
960
|
+
...(source.logEndpoint === undefined ? {} : { logEndpoint: require('../toolchain/logging.js').normalizeFastlyLogEndpoint(source.logEndpoint) }),
|
|
802
961
|
configStore,
|
|
803
962
|
secretStore,
|
|
963
|
+
s3: require('../toolchain/s3.js').resolveFastlyS3(effects.map((effect) => ({ ...effect, payload: Object.fromEntries(['contentType', 'method', 'range', 'ifNoneMatch'].map(name => [name, literalObjectField(inputExpression(effect, 'payload'), name) ?? undefined])) })), source.s3),
|
|
804
964
|
kv: Object.freeze(kv),
|
|
805
965
|
fetch: Object.freeze(fetch),
|
|
806
966
|
backends: Object.freeze(backends),
|
|
@@ -834,7 +994,7 @@ function planRequiresFetchBodyRead(plan) {
|
|
|
834
994
|
if (Array.isArray(value)) return value.some(visit);
|
|
835
995
|
return Object.values(value).some(visit);
|
|
836
996
|
}
|
|
837
|
-
return visit(plan.entry);
|
|
997
|
+
return visit(plan.entry) || visit(plan.handlers) || visit(plan.stages) || visit(plan.helpers);
|
|
838
998
|
}
|
|
839
999
|
|
|
840
1000
|
function requiredImportsForPlan(plan, bindings) {
|
|
@@ -849,33 +1009,45 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
849
1009
|
'fastly_http_body:new',
|
|
850
1010
|
'fastly_http_body:write'
|
|
851
1011
|
]);
|
|
1012
|
+
if (bindings && bindings.maxDurationMs !== undefined) { keys.add('wasi_snapshot_preview1:clock_time_get'); keys.add('fastly_async_io:select'); }
|
|
852
1013
|
const kinds = new Set((plan.effects || []).map((effect) => effect.kind));
|
|
853
1014
|
if (kinds.has('fetch') || kinds.has('grip.publish') || kinds.has('grip.broadcast')) {
|
|
854
1015
|
for (const key of [
|
|
855
1016
|
'fastly_http_req:new', 'fastly_http_req:method_set', 'fastly_http_req:uri_set',
|
|
856
|
-
'fastly_http_req:
|
|
1017
|
+
'fastly_http_req:header_append', 'fastly_http_req:send_async', 'fastly_http_req:pending_req_wait_v2'
|
|
857
1018
|
]) keys.add(key);
|
|
858
1019
|
}
|
|
1020
|
+
if (bindings && dynamicBackends.active(bindings)) keys.add('fastly_http_req:register_dynamic_backend');
|
|
1021
|
+
if (plan.effects.some(effect=>effect.fetchTiming)) keys.add('fastly_async_io:is_ready');
|
|
1022
|
+
if (kinds.has('fetch')) for (const key of ['wasi_snapshot_preview1:clock_time_get', 'fastly_async_io:select', 'fastly_http_req:pending_req_poll_v2', 'fastly_http_resp:close', 'fastly_http_body:close']) keys.add(key);
|
|
859
1023
|
if (planRequiresFetchBodyRead(plan)) keys.add('fastly_http_body:read');
|
|
860
1024
|
if (kinds.has('config.get')) for (const key of ['fastly_config_store:open', 'fastly_config_store:get']) keys.add(key);
|
|
861
1025
|
const gripAuthenticationRequired = kinds.has('grip.broadcast')
|
|
862
1026
|
&& (!bindings || Boolean(bindings.grip && bindings.grip.authentication));
|
|
863
1027
|
const jwtSecretRequired = (plan.effects || []).some((effect) => (
|
|
864
|
-
|
|
1028
|
+
['jwt.verify', 'jwt.sign'].includes(effect.kind)
|
|
865
1029
|
&& effect.resource
|
|
866
1030
|
&& effect.resource.keyType === 'secret'
|
|
867
1031
|
));
|
|
868
1032
|
if (kinds.has('secret.get') || jwtSecretRequired || gripAuthenticationRequired) {
|
|
869
1033
|
for (const key of ['fastly_secret_store:open', 'fastly_secret_store:get', 'fastly_secret_store:plaintext']) keys.add(key);
|
|
870
1034
|
}
|
|
871
|
-
if (kinds.has(
|
|
1035
|
+
for (const kind of conditionalKv.KV_CONDITIONAL_KINDS) if (kinds.has(kind)) for (const key of conditionalKv.kvImports(kind)) keys.add(key);
|
|
1036
|
+
if (kinds.has('jwt.verify') || kinds.has('jwt.sign') || kinds.has('time.now')) keys.add('wasi_snapshot_preview1:clock_time_get');
|
|
872
1037
|
if (kinds.has('kv.get') || kinds.has('kv.put') || kinds.has('assets.lookup')) keys.add('fastly_kv_store:open');
|
|
873
1038
|
if (kinds.has('kv.get') || kinds.has('assets.lookup')) for (const key of ['fastly_kv_store:lookup', 'fastly_kv_store:lookup_wait_v2', 'fastly_http_body:read']) keys.add(key);
|
|
874
1039
|
if (kinds.has('kv.put')) for (const key of ['fastly_kv_store:insert', 'fastly_kv_store:insert_wait']) keys.add(key);
|
|
875
1040
|
if (kinds.has('grip.hold')) keys.add('fastly_http_resp:header_append');
|
|
876
|
-
if (plan.logging && plan.logging.enabledStatements > 0) {
|
|
877
|
-
keys.add('
|
|
878
|
-
keys.add('fastly_log:write');
|
|
1041
|
+
if ((plan.logging && plan.logging.enabledStatements > 0) || ['fetch', 'grip.publish', 'grip.broadcast', 's3.head', 's3.getText', 's3.putText', 's3.getBody'].some(kind => kinds.has(kind))) {
|
|
1042
|
+
if (bindings?.logEndpoint === undefined) keys.add('wasi_snapshot_preview1:fd_write');
|
|
1043
|
+
else { keys.add('fastly_log:endpoint_get'); keys.add('fastly_log:write'); }
|
|
1044
|
+
}
|
|
1045
|
+
if (kinds.has('s3.head') || kinds.has('s3.getText') || kinds.has('s3.putText') || kinds.has('s3.getBody')) for (const key of require('./s3-native.js').S3_IMPORTS) keys.add(key);
|
|
1046
|
+
if (require('./native-body-forwarding.js').enabled(plan)) {
|
|
1047
|
+
keys.delete('fastly_http_req:header_append'); keys.add('fastly_http_req:header_insert');
|
|
1048
|
+
keys.delete('fastly_http_req:send_async'); keys.delete('fastly_http_req:pending_req_wait_v2');
|
|
1049
|
+
for (const key of require('./native-body-forwarding.js').IMPORTS) keys.add(key);
|
|
1050
|
+
keys.add('fastly_http_body:read');
|
|
879
1051
|
}
|
|
880
1052
|
return Object.freeze([...keys].sort());
|
|
881
1053
|
}
|
|
@@ -887,15 +1059,40 @@ function stripPulseHostImports(source) {
|
|
|
887
1059
|
.join('\n');
|
|
888
1060
|
}
|
|
889
1061
|
|
|
1062
|
+
function schemaTextEncodeIds(plan) {
|
|
1063
|
+
// Keep the PS3 accounting/failure boundary and all unproved codec families.
|
|
1064
|
+
if (nativeValueBudget.hasBoundedReadLoop(plan)) return [];
|
|
1065
|
+
function closedText(node, depth = 0) {
|
|
1066
|
+
if (depth > 16) return false;
|
|
1067
|
+
if (node.kind === 'string' || node.kind === 'boolean') return true;
|
|
1068
|
+
if (node.kind === 'nullable') return closedText(node.value, depth);
|
|
1069
|
+
if (node.kind === 'array') return closedText(node.element, depth + 1);
|
|
1070
|
+
return node.kind === 'object' && !node.additionalProperties
|
|
1071
|
+
&& node.fields.every(field => closedText(field.value, depth + 1));
|
|
1072
|
+
}
|
|
1073
|
+
return ((plan.schemas && plan.schemas.registry && plan.schemas.registry.schemas) || [])
|
|
1074
|
+
.filter(schema => !schema.jsonLimits && schema.root.kind === 'object'
|
|
1075
|
+
&& schemaNeedsValueProjection(schema.root) && closedText(schema.root))
|
|
1076
|
+
.map(schema => schema.id);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
890
1079
|
function generateSchemaRuntime(plan) {
|
|
891
1080
|
const registry = plan.schemas && plan.schemas.registry ? plan.schemas.registry : { schemas: [], maxBytes: FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES, contentTypePolicy: 'accept-json-or-missing' };
|
|
892
1081
|
const schemas = registry.schemas || [];
|
|
1082
|
+
const textEncodeIds = new Set(schemaTextEncodeIds(plan));
|
|
893
1083
|
const nodeLines = [];
|
|
1084
|
+
const scalarProjectors = new Map();
|
|
1085
|
+
const flatObjectProjectors = new Map();
|
|
1086
|
+
const scalarKinds = ['string', 'boolean', 'i32', 'u32', 'f64', 'string-enum'];
|
|
894
1087
|
let nodeIndex = 0;
|
|
895
1088
|
|
|
896
1089
|
function emitNode(schemaIndex, node) {
|
|
897
1090
|
const currentIndex = nodeIndex++;
|
|
898
1091
|
const functionName = `__pulse_fastly_schema_${schemaIndex}_${currentIndex}`;
|
|
1092
|
+
const flatObject = node.kind === 'object' && !node.additionalProperties
|
|
1093
|
+
&& node.fields.every(field => scalarKinds.includes(field.value.kind));
|
|
1094
|
+
// Locals belong to this function, so equivalent flat bodies need no node ID.
|
|
1095
|
+
const localIndex = flatObject ? 'flat' : currentIndex;
|
|
899
1096
|
const child = node.kind === 'nullable'
|
|
900
1097
|
? emitNode(schemaIndex, node.value)
|
|
901
1098
|
: node.kind === 'array'
|
|
@@ -904,30 +1101,43 @@ function generateSchemaRuntime(plan) {
|
|
|
904
1101
|
const fields = node.kind === 'object'
|
|
905
1102
|
? node.fields.map((field) => Object.freeze({ field, apply: emitNode(schemaIndex, field.value) }))
|
|
906
1103
|
: [];
|
|
907
|
-
const body = [`function ${functionName}(valueHandle: i32): i32 {`, ' const input = __pulse_fastly_value(valueHandle)'];
|
|
1104
|
+
const body = [`function ${functionName}(valueHandle: i32, checkDuplicates: bool): i32 {`, ' const input = __pulse_fastly_value(valueHandle)'];
|
|
908
1105
|
if (node.kind === 'nullable') {
|
|
909
1106
|
body.push(' if (input.kind == PULSE_VALUE_NULL) return valueHandle');
|
|
910
|
-
body.push(` return ${child}(valueHandle)`);
|
|
1107
|
+
body.push(` return ${child}(valueHandle, checkDuplicates)`);
|
|
911
1108
|
} else if (node.kind === 'array') {
|
|
912
1109
|
body.push(' if (input.kind != PULSE_VALUE_ARRAY) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }');
|
|
913
1110
|
body.push(' const output = host_value_array()');
|
|
914
1111
|
body.push(' for (let index = 0; index < input.values.length; index += 1) {');
|
|
915
|
-
body.push(` const item = ${child}(unchecked(input.values[index]))`);
|
|
1112
|
+
body.push(` const item = ${child}(unchecked(input.values[index]), checkDuplicates)`);
|
|
916
1113
|
body.push(' if (item <= 0) return 0');
|
|
917
1114
|
body.push(' host_value_array_push(output, item)');
|
|
918
1115
|
body.push(' }');
|
|
919
1116
|
body.push(' return output');
|
|
1117
|
+
} else if (node.kind === 'json-value' || node.kind === 'json-object') {
|
|
1118
|
+
body.push(...nestedJsonProjectionLines(node));
|
|
1119
|
+
} else if (node.kind === 'scalar-record') {
|
|
1120
|
+
body.push(...scalarRecordProjectionLines(node));
|
|
920
1121
|
} else if (node.kind === 'object') {
|
|
921
1122
|
body.push(' if (input.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 50, -1); return 0 }');
|
|
1123
|
+
if (node.additionalProperties) body.push(' if (checkDuplicates && input.duplicateJsonKeys) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 57, -1); return 0 }');
|
|
922
1124
|
body.push(' const output = host_value_object()');
|
|
923
1125
|
fields.forEach(({ field, apply }, fieldIndex) => {
|
|
924
|
-
body.push(` const key_${
|
|
925
|
-
body.push(`
|
|
926
|
-
body.push(`
|
|
927
|
-
body.push(`
|
|
928
|
-
body.push(`
|
|
929
|
-
body.push(`
|
|
1126
|
+
body.push(` const key_${localIndex}_${fieldIndex} = __pulse_fastly_string_value(${quote(field.name)})`);
|
|
1127
|
+
if (!field.required) body.push(` if (__pulse_fastly_find(input, ${quote(field.name)}) >= 0) {`);
|
|
1128
|
+
body.push(` const field_${localIndex}_${fieldIndex} = host_value_property(valueHandle, key_${localIndex}_${fieldIndex})`);
|
|
1129
|
+
body.push(` if (__pulse_fastly_value(field_${localIndex}_${fieldIndex}).kind == PULSE_VALUE_UNDEFINED) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 51, -1); return 0 }`);
|
|
1130
|
+
body.push(` const projected_${localIndex}_${fieldIndex} = ${apply}(field_${localIndex}_${fieldIndex}, checkDuplicates)`);
|
|
1131
|
+
body.push(` if (projected_${localIndex}_${fieldIndex} <= 0) return 0`);
|
|
1132
|
+
body.push(` host_value_object_set(output, key_${localIndex}_${fieldIndex}, projected_${localIndex}_${fieldIndex})`);
|
|
1133
|
+
if (!field.required) body.push(' }');
|
|
930
1134
|
});
|
|
1135
|
+
if (node.additionalProperties) {
|
|
1136
|
+
body.push(' for (let index = 0; index < input.keys.length; index++) {', ' const key = unchecked(input.keys[index])');
|
|
1137
|
+
if (node.fields.length) body.push(` if (${node.fields.map(field => `key == ${quote(field.name)}`).join(' || ')}) continue`);
|
|
1138
|
+
body.push(' const item = __pulse_fastly_copy_json(unchecked(input.values[index]), checkDuplicates)',
|
|
1139
|
+
' if (item <= 0) return 0', ' host_value_object_set(output, __pulse_fastly_string_value(key), item)', ' }');
|
|
1140
|
+
}
|
|
931
1141
|
body.push(' return output');
|
|
932
1142
|
} else if (node.kind === 'string') {
|
|
933
1143
|
body.push(' if (input.kind != PULSE_VALUE_STRING) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }');
|
|
@@ -952,6 +1162,32 @@ function generateSchemaRuntime(plan) {
|
|
|
952
1162
|
throw new TypeError(`Unsupported Fastly native schema node ${String(node.kind)}.`);
|
|
953
1163
|
}
|
|
954
1164
|
body.push('}', '');
|
|
1165
|
+
// Only scalar validators preserve the original handle without allocating or
|
|
1166
|
+
// calling child projectors. Compare the exact emitted body, including enum
|
|
1167
|
+
// values/order and error stages.
|
|
1168
|
+
if (scalarKinds.includes(node.kind)) {
|
|
1169
|
+
const key = body.slice(1).join('\n');
|
|
1170
|
+
const existing = scalarProjectors.get(key);
|
|
1171
|
+
if (existing) return existing;
|
|
1172
|
+
const sharedName = `__pulse_fastly_schema_scalar_${scalarProjectors.size}`;
|
|
1173
|
+
scalarProjectors.set(key, sharedName);
|
|
1174
|
+
body[0] = `function ${sharedName}(valueHandle: i32, checkDuplicates: bool): i32 {`;
|
|
1175
|
+
nodeLines.push(...body);
|
|
1176
|
+
return sharedName;
|
|
1177
|
+
}
|
|
1178
|
+
// Share code only after child scalar identities are resolved. Exact bodies
|
|
1179
|
+
// retain field order, presence checks, validation and errors. Each call still
|
|
1180
|
+
// allocates its own output; open/nested/nullable/array bodies remain separate.
|
|
1181
|
+
if (flatObject) {
|
|
1182
|
+
const key = body.slice(1).join('\n');
|
|
1183
|
+
const existing = flatObjectProjectors.get(key);
|
|
1184
|
+
if (existing) return existing;
|
|
1185
|
+
const sharedName = `__pulse_fastly_schema_flat_object_${flatObjectProjectors.size}`;
|
|
1186
|
+
flatObjectProjectors.set(key, sharedName);
|
|
1187
|
+
body[0] = `function ${sharedName}(valueHandle: i32, checkDuplicates: bool): i32 {`;
|
|
1188
|
+
nodeLines.push(...body);
|
|
1189
|
+
return sharedName;
|
|
1190
|
+
}
|
|
955
1191
|
nodeLines.push(...body);
|
|
956
1192
|
return functionName;
|
|
957
1193
|
}
|
|
@@ -961,17 +1197,23 @@ function generateSchemaRuntime(plan) {
|
|
|
961
1197
|
`const __PULSE_SCHEMA_MAX_BYTES: i32 = ${Number(registry.maxBytes || FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES)}`,
|
|
962
1198
|
`const __PULSE_SCHEMA_REQUIRE_JSON: bool = ${registry.contentTypePolicy === 'require-json' ? 'true' : 'false'}`,
|
|
963
1199
|
'',
|
|
1200
|
+
schemaJsonAdmissionSource(plan),
|
|
1201
|
+
fetchHeadersSource(),
|
|
1202
|
+
schemaFetchJsonSource(),
|
|
964
1203
|
...nodeLines,
|
|
965
|
-
|
|
1204
|
+
`function __pulse_fastly_schema_apply(schemaId: string, valueHandle: i32, encode: bool${textEncodeIds.size ? ', textResult: bool = false' : ''}): i32 {`,
|
|
966
1205
|
' if (schemaId.length == 0) return valueHandle'
|
|
967
1206
|
];
|
|
968
1207
|
for (const [schemaIndex, schema] of schemas.entries()) {
|
|
969
1208
|
lines.push(`${schemaIndex === 0 ? ' if' : ' else if'} (schemaId == ${quote(schema.id)}) {`);
|
|
970
|
-
lines.push(`
|
|
1209
|
+
if (schema.jsonLimits) lines.push(` if (__pulse_fastly_admit_bounded_value(valueHandle, __pulse_schema_json_limits_${schemaIndex}()).failure != 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 57, -1); return 0 }`);
|
|
1210
|
+
lines.push(` const projected = ${roots[schemaIndex]}(valueHandle, !encode)`);
|
|
971
1211
|
lines.push(' if (projected <= 0) return 0');
|
|
972
1212
|
lines.push(' const input = __pulse_fastly_json(projected, 0)');
|
|
973
1213
|
lines.push(` const normalized = encode ? __pulse_schema_encode_${schemaIndex}(input) : __pulse_schema_decode_${schemaIndex}(input)`);
|
|
974
|
-
lines.push(' return
|
|
1214
|
+
if (textEncodeIds.has(schema.id)) lines.push(' if (textResult && encode) return __pulse_fastly_string_value(normalized)');
|
|
1215
|
+
if (schema.jsonLimits) lines.push(' const output = __pulse_fastly_parse_json(normalized)', ' if (output > 0) __pulse_fastly_deep_freeze(output)', ' return output');
|
|
1216
|
+
else lines.push(' return __pulse_fastly_parse_json(normalized)');
|
|
975
1217
|
lines.push(' }');
|
|
976
1218
|
}
|
|
977
1219
|
lines.push(' __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1)');
|
|
@@ -994,7 +1236,7 @@ function effectResultSource(plan, bindings) {
|
|
|
994
1236
|
lines.push(`function __pulse_fastly_secret_store(effectIndex: i32): string { return ${quote(bindings.secretStore)} }`, '');
|
|
995
1237
|
lines.push('function __pulse_fastly_jwt_secret_binding(effectIndex: i32): string {', ' switch (effectIndex) {');
|
|
996
1238
|
for (const [index, effect] of (plan.effects || []).entries()) {
|
|
997
|
-
if (
|
|
1239
|
+
if (['jwt.verify', 'jwt.sign'].includes(effect.kind) && effect.resource.keyType === 'secret') {
|
|
998
1240
|
lines.push(` case ${index}: return ${quote(effect.resource.secretBinding)}`);
|
|
999
1241
|
}
|
|
1000
1242
|
}
|
|
@@ -1009,12 +1251,31 @@ function effectResultSource(plan, bindings) {
|
|
|
1009
1251
|
for (const binding of bindings.grip.publish) lines.push(` case ${binding.index}: return ${quote(binding.authSecretRef || '')}`);
|
|
1010
1252
|
lines.push(' default: return ""', ' }', '}', '');
|
|
1011
1253
|
|
|
1254
|
+
// A config lookup has the same ready-result handoff at every static site.
|
|
1255
|
+
// Keep the site indices in the switch; pass the active index through so the
|
|
1256
|
+
// pending slot and any state error still belong to the original invocation.
|
|
1257
|
+
const configSites = (plan.effects || []).flatMap((effect, index) => effect.kind === 'config.get' ? [index] : []);
|
|
1258
|
+
if (configSites.length > 1) {
|
|
1259
|
+
lines.push('function __pulse_fastly_resolve_config_get(effectIndex: i32): i32 {');
|
|
1260
|
+
lines.push(' const response = __pulse_fastly_wait_ready(effectIndex)');
|
|
1261
|
+
lines.push(' if (response <= 0) return 0');
|
|
1262
|
+
lines.push(' return response');
|
|
1263
|
+
lines.push('}', '');
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1012
1266
|
lines.push('function __pulse_fastly_resolve_effect(effectIndex: i32): i32 {');
|
|
1013
1267
|
lines.push(' switch (effectIndex) {');
|
|
1014
1268
|
for (const [index, effect] of (plan.effects || []).entries()) {
|
|
1269
|
+
if (effect.kind === 'config.get' && configSites.length > 1) {
|
|
1270
|
+
if (index === configSites[0]) {
|
|
1271
|
+
for (const site of configSites) lines.push(` case ${site}:`);
|
|
1272
|
+
lines.push(' return __pulse_fastly_resolve_config_get(effectIndex)');
|
|
1273
|
+
}
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1015
1276
|
const result = effect.result || {};
|
|
1016
1277
|
const decoder = result.decoder;
|
|
1017
|
-
const waiter = effect.kind === 'fetch'
|
|
1278
|
+
const waiter = conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind) ? '__pulse_fastly_kv_conditional_wait' : ['s3.head', 's3.getText', 's3.putText', 's3.getBody'].includes(effect.kind) ? '__pulse_fastly_s3_wait' : effect.kind === 'fetch'
|
|
1018
1279
|
? '__pulse_fastly_wait_fetch'
|
|
1019
1280
|
: effect.kind === 'grip.publish'
|
|
1020
1281
|
? '__pulse_fastly_wait_grip_publish'
|
|
@@ -1050,21 +1311,31 @@ function effectResultSource(plan, bindings) {
|
|
|
1050
1311
|
|
|
1051
1312
|
function effectDispatchSource(plan) {
|
|
1052
1313
|
const handlerByKind = Object.freeze({
|
|
1314
|
+
'time.now': '__pulse_fastly_time_begin',
|
|
1315
|
+
'crypto.digestText': '__pulse_fastly_digest_begin',
|
|
1053
1316
|
fetch: '__pulse_fastly_fetch_begin',
|
|
1054
1317
|
'config.get': '__pulse_fastly_config_begin',
|
|
1055
1318
|
'secret.get': '__pulse_fastly_secret_begin',
|
|
1056
1319
|
'jwt.verify': '__pulse_fastly_jwt_begin',
|
|
1320
|
+
'jwt.sign': '__pulse_fastly_jwt_sign_begin',
|
|
1321
|
+
's3.head': '__pulse_fastly_s3_begin',
|
|
1322
|
+
's3.getText': '__pulse_fastly_s3_begin',
|
|
1323
|
+
's3.putText': '__pulse_fastly_s3_begin',
|
|
1324
|
+
's3.getBody': '__pulse_fastly_s3_begin',
|
|
1057
1325
|
'kv.get': '__pulse_fastly_kv_get_begin',
|
|
1058
1326
|
'kv.put': '__pulse_fastly_kv_put_begin',
|
|
1327
|
+
...Object.fromEntries(conditionalKv.KV_CONDITIONAL_KINDS.map(kind => [kind, '__pulse_fastly_kv_conditional_begin'])),
|
|
1059
1328
|
'assets.lookup': '__pulse_fastly_assets_begin',
|
|
1060
1329
|
'grip.channel': '__pulse_fastly_grip_channel_begin',
|
|
1061
1330
|
'grip.hold': '__pulse_fastly_grip_hold_begin',
|
|
1062
1331
|
'grip.publish': '__pulse_fastly_grip_publish_begin',
|
|
1063
|
-
'grip.broadcast': '__pulse_fastly_grip_publish_begin'
|
|
1332
|
+
'grip.broadcast': '__pulse_fastly_grip_publish_begin',
|
|
1333
|
+
'host.extension': '__pulse_extension_begin'
|
|
1064
1334
|
});
|
|
1065
1335
|
const kinds = [...new Set((plan.effects || []).map((effect) => effect.kind))];
|
|
1066
1336
|
const lines = [
|
|
1067
1337
|
'function host_effect_begin(effectIndex: i32, payload: i32): void {',
|
|
1338
|
+
...(needsNativeValueFailureGuard(plan) ? [' if (__pulse_fastly_last_error != PULSE_ERROR_NONE) return'] : []),
|
|
1068
1339
|
' if (effectIndex < 0 || effectIndex >= PULSE_FASTLY_EFFECT_COUNT || unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_NONE) { __pulse_fastly_fail(PULSE_ERROR_STATE, 46, effectIndex); return }',
|
|
1069
1340
|
' const payloadValue = __pulse_fastly_value(payload)',
|
|
1070
1341
|
' if (payloadValue.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 47, effectIndex); return }',
|
|
@@ -1085,7 +1356,15 @@ function effectDispatchSource(plan) {
|
|
|
1085
1356
|
}
|
|
1086
1357
|
|
|
1087
1358
|
function fastlyRuntimeSource(plan, bindings, options = {}) {
|
|
1359
|
+
const lifecycle = options.lifecycle;
|
|
1360
|
+
const textEncodeIds = schemaTextEncodeIds(plan);
|
|
1088
1361
|
const effectCount = (plan.effects || []).length;
|
|
1362
|
+
const hasAssets = (plan.effects || []).some(effect => effect.kind === 'assets.lookup');
|
|
1363
|
+
const assetBeginSource = hasAssets ? String.raw`function __pulse_fastly_assets_payload(payload: __PulseFastlyValue): __PulseFastlyValue { const value = __pulse_fastly_value(__pulse_fastly_payload_field(payload, "payload")); if (value.kind != PULSE_VALUE_OBJECT) __pulse_fastly_fail(PULSE_ERROR_VALUE, 146, -1); return value }
|
|
1364
|
+
function __pulse_fastly_assets_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const value = __pulse_fastly_assets_payload(payload); if (__pulse_fastly_last_error != 0) return; if (__pulse_fastly_value(__pulse_fastly_payload_field(value, 'embeddedId')).kind != PULSE_VALUE_UNDEFINED) { __pulse_fastly_ready_effect(effectIndex, __pulse_fastly_embedded_asset(value)); return; } const store = __pulse_fastly_open_kv_store(effectIndex); if (__pulse_fastly_last_error != 0) return; const key = __pulse_fastly_string(__pulse_fastly_payload_field(value, "key")); const keyBytes = __pulse_fastly_utf8(key); const lookupOut = __pulse_fastly_out_i32(); const config = new StaticArray<i32>(1); const status = fastly_kv_store_lookup(store, changetype<usize>(keyBytes), keyBytes.byteLength, 0, changetype<usize>(config), changetype<usize>(lookupOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 147, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(lookupOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }` : '';
|
|
1365
|
+
const assetResultSource = hasAssets ? String.raw`function __pulse_fastly_asset_content_type(key: string): string { const dot = key.lastIndexOf("."); const extension = dot < 0 ? "" : key.substring(dot + 1).toLowerCase(); if (extension == "css") return "text/css; charset=utf-8"; if (extension == "html") return "text/html; charset=utf-8"; if (extension == "js" || extension == "mjs") return "text/javascript; charset=utf-8"; if (extension == "json") return "application/json; charset=utf-8"; if (extension == "svg") return "image/svg+xml"; if (extension == "txt") return "text/plain; charset=utf-8"; if (extension == "webp") return "image/webp"; if (extension == "png") return "image/png"; if (extension == "jpg" || extension == "jpeg") return "image/jpeg"; if (extension == "gif") return "image/gif"; if (extension == "ico") return "image/x-icon"; return "application/octet-stream" }
|
|
1366
|
+
function __pulse_fastly_asset_text(text: string): string { const parser = new __PulseJsonParser(text); const parsed = parser.parse(); if (parser.failed || parsed <= 0) return text; const object = __pulse_fastly_value(parsed); if (object.kind != PULSE_VALUE_OBJECT) return text; const marker = __pulse_fastly_find(object, "__pulseKv"); const value = __pulse_fastly_find(object, "value"); if (marker < 0 || value < 0 || __pulse_fastly_number(unchecked(object.values[marker])) != 1.0) return text; return __pulse_fastly_string(unchecked(object.values[value])) }
|
|
1367
|
+
function __pulse_fastly_wait_assets(effectIndex: i32): i32 { if (__pulse_fastly_pending_mode[effectIndex] == PULSE_FASTLY_PENDING_READY) return __pulse_fastly_wait_ready(effectIndex); if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 148, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const bodyOut = __pulse_fastly_out_i32(); const metadata = new Uint8Array(1); const written = __pulse_fastly_out_i32(); const generation = new StaticArray<i64>(1); const kvError = __pulse_fastly_out_i32(); const status = fastly_kv_store_lookup_wait_v2(pending, changetype<usize>(bodyOut), metadata.dataStart, 0, changetype<usize>(written), changetype<usize>(generation), changetype<usize>(kvError)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 149, effectIndex); return 0 } const outer = __pulse_fastly_value(unchecked(__pulse_fastly_payloads[effectIndex])); const payload = __pulse_fastly_assets_payload(outer); if (__pulse_fastly_last_error != 0) return 0; const key = __pulse_fastly_string(__pulse_fastly_payload_field(payload, "key")); const output = new __PulseFastlyValue(); output.kind = PULSE_VALUE_RESPONSE; const errorCode = __pulse_fastly_out_value(kvError); output.status = errorCode == 3 ? 404 : 200; if (errorCode != 1 && errorCode != 3) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 150, effectIndex); return 0 } const headersHandle = __pulse_fastly_payload_field(payload, "headers"); const headers = __pulse_fastly_value(headersHandle); let hasContentType = false; if (headers.kind == PULSE_VALUE_OBJECT) { for (let i = 0; i < headers.keys.length; i += 1) { const name = unchecked(headers.keys[i]); if (name.toLowerCase() == "content-type") hasContentType = true; output.headers.push(new __PulseFastlyHeader(name, __pulse_fastly_string(unchecked(headers.values[i])))) } } if (!hasContentType) output.headers.push(new __PulseFastlyHeader("content-type", output.status == 200 ? __pulse_fastly_asset_content_type(key) : "text/plain; charset=utf-8")); const cacheHandle = __pulse_fastly_payload_field(payload, "cacheControl"); if (__pulse_fastly_value(cacheHandle).kind != PULSE_VALUE_UNDEFINED) output.headers.push(new __PulseFastlyHeader("cache-control", __pulse_fastly_string(cacheHandle))); const methodHandle = __pulse_fastly_payload_field(payload, "method"); const method = __pulse_fastly_value(methodHandle).kind == PULSE_VALUE_UNDEFINED ? "GET" : __pulse_fastly_string(methodHandle).toUpperCase(); if (output.status == 200 && method != "HEAD") { const text = __pulse_fastly_read_body(__pulse_fastly_out_value(bodyOut), effectIndex); if (__pulse_fastly_last_error != 0) return 0; output.text = __pulse_fastly_asset_text(text) } return __pulse_fastly_put(output) }` : '';
|
|
1089
1368
|
const jwtCrypto = options.jwtCrypto || selectedJwtCrypto(plan);
|
|
1090
1369
|
const es256KeyRecords = options.es256KeyRecords || Object.freeze([]);
|
|
1091
1370
|
const jwtReality = options.jwtReality && options.jwtReality.enabled === true
|
|
@@ -1143,19 +1422,24 @@ function __pulse_fastly_jwt_attach_evidence(handle: i32): void {
|
|
|
1143
1422
|
const value = __pulse_fastly_value(handle)
|
|
1144
1423
|
if (value.kind == PULSE_VALUE_RESPONSE) __pulse_fastly_jwt_evidence_headers(value)
|
|
1145
1424
|
}
|
|
1146
|
-
|
|
1425
|
+
`
|
|
1426
|
+
: String.raw`
|
|
1427
|
+
function __pulse_fastly_jwt_attach_evidence(handle: i32): void { void handle }
|
|
1428
|
+
`;
|
|
1429
|
+
// Keep the legacy hook name: body, fetch, header and deadline owners prepend
|
|
1430
|
+
// their specific responses here. Every remaining fatal path needs a response,
|
|
1431
|
+
// independently of whether JWT evidence was requested for this build.
|
|
1432
|
+
const terminalErrorSource = `
|
|
1433
|
+
${lifecycle?.active ? `function __pulse_fastly_jwt_send_error(): void {\n${lifecycle.site('response-error')}\n __pulse_fastly_jwt_send_error_impl()\n}\n` : ''}function ${lifecycle?.legacyName('__pulse_fastly_jwt_send_error') || '__pulse_fastly_jwt_send_error'}(): void {
|
|
1147
1434
|
const output = new __PulseFastlyValue()
|
|
1148
1435
|
output.kind = PULSE_VALUE_RESPONSE
|
|
1149
|
-
output.status = 400
|
|
1150
|
-
output.text = ""
|
|
1436
|
+
output.status = ${jwtReality ? '__pulse_fastly_jwt_error != 0 ? 400 : 500' : '500'}
|
|
1437
|
+
output.text = output.status == 400 ? "" : "Internal Server Error"
|
|
1151
1438
|
output.headers.push(new __PulseFastlyHeader("content-type", "text/plain; charset=utf-8"))
|
|
1152
|
-
__pulse_fastly_jwt_evidence_headers(output)
|
|
1439
|
+
${jwtReality ? 'if (__pulse_fastly_jwt_error != 0) __pulse_fastly_jwt_evidence_headers(output)' : ''}
|
|
1440
|
+
// Do not clear the original failure or expose its detail in the response.
|
|
1153
1441
|
__pulse_fastly_send_result(__pulse_fastly_put(output))
|
|
1154
1442
|
}
|
|
1155
|
-
`
|
|
1156
|
-
: String.raw`
|
|
1157
|
-
function __pulse_fastly_jwt_attach_evidence(handle: i32): void { void handle }
|
|
1158
|
-
function __pulse_fastly_jwt_send_error(): void {}
|
|
1159
1443
|
`;
|
|
1160
1444
|
const gripAuthentication = bindings.grip && bindings.grip.authentication;
|
|
1161
1445
|
const gripSecretSource = gripAuthentication
|
|
@@ -1164,7 +1448,7 @@ function __pulse_fastly_jwt_send_error(): void {}
|
|
|
1164
1448
|
const gripAuthorizationSource = gripAuthentication
|
|
1165
1449
|
? `const secret = __pulse_fastly_grip_secret(effectIndex, ${quote(gripAuthentication.secretRef)}); if (__pulse_fastly_last_error != 0) return; headers.keys.push("authorization"); headers.values.push(__pulse_fastly_string_value("Bearer " + secret));`
|
|
1166
1450
|
: '';
|
|
1167
|
-
|
|
1451
|
+
const source = String.raw`
|
|
1168
1452
|
@external("fastly_abi", "init") declare function fastly_abi_init(version: i64): i32
|
|
1169
1453
|
@external("fastly_http_req", "body_downstream_get") declare function fastly_http_req_body_downstream_get(requestOut: usize, bodyOut: usize): i32
|
|
1170
1454
|
@external("fastly_http_req", "method_get") declare function fastly_http_req_method_get(handle: i32, buffer: usize, bufferLength: i32, writtenOut: usize): i32
|
|
@@ -1174,14 +1458,15 @@ function __pulse_fastly_jwt_send_error(): void {}
|
|
|
1174
1458
|
@external("fastly_http_req", "method_set") declare function fastly_http_req_method_set(handle: i32, method: usize, methodLength: i32): i32
|
|
1175
1459
|
@external("fastly_http_req", "uri_set") declare function fastly_http_req_uri_set(handle: i32, uri: usize, uriLength: i32): i32
|
|
1176
1460
|
@external("fastly_http_req", "header_insert") declare function fastly_http_req_header_insert(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32): i32
|
|
1461
|
+
@external("fastly_http_req", "header_append") declare function fastly_http_req_header_append(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32): i32
|
|
1177
1462
|
@external("fastly_http_req", "send_async") declare function fastly_http_req_send_async(handle: i32, body: i32, backend: usize, backendLength: i32, pendingOut: usize): i32
|
|
1178
|
-
@external("fastly_http_req", "
|
|
1463
|
+
@external("fastly_http_req", "pending_req_wait_v2") declare function fastly_http_req_pending_req_wait_v2(handle: i32, detail: usize, responseOut: usize, bodyOut: usize): i32
|
|
1179
1464
|
@external("fastly_http_resp", "new") declare function fastly_http_resp_new(handleOut: usize): i32
|
|
1180
1465
|
@external("fastly_http_resp", "header_append") declare function fastly_http_resp_header_append(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32): i32
|
|
1181
1466
|
@external("fastly_http_resp", "header_value_get") declare function fastly_http_resp_header_value_get(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32, writtenOut: usize): i32
|
|
1182
1467
|
@external("fastly_http_resp", "status_get") declare function fastly_http_resp_status_get(handle: i32, statusOut: usize): i32
|
|
1183
1468
|
@external("fastly_http_resp", "status_set") declare function fastly_http_resp_status_set(handle: i32, status: i32): i32
|
|
1184
|
-
@external("fastly_http_resp", "send_downstream") declare function fastly_http_resp_send_downstream(handle: i32, body: i32, streaming: i32): i32
|
|
1469
|
+
@external("fastly_http_resp", "send_downstream") declare function ${lifecycle?.authority ? '__pulse_response_send_raw' : 'fastly_http_resp_send_downstream'}(handle: i32, body: i32, streaming: i32): i32
|
|
1185
1470
|
@external("fastly_http_body", "new") declare function fastly_http_body_new(handleOut: usize): i32
|
|
1186
1471
|
@external("fastly_http_body", "read") declare function fastly_http_body_read(handle: i32, buffer: usize, bufferLength: i32, readOut: usize): i32
|
|
1187
1472
|
@external("fastly_http_body", "write") declare function fastly_http_body_write(handle: i32, buffer: usize, bufferLength: i32, end: i32, writtenOut: usize): i32
|
|
@@ -1195,8 +1480,11 @@ function __pulse_fastly_jwt_send_error(): void {}
|
|
|
1195
1480
|
@external("fastly_kv_store", "lookup_wait_v2") declare function fastly_kv_store_lookup_wait_v2(handle: i32, bodyOut: usize, metadata: usize, metadataLength: i32, writtenOut: usize, generationOut: usize, kvErrorOut: usize): i32
|
|
1196
1481
|
@external("fastly_kv_store", "insert") declare function fastly_kv_store_insert(store: i32, key: usize, keyLength: i32, body: i32, configMask: i32, config: usize, insertOut: usize): i32
|
|
1197
1482
|
@external("fastly_kv_store", "insert_wait") declare function fastly_kv_store_insert_wait(handle: i32, kvErrorOut: usize): i32
|
|
1198
|
-
@external("
|
|
1199
|
-
@external("
|
|
1483
|
+
@external("fastly_kv_store", "delete") declare function fastly_kv_store_delete(store: i32, key: usize, keyLength: i32, configMask: i32, config: usize, pendingOut: usize): i32
|
|
1484
|
+
@external("fastly_kv_store", "delete_wait") declare function fastly_kv_store_delete_wait(handle: i32, kvErrorOut: usize): i32
|
|
1485
|
+
@external("fastly_kv_store", "list") declare function fastly_kv_store_list(store: i32, configMask: i32, config: usize, pendingOut: usize): i32
|
|
1486
|
+
@external("fastly_kv_store", "list_wait") declare function fastly_kv_store_list_wait(handle: i32, bodyOut: usize, kvErrorOut: usize): i32
|
|
1487
|
+
${nativeLoggingSource(bindings.logEndpoint)}
|
|
1200
1488
|
@external("wasi_snapshot_preview1", "clock_time_get") declare function wasi_snapshot_preview1_clock_time_get(clockId: i32, precision: i64, timeOut: usize): i32
|
|
1201
1489
|
|
|
1202
1490
|
const PULSE_FASTLY_EFFECTS_ABI_VERSION: i64 = ${FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION}
|
|
@@ -1233,20 +1521,43 @@ const PULSE_ERROR_SCHEMA: i32 = 1005
|
|
|
1233
1521
|
const PULSE_ERROR_TRANSPORT: i32 = 1006
|
|
1234
1522
|
const PULSE_ERROR_STATE: i32 = 1007
|
|
1235
1523
|
const PULSE_ERROR_JWT: i32 = 1008
|
|
1524
|
+
const PULSE_ERROR_REQUEST_BODY: i32 = 1009
|
|
1236
1525
|
|
|
1237
1526
|
class __PulseFastlyValue {
|
|
1527
|
+
${tracksJsonDuplicates(plan) ? 'duplicateJsonKeys: bool = false' : ''}
|
|
1238
1528
|
kind: i32 = PULSE_VALUE_UNDEFINED
|
|
1239
1529
|
boolean: i32 = 0
|
|
1240
1530
|
number: f64 = 0.0
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1531
|
+
${nativeStringFields}
|
|
1532
|
+
// Scalar handles do not need collection backing stores.
|
|
1533
|
+
private keysStorage: Array<string> | null = null
|
|
1534
|
+
get keys(): Array<string> {
|
|
1535
|
+
if (this.keysStorage === null) this.keysStorage = new Array<string>()
|
|
1536
|
+
return this.keysStorage!
|
|
1537
|
+
}
|
|
1538
|
+
// Scalar handles do not need collection backing stores.
|
|
1539
|
+
private valuesStorage: Array<i32> | null = null
|
|
1540
|
+
get values(): Array<i32> {
|
|
1541
|
+
if (this.valuesStorage === null) this.valuesStorage = new Array<i32>()
|
|
1542
|
+
return this.valuesStorage!
|
|
1543
|
+
}
|
|
1244
1544
|
responseHandle: i32 = 0
|
|
1245
1545
|
bodyHandle: i32 = 0
|
|
1546
|
+
${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? ' fetchHeaders: Array<__FCA_Header> | null = null;\n fetchJsonType: i32 = 0;' : ''}
|
|
1246
1547
|
bodyLoaded: i32 = 0
|
|
1548
|
+
s3Body: bool = false
|
|
1549
|
+
s3Deadline: i64 = 0
|
|
1550
|
+
s3Length: i32 = 0
|
|
1247
1551
|
status: i32 = 200
|
|
1248
|
-
|
|
1552
|
+
// Scalar handles do not need collection backing stores.
|
|
1553
|
+
private headersStorage: Array<__PulseFastlyHeader> | null = null
|
|
1554
|
+
get headers(): Array<__PulseFastlyHeader> {
|
|
1555
|
+
if (this.headersStorage === null) this.headersStorage = new Array<__PulseFastlyHeader>()
|
|
1556
|
+
return this.headersStorage!
|
|
1557
|
+
}
|
|
1558
|
+
binaryBody: Uint8Array | null = null
|
|
1249
1559
|
immutable: bool = false
|
|
1560
|
+
${nativeHandleOwnership.fields(options.handleTracing)}
|
|
1250
1561
|
}
|
|
1251
1562
|
class __PulseFastlyHeader {
|
|
1252
1563
|
constructor(public name: string, public value: string) {}
|
|
@@ -1255,11 +1566,14 @@ class __PulseJsonParser {
|
|
|
1255
1566
|
source: string
|
|
1256
1567
|
index: i32 = 0
|
|
1257
1568
|
failed: bool = false
|
|
1258
|
-
|
|
1569
|
+
tooLarge: bool = false
|
|
1570
|
+
entries: i32 = 0
|
|
1571
|
+
constructor(source: string, public conditionalKv: bool = false) { this.source = source }
|
|
1259
1572
|
skip(): void { while (this.index < this.source.length) { const c = this.source.charCodeAt(this.index); if (c != 32 && c != 9 && c != 10 && c != 13) return; this.index += 1 } }
|
|
1260
1573
|
parse(): i32 { this.skip(); const value = this.value(0); this.skip(); if (this.index != this.source.length) this.failed = true; return this.failed ? 0 : value }
|
|
1261
1574
|
value(depth: i32): i32 {
|
|
1262
|
-
this.skip(); if (this.
|
|
1575
|
+
this.skip(); if (this.conditionalKv && (depth > ${conditionalKv.KV_CONDITIONAL_LIMITS.depth + 1} || ++this.entries > ${conditionalKv.KV_CONDITIONAL_LIMITS.entries + 2})) { this.failed = true; this.tooLarge = true; return 0 }
|
|
1576
|
+
if (this.index >= this.source.length || (!this.conditionalKv && depth > ${schemaParserDepth(plan, plan.effects.some(effect => conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) ? 128 : 64)})) { this.failed = true; return 0 }
|
|
1263
1577
|
const c = this.source.charCodeAt(this.index)
|
|
1264
1578
|
if (c == 34) return __pulse_fastly_string_value(this.string())
|
|
1265
1579
|
if (c == 123) return this.object(depth + 1)
|
|
@@ -1271,37 +1585,47 @@ class __PulseJsonParser {
|
|
|
1271
1585
|
this.failed = true; return 0
|
|
1272
1586
|
}
|
|
1273
1587
|
string(): string {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1588
|
+
// Find a bounded span, then decode each UTF-16 unit once. Concatenating one
|
|
1589
|
+
// character at a time exhausts stub/fixed memory on a valid maximum KV value.
|
|
1590
|
+
const start = this.index + 1; let end = start; let plain = true;
|
|
1591
|
+
while (end < this.source.length) {
|
|
1592
|
+
const c = this.source.charCodeAt(end);
|
|
1593
|
+
if (c == 34) break;
|
|
1594
|
+
if (c == 92) { plain = false; end += 1; }
|
|
1595
|
+
else if (c < 32) plain = false;
|
|
1596
|
+
end += 1;
|
|
1597
|
+
}
|
|
1598
|
+
if (end >= this.source.length) { this.failed = true; return "" }
|
|
1599
|
+
// A validated span needs one owned string, without a temporary UTF-16 array.
|
|
1600
|
+
// Keep escaped and malformed spans on the existing decoder path.
|
|
1601
|
+
if (plain) { this.index = end + 1; return this.source.substring(start, end); }
|
|
1602
|
+
const output = new Uint16Array(end - start); let count = 0; this.index = start;
|
|
1603
|
+
while (this.index < end) {
|
|
1604
|
+
let c = this.source.charCodeAt(this.index++);
|
|
1278
1605
|
if (c == 92) {
|
|
1279
|
-
if (this.index >=
|
|
1280
|
-
c = this.source.charCodeAt(this.index);
|
|
1281
|
-
if (c == 34 || c == 92 || c == 47)
|
|
1282
|
-
else if (c == 98)
|
|
1283
|
-
else if (c == 102)
|
|
1284
|
-
else if (c == 110)
|
|
1285
|
-
else if (c == 114)
|
|
1286
|
-
else if (c == 116)
|
|
1606
|
+
if (this.index >= end) { this.failed = true; return "" }
|
|
1607
|
+
c = this.source.charCodeAt(this.index++);
|
|
1608
|
+
if (c == 34 || c == 92 || c == 47) {}
|
|
1609
|
+
else if (c == 98) c = 8;
|
|
1610
|
+
else if (c == 102) c = 12;
|
|
1611
|
+
else if (c == 110) c = 10;
|
|
1612
|
+
else if (c == 114) c = 13;
|
|
1613
|
+
else if (c == 116) c = 9;
|
|
1287
1614
|
else if (c == 117) {
|
|
1288
|
-
if (this.index + 4 >
|
|
1289
|
-
|
|
1290
|
-
for (let
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
if (digitCode >= 48 && digitCode <= 57) digit = digitCode - 48
|
|
1294
|
-
else if (digitCode >= 65 && digitCode <= 70) digit = digitCode - 55
|
|
1295
|
-
else if (digitCode >= 97 && digitCode <= 102) digit = digitCode - 87
|
|
1615
|
+
if (this.index + 4 > end) { this.failed = true; return "" }
|
|
1616
|
+
c = 0;
|
|
1617
|
+
for (let i = 0; i < 4; i++) {
|
|
1618
|
+
const d = this.source.charCodeAt(this.index++);
|
|
1619
|
+
const digit = d >= 48 && d <= 57 ? d - 48 : d >= 65 && d <= 70 ? d - 55 : d >= 97 && d <= 102 ? d - 87 : -1;
|
|
1296
1620
|
if (digit < 0) { this.failed = true; return "" }
|
|
1297
|
-
|
|
1621
|
+
c = (c << 4) | digit;
|
|
1298
1622
|
}
|
|
1299
|
-
this.index += 4; output += String.fromCharCode(code)
|
|
1300
1623
|
} else { this.failed = true; return "" }
|
|
1301
1624
|
} else if (c < 32) { this.failed = true; return "" }
|
|
1302
|
-
|
|
1625
|
+
output[count++] = u16(c);
|
|
1303
1626
|
}
|
|
1304
|
-
this.
|
|
1627
|
+
this.index = end + 1;
|
|
1628
|
+
return String.UTF16.decodeUnsafe(output.dataStart, count * 2);
|
|
1305
1629
|
}
|
|
1306
1630
|
numberValue(): i32 {
|
|
1307
1631
|
const start = this.index
|
|
@@ -1350,7 +1674,9 @@ class __PulseJsonParser {
|
|
|
1350
1674
|
while (!this.failed) {
|
|
1351
1675
|
this.skip(); if (this.index >= this.source.length || this.source.charCodeAt(this.index) != 34) { this.failed = true; break }
|
|
1352
1676
|
const key = this.string(); this.skip()
|
|
1677
|
+
if (this.conditionalKv && __pulse_fastly_find(__pulse_fastly_value(output), key) >= 0) { this.failed = true; break }
|
|
1353
1678
|
if (this.index >= this.source.length || this.source.charCodeAt(this.index) != 58) { this.failed = true; break }
|
|
1679
|
+
${tracksJsonDuplicates(plan) ? 'if (__pulse_fastly_find(__pulse_fastly_value(output), key) >= 0) __pulse_fastly_value(output).duplicateJsonKeys = true' : ''}
|
|
1354
1680
|
this.index += 1; host_value_object_set(output, __pulse_fastly_string_value(key), this.value(depth)); this.skip()
|
|
1355
1681
|
if (this.index >= this.source.length) { this.failed = true; break }
|
|
1356
1682
|
const c = this.source.charCodeAt(this.index); this.index += 1
|
|
@@ -1364,13 +1690,20 @@ class __PulseJsonParser {
|
|
|
1364
1690
|
const PULSE_FASTLY_PENDING_NONE: i32 = 0
|
|
1365
1691
|
const PULSE_FASTLY_PENDING_ASYNC: i32 = 1
|
|
1366
1692
|
const PULSE_FASTLY_PENDING_READY: i32 = 2
|
|
1367
|
-
|
|
1693
|
+
${nativeHandleOwnership.tableSource(options.handleTracing)}
|
|
1694
|
+
// Primitive handles have value semantics. Bounded caches keep admitted scalar
|
|
1695
|
+
// validation loops from retaining a fresh boxed value for every iteration.
|
|
1696
|
+
@lazy const __pulse_fastly_scalar_handles = new StaticArray<i32>(4)
|
|
1697
|
+
@lazy const __pulse_fastly_integer_handles = new StaticArray<i32>(65536)
|
|
1698
|
+
@lazy const __pulse_fastly_character_handles = new StaticArray<i32>(65537)
|
|
1699
|
+
@lazy const __pulse_fastly_literal_handles = new Map<string, i32>()
|
|
1368
1700
|
@lazy const __pulse_fastly_pending = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1369
1701
|
@lazy const __pulse_fastly_pending_mode = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1370
1702
|
@lazy const __pulse_fastly_payloads = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1371
1703
|
@lazy const __pulse_fastly_ready = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1372
1704
|
@lazy const __pulse_fastly_grip_channels = new Array<string>()
|
|
1373
1705
|
@lazy const __pulse_fastly_redactions = new Array<string>()
|
|
1706
|
+
@lazy const __pulse_fastly_field_redactions = new Array<string>()
|
|
1374
1707
|
let __pulse_fastly_request_handle: i32 = 0
|
|
1375
1708
|
let __pulse_fastly_request_body_handle: i32 = 0
|
|
1376
1709
|
let __pulse_fastly_request_method: string = ""
|
|
@@ -1381,7 +1714,6 @@ let __pulse_fastly_request_body_loaded: i32 = 0
|
|
|
1381
1714
|
let __pulse_fastly_last_error: i32 = 0
|
|
1382
1715
|
let __pulse_fastly_error_stage: i32 = 0
|
|
1383
1716
|
let __pulse_fastly_error_effect: i32 = -1
|
|
1384
|
-
let __pulse_fastly_log_handle: i32 = -1
|
|
1385
1717
|
let __pulse_fastly_jwt_error: i32 = 0
|
|
1386
1718
|
let __pulse_fastly_jwt_secret_calls: i32 = 0
|
|
1387
1719
|
let __pulse_fastly_jwt_crypto_calls: i32 = 0
|
|
@@ -1398,57 +1730,168 @@ function __pulse_fastly_out_i32(): StaticArray<i32> { return new StaticArray<i32
|
|
|
1398
1730
|
function __pulse_fastly_out_value(out: StaticArray<i32>): i32 { return load<i32>(changetype<usize>(out)) }
|
|
1399
1731
|
function __pulse_fastly_utf8(value: string): ArrayBuffer { return String.UTF8.encode(value, false) }
|
|
1400
1732
|
function __pulse_fastly_decode(buffer: Uint8Array, length: i32): string { return String.UTF8.decodeUnsafe(buffer.dataStart, length, false) }
|
|
1401
|
-
|
|
1402
|
-
|
|
1733
|
+
${nativeStringAssembly}
|
|
1734
|
+
${nativeStringConcat}
|
|
1735
|
+
${nativeStringTrim}
|
|
1736
|
+
${nativeStringIndex}
|
|
1403
1737
|
function __pulse_fastly_deep_freeze(handle: i32): void { const value = __pulse_fastly_value(handle); if (value.immutable) return; value.immutable = true; if (value.kind == PULSE_VALUE_ARRAY || value.kind == PULSE_VALUE_OBJECT) for (let index = 0; index < value.values.length; index += 1) __pulse_fastly_deep_freeze(unchecked(value.values[index])) }
|
|
1404
|
-
function __pulse_fastly_string_value(value: string): i32 {
|
|
1738
|
+
function __pulse_fastly_string_value(value: string): i32 {
|
|
1739
|
+
const index = value.length == 0 ? 65536 : value.length == 1 ? value.charCodeAt(0) : -1;
|
|
1740
|
+
if (index >= 0) { const cached = unchecked(__pulse_fastly_character_handles[index]); if (cached != 0) return cached }
|
|
1741
|
+
const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_STRING; item.text = value;
|
|
1742
|
+
const handle = __pulse_fastly_put(item);
|
|
1743
|
+
if (index >= 0) { unchecked(__pulse_fastly_character_handles[index] = handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} }
|
|
1744
|
+
return handle
|
|
1745
|
+
}
|
|
1405
1746
|
function __pulse_fastly_find(object: __PulseFastlyValue, key: string): i32 { for (let i = 0; i < object.keys.length; i += 1) if (unchecked(object.keys[i]) == key) return i; return -1 }
|
|
1406
1747
|
function __pulse_fastly_number_string(value: f64): string { if (value >= -9007199254740991.0 && value <= 9007199254740991.0 && Math.floor(value) == value) return i64(value).toString(); return value.toString() }
|
|
1407
1748
|
function __pulse_fastly_string(handle: i32): string { const value = __pulse_fastly_value(handle); if (value.kind == PULSE_VALUE_STRING) return value.text; if (value.kind == PULSE_VALUE_NUMBER) return __pulse_fastly_number_string(value.number); if (value.kind == PULSE_VALUE_BOOLEAN) return value.boolean != 0 ? "true" : "false"; if (value.kind == PULSE_VALUE_NULL) return "null"; if (value.kind == PULSE_VALUE_UNDEFINED) return "undefined"; return __pulse_fastly_json(handle, 0) }
|
|
1408
|
-
function __pulse_fastly_remember_secret(value: string): string { if (value.length == 0) return value; for (let i = 0; i <
|
|
1749
|
+
function __pulse_fastly_remember_secret(value: string, field: bool = false): string { if (value.length == 0) return value; const values = field ? __pulse_fastly_field_redactions : __pulse_fastly_redactions; for (let i = 0; i < values.length; i += 1) if (unchecked(values[i]) == value) return value; values.push(value); return value }
|
|
1409
1750
|
function __pulse_fastly_replace_secret(input: string, secret: string): string { if (secret.length == 0) return input; let source = input; let output = ""; while (true) { const index = source.indexOf(secret); if (index < 0) return output + source; output += source.substring(0, index) + "<redacted>"; source = source.substring(index + secret.length) } }
|
|
1410
|
-
|
|
1411
|
-
|
|
1751
|
+
// Walk complete JSON string tokens, including escaped quotes. Substring matching
|
|
1752
|
+
// a quoted field would still corrupt an unrelated token such as "prefix\\\"id".
|
|
1753
|
+
function __pulse_fastly_redact_fields(input: string): string {
|
|
1754
|
+
if (__pulse_fastly_field_redactions.length == 0) return input;
|
|
1755
|
+
let output = "", start = 0, cursor = 0;
|
|
1756
|
+
while (cursor < input.length) {
|
|
1757
|
+
if (input.charCodeAt(cursor) != 34) { cursor += 1; continue; }
|
|
1758
|
+
const begin = cursor++;
|
|
1759
|
+
while (cursor < input.length) {
|
|
1760
|
+
const code = input.charCodeAt(cursor++);
|
|
1761
|
+
if (code == 92 && cursor < input.length) cursor += 1;
|
|
1762
|
+
else if (code == 34) break;
|
|
1763
|
+
}
|
|
1764
|
+
const token = input.substring(begin, cursor);
|
|
1765
|
+
for (let i = 0; i < __pulse_fastly_field_redactions.length; i += 1) {
|
|
1766
|
+
if (token != unchecked(__pulse_fastly_field_redactions[i])) continue;
|
|
1767
|
+
output += input.substring(start, begin) + __pulse_fastly_quote("<redacted>");
|
|
1768
|
+
start = cursor; break;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
return output + input.substring(start);
|
|
1772
|
+
}
|
|
1773
|
+
function __pulse_fastly_redact(input: string): string { let output = __pulse_fastly_redact_fields(input); for (let i = 0; i < __pulse_fastly_redactions.length; i += 1) output = __pulse_fastly_replace_secret(output, unchecked(__pulse_fastly_redactions[i])); return output }
|
|
1774
|
+
function host_log(level: i32, messageHandle: i32): void { let label = "unknown"; if (level == 1) label = "error"; else if (level == 2) label = "warn"; else if (level == 3) label = "info"; else if (level == 4) label = "debug"; const bytes = __pulse_fastly_utf8("[pulse:" + label + "] " + __pulse_fastly_redact(__pulse_fastly_string(messageHandle)) + "\n"); __pulse_fastly_write_stdout(bytes) }
|
|
1412
1775
|
function __pulse_fastly_number(handle: i32): f64 { const value = __pulse_fastly_value(handle); if (value.kind == PULSE_VALUE_NUMBER) return value.number; if (value.kind == PULSE_VALUE_BOOLEAN) return value.boolean != 0 ? 1.0 : 0.0; if (value.kind == PULSE_VALUE_STRING) return F64.parseFloat(value.text); return 0.0 }
|
|
1413
1776
|
function __pulse_fastly_hex(value: i32): string { return String.fromCharCode(value < 10 ? 48 + value : 87 + value) }
|
|
1414
|
-
function
|
|
1415
|
-
|
|
1416
|
-
if (
|
|
1777
|
+
function __pulse_fastly_is_unpaired(value: string, i: i32): bool {
|
|
1778
|
+
const c = value.charCodeAt(i);
|
|
1779
|
+
if (c >= 0xd800 && c <= 0xdbff) return i + 1 >= value.length || value.charCodeAt(i + 1) < 0xdc00 || value.charCodeAt(i + 1) > 0xdfff;
|
|
1780
|
+
if (c >= 0xdc00 && c <= 0xdfff) return i == 0 || value.charCodeAt(i - 1) < 0xd800 || value.charCodeAt(i - 1) > 0xdbff;
|
|
1781
|
+
return false;
|
|
1782
|
+
}
|
|
1783
|
+
function __pulse_fastly_quote(value: string): string {
|
|
1784
|
+
// Count once and write UTF-16 units once. Repeated concatenation can allocate
|
|
1785
|
+
// gigabytes for a valid bounded string when every byte needs JSON escaping.
|
|
1786
|
+
let size = 2;
|
|
1787
|
+
for (let i = 0; i < value.length; i++) {
|
|
1788
|
+
const c = value.charCodeAt(i);
|
|
1789
|
+
size += c == 34 || c == 92 || c == 8 || c == 9 || c == 10 || c == 12 || c == 13 ? 2 : (c < 32 || __pulse_fastly_is_unpaired(value, i)) ? 6 : 1;
|
|
1790
|
+
}
|
|
1791
|
+
const out = new Uint16Array(size); let cursor = 0; out[cursor++] = 34;
|
|
1792
|
+
for (let i = 0; i < value.length; i++) {
|
|
1793
|
+
const c = value.charCodeAt(i);
|
|
1794
|
+
if (c == 34 || c == 92) { out[cursor++] = 92; out[cursor++] = u16(c); }
|
|
1795
|
+
else if (c == 8 || c == 9 || c == 10 || c == 12 || c == 13) {
|
|
1796
|
+
out[cursor++] = 92; out[cursor++] = c == 8 ? 98 : c == 9 ? 116 : c == 10 ? 110 : c == 12 ? 102 : 114;
|
|
1797
|
+
} else if (c < 32 || __pulse_fastly_is_unpaired(value, i)) {
|
|
1798
|
+
out[cursor++] = 92; out[cursor++] = 117;
|
|
1799
|
+
out[cursor++] = u16('0123456789abcdef'.charCodeAt((c >> 12) & 15)); out[cursor++] = u16('0123456789abcdef'.charCodeAt((c >> 8) & 15));
|
|
1800
|
+
out[cursor++] = u16('0123456789abcdef'.charCodeAt((c >> 4) & 15)); out[cursor++] = u16('0123456789abcdef'.charCodeAt(c & 15));
|
|
1801
|
+
} else out[cursor++] = u16(c);
|
|
1802
|
+
}
|
|
1803
|
+
out[cursor] = 34;
|
|
1804
|
+
return String.UTF16.decodeUnsafe(out.dataStart, out.byteLength);
|
|
1805
|
+
}
|
|
1806
|
+
function __pulse_fastly_json_parts(handle: i32, depth: i32, parts: Array<string>): void {
|
|
1807
|
+
if (depth > ${schemaParserDepth(plan, plan.effects.some(effect => conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) ? 128 : 64)}) { __pulse_fastly_fail(PULSE_ERROR_JSON, 2, -1); parts.push("null"); return }
|
|
1417
1808
|
const value = __pulse_fastly_value(handle)
|
|
1418
|
-
if (value.kind ==
|
|
1419
|
-
if (value.kind ==
|
|
1420
|
-
if (value.kind ==
|
|
1421
|
-
if (value.kind ==
|
|
1422
|
-
if (value.kind ==
|
|
1423
|
-
|
|
1424
|
-
|
|
1809
|
+
if (value.kind == PULSE_VALUE_BOOLEAN) { parts.push(value.boolean != 0 ? "true" : "false"); return }
|
|
1810
|
+
if (value.kind == PULSE_VALUE_NUMBER) { parts.push(__pulse_fastly_number_string(value.number)); return }
|
|
1811
|
+
if (value.kind == PULSE_VALUE_STRING) { parts.push(__pulse_fastly_quote(value.text)); return }
|
|
1812
|
+
if (value.kind == PULSE_VALUE_ARRAY) { parts.push("["); for (let i = 0; i < value.values.length; i += 1) { if (i > 0) parts.push(","); __pulse_fastly_json_parts(unchecked(value.values[i]), depth + 1, parts) } parts.push("]"); return }
|
|
1813
|
+
if (value.kind == PULSE_VALUE_OBJECT) { parts.push("{"); for (let i = 0; i < value.keys.length; i += 1) { if (i > 0) parts.push(","); parts.push(__pulse_fastly_quote(unchecked(value.keys[i]))); parts.push(":"); __pulse_fastly_json_parts(unchecked(value.values[i]), depth + 1, parts) } parts.push("}"); return }
|
|
1814
|
+
parts.push("null")
|
|
1815
|
+
}
|
|
1816
|
+
function __pulse_fastly_json(handle: i32, depth: i32): string {
|
|
1817
|
+
// Join once, avoiding a complete copy of a multi-MiB leaf at every ancestor
|
|
1818
|
+
// and at every following member in the response or effect result.
|
|
1819
|
+
const parts = new Array<string>(); __pulse_fastly_json_parts(handle, depth, parts);
|
|
1820
|
+
return __pulse_fastly_join_strings(parts)
|
|
1425
1821
|
}
|
|
1426
1822
|
function __pulse_fastly_parse_json(text: string): i32 { const parser = new __PulseJsonParser(text); const value = parser.parse(); if (parser.failed || value <= 0) { __pulse_fastly_fail(PULSE_ERROR_JSON, 3, -1); return 0 } return value }
|
|
1427
1823
|
function __pulse_fastly_path(uri: string): string { let start = 0; const scheme = uri.indexOf("://"); if (scheme >= 0) { const slash = uri.indexOf("/", scheme + 3); start = slash >= 0 ? slash : uri.length } let end = uri.length; const query = uri.indexOf("?", start); if (query >= 0 && query < end) end = query; const fragment = uri.indexOf("#", start); if (fragment >= 0 && fragment < end) end = fragment; return start >= end ? "/" : uri.substring(start, end) }
|
|
1428
|
-
function __pulse_fastly_read_req_string(kind: i32): string { const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES); const written = __pulse_fastly_out_i32(); const status = kind == 0 ? fastly_http_req_method_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)) : fastly_http_req_uri_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 10 + kind, -1); return "" } return __pulse_fastly_decode(buffer, __pulse_fastly_out_value(written)) }
|
|
1429
|
-
function __pulse_fastly_read_body(handle: i32, effectIndex: i32
|
|
1430
|
-
|
|
1431
|
-
|
|
1824
|
+
function __pulse_fastly_read_req_string(kind: i32): string { const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES); const written = __pulse_fastly_out_i32(); const status = kind == 0 ? fastly_http_req_method_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)) : fastly_http_req_uri_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 10 + kind, -1); return "" } ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? 'if (__pulse_fastly_out_value(written)<0 || __pulse_fastly_out_value(written)>buffer.byteLength) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL,10+kind,-1);return ""; }' : ''} return __pulse_fastly_decode(buffer, __pulse_fastly_out_value(written)) }
|
|
1825
|
+
function __pulse_fastly_read_body(handle: i32, effectIndex: i32, requestText: bool = false): string {
|
|
1826
|
+
// One retained input allocation, including a byte to distinguish exact-limit
|
|
1827
|
+
// EOF from overflow. Short reads never allocate another chunk or prefix.
|
|
1828
|
+
if (__PULSE_SCHEMA_MAX_BYTES <= 0 || __PULSE_SCHEMA_MAX_BYTES > 1073741823) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, effectIndex); return "" }
|
|
1829
|
+
const bytes = new Uint8Array(__PULSE_SCHEMA_MAX_BYTES + 1), read = __pulse_fastly_out_i32();
|
|
1830
|
+
let total = 0;
|
|
1831
|
+
while (true) {
|
|
1832
|
+
if (!__pulse_fastly_body_read_ready(handle, effectIndex)) return "";
|
|
1833
|
+
const capacity = min<i32>(PULSE_FASTLY_BUFFER_BYTES, bytes.length - total);
|
|
1834
|
+
const status = fastly_http_body_read(handle, bytes.dataStart + total, capacity, changetype<usize>(read));
|
|
1835
|
+
if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 20, effectIndex); return "" }
|
|
1836
|
+
const count = __pulse_fastly_out_value(read);
|
|
1837
|
+
if (count < 0 || count > capacity) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 20, effectIndex); return "" }
|
|
1838
|
+
if (!count) break;
|
|
1839
|
+
total += count;
|
|
1840
|
+
if (total > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(requestText ? PULSE_ERROR_REQUEST_BODY : PULSE_ERROR_SCHEMA, 21, effectIndex); return "" }
|
|
1841
|
+
}
|
|
1842
|
+
// UTF-8 scalars may straddle host read chunks. Decode the bounded body once.
|
|
1843
|
+
if (requestText && !__pulse_request_utf8_valid(bytes, total)) { __pulse_fastly_fail(PULSE_ERROR_REQUEST_BODY, 24, effectIndex); return "" }
|
|
1844
|
+
return __pulse_fastly_decode(bytes, total);
|
|
1845
|
+
}
|
|
1846
|
+
function __pulse_fastly_body_read_ready(handle: i32, effectIndex: i32): bool { return true }
|
|
1847
|
+
function __pulse_fastly_request_header_text(name: string): string { const value = __pulse_header_lookup(__pulse_fastly_request_handle, name, false); return value === null ? "" : value }
|
|
1848
|
+
function __pulse_fastly_fetch_header_text(responseHandle: i32, name: string): string { const value = __pulse_header_lookup(responseHandle, name, true); return value === null ? "" : value }
|
|
1432
1849
|
function __pulse_fastly_fetch_body(value: __PulseFastlyValue): string { if (value.bodyLoaded == 0) { value.text = __pulse_fastly_read_body(value.bodyHandle, -1); value.bodyLoaded = 1 } return value.text }
|
|
1433
1850
|
|
|
1434
|
-
function
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1851
|
+
function __pulse_fastly_scalar(kind: i32, boolean: i32, slot: i32): i32 {
|
|
1852
|
+
const cached = unchecked(__pulse_fastly_scalar_handles[slot]); if (cached != 0) return cached;
|
|
1853
|
+
const item = new __PulseFastlyValue(); item.kind = kind; item.boolean = boolean;
|
|
1854
|
+
const handle = __pulse_fastly_put(item); unchecked(__pulse_fastly_scalar_handles[slot] = handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} return handle
|
|
1855
|
+
}
|
|
1856
|
+
function host_value_undefined(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_UNDEFINED, 0, 0) }
|
|
1857
|
+
function host_value_null(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_NULL, 0, 1) }
|
|
1858
|
+
function host_value_boolean(value: i32): i32 { return __pulse_fastly_scalar(PULSE_VALUE_BOOLEAN, value != 0 ? 1 : 0, value != 0 ? 3 : 2) }
|
|
1859
|
+
${plan.callables?.length || plan.binaryResources || plan.numericWorkspaces?.length ? require('./native-binary-resources.js').source() : ''}
|
|
1860
|
+
function host_value_number(value: f64): i32 {
|
|
1861
|
+
// Preserve negative zero, NaN, infinities and nonintegral values exactly.
|
|
1862
|
+
const cacheable = value >= 0 && value < 65536 && Math.floor(value) == value && (value != 0 || 1.0 / value > 0);
|
|
1863
|
+
const index = cacheable ? i32(value) : -1;
|
|
1864
|
+
if (index >= 0) { const cached = unchecked(__pulse_fastly_integer_handles[index]); if (cached != 0) return cached }
|
|
1865
|
+
const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_NUMBER; item.number = value;
|
|
1866
|
+
const handle = __pulse_fastly_put(item); if (index >= 0) { unchecked(__pulse_fastly_integer_handles[index] = handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} } return handle
|
|
1867
|
+
}
|
|
1868
|
+
function host_value_string(pointer: i32, length: i32): i32 {
|
|
1869
|
+
const text = changetype<string>(pointer);
|
|
1870
|
+
if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.has(text)) return __pulse_fastly_literal_handles.get(text);
|
|
1871
|
+
const handle = __pulse_fastly_string_value(text);
|
|
1872
|
+
if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.size < 1024) { __pulse_fastly_literal_handles.set(text, handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} }
|
|
1873
|
+
return handle
|
|
1874
|
+
}
|
|
1875
|
+
function host_effect_collection_count(handle: i32, max: i32, stateIndex: i32): i32 {
|
|
1876
|
+
const input = __pulse_fastly_value(handle)
|
|
1877
|
+
if (__pulse_fastly_last_error != 0 || input.kind != PULSE_VALUE_ARRAY || max < 1 || max > 32) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 173, -1); return -1 }
|
|
1878
|
+
if (input.values.length > max) { __pulse_fastly_fail(PULSE_ERROR_STATE, 172, -1); return -1 }
|
|
1879
|
+
if (__pulse_invocation_count + input.values.length > PULSE_FASTLY_MAX_EFFECT_INVOCATIONS) { __pulse_fastly_fail(PULSE_ERROR_STATE, 170, -1); return -1 }
|
|
1880
|
+
return input.values.length
|
|
1881
|
+
}
|
|
1439
1882
|
function host_value_array(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_ARRAY; return __pulse_fastly_put(item) }
|
|
1440
1883
|
function host_value_array_push(target: i32, value: i32): void { const item = __pulse_fastly_value(target); if (item.kind != PULSE_VALUE_ARRAY || item.immutable) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 30, -1); return } item.values.push(value) }
|
|
1441
1884
|
function host_value_array_spread(target: i32, source: i32): void { const from = __pulse_fastly_value(source); if (from.kind != PULSE_VALUE_ARRAY) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 31, -1); return } for (let i = 0; i < from.values.length; i += 1) host_value_array_push(target, unchecked(from.values[i])) }
|
|
1442
1885
|
function host_value_object(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_OBJECT; return __pulse_fastly_put(item) }
|
|
1443
1886
|
function host_value_object_set(target: i32, keyHandle: i32, value: i32): void { const item = __pulse_fastly_value(target); if (item.kind != PULSE_VALUE_OBJECT || item.immutable) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 32, -1); return } const key = __pulse_fastly_string(keyHandle); const index = __pulse_fastly_find(item, key); if (index >= 0) unchecked(item.values[index] = value); else { item.keys.push(key); item.values.push(value) } }
|
|
1444
1887
|
function host_value_object_spread(target: i32, source: i32): void { const from = __pulse_fastly_value(source); if (from.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 33, -1); return } for (let i = 0; i < from.keys.length; i += 1) host_value_object_set(target, __pulse_fastly_string_value(unchecked(from.keys[i])), unchecked(from.values[i])) }
|
|
1445
|
-
function host_value_property(target: i32, keyHandle: i32): i32 { const item = __pulse_fastly_value(target); const key = __pulse_fastly_string(keyHandle); if (item.kind == PULSE_VALUE_OBJECT) { const index = __pulse_fastly_find(item, key); return index >= 0 ? unchecked(item.values[index]) : host_value_undefined() } if (item.kind == PULSE_VALUE_FETCH && key == "status") { const out = __pulse_fastly_out_i32(); const status = fastly_http_resp_status_get(item.responseHandle, changetype<usize>(out)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 34, -1); return 0 } return host_value_number(__pulse_fastly_out_value(out)) } if ((item.kind == PULSE_VALUE_STRING || item.kind == PULSE_VALUE_ARRAY) && key == "length") return host_value_number(item.kind == PULSE_VALUE_STRING ? item.
|
|
1888
|
+
function host_value_property(target: i32, keyHandle: i32): i32 { const item = __pulse_fastly_value(target); const key = __pulse_fastly_string(keyHandle); if (item.kind == PULSE_VALUE_OBJECT) { const index = __pulse_fastly_find(item, key); return index >= 0 ? unchecked(item.values[index]) : host_value_undefined() } if (item.kind == PULSE_VALUE_FETCH && key == "status") { ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? 'return __fca_status(item);' : ''} const out = __pulse_fastly_out_i32(); const status = fastly_http_resp_status_get(item.responseHandle, changetype<usize>(out)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 34, -1); return 0 } return host_value_number(__pulse_fastly_out_value(out)) } if ((item.kind == PULSE_VALUE_STRING || item.kind == PULSE_VALUE_ARRAY) && key == "length") return host_value_number(item.kind == PULSE_VALUE_STRING ? item.textLength : item.values.length); return host_value_undefined() }
|
|
1446
1889
|
function host_value_property_set(target: i32, key: i32, value: i32): i32 { host_value_object_set(target, key, value); return value }
|
|
1447
|
-
function host_value_element(target: i32, keyHandle: i32): i32 { const item = __pulse_fastly_value(target); if (item.kind == PULSE_VALUE_ARRAY) { const index = i32(__pulse_fastly_number(keyHandle)); return index >= 0 && index < item.values.length ? unchecked(item.values[index]) : host_value_undefined() } return host_value_property(target, keyHandle) }
|
|
1890
|
+
function host_value_element(target: i32, keyHandle: i32): i32 { const item = __pulse_fastly_value(target); if (item.kind == PULSE_VALUE_STRING) return __pulse_fastly_string_index(item, keyHandle); if (item.kind == PULSE_VALUE_ARRAY) { const index = i32(__pulse_fastly_number(keyHandle)); return index >= 0 && index < item.values.length ? unchecked(item.values[index]) : host_value_undefined() } return host_value_property(target, keyHandle) }
|
|
1448
1891
|
function host_value_element_set(target: i32, keyHandle: i32, value: i32): i32 { const item = __pulse_fastly_value(target); if (item.immutable) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 35, -1); return 0 } if (item.kind == PULSE_VALUE_ARRAY) { const index = i32(__pulse_fastly_number(keyHandle)); if (index < 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 35, -1); return 0 } while (item.values.length <= index) item.values.push(host_value_undefined()); unchecked(item.values[index] = value); return value } return host_value_property_set(target, keyHandle, value) }
|
|
1449
|
-
function host_value_truthy(handle: i32): i32 { const value = __pulse_fastly_value(handle); if (value.kind == PULSE_VALUE_UNDEFINED || value.kind == PULSE_VALUE_NULL) return 0; if (value.kind == PULSE_VALUE_BOOLEAN) return value.boolean; if (value.kind == PULSE_VALUE_NUMBER) return value.number != 0.0 && !isNaN(value.number) ? 1 : 0; if (value.kind == PULSE_VALUE_STRING) return value.
|
|
1892
|
+
function host_value_truthy(handle: i32): i32 { const value = __pulse_fastly_value(handle); if (value.kind == PULSE_VALUE_UNDEFINED || value.kind == PULSE_VALUE_NULL) return 0; if (value.kind == PULSE_VALUE_BOOLEAN) return value.boolean; if (value.kind == PULSE_VALUE_NUMBER) return value.number != 0.0 && !isNaN(value.number) ? 1 : 0; if (value.kind == PULSE_VALUE_STRING) return value.textLength > 0 ? 1 : 0; return 1 }
|
|
1450
1893
|
function host_value_nullish(handle: i32): i32 { const kind = __pulse_fastly_value(handle).kind; return kind == PULSE_VALUE_UNDEFINED || kind == PULSE_VALUE_NULL ? 1 : 0 }
|
|
1451
|
-
function host_value_binary(operator: i32, leftHandle: i32, rightHandle: i32): i32 { const left = __pulse_fastly_value(leftHandle); const right = __pulse_fastly_value(rightHandle); if (operator == 0 || operator == 2) { let equal = false; if (left.kind == right.kind) { if (left.kind == PULSE_VALUE_UNDEFINED || left.kind == PULSE_VALUE_NULL) equal = true; else if (left.kind == PULSE_VALUE_BOOLEAN) equal = left.boolean == right.boolean; else if (left.kind == PULSE_VALUE_NUMBER) equal = left.number == right.number; else if (left.kind == PULSE_VALUE_STRING) equal = left.text == right.text; else equal = leftHandle == rightHandle } return host_value_boolean(equal ? 1 : 0) } if (operator == 1 || operator == 3) { const result = host_value_binary(operator == 1 ? 0 : 2, leftHandle, rightHandle); return host_value_boolean(host_value_truthy(result) == 0 ? 1 : 0) } if (operator == 8) { if (left.kind == PULSE_VALUE_STRING || right.kind == PULSE_VALUE_STRING) return
|
|
1894
|
+
function host_value_binary(operator: i32, leftHandle: i32, rightHandle: i32): i32 { const left = __pulse_fastly_value(leftHandle); const right = __pulse_fastly_value(rightHandle); if (operator == 0 || operator == 2) { let equal = false; if (left.kind == right.kind) { if (left.kind == PULSE_VALUE_UNDEFINED || left.kind == PULSE_VALUE_NULL) equal = true; else if (left.kind == PULSE_VALUE_BOOLEAN) equal = left.boolean == right.boolean; else if (left.kind == PULSE_VALUE_NUMBER) equal = left.number == right.number; else if (left.kind == PULSE_VALUE_STRING) equal = left.text == right.text; else equal = leftHandle == rightHandle } return host_value_boolean(equal ? 1 : 0) } if (operator == 1 || operator == 3) { const result = host_value_binary(operator == 1 ? 0 : 2, leftHandle, rightHandle); return host_value_boolean(host_value_truthy(result) == 0 ? 1 : 0) } if (operator == 8) { if (left.kind == PULSE_VALUE_STRING || right.kind == PULSE_VALUE_STRING) return __pulse_fastly_concat(leftHandle, rightHandle); return host_value_number(__pulse_fastly_number(leftHandle) + __pulse_fastly_number(rightHandle)) } if (left.kind == PULSE_VALUE_STRING && right.kind == PULSE_VALUE_STRING) { if (operator == 4) return host_value_boolean(left.text < right.text ? 1 : 0); if (operator == 5) return host_value_boolean(left.text <= right.text ? 1 : 0); if (operator == 6) return host_value_boolean(left.text > right.text ? 1 : 0); if (operator == 7) return host_value_boolean(left.text >= right.text ? 1 : 0); } if (operator == 4) return host_value_boolean(__pulse_fastly_number(leftHandle) < __pulse_fastly_number(rightHandle) ? 1 : 0); if (operator == 5) return host_value_boolean(__pulse_fastly_number(leftHandle) <= __pulse_fastly_number(rightHandle) ? 1 : 0); if (operator == 6) return host_value_boolean(__pulse_fastly_number(leftHandle) > __pulse_fastly_number(rightHandle) ? 1 : 0); if (operator == 7) return host_value_boolean(__pulse_fastly_number(leftHandle) >= __pulse_fastly_number(rightHandle) ? 1 : 0); if (operator == 9) return host_value_number(__pulse_fastly_number(leftHandle) - __pulse_fastly_number(rightHandle)); if (operator == 10) return host_value_number(__pulse_fastly_number(leftHandle) * __pulse_fastly_number(rightHandle)); if (operator == 11) return host_value_number(__pulse_fastly_number(leftHandle) / __pulse_fastly_number(rightHandle)); if (operator == 12) return host_value_number(__pulse_fastly_number(leftHandle) % __pulse_fastly_number(rightHandle)); if (operator == 14) return host_value_truthy(leftHandle) != 0 ? rightHandle : leftHandle; if (operator == 15) return host_value_truthy(leftHandle) != 0 ? leftHandle : rightHandle; if (operator == 16) return host_value_nullish(leftHandle) == 0 ? leftHandle : rightHandle; __pulse_fastly_fail(PULSE_ERROR_UNSUPPORTED, 36, -1); return 0 }
|
|
1452
1895
|
function host_value_unary(operator: i32, handle: i32): i32 { if (operator == 0) return host_value_boolean(host_value_truthy(handle) == 0 ? 1 : 0); if (operator == 1) return host_value_number(__pulse_fastly_number(handle)); if (operator == 2) return host_value_number(-__pulse_fastly_number(handle)); if (operator == 5) return host_value_undefined(); __pulse_fastly_fail(PULSE_ERROR_UNSUPPORTED, 37, -1); return 0 }
|
|
1453
1896
|
|
|
1454
1897
|
function host_request_method(): i32 { return __pulse_fastly_string_value(__pulse_fastly_request_method) }
|
|
@@ -1459,13 +1902,36 @@ function __pulse_router_param_text(path: string, pattern: string, name: string):
|
|
|
1459
1902
|
function host_router_match(path: i32, pattern: i32): i32 { return host_value_boolean(__pulse_router_match_text(__pulse_fastly_string(path), __pulse_fastly_string(pattern)) ? 1 : 0) }
|
|
1460
1903
|
function host_router_param(path: i32, pattern: i32, name: i32): i32 { const value = __pulse_router_param_text(__pulse_fastly_string(path), __pulse_fastly_string(pattern), __pulse_fastly_string(name)); return value === null ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1461
1904
|
function host_request_path(): i32 { return __pulse_fastly_string_value(__pulse_fastly_request_path) }
|
|
1462
|
-
function host_request_headers(): i32 { return
|
|
1463
|
-
function host_request_header(name: i32): i32 { const value =
|
|
1464
|
-
function host_request_text(): i32 { if (__pulse_fastly_request_body_loaded == 0) { __pulse_fastly_request_body = __pulse_fastly_read_body(__pulse_fastly_request_body_handle, -1); __pulse_fastly_request_body_loaded = 1 } return __pulse_fastly_string_value(__pulse_fastly_request_body) }
|
|
1465
|
-
function host_request_json(schema: i32): i32 { const parsed = __pulse_fastly_parse_json(__pulse_fastly_string(host_request_text())); if (parsed <= 0) return 0; const schemaValue = __pulse_fastly_value(schema); return schemaValue.kind == PULSE_VALUE_STRING ? __pulse_fastly_schema_apply(schemaValue.text, parsed, false) : parsed }
|
|
1905
|
+
function host_request_headers(): i32 { return __pulse_request_headers_read() }
|
|
1906
|
+
function host_request_header(name: i32): i32 { const value = __pulse_header_lookup(__pulse_fastly_request_handle, __pulse_fastly_string(name), false); return value === null ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1907
|
+
function host_request_text(): i32 { if (__pulse_request_body_failure != 0) { __pulse_fastly_fail(PULSE_ERROR_REQUEST_BODY, __pulse_request_body_failure, -1); return 0 } if (__pulse_fastly_request_body_loaded == 0) { __pulse_fastly_request_body = __pulse_fastly_read_body(__pulse_fastly_request_body_handle, -1, true); __pulse_fastly_request_body_loaded = 1; if (__pulse_fastly_last_error == PULSE_ERROR_REQUEST_BODY) __pulse_request_body_failure = __pulse_fastly_error_stage } if (__pulse_fastly_last_error != 0) return 0; return __pulse_fastly_string_value(__pulse_fastly_request_body) }
|
|
1908
|
+
function host_request_json(schema: i32): i32 { const parsed = ${hasJsonAdmission(plan) ? '__pulse_fastly_schema_parse_json(__pulse_fastly_string(host_request_text()), schema)' : '__pulse_fastly_parse_json(__pulse_fastly_string(host_request_text()))'}; if (parsed <= 0) return 0; const schemaValue = __pulse_fastly_value(schema); return schemaValue.kind == PULSE_VALUE_STRING ? __pulse_fastly_schema_apply(schemaValue.text, parsed, false) : parsed }
|
|
1466
1909
|
function __pulse_fastly_headers_from_options(options: __PulseFastlyValue, output: __PulseFastlyValue): void { const index = __pulse_fastly_find(options, "headers"); if (index < 0) return; const headers = __pulse_fastly_value(unchecked(options.values[index])); if (headers.kind == PULSE_VALUE_OBJECT) { for (let i = 0; i < headers.keys.length; i += 1) { const name = unchecked(headers.keys[i]); const value = __pulse_fastly_value(unchecked(headers.values[i])); if (value.kind == PULSE_VALUE_ARRAY) { for (let j = 0; j < value.values.length; j += 1) output.headers.push(new __PulseFastlyHeader(name, __pulse_fastly_string(unchecked(value.values[j])))) } else output.headers.push(new __PulseFastlyHeader(name, __pulse_fastly_string(unchecked(headers.values[i])))) } return } if (headers.kind == PULSE_VALUE_ARRAY) { for (let i = 0; i < headers.values.length; i += 1) { const pair = __pulse_fastly_value(unchecked(headers.values[i])); if (pair.kind != PULSE_VALUE_ARRAY || pair.values.length < 2) continue; output.headers.push(new __PulseFastlyHeader(__pulse_fastly_string(unchecked(pair.values[0])), __pulse_fastly_string(unchecked(pair.values[1])))) } } }
|
|
1467
1910
|
function __pulse_fastly_response(value: i32, optionsHandle: i32, json: bool): i32 { const output = new __PulseFastlyValue(); output.kind = PULSE_VALUE_RESPONSE; output.status = 200; const options = __pulse_fastly_value(optionsHandle); let bodyValue = value; if (options.kind == PULSE_VALUE_OBJECT) { const statusIndex = __pulse_fastly_find(options, "status"); if (statusIndex >= 0) output.status = i32(__pulse_fastly_number(unchecked(options.values[statusIndex]))); const schemaIndex = __pulse_fastly_find(options, "schema"); if (json && schemaIndex >= 0) { bodyValue = __pulse_fastly_schema_apply(__pulse_fastly_string(unchecked(options.values[schemaIndex])), value, true); if (bodyValue <= 0) return 0 } __pulse_fastly_headers_from_options(options, output) } output.text = json ? __pulse_fastly_json(bodyValue, 0) : __pulse_fastly_string(value); let hasContentType = false; for (let i = 0; i < output.headers.length; i += 1) if (unchecked(output.headers[i]).name.toLowerCase() == "content-type") hasContentType = true; if (!hasContentType) output.headers.push(new __PulseFastlyHeader("content-type", json ? "application/json; charset=utf-8" : "text/plain; charset=utf-8")); return __pulse_fastly_put(output) }
|
|
1468
1911
|
function host_response_json(value: i32, options: i32): i32 { return __pulse_fastly_response(value, options, true) }
|
|
1912
|
+
function host_schema_decode(text: i32, schema: i32): i32 {
|
|
1913
|
+
const id = __pulse_fastly_value(schema)
|
|
1914
|
+
if (id.kind != PULSE_VALUE_STRING || id.text.length == 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1); return 0 }
|
|
1915
|
+
const input = __pulse_fastly_value(text)
|
|
1916
|
+
if (input.kind != PULSE_VALUE_STRING) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }
|
|
1917
|
+
if (String.UTF8.byteLength(input.text) > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, -1); return 0 }
|
|
1918
|
+
const parsed = ${hasJsonAdmission(plan) ? '__pulse_fastly_schema_parse_json(input.text, schema)' : '__pulse_fastly_parse_json(input.text)'}
|
|
1919
|
+
if (parsed <= 0) return 0
|
|
1920
|
+
const decoded = __pulse_fastly_schema_apply(id.text, parsed, false)
|
|
1921
|
+
if (decoded <= 0) return 0
|
|
1922
|
+
__pulse_fastly_deep_freeze(decoded)
|
|
1923
|
+
return decoded
|
|
1924
|
+
}
|
|
1925
|
+
function host_schema_encode(value: i32, schema: i32): i32 {
|
|
1926
|
+
const id = __pulse_fastly_value(schema)
|
|
1927
|
+
if (id.kind != PULSE_VALUE_STRING || id.text.length == 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1); return 0 }
|
|
1928
|
+
${textEncodeIds.length ? `const textResult = ${textEncodeIds.map(id => `id.text == ${quote(id)}`).join(' || ')}
|
|
1929
|
+
const projected = __pulse_fastly_schema_apply(id.text, value, true, textResult)` : 'const projected = __pulse_fastly_schema_apply(id.text, value, true)'}
|
|
1930
|
+
if (projected <= 0) return 0
|
|
1931
|
+
const text = ${textEncodeIds.length ? 'textResult ? __pulse_fastly_string(projected) : ' : ''}__pulse_fastly_json(projected, 0)
|
|
1932
|
+
if (String.UTF8.byteLength(text) > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, -1); return 0 }
|
|
1933
|
+
return ${textEncodeIds.length ? 'textResult ? projected : ' : ''}__pulse_fastly_string_value(text)
|
|
1934
|
+
}
|
|
1469
1935
|
function host_response_text(value: i32, options: i32): i32 { return __pulse_fastly_response(value, options, false) }
|
|
1470
1936
|
function host_response_custom(specHandle: i32): i32 { const spec = __pulse_fastly_value(specHandle); if (spec.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 40, -1); return 0 } const output = new __PulseFastlyValue(); output.kind = PULSE_VALUE_RESPONSE; output.status = 200; const statusIndex = __pulse_fastly_find(spec, "status"); if (statusIndex >= 0) output.status = i32(__pulse_fastly_number(unchecked(spec.values[statusIndex]))); const bodyIndex = __pulse_fastly_find(spec, "body"); if (bodyIndex >= 0) output.text = __pulse_fastly_string(unchecked(spec.values[bodyIndex])); __pulse_fastly_headers_from_options(spec, output); return __pulse_fastly_put(output) }
|
|
1471
1937
|
function __pulse_fastly_grip_frame_channels(options: __PulseFastlyValue, output: __PulseFastlyValue): bool { const channelsIndex = __pulse_fastly_find(options, "channels"); const channelIndex = __pulse_fastly_find(options, "channel"); let count = 0; if (channelsIndex >= 0) { const channels = __pulse_fastly_value(unchecked(options.values[channelsIndex])); if (channels.kind != PULSE_VALUE_ARRAY) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 133, -1); return false } for (let i = 0; i < channels.values.length; i += 1) { const channel = __pulse_fastly_string(unchecked(channels.values[i])).trim(); if (channel.length == 0 || channel.indexOf(",") >= 0 || channel.indexOf("\r") >= 0 || channel.indexOf("\n") >= 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 134, -1); return false } output.headers.push(new __PulseFastlyHeader("Grip-Channel", channel)); count += 1 } } else if (channelIndex >= 0) { const channel = __pulse_fastly_string(unchecked(options.values[channelIndex])).trim(); if (channel.length == 0 || channel.indexOf(",") >= 0 || channel.indexOf("\r") >= 0 || channel.indexOf("\n") >= 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 134, -1); return false } output.headers.push(new __PulseFastlyHeader("Grip-Channel", channel)); count = 1 } if (count == 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 135, -1); return false } return true }
|
|
@@ -1475,8 +1941,8 @@ function host_grip_subscribe(responseHandle: i32, optionsHandle: i32): i32 { con
|
|
|
1475
1941
|
function host_grip_handoff(optionsHandle: i32): i32 { const options = __pulse_fastly_value(optionsHandle); if (options.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 139, -1); return 0 } const output = new __PulseFastlyValue(); output.kind = PULSE_VALUE_RESPONSE; output.status = 200; const statusIndex = __pulse_fastly_find(options, "status"); if (statusIndex >= 0) output.status = i32(__pulse_fastly_number(unchecked(options.values[statusIndex]))); const bodyIndex = __pulse_fastly_find(options, "body"); if (bodyIndex >= 0) output.text = __pulse_fastly_string(unchecked(options.values[bodyIndex])); __pulse_fastly_headers_from_options(options, output); let hasContentType = false; for (let i = 0; i < output.headers.length; i += 1) if (unchecked(output.headers[i]).name.toLowerCase() == "content-type") hasContentType = true; if (!hasContentType) output.headers.push(new __PulseFastlyHeader("Content-Type", "application/websocket-events")); return __pulse_fastly_grip_frame(options, output) ? __pulse_fastly_put(output) : 0 }
|
|
1476
1942
|
function host_kv_namespace(name: i32): i32 { return name }
|
|
1477
1943
|
function host_fetch_text(responseHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 42, -1); return 0 } return __pulse_fastly_string_value(__pulse_fastly_fetch_body(response)) }
|
|
1478
|
-
function host_fetch_json(responseHandle: i32, schemaHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 43, -1); return 0 } if (__PULSE_SCHEMA_REQUIRE_JSON) { const contentType = __pulse_fastly_fetch_header_text(response.responseHandle, "content-type"); if (contentType.length == 0 || contentType.toLowerCase().indexOf("json") < 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 44, -1); return 0 } } const parsed = __pulse_fastly_parse_json(__pulse_fastly_fetch_body(response)); if (parsed <= 0) return 0; const schema = __pulse_fastly_value(schemaHandle); return schema.kind == PULSE_VALUE_STRING ? __pulse_fastly_schema_apply(schema.text, parsed, false) : parsed }
|
|
1479
|
-
function host_fetch_header(responseHandle: i32, nameHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 45, -1); return 0 } const value =
|
|
1944
|
+
function host_fetch_json(responseHandle: i32, schemaHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 43, -1); return 0 } if (__PULSE_SCHEMA_REQUIRE_JSON) { ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? 'if (!__fca_json_type(response)) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA,44,-1); return 0; }' : `${options.fetchAccounting?.selection === 'canonical-single-get-json' ? 'if (!__fca_work()) return 0;' : ''} const contentType = __pulse_fastly_fetch_header_text(response.responseHandle, "content-type"); if (contentType.length == 0 || contentType.toLowerCase().indexOf("json") < 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 44, -1); return 0 }`} } const parsed = ${hasJsonAdmission(plan) ? '__pulse_fastly_schema_parse_json(__pulse_fastly_fetch_body(response), schemaHandle)' : '__pulse_fastly_parse_json(__pulse_fastly_fetch_body(response))'}; if (parsed <= 0) return 0; const schema = __pulse_fastly_value(schemaHandle); return schema.kind == PULSE_VALUE_STRING ? __pulse_fastly_schema_apply(schema.text, parsed, false) : parsed }
|
|
1945
|
+
function host_fetch_header(responseHandle: i32, nameHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 45, -1); return 0 } const value = ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? '__fca_header(response, __pulse_fastly_string(nameHandle))' : '__pulse_header_lookup(response.responseHandle, __pulse_fastly_string(nameHandle), true)'}; return value === null ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1480
1946
|
function __pulse_fastly_payload_field(payload: __PulseFastlyValue, name: string): i32 { const index = __pulse_fastly_find(payload, name); return index < 0 ? host_value_undefined() : unchecked(payload.values[index]) }
|
|
1481
1947
|
function __pulse_fastly_ready_effect(effectIndex: i32, handle: i32): void { unchecked(__pulse_fastly_ready[effectIndex] = handle); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_READY) }
|
|
1482
1948
|
function __pulse_fastly_open_config_store(effectIndex: i32): i32 { const nameBytes = __pulse_fastly_utf8(__pulse_fastly_config_store(effectIndex)); const out = __pulse_fastly_out_i32(); const status = fastly_config_store_open(changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(out)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 110, effectIndex); return 0 } return __pulse_fastly_out_value(out) }
|
|
@@ -1496,12 +1962,12 @@ function __pulse_fastly_jwt_record_verified(): void { __pulse_fastly_jwt_verifie
|
|
|
1496
1962
|
function __pulse_fastly_jwt_record_signing_input(bytes: i32, digest: string): void { __pulse_fastly_jwt_signing_input_bytes = bytes; __pulse_fastly_jwt_signing_input_sha256 = digest }
|
|
1497
1963
|
function __pulse_fastly_jwt_capture_clock(effectIndex: i32): f64 { __pulse_fastly_jwt_clock_calls += 1; __pulse_fastly_jwt_event_order += "C"; const out = new StaticArray<i64>(1); const status = wasi_snapshot_preview1_clock_time_get(0, 1, changetype<usize>(out)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 226, effectIndex); return NaN } ${jwtClockOverride ? `return ${jwtClockOverride}` : 'return f64(load<i64>(changetype<usize>(out))) / 1000000000.0'} }
|
|
1498
1964
|
function __pulse_fastly_jwt_validate_schema(effectIndex: i32, schemaId: string, claimsHandle: i32): i32 { __pulse_fastly_jwt_schema_calls += 1; __pulse_fastly_jwt_event_order += "H"; if (__pulse_fastly_jwt_case_id() == "claims-schema-failure-after-registered-claims") return 0; return __pulse_fastly_schema_apply(schemaId, claimsHandle, false) }
|
|
1965
|
+
function __pulse_fastly_jwt_sign_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const result = pulse_jwt_fastly_sign(effectIndex, unchecked(__pulse_fastly_payloads[effectIndex])); if (result > 0 && __pulse_fastly_last_error == 0) __pulse_fastly_ready_effect(effectIndex, result) }
|
|
1499
1966
|
function __pulse_fastly_jwt_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const result = pulse_jwt_fastly_verify(effectIndex, unchecked(__pulse_fastly_payloads[effectIndex])); if (result > 0 && __pulse_fastly_last_error == 0) __pulse_fastly_ready_effect(effectIndex, result) }
|
|
1500
|
-
function __pulse_fastly_start_request(effectIndex: i32, url: string, method: string, headers: __PulseFastlyValue, body: string, backend: string): void { const requestOut = __pulse_fastly_out_i32(); let status = fastly_http_req_new(changetype<usize>(requestOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 70, effectIndex); return } const requestHandle = __pulse_fastly_out_value(requestOut); const bodyOut = __pulse_fastly_out_i32(); status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 71, effectIndex); return } const bodyHandle = __pulse_fastly_out_value(bodyOut); const methodBytes = __pulse_fastly_utf8(method); status = fastly_http_req_method_set(requestHandle, changetype<usize>(methodBytes), methodBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 72, effectIndex); return } const urlBytes = __pulse_fastly_utf8(url); status = fastly_http_req_uri_set(requestHandle, changetype<usize>(urlBytes), urlBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 73, effectIndex); return } if (headers.kind == PULSE_VALUE_OBJECT) { for (let i = 0; i < headers.keys.length; i += 1) { const nameBytes = __pulse_fastly_utf8(unchecked(headers.keys[i])); const valueBytes = __pulse_fastly_utf8(__pulse_fastly_string(unchecked(headers.values[i]))); status =
|
|
1501
|
-
function __pulse_fastly_fetch_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const urlIndex = __pulse_fastly_find(payload, "url"); if (urlIndex < 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 48, effectIndex); return } const url = __pulse_fastly_string(unchecked(payload.values[urlIndex])); const initIndex = __pulse_fastly_find(payload, "init"); const init = initIndex >= 0 ? __pulse_fastly_value(unchecked(payload.values[initIndex])) : new __PulseFastlyValue(); let method = "GET"; let body = ""; let headers = new __PulseFastlyValue(); headers.kind = PULSE_VALUE_OBJECT; if (init.kind == PULSE_VALUE_OBJECT) { const methodIndex = __pulse_fastly_find(init, "method"); if (methodIndex >= 0) method = __pulse_fastly_string(unchecked(init.values[methodIndex])).toUpperCase(); const headersIndex = __pulse_fastly_find(init, "headers"); if (headersIndex >= 0) headers = __pulse_fastly_value(unchecked(init.values[headersIndex])); const bodyIndex = __pulse_fastly_find(init, "body"); const jsonIndex = __pulse_fastly_find(init, "json"); if (bodyIndex >= 0) { const bodyValue = unchecked(init.values[bodyIndex]); const bodyObject = __pulse_fastly_value(bodyValue); body = bodyObject.kind == PULSE_VALUE_STRING ? bodyObject.text : __pulse_fastly_json(bodyValue, 0) } else if (jsonIndex >= 0) { body =
|
|
1967
|
+
function __pulse_fastly_start_request(effectIndex: i32, url: string, method: string, headers: __PulseFastlyValue, body: string, backend: string): void { __pulse_transport_slots[effectIndex] = null; const requestOut = __pulse_fastly_out_i32(); let status = ${options.fetchAccounting ? '__fca_new(0,' : 'fastly_http_req_new('}changetype<usize>(requestOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 70, effectIndex); return } const requestHandle = __pulse_fastly_out_value(requestOut); const bodyOut = __pulse_fastly_out_i32(); status = ${options.fetchAccounting ? '__fca_new(1,' : 'fastly_http_body_new('}changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 71, effectIndex); return } const bodyHandle = __pulse_fastly_out_value(bodyOut); const methodBytes = __pulse_fastly_utf8(method); ${options.fetchAccounting ? 'if (!__fca_work()) return;' : ''} status = fastly_http_req_method_set(requestHandle, changetype<usize>(methodBytes), methodBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 72, effectIndex); return } const urlBytes = __pulse_fastly_utf8(url); ${options.fetchAccounting ? 'if (!__fca_work()) return;' : ''} status = fastly_http_req_uri_set(requestHandle, changetype<usize>(urlBytes), urlBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 73, effectIndex); return } if (headers.kind == PULSE_VALUE_OBJECT) { for (let i = 0; i < headers.keys.length; i += 1) { const nameBytes = __pulse_fastly_utf8(unchecked(headers.keys[i])); const valueBytes = __pulse_fastly_utf8(__pulse_fastly_string(unchecked(headers.values[i]))); ${options.fetchAccounting ? 'if (!__fca_work()) return;' : ''} status = fastly_http_req_header_append(requestHandle, changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(valueBytes), valueBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 74, effectIndex); return } } } if (body.length > 0) { status = ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? '__fca_write' : '__pulse_fastly_write_body'}(bodyHandle, body); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 75, effectIndex); return } } if (backend.length == 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 76, effectIndex); return } const backendBytes = __pulse_fastly_utf8(backend); const pendingOut = __pulse_fastly_out_i32(); status = ${options.fetchAccounting ? '__fca_dispatch' : 'fastly_http_req_send_async'}(requestHandle, bodyHandle, changetype<usize>(backendBytes), backendBytes.byteLength, changetype<usize>(pendingOut)); if (status != FASTLY_STATUS_OK) { ${options.fetchAccounting ? '__fca_cleanup();' : '__pulse_fetch_close_request(requestHandle); __pulse_fetch_close_body(bodyHandle);'} __pulse_transport_capture(effectIndex, status, 'send'); __pulse_fetch_fail(status, 77, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(pendingOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
1968
|
+
function __pulse_fastly_fetch_begin(effectIndex: i32, payload: __PulseFastlyValue): void { ${options.fetchAccounting ? options.fetchAccounting.selection === 'canonical-fetch-cohorts' ? 'if (!__fca_start(effectIndex)) return;' : 'if (!__fca_start()) return;' : ''} const urlIndex = __pulse_fastly_find(payload, "url"); if (urlIndex < 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 48, effectIndex); return } const url = __pulse_fastly_string(unchecked(payload.values[urlIndex])); const initIndex = __pulse_fastly_find(payload, "init"); const init = initIndex >= 0 ? __pulse_fastly_value(unchecked(payload.values[initIndex])) : new __PulseFastlyValue(); let method = "GET"; let body = ""; let headers = new __PulseFastlyValue(); headers.kind = PULSE_VALUE_OBJECT; if (init.kind == PULSE_VALUE_OBJECT) { const methodIndex = __pulse_fastly_find(init, "method"); if (methodIndex >= 0) method = __pulse_fastly_string(unchecked(init.values[methodIndex])).toUpperCase(); const headersIndex = __pulse_fastly_find(init, "headers"); if (headersIndex >= 0) headers = __pulse_fastly_value(unchecked(init.values[headersIndex])); headers = __pulse_fastly_fetch_headers(headers, effectIndex); if (__pulse_fastly_last_error != 0) return; const bodyIndex = __pulse_fastly_find(init, "body"); const jsonIndex = __pulse_fastly_find(init, "json"); if (bodyIndex >= 0) { const bodyValue = unchecked(init.values[bodyIndex]); const bodyObject = __pulse_fastly_value(bodyValue); body = bodyObject.kind == PULSE_VALUE_STRING ? bodyObject.text : __pulse_fastly_json(bodyValue, 0) } else if (jsonIndex >= 0) { body = __pulse_fastly_fetch_json_body(init, unchecked(init.values[jsonIndex])); if (__pulse_fastly_last_error != 0) return; headers = __pulse_fastly_fetch_json_headers(headers) } } __pulse_fastly_start_request(effectIndex, url, method, headers, body, __pulse_fastly_backend(effectIndex)) }
|
|
1502
1969
|
function __pulse_fastly_kv_get_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const store = __pulse_fastly_open_kv_store(effectIndex); if (__pulse_fastly_last_error != 0) return; const key = __pulse_fastly_string(__pulse_fastly_payload_field(payload, "key")); const keyBytes = __pulse_fastly_utf8(key); const lookupOut = __pulse_fastly_out_i32(); const config = new StaticArray<i32>(1); const status = fastly_kv_store_lookup(store, changetype<usize>(keyBytes), keyBytes.byteLength, 0, changetype<usize>(config), changetype<usize>(lookupOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 116, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(lookupOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
1503
|
-
|
|
1504
|
-
function __pulse_fastly_assets_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const value = __pulse_fastly_assets_payload(payload); if (__pulse_fastly_last_error != 0) return; const store = __pulse_fastly_open_kv_store(effectIndex); if (__pulse_fastly_last_error != 0) return; const key = __pulse_fastly_string(__pulse_fastly_payload_field(value, "key")); const keyBytes = __pulse_fastly_utf8(key); const lookupOut = __pulse_fastly_out_i32(); const config = new StaticArray<i32>(1); const status = fastly_kv_store_lookup(store, changetype<usize>(keyBytes), keyBytes.byteLength, 0, changetype<usize>(config), changetype<usize>(lookupOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 147, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(lookupOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
1970
|
+
${assetBeginSource}
|
|
1505
1971
|
function __pulse_fastly_kv_put_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const store = __pulse_fastly_open_kv_store(effectIndex); if (__pulse_fastly_last_error != 0) return; const key = __pulse_fastly_string(__pulse_fastly_payload_field(payload, "key")); const value = __pulse_fastly_payload_field(payload, "value"); const bodyOut = __pulse_fastly_out_i32(); let status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 117, effectIndex); return } const body = __pulse_fastly_out_value(bodyOut); status = __pulse_fastly_write_body(body, "{\"__pulseKv\":1,\"value\":" + __pulse_fastly_json(value, 0) + "}"); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 118, effectIndex); return } const keyBytes = __pulse_fastly_utf8(key); const insertOut = __pulse_fastly_out_i32(); const config = new StaticArray<i64>(4); status = fastly_kv_store_insert(store, changetype<usize>(keyBytes), keyBytes.byteLength, body, 0, changetype<usize>(config), changetype<usize>(insertOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 119, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(insertOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
1506
1972
|
function __pulse_fastly_grip_add_channel(channel: string): void { if (channel.length == 0) return; for (let i = 0; i < __pulse_fastly_grip_channels.length; i += 1) if (unchecked(__pulse_fastly_grip_channels[i]) == channel) return; __pulse_fastly_grip_channels.push(channel) }
|
|
1507
1973
|
function __pulse_fastly_grip_payload(payload: __PulseFastlyValue): __PulseFastlyValue { return __pulse_fastly_value(__pulse_fastly_payload_field(payload, "payload")) }
|
|
@@ -1510,27 +1976,32 @@ function __pulse_fastly_grip_hold_begin(effectIndex: i32, payload: __PulseFastly
|
|
|
1510
1976
|
${gripSecretSource}
|
|
1511
1977
|
function __pulse_fastly_grip_publish_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const valueHandle = __pulse_fastly_payload_field(payload, "payload"); const value = __pulse_fastly_value(valueHandle); if (value.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 122, effectIndex); return } const headers = new __PulseFastlyValue(); headers.kind = PULSE_VALUE_OBJECT; headers.keys.push("content-type"); headers.values.push(__pulse_fastly_string_value("application/json; charset=utf-8")); headers.keys.push("accept"); headers.values.push(__pulse_fastly_string_value("application/json")); ${gripAuthorizationSource} const body = __pulse_fastly_json(valueHandle, 0); if (String.UTF8.encode(body, false).byteLength > PULSE_FASTLY_BUFFER_BYTES) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 145, effectIndex); return } __pulse_fastly_start_request(effectIndex, __pulse_fastly_grip_publish_url(effectIndex), "POST", headers, body, __pulse_fastly_backend(effectIndex)) }
|
|
1512
1978
|
${effectDispatchSource(plan)}
|
|
1513
|
-
function __pulse_fastly_wait_fetch(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 80, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const responseOut = __pulse_fastly_out_i32(); const bodyOut = __pulse_fastly_out_i32(); const status =
|
|
1979
|
+
function __pulse_fastly_wait_fetch(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 80, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const responseOut = __pulse_fastly_out_i32(); const bodyOut = __pulse_fastly_out_i32(); const detail = new StaticArray<i32>(__PULSE_SEND_ERROR_DETAIL_WORDS); __pulse_send_error_detail_reset(changetype<usize>(detail)); const status = fastly_http_req_pending_req_wait_v2(pending, changetype<usize>(detail), changetype<usize>(responseOut), changetype<usize>(bodyOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK) { __pulse_transport_capture(effectIndex, status, 'wait', changetype<usize>(detail)); __pulse_fetch_fail(status, 81, effectIndex); return 0 } const value = new __PulseFastlyValue(); value.kind = PULSE_VALUE_FETCH; value.responseHandle = __pulse_fastly_out_value(responseOut); value.bodyHandle = __pulse_fastly_out_value(bodyOut); return __pulse_fastly_put(value) }
|
|
1514
1980
|
function __pulse_fastly_wait_grip_publish(effectIndex: i32): i32 { const responseHandle = __pulse_fastly_wait_fetch(effectIndex); if (responseHandle <= 0) return 0; const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 129, effectIndex); return 0 } const statusOut = __pulse_fastly_out_i32(); const statusCall = fastly_http_resp_status_get(response.responseHandle, changetype<usize>(statusOut)); if (statusCall != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 130, effectIndex); return 0 } const originStatus = __pulse_fastly_out_value(statusOut); if (originStatus < 200 || originStatus > 299) { __pulse_fastly_fail(PULSE_ERROR_TRANSPORT, 131, effectIndex); return 0 } const outer = __pulse_fastly_value(unchecked(__pulse_fastly_payloads[effectIndex])); const publicationHandle = __pulse_fastly_payload_field(outer, "payload"); const publication = __pulse_fastly_value(publicationHandle); if (publication.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 132, effectIndex); return 0 } const receipt = host_value_object(); host_value_object_set(receipt, __pulse_fastly_string_value("published"), host_value_boolean(1)); for (let i = 0; i < publication.keys.length; i += 1) host_value_object_set(receipt, __pulse_fastly_string_value(unchecked(publication.keys[i])), unchecked(publication.values[i])); const options = host_value_object(); host_value_object_set(options, __pulse_fastly_string_value("status"), host_value_number(202.0)); return host_response_json(receipt, options) }
|
|
1515
1981
|
function __pulse_fastly_wait_grip_broadcast(effectIndex: i32): i32 { const responseHandle = __pulse_fastly_wait_fetch(effectIndex); if (responseHandle <= 0) return 0; const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 142, effectIndex); return 0 } const statusOut = __pulse_fastly_out_i32(); const statusCall = fastly_http_resp_status_get(response.responseHandle, changetype<usize>(statusOut)); if (statusCall != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 143, effectIndex); return 0 } const originStatus = __pulse_fastly_out_value(statusOut); if (originStatus < 200 || originStatus > 299) { __pulse_fastly_fail(PULSE_ERROR_TRANSPORT, 144, effectIndex); return 0 } const receipt = host_value_object(); host_value_object_set(receipt, __pulse_fastly_string_value("accepted"), host_value_boolean(1)); host_value_object_set(receipt, __pulse_fastly_string_value("status"), host_value_number(f64(originStatus))); return receipt }
|
|
1516
1982
|
function __pulse_fastly_wait_ready(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_READY) { __pulse_fastly_fail(PULSE_ERROR_STATE, 124, effectIndex); return 0 } const value = unchecked(__pulse_fastly_ready[effectIndex]); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); unchecked(__pulse_fastly_ready[effectIndex] = 0); return value }
|
|
1517
1983
|
function __pulse_fastly_wait_kv_get(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) == PULSE_FASTLY_PENDING_READY) return __pulse_fastly_wait_ready(effectIndex); if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 125, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const bodyOut = __pulse_fastly_out_i32(); const metadata = new Uint8Array(1); const written = __pulse_fastly_out_i32(); const generation = new StaticArray<i64>(1); const kvError = __pulse_fastly_out_i32(); const status = fastly_kv_store_lookup_wait_v2(pending, changetype<usize>(bodyOut), metadata.dataStart, 0, changetype<usize>(written), changetype<usize>(generation), changetype<usize>(kvError)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 125, effectIndex); return 0 } const errorCode = __pulse_fastly_out_value(kvError); if (errorCode == 3) return host_value_undefined(); if (errorCode != 1) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 128, effectIndex); return 0 } const text = __pulse_fastly_read_body(__pulse_fastly_out_value(bodyOut), effectIndex); if (__pulse_fastly_last_error != 0) return 0; const parser = new __PulseJsonParser(text); const parsed = parser.parse(); if (parser.failed || parsed <= 0) return __pulse_fastly_string_value(text); const object = __pulse_fastly_value(parsed); if (object.kind == PULSE_VALUE_OBJECT) { const marker = __pulse_fastly_find(object, "__pulseKv"); const value = __pulse_fastly_find(object, "value"); if (marker >= 0 && value >= 0 && __pulse_fastly_number(unchecked(object.values[marker])) == 1.0) return unchecked(object.values[value]) } return parsed }
|
|
1518
|
-
|
|
1519
|
-
function __pulse_fastly_asset_text(text: string): string { const parser = new __PulseJsonParser(text); const parsed = parser.parse(); if (parser.failed || parsed <= 0) return text; const object = __pulse_fastly_value(parsed); if (object.kind != PULSE_VALUE_OBJECT) return text; const marker = __pulse_fastly_find(object, "__pulseKv"); const value = __pulse_fastly_find(object, "value"); if (marker < 0 || value < 0 || __pulse_fastly_number(unchecked(object.values[marker])) != 1.0) return text; return __pulse_fastly_string(unchecked(object.values[value])) }
|
|
1520
|
-
function __pulse_fastly_wait_assets(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 148, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const bodyOut = __pulse_fastly_out_i32(); const metadata = new Uint8Array(1); const written = __pulse_fastly_out_i32(); const generation = new StaticArray<i64>(1); const kvError = __pulse_fastly_out_i32(); const status = fastly_kv_store_lookup_wait_v2(pending, changetype<usize>(bodyOut), metadata.dataStart, 0, changetype<usize>(written), changetype<usize>(generation), changetype<usize>(kvError)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 149, effectIndex); return 0 } const outer = __pulse_fastly_value(unchecked(__pulse_fastly_payloads[effectIndex])); const payload = __pulse_fastly_assets_payload(outer); if (__pulse_fastly_last_error != 0) return 0; const key = __pulse_fastly_string(__pulse_fastly_payload_field(payload, "key")); const output = new __PulseFastlyValue(); output.kind = PULSE_VALUE_RESPONSE; const errorCode = __pulse_fastly_out_value(kvError); output.status = errorCode == 3 ? 404 : 200; if (errorCode != 1 && errorCode != 3) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 150, effectIndex); return 0 } const headersHandle = __pulse_fastly_payload_field(payload, "headers"); const headers = __pulse_fastly_value(headersHandle); let hasContentType = false; if (headers.kind == PULSE_VALUE_OBJECT) { for (let i = 0; i < headers.keys.length; i += 1) { const name = unchecked(headers.keys[i]); if (name.toLowerCase() == "content-type") hasContentType = true; output.headers.push(new __PulseFastlyHeader(name, __pulse_fastly_string(unchecked(headers.values[i])))) } } if (!hasContentType) output.headers.push(new __PulseFastlyHeader("content-type", output.status == 200 ? __pulse_fastly_asset_content_type(key) : "text/plain; charset=utf-8")); const cacheHandle = __pulse_fastly_payload_field(payload, "cacheControl"); if (__pulse_fastly_value(cacheHandle).kind != PULSE_VALUE_UNDEFINED) output.headers.push(new __PulseFastlyHeader("cache-control", __pulse_fastly_string(cacheHandle))); const methodHandle = __pulse_fastly_payload_field(payload, "method"); const method = __pulse_fastly_value(methodHandle).kind == PULSE_VALUE_UNDEFINED ? "GET" : __pulse_fastly_string(methodHandle).toUpperCase(); if (output.status == 200 && method != "HEAD") { const text = __pulse_fastly_read_body(__pulse_fastly_out_value(bodyOut), effectIndex); if (__pulse_fastly_last_error != 0) return 0; output.text = __pulse_fastly_asset_text(text) } return __pulse_fastly_put(output) }
|
|
1984
|
+
${assetResultSource}
|
|
1521
1985
|
function __pulse_fastly_wait_kv_put(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 126, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const kvError = __pulse_fastly_out_i32(); const status = fastly_kv_store_insert_wait(pending, changetype<usize>(kvError)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK || __pulse_fastly_out_value(kvError) != 1) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 127, effectIndex); return 0 } return host_value_boolean(1) }
|
|
1522
1986
|
function __pulse_fastly_write_body(bodyHandle: i32, value: string): i32 { const bytes = __pulse_fastly_utf8(value); let offset = 0; while (offset < bytes.byteLength) { const written = __pulse_fastly_out_i32(); const status = fastly_http_body_write(bodyHandle, changetype<usize>(bytes) + offset, bytes.byteLength - offset, 0, changetype<usize>(written)); if (status != FASTLY_STATUS_OK) return status; const count = __pulse_fastly_out_value(written); if (count <= 0) return 1; offset += count } return FASTLY_STATUS_OK }
|
|
1523
|
-
function __pulse_fastly_send_result(handle: i32): i32 { const value = __pulse_fastly_value(handle); if (value.kind == PULSE_VALUE_FETCH) return fastly_http_resp_send_downstream(value.responseHandle, value.bodyHandle, 0); if (value.kind != PULSE_VALUE_RESPONSE) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 90, -1); return PULSE_ERROR_VALUE } const responseOut = __pulse_fastly_out_i32(); let status = fastly_http_resp_new(changetype<usize>(responseOut)); if (status != FASTLY_STATUS_OK) return status; const responseHandle = __pulse_fastly_out_value(responseOut); const bodyOut = __pulse_fastly_out_i32(); status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) return status; const bodyHandle = __pulse_fastly_out_value(bodyOut); status = fastly_http_resp_status_set(responseHandle, value.status); if (status != FASTLY_STATUS_OK) return status; for (let i = 0; i < value.headers.length; i += 1) { const header = unchecked(value.headers[i]); const nameBytes = __pulse_fastly_utf8(header.name); const valueBytes = __pulse_fastly_utf8(header.value); status = fastly_http_resp_header_append(responseHandle, changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(valueBytes), valueBytes.byteLength); if (status != FASTLY_STATUS_OK) return status } status = __pulse_fastly_write_body(bodyHandle, value.text); if (status != FASTLY_STATUS_OK) return status; return fastly_http_resp_send_downstream(responseHandle, bodyHandle, 0) }
|
|
1987
|
+
${lifecycle?.active ? `function __pulse_fastly_send_result(handle: i32): i32 {\n${lifecycle.site('response-send')}\n return __pulse_fastly_send_result_impl(handle)\n}\n` : ''}function ${lifecycle?.legacyName('__pulse_fastly_send_result') || '__pulse_fastly_send_result'}(handle: i32): i32 { const value = __pulse_fastly_value(handle); ${bindings.s3.length ? 'if (value.s3Body) return __s3_send_body(value);' : ''} if (value.kind == PULSE_VALUE_FETCH) return ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? '__fca_transfer(value.responseHandle, value.bodyHandle)' : 'fastly_http_resp_send_downstream(value.responseHandle, value.bodyHandle, 0)'}; if (value.kind != PULSE_VALUE_RESPONSE) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 90, -1); return PULSE_ERROR_VALUE } const responseOut = __pulse_fastly_out_i32(); let status = fastly_http_resp_new(changetype<usize>(responseOut)); if (status != FASTLY_STATUS_OK) return status; const responseHandle = __pulse_fastly_out_value(responseOut); const bodyOut = __pulse_fastly_out_i32(); status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) return status; const bodyHandle = __pulse_fastly_out_value(bodyOut); status = fastly_http_resp_status_set(responseHandle, value.status); if (status != FASTLY_STATUS_OK) return status; for (let i = 0; i < value.headers.length; i += 1) { const header = unchecked(value.headers[i]); const nameBytes = __pulse_fastly_utf8(header.name); const valueBytes = __pulse_fastly_utf8(header.value); status = fastly_http_resp_header_append(responseHandle, changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(valueBytes), valueBytes.byteLength); if (status != FASTLY_STATUS_OK) return status } status = __pulse_fastly_write_body(bodyHandle, value.text); if (status != FASTLY_STATUS_OK) return status; return fastly_http_resp_send_downstream(responseHandle, bodyHandle, 0) }
|
|
1524
1988
|
|
|
1525
1989
|
${jwtEvidenceSource}
|
|
1990
|
+
${terminalErrorSource}
|
|
1526
1991
|
${es256KeyArtifactSource(es256KeyRecords)}
|
|
1527
1992
|
${jwtCryptoCompositionSource(jwtCrypto)}
|
|
1993
|
+
${jwtSigningCryptoCompositionSource(jwtCrypto)}
|
|
1528
1994
|
${generateSchemaRuntime(plan)}
|
|
1529
1995
|
${effectResultSource(plan, bindings)}
|
|
1530
1996
|
`;
|
|
1997
|
+
if (!hasAssets) return source;
|
|
1998
|
+
const writer = 'status = __pulse_fastly_write_body(bodyHandle, value.text);';
|
|
1999
|
+
if (!source.includes(writer)) throw new Error('Embedded asset response writer boundary drift.');
|
|
2000
|
+
return source.replace(writer, 'status = value.binaryBody !== null ? __pulse_fastly_write_binary(bodyHandle, value.binaryBody!) : __pulse_fastly_write_body(bodyHandle, value.text);');
|
|
1531
2001
|
}
|
|
1532
2002
|
|
|
1533
2003
|
function driverSource(plan, options = {}) {
|
|
2004
|
+
const lifecycle = options.lifecycle;
|
|
1534
2005
|
const entryName = options.guestLinked === true
|
|
1535
2006
|
? 'pulse_fastly_handle'
|
|
1536
2007
|
: '_start';
|
|
@@ -1544,33 +2015,41 @@ export function pulse_fastly_jwt_crypto_calls(): i32 { return __pulse_fastly_jwt
|
|
|
1544
2015
|
export function pulse_fastly_jwt_clock_calls(): i32 { return __pulse_fastly_jwt_clock_calls }
|
|
1545
2016
|
export function pulse_fastly_jwt_schema_calls(): i32 { return __pulse_fastly_jwt_schema_calls }
|
|
1546
2017
|
export function ${entryName}(): void {
|
|
2018
|
+
if (__pulse_invocation_started) { __pulse_fastly_fail(PULSE_ERROR_STATE, 171, -1); return }
|
|
2019
|
+
__pulse_invocation_started = true
|
|
2020
|
+
__pulse_fastly_run_invocation()
|
|
2021
|
+
${lifecycle?.site('invocation-end') ? lifecycle.site('invocation-end') + '\n' : ''} ${plan.effects.some(e => e.kind === 's3.getBody') ? '__s3_close_bodies()' : ''}
|
|
2022
|
+
__pulse_invocation_close()
|
|
2023
|
+
}
|
|
2024
|
+
function __pulse_fastly_run_invocation(): void {
|
|
1547
2025
|
__pulse_fastly_last_error = fastly_abi_init(PULSE_FASTLY_EFFECTS_ABI_VERSION)
|
|
1548
2026
|
if (__pulse_fastly_last_error != FASTLY_STATUS_OK) { __pulse_fastly_jwt_send_error(); return }
|
|
1549
2027
|
const handles = new StaticArray<i32>(2)
|
|
1550
|
-
const downstreamStatus = fastly_http_req_body_downstream_get(changetype<usize>(handles), changetype<usize>(handles) + 4)
|
|
2028
|
+
const downstreamStatus = ${options.resourceAccounting ? '__fra_incoming' : 'fastly_http_req_body_downstream_get'}(changetype<usize>(handles), changetype<usize>(handles) + 4)
|
|
1551
2029
|
if (downstreamStatus != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 100, -1); __pulse_fastly_jwt_send_error(); return }
|
|
1552
2030
|
__pulse_fastly_request_handle = load<i32>(changetype<usize>(handles))
|
|
1553
2031
|
__pulse_fastly_request_body_handle = load<i32>(changetype<usize>(handles) + 4)
|
|
1554
|
-
__pulse_fastly_request_method = __pulse_fastly_read_req_string(0)
|
|
2032
|
+
${lifecycle?.site('request-acquired') ? lifecycle.site('request-acquired') + '\n' : ''} __pulse_fastly_request_method = __pulse_fastly_read_req_string(0)
|
|
1555
2033
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1556
2034
|
__pulse_fastly_request_url = __pulse_fastly_read_req_string(1)
|
|
1557
2035
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1558
2036
|
__pulse_fastly_request_path = __pulse_fastly_path(__pulse_fastly_request_url)
|
|
1559
2037
|
let runStatus = pulse_start()
|
|
1560
|
-
while (runStatus == 1 && __pulse_fastly_last_error == 0) {
|
|
2038
|
+
${packageApplication.enabled(plan) ? packageApplication.driverLoop() : applicationErrors.settlementEnabled(plan) ? applicationErrors.driverLoop(plan) : ` while (runStatus == 1 && __pulse_fastly_last_error == 0) {
|
|
1561
2039
|
for (let effectIndex = 0; effectIndex < PULSE_FASTLY_EFFECT_COUNT; effectIndex += 1) {
|
|
1562
2040
|
if (unchecked(__pulse_fastly_pending_mode[effectIndex]) == PULSE_FASTLY_PENDING_NONE) continue
|
|
2041
|
+
const invocation = unchecked(__pulse_invocation_tickets[effectIndex])
|
|
1563
2042
|
const result = __pulse_fastly_resolve_effect(effectIndex)
|
|
1564
2043
|
if (result <= 0 || __pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1565
|
-
if (
|
|
2044
|
+
if (__pulse_invocation_settle(effectIndex, invocation, result) != 1) { __pulse_fastly_fail(PULSE_ERROR_STATE, 101, effectIndex); __pulse_fastly_jwt_send_error(); return }
|
|
1566
2045
|
}
|
|
1567
2046
|
runStatus = pulse_resume()
|
|
1568
|
-
}
|
|
2047
|
+
}`}
|
|
1569
2048
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1570
2049
|
if (runStatus != 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 102, -1); __pulse_fastly_jwt_send_error(); return }
|
|
1571
2050
|
const result = pulse_result_handle()
|
|
1572
2051
|
if (result <= 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 103, -1); __pulse_fastly_jwt_send_error(); return }
|
|
1573
|
-
__pulse_fastly_jwt_attach_evidence(result)
|
|
2052
|
+
${lifecycle?.site('result-claim') ? lifecycle.site('result-claim') + '\n' : ''} __pulse_fastly_jwt_attach_evidence(result)
|
|
1574
2053
|
const sendStatus = __pulse_fastly_send_result(result)
|
|
1575
2054
|
if (sendStatus != FASTLY_STATUS_OK) __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 104, -1)
|
|
1576
2055
|
}
|
|
@@ -1578,9 +2057,10 @@ export function ${entryName}(): void {
|
|
|
1578
2057
|
}
|
|
1579
2058
|
|
|
1580
2059
|
function capabilityImports(effect) {
|
|
2060
|
+
if (effect.kind === 'time.now') return ['wasi_snapshot_preview1.clock_time_get'];
|
|
1581
2061
|
if (effect.kind === 'config.get') return ['fastly_config_store.open', 'fastly_config_store.get'];
|
|
1582
2062
|
if (effect.kind === 'secret.get') return ['fastly_secret_store.open', 'fastly_secret_store.get', 'fastly_secret_store.plaintext'];
|
|
1583
|
-
if (
|
|
2063
|
+
if (['jwt.verify', 'jwt.sign'].includes(effect.kind)) {
|
|
1584
2064
|
return [
|
|
1585
2065
|
...(effect.resource && effect.resource.keyType === 'secret'
|
|
1586
2066
|
? [
|
|
@@ -1592,6 +2072,7 @@ function capabilityImports(effect) {
|
|
|
1592
2072
|
'wasi_snapshot_preview1.clock_time_get'
|
|
1593
2073
|
];
|
|
1594
2074
|
}
|
|
2075
|
+
if (conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) return conditionalKv.kvImports(effect.kind).map(key => key.replace(':', '.'));
|
|
1595
2076
|
if (effect.kind === 'kv.get') return ['fastly_kv_store.open', 'fastly_kv_store.lookup', 'fastly_kv_store.lookup_wait_v2', 'fastly_http_body.read'];
|
|
1596
2077
|
if (effect.kind === 'kv.put') return ['fastly_kv_store.open', 'fastly_kv_store.insert', 'fastly_kv_store.insert_wait', 'fastly_http_body.new', 'fastly_http_body.write'];
|
|
1597
2078
|
if (effect.kind === 'assets.lookup') return ['fastly_kv_store.open', 'fastly_kv_store.lookup', 'fastly_kv_store.lookup_wait_v2', 'fastly_http_body.read'];
|
|
@@ -1599,7 +2080,7 @@ function capabilityImports(effect) {
|
|
|
1599
2080
|
return [
|
|
1600
2081
|
'fastly_http_req.new',
|
|
1601
2082
|
'fastly_http_req.send_async',
|
|
1602
|
-
'fastly_http_req.
|
|
2083
|
+
'fastly_http_req.pending_req_wait_v2',
|
|
1603
2084
|
...(effect.kind === 'grip.broadcast' ? ['fastly_secret_store.open', 'fastly_secret_store.get', 'fastly_secret_store.plaintext'] : [])
|
|
1604
2085
|
];
|
|
1605
2086
|
}
|
|
@@ -1611,7 +2092,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1611
2092
|
validatePlanBoundary(plan, options);
|
|
1612
2093
|
const facts = packageFacts(plan, options);
|
|
1613
2094
|
const jwtCrypto = selectedJwtCrypto(plan);
|
|
1614
|
-
const es256KeyRecords = jwtCrypto && jwtCrypto.algorithm
|
|
2095
|
+
const es256KeyRecords = jwtCrypto && ['ES256', 'RS256'].includes(jwtCrypto.algorithm)
|
|
1615
2096
|
? resolveEs256KeyArtifacts(plan, facts)
|
|
1616
2097
|
: Object.freeze([]);
|
|
1617
2098
|
if (jwtCrypto && jwtCrypto.guestUnitRequired && facts.guestUnits.length !== 1) {
|
|
@@ -1634,25 +2115,89 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1634
2115
|
}
|
|
1635
2116
|
);
|
|
1636
2117
|
}
|
|
2118
|
+
const packageDuration = plan.packages?.application?.requestLimits?.maxDurationMs;
|
|
2119
|
+
if (packageDuration !== undefined) options = { ...options, maxDurationMs: Math.min(options.maxDurationMs ?? packageDuration, packageDuration) };
|
|
1637
2120
|
const bindings = resolveCapabilityBindings(plan, options);
|
|
1638
|
-
const
|
|
2121
|
+
const selectedExtensions = hostExtensions.select(plan, options);
|
|
2122
|
+
const extensionPolicy = selectedExtensions.length ? hostExtensions.policy(selectedExtensions,
|
|
2123
|
+
(options.targetDescriptor?.finalWasmPolicy?.permittedImports || FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS.map(([module, name]) => ({ module, name, kind: 'function' })))) : undefined;
|
|
2124
|
+
const forwardingAccounting = require('./native-forwarding-accounting.js').profile(plan, bindings);
|
|
2125
|
+
const fetchAccounting = require('./native-fetch-accounting.js').profile(plan, bindings, FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES)
|
|
2126
|
+
|| require('./native-fetch-cohorts.js').profile(plan, bindings, FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES);
|
|
2127
|
+
const resourceAccounting = forwardingAccounting || fetchAccounting;
|
|
2128
|
+
// Trusted provider-internal services, never a project/package hook API.
|
|
2129
|
+
const lifecycle = lifecycleBindings.create(plan, bindings, [...require('./native-fetch-accounting.js').hooks(fetchAccounting), ...hostExtensions.hooks(selectedExtensions), ...(options.lifecycleServices || [])]);
|
|
2130
|
+
// Keep final provider eligibility authoritative over the portable artifact
|
|
2131
|
+
// and caller options. Only the selected crypto guest uses the linked policy;
|
|
2132
|
+
// that policy excludes active codecs regardless of JSON arena configuration.
|
|
2133
|
+
const numericRegions = facts.guestUnits.length === 0 ? 'fastly-numeric-regions-v2'
|
|
2134
|
+
: jwtCrypto && jwtCrypto.guestUnitRequired ? 'fastly-linked-numeric-regions-v1' : undefined;
|
|
2135
|
+
const portable = packageApplication.enabled(plan) ? packageApplication.portable(plan) : generateCanonicalNativeAssemblyScript(plan, {
|
|
2136
|
+
...options, numericRegions, lifecycle,
|
|
2137
|
+
handleOwnership: facts.guestUnits.length === 0 ? 'fastly-handle-tracing-v1' : undefined
|
|
2138
|
+
});
|
|
2139
|
+
nativeHandleOwnership.validateRoots(plan, portable.manifest);
|
|
1639
2140
|
const portableSource = stripPulseHostImports(portable.source);
|
|
1640
2141
|
const hasJwt = (plan.effects || []).some((effect) => effect.kind === 'jwt.verify');
|
|
1641
|
-
const
|
|
1642
|
-
const
|
|
2142
|
+
const hasSign = plan.effects.some(effect => effect.kind === 'jwt.sign');
|
|
2143
|
+
const jwtSource = hasJwt || hasSign ? pulseJwtAssemblyScriptSource({ includeSigning: hasSign }) : undefined;
|
|
2144
|
+
let source = [
|
|
1643
2145
|
'/* Generated by Pulse Pass98 Fastly native platform capability compiler. */',
|
|
1644
2146
|
'export function __pulse_fastly_abort(message: string | null, fileName: string | null = null, line: u32 = 0, column: u32 = 0): void { unreachable() }',
|
|
1645
2147
|
'',
|
|
1646
2148
|
fastlyRuntimeSource(plan, bindings, {
|
|
1647
|
-
...options,
|
|
2149
|
+
...options, lifecycle, fetchAccounting,
|
|
2150
|
+
handleTracing: portable.manifest.handleOwnership?.enabled === true,
|
|
1648
2151
|
jwtCrypto,
|
|
1649
2152
|
es256KeyRecords
|
|
1650
2153
|
}),
|
|
1651
2154
|
jwtSource ? jwtSource.source : '',
|
|
2155
|
+
...(selectedExtensions.length ? [hostExtensions.source(selectedExtensions)] : []),
|
|
2156
|
+
((plan.effects || []).some(effect => ['fetch', 'grip.publish', 'grip.broadcast'].includes(effect.kind)) || bindings.s3.length > 0)
|
|
2157
|
+
? fs.readFileSync(path.join(__dirname, 'native-send-error-detail.as.ts'), 'utf8') : '',
|
|
2158
|
+
require('./s3-native.js').s3NativeSource(plan, bindings),
|
|
2159
|
+
plan.effects.some(e => e.kind === 'assets.lookup') ? require('@pulse-compute/wasm-contracts/assets/contracts').embeddedNativeSource() + fs.readFileSync(path.join(__dirname, 'embedded-assets.as.ts'), 'utf8') : '',
|
|
2160
|
+
require('./time-native.js').timeNativeSource(plan),
|
|
2161
|
+
require('./digest-native.js').digestNativeSource(plan),
|
|
2162
|
+
conditionalKv.kvNativeSource(plan),
|
|
2163
|
+
require('./native-request-body.js').runtimeSource(),
|
|
2164
|
+
require('./native-request-headers.js').runtimeSource(),
|
|
2165
|
+
require('./native-header-lookup.js').runtimeSource(),
|
|
2166
|
+
fs.readFileSync(path.join(__dirname, 'native-transport-details.as.ts'), 'utf8'),
|
|
2167
|
+
require('./native-fetch-errors.js').runtimeSource(dynamicBackends.active(bindings)),
|
|
2168
|
+
require('./native-fetch-deadlines.js').runtimeSource(plan, fetchAccounting),
|
|
2169
|
+
applicationErrors.settlementEnabled(plan) ? applicationErrors.runtimeSource(plan) : '',
|
|
2170
|
+
effectInvocations.runtimeSource({ packageCompletion: packageApplication.enabled(plan), lifecycle }),
|
|
2171
|
+
...(portable.manifest.numericRegions?.accounting === 'fastly-read-loop-v1' && portable.manifest.numericRegions.regions.length > 0
|
|
2172
|
+
? [nativeValueBudget.numericRegionSource()] : []),
|
|
1652
2173
|
portableSource,
|
|
1653
|
-
|
|
2174
|
+
nativeHandleOwnership.providerSource(plan, portable.manifest.handleOwnership?.enabled === true),
|
|
2175
|
+
driverSource(plan, { guestLinked: facts.guestUnits.length > 0, lifecycle, resourceAccounting: forwardingAccounting }),
|
|
2176
|
+
...(forwardingAccounting ? [require('./native-forwarding-accounting.js').source(forwardingAccounting)] : []),
|
|
2177
|
+
...(fetchAccounting ? [require(fetchAccounting.selection === 'canonical-fetch-cohorts' ? './native-fetch-cohorts.js' : './native-fetch-accounting.js').source(fetchAccounting)] : []),
|
|
1654
2178
|
''
|
|
1655
2179
|
].join('\n');
|
|
2180
|
+
if (applicationErrors.settlementEnabled(plan)) source = applicationErrors.instrument(source);
|
|
2181
|
+
source = require('./native-request-headers.js').instrument(source, applicationErrors.enabled(plan), /\bhost_request_headers\(/.test(portableSource));
|
|
2182
|
+
source = require('./native-header-lookup.js').instrument(source, lifecycle);
|
|
2183
|
+
source = require('./native-fetch-errors.js').instrument(source, lifecycle);
|
|
2184
|
+
source = require('./native-fetch-deadlines.js').instrument(source, plan, fetchAccounting);
|
|
2185
|
+
source = require('./native-request-body.js').instrument(source, applicationErrors.enabled(plan), lifecycle);
|
|
2186
|
+
if (packageApplication.enabled(plan)) source = packageApplication.instrument(source);
|
|
2187
|
+
source = effectInvocations.instrument(source);
|
|
2188
|
+
source = require('./request-budget.js').instrumentRequestBudget(source, bindings.maxDurationMs, plan, lifecycle, fetchAccounting);
|
|
2189
|
+
source = packageApplication.terminalFailure(source, plan);
|
|
2190
|
+
source = nativeValueBudget.instrument(source, plan);
|
|
2191
|
+
source = require('./native-body-forwarding.js').instrument(source, plan, bindings);
|
|
2192
|
+
source = dynamicBackends.instrument(source, bindings, true, (options.bindings || options).backends);
|
|
2193
|
+
// Forwarding owns a specialized terminal fetch implementation. Admission
|
|
2194
|
+
// above rejects outcome/timing options; generic fetch anchors do not exist here.
|
|
2195
|
+
if (!require('./native-body-forwarding.js').enabled(plan)) {
|
|
2196
|
+
source = require('./native-fetch-outcomes.js').instrument(source, plan);
|
|
2197
|
+
source = require('./native-fetch-timing.js').instrument(source, plan);
|
|
2198
|
+
}
|
|
2199
|
+
lifecycle.assertSites();
|
|
2200
|
+
if (lifecycle.supportSource) source += '\n' + lifecycle.supportSource;
|
|
1656
2201
|
const effectKinds = Object.freeze((plan.effects || []).reduce((output, effect) => {
|
|
1657
2202
|
output[effect.kind] = (output[effect.kind] || 0) + 1;
|
|
1658
2203
|
return output;
|
|
@@ -1664,9 +2209,10 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1664
2209
|
capability: effect.capability,
|
|
1665
2210
|
package: effect.package,
|
|
1666
2211
|
contractId: effect.contractId,
|
|
1667
|
-
imports: Object.freeze(capabilityImports(effect))
|
|
2212
|
+
imports: Object.freeze([...capabilityImports(effect), ...(bindings.backends.some(b => b.index === index && b.dynamic) ? ['fastly_http_req.register_dynamic_backend'] : [])])
|
|
1668
2213
|
})));
|
|
1669
2214
|
const manifest = Object.freeze({
|
|
2215
|
+
...(resourceAccounting ? { resourceAccounting } : {}),
|
|
1670
2216
|
version: FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION,
|
|
1671
2217
|
generatorVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_GENERATOR_VERSION,
|
|
1672
2218
|
abiVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION,
|
|
@@ -1676,10 +2222,23 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1676
2222
|
portableGeneratorVersion: portable.version,
|
|
1677
2223
|
portableSourceHash: portable.sourceHash,
|
|
1678
2224
|
schemaCodecs: portable.manifest.schemaCodecs,
|
|
2225
|
+
...(portable.manifest.numericRegions ? { numericRegions: portable.manifest.numericRegions } : {}),
|
|
2226
|
+
handleOwnership: portable.manifest.handleOwnership ? Object.freeze({ ...portable.manifest.handleOwnership,
|
|
2227
|
+
providerRoots: portable.manifest.handleOwnership.enabled ? Object.freeze(nativeHandleOwnership.providerRoots(plan)) : Object.freeze([])
|
|
2228
|
+
}) : Object.freeze({
|
|
2229
|
+
policy: null, enabled: false,
|
|
2230
|
+
reason: packageApplication.enabled(plan) ? 'package-owned-emitter' : 'linked-runtime',
|
|
2231
|
+
roots: Object.freeze([]), checkpoints: 'none'
|
|
2232
|
+
}),
|
|
2233
|
+
handlerBodies: portable.manifest.handlerBodies,
|
|
2234
|
+
...(portable.manifest.reportOwnership ? { reportOwnership: portable.manifest.reportOwnership } : {}),
|
|
2235
|
+
dispatcher: portable.manifest.dispatcher,
|
|
2236
|
+
stages: portable.manifest.stages,
|
|
2237
|
+
helperBodies: portable.manifest.helperBodies,
|
|
1679
2238
|
crypto: portable.manifest.crypto,
|
|
1680
2239
|
jwt: jwtSource
|
|
1681
2240
|
? Object.freeze({
|
|
1682
|
-
status: jwtCrypto && jwtCrypto.algorithm
|
|
2241
|
+
status: jwtCrypto && ['ES256', 'RS256'].includes(jwtCrypto.algorithm)
|
|
1683
2242
|
? 'implemented-g4'
|
|
1684
2243
|
: 'implemented-e2',
|
|
1685
2244
|
package: jwtSource.owner,
|
|
@@ -1698,13 +2257,19 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1698
2257
|
})
|
|
1699
2258
|
: Object.freeze({ status: 'inactive' }),
|
|
1700
2259
|
effectCount: (plan.effects || []).length,
|
|
2260
|
+
effectInvocations: effectInvocations.policy,
|
|
2261
|
+
...(lifecycle.active ? { lifecycle: lifecycle.plan } : {}),
|
|
2262
|
+
...(selectedExtensions.length ? { hostExtensions: hostExtensions.evidence(selectedExtensions, lifecycle, extensionPolicy) } : {}),
|
|
1701
2263
|
effectKinds,
|
|
1702
2264
|
continuationCount: (plan.continuations || []).length,
|
|
1703
2265
|
groupedContinuationCount: (plan.continuations || []).filter((entry) => (entry.effectIds || []).length > 1).length,
|
|
1704
2266
|
bindings,
|
|
1705
2267
|
backends: bindings.backends,
|
|
1706
2268
|
capabilityDiagnostics,
|
|
1707
|
-
|
|
2269
|
+
...(dynamicBackends.active(bindings) ? { dynamicBackends: dynamicBackends.describe((options.bindings || options).backends) } : {}),
|
|
2270
|
+
allowedImports: dynamicBackends.active(bindings)
|
|
2271
|
+
? Object.freeze([...FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS, Object.freeze(['fastly_http_req', 'register_dynamic_backend'])])
|
|
2272
|
+
: FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS,
|
|
1708
2273
|
requiredImports: requiredImportsForPlan(plan, bindings),
|
|
1709
2274
|
requiredExports: Object.freeze([
|
|
1710
2275
|
'_start',
|
|
@@ -1712,27 +2277,30 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1712
2277
|
'pulse_fastly_error_stage',
|
|
1713
2278
|
'pulse_fastly_error_effect',
|
|
1714
2279
|
'pulse_plan_hash_ptr',
|
|
1715
|
-
'pulse_plan_hash_length'
|
|
2280
|
+
'pulse_plan_hash_length',
|
|
2281
|
+
...(bindings.maxDurationMs === undefined ? [] : ['pulse_fastly_request_expired'])
|
|
1716
2282
|
]),
|
|
1717
2283
|
policy: Object.freeze({
|
|
2284
|
+
...(dynamicBackends.active(bindings) ? { dynamicBackends: dynamicBackends.describe((options.bindings || options).backends) } : {}),
|
|
2285
|
+
...(nativeValueBudget.hasBoundedReadLoop(plan) ? { readLoopMemory: nativeValueBudget.policy } : {}),
|
|
1718
2286
|
nativeFastly: true,
|
|
1719
2287
|
provider: 'fastly',
|
|
1720
2288
|
providerNeutralInput: true,
|
|
1721
2289
|
javascriptRuntime: false,
|
|
1722
2290
|
jsComputeRuntime: false,
|
|
1723
|
-
wasi:
|
|
1724
|
-
effects: '
|
|
2291
|
+
wasi: requiredImportsForPlan(plan, bindings).filter(key => key.startsWith('wasi_snapshot_preview1:')).map(key => key.split(':')[1]).join(', ').replace(/(.+)/, '$1 only') || false,
|
|
2292
|
+
effects: FASTLY_NATIVE_PLATFORM_EFFECT_KINDS.join(', '),
|
|
1725
2293
|
continuations: true,
|
|
1726
2294
|
groupedEffects: 'existing fetch start-before-wait ordering remains intact',
|
|
1727
2295
|
config: 'Fastly Config Store open/get with undefined for missing values',
|
|
1728
2296
|
secrets: 'Fastly Secret Store open/get/plaintext; proof traces redact plaintext values',
|
|
1729
|
-
jwt: hasJwt
|
|
1730
|
-
? `package-owned compact-JWS
|
|
2297
|
+
jwt: hasJwt || hasSign
|
|
2298
|
+
? `package-owned compact-JWS, registered-claim, and bounded issuance semantics composed with ${jwtCrypto.realization}; provider-owned request, secret, clock, schema, and transport`
|
|
1731
2299
|
: 'inactive',
|
|
1732
|
-
kv: 'Fastly KV Store async lookup/insert with Pulse
|
|
2300
|
+
kv: 'Fastly KV Store async lookup/insert/delete and one bounded list page with opaque cursor; conditional operations preserve u64 generations, bounded readiness, strict Pulse envelopes and uncertain dispatch',
|
|
1733
2301
|
assets: 'Fastly KV Store lookup with provider-owned content type, cache headers, and GET/HEAD body ownership',
|
|
1734
2302
|
grip: 'canonical stateless framing plus bound, secret-referenced broadcast HTTP realization and stable acknowledgement',
|
|
1735
|
-
logging: 'best-effort redacted
|
|
2303
|
+
logging: bindings.logEndpoint === undefined ? 'best-effort redacted WASI fd_write to stdout (descriptor 1), one attempt per statement' : 'best-effort named fastly_log endpoint; one lookup and at most one write per statement, 65536 UTF-8 byte maximum, no retries or stdout fallback; downstream delivery unverified',
|
|
1736
2304
|
schemas: 'fetch JSON decode and structured response encode',
|
|
1737
2305
|
opaqueResponse: 'origin fetch response/body handles and direct GRIP hold responses pass through without JavaScript runtime ownership',
|
|
1738
2306
|
transportErrors: 'host status failures remain distinct from HTTP response status',
|
|
@@ -1763,13 +2331,16 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1763
2331
|
});
|
|
1764
2332
|
}
|
|
1765
2333
|
|
|
1766
|
-
function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
2334
|
+
function inspectFastlyNativePlatformCapabilitiesWasm(input, options = {}) {
|
|
1767
2335
|
const bytes = Buffer.isBuffer(input) ? input : fs.readFileSync(path.resolve(input));
|
|
2336
|
+
assertFastlyWasmBudget(bytes.length, options.maxWasmBytes);
|
|
1768
2337
|
if (!WebAssembly.validate(bytes)) fail('Generated Fastly native platform capabilities module is not valid WebAssembly.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_WASM_INVALID');
|
|
1769
2338
|
const module = new WebAssembly.Module(bytes);
|
|
1770
2339
|
const imports = WebAssembly.Module.imports(module).map((entry) => Object.freeze({ module: entry.module, name: entry.name, kind: entry.kind }));
|
|
1771
2340
|
const exports = WebAssembly.Module.exports(module).map((entry) => Object.freeze({ name: entry.name, kind: entry.kind }));
|
|
1772
|
-
const
|
|
2341
|
+
const permitted = options.extensionPolicy ? new Set(options.extensionPolicy.permittedImports.map(entry => `${entry.module}:${entry.name}:${entry.kind}`)) : null;
|
|
2342
|
+
if (options.extensionPolicy) require('@pulse-compute/wasm-contracts/provider/toolchain').hostExtensions.auditWasm(bytes, options.extensionPolicy);
|
|
2343
|
+
const unexpected = imports.filter((entry) => permitted ? !permitted.has(`${entry.module}:${entry.name}:${entry.kind}`) : entry.kind !== 'function' || !FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS.has(`${entry.module}:${entry.name}`));
|
|
1773
2344
|
if (unexpected.length > 0) fail('Generated Fastly native platform capabilities module imports functions outside the Pass98 ABI.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORT_UNSUPPORTED', { unexpected, imports });
|
|
1774
2345
|
const forbidden = imports.filter((entry) => /pulse_host|js[_-]?compute/i.test(`${entry.module}:${entry.name}`));
|
|
1775
2346
|
if (forbidden.length > 0) fail('Generated Fastly native platform capabilities module imports a forbidden runtime.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_RUNTIME_FORBIDDEN', { forbidden });
|
|
@@ -1780,7 +2351,6 @@ function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
|
1780
2351
|
const requiredExports = ['_start', 'memory', 'pulse_fastly_last_error', 'pulse_fastly_error_stage', 'pulse_fastly_error_effect', 'pulse_start', 'pulse_resume', 'pulse_set_effect_result', 'pulse_result_handle'];
|
|
1781
2352
|
const missingExports = requiredExports.filter((name) => !exportNames.has(name));
|
|
1782
2353
|
if (missingExports.length > 0) fail('Generated Fastly native platform capabilities module is missing required exports.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_EXPORT_MISSING', { missingExports, exports });
|
|
1783
|
-
if (bytes.length > FASTLY_NATIVE_PLATFORM_CAPABILITIES_MAX_WASM_BYTES) fail('Generated Fastly native platform capabilities module exceeds the Pass98 size ceiling.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_WASM_TOO_LARGE', { bytes: bytes.length, maxBytes: FASTLY_NATIVE_PLATFORM_CAPABILITIES_MAX_WASM_BYTES });
|
|
1784
2354
|
return Object.freeze({
|
|
1785
2355
|
valid: true,
|
|
1786
2356
|
bytes: bytes.length,
|
|
@@ -1793,6 +2363,7 @@ function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
|
1793
2363
|
}
|
|
1794
2364
|
|
|
1795
2365
|
function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
2366
|
+
const maxWasmBytes = normalizeFastlyMaxWasmBytes(options.maxWasmBytes);
|
|
1796
2367
|
const generated = generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options);
|
|
1797
2368
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
1798
2369
|
const compilerFallback = path.resolve(__dirname, '..', '..', '..', '..', 'wasm', 'packages', 'compiler');
|
|
@@ -1825,6 +2396,8 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1825
2396
|
}
|
|
1826
2397
|
jsonAs = Object.freeze({ transform, dependencyRoot, version: packageJson.version });
|
|
1827
2398
|
}
|
|
2399
|
+
if (options.emitWat !== undefined && typeof options.emitWat !== 'boolean') throw new TypeError('emitWat must be a boolean.');
|
|
2400
|
+
const emitWat = options.emitWat === true;
|
|
1828
2401
|
const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-fastly-native-platform-'));
|
|
1829
2402
|
const requestedOutDir = options.outDir ? path.resolve(options.outDir) : undefined;
|
|
1830
2403
|
const outputDir = requestedOutDir || stagingDir;
|
|
@@ -1832,6 +2405,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1832
2405
|
const sourceFile = path.join(stagingDir, 'fastly-native-platform-capabilities.as.ts');
|
|
1833
2406
|
const wasmFile = path.join(outputDir, options.wasmFile || 'fastly-native-platform-capabilities.wasm');
|
|
1834
2407
|
const watFile = path.join(outputDir, options.watFile || 'fastly-native-platform-capabilities.wat');
|
|
2408
|
+
if (!emitWat) fs.rmSync(watFile, { force: true });
|
|
1835
2409
|
const guestLinked = generated.guestUnits.length > 0;
|
|
1836
2410
|
// asc resolves custom runtimes like module names and appends ".ts".
|
|
1837
2411
|
const fixedMemoryRuntimeModule = path.join(__dirname, 'fixed-memory-runtime.as');
|
|
@@ -1848,14 +2422,17 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1848
2422
|
asc.script,
|
|
1849
2423
|
path.basename(sourceFile),
|
|
1850
2424
|
'--outFile', assemblyScriptWasmFile,
|
|
1851
|
-
'--textFile', assemblyScriptWatFile,
|
|
1852
2425
|
'--runtime', guestLinked
|
|
1853
2426
|
? fixedMemoryRuntimeModule
|
|
1854
2427
|
: (schemaCodecsActive ? 'incremental' : 'stub'),
|
|
1855
2428
|
'--noAssert',
|
|
1856
2429
|
'--optimize'
|
|
1857
2430
|
];
|
|
1858
|
-
|
|
2431
|
+
if (emitWat) args.push('--textFile', assemblyScriptWatFile);
|
|
2432
|
+
const optimization = appendAssemblyScriptOptimizationArgs(args, nativeOptimization, { guestLinked });
|
|
2433
|
+
if (!guestLinked && (plan.callables?.length || nativeValueBudget.hasBoundedReadLoop(plan) || plan.effects.some(effect => effect.kind === 'crypto.digestText' || effect.kind.startsWith('s3.')))) {
|
|
2434
|
+
args.push('--maximumMemory', String(nativeValueBudget.policy.maximumMemoryPages));
|
|
2435
|
+
}
|
|
1859
2436
|
if (generated.guestUnits.length > 0) {
|
|
1860
2437
|
args.push(
|
|
1861
2438
|
'--disable', 'bulk-memory',
|
|
@@ -1878,17 +2455,20 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1878
2455
|
'--path', jsonAs.dependencyRoot
|
|
1879
2456
|
);
|
|
1880
2457
|
}
|
|
1881
|
-
|
|
2458
|
+
require('@pulse-compute/wasm-build-support/native-optimization').appendJsonCacheArenaArgs(args, options.elideJsonCacheArena, schemaCodecsActive);
|
|
2459
|
+
const readReportCapture = options.reportCapture === false ? () => null : prepareReportCapture(args, stagingDir, generated.manifest,
|
|
2460
|
+
require(path.join(asc.packageRoot, 'package.json')).version, 'fastly-native-platform-capabilities.as/');
|
|
2461
|
+
const startedAt = process.hrtime.bigint();
|
|
1882
2462
|
const result = spawnSync(asc.executable, args, {
|
|
1883
2463
|
cwd: stagingDir,
|
|
1884
2464
|
encoding: 'utf8',
|
|
1885
2465
|
maxBuffer: 64 * 1024 * 1024,
|
|
1886
2466
|
timeout: Number(options.timeoutMs || 180000),
|
|
1887
2467
|
env: schemaCodecsActive
|
|
1888
|
-
? { ...process.env, JSON_STRICT: 'true', JSON_USE_FAST_PATH: '0' }
|
|
2468
|
+
? { ...process.env, JSON_STRICT: 'true', JSON_USE_FAST_PATH: '0', JSON_MODE: 'NAIVE' }
|
|
1889
2469
|
: process.env
|
|
1890
2470
|
});
|
|
1891
|
-
const assemblyScriptDurationMs =
|
|
2471
|
+
const assemblyScriptDurationMs = Math.round(Number(process.hrtime.bigint() - startedAt) / 1e6);
|
|
1892
2472
|
if (result.error || result.status !== 0 || !fs.existsSync(assemblyScriptWasmFile)) {
|
|
1893
2473
|
fail('AssemblyScript failed to compile the Fastly native platform capabilities module.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_ASC_FAILED', {
|
|
1894
2474
|
status: result.status,
|
|
@@ -1932,7 +2512,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1932
2512
|
failureMessage: 'Fastly Native start wrapper composition failed.'
|
|
1933
2513
|
}
|
|
1934
2514
|
);
|
|
1935
|
-
runGuestTool(
|
|
2515
|
+
if (emitWat) runGuestTool(
|
|
1936
2516
|
'wasm-dis',
|
|
1937
2517
|
[wasmFile, '--mvp-features', '-o', watFile],
|
|
1938
2518
|
{
|
|
@@ -1962,7 +2542,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1962
2542
|
});
|
|
1963
2543
|
}
|
|
1964
2544
|
const primaryWasm = fs.readFileSync(wasmFile);
|
|
1965
|
-
const primaryWat =
|
|
2545
|
+
const primaryWat = emitWat ? fs.readFileSync(watFile, 'utf8') : '';
|
|
1966
2546
|
const primaryArtifact = Object.freeze({
|
|
1967
2547
|
bytes: primaryWasm.length,
|
|
1968
2548
|
sha256: sha256(primaryWasm)
|
|
@@ -1995,7 +2575,8 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1995
2575
|
realization = Object.freeze({
|
|
1996
2576
|
...realization,
|
|
1997
2577
|
wasm: stageResult.wasm,
|
|
1998
|
-
|
|
2578
|
+
// Required guest-link disassembly audits remain enabled.
|
|
2579
|
+
wat: emitWat ? stageResult.wat : '',
|
|
1999
2580
|
guestUnits: stageResult.guestUnits,
|
|
2000
2581
|
guestLink: Object.freeze({
|
|
2001
2582
|
version: stageResult.version,
|
|
@@ -2013,23 +2594,28 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2013
2594
|
const wat = realization.wat;
|
|
2014
2595
|
if (requestedOutDir && generated.guestUnits.length > 0) {
|
|
2015
2596
|
fs.writeFileSync(wasmFile, wasm);
|
|
2016
|
-
fs.writeFileSync(watFile, wat, 'utf8');
|
|
2597
|
+
if (emitWat) fs.writeFileSync(watFile, wat, 'utf8');
|
|
2017
2598
|
}
|
|
2018
|
-
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm);
|
|
2599
|
+
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm, { maxWasmBytes, extensionPolicy: generated.manifest.hostExtensions?.importPolicy });
|
|
2019
2600
|
const imported = new Set(inspection.imports.map((entry) => `${entry.module}:${entry.name}`));
|
|
2020
2601
|
const requiredImports = requiredImportsForPlan(plan, generated.bindings);
|
|
2021
2602
|
const missingRequiredImports = requiredImports.filter((key) => !imported.has(key));
|
|
2022
2603
|
if (missingRequiredImports.length > 0) fail('Generated Fastly native platform capabilities module is missing plan-required host imports.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORT_MISSING', { missingRequiredImports, imports: inspection.imports, requiredImports });
|
|
2023
2604
|
const manifest = Object.freeze({
|
|
2024
2605
|
...generated.manifest,
|
|
2606
|
+
...(generated.manifest.hostExtensions ? { hostExtensions: Object.freeze({ ...generated.manifest.hostExtensions,
|
|
2607
|
+
finalWasmSha256: inspection.sha256,
|
|
2608
|
+
observedImports: require('@pulse-compute/wasm-contracts/provider/toolchain').hostExtensions.auditWasm(wasm, generated.manifest.hostExtensions.importPolicy).observedImports }) } : {}),
|
|
2025
2609
|
compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
|
|
2026
2610
|
assemblyScript: Object.freeze({ package: 'assemblyscript', version: require(path.join(asc.packageRoot, 'package.json')).version }),
|
|
2027
2611
|
jsonAs: jsonAs
|
|
2028
|
-
? Object.freeze({ package: 'json-as', version: jsonAs.version, transform: true, strict: true })
|
|
2612
|
+
? Object.freeze({ package: 'json-as', version: jsonAs.version, transform: true, strict: true, mode: 'NAIVE', fastPath: false, elideDisabledCacheArena: options.elideJsonCacheArena === true })
|
|
2029
2613
|
: undefined,
|
|
2030
2614
|
optimization,
|
|
2031
2615
|
wasm: Object.freeze({ bytes: inspection.bytes, sha256: inspection.sha256, magic: inspection.magic }),
|
|
2032
|
-
wat: Object.freeze(
|
|
2616
|
+
wat: Object.freeze(emitWat
|
|
2617
|
+
? { emitted: true, bytes: Buffer.byteLength(wat), sha256: sha256(wat) }
|
|
2618
|
+
: { emitted: false, bytes: 0, sha256: null }),
|
|
2033
2619
|
importModules: inspection.importModules,
|
|
2034
2620
|
imports: inspection.imports,
|
|
2035
2621
|
requiredImports,
|
|
@@ -2043,10 +2629,17 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2043
2629
|
providerPackaging: realization.guestLink.providerPackaging
|
|
2044
2630
|
} : {})
|
|
2045
2631
|
});
|
|
2046
|
-
const durationMs =
|
|
2632
|
+
const durationMs = Math.round(Number(process.hrtime.bigint() - startedAt) / 1e6);
|
|
2047
2633
|
return Object.freeze({
|
|
2048
2634
|
version: FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION,
|
|
2049
2635
|
compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
|
|
2636
|
+
reportAttribution: readReportCapture(wasm),
|
|
2637
|
+
buildObservation: require('@pulse-compute/wasm-build-support/native-optimization').observeAssemblyScript(args, {
|
|
2638
|
+
version: require(path.join(asc.packageRoot, 'package.json')).version,
|
|
2639
|
+
assemblyScriptRoot: asc.packageRoot, timeoutMs: Number(options.timeoutMs || 180000),
|
|
2640
|
+
compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
|
|
2641
|
+
durationMs: assemblyScriptDurationMs, jsonAsVersion: jsonAs && jsonAs.version
|
|
2642
|
+
}),
|
|
2050
2643
|
plan,
|
|
2051
2644
|
generated,
|
|
2052
2645
|
source: generated.source,
|
|
@@ -2062,7 +2655,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2062
2655
|
assemblyScriptDurationMs,
|
|
2063
2656
|
primaryArtifact,
|
|
2064
2657
|
startWrapper,
|
|
2065
|
-
output: requestedOutDir ? Object.freeze({ outDir: outputDir, wasmFile, watFile }) : undefined
|
|
2658
|
+
output: requestedOutDir ? Object.freeze({ outDir: outputDir, wasmFile, watFile: emitWat ? watFile : null }) : undefined
|
|
2066
2659
|
});
|
|
2067
2660
|
} finally {
|
|
2068
2661
|
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
@@ -2080,10 +2673,12 @@ function writeFastlyNativePlatformCapabilitiesModule(compiled, outDir, options =
|
|
|
2080
2673
|
const manifestFile = path.join(target, options.manifestFile || 'fastly-native-platform-capabilities-manifest.json');
|
|
2081
2674
|
fs.writeFileSync(sourceFile, compiled.source, 'utf8');
|
|
2082
2675
|
fs.writeFileSync(wasmFile, compiled.wasm);
|
|
2083
|
-
|
|
2676
|
+
const emitWat = compiled.manifest.wat.emitted !== false;
|
|
2677
|
+
if (emitWat) fs.writeFileSync(watFile, compiled.wat, 'utf8');
|
|
2678
|
+
else fs.rmSync(watFile, { force: true });
|
|
2084
2679
|
fs.writeFileSync(planFile, `${stableStringify(compiled.plan, 2)}\n`, 'utf8');
|
|
2085
2680
|
fs.writeFileSync(manifestFile, `${stableStringify(compiled.manifest, 2)}\n`, 'utf8');
|
|
2086
|
-
return Object.freeze({ target, sourceFile, wasmFile, watFile, planFile, manifestFile, manifest: compiled.manifest });
|
|
2681
|
+
return Object.freeze({ target, sourceFile, wasmFile, watFile: emitWat ? watFile : null, planFile, manifestFile, manifest: compiled.manifest });
|
|
2087
2682
|
}
|
|
2088
2683
|
|
|
2089
2684
|
module.exports = Object.freeze({
|