@pulse-compute/provider-fastly 1.0.0-beta.4 → 1.0.0-beta.6
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 +6 -6
- package/dist/config-schema.json +10 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/build/canonical-target.js +15 -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/kv-native.as.ts +18 -8
- package/src/build/native-application-errors.js +140 -0
- package/src/build/native-http-effects.js +87 -22
- package/src/build/native-platform-capabilities.js +480 -151
- package/src/build/native-request-body.js +44 -0
- package/src/build/native-request-headers.js +93 -0
- package/src/build/native-string-values.js +113 -0
- package/src/build/native-value-budget.js +58 -0
- package/src/build/package-native-application.js +94 -0
- package/src/build/request-budget.as.ts +49 -0
- package/src/build/request-budget.js +45 -0
- package/src/build/s3-body.as.ts +97 -0
- package/src/build/s3-native.as.ts +12 -4
- package/src/build/s3-native.js +2 -2
- 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 +3 -0
- package/src/config-schema.json +10 -0
- package/src/index.ts +9 -0
- package/src/javascript/jwt-verifier.js +14 -4
- package/src/javascript/package-effects.js +5 -2
- package/src/javascript/runtime-host.js +5 -2
- package/src/javascript/target-support-policy.js +18 -5
- package/src/javascript/target.js +16 -1
- package/src/provider-contract.js +7 -3
- package/src/runtime/canonical-api-runtime.js +2 -0
- package/src/testing/native-http-effects-host.js +5 -1
- package/src/testing/native-platform-capabilities-host.js +78 -6
- package/src/toolchain/config.js +8 -1
- package/src/toolchain/index.js +31 -2
- package/src/toolchain/native-execution.js +1 -0
- package/src/toolchain/s3.js +2 -2
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { scalarRecordProjectionLines } = require('./schema-scalar-record.js');
|
|
4
|
+
const { hasJsonAdmission, tracksJsonDuplicates, schemaParserDepth, nestedJsonProjectionLines, schemaJsonAdmissionSource, schemaFetchJsonSource } = require('./schema-nested-json.js');
|
|
5
|
+
const applicationErrors = require('./native-application-errors.js');
|
|
6
|
+
const effectInvocations = require('./effect-invocations.js');
|
|
7
|
+
const packageApplication = require('./package-native-application.js');
|
|
8
|
+
const nativeValueBudget = require('./native-value-budget.js');
|
|
9
|
+
const { schemaNeedsValueProjection } = require('@pulse-compute/wasm-contracts/schema-json/registry');
|
|
10
|
+
|
|
3
11
|
const crypto = require('node:crypto');
|
|
12
|
+
const { nativeStringFields, nativeStringConcat, nativeStringTrim, nativeStringIndex, needsNativeValueFailureGuard } = require('./native-string-values.js');
|
|
4
13
|
const conditionalKv = require('./kv-native.js');
|
|
5
14
|
const fs = require('node:fs');
|
|
6
15
|
const os = require('node:os');
|
|
@@ -90,7 +99,10 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION = 'pulse.fastly-native-platfor
|
|
|
90
99
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_GENERATOR_VERSION = 'pulse.fastly-native-platform-capabilities-generator.v1';
|
|
91
100
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION = 'pulse.fastly-native-platform-capabilities-compiler.v1';
|
|
92
101
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION = 1;
|
|
93
|
-
|
|
102
|
+
// Internal compilation budget, not a Fastly service limit. Keep a finite guard
|
|
103
|
+
// while allowing composed applications beyond the original 1 MiB proof fixture.
|
|
104
|
+
const { DEFAULT_FASTLY_MAX_WASM_BYTES, normalizeFastlyMaxWasmBytes, assertFastlyWasmBudget } = require('./wasm-budget.js');
|
|
105
|
+
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_MAX_WASM_BYTES = DEFAULT_FASTLY_MAX_WASM_BYTES;
|
|
94
106
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES = 65536;
|
|
95
107
|
const FASTLY_NATIVE_SIZE_OPTIMIZATION = EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION;
|
|
96
108
|
|
|
@@ -102,6 +114,8 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
|
|
|
102
114
|
Object.freeze(['fastly_http_req', 'method_get']),
|
|
103
115
|
Object.freeze(['fastly_http_req', 'uri_get']),
|
|
104
116
|
Object.freeze(['fastly_http_req', 'header_value_get']),
|
|
117
|
+
Object.freeze(['fastly_http_req', 'header_names_get']),
|
|
118
|
+
Object.freeze(['fastly_http_req', 'header_values_get']),
|
|
105
119
|
Object.freeze(['fastly_http_req', 'new']),
|
|
106
120
|
Object.freeze(['fastly_http_req', 'method_set']),
|
|
107
121
|
Object.freeze(['fastly_http_req', 'uri_set']),
|
|
@@ -152,12 +166,16 @@ const FASTLY_NATIVE_PLATFORM_EFFECT_KIND = Object.freeze({
|
|
|
152
166
|
'grip.broadcast': 9,
|
|
153
167
|
'assets.lookup': 10,
|
|
154
168
|
'jwt.verify': 11,
|
|
169
|
+
'jwt.sign': 20,
|
|
155
170
|
's3.head': 12,
|
|
156
171
|
's3.getText': 13,
|
|
157
172
|
's3.putText': 14,
|
|
173
|
+
's3.getBody': 21,
|
|
158
174
|
'kv.getVersioned': 15,
|
|
159
175
|
'kv.insertIfAbsent': 16,
|
|
160
|
-
'kv.compareAndSwap': 17
|
|
176
|
+
'kv.compareAndSwap': 17,
|
|
177
|
+
'time.now': 18,
|
|
178
|
+
'crypto.digestText': 19
|
|
161
179
|
});
|
|
162
180
|
|
|
163
181
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS = new Set([
|
|
@@ -165,6 +183,8 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS = new Set([
|
|
|
165
183
|
]);
|
|
166
184
|
|
|
167
185
|
const FASTLY_NATIVE_PLATFORM_EFFECT_KINDS = Object.freeze([
|
|
186
|
+
'time.now',
|
|
187
|
+
'crypto.digestText',
|
|
168
188
|
'fetch',
|
|
169
189
|
'config.get',
|
|
170
190
|
'secret.get',
|
|
@@ -177,12 +197,16 @@ const FASTLY_NATIVE_PLATFORM_EFFECT_KINDS = Object.freeze([
|
|
|
177
197
|
'grip.broadcast',
|
|
178
198
|
'assets.lookup',
|
|
179
199
|
'jwt.verify',
|
|
200
|
+
'jwt.sign',
|
|
180
201
|
's3.head',
|
|
181
202
|
's3.getText',
|
|
182
|
-
's3.putText'
|
|
203
|
+
's3.putText',
|
|
204
|
+
's3.getBody'
|
|
183
205
|
]);
|
|
184
206
|
|
|
185
207
|
const FASTLY_NATIVE_PLATFORM_CAPABILITY_KINDS = new Set([
|
|
208
|
+
'time.now',
|
|
209
|
+
'crypto.digestText',
|
|
186
210
|
'config.get',
|
|
187
211
|
'secret.get',
|
|
188
212
|
'kv.get',
|
|
@@ -194,9 +218,11 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITY_KINDS = new Set([
|
|
|
194
218
|
'grip.publish',
|
|
195
219
|
'grip.broadcast',
|
|
196
220
|
'jwt.verify',
|
|
221
|
+
'jwt.sign',
|
|
197
222
|
's3.head',
|
|
198
223
|
's3.getText',
|
|
199
|
-
's3.putText'
|
|
224
|
+
's3.putText',
|
|
225
|
+
's3.getBody'
|
|
200
226
|
]);
|
|
201
227
|
|
|
202
228
|
class FastlyNativePlatformCapabilitiesError extends Error {
|
|
@@ -258,45 +284,38 @@ function packageFacts(_plan, options = {}) {
|
|
|
258
284
|
});
|
|
259
285
|
}
|
|
260
286
|
|
|
287
|
+
function jwtVerificationAlgorithm(effect) {
|
|
288
|
+
return effect.resource.keyType === 'secret' ? 'HS256' : String(effect.resource.keyArtifactId).startsWith('pulse.jwt.rs256-key.') ? 'RS256' : 'ES256';
|
|
289
|
+
}
|
|
290
|
+
|
|
261
291
|
function selectedJwtCrypto(plan) {
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
&& selected.kind === 'guest-linked'
|
|
284
|
-
&& selected.targetImplemented === true;
|
|
285
|
-
if ((!hs256 && !es256) || selected.automaticFallback !== false) {
|
|
286
|
-
fail(
|
|
287
|
-
'Fastly Native JWT selected an unavailable or non-exact crypto realization.',
|
|
288
|
-
'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID',
|
|
289
|
-
{ selected, automaticFallback: false }
|
|
290
|
-
);
|
|
292
|
+
const effects = plan.effects || [];
|
|
293
|
+
const signing = effects.filter(effect => effect.kind === 'jwt.sign');
|
|
294
|
+
const verifies = effects.filter(effect => effect.kind === 'jwt.verify');
|
|
295
|
+
const required = new Set([
|
|
296
|
+
...signing.map(effect => literalObjectField(inputExpression(effect, 'payload'), 'algorithm') === 'HS256' ? 'HMAC-SHA256' : literalObjectField(inputExpression(effect, 'payload'), 'algorithm')),
|
|
297
|
+
...verifies.map(effect => jwtVerificationAlgorithm(effect))
|
|
298
|
+
]);
|
|
299
|
+
if (!required.size) return undefined;
|
|
300
|
+
const definitions = {
|
|
301
|
+
'HMAC-SHA256': ['guest-source:pulse-hmac-as', 'pulse-hmac-as.v1', 'guest-source'],
|
|
302
|
+
HS256: ['guest-source:pulse-hmac-as', 'pulse-hmac-as.v1', 'guest-source'],
|
|
303
|
+
RS256: ['guest-linked:pulse-rs256-bearssl-i31', 'bearssl.0.6.rsa-i31.sha256.v1', 'guest-linked'],
|
|
304
|
+
ES256: ['guest-linked:pulse-es256-rustcrypto-p256', 'rustcrypto.p256-0.13.2.ecdsa-0.16.9.sha2-0.10.9.v1', 'guest-linked']
|
|
305
|
+
};
|
|
306
|
+
for (const algorithm of required) {
|
|
307
|
+
const selected = plan.crypto?.algorithms.find(entry => entry.algorithm === algorithm);
|
|
308
|
+
const expected = definitions[algorithm];
|
|
309
|
+
if (!expected || !selected || selected.realization !== expected[0] || selected.implementation !== expected[1]
|
|
310
|
+
|| selected.kind !== expected[2] || selected.targetImplemented !== true || selected.automaticFallback !== false) {
|
|
311
|
+
fail('JWT requires its explicitly selected Crypto realization.', 'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID');
|
|
312
|
+
}
|
|
291
313
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
guestUnitRequired: es256,
|
|
298
|
-
automaticFallback: false
|
|
299
|
-
});
|
|
314
|
+
const verificationAlgorithms = [...new Set(verifies.map(effect => jwtVerificationAlgorithm(effect)))];
|
|
315
|
+
if (verificationAlgorithms.length > 1) fail('Fastly Native JWT verification requires one exact algorithm.', 'PULSE_FASTLY_NATIVE_JWT_CRYPTO_REALIZATION_INVALID');
|
|
316
|
+
const algorithm = verificationAlgorithms[0] || (required.has('RS256') ? 'RS256' : required.has('ES256') ? 'ES256' : 'HMAC-SHA256');
|
|
317
|
+
return Object.freeze({ ...plan.crypto.algorithms.find(entry => entry.algorithm === algorithm),
|
|
318
|
+
guestUnitRequired: required.has('ES256') || required.has('RS256'), signingAlgorithms: signing.map(effect => literalObjectField(inputExpression(effect, 'payload'), 'algorithm')) });
|
|
300
319
|
}
|
|
301
320
|
|
|
302
321
|
function decodeP256Coordinate(value, artifactId, field) {
|
|
@@ -318,6 +337,26 @@ function decodeP256Coordinate(value, artifactId, field) {
|
|
|
318
337
|
return bytes;
|
|
319
338
|
}
|
|
320
339
|
|
|
340
|
+
function normalizedRs256Jwk(value, artifactId) {
|
|
341
|
+
const invalid = () => fail('Invalid bounded RSA public key artifact.', 'PULSE_FASTLY_NATIVE_JWT_KEY_ARTIFACT_INVALID', { artifactId });
|
|
342
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)
|
|
343
|
+
|| Object.keys(value).some(name => !['kty', 'n', 'e', 'alg', 'use', 'key_ops', 'kid'].includes(name))
|
|
344
|
+
|| value.kty !== 'RSA' || value.alg !== undefined && value.alg !== 'RS256'
|
|
345
|
+
|| value.use !== undefined && value.use !== 'sig'
|
|
346
|
+
|| value.key_ops !== undefined && (!Array.isArray(value.key_ops) || value.key_ops.length !== 1 || value.key_ops[0] !== 'verify')
|
|
347
|
+
|| value.kid !== undefined && (typeof value.kid !== 'string' || !value.kid.length || value.kid.includes('\0') || Buffer.byteLength(value.kid) > 256)) invalid();
|
|
348
|
+
const decode = (text, maximum) => {
|
|
349
|
+
if (typeof text !== 'string' || text.length > Math.ceil(maximum * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(text)) invalid();
|
|
350
|
+
const bytes = Buffer.from(text, 'base64url');
|
|
351
|
+
if (!bytes.length || bytes.length > maximum || bytes[0] === 0 || bytes.toString('base64url') !== text) invalid();
|
|
352
|
+
return bytes;
|
|
353
|
+
};
|
|
354
|
+
const n = decode(value.n, 512), e = decode(value.e, 4), k = n.length;
|
|
355
|
+
if (![256, 384, 512].includes(k) || n[0] < 128 || !(n[k-1] & 1) || e.readUIntBE(0, e.length) < 3 || !(e.at(-1) & 1)) invalid();
|
|
356
|
+
const bytes = Buffer.alloc(8+k); bytes.writeUInt32LE(k); n.copy(bytes, 4); e.copy(bytes, 8+k-e.length);
|
|
357
|
+
return Object.freeze({ kid: value.kid, bytes });
|
|
358
|
+
}
|
|
359
|
+
|
|
321
360
|
function normalizedEs256Jwk(value, artifactId) {
|
|
322
361
|
if (
|
|
323
362
|
!value
|
|
@@ -364,13 +403,14 @@ function resolveEs256KeyArtifacts(plan, facts) {
|
|
|
364
403
|
}
|
|
365
404
|
const artifactId = effect.resource.keyArtifactId;
|
|
366
405
|
const artifact = artifacts.get(artifactId);
|
|
406
|
+
const algorithm = jwtVerificationAlgorithm(effect).toLowerCase();
|
|
367
407
|
if (
|
|
368
408
|
!artifact
|
|
369
|
-
|| artifact.version !==
|
|
409
|
+
|| artifact.version !== `pulse.jwt-${algorithm}-key-artifact.v1`
|
|
370
410
|
|| artifact.contractId !== 'pulse.jwt'
|
|
371
411
|
|| artifact.package !== '@pulse-compute/jwt'
|
|
372
|
-
|| artifact.kind !==
|
|
373
|
-
|| artifact.mediaType !==
|
|
412
|
+
|| artifact.kind !== `jwt-${algorithm}-static-public-key`
|
|
413
|
+
|| artifact.mediaType !== `application/vnd.pulse.jwt-${algorithm}-key+json`
|
|
374
414
|
|| artifact.id !== artifactId
|
|
375
415
|
|| artifact.materialHash !== sha256(stableStringify(artifact.data))
|
|
376
416
|
|| !artifact.data
|
|
@@ -396,7 +436,7 @@ function resolveEs256KeyArtifacts(plan, facts) {
|
|
|
396
436
|
{ effectId: effect.id, artifactId, keyCount: keys && keys.length }
|
|
397
437
|
);
|
|
398
438
|
}
|
|
399
|
-
const normalized = keys.map((key) => normalizedEs256Jwk(key, artifactId));
|
|
439
|
+
const normalized = keys.map((key) => algorithm === 'rs256' ? normalizedRs256Jwk(key, artifactId) : normalizedEs256Jwk(key, artifactId));
|
|
400
440
|
const kids = normalized.map((key) => key.kid).filter((kid) => kid !== undefined);
|
|
401
441
|
if (new Set(kids).size !== kids.length) {
|
|
402
442
|
fail(
|
|
@@ -472,8 +512,71 @@ function es256KeyArtifactSource(records) {
|
|
|
472
512
|
return lines.join('\n');
|
|
473
513
|
}
|
|
474
514
|
|
|
515
|
+
function jwtSigningCryptoCompositionSource(selected) {
|
|
516
|
+
const algorithms = selected?.signingAlgorithms || [];
|
|
517
|
+
const hs = algorithms.includes('HS256');
|
|
518
|
+
const es = algorithms.includes('ES256');
|
|
519
|
+
const rs = algorithms.includes('RS256');
|
|
520
|
+
return `function __pulse_fastly_jwt_crypto_sign(algorithm: string, key: Uint8Array, data: Uint8Array, output: Uint8Array): i32 {
|
|
521
|
+
${hs ? `if (algorithm == "HS256") return pulse_crypto_hmac_sha256_bytes_v1(key.dataStart, key.length, data.dataStart, data.length, output.dataStart, output.length)` : ''}
|
|
522
|
+
${rs ? `if (algorithm == "RS256") return __pulse_fastly_rs256(true, key, data, output)` : ''}
|
|
523
|
+
${es ? `if (algorithm == "ES256") {
|
|
524
|
+
if (key.length != 96 || output.length != 64 || data.length > 16340) return -2
|
|
525
|
+
const pointer: usize = 524288
|
|
526
|
+
memory.fill(pointer, 0, 16640)
|
|
527
|
+
store<u32>(pointer, 0x32534550); store<u32>(pointer + 4, 2); store<u32>(pointer + 8, 64)
|
|
528
|
+
store<u32>(pointer + 12, (224 + data.length + 15) & ~15); store<u32>(pointer + 16, 1)
|
|
529
|
+
store<u32>(pointer + 24, 224); store<u32>(pointer + 28, data.length)
|
|
530
|
+
store<u32>(pointer + 32, 64); store<u32>(pointer + 36, 96)
|
|
531
|
+
store<u32>(pointer + 40, 160); store<u32>(pointer + 44, 64)
|
|
532
|
+
memory.copy(pointer + 64, key.dataStart, 96); memory.copy(pointer + 224, data.dataStart, data.length)
|
|
533
|
+
const status = __pulse_crypto_es256_sign_anchor(i32(pointer), 16640)
|
|
534
|
+
if (status == 1) memory.copy(output.dataStart, pointer + 160, 64)
|
|
535
|
+
memory.fill(pointer, 0, 16640); memory.fill(0, 0, 65536)
|
|
536
|
+
return status
|
|
537
|
+
}` : ''}
|
|
538
|
+
return -3
|
|
539
|
+
}`;
|
|
540
|
+
}
|
|
541
|
+
|
|
475
542
|
function jwtCryptoCompositionSource(selected) {
|
|
476
|
-
|
|
543
|
+
const rsa = selected && (selected.algorithm === 'RS256' || selected.signingAlgorithms?.includes('RS256'));
|
|
544
|
+
const helper = rsa ? rs256CompositionSource() : '';
|
|
545
|
+
return helper + jwtVerificationCompositionSource(selected);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function rs256CompositionSource() {
|
|
549
|
+
return `function __pulse_fastly_rs256(signing: bool, key: Uint8Array, data: Uint8Array, signature: Uint8Array): i32 {
|
|
550
|
+
if (key.length < 8 || data.length > 12288) return -2
|
|
551
|
+
const k = load<u32>(key.dataStart)
|
|
552
|
+
if ((k != 256 && k != 384 && k != 512) || signature.length != i32(k) || key.length != i32(signing ? 8 + k * 9 / 2 : 8 + k)) return -2
|
|
553
|
+
const pointer: usize = 524288
|
|
554
|
+
const output = (64 + key.length + 15) & ~15
|
|
555
|
+
const input = output + signature.length
|
|
556
|
+
memory.fill(pointer, 0, 16640)
|
|
557
|
+
store<u32>(pointer, 0x32534550); store<u32>(pointer + 4, 2); store<u32>(pointer + 8, 64)
|
|
558
|
+
store<u32>(pointer + 12, (input + data.length + 15) & ~15); store<u32>(pointer + 16, 2)
|
|
559
|
+
store<u32>(pointer + 24, input); store<u32>(pointer + 28, data.length)
|
|
560
|
+
store<u32>(pointer + 32, 64); store<u32>(pointer + 36, key.length)
|
|
561
|
+
store<u32>(pointer + 40, output); store<u32>(pointer + 44, k)
|
|
562
|
+
memory.copy(pointer + 64, key.dataStart, key.length); memory.copy(pointer + input, data.dataStart, data.length)
|
|
563
|
+
if (!signing) memory.copy(pointer + output, signature.dataStart, signature.length)
|
|
564
|
+
const status = signing ? __pulse_crypto_rs256_sign_anchor(i32(pointer), 16640) : __pulse_crypto_rs256_verify_anchor(i32(pointer), 16640)
|
|
565
|
+
if (signing && status == 1) memory.copy(signature.dataStart, pointer + output, signature.length)
|
|
566
|
+
memory.fill(pointer, 0, 16640); memory.fill(0, 0, 65536)
|
|
567
|
+
return status
|
|
568
|
+
}\n`;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function jwtVerificationCompositionSource(selected) {
|
|
572
|
+
if (selected?.algorithm === 'RS256') return `
|
|
573
|
+
function __pulse_fastly_jwt_capture_signing_input(input: Uint8Array): void { __pulse_fastly_jwt_record_signing_input(input.byteLength, "") }
|
|
574
|
+
function __pulse_fastly_jwt_crypto_verify(algorithm: string, key: Uint8Array, data: Uint8Array, signature: Uint8Array): i32 {
|
|
575
|
+
if (algorithm != "RS256") return -2
|
|
576
|
+
return __pulse_fastly_rs256(false, key, data, signature)
|
|
577
|
+
}`;
|
|
578
|
+
|
|
579
|
+
if (!selected || selected.algorithm === 'HMAC-SHA256') {
|
|
477
580
|
return [
|
|
478
581
|
'function __pulse_fastly_jwt_capture_signing_input(input: Uint8Array): void { void input }',
|
|
479
582
|
'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 }'
|
|
@@ -586,15 +689,16 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
586
689
|
&& effect.kind === 'assets.lookup';
|
|
587
690
|
const jwt = effect.package === '@pulse-compute/jwt'
|
|
588
691
|
&& effect.contractId === 'pulse.jwt'
|
|
589
|
-
&&
|
|
590
|
-
&& effect.operation ===
|
|
591
|
-
&& effect.capability === 'jwt.verify';
|
|
692
|
+
&& ['verify', 'sign'].includes(effect.operation)
|
|
693
|
+
&& effect.kind === `jwt.${effect.operation}` && effect.capability === effect.kind;
|
|
592
694
|
const s3 = effect.package === '@pulse-compute/s3' && effect.contractId === 'pulse.s3'
|
|
593
|
-
&& ['head', 'getText', 'putText'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
594
|
-
|
|
695
|
+
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
696
|
+
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
697
|
+
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
698
|
+
return !grip && !assets && !jwt && !s3 && !digest;
|
|
595
699
|
});
|
|
596
700
|
if (unsupportedPackages.length > 0) {
|
|
597
|
-
fail('Fastly native realization only accepts the trusted first-party Assets, GRIP, and
|
|
701
|
+
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', {
|
|
598
702
|
effects: unsupportedPackages.map((entry) => ({ id: entry.id, kind: entry.kind, package: entry.package, contractId: entry.contractId }))
|
|
599
703
|
});
|
|
600
704
|
}
|
|
@@ -608,17 +712,18 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
608
712
|
&& effect.kind === 'assets.lookup';
|
|
609
713
|
const jwt = effect.package === '@pulse-compute/jwt'
|
|
610
714
|
&& effect.contractId === 'pulse.jwt'
|
|
611
|
-
&&
|
|
612
|
-
&& effect.operation ===
|
|
613
|
-
&& effect.capability === 'jwt.verify';
|
|
715
|
+
&& ['verify', 'sign'].includes(effect.operation)
|
|
716
|
+
&& effect.kind === `jwt.${effect.operation}` && effect.capability === effect.kind;
|
|
614
717
|
const s3 = effect.package === '@pulse-compute/s3' && effect.contractId === 'pulse.s3'
|
|
615
|
-
&& ['head', 'getText', 'putText'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
616
|
-
|
|
718
|
+
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
719
|
+
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
720
|
+
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
721
|
+
return !grip && !assets && !jwt && !s3 && !digest;
|
|
617
722
|
});
|
|
618
723
|
if (unsupportedDeclarations.length > 0) {
|
|
619
|
-
fail('Fastly native package realization is restricted to the Assets, GRIP, and
|
|
724
|
+
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 });
|
|
620
725
|
}
|
|
621
|
-
for (const effect of effects.filter((entry) =>
|
|
726
|
+
for (const effect of effects.filter((entry) => ['jwt.verify', 'jwt.sign'].includes(entry.kind))) {
|
|
622
727
|
const resource = effect.resource;
|
|
623
728
|
const knownShape = resource
|
|
624
729
|
&& !Object.keys(resource)
|
|
@@ -633,7 +738,7 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
633
738
|
&& typeof resource.keyArtifactId === 'string'
|
|
634
739
|
&& resource.keyArtifactId.length > 0
|
|
635
740
|
&& resource.secretBinding === null;
|
|
636
|
-
if (!namedSecret && !staticPublicKey) {
|
|
741
|
+
if (!namedSecret && (!staticPublicKey || effect.kind === 'jwt.sign')) {
|
|
637
742
|
fail('Fastly Native JWT requires one exact named-secret or bounded static public-key descriptor.', 'PULSE_FASTLY_NATIVE_JWT_KEY_DESCRIPTOR_INVALID', {
|
|
638
743
|
effectId: effect.id,
|
|
639
744
|
resource,
|
|
@@ -699,8 +804,8 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
699
804
|
const effects = plan.effects || [];
|
|
700
805
|
const hasConfig = effects.some((effect) => effect.kind === 'config.get');
|
|
701
806
|
const hasSecret = effects.some((effect) => (
|
|
702
|
-
effect.kind === 'secret.get' || ['s3.head', 's3.getText', 's3.putText'].includes(effect.kind)
|
|
703
|
-
|| (
|
|
807
|
+
effect.kind === 'secret.get' || ['s3.head', 's3.getText', 's3.putText', 's3.getBody'].includes(effect.kind)
|
|
808
|
+
|| (['jwt.verify', 'jwt.sign'].includes(effect.kind) && effect.resource && effect.resource.keyType === 'secret')
|
|
704
809
|
));
|
|
705
810
|
const hasGripHold = effects.some((effect) => effect.kind === 'grip.hold');
|
|
706
811
|
const hasGripBroadcast = effects.some((effect) => effect.kind === 'grip.broadcast');
|
|
@@ -726,6 +831,7 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
726
831
|
for (const [index, effect] of effects.entries()) {
|
|
727
832
|
if (effect.kind !== 'kv.get' && effect.kind !== 'kv.put' && !conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind) && effect.kind !== 'assets.lookup') continue;
|
|
728
833
|
const packagePayload = inputExpression(effect, 'payload');
|
|
834
|
+
if (effect.kind === 'assets.lookup' && literalObjectField(packagePayload, 'embeddedId')) continue;
|
|
729
835
|
const logical = effect.resource && effect.resource.kind === 'literal'
|
|
730
836
|
&& effect.kind !== 'assets.lookup'
|
|
731
837
|
? String(effect.resource.value)
|
|
@@ -821,9 +927,10 @@ function resolveCapabilityBindings(plan, options = {}) {
|
|
|
821
927
|
}
|
|
822
928
|
|
|
823
929
|
return Object.freeze({
|
|
930
|
+
maxDurationMs: require('@pulse-compute/runtime/host').normalizeRequestDuration(options.maxDurationMs),
|
|
824
931
|
configStore,
|
|
825
932
|
secretStore,
|
|
826
|
-
s3: require('../toolchain/s3.js').resolveFastlyS3(effects.map((effect) => ({ ...effect, payload:
|
|
933
|
+
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),
|
|
827
934
|
kv: Object.freeze(kv),
|
|
828
935
|
fetch: Object.freeze(fetch),
|
|
829
936
|
backends: Object.freeze(backends),
|
|
@@ -857,7 +964,7 @@ function planRequiresFetchBodyRead(plan) {
|
|
|
857
964
|
if (Array.isArray(value)) return value.some(visit);
|
|
858
965
|
return Object.values(value).some(visit);
|
|
859
966
|
}
|
|
860
|
-
return visit(plan.entry);
|
|
967
|
+
return visit(plan.entry) || visit(plan.handlers) || visit(plan.stages) || visit(plan.helpers);
|
|
861
968
|
}
|
|
862
969
|
|
|
863
970
|
function requiredImportsForPlan(plan, bindings) {
|
|
@@ -872,6 +979,7 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
872
979
|
'fastly_http_body:new',
|
|
873
980
|
'fastly_http_body:write'
|
|
874
981
|
]);
|
|
982
|
+
if (bindings && bindings.maxDurationMs !== undefined) { keys.add('wasi_snapshot_preview1:clock_time_get'); keys.add('fastly_async_io:select'); }
|
|
875
983
|
const kinds = new Set((plan.effects || []).map((effect) => effect.kind));
|
|
876
984
|
if (kinds.has('fetch') || kinds.has('grip.publish') || kinds.has('grip.broadcast')) {
|
|
877
985
|
for (const key of [
|
|
@@ -884,7 +992,7 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
884
992
|
const gripAuthenticationRequired = kinds.has('grip.broadcast')
|
|
885
993
|
&& (!bindings || Boolean(bindings.grip && bindings.grip.authentication));
|
|
886
994
|
const jwtSecretRequired = (plan.effects || []).some((effect) => (
|
|
887
|
-
|
|
995
|
+
['jwt.verify', 'jwt.sign'].includes(effect.kind)
|
|
888
996
|
&& effect.resource
|
|
889
997
|
&& effect.resource.keyType === 'secret'
|
|
890
998
|
));
|
|
@@ -892,7 +1000,7 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
892
1000
|
for (const key of ['fastly_secret_store:open', 'fastly_secret_store:get', 'fastly_secret_store:plaintext']) keys.add(key);
|
|
893
1001
|
}
|
|
894
1002
|
for (const kind of conditionalKv.KV_CONDITIONAL_KINDS) if (kinds.has(kind)) for (const key of conditionalKv.kvImports(kind)) keys.add(key);
|
|
895
|
-
if (kinds.has('jwt.verify')) keys.add('wasi_snapshot_preview1:clock_time_get');
|
|
1003
|
+
if (kinds.has('jwt.verify') || kinds.has('jwt.sign') || kinds.has('time.now')) keys.add('wasi_snapshot_preview1:clock_time_get');
|
|
896
1004
|
if (kinds.has('kv.get') || kinds.has('kv.put') || kinds.has('assets.lookup')) keys.add('fastly_kv_store:open');
|
|
897
1005
|
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);
|
|
898
1006
|
if (kinds.has('kv.put')) for (const key of ['fastly_kv_store:insert', 'fastly_kv_store:insert_wait']) keys.add(key);
|
|
@@ -901,7 +1009,7 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
901
1009
|
keys.add('fastly_log:endpoint_get');
|
|
902
1010
|
keys.add('fastly_log:write');
|
|
903
1011
|
}
|
|
904
|
-
if (kinds.has('s3.head') || kinds.has('s3.getText') || kinds.has('s3.putText')) for (const key of require('./s3-native.js').S3_IMPORTS) keys.add(key);
|
|
1012
|
+
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);
|
|
905
1013
|
return Object.freeze([...keys].sort());
|
|
906
1014
|
}
|
|
907
1015
|
|
|
@@ -912,15 +1020,40 @@ function stripPulseHostImports(source) {
|
|
|
912
1020
|
.join('\n');
|
|
913
1021
|
}
|
|
914
1022
|
|
|
1023
|
+
function schemaTextEncodeIds(plan) {
|
|
1024
|
+
// Keep the PS3 accounting/failure boundary and all unproved codec families.
|
|
1025
|
+
if (nativeValueBudget.hasBoundedReadLoop(plan)) return [];
|
|
1026
|
+
function closedText(node, depth = 0) {
|
|
1027
|
+
if (depth > 16) return false;
|
|
1028
|
+
if (node.kind === 'string' || node.kind === 'boolean') return true;
|
|
1029
|
+
if (node.kind === 'nullable') return closedText(node.value, depth);
|
|
1030
|
+
if (node.kind === 'array') return closedText(node.element, depth + 1);
|
|
1031
|
+
return node.kind === 'object' && !node.additionalProperties
|
|
1032
|
+
&& node.fields.every(field => closedText(field.value, depth + 1));
|
|
1033
|
+
}
|
|
1034
|
+
return ((plan.schemas && plan.schemas.registry && plan.schemas.registry.schemas) || [])
|
|
1035
|
+
.filter(schema => !schema.jsonLimits && schema.root.kind === 'object'
|
|
1036
|
+
&& schemaNeedsValueProjection(schema.root) && closedText(schema.root))
|
|
1037
|
+
.map(schema => schema.id);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
915
1040
|
function generateSchemaRuntime(plan) {
|
|
916
1041
|
const registry = plan.schemas && plan.schemas.registry ? plan.schemas.registry : { schemas: [], maxBytes: FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES, contentTypePolicy: 'accept-json-or-missing' };
|
|
917
1042
|
const schemas = registry.schemas || [];
|
|
1043
|
+
const textEncodeIds = new Set(schemaTextEncodeIds(plan));
|
|
918
1044
|
const nodeLines = [];
|
|
1045
|
+
const scalarProjectors = new Map();
|
|
1046
|
+
const flatObjectProjectors = new Map();
|
|
1047
|
+
const scalarKinds = ['string', 'boolean', 'i32', 'u32', 'f64', 'string-enum'];
|
|
919
1048
|
let nodeIndex = 0;
|
|
920
1049
|
|
|
921
1050
|
function emitNode(schemaIndex, node) {
|
|
922
1051
|
const currentIndex = nodeIndex++;
|
|
923
1052
|
const functionName = `__pulse_fastly_schema_${schemaIndex}_${currentIndex}`;
|
|
1053
|
+
const flatObject = node.kind === 'object' && !node.additionalProperties
|
|
1054
|
+
&& node.fields.every(field => scalarKinds.includes(field.value.kind));
|
|
1055
|
+
// Locals belong to this function, so equivalent flat bodies need no node ID.
|
|
1056
|
+
const localIndex = flatObject ? 'flat' : currentIndex;
|
|
924
1057
|
const child = node.kind === 'nullable'
|
|
925
1058
|
? emitNode(schemaIndex, node.value)
|
|
926
1059
|
: node.kind === 'array'
|
|
@@ -929,30 +1062,43 @@ function generateSchemaRuntime(plan) {
|
|
|
929
1062
|
const fields = node.kind === 'object'
|
|
930
1063
|
? node.fields.map((field) => Object.freeze({ field, apply: emitNode(schemaIndex, field.value) }))
|
|
931
1064
|
: [];
|
|
932
|
-
const body = [`function ${functionName}(valueHandle: i32): i32 {`, ' const input = __pulse_fastly_value(valueHandle)'];
|
|
1065
|
+
const body = [`function ${functionName}(valueHandle: i32, checkDuplicates: bool): i32 {`, ' const input = __pulse_fastly_value(valueHandle)'];
|
|
933
1066
|
if (node.kind === 'nullable') {
|
|
934
1067
|
body.push(' if (input.kind == PULSE_VALUE_NULL) return valueHandle');
|
|
935
|
-
body.push(` return ${child}(valueHandle)`);
|
|
1068
|
+
body.push(` return ${child}(valueHandle, checkDuplicates)`);
|
|
936
1069
|
} else if (node.kind === 'array') {
|
|
937
1070
|
body.push(' if (input.kind != PULSE_VALUE_ARRAY) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }');
|
|
938
1071
|
body.push(' const output = host_value_array()');
|
|
939
1072
|
body.push(' for (let index = 0; index < input.values.length; index += 1) {');
|
|
940
|
-
body.push(` const item = ${child}(unchecked(input.values[index]))`);
|
|
1073
|
+
body.push(` const item = ${child}(unchecked(input.values[index]), checkDuplicates)`);
|
|
941
1074
|
body.push(' if (item <= 0) return 0');
|
|
942
1075
|
body.push(' host_value_array_push(output, item)');
|
|
943
1076
|
body.push(' }');
|
|
944
1077
|
body.push(' return output');
|
|
1078
|
+
} else if (node.kind === 'json-value' || node.kind === 'json-object') {
|
|
1079
|
+
body.push(...nestedJsonProjectionLines(node));
|
|
1080
|
+
} else if (node.kind === 'scalar-record') {
|
|
1081
|
+
body.push(...scalarRecordProjectionLines(node));
|
|
945
1082
|
} else if (node.kind === 'object') {
|
|
946
1083
|
body.push(' if (input.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 50, -1); return 0 }');
|
|
1084
|
+
if (node.additionalProperties) body.push(' if (checkDuplicates && input.duplicateJsonKeys) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 57, -1); return 0 }');
|
|
947
1085
|
body.push(' const output = host_value_object()');
|
|
948
1086
|
fields.forEach(({ field, apply }, fieldIndex) => {
|
|
949
|
-
body.push(` const key_${
|
|
950
|
-
body.push(`
|
|
951
|
-
body.push(`
|
|
952
|
-
body.push(`
|
|
953
|
-
body.push(`
|
|
954
|
-
body.push(`
|
|
1087
|
+
body.push(` const key_${localIndex}_${fieldIndex} = __pulse_fastly_string_value(${quote(field.name)})`);
|
|
1088
|
+
if (!field.required) body.push(` if (__pulse_fastly_find(input, ${quote(field.name)}) >= 0) {`);
|
|
1089
|
+
body.push(` const field_${localIndex}_${fieldIndex} = host_value_property(valueHandle, key_${localIndex}_${fieldIndex})`);
|
|
1090
|
+
body.push(` if (__pulse_fastly_value(field_${localIndex}_${fieldIndex}).kind == PULSE_VALUE_UNDEFINED) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 51, -1); return 0 }`);
|
|
1091
|
+
body.push(` const projected_${localIndex}_${fieldIndex} = ${apply}(field_${localIndex}_${fieldIndex}, checkDuplicates)`);
|
|
1092
|
+
body.push(` if (projected_${localIndex}_${fieldIndex} <= 0) return 0`);
|
|
1093
|
+
body.push(` host_value_object_set(output, key_${localIndex}_${fieldIndex}, projected_${localIndex}_${fieldIndex})`);
|
|
1094
|
+
if (!field.required) body.push(' }');
|
|
955
1095
|
});
|
|
1096
|
+
if (node.additionalProperties) {
|
|
1097
|
+
body.push(' for (let index = 0; index < input.keys.length; index++) {', ' const key = unchecked(input.keys[index])');
|
|
1098
|
+
if (node.fields.length) body.push(` if (${node.fields.map(field => `key == ${quote(field.name)}`).join(' || ')}) continue`);
|
|
1099
|
+
body.push(' const item = __pulse_fastly_copy_json(unchecked(input.values[index]), checkDuplicates)',
|
|
1100
|
+
' if (item <= 0) return 0', ' host_value_object_set(output, __pulse_fastly_string_value(key), item)', ' }');
|
|
1101
|
+
}
|
|
956
1102
|
body.push(' return output');
|
|
957
1103
|
} else if (node.kind === 'string') {
|
|
958
1104
|
body.push(' if (input.kind != PULSE_VALUE_STRING) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }');
|
|
@@ -977,6 +1123,32 @@ function generateSchemaRuntime(plan) {
|
|
|
977
1123
|
throw new TypeError(`Unsupported Fastly native schema node ${String(node.kind)}.`);
|
|
978
1124
|
}
|
|
979
1125
|
body.push('}', '');
|
|
1126
|
+
// Only scalar validators preserve the original handle without allocating or
|
|
1127
|
+
// calling child projectors. Compare the exact emitted body, including enum
|
|
1128
|
+
// values/order and error stages.
|
|
1129
|
+
if (scalarKinds.includes(node.kind)) {
|
|
1130
|
+
const key = body.slice(1).join('\n');
|
|
1131
|
+
const existing = scalarProjectors.get(key);
|
|
1132
|
+
if (existing) return existing;
|
|
1133
|
+
const sharedName = `__pulse_fastly_schema_scalar_${scalarProjectors.size}`;
|
|
1134
|
+
scalarProjectors.set(key, sharedName);
|
|
1135
|
+
body[0] = `function ${sharedName}(valueHandle: i32, checkDuplicates: bool): i32 {`;
|
|
1136
|
+
nodeLines.push(...body);
|
|
1137
|
+
return sharedName;
|
|
1138
|
+
}
|
|
1139
|
+
// Share code only after child scalar identities are resolved. Exact bodies
|
|
1140
|
+
// retain field order, presence checks, validation and errors. Each call still
|
|
1141
|
+
// allocates its own output; open/nested/nullable/array bodies remain separate.
|
|
1142
|
+
if (flatObject) {
|
|
1143
|
+
const key = body.slice(1).join('\n');
|
|
1144
|
+
const existing = flatObjectProjectors.get(key);
|
|
1145
|
+
if (existing) return existing;
|
|
1146
|
+
const sharedName = `__pulse_fastly_schema_flat_object_${flatObjectProjectors.size}`;
|
|
1147
|
+
flatObjectProjectors.set(key, sharedName);
|
|
1148
|
+
body[0] = `function ${sharedName}(valueHandle: i32, checkDuplicates: bool): i32 {`;
|
|
1149
|
+
nodeLines.push(...body);
|
|
1150
|
+
return sharedName;
|
|
1151
|
+
}
|
|
980
1152
|
nodeLines.push(...body);
|
|
981
1153
|
return functionName;
|
|
982
1154
|
}
|
|
@@ -986,17 +1158,22 @@ function generateSchemaRuntime(plan) {
|
|
|
986
1158
|
`const __PULSE_SCHEMA_MAX_BYTES: i32 = ${Number(registry.maxBytes || FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES)}`,
|
|
987
1159
|
`const __PULSE_SCHEMA_REQUIRE_JSON: bool = ${registry.contentTypePolicy === 'require-json' ? 'true' : 'false'}`,
|
|
988
1160
|
'',
|
|
1161
|
+
schemaJsonAdmissionSource(plan),
|
|
1162
|
+
schemaFetchJsonSource(),
|
|
989
1163
|
...nodeLines,
|
|
990
|
-
|
|
1164
|
+
`function __pulse_fastly_schema_apply(schemaId: string, valueHandle: i32, encode: bool${textEncodeIds.size ? ', textResult: bool = false' : ''}): i32 {`,
|
|
991
1165
|
' if (schemaId.length == 0) return valueHandle'
|
|
992
1166
|
];
|
|
993
1167
|
for (const [schemaIndex, schema] of schemas.entries()) {
|
|
994
1168
|
lines.push(`${schemaIndex === 0 ? ' if' : ' else if'} (schemaId == ${quote(schema.id)}) {`);
|
|
995
|
-
lines.push(`
|
|
1169
|
+
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 }`);
|
|
1170
|
+
lines.push(` const projected = ${roots[schemaIndex]}(valueHandle, !encode)`);
|
|
996
1171
|
lines.push(' if (projected <= 0) return 0');
|
|
997
1172
|
lines.push(' const input = __pulse_fastly_json(projected, 0)');
|
|
998
1173
|
lines.push(` const normalized = encode ? __pulse_schema_encode_${schemaIndex}(input) : __pulse_schema_decode_${schemaIndex}(input)`);
|
|
999
|
-
lines.push(' return
|
|
1174
|
+
if (textEncodeIds.has(schema.id)) lines.push(' if (textResult && encode) return __pulse_fastly_string_value(normalized)');
|
|
1175
|
+
if (schema.jsonLimits) lines.push(' const output = __pulse_fastly_parse_json(normalized)', ' if (output > 0) __pulse_fastly_deep_freeze(output)', ' return output');
|
|
1176
|
+
else lines.push(' return __pulse_fastly_parse_json(normalized)');
|
|
1000
1177
|
lines.push(' }');
|
|
1001
1178
|
}
|
|
1002
1179
|
lines.push(' __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1)');
|
|
@@ -1019,7 +1196,7 @@ function effectResultSource(plan, bindings) {
|
|
|
1019
1196
|
lines.push(`function __pulse_fastly_secret_store(effectIndex: i32): string { return ${quote(bindings.secretStore)} }`, '');
|
|
1020
1197
|
lines.push('function __pulse_fastly_jwt_secret_binding(effectIndex: i32): string {', ' switch (effectIndex) {');
|
|
1021
1198
|
for (const [index, effect] of (plan.effects || []).entries()) {
|
|
1022
|
-
if (
|
|
1199
|
+
if (['jwt.verify', 'jwt.sign'].includes(effect.kind) && effect.resource.keyType === 'secret') {
|
|
1023
1200
|
lines.push(` case ${index}: return ${quote(effect.resource.secretBinding)}`);
|
|
1024
1201
|
}
|
|
1025
1202
|
}
|
|
@@ -1034,12 +1211,31 @@ function effectResultSource(plan, bindings) {
|
|
|
1034
1211
|
for (const binding of bindings.grip.publish) lines.push(` case ${binding.index}: return ${quote(binding.authSecretRef || '')}`);
|
|
1035
1212
|
lines.push(' default: return ""', ' }', '}', '');
|
|
1036
1213
|
|
|
1214
|
+
// A config lookup has the same ready-result handoff at every static site.
|
|
1215
|
+
// Keep the site indices in the switch; pass the active index through so the
|
|
1216
|
+
// pending slot and any state error still belong to the original invocation.
|
|
1217
|
+
const configSites = (plan.effects || []).flatMap((effect, index) => effect.kind === 'config.get' ? [index] : []);
|
|
1218
|
+
if (configSites.length > 1) {
|
|
1219
|
+
lines.push('function __pulse_fastly_resolve_config_get(effectIndex: i32): i32 {');
|
|
1220
|
+
lines.push(' const response = __pulse_fastly_wait_ready(effectIndex)');
|
|
1221
|
+
lines.push(' if (response <= 0) return 0');
|
|
1222
|
+
lines.push(' return response');
|
|
1223
|
+
lines.push('}', '');
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1037
1226
|
lines.push('function __pulse_fastly_resolve_effect(effectIndex: i32): i32 {');
|
|
1038
1227
|
lines.push(' switch (effectIndex) {');
|
|
1039
1228
|
for (const [index, effect] of (plan.effects || []).entries()) {
|
|
1229
|
+
if (effect.kind === 'config.get' && configSites.length > 1) {
|
|
1230
|
+
if (index === configSites[0]) {
|
|
1231
|
+
for (const site of configSites) lines.push(` case ${site}:`);
|
|
1232
|
+
lines.push(' return __pulse_fastly_resolve_config_get(effectIndex)');
|
|
1233
|
+
}
|
|
1234
|
+
continue;
|
|
1235
|
+
}
|
|
1040
1236
|
const result = effect.result || {};
|
|
1041
1237
|
const decoder = result.decoder;
|
|
1042
|
-
const waiter = conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind) ? '__pulse_fastly_kv_conditional_wait' : ['s3.head', 's3.getText', 's3.putText'].includes(effect.kind) ? '__pulse_fastly_s3_wait' : effect.kind === 'fetch'
|
|
1238
|
+
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'
|
|
1043
1239
|
? '__pulse_fastly_wait_fetch'
|
|
1044
1240
|
: effect.kind === 'grip.publish'
|
|
1045
1241
|
? '__pulse_fastly_wait_grip_publish'
|
|
@@ -1075,13 +1271,17 @@ function effectResultSource(plan, bindings) {
|
|
|
1075
1271
|
|
|
1076
1272
|
function effectDispatchSource(plan) {
|
|
1077
1273
|
const handlerByKind = Object.freeze({
|
|
1274
|
+
'time.now': '__pulse_fastly_time_begin',
|
|
1275
|
+
'crypto.digestText': '__pulse_fastly_digest_begin',
|
|
1078
1276
|
fetch: '__pulse_fastly_fetch_begin',
|
|
1079
1277
|
'config.get': '__pulse_fastly_config_begin',
|
|
1080
1278
|
'secret.get': '__pulse_fastly_secret_begin',
|
|
1081
1279
|
'jwt.verify': '__pulse_fastly_jwt_begin',
|
|
1280
|
+
'jwt.sign': '__pulse_fastly_jwt_sign_begin',
|
|
1082
1281
|
's3.head': '__pulse_fastly_s3_begin',
|
|
1083
1282
|
's3.getText': '__pulse_fastly_s3_begin',
|
|
1084
1283
|
's3.putText': '__pulse_fastly_s3_begin',
|
|
1284
|
+
's3.getBody': '__pulse_fastly_s3_begin',
|
|
1085
1285
|
'kv.get': '__pulse_fastly_kv_get_begin',
|
|
1086
1286
|
'kv.put': '__pulse_fastly_kv_put_begin',
|
|
1087
1287
|
...Object.fromEntries(conditionalKv.KV_CONDITIONAL_KINDS.map(kind => [kind, '__pulse_fastly_kv_conditional_begin'])),
|
|
@@ -1094,7 +1294,7 @@ function effectDispatchSource(plan) {
|
|
|
1094
1294
|
const kinds = [...new Set((plan.effects || []).map((effect) => effect.kind))];
|
|
1095
1295
|
const lines = [
|
|
1096
1296
|
'function host_effect_begin(effectIndex: i32, payload: i32): void {',
|
|
1097
|
-
...((plan
|
|
1297
|
+
...(needsNativeValueFailureGuard(plan) ? [' if (__pulse_fastly_last_error != PULSE_ERROR_NONE) return'] : []),
|
|
1098
1298
|
' 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 }',
|
|
1099
1299
|
' const payloadValue = __pulse_fastly_value(payload)',
|
|
1100
1300
|
' if (payloadValue.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 47, effectIndex); return }',
|
|
@@ -1115,7 +1315,14 @@ function effectDispatchSource(plan) {
|
|
|
1115
1315
|
}
|
|
1116
1316
|
|
|
1117
1317
|
function fastlyRuntimeSource(plan, bindings, options = {}) {
|
|
1318
|
+
const textEncodeIds = schemaTextEncodeIds(plan);
|
|
1118
1319
|
const effectCount = (plan.effects || []).length;
|
|
1320
|
+
const hasAssets = (plan.effects || []).some(effect => effect.kind === 'assets.lookup');
|
|
1321
|
+
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 }
|
|
1322
|
+
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) }` : '';
|
|
1323
|
+
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" }
|
|
1324
|
+
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])) }
|
|
1325
|
+
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) }` : '';
|
|
1119
1326
|
const jwtCrypto = options.jwtCrypto || selectedJwtCrypto(plan);
|
|
1120
1327
|
const es256KeyRecords = options.es256KeyRecords || Object.freeze([]);
|
|
1121
1328
|
const jwtReality = options.jwtReality && options.jwtReality.enabled === true
|
|
@@ -1194,7 +1401,7 @@ function __pulse_fastly_jwt_send_error(): void {}
|
|
|
1194
1401
|
const gripAuthorizationSource = gripAuthentication
|
|
1195
1402
|
? `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));`
|
|
1196
1403
|
: '';
|
|
1197
|
-
|
|
1404
|
+
const source = String.raw`
|
|
1198
1405
|
@external("fastly_abi", "init") declare function fastly_abi_init(version: i64): i32
|
|
1199
1406
|
@external("fastly_http_req", "body_downstream_get") declare function fastly_http_req_body_downstream_get(requestOut: usize, bodyOut: usize): i32
|
|
1200
1407
|
@external("fastly_http_req", "method_get") declare function fastly_http_req_method_get(handle: i32, buffer: usize, bufferLength: i32, writtenOut: usize): i32
|
|
@@ -1263,19 +1470,40 @@ const PULSE_ERROR_SCHEMA: i32 = 1005
|
|
|
1263
1470
|
const PULSE_ERROR_TRANSPORT: i32 = 1006
|
|
1264
1471
|
const PULSE_ERROR_STATE: i32 = 1007
|
|
1265
1472
|
const PULSE_ERROR_JWT: i32 = 1008
|
|
1473
|
+
const PULSE_ERROR_REQUEST_BODY: i32 = 1009
|
|
1266
1474
|
|
|
1267
1475
|
class __PulseFastlyValue {
|
|
1476
|
+
${tracksJsonDuplicates(plan) ? 'duplicateJsonKeys: bool = false' : ''}
|
|
1268
1477
|
kind: i32 = PULSE_VALUE_UNDEFINED
|
|
1269
1478
|
boolean: i32 = 0
|
|
1270
1479
|
number: f64 = 0.0
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1480
|
+
${nativeStringFields}
|
|
1481
|
+
// Scalar handles do not need collection backing stores.
|
|
1482
|
+
private keysStorage: Array<string> | null = null
|
|
1483
|
+
get keys(): Array<string> {
|
|
1484
|
+
if (this.keysStorage === null) this.keysStorage = new Array<string>()
|
|
1485
|
+
return this.keysStorage!
|
|
1486
|
+
}
|
|
1487
|
+
// Scalar handles do not need collection backing stores.
|
|
1488
|
+
private valuesStorage: Array<i32> | null = null
|
|
1489
|
+
get values(): Array<i32> {
|
|
1490
|
+
if (this.valuesStorage === null) this.valuesStorage = new Array<i32>()
|
|
1491
|
+
return this.valuesStorage!
|
|
1492
|
+
}
|
|
1274
1493
|
responseHandle: i32 = 0
|
|
1275
1494
|
bodyHandle: i32 = 0
|
|
1276
1495
|
bodyLoaded: i32 = 0
|
|
1496
|
+
s3Body: bool = false
|
|
1497
|
+
s3Deadline: i64 = 0
|
|
1498
|
+
s3Length: i32 = 0
|
|
1277
1499
|
status: i32 = 200
|
|
1278
|
-
|
|
1500
|
+
// Scalar handles do not need collection backing stores.
|
|
1501
|
+
private headersStorage: Array<__PulseFastlyHeader> | null = null
|
|
1502
|
+
get headers(): Array<__PulseFastlyHeader> {
|
|
1503
|
+
if (this.headersStorage === null) this.headersStorage = new Array<__PulseFastlyHeader>()
|
|
1504
|
+
return this.headersStorage!
|
|
1505
|
+
}
|
|
1506
|
+
binaryBody: Uint8Array | null = null
|
|
1279
1507
|
immutable: bool = false
|
|
1280
1508
|
}
|
|
1281
1509
|
class __PulseFastlyHeader {
|
|
@@ -1292,7 +1520,7 @@ class __PulseJsonParser {
|
|
|
1292
1520
|
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 }
|
|
1293
1521
|
value(depth: i32): i32 {
|
|
1294
1522
|
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 }
|
|
1295
|
-
if (this.index >= this.source.length || (!this.conditionalKv && depth > ${plan.effects.some(effect => conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) ? 128 : 64})) { this.failed = true; return 0 }
|
|
1523
|
+
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 }
|
|
1296
1524
|
const c = this.source.charCodeAt(this.index)
|
|
1297
1525
|
if (c == 34) return __pulse_fastly_string_value(this.string())
|
|
1298
1526
|
if (c == 123) return this.object(depth + 1)
|
|
@@ -1306,14 +1534,18 @@ class __PulseJsonParser {
|
|
|
1306
1534
|
string(): string {
|
|
1307
1535
|
// Find a bounded span, then decode each UTF-16 unit once. Concatenating one
|
|
1308
1536
|
// character at a time exhausts stub/fixed memory on a valid maximum KV value.
|
|
1309
|
-
const start = this.index + 1; let end = start;
|
|
1537
|
+
const start = this.index + 1; let end = start; let plain = true;
|
|
1310
1538
|
while (end < this.source.length) {
|
|
1311
1539
|
const c = this.source.charCodeAt(end);
|
|
1312
1540
|
if (c == 34) break;
|
|
1313
|
-
if (c == 92) end += 1;
|
|
1541
|
+
if (c == 92) { plain = false; end += 1; }
|
|
1542
|
+
else if (c < 32) plain = false;
|
|
1314
1543
|
end += 1;
|
|
1315
1544
|
}
|
|
1316
1545
|
if (end >= this.source.length) { this.failed = true; return "" }
|
|
1546
|
+
// A validated span needs one owned string, without a temporary UTF-16 array.
|
|
1547
|
+
// Keep escaped and malformed spans on the existing decoder path.
|
|
1548
|
+
if (plain) { this.index = end + 1; return this.source.substring(start, end); }
|
|
1317
1549
|
const output = new Uint16Array(end - start); let count = 0; this.index = start;
|
|
1318
1550
|
while (this.index < end) {
|
|
1319
1551
|
let c = this.source.charCodeAt(this.index++);
|
|
@@ -1391,6 +1623,7 @@ class __PulseJsonParser {
|
|
|
1391
1623
|
const key = this.string(); this.skip()
|
|
1392
1624
|
if (this.conditionalKv && __pulse_fastly_find(__pulse_fastly_value(output), key) >= 0) { this.failed = true; break }
|
|
1393
1625
|
if (this.index >= this.source.length || this.source.charCodeAt(this.index) != 58) { this.failed = true; break }
|
|
1626
|
+
${tracksJsonDuplicates(plan) ? 'if (__pulse_fastly_find(__pulse_fastly_value(output), key) >= 0) __pulse_fastly_value(output).duplicateJsonKeys = true' : ''}
|
|
1394
1627
|
this.index += 1; host_value_object_set(output, __pulse_fastly_string_value(key), this.value(depth)); this.skip()
|
|
1395
1628
|
if (this.index >= this.source.length) { this.failed = true; break }
|
|
1396
1629
|
const c = this.source.charCodeAt(this.index); this.index += 1
|
|
@@ -1405,6 +1638,12 @@ const PULSE_FASTLY_PENDING_NONE: i32 = 0
|
|
|
1405
1638
|
const PULSE_FASTLY_PENDING_ASYNC: i32 = 1
|
|
1406
1639
|
const PULSE_FASTLY_PENDING_READY: i32 = 2
|
|
1407
1640
|
@lazy const __pulse_fastly_values = new Array<__PulseFastlyValue>()
|
|
1641
|
+
// Primitive handles have value semantics. Bounded caches keep admitted scalar
|
|
1642
|
+
// validation loops from retaining a fresh boxed value for every iteration.
|
|
1643
|
+
@lazy const __pulse_fastly_scalar_handles = new StaticArray<i32>(4)
|
|
1644
|
+
@lazy const __pulse_fastly_integer_handles = new StaticArray<i32>(65536)
|
|
1645
|
+
@lazy const __pulse_fastly_character_handles = new StaticArray<i32>(65537)
|
|
1646
|
+
@lazy const __pulse_fastly_literal_handles = new Map<string, i32>()
|
|
1408
1647
|
@lazy const __pulse_fastly_pending = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1409
1648
|
@lazy const __pulse_fastly_pending_mode = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
1410
1649
|
@lazy const __pulse_fastly_payloads = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
|
|
@@ -1440,8 +1679,18 @@ function __pulse_fastly_utf8(value: string): ArrayBuffer { return String.UTF8.en
|
|
|
1440
1679
|
function __pulse_fastly_decode(buffer: Uint8Array, length: i32): string { return String.UTF8.decodeUnsafe(buffer.dataStart, length, false) }
|
|
1441
1680
|
function __pulse_fastly_put(value: __PulseFastlyValue): i32 { __pulse_fastly_values.push(value); return __pulse_fastly_values.length }
|
|
1442
1681
|
function __pulse_fastly_value(handle: i32): __PulseFastlyValue { if (handle <= 0 || handle > __pulse_fastly_values.length) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 1, -1); return new __PulseFastlyValue() } return unchecked(__pulse_fastly_values[handle - 1]) }
|
|
1682
|
+
${nativeStringConcat}
|
|
1683
|
+
${nativeStringTrim}
|
|
1684
|
+
${nativeStringIndex}
|
|
1443
1685
|
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])) }
|
|
1444
|
-
function __pulse_fastly_string_value(value: string): i32 {
|
|
1686
|
+
function __pulse_fastly_string_value(value: string): i32 {
|
|
1687
|
+
const index = value.length == 0 ? 65536 : value.length == 1 ? value.charCodeAt(0) : -1;
|
|
1688
|
+
if (index >= 0) { const cached = unchecked(__pulse_fastly_character_handles[index]); if (cached != 0) return cached }
|
|
1689
|
+
const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_STRING; item.text = value;
|
|
1690
|
+
const handle = __pulse_fastly_put(item);
|
|
1691
|
+
if (index >= 0) unchecked(__pulse_fastly_character_handles[index] = handle);
|
|
1692
|
+
return handle
|
|
1693
|
+
}
|
|
1445
1694
|
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 }
|
|
1446
1695
|
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() }
|
|
1447
1696
|
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) }
|
|
@@ -1480,21 +1729,26 @@ function __pulse_fastly_quote(value: string): string {
|
|
|
1480
1729
|
out[cursor] = 34;
|
|
1481
1730
|
return String.UTF16.decodeUnsafe(out.dataStart, out.byteLength);
|
|
1482
1731
|
}
|
|
1483
|
-
function
|
|
1484
|
-
if (depth > ${plan.effects.some(effect => conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) ? 128 : 64}) { __pulse_fastly_fail(PULSE_ERROR_JSON, 2, -1);
|
|
1732
|
+
function __pulse_fastly_json_parts(handle: i32, depth: i32, parts: Array<string>): void {
|
|
1733
|
+
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 }
|
|
1485
1734
|
const value = __pulse_fastly_value(handle)
|
|
1486
|
-
if (value.kind ==
|
|
1487
|
-
if (value.kind ==
|
|
1488
|
-
if (value.kind ==
|
|
1489
|
-
if (value.kind ==
|
|
1490
|
-
if (value.kind ==
|
|
1491
|
-
|
|
1492
|
-
|
|
1735
|
+
if (value.kind == PULSE_VALUE_BOOLEAN) { parts.push(value.boolean != 0 ? "true" : "false"); return }
|
|
1736
|
+
if (value.kind == PULSE_VALUE_NUMBER) { parts.push(__pulse_fastly_number_string(value.number)); return }
|
|
1737
|
+
if (value.kind == PULSE_VALUE_STRING) { parts.push(__pulse_fastly_quote(value.text)); return }
|
|
1738
|
+
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 }
|
|
1739
|
+
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 }
|
|
1740
|
+
parts.push("null")
|
|
1741
|
+
}
|
|
1742
|
+
function __pulse_fastly_json(handle: i32, depth: i32): string {
|
|
1743
|
+
// Join once, avoiding a complete copy of a multi-MiB leaf at every ancestor
|
|
1744
|
+
// and at every following member in the response or effect result.
|
|
1745
|
+
const parts = new Array<string>(); __pulse_fastly_json_parts(handle, depth, parts);
|
|
1746
|
+
return parts.join("")
|
|
1493
1747
|
}
|
|
1494
1748
|
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 }
|
|
1495
1749
|
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) }
|
|
1496
1750
|
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)) }
|
|
1497
|
-
function __pulse_fastly_read_body(handle: i32, effectIndex: i32): string {
|
|
1751
|
+
function __pulse_fastly_read_body(handle: i32, effectIndex: i32, requestText: bool = false): string {
|
|
1498
1752
|
const chunks = new Array<Uint8Array>(); let total = 0;
|
|
1499
1753
|
while (true) {
|
|
1500
1754
|
const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES), read = __pulse_fastly_out_i32();
|
|
@@ -1504,36 +1758,55 @@ function __pulse_fastly_read_body(handle: i32, effectIndex: i32): string {
|
|
|
1504
1758
|
if (count < 0 || count > buffer.length) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 20, effectIndex); return "" }
|
|
1505
1759
|
if (!count) break;
|
|
1506
1760
|
total += count;
|
|
1507
|
-
if (total > __PULSE_SCHEMA_MAX_BYTES && __PULSE_SCHEMA_MAX_BYTES > 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, effectIndex); return "" }
|
|
1761
|
+
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 "" }
|
|
1508
1762
|
chunks.push(buffer.subarray(0, count));
|
|
1509
1763
|
}
|
|
1510
1764
|
// UTF-8 scalars may straddle host read chunks. Decode the bounded body once.
|
|
1511
1765
|
const bytes = new Uint8Array(total); let offset = 0;
|
|
1512
1766
|
for (let i = 0; i < chunks.length; i++) { bytes.set(chunks[i], offset); offset += chunks[i].length; }
|
|
1767
|
+
if (requestText && !__pulse_request_utf8_valid(bytes)) { __pulse_fastly_fail(PULSE_ERROR_REQUEST_BODY, 24, effectIndex); return "" }
|
|
1513
1768
|
return __pulse_fastly_decode(bytes, total);
|
|
1514
1769
|
}
|
|
1515
1770
|
function __pulse_fastly_request_header_text(name: string): string { const nameBytes = __pulse_fastly_utf8(name); const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES); const written = __pulse_fastly_out_i32(); const status = fastly_http_req_header_value_get(__pulse_fastly_request_handle, changetype<usize>(nameBytes), nameBytes.byteLength, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)); if (status == FASTLY_STATUS_NONE) return ""; if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 22, -1); return "" } return __pulse_fastly_decode(buffer, __pulse_fastly_out_value(written)) }
|
|
1516
1771
|
function __pulse_fastly_fetch_header_text(responseHandle: i32, name: string): string { const nameBytes = __pulse_fastly_utf8(name); const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES); const written = __pulse_fastly_out_i32(); const status = fastly_http_resp_header_value_get(responseHandle, changetype<usize>(nameBytes), nameBytes.byteLength, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)); if (status == FASTLY_STATUS_NONE) return ""; if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 23, -1); return "" } return __pulse_fastly_decode(buffer, __pulse_fastly_out_value(written)) }
|
|
1517
1772
|
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 }
|
|
1518
1773
|
|
|
1519
|
-
function
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1774
|
+
function __pulse_fastly_scalar(kind: i32, boolean: i32, slot: i32): i32 {
|
|
1775
|
+
const cached = unchecked(__pulse_fastly_scalar_handles[slot]); if (cached != 0) return cached;
|
|
1776
|
+
const item = new __PulseFastlyValue(); item.kind = kind; item.boolean = boolean;
|
|
1777
|
+
const handle = __pulse_fastly_put(item); unchecked(__pulse_fastly_scalar_handles[slot] = handle); return handle
|
|
1778
|
+
}
|
|
1779
|
+
function host_value_undefined(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_UNDEFINED, 0, 0) }
|
|
1780
|
+
function host_value_null(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_NULL, 0, 1) }
|
|
1781
|
+
function host_value_boolean(value: i32): i32 { return __pulse_fastly_scalar(PULSE_VALUE_BOOLEAN, value != 0 ? 1 : 0, value != 0 ? 3 : 2) }
|
|
1782
|
+
function host_value_number(value: f64): i32 {
|
|
1783
|
+
// Preserve negative zero, NaN, infinities and nonintegral values exactly.
|
|
1784
|
+
const cacheable = value >= 0 && value < 65536 && Math.floor(value) == value && (value != 0 || 1.0 / value > 0);
|
|
1785
|
+
const index = cacheable ? i32(value) : -1;
|
|
1786
|
+
if (index >= 0) { const cached = unchecked(__pulse_fastly_integer_handles[index]); if (cached != 0) return cached }
|
|
1787
|
+
const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_NUMBER; item.number = value;
|
|
1788
|
+
const handle = __pulse_fastly_put(item); if (index >= 0) unchecked(__pulse_fastly_integer_handles[index] = handle); return handle
|
|
1789
|
+
}
|
|
1790
|
+
function host_value_string(pointer: i32, length: i32): i32 {
|
|
1791
|
+
const text = changetype<string>(pointer);
|
|
1792
|
+
if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.has(text)) return __pulse_fastly_literal_handles.get(text);
|
|
1793
|
+
const handle = __pulse_fastly_string_value(text);
|
|
1794
|
+
if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.size < 1024) __pulse_fastly_literal_handles.set(text, handle);
|
|
1795
|
+
return handle
|
|
1796
|
+
}
|
|
1524
1797
|
function host_value_array(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_ARRAY; return __pulse_fastly_put(item) }
|
|
1525
1798
|
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) }
|
|
1526
1799
|
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])) }
|
|
1527
1800
|
function host_value_object(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_OBJECT; return __pulse_fastly_put(item) }
|
|
1528
1801
|
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) } }
|
|
1529
1802
|
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])) }
|
|
1530
|
-
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.
|
|
1803
|
+
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() }
|
|
1531
1804
|
function host_value_property_set(target: i32, key: i32, value: i32): i32 { host_value_object_set(target, key, value); return value }
|
|
1532
|
-
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) }
|
|
1805
|
+
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) }
|
|
1533
1806
|
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) }
|
|
1534
|
-
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.
|
|
1807
|
+
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 }
|
|
1535
1808
|
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 }
|
|
1536
|
-
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
|
|
1809
|
+
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 }
|
|
1537
1810
|
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 }
|
|
1538
1811
|
|
|
1539
1812
|
function host_request_method(): i32 { return __pulse_fastly_string_value(__pulse_fastly_request_method) }
|
|
@@ -1544,21 +1817,35 @@ function __pulse_router_param_text(path: string, pattern: string, name: string):
|
|
|
1544
1817
|
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) }
|
|
1545
1818
|
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) }
|
|
1546
1819
|
function host_request_path(): i32 { return __pulse_fastly_string_value(__pulse_fastly_request_path) }
|
|
1547
|
-
function host_request_headers(): i32 { return
|
|
1820
|
+
function host_request_headers(): i32 { return __pulse_request_headers_read() }
|
|
1548
1821
|
function host_request_header(name: i32): i32 { const value = __pulse_fastly_request_header_text(__pulse_fastly_string(name)); return value.length == 0 ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1549
|
-
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) }
|
|
1550
|
-
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 }
|
|
1822
|
+
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) }
|
|
1823
|
+
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 }
|
|
1551
1824
|
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])))) } } }
|
|
1552
1825
|
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) }
|
|
1553
1826
|
function host_response_json(value: i32, options: i32): i32 { return __pulse_fastly_response(value, options, true) }
|
|
1827
|
+
function host_schema_decode(text: i32, schema: i32): i32 {
|
|
1828
|
+
const id = __pulse_fastly_value(schema)
|
|
1829
|
+
if (id.kind != PULSE_VALUE_STRING || id.text.length == 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1); return 0 }
|
|
1830
|
+
const input = __pulse_fastly_value(text)
|
|
1831
|
+
if (input.kind != PULSE_VALUE_STRING) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 52, -1); return 0 }
|
|
1832
|
+
if (String.UTF8.byteLength(input.text) > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, -1); return 0 }
|
|
1833
|
+
const parsed = ${hasJsonAdmission(plan) ? '__pulse_fastly_schema_parse_json(input.text, schema)' : '__pulse_fastly_parse_json(input.text)'}
|
|
1834
|
+
if (parsed <= 0) return 0
|
|
1835
|
+
const decoded = __pulse_fastly_schema_apply(id.text, parsed, false)
|
|
1836
|
+
if (decoded <= 0) return 0
|
|
1837
|
+
__pulse_fastly_deep_freeze(decoded)
|
|
1838
|
+
return decoded
|
|
1839
|
+
}
|
|
1554
1840
|
function host_schema_encode(value: i32, schema: i32): i32 {
|
|
1555
1841
|
const id = __pulse_fastly_value(schema)
|
|
1556
1842
|
if (id.kind != PULSE_VALUE_STRING || id.text.length == 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 55, -1); return 0 }
|
|
1557
|
-
const
|
|
1843
|
+
${textEncodeIds.length ? `const textResult = ${textEncodeIds.map(id => `id.text == ${quote(id)}`).join(' || ')}
|
|
1844
|
+
const projected = __pulse_fastly_schema_apply(id.text, value, true, textResult)` : 'const projected = __pulse_fastly_schema_apply(id.text, value, true)'}
|
|
1558
1845
|
if (projected <= 0) return 0
|
|
1559
|
-
const text = __pulse_fastly_json(projected, 0)
|
|
1846
|
+
const text = ${textEncodeIds.length ? 'textResult ? __pulse_fastly_string(projected) : ' : ''}__pulse_fastly_json(projected, 0)
|
|
1560
1847
|
if (String.UTF8.byteLength(text) > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, -1); return 0 }
|
|
1561
|
-
return __pulse_fastly_string_value(text)
|
|
1848
|
+
return ${textEncodeIds.length ? 'textResult ? projected : ' : ''}__pulse_fastly_string_value(text)
|
|
1562
1849
|
}
|
|
1563
1850
|
function host_response_text(value: i32, options: i32): i32 { return __pulse_fastly_response(value, options, false) }
|
|
1564
1851
|
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) }
|
|
@@ -1569,7 +1856,7 @@ function host_grip_subscribe(responseHandle: i32, optionsHandle: i32): i32 { con
|
|
|
1569
1856
|
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 }
|
|
1570
1857
|
function host_kv_namespace(name: i32): i32 { return name }
|
|
1571
1858
|
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)) }
|
|
1572
|
-
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 }
|
|
1859
|
+
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 }
|
|
1573
1860
|
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_fastly_fetch_header_text(response.responseHandle, __pulse_fastly_string(nameHandle)); return value.length == 0 ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1574
1861
|
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]) }
|
|
1575
1862
|
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) }
|
|
@@ -1590,12 +1877,12 @@ function __pulse_fastly_jwt_record_verified(): void { __pulse_fastly_jwt_verifie
|
|
|
1590
1877
|
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 }
|
|
1591
1878
|
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'} }
|
|
1592
1879
|
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) }
|
|
1880
|
+
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) }
|
|
1593
1881
|
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) }
|
|
1594
1882
|
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) { __pulse_fastly_fail(PULSE_ERROR_TRANSPORT, 77, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(pendingOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
1595
|
-
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 =
|
|
1883
|
+
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)) }
|
|
1596
1884
|
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) }
|
|
1597
|
-
|
|
1598
|
-
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) }
|
|
1885
|
+
${assetBeginSource}
|
|
1599
1886
|
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) }
|
|
1600
1887
|
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) }
|
|
1601
1888
|
function __pulse_fastly_grip_payload(payload: __PulseFastlyValue): __PulseFastlyValue { return __pulse_fastly_value(__pulse_fastly_payload_field(payload, "payload")) }
|
|
@@ -1609,19 +1896,22 @@ function __pulse_fastly_wait_grip_publish(effectIndex: i32): i32 { const respons
|
|
|
1609
1896
|
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 }
|
|
1610
1897
|
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 }
|
|
1611
1898
|
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 }
|
|
1612
|
-
|
|
1613
|
-
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])) }
|
|
1614
|
-
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) }
|
|
1899
|
+
${assetResultSource}
|
|
1615
1900
|
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) }
|
|
1616
1901
|
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 }
|
|
1617
|
-
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) }
|
|
1902
|
+
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) }
|
|
1618
1903
|
|
|
1619
1904
|
${jwtEvidenceSource}
|
|
1620
1905
|
${es256KeyArtifactSource(es256KeyRecords)}
|
|
1621
1906
|
${jwtCryptoCompositionSource(jwtCrypto)}
|
|
1907
|
+
${jwtSigningCryptoCompositionSource(jwtCrypto)}
|
|
1622
1908
|
${generateSchemaRuntime(plan)}
|
|
1623
1909
|
${effectResultSource(plan, bindings)}
|
|
1624
1910
|
`;
|
|
1911
|
+
if (!hasAssets) return source;
|
|
1912
|
+
const writer = 'status = __pulse_fastly_write_body(bodyHandle, value.text);';
|
|
1913
|
+
if (!source.includes(writer)) throw new Error('Embedded asset response writer boundary drift.');
|
|
1914
|
+
return source.replace(writer, 'status = value.binaryBody !== null ? __pulse_fastly_write_binary(bodyHandle, value.binaryBody!) : __pulse_fastly_write_body(bodyHandle, value.text);');
|
|
1625
1915
|
}
|
|
1626
1916
|
|
|
1627
1917
|
function driverSource(plan, options = {}) {
|
|
@@ -1638,6 +1928,13 @@ export function pulse_fastly_jwt_crypto_calls(): i32 { return __pulse_fastly_jwt
|
|
|
1638
1928
|
export function pulse_fastly_jwt_clock_calls(): i32 { return __pulse_fastly_jwt_clock_calls }
|
|
1639
1929
|
export function pulse_fastly_jwt_schema_calls(): i32 { return __pulse_fastly_jwt_schema_calls }
|
|
1640
1930
|
export function ${entryName}(): void {
|
|
1931
|
+
if (__pulse_invocation_started) { __pulse_fastly_fail(PULSE_ERROR_STATE, 171, -1); return }
|
|
1932
|
+
__pulse_invocation_started = true
|
|
1933
|
+
__pulse_fastly_run_invocation()
|
|
1934
|
+
${plan.effects.some(e => e.kind === 's3.getBody') ? '__s3_close_bodies()' : ''}
|
|
1935
|
+
__pulse_invocation_close()
|
|
1936
|
+
}
|
|
1937
|
+
function __pulse_fastly_run_invocation(): void {
|
|
1641
1938
|
__pulse_fastly_last_error = fastly_abi_init(PULSE_FASTLY_EFFECTS_ABI_VERSION)
|
|
1642
1939
|
if (__pulse_fastly_last_error != FASTLY_STATUS_OK) { __pulse_fastly_jwt_send_error(); return }
|
|
1643
1940
|
const handles = new StaticArray<i32>(2)
|
|
@@ -1651,15 +1948,16 @@ export function ${entryName}(): void {
|
|
|
1651
1948
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1652
1949
|
__pulse_fastly_request_path = __pulse_fastly_path(__pulse_fastly_request_url)
|
|
1653
1950
|
let runStatus = pulse_start()
|
|
1654
|
-
while (runStatus == 1 && __pulse_fastly_last_error == 0) {
|
|
1951
|
+
${packageApplication.enabled(plan) ? packageApplication.driverLoop() : applicationErrors.enabled(plan) ? applicationErrors.driverLoop(plan) : ` while (runStatus == 1 && __pulse_fastly_last_error == 0) {
|
|
1655
1952
|
for (let effectIndex = 0; effectIndex < PULSE_FASTLY_EFFECT_COUNT; effectIndex += 1) {
|
|
1656
1953
|
if (unchecked(__pulse_fastly_pending_mode[effectIndex]) == PULSE_FASTLY_PENDING_NONE) continue
|
|
1954
|
+
const invocation = unchecked(__pulse_invocation_tickets[effectIndex])
|
|
1657
1955
|
const result = __pulse_fastly_resolve_effect(effectIndex)
|
|
1658
1956
|
if (result <= 0 || __pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1659
|
-
if (
|
|
1957
|
+
if (__pulse_invocation_settle(effectIndex, invocation, result) != 1) { __pulse_fastly_fail(PULSE_ERROR_STATE, 101, effectIndex); __pulse_fastly_jwt_send_error(); return }
|
|
1660
1958
|
}
|
|
1661
1959
|
runStatus = pulse_resume()
|
|
1662
|
-
}
|
|
1960
|
+
}`}
|
|
1663
1961
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
1664
1962
|
if (runStatus != 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 102, -1); __pulse_fastly_jwt_send_error(); return }
|
|
1665
1963
|
const result = pulse_result_handle()
|
|
@@ -1672,9 +1970,10 @@ export function ${entryName}(): void {
|
|
|
1672
1970
|
}
|
|
1673
1971
|
|
|
1674
1972
|
function capabilityImports(effect) {
|
|
1973
|
+
if (effect.kind === 'time.now') return ['wasi_snapshot_preview1.clock_time_get'];
|
|
1675
1974
|
if (effect.kind === 'config.get') return ['fastly_config_store.open', 'fastly_config_store.get'];
|
|
1676
1975
|
if (effect.kind === 'secret.get') return ['fastly_secret_store.open', 'fastly_secret_store.get', 'fastly_secret_store.plaintext'];
|
|
1677
|
-
if (
|
|
1976
|
+
if (['jwt.verify', 'jwt.sign'].includes(effect.kind)) {
|
|
1678
1977
|
return [
|
|
1679
1978
|
...(effect.resource && effect.resource.keyType === 'secret'
|
|
1680
1979
|
? [
|
|
@@ -1706,7 +2005,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1706
2005
|
validatePlanBoundary(plan, options);
|
|
1707
2006
|
const facts = packageFacts(plan, options);
|
|
1708
2007
|
const jwtCrypto = selectedJwtCrypto(plan);
|
|
1709
|
-
const es256KeyRecords = jwtCrypto && jwtCrypto.algorithm
|
|
2008
|
+
const es256KeyRecords = jwtCrypto && ['ES256', 'RS256'].includes(jwtCrypto.algorithm)
|
|
1710
2009
|
? resolveEs256KeyArtifacts(plan, facts)
|
|
1711
2010
|
: Object.freeze([]);
|
|
1712
2011
|
if (jwtCrypto && jwtCrypto.guestUnitRequired && facts.guestUnits.length !== 1) {
|
|
@@ -1730,11 +2029,12 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1730
2029
|
);
|
|
1731
2030
|
}
|
|
1732
2031
|
const bindings = resolveCapabilityBindings(plan, options);
|
|
1733
|
-
const portable = generateCanonicalNativeAssemblyScript(plan, options);
|
|
2032
|
+
const portable = packageApplication.enabled(plan) ? packageApplication.portable(plan) : generateCanonicalNativeAssemblyScript(plan, options);
|
|
1734
2033
|
const portableSource = stripPulseHostImports(portable.source);
|
|
1735
2034
|
const hasJwt = (plan.effects || []).some((effect) => effect.kind === 'jwt.verify');
|
|
1736
|
-
const
|
|
1737
|
-
const
|
|
2035
|
+
const hasSign = plan.effects.some(effect => effect.kind === 'jwt.sign');
|
|
2036
|
+
const jwtSource = hasJwt || hasSign ? pulseJwtAssemblyScriptSource({ includeSigning: hasSign }) : undefined;
|
|
2037
|
+
let source = [
|
|
1738
2038
|
'/* Generated by Pulse Pass98 Fastly native platform capability compiler. */',
|
|
1739
2039
|
'export function __pulse_fastly_abort(message: string | null, fileName: string | null = null, line: u32 = 0, column: u32 = 0): void { unreachable() }',
|
|
1740
2040
|
'',
|
|
@@ -1745,11 +2045,25 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1745
2045
|
}),
|
|
1746
2046
|
jwtSource ? jwtSource.source : '',
|
|
1747
2047
|
require('./s3-native.js').s3NativeSource(plan, bindings),
|
|
2048
|
+
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') : '',
|
|
2049
|
+
require('./time-native.js').timeNativeSource(plan),
|
|
2050
|
+
require('./digest-native.js').digestNativeSource(plan),
|
|
1748
2051
|
conditionalKv.kvNativeSource(plan),
|
|
2052
|
+
require('./native-request-body.js').runtimeSource(),
|
|
2053
|
+
require('./native-request-headers.js').runtimeSource(),
|
|
2054
|
+
applicationErrors.enabled(plan) ? applicationErrors.runtimeSource(plan) : '',
|
|
2055
|
+
effectInvocations.runtimeSource({ packageCompletion: packageApplication.enabled(plan) }),
|
|
1749
2056
|
portableSource,
|
|
1750
2057
|
driverSource(plan, { guestLinked: facts.guestUnits.length > 0 }),
|
|
1751
2058
|
''
|
|
1752
2059
|
].join('\n');
|
|
2060
|
+
if (applicationErrors.enabled(plan)) source = applicationErrors.instrument(source);
|
|
2061
|
+
source = require('./native-request-headers.js').instrument(source, applicationErrors.enabled(plan), /\bhost_request_headers\(/.test(portableSource));
|
|
2062
|
+
source = require('./native-request-body.js').instrument(source, applicationErrors.enabled(plan));
|
|
2063
|
+
if (packageApplication.enabled(plan)) source = packageApplication.instrument(source);
|
|
2064
|
+
source = effectInvocations.instrument(source);
|
|
2065
|
+
source = require('./request-budget.js').instrumentRequestBudget(source, bindings.maxDurationMs, plan);
|
|
2066
|
+
source = nativeValueBudget.instrument(source, plan);
|
|
1753
2067
|
const effectKinds = Object.freeze((plan.effects || []).reduce((output, effect) => {
|
|
1754
2068
|
output[effect.kind] = (output[effect.kind] || 0) + 1;
|
|
1755
2069
|
return output;
|
|
@@ -1776,7 +2090,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1776
2090
|
crypto: portable.manifest.crypto,
|
|
1777
2091
|
jwt: jwtSource
|
|
1778
2092
|
? Object.freeze({
|
|
1779
|
-
status: jwtCrypto && jwtCrypto.algorithm
|
|
2093
|
+
status: jwtCrypto && ['ES256', 'RS256'].includes(jwtCrypto.algorithm)
|
|
1780
2094
|
? 'implemented-g4'
|
|
1781
2095
|
: 'implemented-e2',
|
|
1782
2096
|
package: jwtSource.owner,
|
|
@@ -1795,6 +2109,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1795
2109
|
})
|
|
1796
2110
|
: Object.freeze({ status: 'inactive' }),
|
|
1797
2111
|
effectCount: (plan.effects || []).length,
|
|
2112
|
+
effectInvocations: effectInvocations.policy,
|
|
1798
2113
|
effectKinds,
|
|
1799
2114
|
continuationCount: (plan.continuations || []).length,
|
|
1800
2115
|
groupedContinuationCount: (plan.continuations || []).filter((entry) => (entry.effectIds || []).length > 1).length,
|
|
@@ -1809,22 +2124,24 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1809
2124
|
'pulse_fastly_error_stage',
|
|
1810
2125
|
'pulse_fastly_error_effect',
|
|
1811
2126
|
'pulse_plan_hash_ptr',
|
|
1812
|
-
'pulse_plan_hash_length'
|
|
2127
|
+
'pulse_plan_hash_length',
|
|
2128
|
+
...(bindings.maxDurationMs === undefined ? [] : ['pulse_fastly_request_expired'])
|
|
1813
2129
|
]),
|
|
1814
2130
|
policy: Object.freeze({
|
|
2131
|
+
...(nativeValueBudget.hasBoundedReadLoop(plan) ? { readLoopMemory: nativeValueBudget.policy } : {}),
|
|
1815
2132
|
nativeFastly: true,
|
|
1816
2133
|
provider: 'fastly',
|
|
1817
2134
|
providerNeutralInput: true,
|
|
1818
2135
|
javascriptRuntime: false,
|
|
1819
2136
|
jsComputeRuntime: false,
|
|
1820
|
-
wasi: hasJwt || bindings.s3.length || plan.effects.some(effect => conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) ? 'clock_time_get only' : false,
|
|
2137
|
+
wasi: bindings.maxDurationMs !== undefined || hasJwt || hasSign || bindings.s3.length || plan.effects.some(effect => conditionalKv.KV_CONDITIONAL_KINDS.includes(effect.kind)) ? 'clock_time_get only' : false,
|
|
1821
2138
|
effects: FASTLY_NATIVE_PLATFORM_EFFECT_KINDS.join(', '),
|
|
1822
2139
|
continuations: true,
|
|
1823
2140
|
groupedEffects: 'existing fetch start-before-wait ordering remains intact',
|
|
1824
2141
|
config: 'Fastly Config Store open/get with undefined for missing values',
|
|
1825
2142
|
secrets: 'Fastly Secret Store open/get/plaintext; proof traces redact plaintext values',
|
|
1826
|
-
jwt: hasJwt
|
|
1827
|
-
? `package-owned compact-JWS
|
|
2143
|
+
jwt: hasJwt || hasSign
|
|
2144
|
+
? `package-owned compact-JWS, registered-claim, and bounded issuance semantics composed with ${jwtCrypto.realization}; provider-owned request, secret, clock, schema, and transport`
|
|
1828
2145
|
: 'inactive',
|
|
1829
2146
|
kv: 'Fastly KV Store async lookup/insert; conditional operations preserve u64 generations, bounded readiness, strict Pulse envelopes and uncertain dispatch',
|
|
1830
2147
|
assets: 'Fastly KV Store lookup with provider-owned content type, cache headers, and GET/HEAD body ownership',
|
|
@@ -1860,8 +2177,9 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
1860
2177
|
});
|
|
1861
2178
|
}
|
|
1862
2179
|
|
|
1863
|
-
function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
2180
|
+
function inspectFastlyNativePlatformCapabilitiesWasm(input, options = {}) {
|
|
1864
2181
|
const bytes = Buffer.isBuffer(input) ? input : fs.readFileSync(path.resolve(input));
|
|
2182
|
+
assertFastlyWasmBudget(bytes.length, options.maxWasmBytes);
|
|
1865
2183
|
if (!WebAssembly.validate(bytes)) fail('Generated Fastly native platform capabilities module is not valid WebAssembly.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_WASM_INVALID');
|
|
1866
2184
|
const module = new WebAssembly.Module(bytes);
|
|
1867
2185
|
const imports = WebAssembly.Module.imports(module).map((entry) => Object.freeze({ module: entry.module, name: entry.name, kind: entry.kind }));
|
|
@@ -1877,7 +2195,6 @@ function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
|
1877
2195
|
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'];
|
|
1878
2196
|
const missingExports = requiredExports.filter((name) => !exportNames.has(name));
|
|
1879
2197
|
if (missingExports.length > 0) fail('Generated Fastly native platform capabilities module is missing required exports.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_EXPORT_MISSING', { missingExports, exports });
|
|
1880
|
-
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 });
|
|
1881
2198
|
return Object.freeze({
|
|
1882
2199
|
valid: true,
|
|
1883
2200
|
bytes: bytes.length,
|
|
@@ -1890,6 +2207,7 @@ function inspectFastlyNativePlatformCapabilitiesWasm(input) {
|
|
|
1890
2207
|
}
|
|
1891
2208
|
|
|
1892
2209
|
function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
2210
|
+
const maxWasmBytes = normalizeFastlyMaxWasmBytes(options.maxWasmBytes);
|
|
1893
2211
|
const generated = generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options);
|
|
1894
2212
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
1895
2213
|
const compilerFallback = path.resolve(__dirname, '..', '..', '..', '..', 'wasm', 'packages', 'compiler');
|
|
@@ -1922,6 +2240,8 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1922
2240
|
}
|
|
1923
2241
|
jsonAs = Object.freeze({ transform, dependencyRoot, version: packageJson.version });
|
|
1924
2242
|
}
|
|
2243
|
+
if (options.emitWat !== undefined && typeof options.emitWat !== 'boolean') throw new TypeError('emitWat must be a boolean.');
|
|
2244
|
+
const emitWat = options.emitWat === true;
|
|
1925
2245
|
const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-fastly-native-platform-'));
|
|
1926
2246
|
const requestedOutDir = options.outDir ? path.resolve(options.outDir) : undefined;
|
|
1927
2247
|
const outputDir = requestedOutDir || stagingDir;
|
|
@@ -1929,6 +2249,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1929
2249
|
const sourceFile = path.join(stagingDir, 'fastly-native-platform-capabilities.as.ts');
|
|
1930
2250
|
const wasmFile = path.join(outputDir, options.wasmFile || 'fastly-native-platform-capabilities.wasm');
|
|
1931
2251
|
const watFile = path.join(outputDir, options.watFile || 'fastly-native-platform-capabilities.wat');
|
|
2252
|
+
if (!emitWat) fs.rmSync(watFile, { force: true });
|
|
1932
2253
|
const guestLinked = generated.guestUnits.length > 0;
|
|
1933
2254
|
// asc resolves custom runtimes like module names and appends ".ts".
|
|
1934
2255
|
const fixedMemoryRuntimeModule = path.join(__dirname, 'fixed-memory-runtime.as');
|
|
@@ -1945,14 +2266,17 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
1945
2266
|
asc.script,
|
|
1946
2267
|
path.basename(sourceFile),
|
|
1947
2268
|
'--outFile', assemblyScriptWasmFile,
|
|
1948
|
-
'--textFile', assemblyScriptWatFile,
|
|
1949
2269
|
'--runtime', guestLinked
|
|
1950
2270
|
? fixedMemoryRuntimeModule
|
|
1951
2271
|
: (schemaCodecsActive ? 'incremental' : 'stub'),
|
|
1952
2272
|
'--noAssert',
|
|
1953
2273
|
'--optimize'
|
|
1954
2274
|
];
|
|
1955
|
-
|
|
2275
|
+
if (emitWat) args.push('--textFile', assemblyScriptWatFile);
|
|
2276
|
+
const optimization = appendAssemblyScriptOptimizationArgs(args, nativeOptimization, { guestLinked });
|
|
2277
|
+
if (!guestLinked && (nativeValueBudget.hasBoundedReadLoop(plan) || plan.effects.some(effect => effect.kind === 'crypto.digestText' || effect.kind.startsWith('s3.')))) {
|
|
2278
|
+
args.push('--maximumMemory', String(nativeValueBudget.policy.maximumMemoryPages));
|
|
2279
|
+
}
|
|
1956
2280
|
if (generated.guestUnits.length > 0) {
|
|
1957
2281
|
args.push(
|
|
1958
2282
|
'--disable', 'bulk-memory',
|
|
@@ -2029,7 +2353,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2029
2353
|
failureMessage: 'Fastly Native start wrapper composition failed.'
|
|
2030
2354
|
}
|
|
2031
2355
|
);
|
|
2032
|
-
runGuestTool(
|
|
2356
|
+
if (emitWat) runGuestTool(
|
|
2033
2357
|
'wasm-dis',
|
|
2034
2358
|
[wasmFile, '--mvp-features', '-o', watFile],
|
|
2035
2359
|
{
|
|
@@ -2059,7 +2383,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2059
2383
|
});
|
|
2060
2384
|
}
|
|
2061
2385
|
const primaryWasm = fs.readFileSync(wasmFile);
|
|
2062
|
-
const primaryWat =
|
|
2386
|
+
const primaryWat = emitWat ? fs.readFileSync(watFile, 'utf8') : '';
|
|
2063
2387
|
const primaryArtifact = Object.freeze({
|
|
2064
2388
|
bytes: primaryWasm.length,
|
|
2065
2389
|
sha256: sha256(primaryWasm)
|
|
@@ -2092,7 +2416,8 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2092
2416
|
realization = Object.freeze({
|
|
2093
2417
|
...realization,
|
|
2094
2418
|
wasm: stageResult.wasm,
|
|
2095
|
-
|
|
2419
|
+
// Required guest-link disassembly audits remain enabled.
|
|
2420
|
+
wat: emitWat ? stageResult.wat : '',
|
|
2096
2421
|
guestUnits: stageResult.guestUnits,
|
|
2097
2422
|
guestLink: Object.freeze({
|
|
2098
2423
|
version: stageResult.version,
|
|
@@ -2110,9 +2435,9 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2110
2435
|
const wat = realization.wat;
|
|
2111
2436
|
if (requestedOutDir && generated.guestUnits.length > 0) {
|
|
2112
2437
|
fs.writeFileSync(wasmFile, wasm);
|
|
2113
|
-
fs.writeFileSync(watFile, wat, 'utf8');
|
|
2438
|
+
if (emitWat) fs.writeFileSync(watFile, wat, 'utf8');
|
|
2114
2439
|
}
|
|
2115
|
-
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm);
|
|
2440
|
+
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm, { maxWasmBytes });
|
|
2116
2441
|
const imported = new Set(inspection.imports.map((entry) => `${entry.module}:${entry.name}`));
|
|
2117
2442
|
const requiredImports = requiredImportsForPlan(plan, generated.bindings);
|
|
2118
2443
|
const missingRequiredImports = requiredImports.filter((key) => !imported.has(key));
|
|
@@ -2126,7 +2451,9 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2126
2451
|
: undefined,
|
|
2127
2452
|
optimization,
|
|
2128
2453
|
wasm: Object.freeze({ bytes: inspection.bytes, sha256: inspection.sha256, magic: inspection.magic }),
|
|
2129
|
-
wat: Object.freeze(
|
|
2454
|
+
wat: Object.freeze(emitWat
|
|
2455
|
+
? { emitted: true, bytes: Buffer.byteLength(wat), sha256: sha256(wat) }
|
|
2456
|
+
: { emitted: false, bytes: 0, sha256: null }),
|
|
2130
2457
|
importModules: inspection.importModules,
|
|
2131
2458
|
imports: inspection.imports,
|
|
2132
2459
|
requiredImports,
|
|
@@ -2159,7 +2486,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2159
2486
|
assemblyScriptDurationMs,
|
|
2160
2487
|
primaryArtifact,
|
|
2161
2488
|
startWrapper,
|
|
2162
|
-
output: requestedOutDir ? Object.freeze({ outDir: outputDir, wasmFile, watFile }) : undefined
|
|
2489
|
+
output: requestedOutDir ? Object.freeze({ outDir: outputDir, wasmFile, watFile: emitWat ? watFile : null }) : undefined
|
|
2163
2490
|
});
|
|
2164
2491
|
} finally {
|
|
2165
2492
|
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
@@ -2177,10 +2504,12 @@ function writeFastlyNativePlatformCapabilitiesModule(compiled, outDir, options =
|
|
|
2177
2504
|
const manifestFile = path.join(target, options.manifestFile || 'fastly-native-platform-capabilities-manifest.json');
|
|
2178
2505
|
fs.writeFileSync(sourceFile, compiled.source, 'utf8');
|
|
2179
2506
|
fs.writeFileSync(wasmFile, compiled.wasm);
|
|
2180
|
-
|
|
2507
|
+
const emitWat = compiled.manifest.wat.emitted !== false;
|
|
2508
|
+
if (emitWat) fs.writeFileSync(watFile, compiled.wat, 'utf8');
|
|
2509
|
+
else fs.rmSync(watFile, { force: true });
|
|
2181
2510
|
fs.writeFileSync(planFile, `${stableStringify(compiled.plan, 2)}\n`, 'utf8');
|
|
2182
2511
|
fs.writeFileSync(manifestFile, `${stableStringify(compiled.manifest, 2)}\n`, 'utf8');
|
|
2183
|
-
return Object.freeze({ target, sourceFile, wasmFile, watFile, planFile, manifestFile, manifest: compiled.manifest });
|
|
2512
|
+
return Object.freeze({ target, sourceFile, wasmFile, watFile: emitWat ? watFile : null, planFile, manifestFile, manifest: compiled.manifest });
|
|
2184
2513
|
}
|
|
2185
2514
|
|
|
2186
2515
|
module.exports = Object.freeze({
|