@pulse-compute/provider-fastly 1.0.0-beta.1 → 1.0.0-beta.10
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 +35 -2
- package/src/build/digest-native.js +25 -0
- package/src/build/effect-invocations.js +49 -0
- package/src/build/embedded-assets.as.ts +32 -0
- package/src/build/entities-native.js +6 -11
- package/src/build/kv-native.as.ts +331 -0
- package/src/build/kv-native.js +27 -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 +341 -0
- package/src/build/native-body-forwarding.js +69 -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-deadlines.as.ts +164 -0
- package/src/build/native-fetch-deadlines.js +27 -0
- package/src/build/native-fetch-errors.js +38 -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 +155 -32
- package/src/build/native-platform-capabilities.js +755 -210
- package/src/build/native-request-body.js +44 -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.as.ts +50 -0
- package/src/build/request-budget.js +47 -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/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 +7 -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 +22 -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 +9 -1
- package/src/testing/native-platform-capabilities-host.js +349 -24
- package/src/toolchain/body-forwarding.js +25 -0
- package/src/toolchain/config.js +16 -0
- package/src/toolchain/index.js +48 -5
- package/src/toolchain/logging.js +18 -0
- package/src/toolchain/native-execution.js +78 -0
- package/src/toolchain/s3.js +26 -0
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const dynamicBackends = require('./native-dynamic-backends.js');
|
|
4
|
+
|
|
5
|
+
const { scalarRecordProjectionLines } = require('./schema-scalar-record.js');
|
|
6
|
+
const { hasJsonAdmission, tracksJsonDuplicates, schemaParserDepth, nestedJsonProjectionLines, schemaJsonAdmissionSource, schemaFetchJsonSource } = require('./schema-nested-json.js');
|
|
7
|
+
const { nativeLoggingSource } = require('./native-stdio.js');
|
|
8
|
+
const applicationErrors = require('./native-application-errors.js');
|
|
9
|
+
const effectInvocations = require('./effect-invocations.js');
|
|
10
|
+
const packageApplication = require('./package-native-application.js');
|
|
11
|
+
const nativeValueBudget = require('./native-value-budget.js');
|
|
12
|
+
const { schemaNeedsValueProjection } = require('@pulse-compute/wasm-contracts/schema-json/registry');
|
|
13
|
+
|
|
3
14
|
const crypto = require('node:crypto');
|
|
15
|
+
const { nativeStringAssembly, nativeStringFields, nativeStringConcat, nativeStringTrim, nativeStringIndex, needsNativeValueFailureGuard } = require('./native-string-values.js');
|
|
16
|
+
const conditionalKv = require('./kv-native.js');
|
|
4
17
|
const fs = require('node:fs');
|
|
5
18
|
const os = require('node:os');
|
|
6
19
|
const path = require('node:path');
|
|
@@ -12,6 +25,7 @@ const {
|
|
|
12
25
|
} = require('@pulse-compute/wasm-contracts/package/package-contract');
|
|
13
26
|
const {
|
|
14
27
|
EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION,
|
|
28
|
+
prepareReportCapture,
|
|
15
29
|
appendAssemblyScriptOptimizationArgs
|
|
16
30
|
} = require('@pulse-compute/wasm-build-support/native-optimization');
|
|
17
31
|
|
|
@@ -77,6 +91,7 @@ function loadGuestToolchain() {
|
|
|
77
91
|
|
|
78
92
|
const { resolveAsc } = loadBuildSupport();
|
|
79
93
|
const { generateCanonicalNativeAssemblyScript } = loadNativeGenerator();
|
|
94
|
+
const nativeHandleOwnership = require('./native-handle-ownership.js');
|
|
80
95
|
const { pulseJwtAssemblyScriptSource } = loadJwtNativeSource();
|
|
81
96
|
const {
|
|
82
97
|
GUEST_LINK_STAGE_INVOCATION_VERSION,
|
|
@@ -89,25 +104,34 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION = 'pulse.fastly-native-platfor
|
|
|
89
104
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_GENERATOR_VERSION = 'pulse.fastly-native-platform-capabilities-generator.v1';
|
|
90
105
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION = 'pulse.fastly-native-platform-capabilities-compiler.v1';
|
|
91
106
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION = 1;
|
|
92
|
-
|
|
107
|
+
// Internal compilation budget, not a Fastly service limit. Keep a finite guard
|
|
108
|
+
// while allowing composed applications beyond the original 1 MiB proof fixture.
|
|
109
|
+
const { DEFAULT_FASTLY_MAX_WASM_BYTES, normalizeFastlyMaxWasmBytes, assertFastlyWasmBudget } = require('./wasm-budget.js');
|
|
110
|
+
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_MAX_WASM_BYTES = DEFAULT_FASTLY_MAX_WASM_BYTES;
|
|
93
111
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES = 65536;
|
|
94
112
|
const FASTLY_NATIVE_SIZE_OPTIMIZATION = EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION;
|
|
95
113
|
|
|
96
114
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
|
|
115
|
+
...require('./native-body-forwarding.js').IMPORTS.map(key => Object.freeze(key.split(':'))),
|
|
116
|
+
...conditionalKv.KV_IMPORTS.map((key) => Object.freeze(key.split(':'))),
|
|
117
|
+
...require('./s3-native.js').S3_IMPORTS.map((key) => Object.freeze(key.split(':'))),
|
|
97
118
|
Object.freeze(['fastly_abi', 'init']),
|
|
98
119
|
Object.freeze(['fastly_http_req', 'body_downstream_get']),
|
|
99
120
|
Object.freeze(['fastly_http_req', 'method_get']),
|
|
100
121
|
Object.freeze(['fastly_http_req', 'uri_get']),
|
|
101
122
|
Object.freeze(['fastly_http_req', 'header_value_get']),
|
|
123
|
+
Object.freeze(['fastly_http_req', 'header_names_get']),
|
|
124
|
+
Object.freeze(['fastly_http_req', 'header_values_get']),
|
|
102
125
|
Object.freeze(['fastly_http_req', 'new']),
|
|
103
126
|
Object.freeze(['fastly_http_req', 'method_set']),
|
|
104
127
|
Object.freeze(['fastly_http_req', 'uri_set']),
|
|
105
128
|
Object.freeze(['fastly_http_req', 'header_insert']),
|
|
106
129
|
Object.freeze(['fastly_http_req', 'send_async']),
|
|
107
|
-
Object.freeze(['fastly_http_req', '
|
|
130
|
+
Object.freeze(['fastly_http_req', 'pending_req_wait_v2']),
|
|
108
131
|
Object.freeze(['fastly_http_resp', 'new']),
|
|
109
132
|
Object.freeze(['fastly_http_resp', 'header_append']),
|
|
110
133
|
Object.freeze(['fastly_http_resp', 'header_value_get']),
|
|
134
|
+
Object.freeze(['fastly_http_resp', 'header_values_get']),
|
|
111
135
|
Object.freeze(['fastly_http_resp', 'status_get']),
|
|
112
136
|
Object.freeze(['fastly_http_resp', 'status_set']),
|
|
113
137
|
Object.freeze(['fastly_http_resp', 'send_downstream']),
|
|
@@ -124,10 +148,15 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
|
|
|
124
148
|
Object.freeze(['fastly_kv_store', 'lookup_wait_v2']),
|
|
125
149
|
Object.freeze(['fastly_kv_store', 'insert']),
|
|
126
150
|
Object.freeze(['fastly_kv_store', 'insert_wait']),
|
|
151
|
+
Object.freeze(['fastly_kv_store', 'list']),
|
|
152
|
+
Object.freeze(['fastly_kv_store', 'list_wait']),
|
|
153
|
+
Object.freeze(['fastly_kv_store', 'delete']),
|
|
154
|
+
Object.freeze(['fastly_kv_store', 'delete_wait']),
|
|
127
155
|
Object.freeze(['fastly_log', 'endpoint_get']),
|
|
128
156
|
Object.freeze(['fastly_log', 'write']),
|
|
157
|
+
Object.freeze(['wasi_snapshot_preview1', 'fd_write']),
|
|
129
158
|
Object.freeze(['wasi_snapshot_preview1', 'clock_time_get'])
|
|
130
|
-
]);
|
|
159
|
+
].filter(([module, name], index, entries) => entries.findIndex((item) => item[0] === module && item[1] === name) === index));
|
|
131
160
|
|
|
132
161
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_REQUIRED_IMPORTS = Object.freeze([
|
|
133
162
|
Object.freeze(['fastly_abi', 'init']),
|
|
@@ -148,38 +177,67 @@ const FASTLY_NATIVE_PLATFORM_EFFECT_KIND = Object.freeze({
|
|
|
148
177
|
'grip.publish': 8,
|
|
149
178
|
'grip.broadcast': 9,
|
|
150
179
|
'assets.lookup': 10,
|
|
151
|
-
'jwt.verify': 11
|
|
180
|
+
'jwt.verify': 11,
|
|
181
|
+
'jwt.sign': 20,
|
|
182
|
+
's3.head': 12,
|
|
183
|
+
's3.getText': 13,
|
|
184
|
+
's3.putText': 14,
|
|
185
|
+
's3.getBody': 21,
|
|
186
|
+
'kv.delete': 22,
|
|
187
|
+
'kv.list': 23,
|
|
188
|
+
'kv.getVersioned': 15,
|
|
189
|
+
'kv.insertIfAbsent': 16,
|
|
190
|
+
'kv.compareAndSwap': 17,
|
|
191
|
+
'time.now': 18,
|
|
192
|
+
'crypto.digestText': 19
|
|
152
193
|
});
|
|
153
194
|
|
|
154
195
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS = new Set([
|
|
196
|
+
'fastly_http_req:register_dynamic_backend',
|
|
155
197
|
...FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS.map(([moduleName, name]) => `${moduleName}:${name}`)
|
|
156
198
|
]);
|
|
157
199
|
|
|
158
200
|
const FASTLY_NATIVE_PLATFORM_EFFECT_KINDS = Object.freeze([
|
|
201
|
+
'time.now',
|
|
202
|
+
'crypto.digestText',
|
|
159
203
|
'fetch',
|
|
160
204
|
'config.get',
|
|
161
205
|
'secret.get',
|
|
162
206
|
'kv.get',
|
|
163
207
|
'kv.put',
|
|
208
|
+
...conditionalKv.KV_CONDITIONAL_KINDS,
|
|
164
209
|
'grip.channel',
|
|
165
210
|
'grip.hold',
|
|
166
211
|
'grip.publish',
|
|
167
212
|
'grip.broadcast',
|
|
168
213
|
'assets.lookup',
|
|
169
|
-
'jwt.verify'
|
|
214
|
+
'jwt.verify',
|
|
215
|
+
'jwt.sign',
|
|
216
|
+
's3.head',
|
|
217
|
+
's3.getText',
|
|
218
|
+
's3.putText',
|
|
219
|
+
's3.getBody'
|
|
170
220
|
]);
|
|
171
221
|
|
|
172
222
|
const FASTLY_NATIVE_PLATFORM_CAPABILITY_KINDS = new Set([
|
|
223
|
+
'time.now',
|
|
224
|
+
'crypto.digestText',
|
|
173
225
|
'config.get',
|
|
174
226
|
'secret.get',
|
|
175
227
|
'kv.get',
|
|
176
228
|
'kv.put',
|
|
229
|
+
...conditionalKv.KV_CONDITIONAL_KINDS,
|
|
177
230
|
'assets.lookup',
|
|
178
231
|
'grip.channel',
|
|
179
232
|
'grip.hold',
|
|
180
233
|
'grip.publish',
|
|
181
234
|
'grip.broadcast',
|
|
182
|
-
'jwt.verify'
|
|
235
|
+
'jwt.verify',
|
|
236
|
+
'jwt.sign',
|
|
237
|
+
's3.head',
|
|
238
|
+
's3.getText',
|
|
239
|
+
's3.putText',
|
|
240
|
+
's3.getBody'
|
|
183
241
|
]);
|
|
184
242
|
|
|
185
243
|
class FastlyNativePlatformCapabilitiesError extends Error {
|
|
@@ -241,45 +299,38 @@ function packageFacts(_plan, options = {}) {
|
|
|
241
299
|
});
|
|
242
300
|
}
|
|
243
301
|
|
|
302
|
+
function jwtVerificationAlgorithm(effect) {
|
|
303
|
+
return effect.resource.keyType === 'secret' ? 'HS256' : String(effect.resource.keyArtifactId).startsWith('pulse.jwt.rs256-key.') ? 'RS256' : 'ES256';
|
|
304
|
+
}
|
|
305
|
+
|
|
244
306
|
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
|
-
);
|
|
307
|
+
const effects = plan.effects || [];
|
|
308
|
+
const signing = effects.filter(effect => effect.kind === 'jwt.sign');
|
|
309
|
+
const verifies = effects.filter(effect => effect.kind === 'jwt.verify');
|
|
310
|
+
const required = new Set([
|
|
311
|
+
...signing.map(effect => literalObjectField(inputExpression(effect, 'payload'), 'algorithm') === 'HS256' ? 'HMAC-SHA256' : literalObjectField(inputExpression(effect, 'payload'), 'algorithm')),
|
|
312
|
+
...verifies.map(effect => jwtVerificationAlgorithm(effect))
|
|
313
|
+
]);
|
|
314
|
+
if (!required.size) return undefined;
|
|
315
|
+
const definitions = {
|
|
316
|
+
'HMAC-SHA256': ['guest-source:pulse-hmac-as', 'pulse-hmac-as.v1', 'guest-source'],
|
|
317
|
+
HS256: ['guest-source:pulse-hmac-as', 'pulse-hmac-as.v1', 'guest-source'],
|
|
318
|
+
RS256: ['guest-linked:pulse-rs256-bearssl-i31', 'bearssl.0.6.rsa-i31.sha256.v1', 'guest-linked'],
|
|
319
|
+
ES256: ['guest-linked:pulse-es256-rustcrypto-p256', 'rustcrypto.p256-0.13.2.ecdsa-0.16.9.sha2-0.10.9.v1', 'guest-linked']
|
|
320
|
+
};
|
|
321
|
+
for (const algorithm of required) {
|
|
322
|
+
const selected = plan.crypto?.algorithms.find(entry => entry.algorithm === algorithm);
|
|
323
|
+
const expected = definitions[algorithm];
|
|
324
|
+
if (!expected || !selected || selected.realization !== expected[0] || selected.implementation !== expected[1]
|
|
325
|
+
|| selected.kind !== expected[2] || selected.targetImplemented !== true || selected.automaticFallback !== false) {
|
|
326
|
+
fail('JWT requires its explicitly selected Crypto realization.', 'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID');
|
|
327
|
+
}
|
|
274
328
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
guestUnitRequired: es256,
|
|
281
|
-
automaticFallback: false
|
|
282
|
-
});
|
|
329
|
+
const verificationAlgorithms = [...new Set(verifies.map(effect => jwtVerificationAlgorithm(effect)))];
|
|
330
|
+
if (verificationAlgorithms.length > 1) fail('Fastly Native JWT verification requires one exact algorithm.', 'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID');
|
|
331
|
+
const algorithm = verificationAlgorithms[0] || (required.has('RS256') ? 'RS256' : required.has('ES256') ? 'ES256' : 'HMAC-SHA256');
|
|
332
|
+
return Object.freeze({ ...plan.crypto.algorithms.find(entry => entry.algorithm === algorithm),
|
|
333
|
+
guestUnitRequired: required.has('ES256') || required.has('RS256'), signingAlgorithms: signing.map(effect => literalObjectField(inputExpression(effect, 'payload'), 'algorithm')) });
|
|
283
334
|
}
|
|
284
335
|
|
|
285
336
|
function decodeP256Coordinate(value, artifactId, field) {
|
|
@@ -301,6 +352,26 @@ function decodeP256Coordinate(value, artifactId, field) {
|
|
|
301
352
|
return bytes;
|
|
302
353
|
}
|
|
303
354
|
|
|
355
|
+
function normalizedRs256Jwk(value, artifactId) {
|
|
356
|
+
const invalid = () => fail('Invalid bounded RSA public key artifact.', 'PULSE_FASTLY_NATIVE_JWT_KEY_ARTIFACT_INVALID', { artifactId });
|
|
357
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
358
|
+
|| Object.keys(value).some(name => !['kty', 'n', 'e', 'alg', 'use', 'key_ops', 'kid'].includes(name))
|
|
359
|
+
|| value.kty !== 'RSA' || value.alg !== undefined && value.alg !== 'RS256'
|
|
360
|
+
|| value.use !== undefined && value.use !== 'sig'
|
|
361
|
+
|| value.key_ops !== undefined && (!Array.isArray(value.key_ops) || value.key_ops.length !== 1 || value.key_ops[0] !== 'verify')
|
|
362
|
+
|| value.kid !== undefined && (typeof value.kid !== 'string' || !value.kid.length || value.kid.includes('\0') || Buffer.byteLength(value.kid) > 256)) invalid();
|
|
363
|
+
const decode = (text, maximum) => {
|
|
364
|
+
if (typeof text !== 'string' || text.length > Math.ceil(maximum * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(text)) invalid();
|
|
365
|
+
const bytes = Buffer.from(text, 'base64url');
|
|
366
|
+
if (!bytes.length || bytes.length > maximum || bytes[0] === 0 || bytes.toString('base64url') !== text) invalid();
|
|
367
|
+
return bytes;
|
|
368
|
+
};
|
|
369
|
+
const n = decode(value.n, 512), e = decode(value.e, 4), k = n.length;
|
|
370
|
+
if (![256, 384, 512].includes(k) || n[0] < 128 || !(n[k-1] & 1) || e.readUIntBE(0, e.length) < 3 || !(e.at(-1) & 1)) invalid();
|
|
371
|
+
const bytes = Buffer.alloc(8+k); bytes.writeUInt32LE(k); n.copy(bytes, 4); e.copy(bytes, 8+k-e.length);
|
|
372
|
+
return Object.freeze({ kid: value.kid, bytes });
|
|
373
|
+
}
|
|
374
|
+
|
|
304
375
|
function normalizedEs256Jwk(value, artifactId) {
|
|
305
376
|
if (
|
|
306
377
|
!value
|
|
@@ -347,13 +418,14 @@ function resolveEs256KeyArtifacts(plan, facts) {
|
|
|
347
418
|
}
|
|
348
419
|
const artifactId = effect.resource.keyArtifactId;
|
|
349
420
|
const artifact = artifacts.get(artifactId);
|
|
421
|
+
const algorithm = jwtVerificationAlgorithm(effect).toLowerCase();
|
|
350
422
|
if (
|
|
351
423
|
!artifact
|
|
352
|
-
|| artifact.version !==
|
|
424
|
+
|| artifact.version !== `pulse.jwt-${algorithm}-key-artifact.v1`
|
|
353
425
|
|| artifact.contractId !== 'pulse.jwt'
|
|
354
426
|
|| artifact.package !== '@pulse-compute/jwt'
|
|
355
|
-
|| artifact.kind !==
|
|
356
|
-
|| artifact.mediaType !==
|
|
427
|
+
|| artifact.kind !== `jwt-${algorithm}-static-public-key`
|
|
428
|
+
|| artifact.mediaType !== `application/vnd.pulse.jwt-${algorithm}-key+json`
|
|
357
429
|
|| artifact.id !== artifactId
|
|
358
430
|
|| artifact.materialHash !== sha256(stableStringify(artifact.data))
|
|
359
431
|
|| !artifact.data
|
|
@@ -379,7 +451,7 @@ function resolveEs256KeyArtifacts(plan, facts) {
|
|
|
379
451
|
{ effectId: effect.id, artifactId, keyCount: keys && keys.length }
|
|
380
452
|
);
|
|
381
453
|
}
|
|
382
|
-
const normalized = keys.map((key) => normalizedEs256Jwk(key, artifactId));
|
|
454
|
+
const normalized = keys.map((key) => algorithm === 'rs256' ? normalizedRs256Jwk(key, artifactId) : normalizedEs256Jwk(key, artifactId));
|
|
383
455
|
const kids = normalized.map((key) => key.kid).filter((kid) => kid !== undefined);
|
|
384
456
|
if (new Set(kids).size !== kids.length) {
|
|
385
457
|
fail(
|
|
@@ -455,8 +527,71 @@ function es256KeyArtifactSource(records) {
|
|
|
455
527
|
return lines.join('\n');
|
|
456
528
|
}
|
|
457
529
|
|
|
530
|
+
function jwtSigningCryptoCompositionSource(selected) {
|
|
531
|
+
const algorithms = selected?.signingAlgorithms || [];
|
|
532
|
+
const hs = algorithms.includes('HS256');
|
|
533
|
+
const es = algorithms.includes('ES256');
|
|
534
|
+
const rs = algorithms.includes('RS256');
|
|
535
|
+
return `function __pulse_fastly_jwt_crypto_sign(algorithm: string, key: Uint8Array, data: Uint8Array, output: Uint8Array): i32 {
|
|
536
|
+
${hs ? `if (algorithm == "HS256") return pulse_crypto_hmac_sha256_bytes_v1(key.dataStart, key.length, data.dataStart, data.length, output.dataStart, output.length)` : ''}
|
|
537
|
+
${rs ? `if (algorithm == "RS256") return __pulse_fastly_rs256(true, key, data, output)` : ''}
|
|
538
|
+
${es ? `if (algorithm == "ES256") {
|
|
539
|
+
if (key.length != 96 || output.length != 64 || data.length > 16340) return -2
|
|
540
|
+
const pointer: usize = 524288
|
|
541
|
+
memory.fill(pointer, 0, 16640)
|
|
542
|
+
store<u32>(pointer, 0x32534550); store<u32>(pointer + 4, 2); store<u32>(pointer + 8, 64)
|
|
543
|
+
store<u32>(pointer + 12, (224 + data.length + 15) & ~15); store<u32>(pointer + 16, 1)
|
|
544
|
+
store<u32>(pointer + 24, 224); store<u32>(pointer + 28, data.length)
|
|
545
|
+
store<u32>(pointer + 32, 64); store<u32>(pointer + 36, 96)
|
|
546
|
+
store<u32>(pointer + 40, 160); store<u32>(pointer + 44, 64)
|
|
547
|
+
memory.copy(pointer + 64, key.dataStart, 96); memory.copy(pointer + 224, data.dataStart, data.length)
|
|
548
|
+
const status = __pulse_crypto_es256_sign_anchor(i32(pointer), 16640)
|
|
549
|
+
if (status == 1) memory.copy(output.dataStart, pointer + 160, 64)
|
|
550
|
+
memory.fill(pointer, 0, 16640); memory.fill(0, 0, 65536)
|
|
551
|
+
return status
|
|
552
|
+
}` : ''}
|
|
553
|
+
return -3
|
|
554
|
+
}`;
|
|
555
|
+
}
|
|
556
|
+
|
|
458
557
|
function jwtCryptoCompositionSource(selected) {
|
|
459
|
-
|
|
558
|
+
const rsa = selected && (selected.algorithm === 'RS256' || selected.signingAlgorithms?.includes('RS256'));
|
|
559
|
+
const helper = rsa ? rs256CompositionSource() : '';
|
|
560
|
+
return helper + jwtVerificationCompositionSource(selected);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function rs256CompositionSource() {
|
|
564
|
+
return `function __pulse_fastly_rs256(signing: bool, key: Uint8Array, data: Uint8Array, signature: Uint8Array): i32 {
|
|
565
|
+
if (key.length < 8 || data.length > 12288) return -2
|
|
566
|
+
const k = load<u32>(key.dataStart)
|
|
567
|
+
if ((k != 256 && k != 384 && k != 512) || signature.length != i32(k) || key.length != i32(signing ? 8 + k * 9 / 2 : 8 + k)) return -2
|
|
568
|
+
const pointer: usize = 524288
|
|
569
|
+
const output = (64 + key.length + 15) & ~15
|
|
570
|
+
const input = output + signature.length
|
|
571
|
+
memory.fill(pointer, 0, 16640)
|
|
572
|
+
store<u32>(pointer, 0x32534550); store<u32>(pointer + 4, 2); store<u32>(pointer + 8, 64)
|
|
573
|
+
store<u32>(pointer + 12, (input + data.length + 15) & ~15); store<u32>(pointer + 16, 2)
|
|
574
|
+
store<u32>(pointer + 24, input); store<u32>(pointer + 28, data.length)
|
|
575
|
+
store<u32>(pointer + 32, 64); store<u32>(pointer + 36, key.length)
|
|
576
|
+
store<u32>(pointer + 40, output); store<u32>(pointer + 44, k)
|
|
577
|
+
memory.copy(pointer + 64, key.dataStart, key.length); memory.copy(pointer + input, data.dataStart, data.length)
|
|
578
|
+
if (!signing) memory.copy(pointer + output, signature.dataStart, signature.length)
|
|
579
|
+
const status = signing ? __pulse_crypto_rs256_sign_anchor(i32(pointer), 16640) : __pulse_crypto_rs256_verify_anchor(i32(pointer), 16640)
|
|
580
|
+
if (signing && status == 1) memory.copy(signature.dataStart, pointer + output, signature.length)
|
|
581
|
+
memory.fill(pointer, 0, 16640); memory.fill(0, 0, 65536)
|
|
582
|
+
return status
|
|
583
|
+
}\n`;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function jwtVerificationCompositionSource(selected) {
|
|
587
|
+
if (selected?.algorithm === 'RS256') return `
|
|
588
|
+
function __pulse_fastly_jwt_capture_signing_input(input: Uint8Array): void { __pulse_fastly_jwt_record_signing_input(input.byteLength, "") }
|
|
589
|
+
function __pulse_fastly_jwt_crypto_verify(algorithm: string, key: Uint8Array, data: Uint8Array, signature: Uint8Array): i32 {
|
|
590
|
+
if (algorithm != "RS256") return -2
|
|
591
|
+
return __pulse_fastly_rs256(false, key, data, signature)
|
|
592
|
+
}`;
|
|
593
|
+
|
|
594
|
+
if (!selected || selected.algorithm === 'HMAC-SHA256') {
|
|
460
595
|
return [
|
|
461
596
|
'function __pulse_fastly_jwt_capture_signing_input(input: Uint8Array): void { void input }',
|
|
462
597
|
'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 +676,7 @@ function fastlyGuestLinkedStartWrapperWat() {
|
|
|
541
676
|
}
|
|
542
677
|
|
|
543
678
|
function validatePlanBoundary(plan, options = {}) {
|
|
679
|
+
require('./native-body-forwarding.js').validate(plan, options);
|
|
544
680
|
if (!plan || typeof plan !== 'object') throw new TypeError('Fastly native platform capability realization requires a canonical native plan.');
|
|
545
681
|
if (plan.version !== CANONICAL_NATIVE_PLAN_VERSION) {
|
|
546
682
|
fail(`Fastly native platform capability realization requires ${CANONICAL_NATIVE_PLAN_VERSION}.`, 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_PLAN_VERSION_UNSUPPORTED', { version: plan.version });
|
|
@@ -569,13 +705,16 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
569
705
|
&& effect.kind === 'assets.lookup';
|
|
570
706
|
const jwt = effect.package === '@pulse-compute/jwt'
|
|
571
707
|
&& effect.contractId === 'pulse.jwt'
|
|
572
|
-
&&
|
|
573
|
-
&& effect.operation ===
|
|
574
|
-
|
|
575
|
-
|
|
708
|
+
&& ['verify', 'sign'].includes(effect.operation)
|
|
709
|
+
&& effect.kind === `jwt.${effect.operation}` && effect.capability === effect.kind;
|
|
710
|
+
const s3 = effect.package === '@pulse-compute/s3' && effect.contractId === 'pulse.s3'
|
|
711
|
+
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
712
|
+
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
713
|
+
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
714
|
+
return !grip && !assets && !jwt && !s3 && !digest;
|
|
576
715
|
});
|
|
577
716
|
if (unsupportedPackages.length > 0) {
|
|
578
|
-
fail('Fastly native realization only accepts the trusted first-party Assets, GRIP, and
|
|
717
|
+
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
718
|
effects: unsupportedPackages.map((entry) => ({ id: entry.id, kind: entry.kind, package: entry.package, contractId: entry.contractId }))
|
|
580
719
|
});
|
|
581
720
|
}
|
|
@@ -589,15 +728,18 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
589
728
|
&& effect.kind === 'assets.lookup';
|
|
590
729
|
const jwt = effect.package === '@pulse-compute/jwt'
|
|
591
730
|
&& effect.contractId === 'pulse.jwt'
|
|
592
|
-
&&
|
|
593
|
-
&& effect.operation ===
|
|
594
|
-
|
|
595
|
-
|
|
731
|
+
&& ['verify', 'sign'].includes(effect.operation)
|
|
732
|
+
&& effect.kind === `jwt.${effect.operation}` && effect.capability === effect.kind;
|
|
733
|
+
const s3 = effect.package === '@pulse-compute/s3' && effect.contractId === 'pulse.s3'
|
|
734
|
+
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
735
|
+
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
736
|
+
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
737
|
+
return !grip && !assets && !jwt && !s3 && !digest;
|
|
596
738
|
});
|
|
597
739
|
if (unsupportedDeclarations.length > 0) {
|
|
598
|
-
fail('Fastly native package realization is restricted to the Assets, GRIP, and
|
|
740
|
+
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
741
|
}
|
|
600
|
-
for (const effect of effects.filter((entry) =>
|
|
742
|
+
for (const effect of effects.filter((entry) => ['jwt.verify', 'jwt.sign'].includes(entry.kind))) {
|
|
601
743
|
const resource = effect.resource;
|
|
602
744
|
const knownShape = resource
|
|
603
745
|
&& !Object.keys(resource)
|
|
@@ -612,7 +754,7 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
612
754
|
&& typeof resource.keyArtifactId === 'string'
|
|
613
755
|
&& resource.keyArtifactId.length > 0
|
|
614
756
|
&& resource.secretBinding === null;
|
|
615
|
-
if (!namedSecret && !staticPublicKey) {
|
|
757
|
+
if (!namedSecret && (!staticPublicKey || effect.kind === 'jwt.sign')) {
|
|
616
758
|
fail('Fastly Native JWT requires one exact named-secret or bounded static public-key descriptor.', 'PULSE_FASTLY_NATIVE_JWT_KEY_DESCRIPTOR_INVALID', {
|
|
617
759
|
effectId: effect.id,
|
|
618
760
|
resource,
|
|
@@ -678,8 +820,8 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
678
820
|
const effects = plan.effects || [];
|
|
679
821
|
const hasConfig = effects.some((effect) => effect.kind === 'config.get');
|
|
680
822
|
const hasSecret = effects.some((effect) => (
|
|
681
|
-
effect.kind === 'secret.get'
|
|
682
|
-
|| (
|
|
823
|
+
effect.kind === 'secret.get' || ['s3.head', 's3.getText', 's3.putText', 's3.getBody'].includes(effect.kind)
|
|
824
|
+
|| (['jwt.verify', 'jwt.sign'].includes(effect.kind) && effect.resource && effect.resource.keyType === 'secret')
|
|
683
825
|
));
|
|
684
826
|
const hasGripHold = effects.some((effect) => effect.kind === 'grip.hold');
|
|
685
827
|
const hasGripBroadcast = effects.some((effect) => effect.kind === 'grip.broadcast');
|
|
@@ -703,8 +845,9 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
703
845
|
|
|
704
846
|
const kv = [];
|
|
705
847
|
for (const [index, effect] of effects.entries()) {
|
|
706
|
-
if (effect.kind !== 'kv.get' && effect.kind !== 'kv.put' && effect.kind !== 'assets.lookup') continue;
|
|
848
|
+
if (effect.kind !== 'kv.get' && effect.kind !== 'kv.put' && !conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind) && effect.kind !== 'assets.lookup') continue;
|
|
707
849
|
const packagePayload = inputExpression(effect, 'payload');
|
|
850
|
+
if (effect.kind === 'assets.lookup' && literalObjectField(packagePayload, 'embeddedId')) continue;
|
|
708
851
|
const logical = effect.resource && effect.resource.kind === 'literal'
|
|
709
852
|
&& effect.kind !== 'assets.lookup'
|
|
710
853
|
? String(effect.resource.value)
|
|
@@ -715,6 +858,7 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
715
858
|
resource: effect.resource
|
|
716
859
|
});
|
|
717
860
|
}
|
|
861
|
+
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
862
|
const physical = stringBinding(
|
|
719
863
|
kvMap[logical],
|
|
720
864
|
`Fastly KV binding for ${logical}`,
|
|
@@ -770,11 +914,11 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
770
914
|
backend = byEffect[effect.id]
|
|
771
915
|
|| (origin && byOrigin[origin])
|
|
772
916
|
|| (effect.resource && effect.resource.value && byOrigin[effect.resource.value]);
|
|
773
|
-
if ((typeof backend !== 'string' || backend.trim() === '') && !origin) {
|
|
917
|
+
if ((typeof backend !== 'string' || backend.trim() === '') && !origin && source.dynamicBackends !== true) {
|
|
774
918
|
const configured = [...new Set(Object.values(byOrigin).filter((value) => typeof value === 'string' && value.trim()).map((value) => value.trim()))];
|
|
775
919
|
if (configured.length === 1) backend = configured[0];
|
|
776
920
|
}
|
|
777
|
-
if (typeof backend !== 'string' || backend.trim() === '') {
|
|
921
|
+
if ((typeof backend !== 'string' || backend.trim() === '') && !(source.dynamicBackends === true && !(plan.capabilities || []).includes('request.body.forward'))) {
|
|
778
922
|
fail(`Fastly native effect ${effect.id} has no static backend binding.`, 'PULSE_FASTLY_BACKEND_REQUIRED', {
|
|
779
923
|
effectId: effect.id,
|
|
780
924
|
effectIndex: index,
|
|
@@ -782,7 +926,7 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
782
926
|
hint: `Provide bindings.backends[${JSON.stringify(origin || '<origin>')}] or an explicit effect backend.`
|
|
783
927
|
});
|
|
784
928
|
}
|
|
785
|
-
backend = backend.trim();
|
|
929
|
+
backend = typeof backend === 'string' ? backend.trim() : '';
|
|
786
930
|
}
|
|
787
931
|
const binding = Object.freeze({
|
|
788
932
|
index,
|
|
@@ -791,6 +935,7 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
791
935
|
origin,
|
|
792
936
|
url,
|
|
793
937
|
backend,
|
|
938
|
+
...(!backend && effect.kind === 'fetch' ? { dynamic: true } : {}),
|
|
794
939
|
...(effect.kind === 'grip.broadcast' && gripSecretRef ? { authSecretRef: gripSecretRef } : {})
|
|
795
940
|
});
|
|
796
941
|
backends.push(binding);
|
|
@@ -799,8 +944,12 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
799
944
|
}
|
|
800
945
|
|
|
801
946
|
return Object.freeze({
|
|
947
|
+
maxDurationMs: require('@pulse-compute/runtime/host').normalizeRequestDuration(options.maxDurationMs),
|
|
948
|
+
bodyForwarding: require('../toolchain/body-forwarding.js').normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs),
|
|
949
|
+
...(source.logEndpoint === undefined ? {} : { logEndpoint: require('../toolchain/logging.js').normalizeFastlyLogEndpoint(source.logEndpoint) }),
|
|
802
950
|
configStore,
|
|
803
951
|
secretStore,
|
|
952
|
+
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
953
|
kv: Object.freeze(kv),
|
|
805
954
|
fetch: Object.freeze(fetch),
|
|
806
955
|
backends: Object.freeze(backends),
|
|
@@ -834,7 +983,7 @@ function planRequiresFetchBodyRead(plan) {
|
|
|
834
983
|
if (Array.isArray(value)) return value.some(visit);
|
|
835
984
|
return Object.values(value).some(visit);
|
|
836
985
|
}
|
|
837
|
-
return visit(plan.entry);
|
|
986
|
+
return visit(plan.entry) || visit(plan.handlers) || visit(plan.stages) || visit(plan.helpers);
|
|
838
987
|
}
|
|
839
988
|
|
|
840
989
|
function requiredImportsForPlan(plan, bindings) {
|
|
@@ -849,33 +998,43 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
849
998
|
'fastly_http_body:new',
|
|
850
999
|
'fastly_http_body:write'
|
|
851
1000
|
]);
|
|
1001
|
+
if (bindings && bindings.maxDurationMs !== undefined) { keys.add('wasi_snapshot_preview1:clock_time_get'); keys.add('fastly_async_io:select'); }
|
|
852
1002
|
const kinds = new Set((plan.effects || []).map((effect) => effect.kind));
|
|
853
1003
|
if (kinds.has('fetch') || kinds.has('grip.publish') || kinds.has('grip.broadcast')) {
|
|
854
1004
|
for (const key of [
|
|
855
1005
|
'fastly_http_req:new', 'fastly_http_req:method_set', 'fastly_http_req:uri_set',
|
|
856
|
-
'fastly_http_req:header_insert', 'fastly_http_req:send_async', 'fastly_http_req:
|
|
1006
|
+
'fastly_http_req:header_insert', 'fastly_http_req:send_async', 'fastly_http_req:pending_req_wait_v2'
|
|
857
1007
|
]) keys.add(key);
|
|
858
1008
|
}
|
|
1009
|
+
if (bindings && dynamicBackends.active(bindings)) keys.add('fastly_http_req:register_dynamic_backend');
|
|
1010
|
+
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
1011
|
if (planRequiresFetchBodyRead(plan)) keys.add('fastly_http_body:read');
|
|
860
1012
|
if (kinds.has('config.get')) for (const key of ['fastly_config_store:open', 'fastly_config_store:get']) keys.add(key);
|
|
861
1013
|
const gripAuthenticationRequired = kinds.has('grip.broadcast')
|
|
862
1014
|
&& (!bindings || Boolean(bindings.grip && bindings.grip.authentication));
|
|
863
1015
|
const jwtSecretRequired = (plan.effects || []).some((effect) => (
|
|
864
|
-
|
|
1016
|
+
['jwt.verify', 'jwt.sign'].includes(effect.kind)
|
|
865
1017
|
&& effect.resource
|
|
866
1018
|
&& effect.resource.keyType === 'secret'
|
|
867
1019
|
));
|
|
868
1020
|
if (kinds.has('secret.get') || jwtSecretRequired || gripAuthenticationRequired) {
|
|
869
1021
|
for (const key of ['fastly_secret_store:open', 'fastly_secret_store:get', 'fastly_secret_store:plaintext']) keys.add(key);
|
|
870
1022
|
}
|
|
871
|
-
if (kinds.has(
|
|
1023
|
+
for (const kind of conditionalKv.KV_CONDITIONAL_KINDS) if (kinds.has(kind)) for (const key of conditionalKv.kvImports(kind)) keys.add(key);
|
|
1024
|
+
if (kinds.has('jwt.verify') || kinds.has('jwt.sign') || kinds.has('time.now')) keys.add('wasi_snapshot_preview1:clock_time_get');
|
|
872
1025
|
if (kinds.has('kv.get') || kinds.has('kv.put') || kinds.has('assets.lookup')) keys.add('fastly_kv_store:open');
|
|
873
1026
|
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
1027
|
if (kinds.has('kv.put')) for (const key of ['fastly_kv_store:insert', 'fastly_kv_store:insert_wait']) keys.add(key);
|
|
875
1028
|
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');
|
|
1029
|
+
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))) {
|
|
1030
|
+
if (bindings?.logEndpoint === undefined) keys.add('wasi_snapshot_preview1:fd_write');
|
|
1031
|
+
else { keys.add('fastly_log:endpoint_get'); keys.add('fastly_log:write'); }
|
|
1032
|
+
}
|
|
1033
|
+
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);
|
|
1034
|
+
if (require('./native-body-forwarding.js').enabled(plan)) {
|
|
1035
|
+
keys.delete('fastly_http_req:send_async'); keys.delete('fastly_http_req:pending_req_wait_v2');
|
|
1036
|
+
for (const key of require('./native-body-forwarding.js').IMPORTS) keys.add(key);
|
|
1037
|
+
keys.add('fastly_http_body:read');
|
|
879
1038
|
}
|
|
880
1039
|
return Object.freeze([...keys].sort());
|
|
881
1040
|
}
|
|
@@ -887,15 +1046,40 @@ function stripPulseHostImports(source) {
|
|
|
887
1046
|
.join('\n');
|
|
888
1047
|
}
|
|
889
1048
|
|
|
1049
|
+
function schemaTextEncodeIds(plan) {
|
|
1050
|
+
// Keep the PS3 accounting/failure boundary and all unproved codec families.
|
|
1051
|
+
if (nativeValueBudget.hasBoundedReadLoop(plan)) return [];
|
|
1052
|
+
function closedText(node, depth = 0) {
|
|
1053
|
+
if (depth > 16) return false;
|
|
1054
|
+
if (node.kind === 'string' || node.kind === 'boolean') return true;
|
|
1055
|
+
if (node.kind === 'nullable') return closedText(node.value, depth);
|
|
1056
|
+
if (node.kind === 'array') return closedText(node.element, depth + 1);
|
|
1057
|
+
return node.kind === 'object' && !node.additionalProperties
|
|
1058
|
+
&& node.fields.every(field => closedText(field.value, depth + 1));
|
|
1059
|
+
}
|
|
1060
|
+
return ((plan.schemas && plan.schemas.registry && plan.schemas.registry.schemas) || [])
|
|
1061
|
+
.filter(schema => !schema.jsonLimits && schema.root.kind === 'object'
|
|
1062
|
+
&& schemaNeedsValueProjection(schema.root) && closedText(schema.root))
|
|
1063
|
+
.map(schema => schema.id);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
890
1066
|
function generateSchemaRuntime(plan) {
|
|
891
1067
|
const registry = plan.schemas && plan.schemas.registry ? plan.schemas.registry : { schemas: [], maxBytes: FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES, contentTypePolicy: 'accept-json-or-missing' };
|
|
892
1068
|
const schemas = registry.schemas || [];
|
|
1069
|
+
const textEncodeIds = new Set(schemaTextEncodeIds(plan));
|
|
893
1070
|
const nodeLines = [];
|
|
1071
|
+
const scalarProjectors = new Map();
|
|
1072
|
+
const flatObjectProjectors = new Map();
|
|
1073
|
+
const scalarKinds = ['string', 'boolean', 'i32', 'u32', 'f64', 'string-enum'];
|
|
894
1074
|
let nodeIndex = 0;
|
|
895
1075
|
|
|
896
1076
|
function emitNode(schemaIndex, node) {
|
|
897
1077
|
const currentIndex = nodeIndex++;
|
|
898
1078
|
const functionName = `__pulse_fastly_schema_${schemaIndex}_${currentIndex}`;
|
|
1079
|
+
const flatObject = node.kind === 'object' && !node.additionalProperties
|
|
1080
|
+
&& node.fields.every(field => scalarKinds.includes(field.value.kind));
|
|
1081
|
+
// Locals belong to this function, so equivalent flat bodies need no node ID.
|
|
1082
|
+
const localIndex = flatObject ? 'flat' : currentIndex;
|
|
899
1083
|
const child = node.kind === 'nullable'
|
|
900
1084
|
? emitNode(schemaIndex, node.value)
|
|
901
1085
|
: node.kind === 'array'
|
|
@@ -904,30 +1088,43 @@ function generateSchemaRuntime(plan) {
|
|
|
904
1088
|
const fields = node.kind === 'object'
|
|
905
1089
|
? node.fields.map((field) => Object.freeze({ field, apply: emitNode(schemaIndex, field.value) }))
|
|
906
1090
|
: [];
|
|
907
|
-
const body = [`function ${functionName}(valueHandle: i32): i32 {`, ' const input = __pulse_fastly_value(valueHandle)'];
|
|
1091
|
+
const body = [`function ${functionName}(valueHandle: i32, checkDuplicates: bool): i32 {`, ' const input = __pulse_fastly_value(valueHandle)'];
|
|
908
1092
|
if (node.kind === 'nullable') {
|
|
909
1093
|
body.push(' if (input.kind == PULSE_VALUE_NULL) return valueHandle');
|
|
910
|
-
body.push(` return ${child}(valueHandle)`);
|
|
1094
|
+
body.push(` return ${child}(valueHandle, checkDuplicates)`);
|
|
911
1095
|
} else if (node.kind === 'array') {
|
|
912
1096
|
body.push(' if (input.kind != PULSE_VALUE_ARRAY) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }');
|
|
913
1097
|
body.push(' const output = host_value_array()');
|
|
914
1098
|
body.push(' for (let index = 0; index < input.values.length; index += 1) {');
|
|
915
|
-
body.push(` const item = ${child}(unchecked(input.values[index]))`);
|
|
1099
|
+
body.push(` const item = ${child}(unchecked(input.values[index]), checkDuplicates)`);
|
|
916
1100
|
body.push(' if (item <= 0) return 0');
|
|
917
1101
|
body.push(' host_value_array_push(output, item)');
|
|
918
1102
|
body.push(' }');
|
|
919
1103
|
body.push(' return output');
|
|
1104
|
+
} else if (node.kind === 'json-value' || node.kind === 'json-object') {
|
|
1105
|
+
body.push(...nestedJsonProjectionLines(node));
|
|
1106
|
+
} else if (node.kind === 'scalar-record') {
|
|
1107
|
+
body.push(...scalarRecordProjectionLines(node));
|
|
920
1108
|
} else if (node.kind === 'object') {
|
|
921
1109
|
body.push(' if (input.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 50, -1); return 0 }');
|
|
1110
|
+
if (node.additionalProperties) body.push(' if (checkDuplicates && input.duplicateJsonKeys) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 57, -1); return 0 }');
|
|
922
1111
|
body.push(' const output = host_value_object()');
|
|
923
1112
|
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(`
|
|
1113
|
+
body.push(` const key_${localIndex}_${fieldIndex} = __pulse_fastly_string_value(${quote(field.name)})`);
|
|
1114
|
+
if (!field.required) body.push(` if (__pulse_fastly_find(input, ${quote(field.name)}) >= 0) {`);
|
|
1115
|
+
body.push(` const field_${localIndex}_${fieldIndex} = host_value_property(valueHandle, key_${localIndex}_${fieldIndex})`);
|
|
1116
|
+
body.push(` if (__pulse_fastly_value(field_${localIndex}_${fieldIndex}).kind == PULSE_VALUE_UNDEFINED) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 51, -1); return 0 }`);
|
|
1117
|
+
body.push(` const projected_${localIndex}_${fieldIndex} = ${apply}(field_${localIndex}_${fieldIndex}, checkDuplicates)`);
|
|
1118
|
+
body.push(` if (projected_${localIndex}_${fieldIndex} <= 0) return 0`);
|
|
1119
|
+
body.push(` host_value_object_set(output, key_${localIndex}_${fieldIndex}, projected_${localIndex}_${fieldIndex})`);
|
|
1120
|
+
if (!field.required) body.push(' }');
|
|
930
1121
|
});
|
|
1122
|
+
if (node.additionalProperties) {
|
|
1123
|
+
body.push(' for (let index = 0; index < input.keys.length; index++) {', ' const key = unchecked(input.keys[index])');
|
|
1124
|
+
if (node.fields.length) body.push(` if (${node.fields.map(field => `key == ${quote(field.name)}`).join(' || ')}) continue`);
|
|
1125
|
+
body.push(' const item = __pulse_fastly_copy_json(unchecked(input.values[index]), checkDuplicates)',
|
|
1126
|
+
' if (item <= 0) return 0', ' host_value_object_set(output, __pulse_fastly_string_value(key), item)', ' }');
|
|
1127
|
+
}
|
|
931
1128
|
body.push(' return output');
|
|
932
1129
|
} else if (node.kind === 'string') {
|
|
933
1130
|
body.push(' if (input.kind != PULSE_VALUE_STRING) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }');
|
|
@@ -952,6 +1149,32 @@ function generateSchemaRuntime(plan) {
|
|
|
952
1149
|
throw new TypeError(`Unsupported Fastly native schema node ${String(node.kind)}.`);
|
|
953
1150
|
}
|
|
954
1151
|
body.push('}', '');
|
|
1152
|
+
// Only scalar validators preserve the original handle without allocating or
|
|
1153
|
+
// calling child projectors. Compare the exact emitted body, including enum
|
|
1154
|
+
// values/order and error stages.
|
|
1155
|
+
if (scalarKinds.includes(node.kind)) {
|
|
1156
|
+
const key = body.slice(1).join('\n');
|
|
1157
|
+
const existing = scalarProjectors.get(key);
|
|
1158
|
+
if (existing) return existing;
|
|
1159
|
+
const sharedName = `__pulse_fastly_schema_scalar_${scalarProjectors.size}`;
|
|
1160
|
+
scalarProjectors.set(key, sharedName);
|
|
1161
|
+
body[0] = `function ${sharedName}(valueHandle: i32, checkDuplicates: bool): i32 {`;
|
|
1162
|
+
nodeLines.push(...body);
|
|
1163
|
+
return sharedName;
|
|
1164
|
+
}
|
|
1165
|
+
// Share code only after child scalar identities are resolved. Exact bodies
|
|
1166
|
+
// retain field order, presence checks, validation and errors. Each call still
|
|
1167
|
+
// allocates its own output; open/nested/nullable/array bodies remain separate.
|
|
1168
|
+
if (flatObject) {
|
|
1169
|
+
const key = body.slice(1).join('\n');
|
|
1170
|
+
const existing = flatObjectProjectors.get(key);
|
|
1171
|
+
if (existing) return existing;
|
|
1172
|
+
const sharedName = `__pulse_fastly_schema_flat_object_${flatObjectProjectors.size}`;
|
|
1173
|
+
flatObjectProjectors.set(key, sharedName);
|
|
1174
|
+
body[0] = `function ${sharedName}(valueHandle: i32, checkDuplicates: bool): i32 {`;
|
|
1175
|
+
nodeLines.push(...body);
|
|
1176
|
+
return sharedName;
|
|
1177
|
+
}
|
|
955
1178
|
nodeLines.push(...body);
|
|
956
1179
|
return functionName;
|
|
957
1180
|
}
|
|
@@ -961,17 +1184,22 @@ function generateSchemaRuntime(plan) {
|
|
|
961
1184
|
`const __PULSE_SCHEMA_MAX_BYTES: i32 = ${Number(registry.maxBytes || FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES)}`,
|
|
962
1185
|
`const __PULSE_SCHEMA_REQUIRE_JSON: bool = ${registry.contentTypePolicy === 'require-json' ? 'true' : 'false'}`,
|
|
963
1186
|
'',
|
|
1187
|
+
schemaJsonAdmissionSource(plan),
|
|
1188
|
+
schemaFetchJsonSource(),
|
|
964
1189
|
...nodeLines,
|
|
965
|
-
|
|
1190
|
+
`function __pulse_fastly_schema_apply(schemaId: string, valueHandle: i32, encode: bool${textEncodeIds.size ? ', textResult: bool = false' : ''}): i32 {`,
|
|
966
1191
|
' if (schemaId.length == 0) return valueHandle'
|
|
967
1192
|
];
|
|
968
1193
|
for (const [schemaIndex, schema] of schemas.entries()) {
|
|
969
1194
|
lines.push(`${schemaIndex === 0 ? ' if' : ' else if'} (schemaId == ${quote(schema.id)}) {`);
|
|
970
|
-
lines.push(`
|
|
1195
|
+
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 }`);
|
|
1196
|
+
lines.push(` const projected = ${roots[schemaIndex]}(valueHandle, !encode)`);
|
|
971
1197
|
lines.push(' if (projected <= 0) return 0');
|
|
972
1198
|
lines.push(' const input = __pulse_fastly_json(projected, 0)');
|
|
973
1199
|
lines.push(` const normalized = encode ? __pulse_schema_encode_${schemaIndex}(input) : __pulse_schema_decode_${schemaIndex}(input)`);
|
|
974
|
-
lines.push(' return
|
|
1200
|
+
if (textEncodeIds.has(schema.id)) lines.push(' if (textResult && encode) return __pulse_fastly_string_value(normalized)');
|
|
1201
|
+
if (schema.jsonLimits) lines.push(' const output = __pulse_fastly_parse_json(normalized)', ' if (output > 0) __pulse_fastly_deep_freeze(output)', ' return output');
|
|
1202
|
+
else lines.push(' return __pulse_fastly_parse_json(normalized)');
|
|
975
1203
|
lines.push(' }');
|
|
976
1204
|
}
|
|
977
1205
|
lines.push(' __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1)');
|
|
@@ -994,7 +1222,7 @@ function effectResultSource(plan, bindings) {
|
|
|
994
1222
|
lines.push(`function __pulse_fastly_secret_store(effectIndex: i32): string { return ${quote(bindings.secretStore)} }`, '');
|
|
995
1223
|
lines.push('function __pulse_fastly_jwt_secret_binding(effectIndex: i32): string {', ' switch (effectIndex) {');
|
|
996
1224
|
for (const [index, effect] of (plan.effects || []).entries()) {
|
|
997
|
-
if (
|
|
1225
|
+
if (['jwt.verify', 'jwt.sign'].includes(effect.kind) && effect.resource.keyType === 'secret') {
|
|
998
1226
|
lines.push(` case ${index}: return ${quote(effect.resource.secretBinding)}`);
|
|
999
1227
|
}
|
|
1000
1228
|
}
|
|
@@ -1009,12 +1237,31 @@ function effectResultSource(plan, bindings) {
|
|
|
1009
1237
|
for (const binding of bindings.grip.publish) lines.push(` case ${binding.index}: return ${quote(binding.authSecretRef || '')}`);
|
|
1010
1238
|
lines.push(' default: return ""', ' }', '}', '');
|
|
1011
1239
|
|
|
1240
|
+
// A config lookup has the same ready-result handoff at every static site.
|
|
1241
|
+
// Keep the site indices in the switch; pass the active index through so the
|
|
1242
|
+
// pending slot and any state error still belong to the original invocation.
|
|
1243
|
+
const configSites = (plan.effects || []).flatMap((effect, index) => effect.kind === 'config.get' ? [index] : []);
|
|
1244
|
+
if (configSites.length > 1) {
|
|
1245
|
+
lines.push('function __pulse_fastly_resolve_config_get(effectIndex: i32): i32 {');
|
|
1246
|
+
lines.push(' const response = __pulse_fastly_wait_ready(effectIndex)');
|
|
1247
|
+
lines.push(' if (response <= 0) return 0');
|
|
1248
|
+
lines.push(' return response');
|
|
1249
|
+
lines.push('}', '');
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1012
1252
|
lines.push('function __pulse_fastly_resolve_effect(effectIndex: i32): i32 {');
|
|
1013
1253
|
lines.push(' switch (effectIndex) {');
|
|
1014
1254
|
for (const [index, effect] of (plan.effects || []).entries()) {
|
|
1255
|
+
if (effect.kind === 'config.get' && configSites.length > 1) {
|
|
1256
|
+
if (index === configSites[0]) {
|
|
1257
|
+
for (const site of configSites) lines.push(` case ${site}:`);
|
|
1258
|
+
lines.push(' return __pulse_fastly_resolve_config_get(effectIndex)');
|
|
1259
|
+
}
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1015
1262
|
const result = effect.result || {};
|
|
1016
1263
|
const decoder = result.decoder;
|
|
1017
|
-
const waiter = effect.kind === 'fetch'
|
|
1264
|
+
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
1265
|
? '__pulse_fastly_wait_fetch'
|
|
1019
1266
|
: effect.kind === 'grip.publish'
|
|
1020
1267
|
? '__pulse_fastly_wait_grip_publish'
|
|
@@ -1050,12 +1297,20 @@ function effectResultSource(plan, bindings) {
|
|
|
1050
1297
|
|
|
1051
1298
|
function effectDispatchSource(plan) {
|
|
1052
1299
|
const handlerByKind = Object.freeze({
|
|
1300
|
+
'time.now': '__pulse_fastly_time_begin',
|
|
1301
|
+
'crypto.digestText': '__pulse_fastly_digest_begin',
|
|
1053
1302
|
fetch: '__pulse_fastly_fetch_begin',
|
|
1054
1303
|
'config.get': '__pulse_fastly_config_begin',
|
|
1055
1304
|
'secret.get': '__pulse_fastly_secret_begin',
|
|
1056
1305
|
'jwt.verify': '__pulse_fastly_jwt_begin',
|
|
1306
|
+
'jwt.sign': '__pulse_fastly_jwt_sign_begin',
|
|
1307
|
+
's3.head': '__pulse_fastly_s3_begin',
|
|
1308
|
+
's3.getText': '__pulse_fastly_s3_begin',
|
|
1309
|
+
's3.putText': '__pulse_fastly_s3_begin',
|
|
1310
|
+
's3.getBody': '__pulse_fastly_s3_begin',
|
|
1057
1311
|
'kv.get': '__pulse_fastly_kv_get_begin',
|
|
1058
1312
|
'kv.put': '__pulse_fastly_kv_put_begin',
|
|
1313
|
+
...Object.fromEntries(conditionalKv.KV_CONDITIONAL_KINDS.map(kind => [kind, '__pulse_fastly_kv_conditional_begin'])),
|
|
1059
1314
|
'assets.lookup': '__pulse_fastly_assets_begin',
|
|
1060
1315
|
'grip.channel': '__pulse_fastly_grip_channel_begin',
|
|
1061
1316
|
'grip.hold': '__pulse_fastly_grip_hold_begin',
|
|
@@ -1065,6 +1320,7 @@ function effectDispatchSource(plan) {
|
|
|
1065
1320
|
const kinds = [...new Set((plan.effects || []).map((effect) => effect.kind))];
|
|
1066
1321
|
const lines = [
|
|
1067
1322
|
'function host_effect_begin(effectIndex: i32, payload: i32): void {',
|
|
1323
|
+
...(needsNativeValueFailureGuard(plan) ? [' if (__pulse_fastly_last_error != PULSE_ERROR_NONE) return'] : []),
|
|
1068
1324
|
' 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
1325
|
' const payloadValue = __pulse_fastly_value(payload)',
|
|
1070
1326
|
' if (payloadValue.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 47, effectIndex); return }',
|
|
@@ -1085,7 +1341,14 @@ function effectDispatchSource(plan) {
|
|
|
1085
1341
|
}
|
|
1086
1342
|
|
|
1087
1343
|
function fastlyRuntimeSource(plan, bindings, options = {}) {
|
|
1344
|
+
const textEncodeIds = schemaTextEncodeIds(plan);
|
|
1088
1345
|
const effectCount = (plan.effects || []).length;
|
|
1346
|
+
const hasAssets = (plan.effects || []).some(effect => effect.kind === 'assets.lookup');
|
|
1347
|
+
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 }
|
|
1348
|
+
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) }` : '';
|
|
1349
|
+
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" }
|
|
1350
|
+
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])) }
|
|
1351
|
+
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
1352
|
const jwtCrypto = options.jwtCrypto || selectedJwtCrypto(plan);
|
|
1090
1353
|
const es256KeyRecords = options.es256KeyRecords || Object.freeze([]);
|
|
1091
1354
|
const jwtReality = options.jwtReality && options.jwtReality.enabled === true
|
|
@@ -1143,19 +1406,24 @@ function __pulse_fastly_jwt_attach_evidence(handle: i32): void {
|
|
|
1143
1406
|
const value = __pulse_fastly_value(handle)
|
|
1144
1407
|
if (value.kind == PULSE_VALUE_RESPONSE) __pulse_fastly_jwt_evidence_headers(value)
|
|
1145
1408
|
}
|
|
1409
|
+
`
|
|
1410
|
+
: String.raw`
|
|
1411
|
+
function __pulse_fastly_jwt_attach_evidence(handle: i32): void { void handle }
|
|
1412
|
+
`;
|
|
1413
|
+
// Keep the legacy hook name: body, fetch, header and deadline owners prepend
|
|
1414
|
+
// their specific responses here. Every remaining fatal path needs a response,
|
|
1415
|
+
// independently of whether JWT evidence was requested for this build.
|
|
1416
|
+
const terminalErrorSource = `
|
|
1146
1417
|
function __pulse_fastly_jwt_send_error(): void {
|
|
1147
1418
|
const output = new __PulseFastlyValue()
|
|
1148
1419
|
output.kind = PULSE_VALUE_RESPONSE
|
|
1149
|
-
output.status = 400
|
|
1150
|
-
output.text = ""
|
|
1420
|
+
output.status = ${jwtReality ? '__pulse_fastly_jwt_error != 0 ? 400 : 500' : '500'}
|
|
1421
|
+
output.text = output.status == 400 ? "" : "Internal Server Error"
|
|
1151
1422
|
output.headers.push(new __PulseFastlyHeader("content-type", "text/plain; charset=utf-8"))
|
|
1152
|
-
__pulse_fastly_jwt_evidence_headers(output)
|
|
1423
|
+
${jwtReality ? 'if (__pulse_fastly_jwt_error != 0) __pulse_fastly_jwt_evidence_headers(output)' : ''}
|
|
1424
|
+
// Do not clear the original failure or expose its detail in the response.
|
|
1153
1425
|
__pulse_fastly_send_result(__pulse_fastly_put(output))
|
|
1154
1426
|
}
|
|
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
1427
|
`;
|
|
1160
1428
|
const gripAuthentication = bindings.grip && bindings.grip.authentication;
|
|
1161
1429
|
const gripSecretSource = gripAuthentication
|
|
@@ -1164,7 +1432,7 @@ function __pulse_fastly_jwt_send_error(): void {}
|
|
|
1164
1432
|
const gripAuthorizationSource = gripAuthentication
|
|
1165
1433
|
? `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
1434
|
: '';
|
|
1167
|
-
|
|
1435
|
+
const source = String.raw`
|
|
1168
1436
|
@external("fastly_abi", "init") declare function fastly_abi_init(version: i64): i32
|
|
1169
1437
|
@external("fastly_http_req", "body_downstream_get") declare function fastly_http_req_body_downstream_get(requestOut: usize, bodyOut: usize): i32
|
|
1170
1438
|
@external("fastly_http_req", "method_get") declare function fastly_http_req_method_get(handle: i32, buffer: usize, bufferLength: i32, writtenOut: usize): i32
|
|
@@ -1175,7 +1443,7 @@ function __pulse_fastly_jwt_send_error(): void {}
|
|
|
1175
1443
|
@external("fastly_http_req", "uri_set") declare function fastly_http_req_uri_set(handle: i32, uri: usize, uriLength: i32): i32
|
|
1176
1444
|
@external("fastly_http_req", "header_insert") declare function fastly_http_req_header_insert(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32): i32
|
|
1177
1445
|
@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", "
|
|
1446
|
+
@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
1447
|
@external("fastly_http_resp", "new") declare function fastly_http_resp_new(handleOut: usize): i32
|
|
1180
1448
|
@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
1449
|
@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
|
|
@@ -1195,8 +1463,11 @@ function __pulse_fastly_jwt_send_error(): void {}
|
|
|
1195
1463
|
@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
1464
|
@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
1465
|
@external("fastly_kv_store", "insert_wait") declare function fastly_kv_store_insert_wait(handle: i32, kvErrorOut: usize): i32
|
|
1198
|
-
@external("
|
|
1199
|
-
@external("
|
|
1466
|
+
@external("fastly_kv_store", "delete") declare function fastly_kv_store_delete(store: i32, key: usize, keyLength: i32, configMask: i32, config: usize, pendingOut: usize): i32
|
|
1467
|
+
@external("fastly_kv_store", "delete_wait") declare function fastly_kv_store_delete_wait(handle: i32, kvErrorOut: usize): i32
|
|
1468
|
+
@external("fastly_kv_store", "list") declare function fastly_kv_store_list(store: i32, configMask: i32, config: usize, pendingOut: usize): i32
|
|
1469
|
+
@external("fastly_kv_store", "list_wait") declare function fastly_kv_store_list_wait(handle: i32, bodyOut: usize, kvErrorOut: usize): i32
|
|
1470
|
+
${nativeLoggingSource(bindings.logEndpoint)}
|
|
1200
1471
|
@external("wasi_snapshot_preview1", "clock_time_get") declare function wasi_snapshot_preview1_clock_time_get(clockId: i32, precision: i64, timeOut: usize): i32
|
|
1201
1472
|
|
|
1202
1473
|
const PULSE_FASTLY_EFFECTS_ABI_VERSION: i64 = ${FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION}
|
|
@@ -1233,20 +1504,42 @@ const PULSE_ERROR_SCHEMA: i32 = 1005
|
|
|
1233
1504
|
const PULSE_ERROR_TRANSPORT: i32 = 1006
|
|
1234
1505
|
const PULSE_ERROR_STATE: i32 = 1007
|
|
1235
1506
|
const PULSE_ERROR_JWT: i32 = 1008
|
|
1507
|
+
const PULSE_ERROR_REQUEST_BODY: i32 = 1009
|
|
1236
1508
|
|
|
1237
1509
|
class __PulseFastlyValue {
|
|
1510
|
+
${tracksJsonDuplicates(plan) ? 'duplicateJsonKeys: bool = false' : ''}
|
|
1238
1511
|
kind: i32 = PULSE_VALUE_UNDEFINED
|
|
1239
1512
|
boolean: i32 = 0
|
|
1240
1513
|
number: f64 = 0.0
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1514
|
+
${nativeStringFields}
|
|
1515
|
+
// Scalar handles do not need collection backing stores.
|
|
1516
|
+
private keysStorage: Array<string> | null = null
|
|
1517
|
+
get keys(): Array<string> {
|
|
1518
|
+
if (this.keysStorage === null) this.keysStorage = new Array<string>()
|
|
1519
|
+
return this.keysStorage!
|
|
1520
|
+
}
|
|
1521
|
+
// Scalar handles do not need collection backing stores.
|
|
1522
|
+
private valuesStorage: Array<i32> | null = null
|
|
1523
|
+
get values(): Array<i32> {
|
|
1524
|
+
if (this.valuesStorage === null) this.valuesStorage = new Array<i32>()
|
|
1525
|
+
return this.valuesStorage!
|
|
1526
|
+
}
|
|
1244
1527
|
responseHandle: i32 = 0
|
|
1245
1528
|
bodyHandle: i32 = 0
|
|
1246
1529
|
bodyLoaded: i32 = 0
|
|
1530
|
+
s3Body: bool = false
|
|
1531
|
+
s3Deadline: i64 = 0
|
|
1532
|
+
s3Length: i32 = 0
|
|
1247
1533
|
status: i32 = 200
|
|
1248
|
-
|
|
1534
|
+
// Scalar handles do not need collection backing stores.
|
|
1535
|
+
private headersStorage: Array<__PulseFastlyHeader> | null = null
|
|
1536
|
+
get headers(): Array<__PulseFastlyHeader> {
|
|
1537
|
+
if (this.headersStorage === null) this.headersStorage = new Array<__PulseFastlyHeader>()
|
|
1538
|
+
return this.headersStorage!
|
|
1539
|
+
}
|
|
1540
|
+
binaryBody: Uint8Array | null = null
|
|
1249
1541
|
immutable: bool = false
|
|
1542
|
+
${nativeHandleOwnership.fields(options.handleTracing)}
|
|
1250
1543
|
}
|
|
1251
1544
|
class __PulseFastlyHeader {
|
|
1252
1545
|
constructor(public name: string, public value: string) {}
|
|
@@ -1255,11 +1548,14 @@ class __PulseJsonParser {
|
|
|
1255
1548
|
source: string
|
|
1256
1549
|
index: i32 = 0
|
|
1257
1550
|
failed: bool = false
|
|
1258
|
-
|
|
1551
|
+
tooLarge: bool = false
|
|
1552
|
+
entries: i32 = 0
|
|
1553
|
+
constructor(source: string, public conditionalKv: bool = false) { this.source = source }
|
|
1259
1554
|
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
1555
|
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
1556
|
value(depth: i32): i32 {
|
|
1262
|
-
this.skip(); if (this.
|
|
1557
|
+
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 }
|
|
1558
|
+
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
1559
|
const c = this.source.charCodeAt(this.index)
|
|
1264
1560
|
if (c == 34) return __pulse_fastly_string_value(this.string())
|
|
1265
1561
|
if (c == 123) return this.object(depth + 1)
|
|
@@ -1271,37 +1567,47 @@ class __PulseJsonParser {
|
|
|
1271
1567
|
this.failed = true; return 0
|
|
1272
1568
|
}
|
|
1273
1569
|
string(): string {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1570
|
+
// Find a bounded span, then decode each UTF-16 unit once. Concatenating one
|
|
1571
|
+
// character at a time exhausts stub/fixed memory on a valid maximum KV value.
|
|
1572
|
+
const start = this.index + 1; let end = start; let plain = true;
|
|
1573
|
+
while (end < this.source.length) {
|
|
1574
|
+
const c = this.source.charCodeAt(end);
|
|
1575
|
+
if (c == 34) break;
|
|
1576
|
+
if (c == 92) { plain = false; end += 1; }
|
|
1577
|
+
else if (c < 32) plain = false;
|
|
1578
|
+
end += 1;
|
|
1579
|
+
}
|
|
1580
|
+
if (end >= this.source.length) { this.failed = true; return "" }
|
|
1581
|
+
// A validated span needs one owned string, without a temporary UTF-16 array.
|
|
1582
|
+
// Keep escaped and malformed spans on the existing decoder path.
|
|
1583
|
+
if (plain) { this.index = end + 1; return this.source.substring(start, end); }
|
|
1584
|
+
const output = new Uint16Array(end - start); let count = 0; this.index = start;
|
|
1585
|
+
while (this.index < end) {
|
|
1586
|
+
let c = this.source.charCodeAt(this.index++);
|
|
1278
1587
|
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)
|
|
1588
|
+
if (this.index >= end) { this.failed = true; return "" }
|
|
1589
|
+
c = this.source.charCodeAt(this.index++);
|
|
1590
|
+
if (c == 34 || c == 92 || c == 47) {}
|
|
1591
|
+
else if (c == 98) c = 8;
|
|
1592
|
+
else if (c == 102) c = 12;
|
|
1593
|
+
else if (c == 110) c = 10;
|
|
1594
|
+
else if (c == 114) c = 13;
|
|
1595
|
+
else if (c == 116) c = 9;
|
|
1287
1596
|
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
|
|
1597
|
+
if (this.index + 4 > end) { this.failed = true; return "" }
|
|
1598
|
+
c = 0;
|
|
1599
|
+
for (let i = 0; i < 4; i++) {
|
|
1600
|
+
const d = this.source.charCodeAt(this.index++);
|
|
1601
|
+
const digit = d >= 48 && d <= 57 ? d - 48 : d >= 65 && d <= 70 ? d - 55 : d >= 97 && d <= 102 ? d - 87 : -1;
|
|
1296
1602
|
if (digit < 0) { this.failed = true; return "" }
|
|
1297
|
-
|
|
1603
|
+
c = (c << 4) | digit;
|
|
1298
1604
|
}
|
|
1299
|
-
this.index += 4; output += String.fromCharCode(code)
|
|
1300
1605
|
} else { this.failed = true; return "" }
|
|
1301
1606
|
} else if (c < 32) { this.failed = true; return "" }
|
|
1302
|
-
|
|
1607
|
+
output[count++] = u16(c);
|
|
1303
1608
|
}
|
|
1304
|
-
this.
|
|
1609
|
+
this.index = end + 1;
|
|
1610
|
+
return String.UTF16.decodeUnsafe(output.dataStart, count * 2);
|
|
1305
1611
|
}
|
|
1306
1612
|
numberValue(): i32 {
|
|
1307
1613
|
const start = this.index
|
|
@@ -1350,7 +1656,9 @@ class __PulseJsonParser {
|
|
|
1350
1656
|
while (!this.failed) {
|
|
1351
1657
|
this.skip(); if (this.index >= this.source.length || this.source.charCodeAt(this.index) != 34) { this.failed = true; break }
|
|
1352
1658
|
const key = this.string(); this.skip()
|
|
1659
|
+
if (this.conditionalKv && __pulse_fastly_find(__pulse_fastly_value(output), key) >= 0) { this.failed = true; break }
|
|
1353
1660
|
if (this.index >= this.source.length || this.source.charCodeAt(this.index) != 58) { this.failed = true; break }
|
|
1661
|
+
${tracksJsonDuplicates(plan) ? 'if (__pulse_fastly_find(__pulse_fastly_value(output), key) >= 0) __pulse_fastly_value(output).duplicateJsonKeys = true' : ''}
|
|
1354
1662
|
this.index += 1; host_value_object_set(output, __pulse_fastly_string_value(key), this.value(depth)); this.skip()
|
|
1355
1663
|
if (this.index >= this.source.length) { this.failed = true; break }
|
|
1356
1664
|
const c = this.source.charCodeAt(this.index); this.index += 1
|
|
@@ -1364,13 +1672,20 @@ class __PulseJsonParser {
|
|
|
1364
1672
|
const PULSE_FASTLY_PENDING_NONE: i32 = 0
|
|
1365
1673
|
const PULSE_FASTLY_PENDING_ASYNC: i32 = 1
|
|
1366
1674
|
const PULSE_FASTLY_PENDING_READY: i32 = 2
|
|
1367
|
-
|
|
1675
|
+
${nativeHandleOwnership.tableSource(options.handleTracing)}
|
|
1676
|
+
// Primitive handles have value semantics. Bounded caches keep admitted scalar
|
|
1677
|
+
// validation loops from retaining a fresh boxed value for every iteration.
|
|
1678
|
+
@lazy const __pulse_fastly_scalar_handles = new StaticArray<i32>(4)
|
|
1679
|
+
@lazy const __pulse_fastly_integer_handles = new StaticArray<i32>(65536)
|
|
1680
|
+
@lazy const __pulse_fastly_character_handles = new StaticArray<i32>(65537)
|
|
1681
|
+
@lazy const __pulse_fastly_literal_handles = new Map<string, i32>()
|
|
1368
1682
|
@lazy const __pulse_fastly_pending = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1369
1683
|
@lazy const __pulse_fastly_pending_mode = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1370
1684
|
@lazy const __pulse_fastly_payloads = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1371
1685
|
@lazy const __pulse_fastly_ready = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1372
1686
|
@lazy const __pulse_fastly_grip_channels = new Array<string>()
|
|
1373
1687
|
@lazy const __pulse_fastly_redactions = new Array<string>()
|
|
1688
|
+
@lazy const __pulse_fastly_field_redactions = new Array<string>()
|
|
1374
1689
|
let __pulse_fastly_request_handle: i32 = 0
|
|
1375
1690
|
let __pulse_fastly_request_body_handle: i32 = 0
|
|
1376
1691
|
let __pulse_fastly_request_method: string = ""
|
|
@@ -1381,7 +1696,6 @@ let __pulse_fastly_request_body_loaded: i32 = 0
|
|
|
1381
1696
|
let __pulse_fastly_last_error: i32 = 0
|
|
1382
1697
|
let __pulse_fastly_error_stage: i32 = 0
|
|
1383
1698
|
let __pulse_fastly_error_effect: i32 = -1
|
|
1384
|
-
let __pulse_fastly_log_handle: i32 = -1
|
|
1385
1699
|
let __pulse_fastly_jwt_error: i32 = 0
|
|
1386
1700
|
let __pulse_fastly_jwt_secret_calls: i32 = 0
|
|
1387
1701
|
let __pulse_fastly_jwt_crypto_calls: i32 = 0
|
|
@@ -1398,57 +1712,165 @@ function __pulse_fastly_out_i32(): StaticArray<i32> { return new StaticArray<i32
|
|
|
1398
1712
|
function __pulse_fastly_out_value(out: StaticArray<i32>): i32 { return load<i32>(changetype<usize>(out)) }
|
|
1399
1713
|
function __pulse_fastly_utf8(value: string): ArrayBuffer { return String.UTF8.encode(value, false) }
|
|
1400
1714
|
function __pulse_fastly_decode(buffer: Uint8Array, length: i32): string { return String.UTF8.decodeUnsafe(buffer.dataStart, length, false) }
|
|
1401
|
-
|
|
1402
|
-
|
|
1715
|
+
${nativeStringAssembly}
|
|
1716
|
+
${nativeStringConcat}
|
|
1717
|
+
${nativeStringTrim}
|
|
1718
|
+
${nativeStringIndex}
|
|
1403
1719
|
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 {
|
|
1720
|
+
function __pulse_fastly_string_value(value: string): i32 {
|
|
1721
|
+
const index = value.length == 0 ? 65536 : value.length == 1 ? value.charCodeAt(0) : -1;
|
|
1722
|
+
if (index >= 0) { const cached = unchecked(__pulse_fastly_character_handles[index]); if (cached != 0) return cached }
|
|
1723
|
+
const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_STRING; item.text = value;
|
|
1724
|
+
const handle = __pulse_fastly_put(item);
|
|
1725
|
+
if (index >= 0) { unchecked(__pulse_fastly_character_handles[index] = handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} }
|
|
1726
|
+
return handle
|
|
1727
|
+
}
|
|
1405
1728
|
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
1729
|
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
1730
|
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 <
|
|
1731
|
+
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
1732
|
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
|
-
|
|
1733
|
+
// Walk complete JSON string tokens, including escaped quotes. Substring matching
|
|
1734
|
+
// a quoted field would still corrupt an unrelated token such as "prefix\\\"id".
|
|
1735
|
+
function __pulse_fastly_redact_fields(input: string): string {
|
|
1736
|
+
if (__pulse_fastly_field_redactions.length == 0) return input;
|
|
1737
|
+
let output = "", start = 0, cursor = 0;
|
|
1738
|
+
while (cursor < input.length) {
|
|
1739
|
+
if (input.charCodeAt(cursor) != 34) { cursor += 1; continue; }
|
|
1740
|
+
const begin = cursor++;
|
|
1741
|
+
while (cursor < input.length) {
|
|
1742
|
+
const code = input.charCodeAt(cursor++);
|
|
1743
|
+
if (code == 92 && cursor < input.length) cursor += 1;
|
|
1744
|
+
else if (code == 34) break;
|
|
1745
|
+
}
|
|
1746
|
+
const token = input.substring(begin, cursor);
|
|
1747
|
+
for (let i = 0; i < __pulse_fastly_field_redactions.length; i += 1) {
|
|
1748
|
+
if (token != unchecked(__pulse_fastly_field_redactions[i])) continue;
|
|
1749
|
+
output += input.substring(start, begin) + __pulse_fastly_quote("<redacted>");
|
|
1750
|
+
start = cursor; break;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return output + input.substring(start);
|
|
1754
|
+
}
|
|
1755
|
+
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 }
|
|
1756
|
+
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
1757
|
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
1758
|
function __pulse_fastly_hex(value: i32): string { return String.fromCharCode(value < 10 ? 48 + value : 87 + value) }
|
|
1414
|
-
function
|
|
1415
|
-
|
|
1416
|
-
if (
|
|
1759
|
+
function __pulse_fastly_is_unpaired(value: string, i: i32): bool {
|
|
1760
|
+
const c = value.charCodeAt(i);
|
|
1761
|
+
if (c >= 0xd800 && c <= 0xdbff) return i + 1 >= value.length || value.charCodeAt(i + 1) < 0xdc00 || value.charCodeAt(i + 1) > 0xdfff;
|
|
1762
|
+
if (c >= 0xdc00 && c <= 0xdfff) return i == 0 || value.charCodeAt(i - 1) < 0xd800 || value.charCodeAt(i - 1) > 0xdbff;
|
|
1763
|
+
return false;
|
|
1764
|
+
}
|
|
1765
|
+
function __pulse_fastly_quote(value: string): string {
|
|
1766
|
+
// Count once and write UTF-16 units once. Repeated concatenation can allocate
|
|
1767
|
+
// gigabytes for a valid bounded string when every byte needs JSON escaping.
|
|
1768
|
+
let size = 2;
|
|
1769
|
+
for (let i = 0; i < value.length; i++) {
|
|
1770
|
+
const c = value.charCodeAt(i);
|
|
1771
|
+
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;
|
|
1772
|
+
}
|
|
1773
|
+
const out = new Uint16Array(size); let cursor = 0; out[cursor++] = 34;
|
|
1774
|
+
for (let i = 0; i < value.length; i++) {
|
|
1775
|
+
const c = value.charCodeAt(i);
|
|
1776
|
+
if (c == 34 || c == 92) { out[cursor++] = 92; out[cursor++] = u16(c); }
|
|
1777
|
+
else if (c == 8 || c == 9 || c == 10 || c == 12 || c == 13) {
|
|
1778
|
+
out[cursor++] = 92; out[cursor++] = c == 8 ? 98 : c == 9 ? 116 : c == 10 ? 110 : c == 12 ? 102 : 114;
|
|
1779
|
+
} else if (c < 32 || __pulse_fastly_is_unpaired(value, i)) {
|
|
1780
|
+
out[cursor++] = 92; out[cursor++] = 117;
|
|
1781
|
+
out[cursor++] = u16('0123456789abcdef'.charCodeAt((c >> 12) & 15)); out[cursor++] = u16('0123456789abcdef'.charCodeAt((c >> 8) & 15));
|
|
1782
|
+
out[cursor++] = u16('0123456789abcdef'.charCodeAt((c >> 4) & 15)); out[cursor++] = u16('0123456789abcdef'.charCodeAt(c & 15));
|
|
1783
|
+
} else out[cursor++] = u16(c);
|
|
1784
|
+
}
|
|
1785
|
+
out[cursor] = 34;
|
|
1786
|
+
return String.UTF16.decodeUnsafe(out.dataStart, out.byteLength);
|
|
1787
|
+
}
|
|
1788
|
+
function __pulse_fastly_json_parts(handle: i32, depth: i32, parts: Array<string>): void {
|
|
1789
|
+
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
1790
|
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
|
-
|
|
1791
|
+
if (value.kind == PULSE_VALUE_BOOLEAN) { parts.push(value.boolean != 0 ? "true" : "false"); return }
|
|
1792
|
+
if (value.kind == PULSE_VALUE_NUMBER) { parts.push(__pulse_fastly_number_string(value.number)); return }
|
|
1793
|
+
if (value.kind == PULSE_VALUE_STRING) { parts.push(__pulse_fastly_quote(value.text)); return }
|
|
1794
|
+
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 }
|
|
1795
|
+
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 }
|
|
1796
|
+
parts.push("null")
|
|
1797
|
+
}
|
|
1798
|
+
function __pulse_fastly_json(handle: i32, depth: i32): string {
|
|
1799
|
+
// Join once, avoiding a complete copy of a multi-MiB leaf at every ancestor
|
|
1800
|
+
// and at every following member in the response or effect result.
|
|
1801
|
+
const parts = new Array<string>(); __pulse_fastly_json_parts(handle, depth, parts);
|
|
1802
|
+
return __pulse_fastly_join_strings(parts)
|
|
1425
1803
|
}
|
|
1426
1804
|
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
1805
|
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
1806
|
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
|
-
|
|
1807
|
+
function __pulse_fastly_read_body(handle: i32, effectIndex: i32, requestText: bool = false): string {
|
|
1808
|
+
const chunks = new Array<Uint8Array>(); let total = 0;
|
|
1809
|
+
while (true) {
|
|
1810
|
+
const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES), read = __pulse_fastly_out_i32();
|
|
1811
|
+
const status = fastly_http_body_read(handle, buffer.dataStart, buffer.length, changetype<usize>(read));
|
|
1812
|
+
if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 20, effectIndex); return "" }
|
|
1813
|
+
const count = __pulse_fastly_out_value(read);
|
|
1814
|
+
if (count < 0 || count > buffer.length) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 20, effectIndex); return "" }
|
|
1815
|
+
if (!count) break;
|
|
1816
|
+
total += count;
|
|
1817
|
+
if (total > __PULSE_SCHEMA_MAX_BYTES && __PULSE_SCHEMA_MAX_BYTES > 0) { __pulse_fastly_fail(requestText ? PULSE_ERROR_REQUEST_BODY : PULSE_ERROR_SCHEMA, 21, effectIndex); return "" }
|
|
1818
|
+
chunks.push(buffer.subarray(0, count));
|
|
1819
|
+
}
|
|
1820
|
+
// UTF-8 scalars may straddle host read chunks. Decode the bounded body once.
|
|
1821
|
+
const bytes = new Uint8Array(total); let offset = 0;
|
|
1822
|
+
for (let i = 0; i < chunks.length; i++) { bytes.set(chunks[i], offset); offset += chunks[i].length; }
|
|
1823
|
+
if (requestText && !__pulse_request_utf8_valid(bytes)) { __pulse_fastly_fail(PULSE_ERROR_REQUEST_BODY, 24, effectIndex); return "" }
|
|
1824
|
+
return __pulse_fastly_decode(bytes, total);
|
|
1825
|
+
}
|
|
1826
|
+
function __pulse_fastly_request_header_text(name: string): string { const value = __pulse_header_lookup(__pulse_fastly_request_handle, name, false); return value === null ? "" : value }
|
|
1827
|
+
function __pulse_fastly_fetch_header_text(responseHandle: i32, name: string): string { const value = __pulse_header_lookup(responseHandle, name, true); return value === null ? "" : value }
|
|
1432
1828
|
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
1829
|
|
|
1434
|
-
function
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1830
|
+
function __pulse_fastly_scalar(kind: i32, boolean: i32, slot: i32): i32 {
|
|
1831
|
+
const cached = unchecked(__pulse_fastly_scalar_handles[slot]); if (cached != 0) return cached;
|
|
1832
|
+
const item = new __PulseFastlyValue(); item.kind = kind; item.boolean = boolean;
|
|
1833
|
+
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
|
|
1834
|
+
}
|
|
1835
|
+
function host_value_undefined(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_UNDEFINED, 0, 0) }
|
|
1836
|
+
function host_value_null(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_NULL, 0, 1) }
|
|
1837
|
+
function host_value_boolean(value: i32): i32 { return __pulse_fastly_scalar(PULSE_VALUE_BOOLEAN, value != 0 ? 1 : 0, value != 0 ? 3 : 2) }
|
|
1838
|
+
${plan.callables?.length || plan.binaryResources || plan.numericWorkspaces?.length ? require('./native-binary-resources.js').source() : ''}
|
|
1839
|
+
function host_value_number(value: f64): i32 {
|
|
1840
|
+
// Preserve negative zero, NaN, infinities and nonintegral values exactly.
|
|
1841
|
+
const cacheable = value >= 0 && value < 65536 && Math.floor(value) == value && (value != 0 || 1.0 / value > 0);
|
|
1842
|
+
const index = cacheable ? i32(value) : -1;
|
|
1843
|
+
if (index >= 0) { const cached = unchecked(__pulse_fastly_integer_handles[index]); if (cached != 0) return cached }
|
|
1844
|
+
const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_NUMBER; item.number = value;
|
|
1845
|
+
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
|
|
1846
|
+
}
|
|
1847
|
+
function host_value_string(pointer: i32, length: i32): i32 {
|
|
1848
|
+
const text = changetype<string>(pointer);
|
|
1849
|
+
if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.has(text)) return __pulse_fastly_literal_handles.get(text);
|
|
1850
|
+
const handle = __pulse_fastly_string_value(text);
|
|
1851
|
+
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);' : ''} }
|
|
1852
|
+
return handle
|
|
1853
|
+
}
|
|
1854
|
+
function host_effect_collection_count(handle: i32, max: i32, stateIndex: i32): i32 {
|
|
1855
|
+
const input = __pulse_fastly_value(handle)
|
|
1856
|
+
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 }
|
|
1857
|
+
if (input.values.length > max) { __pulse_fastly_fail(PULSE_ERROR_STATE, 172, -1); return -1 }
|
|
1858
|
+
if (__pulse_invocation_count + input.values.length > PULSE_FASTLY_MAX_EFFECT_INVOCATIONS) { __pulse_fastly_fail(PULSE_ERROR_STATE, 170, -1); return -1 }
|
|
1859
|
+
return input.values.length
|
|
1860
|
+
}
|
|
1439
1861
|
function host_value_array(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_ARRAY; return __pulse_fastly_put(item) }
|
|
1440
1862
|
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
1863
|
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
1864
|
function host_value_object(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_OBJECT; return __pulse_fastly_put(item) }
|
|
1443
1865
|
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
1866
|
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.
|
|
1867
|
+
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.textLength : item.values.length); return host_value_undefined() }
|
|
1446
1868
|
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) }
|
|
1869
|
+
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
1870
|
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.
|
|
1871
|
+
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
1872
|
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
|
|
1873
|
+
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
1874
|
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
1875
|
|
|
1454
1876
|
function host_request_method(): i32 { return __pulse_fastly_string_value(__pulse_fastly_request_method) }
|
|
@@ -1459,13 +1881,36 @@ function __pulse_router_param_text(path: string, pattern: string, name: string):
|
|
|
1459
1881
|
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
1882
|
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
1883
|
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 }
|
|
1884
|
+
function host_request_headers(): i32 { return __pulse_request_headers_read() }
|
|
1885
|
+
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) }
|
|
1886
|
+
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) }
|
|
1887
|
+
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
1888
|
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
1889
|
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
1890
|
function host_response_json(value: i32, options: i32): i32 { return __pulse_fastly_response(value, options, true) }
|
|
1891
|
+
function host_schema_decode(text: i32, schema: i32): i32 {
|
|
1892
|
+
const id = __pulse_fastly_value(schema)
|
|
1893
|
+
if (id.kind != PULSE_VALUE_STRING || id.text.length == 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1); return 0 }
|
|
1894
|
+
const input = __pulse_fastly_value(text)
|
|
1895
|
+
if (input.kind != PULSE_VALUE_STRING) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }
|
|
1896
|
+
if (String.UTF8.byteLength(input.text) > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, -1); return 0 }
|
|
1897
|
+
const parsed = ${hasJsonAdmission(plan) ? '__pulse_fastly_schema_parse_json(input.text, schema)' : '__pulse_fastly_parse_json(input.text)'}
|
|
1898
|
+
if (parsed <= 0) return 0
|
|
1899
|
+
const decoded = __pulse_fastly_schema_apply(id.text, parsed, false)
|
|
1900
|
+
if (decoded <= 0) return 0
|
|
1901
|
+
__pulse_fastly_deep_freeze(decoded)
|
|
1902
|
+
return decoded
|
|
1903
|
+
}
|
|
1904
|
+
function host_schema_encode(value: i32, schema: i32): i32 {
|
|
1905
|
+
const id = __pulse_fastly_value(schema)
|
|
1906
|
+
if (id.kind != PULSE_VALUE_STRING || id.text.length == 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1); return 0 }
|
|
1907
|
+
${textEncodeIds.length ? `const textResult = ${textEncodeIds.map(id => `id.text == ${quote(id)}`).join(' || ')}
|
|
1908
|
+
const projected = __pulse_fastly_schema_apply(id.text, value, true, textResult)` : 'const projected = __pulse_fastly_schema_apply(id.text, value, true)'}
|
|
1909
|
+
if (projected <= 0) return 0
|
|
1910
|
+
const text = ${textEncodeIds.length ? 'textResult ? __pulse_fastly_string(projected) : ' : ''}__pulse_fastly_json(projected, 0)
|
|
1911
|
+
if (String.UTF8.byteLength(text) > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, -1); return 0 }
|
|
1912
|
+
return ${textEncodeIds.length ? 'textResult ? projected : ' : ''}__pulse_fastly_string_value(text)
|
|
1913
|
+
}
|
|
1469
1914
|
function host_response_text(value: i32, options: i32): i32 { return __pulse_fastly_response(value, options, false) }
|
|
1470
1915
|
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
1916
|
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 +1920,8 @@ function host_grip_subscribe(responseHandle: i32, optionsHandle: i32): i32 { con
|
|
|
1475
1920
|
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
1921
|
function host_kv_namespace(name: i32): i32 { return name }
|
|
1477
1922
|
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 =
|
|
1923
|
+
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 = ${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 }
|
|
1924
|
+
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 = __pulse_header_lookup(response.responseHandle, __pulse_fastly_string(nameHandle), true); return value === null ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1480
1925
|
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
1926
|
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
1927
|
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 +1941,12 @@ function __pulse_fastly_jwt_record_verified(): void { __pulse_fastly_jwt_verifie
|
|
|
1496
1941
|
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
1942
|
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
1943
|
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) }
|
|
1944
|
+
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
1945
|
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 = fastly_http_req_header_insert(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 = __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 = fastly_http_req_send_async(requestHandle, bodyHandle, changetype<usize>(backendBytes), backendBytes.byteLength, changetype<usize>(pendingOut)); if (status != FASTLY_STATUS_OK) {
|
|
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 =
|
|
1946
|
+
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 = 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 = fastly_http_req_header_insert(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 = __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 = fastly_http_req_send_async(requestHandle, bodyHandle, changetype<usize>(backendBytes), backendBytes.byteLength, changetype<usize>(pendingOut)); if (status != FASTLY_STATUS_OK) { __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) }
|
|
1947
|
+
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 = __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
1948
|
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) }
|
|
1949
|
+
${assetBeginSource}
|
|
1505
1950
|
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
1951
|
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
1952
|
function __pulse_fastly_grip_payload(payload: __PulseFastlyValue): __PulseFastlyValue { return __pulse_fastly_value(__pulse_fastly_payload_field(payload, "payload")) }
|
|
@@ -1510,24 +1955,28 @@ function __pulse_fastly_grip_hold_begin(effectIndex: i32, payload: __PulseFastly
|
|
|
1510
1955
|
${gripSecretSource}
|
|
1511
1956
|
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
1957
|
${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 =
|
|
1958
|
+
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
1959
|
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
1960
|
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
1961
|
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
1962
|
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) }
|
|
1963
|
+
${assetResultSource}
|
|
1521
1964
|
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
1965
|
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) }
|
|
1966
|
+
function __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 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
1967
|
|
|
1525
1968
|
${jwtEvidenceSource}
|
|
1969
|
+
${terminalErrorSource}
|
|
1526
1970
|
${es256KeyArtifactSource(es256KeyRecords)}
|
|
1527
1971
|
${jwtCryptoCompositionSource(jwtCrypto)}
|
|
1972
|
+
${jwtSigningCryptoCompositionSource(jwtCrypto)}
|
|
1528
1973
|
${generateSchemaRuntime(plan)}
|
|
1529
1974
|
${effectResultSource(plan, bindings)}
|
|
1530
1975
|
`;
|
|
1976
|
+
if (!hasAssets) return source;
|
|
1977
|
+
const writer = 'status = __pulse_fastly_write_body(bodyHandle, value.text);';
|
|
1978
|
+
if (!source.includes(writer)) throw new Error('Embedded asset response writer boundary drift.');
|
|
1979
|
+
return source.replace(writer, 'status = value.binaryBody !== null ? __pulse_fastly_write_binary(bodyHandle, value.binaryBody!) : __pulse_fastly_write_body(bodyHandle, value.text);');
|
|
1531
1980
|
}
|
|
1532
1981
|
|
|
1533
1982
|
function driverSource(plan, options = {}) {
|
|
@@ -1544,6 +1993,13 @@ export function pulse_fastly_jwt_crypto_calls(): i32 { return __pulse_fastly_jwt
|
|
|
1544
1993
|
export function pulse_fastly_jwt_clock_calls(): i32 { return __pulse_fastly_jwt_clock_calls }
|
|
1545
1994
|
export function pulse_fastly_jwt_schema_calls(): i32 { return __pulse_fastly_jwt_schema_calls }
|
|
1546
1995
|
export function ${entryName}(): void {
|
|
1996
|
+
if (__pulse_invocation_started) { __pulse_fastly_fail(PULSE_ERROR_STATE, 171, -1); return }
|
|
1997
|
+
__pulse_invocation_started = true
|
|
1998
|
+
__pulse_fastly_run_invocation()
|
|
1999
|
+
${plan.effects.some(e => e.kind === 's3.getBody') ? '__s3_close_bodies()' : ''}
|
|
2000
|
+
__pulse_invocation_close()
|
|
2001
|
+
}
|
|
2002
|
+
function __pulse_fastly_run_invocation(): void {
|
|
1547
2003
|
__pulse_fastly_last_error = fastly_abi_init(PULSE_FASTLY_EFFECTS_ABI_VERSION)
|
|
1548
2004
|
if (__pulse_fastly_last_error != FASTLY_STATUS_OK) { __pulse_fastly_jwt_send_error(); return }
|
|
1549
2005
|
const handles = new StaticArray<i32>(2)
|
|
@@ -1557,15 +2013,16 @@ export function ${entryName}(): void {
|
|
|
1557
2013
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1558
2014
|
__pulse_fastly_request_path = __pulse_fastly_path(__pulse_fastly_request_url)
|
|
1559
2015
|
let runStatus = pulse_start()
|
|
1560
|
-
while (runStatus == 1 && __pulse_fastly_last_error == 0) {
|
|
2016
|
+
${packageApplication.enabled(plan) ? packageApplication.driverLoop() : applicationErrors.settlementEnabled(plan) ? applicationErrors.driverLoop(plan) : ` while (runStatus == 1 && __pulse_fastly_last_error == 0) {
|
|
1561
2017
|
for (let effectIndex = 0; effectIndex < PULSE_FASTLY_EFFECT_COUNT; effectIndex += 1) {
|
|
1562
2018
|
if (unchecked(__pulse_fastly_pending_mode[effectIndex]) == PULSE_FASTLY_PENDING_NONE) continue
|
|
2019
|
+
const invocation = unchecked(__pulse_invocation_tickets[effectIndex])
|
|
1563
2020
|
const result = __pulse_fastly_resolve_effect(effectIndex)
|
|
1564
2021
|
if (result <= 0 || __pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1565
|
-
if (
|
|
2022
|
+
if (__pulse_invocation_settle(effectIndex, invocation, result) != 1) { __pulse_fastly_fail(PULSE_ERROR_STATE, 101, effectIndex); __pulse_fastly_jwt_send_error(); return }
|
|
1566
2023
|
}
|
|
1567
2024
|
runStatus = pulse_resume()
|
|
1568
|
-
}
|
|
2025
|
+
}`}
|
|
1569
2026
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1570
2027
|
if (runStatus != 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 102, -1); __pulse_fastly_jwt_send_error(); return }
|
|
1571
2028
|
const result = pulse_result_handle()
|
|
@@ -1578,9 +2035,10 @@ export function ${entryName}(): void {
|
|
|
1578
2035
|
}
|
|
1579
2036
|
|
|
1580
2037
|
function capabilityImports(effect) {
|
|
2038
|
+
if (effect.kind === 'time.now') return ['wasi_snapshot_preview1.clock_time_get'];
|
|
1581
2039
|
if (effect.kind === 'config.get') return ['fastly_config_store.open', 'fastly_config_store.get'];
|
|
1582
2040
|
if (effect.kind === 'secret.get') return ['fastly_secret_store.open', 'fastly_secret_store.get', 'fastly_secret_store.plaintext'];
|
|
1583
|
-
if (
|
|
2041
|
+
if (['jwt.verify', 'jwt.sign'].includes(effect.kind)) {
|
|
1584
2042
|
return [
|
|
1585
2043
|
...(effect.resource && effect.resource.keyType === 'secret'
|
|
1586
2044
|
? [
|
|
@@ -1592,6 +2050,7 @@ function capabilityImports(effect) {
|
|
|
1592
2050
|
'wasi_snapshot_preview1.clock_time_get'
|
|
1593
2051
|
];
|
|
1594
2052
|
}
|
|
2053
|
+
if (conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) return conditionalKv.kvImports(effect.kind).map(key => key.replace(':', '.'));
|
|
1595
2054
|
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
2055
|
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
2056
|
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 +2058,7 @@ function capabilityImports(effect) {
|
|
|
1599
2058
|
return [
|
|
1600
2059
|
'fastly_http_req.new',
|
|
1601
2060
|
'fastly_http_req.send_async',
|
|
1602
|
-
'fastly_http_req.
|
|
2061
|
+
'fastly_http_req.pending_req_wait_v2',
|
|
1603
2062
|
...(effect.kind === 'grip.broadcast' ? ['fastly_secret_store.open', 'fastly_secret_store.get', 'fastly_secret_store.plaintext'] : [])
|
|
1604
2063
|
];
|
|
1605
2064
|
}
|
|
@@ -1611,7 +2070,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1611
2070
|
validatePlanBoundary(plan, options);
|
|
1612
2071
|
const facts = packageFacts(plan, options);
|
|
1613
2072
|
const jwtCrypto = selectedJwtCrypto(plan);
|
|
1614
|
-
const es256KeyRecords = jwtCrypto && jwtCrypto.algorithm
|
|
2073
|
+
const es256KeyRecords = jwtCrypto && ['ES256', 'RS256'].includes(jwtCrypto.algorithm)
|
|
1615
2074
|
? resolveEs256KeyArtifacts(plan, facts)
|
|
1616
2075
|
: Object.freeze([]);
|
|
1617
2076
|
if (jwtCrypto && jwtCrypto.guestUnitRequired && facts.guestUnits.length !== 1) {
|
|
@@ -1634,25 +2093,69 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1634
2093
|
}
|
|
1635
2094
|
);
|
|
1636
2095
|
}
|
|
2096
|
+
const packageDuration = plan.packages?.application?.requestLimits?.maxDurationMs;
|
|
2097
|
+
if (packageDuration !== undefined) options = { ...options, maxDurationMs: Math.min(options.maxDurationMs ?? packageDuration, packageDuration) };
|
|
1637
2098
|
const bindings = resolveCapabilityBindings(plan, options);
|
|
1638
|
-
|
|
2099
|
+
// Keep final provider eligibility authoritative over the portable artifact
|
|
2100
|
+
// and caller options. Only the selected crypto guest uses the linked policy;
|
|
2101
|
+
// that policy excludes active codecs regardless of JSON arena configuration.
|
|
2102
|
+
const numericRegions = facts.guestUnits.length === 0 ? 'fastly-numeric-regions-v2'
|
|
2103
|
+
: jwtCrypto && jwtCrypto.guestUnitRequired ? 'fastly-linked-numeric-regions-v1' : undefined;
|
|
2104
|
+
const portable = packageApplication.enabled(plan) ? packageApplication.portable(plan) : generateCanonicalNativeAssemblyScript(plan, {
|
|
2105
|
+
...options, numericRegions,
|
|
2106
|
+
handleOwnership: facts.guestUnits.length === 0 ? 'fastly-handle-tracing-v1' : undefined
|
|
2107
|
+
});
|
|
2108
|
+
nativeHandleOwnership.validateRoots(plan, portable.manifest);
|
|
1639
2109
|
const portableSource = stripPulseHostImports(portable.source);
|
|
1640
2110
|
const hasJwt = (plan.effects || []).some((effect) => effect.kind === 'jwt.verify');
|
|
1641
|
-
const
|
|
1642
|
-
const
|
|
2111
|
+
const hasSign = plan.effects.some(effect => effect.kind === 'jwt.sign');
|
|
2112
|
+
const jwtSource = hasJwt || hasSign ? pulseJwtAssemblyScriptSource({ includeSigning: hasSign }) : undefined;
|
|
2113
|
+
let source = [
|
|
1643
2114
|
'/* Generated by Pulse Pass98 Fastly native platform capability compiler. */',
|
|
1644
2115
|
'export function __pulse_fastly_abort(message: string | null, fileName: string | null = null, line: u32 = 0, column: u32 = 0): void { unreachable() }',
|
|
1645
2116
|
'',
|
|
1646
2117
|
fastlyRuntimeSource(plan, bindings, {
|
|
1647
2118
|
...options,
|
|
2119
|
+
handleTracing: portable.manifest.handleOwnership?.enabled === true,
|
|
1648
2120
|
jwtCrypto,
|
|
1649
2121
|
es256KeyRecords
|
|
1650
2122
|
}),
|
|
1651
2123
|
jwtSource ? jwtSource.source : '',
|
|
2124
|
+
((plan.effects || []).some(effect => ['fetch', 'grip.publish', 'grip.broadcast'].includes(effect.kind)) || bindings.s3.length > 0)
|
|
2125
|
+
? fs.readFileSync(path.join(__dirname, 'native-send-error-detail.as.ts'), 'utf8') : '',
|
|
2126
|
+
require('./s3-native.js').s3NativeSource(plan, bindings),
|
|
2127
|
+
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') : '',
|
|
2128
|
+
require('./time-native.js').timeNativeSource(plan),
|
|
2129
|
+
require('./digest-native.js').digestNativeSource(plan),
|
|
2130
|
+
conditionalKv.kvNativeSource(plan),
|
|
2131
|
+
require('./native-request-body.js').runtimeSource(),
|
|
2132
|
+
require('./native-request-headers.js').runtimeSource(),
|
|
2133
|
+
require('./native-header-lookup.js').runtimeSource(),
|
|
2134
|
+
fs.readFileSync(path.join(__dirname, 'native-transport-details.as.ts'), 'utf8'),
|
|
2135
|
+
require('./native-fetch-errors.js').runtimeSource(dynamicBackends.active(bindings)),
|
|
2136
|
+
require('./native-fetch-deadlines.js').runtimeSource(plan),
|
|
2137
|
+
applicationErrors.settlementEnabled(plan) ? applicationErrors.runtimeSource(plan) : '',
|
|
2138
|
+
effectInvocations.runtimeSource({ packageCompletion: packageApplication.enabled(plan) }),
|
|
2139
|
+
...(portable.manifest.numericRegions?.accounting === 'fastly-read-loop-v1' && portable.manifest.numericRegions.regions.length > 0
|
|
2140
|
+
? [nativeValueBudget.numericRegionSource()] : []),
|
|
1652
2141
|
portableSource,
|
|
2142
|
+
nativeHandleOwnership.providerSource(plan, portable.manifest.handleOwnership?.enabled === true),
|
|
1653
2143
|
driverSource(plan, { guestLinked: facts.guestUnits.length > 0 }),
|
|
1654
2144
|
''
|
|
1655
2145
|
].join('\n');
|
|
2146
|
+
if (applicationErrors.settlementEnabled(plan)) source = applicationErrors.instrument(source);
|
|
2147
|
+
source = require('./native-request-headers.js').instrument(source, applicationErrors.enabled(plan), /\bhost_request_headers\(/.test(portableSource));
|
|
2148
|
+
source = require('./native-header-lookup.js').instrument(source);
|
|
2149
|
+
source = require('./native-fetch-errors.js').instrument(source);
|
|
2150
|
+
source = require('./native-fetch-deadlines.js').instrument(source, plan);
|
|
2151
|
+
source = require('./native-request-body.js').instrument(source, applicationErrors.enabled(plan));
|
|
2152
|
+
if (packageApplication.enabled(plan)) source = packageApplication.instrument(source);
|
|
2153
|
+
source = effectInvocations.instrument(source);
|
|
2154
|
+
source = require('./request-budget.js').instrumentRequestBudget(source, bindings.maxDurationMs, plan);
|
|
2155
|
+
source = packageApplication.terminalFailure(source, plan);
|
|
2156
|
+
source = nativeValueBudget.instrument(source, plan);
|
|
2157
|
+
source = require('./native-body-forwarding.js').instrument(source, plan, bindings);
|
|
2158
|
+
source = dynamicBackends.instrument(source, bindings, true, (options.bindings || options).backends);
|
|
1656
2159
|
const effectKinds = Object.freeze((plan.effects || []).reduce((output, effect) => {
|
|
1657
2160
|
output[effect.kind] = (output[effect.kind] || 0) + 1;
|
|
1658
2161
|
return output;
|
|
@@ -1664,7 +2167,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1664
2167
|
capability: effect.capability,
|
|
1665
2168
|
package: effect.package,
|
|
1666
2169
|
contractId: effect.contractId,
|
|
1667
|
-
imports: Object.freeze(capabilityImports(effect))
|
|
2170
|
+
imports: Object.freeze([...capabilityImports(effect), ...(bindings.backends.some(b => b.index === index && b.dynamic) ? ['fastly_http_req.register_dynamic_backend'] : [])])
|
|
1668
2171
|
})));
|
|
1669
2172
|
const manifest = Object.freeze({
|
|
1670
2173
|
version: FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION,
|
|
@@ -1676,10 +2179,23 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1676
2179
|
portableGeneratorVersion: portable.version,
|
|
1677
2180
|
portableSourceHash: portable.sourceHash,
|
|
1678
2181
|
schemaCodecs: portable.manifest.schemaCodecs,
|
|
2182
|
+
...(portable.manifest.numericRegions ? { numericRegions: portable.manifest.numericRegions } : {}),
|
|
2183
|
+
handleOwnership: portable.manifest.handleOwnership ? Object.freeze({ ...portable.manifest.handleOwnership,
|
|
2184
|
+
providerRoots: portable.manifest.handleOwnership.enabled ? Object.freeze(nativeHandleOwnership.providerRoots(plan)) : Object.freeze([])
|
|
2185
|
+
}) : Object.freeze({
|
|
2186
|
+
policy: null, enabled: false,
|
|
2187
|
+
reason: packageApplication.enabled(plan) ? 'package-owned-emitter' : 'linked-runtime',
|
|
2188
|
+
roots: Object.freeze([]), checkpoints: 'none'
|
|
2189
|
+
}),
|
|
2190
|
+
handlerBodies: portable.manifest.handlerBodies,
|
|
2191
|
+
...(portable.manifest.reportOwnership ? { reportOwnership: portable.manifest.reportOwnership } : {}),
|
|
2192
|
+
dispatcher: portable.manifest.dispatcher,
|
|
2193
|
+
stages: portable.manifest.stages,
|
|
2194
|
+
helperBodies: portable.manifest.helperBodies,
|
|
1679
2195
|
crypto: portable.manifest.crypto,
|
|
1680
2196
|
jwt: jwtSource
|
|
1681
2197
|
? Object.freeze({
|
|
1682
|
-
status: jwtCrypto && jwtCrypto.algorithm
|
|
2198
|
+
status: jwtCrypto && ['ES256', 'RS256'].includes(jwtCrypto.algorithm)
|
|
1683
2199
|
? 'implemented-g4'
|
|
1684
2200
|
: 'implemented-e2',
|
|
1685
2201
|
package: jwtSource.owner,
|
|
@@ -1698,13 +2214,17 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1698
2214
|
})
|
|
1699
2215
|
: Object.freeze({ status: 'inactive' }),
|
|
1700
2216
|
effectCount: (plan.effects || []).length,
|
|
2217
|
+
effectInvocations: effectInvocations.policy,
|
|
1701
2218
|
effectKinds,
|
|
1702
2219
|
continuationCount: (plan.continuations || []).length,
|
|
1703
2220
|
groupedContinuationCount: (plan.continuations || []).filter((entry) => (entry.effectIds || []).length > 1).length,
|
|
1704
2221
|
bindings,
|
|
1705
2222
|
backends: bindings.backends,
|
|
1706
2223
|
capabilityDiagnostics,
|
|
1707
|
-
|
|
2224
|
+
...(dynamicBackends.active(bindings) ? { dynamicBackends: dynamicBackends.describe((options.bindings || options).backends) } : {}),
|
|
2225
|
+
allowedImports: dynamicBackends.active(bindings)
|
|
2226
|
+
? Object.freeze([...FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS, Object.freeze(['fastly_http_req', 'register_dynamic_backend'])])
|
|
2227
|
+
: FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS,
|
|
1708
2228
|
requiredImports: requiredImportsForPlan(plan, bindings),
|
|
1709
2229
|
requiredExports: Object.freeze([
|
|
1710
2230
|
'_start',
|
|
@@ -1712,27 +2232,30 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1712
2232
|
'pulse_fastly_error_stage',
|
|
1713
2233
|
'pulse_fastly_error_effect',
|
|
1714
2234
|
'pulse_plan_hash_ptr',
|
|
1715
|
-
'pulse_plan_hash_length'
|
|
2235
|
+
'pulse_plan_hash_length',
|
|
2236
|
+
...(bindings.maxDurationMs === undefined ? [] : ['pulse_fastly_request_expired'])
|
|
1716
2237
|
]),
|
|
1717
2238
|
policy: Object.freeze({
|
|
2239
|
+
...(dynamicBackends.active(bindings) ? { dynamicBackends: dynamicBackends.describe((options.bindings || options).backends) } : {}),
|
|
2240
|
+
...(nativeValueBudget.hasBoundedReadLoop(plan) ? { readLoopMemory: nativeValueBudget.policy } : {}),
|
|
1718
2241
|
nativeFastly: true,
|
|
1719
2242
|
provider: 'fastly',
|
|
1720
2243
|
providerNeutralInput: true,
|
|
1721
2244
|
javascriptRuntime: false,
|
|
1722
2245
|
jsComputeRuntime: false,
|
|
1723
|
-
wasi:
|
|
1724
|
-
effects: '
|
|
2246
|
+
wasi: requiredImportsForPlan(plan, bindings).filter(key => key.startsWith('wasi_snapshot_preview1:')).map(key => key.split(':')[1]).join(', ').replace(/(.+)/, '$1 only') || false,
|
|
2247
|
+
effects: FASTLY_NATIVE_PLATFORM_EFFECT_KINDS.join(', '),
|
|
1725
2248
|
continuations: true,
|
|
1726
2249
|
groupedEffects: 'existing fetch start-before-wait ordering remains intact',
|
|
1727
2250
|
config: 'Fastly Config Store open/get with undefined for missing values',
|
|
1728
2251
|
secrets: 'Fastly Secret Store open/get/plaintext; proof traces redact plaintext values',
|
|
1729
|
-
jwt: hasJwt
|
|
1730
|
-
? `package-owned compact-JWS
|
|
2252
|
+
jwt: hasJwt || hasSign
|
|
2253
|
+
? `package-owned compact-JWS, registered-claim, and bounded issuance semantics composed with ${jwtCrypto.realization}; provider-owned request, secret, clock, schema, and transport`
|
|
1731
2254
|
: 'inactive',
|
|
1732
|
-
kv: 'Fastly KV Store async lookup/insert with Pulse
|
|
2255
|
+
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
2256
|
assets: 'Fastly KV Store lookup with provider-owned content type, cache headers, and GET/HEAD body ownership',
|
|
1734
2257
|
grip: 'canonical stateless framing plus bound, secret-referenced broadcast HTTP realization and stable acknowledgement',
|
|
1735
|
-
logging: 'best-effort redacted
|
|
2258
|
+
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
2259
|
schemas: 'fetch JSON decode and structured response encode',
|
|
1737
2260
|
opaqueResponse: 'origin fetch response/body handles and direct GRIP hold responses pass through without JavaScript runtime ownership',
|
|
1738
2261
|
transportErrors: 'host status failures remain distinct from HTTP response status',
|
|
@@ -1763,8 +2286,9 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1763
2286
|
});
|
|
1764
2287
|
}
|
|
1765
2288
|
|
|
1766
|
-
function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
2289
|
+
function inspectFastlyNativePlatformCapabilitiesWasm(input, options = {}) {
|
|
1767
2290
|
const bytes = Buffer.isBuffer(input) ? input : fs.readFileSync(path.resolve(input));
|
|
2291
|
+
assertFastlyWasmBudget(bytes.length, options.maxWasmBytes);
|
|
1768
2292
|
if (!WebAssembly.validate(bytes)) fail('Generated Fastly native platform capabilities module is not valid WebAssembly.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_WASM_INVALID');
|
|
1769
2293
|
const module = new WebAssembly.Module(bytes);
|
|
1770
2294
|
const imports = WebAssembly.Module.imports(module).map((entry) => Object.freeze({ module: entry.module, name: entry.name, kind: entry.kind }));
|
|
@@ -1780,7 +2304,6 @@ function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
|
1780
2304
|
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
2305
|
const missingExports = requiredExports.filter((name) => !exportNames.has(name));
|
|
1782
2306
|
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
2307
|
return Object.freeze({
|
|
1785
2308
|
valid: true,
|
|
1786
2309
|
bytes: bytes.length,
|
|
@@ -1793,6 +2316,7 @@ function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
|
1793
2316
|
}
|
|
1794
2317
|
|
|
1795
2318
|
function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
2319
|
+
const maxWasmBytes = normalizeFastlyMaxWasmBytes(options.maxWasmBytes);
|
|
1796
2320
|
const generated = generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options);
|
|
1797
2321
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
1798
2322
|
const compilerFallback = path.resolve(__dirname, '..', '..', '..', '..', 'wasm', 'packages', 'compiler');
|
|
@@ -1825,6 +2349,8 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1825
2349
|
}
|
|
1826
2350
|
jsonAs = Object.freeze({ transform, dependencyRoot, version: packageJson.version });
|
|
1827
2351
|
}
|
|
2352
|
+
if (options.emitWat !== undefined && typeof options.emitWat !== 'boolean') throw new TypeError('emitWat must be a boolean.');
|
|
2353
|
+
const emitWat = options.emitWat === true;
|
|
1828
2354
|
const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-fastly-native-platform-'));
|
|
1829
2355
|
const requestedOutDir = options.outDir ? path.resolve(options.outDir) : undefined;
|
|
1830
2356
|
const outputDir = requestedOutDir || stagingDir;
|
|
@@ -1832,6 +2358,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1832
2358
|
const sourceFile = path.join(stagingDir, 'fastly-native-platform-capabilities.as.ts');
|
|
1833
2359
|
const wasmFile = path.join(outputDir, options.wasmFile || 'fastly-native-platform-capabilities.wasm');
|
|
1834
2360
|
const watFile = path.join(outputDir, options.watFile || 'fastly-native-platform-capabilities.wat');
|
|
2361
|
+
if (!emitWat) fs.rmSync(watFile, { force: true });
|
|
1835
2362
|
const guestLinked = generated.guestUnits.length > 0;
|
|
1836
2363
|
// asc resolves custom runtimes like module names and appends ".ts".
|
|
1837
2364
|
const fixedMemoryRuntimeModule = path.join(__dirname, 'fixed-memory-runtime.as');
|
|
@@ -1848,14 +2375,17 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1848
2375
|
asc.script,
|
|
1849
2376
|
path.basename(sourceFile),
|
|
1850
2377
|
'--outFile', assemblyScriptWasmFile,
|
|
1851
|
-
'--textFile', assemblyScriptWatFile,
|
|
1852
2378
|
'--runtime', guestLinked
|
|
1853
2379
|
? fixedMemoryRuntimeModule
|
|
1854
2380
|
: (schemaCodecsActive ? 'incremental' : 'stub'),
|
|
1855
2381
|
'--noAssert',
|
|
1856
2382
|
'--optimize'
|
|
1857
2383
|
];
|
|
1858
|
-
|
|
2384
|
+
if (emitWat) args.push('--textFile', assemblyScriptWatFile);
|
|
2385
|
+
const optimization = appendAssemblyScriptOptimizationArgs(args, nativeOptimization, { guestLinked });
|
|
2386
|
+
if (!guestLinked && (plan.callables?.length || nativeValueBudget.hasBoundedReadLoop(plan) || plan.effects.some(effect => effect.kind === 'crypto.digestText' || effect.kind.startsWith('s3.')))) {
|
|
2387
|
+
args.push('--maximumMemory', String(nativeValueBudget.policy.maximumMemoryPages));
|
|
2388
|
+
}
|
|
1859
2389
|
if (generated.guestUnits.length > 0) {
|
|
1860
2390
|
args.push(
|
|
1861
2391
|
'--disable', 'bulk-memory',
|
|
@@ -1878,17 +2408,20 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1878
2408
|
'--path', jsonAs.dependencyRoot
|
|
1879
2409
|
);
|
|
1880
2410
|
}
|
|
1881
|
-
|
|
2411
|
+
require('@pulse-compute/wasm-build-support/native-optimization').appendJsonCacheArenaArgs(args, options.elideJsonCacheArena, schemaCodecsActive);
|
|
2412
|
+
const readReportCapture = options.reportCapture === false ? () => null : prepareReportCapture(args, stagingDir, generated.manifest,
|
|
2413
|
+
require(path.join(asc.packageRoot, 'package.json')).version, 'fastly-native-platform-capabilities.as/');
|
|
2414
|
+
const startedAt = process.hrtime.bigint();
|
|
1882
2415
|
const result = spawnSync(asc.executable, args, {
|
|
1883
2416
|
cwd: stagingDir,
|
|
1884
2417
|
encoding: 'utf8',
|
|
1885
2418
|
maxBuffer: 64 * 1024 * 1024,
|
|
1886
2419
|
timeout: Number(options.timeoutMs || 180000),
|
|
1887
2420
|
env: schemaCodecsActive
|
|
1888
|
-
? { ...process.env, JSON_STRICT: 'true', JSON_USE_FAST_PATH: '0' }
|
|
2421
|
+
? { ...process.env, JSON_STRICT: 'true', JSON_USE_FAST_PATH: '0', JSON_MODE: 'NAIVE' }
|
|
1889
2422
|
: process.env
|
|
1890
2423
|
});
|
|
1891
|
-
const assemblyScriptDurationMs =
|
|
2424
|
+
const assemblyScriptDurationMs = Math.round(Number(process.hrtime.bigint() - startedAt) / 1e6);
|
|
1892
2425
|
if (result.error || result.status !== 0 || !fs.existsSync(assemblyScriptWasmFile)) {
|
|
1893
2426
|
fail('AssemblyScript failed to compile the Fastly native platform capabilities module.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_ASC_FAILED', {
|
|
1894
2427
|
status: result.status,
|
|
@@ -1932,7 +2465,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1932
2465
|
failureMessage: 'Fastly Native start wrapper composition failed.'
|
|
1933
2466
|
}
|
|
1934
2467
|
);
|
|
1935
|
-
runGuestTool(
|
|
2468
|
+
if (emitWat) runGuestTool(
|
|
1936
2469
|
'wasm-dis',
|
|
1937
2470
|
[wasmFile, '--mvp-features', '-o', watFile],
|
|
1938
2471
|
{
|
|
@@ -1962,7 +2495,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1962
2495
|
});
|
|
1963
2496
|
}
|
|
1964
2497
|
const primaryWasm = fs.readFileSync(wasmFile);
|
|
1965
|
-
const primaryWat =
|
|
2498
|
+
const primaryWat = emitWat ? fs.readFileSync(watFile, 'utf8') : '';
|
|
1966
2499
|
const primaryArtifact = Object.freeze({
|
|
1967
2500
|
bytes: primaryWasm.length,
|
|
1968
2501
|
sha256: sha256(primaryWasm)
|
|
@@ -1995,7 +2528,8 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1995
2528
|
realization = Object.freeze({
|
|
1996
2529
|
...realization,
|
|
1997
2530
|
wasm: stageResult.wasm,
|
|
1998
|
-
|
|
2531
|
+
// Required guest-link disassembly audits remain enabled.
|
|
2532
|
+
wat: emitWat ? stageResult.wat : '',
|
|
1999
2533
|
guestUnits: stageResult.guestUnits,
|
|
2000
2534
|
guestLink: Object.freeze({
|
|
2001
2535
|
version: stageResult.version,
|
|
@@ -2013,9 +2547,9 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2013
2547
|
const wat = realization.wat;
|
|
2014
2548
|
if (requestedOutDir && generated.guestUnits.length > 0) {
|
|
2015
2549
|
fs.writeFileSync(wasmFile, wasm);
|
|
2016
|
-
fs.writeFileSync(watFile, wat, 'utf8');
|
|
2550
|
+
if (emitWat) fs.writeFileSync(watFile, wat, 'utf8');
|
|
2017
2551
|
}
|
|
2018
|
-
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm);
|
|
2552
|
+
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm, { maxWasmBytes });
|
|
2019
2553
|
const imported = new Set(inspection.imports.map((entry) => `${entry.module}:${entry.name}`));
|
|
2020
2554
|
const requiredImports = requiredImportsForPlan(plan, generated.bindings);
|
|
2021
2555
|
const missingRequiredImports = requiredImports.filter((key) => !imported.has(key));
|
|
@@ -2025,11 +2559,13 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2025
2559
|
compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
|
|
2026
2560
|
assemblyScript: Object.freeze({ package: 'assemblyscript', version: require(path.join(asc.packageRoot, 'package.json')).version }),
|
|
2027
2561
|
jsonAs: jsonAs
|
|
2028
|
-
? Object.freeze({ package: 'json-as', version: jsonAs.version, transform: true, strict: true })
|
|
2562
|
+
? Object.freeze({ package: 'json-as', version: jsonAs.version, transform: true, strict: true, mode: 'NAIVE', fastPath: false, elideDisabledCacheArena: options.elideJsonCacheArena === true })
|
|
2029
2563
|
: undefined,
|
|
2030
2564
|
optimization,
|
|
2031
2565
|
wasm: Object.freeze({ bytes: inspection.bytes, sha256: inspection.sha256, magic: inspection.magic }),
|
|
2032
|
-
wat: Object.freeze(
|
|
2566
|
+
wat: Object.freeze(emitWat
|
|
2567
|
+
? { emitted: true, bytes: Buffer.byteLength(wat), sha256: sha256(wat) }
|
|
2568
|
+
: { emitted: false, bytes: 0, sha256: null }),
|
|
2033
2569
|
importModules: inspection.importModules,
|
|
2034
2570
|
imports: inspection.imports,
|
|
2035
2571
|
requiredImports,
|
|
@@ -2043,10 +2579,17 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2043
2579
|
providerPackaging: realization.guestLink.providerPackaging
|
|
2044
2580
|
} : {})
|
|
2045
2581
|
});
|
|
2046
|
-
const durationMs =
|
|
2582
|
+
const durationMs = Math.round(Number(process.hrtime.bigint() - startedAt) / 1e6);
|
|
2047
2583
|
return Object.freeze({
|
|
2048
2584
|
version: FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION,
|
|
2049
2585
|
compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
|
|
2586
|
+
reportAttribution: readReportCapture(wasm),
|
|
2587
|
+
buildObservation: require('@pulse-compute/wasm-build-support/native-optimization').observeAssemblyScript(args, {
|
|
2588
|
+
version: require(path.join(asc.packageRoot, 'package.json')).version,
|
|
2589
|
+
assemblyScriptRoot: asc.packageRoot, timeoutMs: Number(options.timeoutMs || 180000),
|
|
2590
|
+
compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
|
|
2591
|
+
durationMs: assemblyScriptDurationMs, jsonAsVersion: jsonAs && jsonAs.version
|
|
2592
|
+
}),
|
|
2050
2593
|
plan,
|
|
2051
2594
|
generated,
|
|
2052
2595
|
source: generated.source,
|
|
@@ -2062,7 +2605,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2062
2605
|
assemblyScriptDurationMs,
|
|
2063
2606
|
primaryArtifact,
|
|
2064
2607
|
startWrapper,
|
|
2065
|
-
output: requestedOutDir ? Object.freeze({ outDir: outputDir, wasmFile, watFile }) : undefined
|
|
2608
|
+
output: requestedOutDir ? Object.freeze({ outDir: outputDir, wasmFile, watFile: emitWat ? watFile : null }) : undefined
|
|
2066
2609
|
});
|
|
2067
2610
|
} finally {
|
|
2068
2611
|
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
@@ -2080,10 +2623,12 @@ function writeFastlyNativePlatformCapabilitiesModule(compiled, outDir, options =
|
|
|
2080
2623
|
const manifestFile = path.join(target, options.manifestFile || 'fastly-native-platform-capabilities-manifest.json');
|
|
2081
2624
|
fs.writeFileSync(sourceFile, compiled.source, 'utf8');
|
|
2082
2625
|
fs.writeFileSync(wasmFile, compiled.wasm);
|
|
2083
|
-
|
|
2626
|
+
const emitWat = compiled.manifest.wat.emitted !== false;
|
|
2627
|
+
if (emitWat) fs.writeFileSync(watFile, compiled.wat, 'utf8');
|
|
2628
|
+
else fs.rmSync(watFile, { force: true });
|
|
2084
2629
|
fs.writeFileSync(planFile, `${stableStringify(compiled.plan, 2)}\n`, 'utf8');
|
|
2085
2630
|
fs.writeFileSync(manifestFile, `${stableStringify(compiled.manifest, 2)}\n`, 'utf8');
|
|
2086
|
-
return Object.freeze({ target, sourceFile, wasmFile, watFile, planFile, manifestFile, manifest: compiled.manifest });
|
|
2631
|
+
return Object.freeze({ target, sourceFile, wasmFile, watFile: emitWat ? watFile : null, planFile, manifestFile, manifest: compiled.manifest });
|
|
2087
2632
|
}
|
|
2088
2633
|
|
|
2089
2634
|
module.exports = Object.freeze({
|