@pulse-compute/provider-fastly 1.0.0-beta.10 → 1.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/package.json +11 -11
- package/src/build/canonical-target.js +6 -0
- package/src/build/effect-invocations.js +6 -4
- package/src/build/host-extensions.js +254 -0
- package/src/build/lifecycle.js +50 -0
- package/src/build/native-body-forwarding.as.ts +28 -44
- package/src/build/native-body-forwarding.js +15 -11
- package/src/build/native-fetch-accounting.as.ts +133 -0
- package/src/build/native-fetch-accounting.js +84 -0
- package/src/build/native-fetch-bounds.js +47 -0
- package/src/build/native-fetch-cohorts.as.ts +126 -0
- package/src/build/native-fetch-cohorts.js +65 -0
- package/src/build/native-fetch-deadlines.as.ts +13 -8
- package/src/build/native-fetch-deadlines.js +13 -4
- package/src/build/native-fetch-errors.js +2 -2
- package/src/build/native-fetch-headers.js +52 -0
- package/src/build/native-fetch-outcomes.js +87 -0
- package/src/build/native-fetch-timing.as.ts +74 -0
- package/src/build/native-fetch-timing.js +50 -0
- package/src/build/native-forwarding-accounting.as.ts +153 -0
- package/src/build/native-forwarding-accounting.js +47 -0
- package/src/build/native-header-lookup.js +2 -2
- package/src/build/native-http-effects.js +10 -4
- package/src/build/native-platform-capabilities.js +89 -39
- package/src/build/native-request-body.js +6 -5
- package/src/build/{request-budget.as.ts → request-budget-source.js} +12 -4
- package/src/build/request-budget.js +7 -8
- package/src/build/response-authority.js +56 -0
- package/src/javascript/fetch-adapter.js +3 -0
- package/src/javascript/runtime-host.js +1 -0
- package/src/provider-contract.js +2 -1
- package/src/testing/native-http-effects-host.js +9 -0
- package/src/testing/native-platform-capabilities-host.js +10 -0
- package/src/toolchain/index.js +2 -1
- package/src/toolchain/native-execution.js +7 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { fetchHeadersSource } = require('./native-fetch-headers.js');
|
|
4
|
+
|
|
3
5
|
const dynamicBackends = require('./native-dynamic-backends.js');
|
|
4
6
|
|
|
5
7
|
const { scalarRecordProjectionLines } = require('./schema-scalar-record.js');
|
|
@@ -7,6 +9,8 @@ const { hasJsonAdmission, tracksJsonDuplicates, schemaParserDepth, nestedJsonPro
|
|
|
7
9
|
const { nativeLoggingSource } = require('./native-stdio.js');
|
|
8
10
|
const applicationErrors = require('./native-application-errors.js');
|
|
9
11
|
const effectInvocations = require('./effect-invocations.js');
|
|
12
|
+
const lifecycleBindings = require('./lifecycle.js');
|
|
13
|
+
const hostExtensions = require('./host-extensions.js');
|
|
10
14
|
const packageApplication = require('./package-native-application.js');
|
|
11
15
|
const nativeValueBudget = require('./native-value-budget.js');
|
|
12
16
|
const { schemaNeedsValueProjection } = require('@pulse-compute/wasm-contracts/schema-json/registry');
|
|
@@ -126,6 +130,7 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
|
|
|
126
130
|
Object.freeze(['fastly_http_req', 'method_set']),
|
|
127
131
|
Object.freeze(['fastly_http_req', 'uri_set']),
|
|
128
132
|
Object.freeze(['fastly_http_req', 'header_insert']),
|
|
133
|
+
Object.freeze(['fastly_http_req', 'header_append']),
|
|
129
134
|
Object.freeze(['fastly_http_req', 'send_async']),
|
|
130
135
|
Object.freeze(['fastly_http_req', 'pending_req_wait_v2']),
|
|
131
136
|
Object.freeze(['fastly_http_resp', 'new']),
|
|
@@ -189,7 +194,8 @@ const FASTLY_NATIVE_PLATFORM_EFFECT_KIND = Object.freeze({
|
|
|
189
194
|
'kv.insertIfAbsent': 16,
|
|
190
195
|
'kv.compareAndSwap': 17,
|
|
191
196
|
'time.now': 18,
|
|
192
|
-
'crypto.digestText': 19
|
|
197
|
+
'crypto.digestText': 19,
|
|
198
|
+
'host.extension': 24
|
|
193
199
|
});
|
|
194
200
|
|
|
195
201
|
const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS = new Set([
|
|
@@ -198,6 +204,7 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS = new Set([
|
|
|
198
204
|
]);
|
|
199
205
|
|
|
200
206
|
const FASTLY_NATIVE_PLATFORM_EFFECT_KINDS = Object.freeze([
|
|
207
|
+
'host.extension',
|
|
201
208
|
'time.now',
|
|
202
209
|
'crypto.digestText',
|
|
203
210
|
'fetch',
|
|
@@ -220,6 +227,7 @@ const FASTLY_NATIVE_PLATFORM_EFFECT_KINDS = Object.freeze([
|
|
|
220
227
|
]);
|
|
221
228
|
|
|
222
229
|
const FASTLY_NATIVE_PLATFORM_CAPABILITY_KINDS = new Set([
|
|
230
|
+
'host.extension',
|
|
223
231
|
'time.now',
|
|
224
232
|
'crypto.digestText',
|
|
225
233
|
'config.get',
|
|
@@ -684,6 +692,7 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
684
692
|
if (!plan.ownership || plan.ownership.providerNeutral !== true || plan.ownership.javascriptRuntime !== false) {
|
|
685
693
|
fail('Fastly native platform capability realization requires a provider-neutral plan without JavaScript runtime ownership.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_PLAN_OWNERSHIP_INVALID', { ownership: plan.ownership });
|
|
686
694
|
}
|
|
695
|
+
const selectedExtensions = hostExtensions.select(plan, options);
|
|
687
696
|
const effects = plan.effects || [];
|
|
688
697
|
const unsupported = effects.filter((effect) => !FASTLY_NATIVE_PLATFORM_EFFECT_KINDS.includes(effect.kind));
|
|
689
698
|
if (unsupported.length > 0) {
|
|
@@ -711,7 +720,8 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
711
720
|
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
712
721
|
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
713
722
|
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
714
|
-
|
|
723
|
+
const extension = effect.kind === 'host.extension' && selectedExtensions.some(item => hostExtensions.matches(effect, item.artifact));
|
|
724
|
+
return !grip && !assets && !jwt && !s3 && !digest && !extension;
|
|
715
725
|
});
|
|
716
726
|
if (unsupportedPackages.length > 0) {
|
|
717
727
|
fail('Fastly native realization only accepts the trusted first-party Assets, GRIP, JWT, S3, and Crypto digest package contracts.', 'PULSE_FASTLY_NATIVE_PLATFORM_PACKAGE_UNSUPPORTED', {
|
|
@@ -734,7 +744,8 @@ function validatePlanBoundary(plan, options = {}) {
|
|
|
734
744
|
&& ['head', 'getText', 'putText', 'getBody'].includes(effect.operation) && effect.kind === `s3.${effect.operation}` && effect.capability === effect.kind;
|
|
735
745
|
const digest = effect.package === '@pulse-compute/crypto' && effect.contractId === 'pulse.crypto'
|
|
736
746
|
&& effect.operation === 'digestText' && effect.kind === 'crypto.digestText' && effect.capability === effect.kind;
|
|
737
|
-
|
|
747
|
+
const extension = effect.kind === 'host.extension' && selectedExtensions.some(item => hostExtensions.matches(effect, item.artifact));
|
|
748
|
+
return !grip && !assets && !jwt && !s3 && !digest && !extension;
|
|
738
749
|
});
|
|
739
750
|
if (unsupportedDeclarations.length > 0) {
|
|
740
751
|
fail('Fastly native package realization is restricted to the Assets, GRIP, JWT, S3, and Crypto digest contracts.', 'PULSE_FASTLY_NATIVE_PLATFORM_PACKAGE_UNSUPPORTED', { effects: unsupportedDeclarations });
|
|
@@ -1003,10 +1014,11 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
1003
1014
|
if (kinds.has('fetch') || kinds.has('grip.publish') || kinds.has('grip.broadcast')) {
|
|
1004
1015
|
for (const key of [
|
|
1005
1016
|
'fastly_http_req:new', 'fastly_http_req:method_set', 'fastly_http_req:uri_set',
|
|
1006
|
-
'fastly_http_req:
|
|
1017
|
+
'fastly_http_req:header_append', 'fastly_http_req:send_async', 'fastly_http_req:pending_req_wait_v2'
|
|
1007
1018
|
]) keys.add(key);
|
|
1008
1019
|
}
|
|
1009
1020
|
if (bindings && dynamicBackends.active(bindings)) keys.add('fastly_http_req:register_dynamic_backend');
|
|
1021
|
+
if (plan.effects.some(effect=>effect.fetchTiming)) keys.add('fastly_async_io:is_ready');
|
|
1010
1022
|
if (kinds.has('fetch')) for (const key of ['wasi_snapshot_preview1:clock_time_get', 'fastly_async_io:select', 'fastly_http_req:pending_req_poll_v2', 'fastly_http_resp:close', 'fastly_http_body:close']) keys.add(key);
|
|
1011
1023
|
if (planRequiresFetchBodyRead(plan)) keys.add('fastly_http_body:read');
|
|
1012
1024
|
if (kinds.has('config.get')) for (const key of ['fastly_config_store:open', 'fastly_config_store:get']) keys.add(key);
|
|
@@ -1032,6 +1044,7 @@ function requiredImportsForPlan(plan, bindings) {
|
|
|
1032
1044
|
}
|
|
1033
1045
|
if (kinds.has('s3.head') || kinds.has('s3.getText') || kinds.has('s3.putText') || kinds.has('s3.getBody')) for (const key of require('./s3-native.js').S3_IMPORTS) keys.add(key);
|
|
1034
1046
|
if (require('./native-body-forwarding.js').enabled(plan)) {
|
|
1047
|
+
keys.delete('fastly_http_req:header_append'); keys.add('fastly_http_req:header_insert');
|
|
1035
1048
|
keys.delete('fastly_http_req:send_async'); keys.delete('fastly_http_req:pending_req_wait_v2');
|
|
1036
1049
|
for (const key of require('./native-body-forwarding.js').IMPORTS) keys.add(key);
|
|
1037
1050
|
keys.add('fastly_http_body:read');
|
|
@@ -1185,6 +1198,7 @@ function generateSchemaRuntime(plan) {
|
|
|
1185
1198
|
`const __PULSE_SCHEMA_REQUIRE_JSON: bool = ${registry.contentTypePolicy === 'require-json' ? 'true' : 'false'}`,
|
|
1186
1199
|
'',
|
|
1187
1200
|
schemaJsonAdmissionSource(plan),
|
|
1201
|
+
fetchHeadersSource(),
|
|
1188
1202
|
schemaFetchJsonSource(),
|
|
1189
1203
|
...nodeLines,
|
|
1190
1204
|
`function __pulse_fastly_schema_apply(schemaId: string, valueHandle: i32, encode: bool${textEncodeIds.size ? ', textResult: bool = false' : ''}): i32 {`,
|
|
@@ -1315,7 +1329,8 @@ function effectDispatchSource(plan) {
|
|
|
1315
1329
|
'grip.channel': '__pulse_fastly_grip_channel_begin',
|
|
1316
1330
|
'grip.hold': '__pulse_fastly_grip_hold_begin',
|
|
1317
1331
|
'grip.publish': '__pulse_fastly_grip_publish_begin',
|
|
1318
|
-
'grip.broadcast': '__pulse_fastly_grip_publish_begin'
|
|
1332
|
+
'grip.broadcast': '__pulse_fastly_grip_publish_begin',
|
|
1333
|
+
'host.extension': '__pulse_extension_begin'
|
|
1319
1334
|
});
|
|
1320
1335
|
const kinds = [...new Set((plan.effects || []).map((effect) => effect.kind))];
|
|
1321
1336
|
const lines = [
|
|
@@ -1341,6 +1356,7 @@ function effectDispatchSource(plan) {
|
|
|
1341
1356
|
}
|
|
1342
1357
|
|
|
1343
1358
|
function fastlyRuntimeSource(plan, bindings, options = {}) {
|
|
1359
|
+
const lifecycle = options.lifecycle;
|
|
1344
1360
|
const textEncodeIds = schemaTextEncodeIds(plan);
|
|
1345
1361
|
const effectCount = (plan.effects || []).length;
|
|
1346
1362
|
const hasAssets = (plan.effects || []).some(effect => effect.kind === 'assets.lookup');
|
|
@@ -1414,7 +1430,7 @@ function __pulse_fastly_jwt_attach_evidence(handle: i32): void { void handle }
|
|
|
1414
1430
|
// their specific responses here. Every remaining fatal path needs a response,
|
|
1415
1431
|
// independently of whether JWT evidence was requested for this build.
|
|
1416
1432
|
const terminalErrorSource = `
|
|
1417
|
-
function __pulse_fastly_jwt_send_error(): void {
|
|
1433
|
+
${lifecycle?.active ? `function __pulse_fastly_jwt_send_error(): void {\n${lifecycle.site('response-error')}\n __pulse_fastly_jwt_send_error_impl()\n}\n` : ''}function ${lifecycle?.legacyName('__pulse_fastly_jwt_send_error') || '__pulse_fastly_jwt_send_error'}(): void {
|
|
1418
1434
|
const output = new __PulseFastlyValue()
|
|
1419
1435
|
output.kind = PULSE_VALUE_RESPONSE
|
|
1420
1436
|
output.status = ${jwtReality ? '__pulse_fastly_jwt_error != 0 ? 400 : 500' : '500'}
|
|
@@ -1442,6 +1458,7 @@ function __pulse_fastly_jwt_send_error(): void {
|
|
|
1442
1458
|
@external("fastly_http_req", "method_set") declare function fastly_http_req_method_set(handle: i32, method: usize, methodLength: i32): i32
|
|
1443
1459
|
@external("fastly_http_req", "uri_set") declare function fastly_http_req_uri_set(handle: i32, uri: usize, uriLength: i32): i32
|
|
1444
1460
|
@external("fastly_http_req", "header_insert") declare function fastly_http_req_header_insert(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32): i32
|
|
1461
|
+
@external("fastly_http_req", "header_append") declare function fastly_http_req_header_append(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32): i32
|
|
1445
1462
|
@external("fastly_http_req", "send_async") declare function fastly_http_req_send_async(handle: i32, body: i32, backend: usize, backendLength: i32, pendingOut: usize): i32
|
|
1446
1463
|
@external("fastly_http_req", "pending_req_wait_v2") declare function fastly_http_req_pending_req_wait_v2(handle: i32, detail: usize, responseOut: usize, bodyOut: usize): i32
|
|
1447
1464
|
@external("fastly_http_resp", "new") declare function fastly_http_resp_new(handleOut: usize): i32
|
|
@@ -1449,7 +1466,7 @@ function __pulse_fastly_jwt_send_error(): void {
|
|
|
1449
1466
|
@external("fastly_http_resp", "header_value_get") declare function fastly_http_resp_header_value_get(handle: i32, name: usize, nameLength: i32, value: usize, valueLength: i32, writtenOut: usize): i32
|
|
1450
1467
|
@external("fastly_http_resp", "status_get") declare function fastly_http_resp_status_get(handle: i32, statusOut: usize): i32
|
|
1451
1468
|
@external("fastly_http_resp", "status_set") declare function fastly_http_resp_status_set(handle: i32, status: i32): i32
|
|
1452
|
-
@external("fastly_http_resp", "send_downstream") declare function fastly_http_resp_send_downstream(handle: i32, body: i32, streaming: i32): i32
|
|
1469
|
+
@external("fastly_http_resp", "send_downstream") declare function ${lifecycle?.authority ? '__pulse_response_send_raw' : 'fastly_http_resp_send_downstream'}(handle: i32, body: i32, streaming: i32): i32
|
|
1453
1470
|
@external("fastly_http_body", "new") declare function fastly_http_body_new(handleOut: usize): i32
|
|
1454
1471
|
@external("fastly_http_body", "read") declare function fastly_http_body_read(handle: i32, buffer: usize, bufferLength: i32, readOut: usize): i32
|
|
1455
1472
|
@external("fastly_http_body", "write") declare function fastly_http_body_write(handle: i32, buffer: usize, bufferLength: i32, end: i32, writtenOut: usize): i32
|
|
@@ -1526,6 +1543,7 @@ ${nativeStringFields}
|
|
|
1526
1543
|
}
|
|
1527
1544
|
responseHandle: i32 = 0
|
|
1528
1545
|
bodyHandle: i32 = 0
|
|
1546
|
+
${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? ' fetchHeaders: Array<__FCA_Header> | null = null;\n fetchJsonType: i32 = 0;' : ''}
|
|
1529
1547
|
bodyLoaded: i32 = 0
|
|
1530
1548
|
s3Body: bool = false
|
|
1531
1549
|
s3Deadline: i64 = 0
|
|
@@ -1803,26 +1821,29 @@ function __pulse_fastly_json(handle: i32, depth: i32): string {
|
|
|
1803
1821
|
}
|
|
1804
1822
|
function __pulse_fastly_parse_json(text: string): i32 { const parser = new __PulseJsonParser(text); const value = parser.parse(); if (parser.failed || value <= 0) { __pulse_fastly_fail(PULSE_ERROR_JSON, 3, -1); return 0 } return value }
|
|
1805
1823
|
function __pulse_fastly_path(uri: string): string { let start = 0; const scheme = uri.indexOf("://"); if (scheme >= 0) { const slash = uri.indexOf("/", scheme + 3); start = slash >= 0 ? slash : uri.length } let end = uri.length; const query = uri.indexOf("?", start); if (query >= 0 && query < end) end = query; const fragment = uri.indexOf("#", start); if (fragment >= 0 && fragment < end) end = fragment; return start >= end ? "/" : uri.substring(start, end) }
|
|
1806
|
-
function __pulse_fastly_read_req_string(kind: i32): string { const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES); const written = __pulse_fastly_out_i32(); const status = kind == 0 ? fastly_http_req_method_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)) : fastly_http_req_uri_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 10 + kind, -1); return "" } return __pulse_fastly_decode(buffer, __pulse_fastly_out_value(written)) }
|
|
1824
|
+
function __pulse_fastly_read_req_string(kind: i32): string { const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES); const written = __pulse_fastly_out_i32(); const status = kind == 0 ? fastly_http_req_method_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)) : fastly_http_req_uri_get(__pulse_fastly_request_handle, buffer.dataStart, PULSE_FASTLY_BUFFER_BYTES, changetype<usize>(written)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 10 + kind, -1); return "" } ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? 'if (__pulse_fastly_out_value(written)<0 || __pulse_fastly_out_value(written)>buffer.byteLength) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL,10+kind,-1);return ""; }' : ''} return __pulse_fastly_decode(buffer, __pulse_fastly_out_value(written)) }
|
|
1807
1825
|
function __pulse_fastly_read_body(handle: i32, effectIndex: i32, requestText: bool = false): string {
|
|
1808
|
-
|
|
1826
|
+
// One retained input allocation, including a byte to distinguish exact-limit
|
|
1827
|
+
// EOF from overflow. Short reads never allocate another chunk or prefix.
|
|
1828
|
+
if (__PULSE_SCHEMA_MAX_BYTES <= 0 || __PULSE_SCHEMA_MAX_BYTES > 1073741823) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 21, effectIndex); return "" }
|
|
1829
|
+
const bytes = new Uint8Array(__PULSE_SCHEMA_MAX_BYTES + 1), read = __pulse_fastly_out_i32();
|
|
1830
|
+
let total = 0;
|
|
1809
1831
|
while (true) {
|
|
1810
|
-
|
|
1811
|
-
const
|
|
1832
|
+
if (!__pulse_fastly_body_read_ready(handle, effectIndex)) return "";
|
|
1833
|
+
const capacity = min<i32>(PULSE_FASTLY_BUFFER_BYTES, bytes.length - total);
|
|
1834
|
+
const status = fastly_http_body_read(handle, bytes.dataStart + total, capacity, changetype<usize>(read));
|
|
1812
1835
|
if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 20, effectIndex); return "" }
|
|
1813
1836
|
const count = __pulse_fastly_out_value(read);
|
|
1814
|
-
if (count < 0 || count >
|
|
1837
|
+
if (count < 0 || count > capacity) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 20, effectIndex); return "" }
|
|
1815
1838
|
if (!count) break;
|
|
1816
1839
|
total += count;
|
|
1817
|
-
if (total > __PULSE_SCHEMA_MAX_BYTES
|
|
1818
|
-
chunks.push(buffer.subarray(0, count));
|
|
1840
|
+
if (total > __PULSE_SCHEMA_MAX_BYTES) { __pulse_fastly_fail(requestText ? PULSE_ERROR_REQUEST_BODY : PULSE_ERROR_SCHEMA, 21, effectIndex); return "" }
|
|
1819
1841
|
}
|
|
1820
1842
|
// UTF-8 scalars may straddle host read chunks. Decode the bounded body once.
|
|
1821
|
-
|
|
1822
|
-
for (let i = 0; i < chunks.length; i++) { bytes.set(chunks[i], offset); offset += chunks[i].length; }
|
|
1823
|
-
if (requestText && !__pulse_request_utf8_valid(bytes)) { __pulse_fastly_fail(PULSE_ERROR_REQUEST_BODY, 24, effectIndex); return "" }
|
|
1843
|
+
if (requestText && !__pulse_request_utf8_valid(bytes, total)) { __pulse_fastly_fail(PULSE_ERROR_REQUEST_BODY, 24, effectIndex); return "" }
|
|
1824
1844
|
return __pulse_fastly_decode(bytes, total);
|
|
1825
1845
|
}
|
|
1846
|
+
function __pulse_fastly_body_read_ready(handle: i32, effectIndex: i32): bool { return true }
|
|
1826
1847
|
function __pulse_fastly_request_header_text(name: string): string { const value = __pulse_header_lookup(__pulse_fastly_request_handle, name, false); return value === null ? "" : value }
|
|
1827
1848
|
function __pulse_fastly_fetch_header_text(responseHandle: i32, name: string): string { const value = __pulse_header_lookup(responseHandle, name, true); return value === null ? "" : value }
|
|
1828
1849
|
function __pulse_fastly_fetch_body(value: __PulseFastlyValue): string { if (value.bodyLoaded == 0) { value.text = __pulse_fastly_read_body(value.bodyHandle, -1); value.bodyLoaded = 1 } return value.text }
|
|
@@ -1864,7 +1885,7 @@ function host_value_array_spread(target: i32, source: i32): void { const from =
|
|
|
1864
1885
|
function host_value_object(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_OBJECT; return __pulse_fastly_put(item) }
|
|
1865
1886
|
function host_value_object_set(target: i32, keyHandle: i32, value: i32): void { const item = __pulse_fastly_value(target); if (item.kind != PULSE_VALUE_OBJECT || item.immutable) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 32, -1); return } const key = __pulse_fastly_string(keyHandle); const index = __pulse_fastly_find(item, key); if (index >= 0) unchecked(item.values[index] = value); else { item.keys.push(key); item.values.push(value) } }
|
|
1866
1887
|
function host_value_object_spread(target: i32, source: i32): void { const from = __pulse_fastly_value(source); if (from.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 33, -1); return } for (let i = 0; i < from.keys.length; i += 1) host_value_object_set(target, __pulse_fastly_string_value(unchecked(from.keys[i])), unchecked(from.values[i])) }
|
|
1867
|
-
function host_value_property(target: i32, keyHandle: i32): i32 { const item = __pulse_fastly_value(target); const key = __pulse_fastly_string(keyHandle); if (item.kind == PULSE_VALUE_OBJECT) { const index = __pulse_fastly_find(item, key); return index >= 0 ? unchecked(item.values[index]) : host_value_undefined() } if (item.kind == PULSE_VALUE_FETCH && key == "status") { const out = __pulse_fastly_out_i32(); const status = fastly_http_resp_status_get(item.responseHandle, changetype<usize>(out)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 34, -1); return 0 } return host_value_number(__pulse_fastly_out_value(out)) } if ((item.kind == PULSE_VALUE_STRING || item.kind == PULSE_VALUE_ARRAY) && key == "length") return host_value_number(item.kind == PULSE_VALUE_STRING ? item.textLength : item.values.length); return host_value_undefined() }
|
|
1888
|
+
function host_value_property(target: i32, keyHandle: i32): i32 { const item = __pulse_fastly_value(target); const key = __pulse_fastly_string(keyHandle); if (item.kind == PULSE_VALUE_OBJECT) { const index = __pulse_fastly_find(item, key); return index >= 0 ? unchecked(item.values[index]) : host_value_undefined() } if (item.kind == PULSE_VALUE_FETCH && key == "status") { ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? 'return __fca_status(item);' : ''} const out = __pulse_fastly_out_i32(); const status = fastly_http_resp_status_get(item.responseHandle, changetype<usize>(out)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 34, -1); return 0 } return host_value_number(__pulse_fastly_out_value(out)) } if ((item.kind == PULSE_VALUE_STRING || item.kind == PULSE_VALUE_ARRAY) && key == "length") return host_value_number(item.kind == PULSE_VALUE_STRING ? item.textLength : item.values.length); return host_value_undefined() }
|
|
1868
1889
|
function host_value_property_set(target: i32, key: i32, value: i32): i32 { host_value_object_set(target, key, value); return value }
|
|
1869
1890
|
function host_value_element(target: i32, keyHandle: i32): i32 { const item = __pulse_fastly_value(target); if (item.kind == PULSE_VALUE_STRING) return __pulse_fastly_string_index(item, keyHandle); if (item.kind == PULSE_VALUE_ARRAY) { const index = i32(__pulse_fastly_number(keyHandle)); return index >= 0 && index < item.values.length ? unchecked(item.values[index]) : host_value_undefined() } return host_value_property(target, keyHandle) }
|
|
1870
1891
|
function host_value_element_set(target: i32, keyHandle: i32, value: i32): i32 { const item = __pulse_fastly_value(target); if (item.immutable) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 35, -1); return 0 } if (item.kind == PULSE_VALUE_ARRAY) { const index = i32(__pulse_fastly_number(keyHandle)); if (index < 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 35, -1); return 0 } while (item.values.length <= index) item.values.push(host_value_undefined()); unchecked(item.values[index] = value); return value } return host_value_property_set(target, keyHandle, value) }
|
|
@@ -1920,8 +1941,8 @@ function host_grip_subscribe(responseHandle: i32, optionsHandle: i32): i32 { con
|
|
|
1920
1941
|
function host_grip_handoff(optionsHandle: i32): i32 { const options = __pulse_fastly_value(optionsHandle); if (options.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 139, -1); return 0 } const output = new __PulseFastlyValue(); output.kind = PULSE_VALUE_RESPONSE; output.status = 200; const statusIndex = __pulse_fastly_find(options, "status"); if (statusIndex >= 0) output.status = i32(__pulse_fastly_number(unchecked(options.values[statusIndex]))); const bodyIndex = __pulse_fastly_find(options, "body"); if (bodyIndex >= 0) output.text = __pulse_fastly_string(unchecked(options.values[bodyIndex])); __pulse_fastly_headers_from_options(options, output); let hasContentType = false; for (let i = 0; i < output.headers.length; i += 1) if (unchecked(output.headers[i]).name.toLowerCase() == "content-type") hasContentType = true; if (!hasContentType) output.headers.push(new __PulseFastlyHeader("Content-Type", "application/websocket-events")); return __pulse_fastly_grip_frame(options, output) ? __pulse_fastly_put(output) : 0 }
|
|
1921
1942
|
function host_kv_namespace(name: i32): i32 { return name }
|
|
1922
1943
|
function host_fetch_text(responseHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 42, -1); return 0 } return __pulse_fastly_string_value(__pulse_fastly_fetch_body(response)) }
|
|
1923
|
-
function host_fetch_json(responseHandle: i32, schemaHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 43, -1); return 0 } if (__PULSE_SCHEMA_REQUIRE_JSON) { const contentType = __pulse_fastly_fetch_header_text(response.responseHandle, "content-type"); if (contentType.length == 0 || contentType.toLowerCase().indexOf("json") < 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 44, -1); return 0 } } const parsed = ${hasJsonAdmission(plan) ? '__pulse_fastly_schema_parse_json(__pulse_fastly_fetch_body(response), schemaHandle)' : '__pulse_fastly_parse_json(__pulse_fastly_fetch_body(response))'}; if (parsed <= 0) return 0; const schema = __pulse_fastly_value(schemaHandle); return schema.kind == PULSE_VALUE_STRING ? __pulse_fastly_schema_apply(schema.text, parsed, false) : parsed }
|
|
1924
|
-
function host_fetch_header(responseHandle: i32, nameHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 45, -1); return 0 } const value = __pulse_header_lookup(response.responseHandle, __pulse_fastly_string(nameHandle), true); return value === null ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1944
|
+
function host_fetch_json(responseHandle: i32, schemaHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 43, -1); return 0 } if (__PULSE_SCHEMA_REQUIRE_JSON) { ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? 'if (!__fca_json_type(response)) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA,44,-1); return 0; }' : `${options.fetchAccounting?.selection === 'canonical-single-get-json' ? 'if (!__fca_work()) return 0;' : ''} const contentType = __pulse_fastly_fetch_header_text(response.responseHandle, "content-type"); if (contentType.length == 0 || contentType.toLowerCase().indexOf("json") < 0) { __pulse_fastly_fail(PULSE_ERROR_SCHEMA, 44, -1); return 0 }`} } const parsed = ${hasJsonAdmission(plan) ? '__pulse_fastly_schema_parse_json(__pulse_fastly_fetch_body(response), schemaHandle)' : '__pulse_fastly_parse_json(__pulse_fastly_fetch_body(response))'}; if (parsed <= 0) return 0; const schema = __pulse_fastly_value(schemaHandle); return schema.kind == PULSE_VALUE_STRING ? __pulse_fastly_schema_apply(schema.text, parsed, false) : parsed }
|
|
1945
|
+
function host_fetch_header(responseHandle: i32, nameHandle: i32): i32 { const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 45, -1); return 0 } const value = ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? '__fca_header(response, __pulse_fastly_string(nameHandle))' : '__pulse_header_lookup(response.responseHandle, __pulse_fastly_string(nameHandle), true)'}; return value === null ? host_value_undefined() : __pulse_fastly_string_value(value) }
|
|
1925
1946
|
function __pulse_fastly_payload_field(payload: __PulseFastlyValue, name: string): i32 { const index = __pulse_fastly_find(payload, name); return index < 0 ? host_value_undefined() : unchecked(payload.values[index]) }
|
|
1926
1947
|
function __pulse_fastly_ready_effect(effectIndex: i32, handle: i32): void { unchecked(__pulse_fastly_ready[effectIndex] = handle); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_READY) }
|
|
1927
1948
|
function __pulse_fastly_open_config_store(effectIndex: i32): i32 { const nameBytes = __pulse_fastly_utf8(__pulse_fastly_config_store(effectIndex)); const out = __pulse_fastly_out_i32(); const status = fastly_config_store_open(changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(out)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 110, effectIndex); return 0 } return __pulse_fastly_out_value(out) }
|
|
@@ -1943,8 +1964,8 @@ function __pulse_fastly_jwt_capture_clock(effectIndex: i32): f64 { __pulse_fastl
|
|
|
1943
1964
|
function __pulse_fastly_jwt_validate_schema(effectIndex: i32, schemaId: string, claimsHandle: i32): i32 { __pulse_fastly_jwt_schema_calls += 1; __pulse_fastly_jwt_event_order += "H"; if (__pulse_fastly_jwt_case_id() == "claims-schema-failure-after-registered-claims") return 0; return __pulse_fastly_schema_apply(schemaId, claimsHandle, false) }
|
|
1944
1965
|
function __pulse_fastly_jwt_sign_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const result = pulse_jwt_fastly_sign(effectIndex, unchecked(__pulse_fastly_payloads[effectIndex])); if (result > 0 && __pulse_fastly_last_error == 0) __pulse_fastly_ready_effect(effectIndex, result) }
|
|
1945
1966
|
function __pulse_fastly_jwt_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const result = pulse_jwt_fastly_verify(effectIndex, unchecked(__pulse_fastly_payloads[effectIndex])); if (result > 0 && __pulse_fastly_last_error == 0) __pulse_fastly_ready_effect(effectIndex, result) }
|
|
1946
|
-
function __pulse_fastly_start_request(effectIndex: i32, url: string, method: string, headers: __PulseFastlyValue, body: string, backend: string): void { __pulse_transport_slots[effectIndex] = null; const requestOut = __pulse_fastly_out_i32(); let status = fastly_http_req_new(changetype<usize>(requestOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 70, effectIndex); return } const requestHandle = __pulse_fastly_out_value(requestOut); const bodyOut = __pulse_fastly_out_i32(); status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 71, effectIndex); return } const bodyHandle = __pulse_fastly_out_value(bodyOut); const methodBytes = __pulse_fastly_utf8(method); status = fastly_http_req_method_set(requestHandle, changetype<usize>(methodBytes), methodBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 72, effectIndex); return } const urlBytes = __pulse_fastly_utf8(url); status = fastly_http_req_uri_set(requestHandle, changetype<usize>(urlBytes), urlBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 73, effectIndex); return } if (headers.kind == PULSE_VALUE_OBJECT) { for (let i = 0; i < headers.keys.length; i += 1) { const nameBytes = __pulse_fastly_utf8(unchecked(headers.keys[i])); const valueBytes = __pulse_fastly_utf8(__pulse_fastly_string(unchecked(headers.values[i]))); status =
|
|
1947
|
-
function __pulse_fastly_fetch_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const urlIndex = __pulse_fastly_find(payload, "url"); if (urlIndex < 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 48, effectIndex); return } const url = __pulse_fastly_string(unchecked(payload.values[urlIndex])); const initIndex = __pulse_fastly_find(payload, "init"); const init = initIndex >= 0 ? __pulse_fastly_value(unchecked(payload.values[initIndex])) : new __PulseFastlyValue(); let method = "GET"; let body = ""; let headers = new __PulseFastlyValue(); headers.kind = PULSE_VALUE_OBJECT; if (init.kind == PULSE_VALUE_OBJECT) { const methodIndex = __pulse_fastly_find(init, "method"); if (methodIndex >= 0) method = __pulse_fastly_string(unchecked(init.values[methodIndex])).toUpperCase(); const headersIndex = __pulse_fastly_find(init, "headers"); if (headersIndex >= 0) headers = __pulse_fastly_value(unchecked(init.values[headersIndex])); const bodyIndex = __pulse_fastly_find(init, "body"); const jsonIndex = __pulse_fastly_find(init, "json"); if (bodyIndex >= 0) { const bodyValue = unchecked(init.values[bodyIndex]); const bodyObject = __pulse_fastly_value(bodyValue); body = bodyObject.kind == PULSE_VALUE_STRING ? bodyObject.text : __pulse_fastly_json(bodyValue, 0) } else if (jsonIndex >= 0) { body = __pulse_fastly_fetch_json_body(init, unchecked(init.values[jsonIndex])); if (__pulse_fastly_last_error != 0) return; headers = __pulse_fastly_fetch_json_headers(headers) } } __pulse_fastly_start_request(effectIndex, url, method, headers, body, __pulse_fastly_backend(effectIndex)) }
|
|
1967
|
+
function __pulse_fastly_start_request(effectIndex: i32, url: string, method: string, headers: __PulseFastlyValue, body: string, backend: string): void { __pulse_transport_slots[effectIndex] = null; const requestOut = __pulse_fastly_out_i32(); let status = ${options.fetchAccounting ? '__fca_new(0,' : 'fastly_http_req_new('}changetype<usize>(requestOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 70, effectIndex); return } const requestHandle = __pulse_fastly_out_value(requestOut); const bodyOut = __pulse_fastly_out_i32(); status = ${options.fetchAccounting ? '__fca_new(1,' : 'fastly_http_body_new('}changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 71, effectIndex); return } const bodyHandle = __pulse_fastly_out_value(bodyOut); const methodBytes = __pulse_fastly_utf8(method); ${options.fetchAccounting ? 'if (!__fca_work()) return;' : ''} status = fastly_http_req_method_set(requestHandle, changetype<usize>(methodBytes), methodBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 72, effectIndex); return } const urlBytes = __pulse_fastly_utf8(url); ${options.fetchAccounting ? 'if (!__fca_work()) return;' : ''} status = fastly_http_req_uri_set(requestHandle, changetype<usize>(urlBytes), urlBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 73, effectIndex); return } if (headers.kind == PULSE_VALUE_OBJECT) { for (let i = 0; i < headers.keys.length; i += 1) { const nameBytes = __pulse_fastly_utf8(unchecked(headers.keys[i])); const valueBytes = __pulse_fastly_utf8(__pulse_fastly_string(unchecked(headers.values[i]))); ${options.fetchAccounting ? 'if (!__fca_work()) return;' : ''} status = fastly_http_req_header_append(requestHandle, changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(valueBytes), valueBytes.byteLength); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 74, effectIndex); return } } } if (body.length > 0) { status = ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? '__fca_write' : '__pulse_fastly_write_body'}(bodyHandle, body); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 75, effectIndex); return } } if (backend.length == 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 76, effectIndex); return } const backendBytes = __pulse_fastly_utf8(backend); const pendingOut = __pulse_fastly_out_i32(); status = ${options.fetchAccounting ? '__fca_dispatch' : 'fastly_http_req_send_async'}(requestHandle, bodyHandle, changetype<usize>(backendBytes), backendBytes.byteLength, changetype<usize>(pendingOut)); if (status != FASTLY_STATUS_OK) { ${options.fetchAccounting ? '__fca_cleanup();' : '__pulse_fetch_close_request(requestHandle); __pulse_fetch_close_body(bodyHandle);'} __pulse_transport_capture(effectIndex, status, 'send'); __pulse_fetch_fail(status, 77, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(pendingOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
1968
|
+
function __pulse_fastly_fetch_begin(effectIndex: i32, payload: __PulseFastlyValue): void { ${options.fetchAccounting ? options.fetchAccounting.selection === 'canonical-fetch-cohorts' ? 'if (!__fca_start(effectIndex)) return;' : 'if (!__fca_start()) return;' : ''} const urlIndex = __pulse_fastly_find(payload, "url"); if (urlIndex < 0) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 48, effectIndex); return } const url = __pulse_fastly_string(unchecked(payload.values[urlIndex])); const initIndex = __pulse_fastly_find(payload, "init"); const init = initIndex >= 0 ? __pulse_fastly_value(unchecked(payload.values[initIndex])) : new __PulseFastlyValue(); let method = "GET"; let body = ""; let headers = new __PulseFastlyValue(); headers.kind = PULSE_VALUE_OBJECT; if (init.kind == PULSE_VALUE_OBJECT) { const methodIndex = __pulse_fastly_find(init, "method"); if (methodIndex >= 0) method = __pulse_fastly_string(unchecked(init.values[methodIndex])).toUpperCase(); const headersIndex = __pulse_fastly_find(init, "headers"); if (headersIndex >= 0) headers = __pulse_fastly_value(unchecked(init.values[headersIndex])); headers = __pulse_fastly_fetch_headers(headers, effectIndex); if (__pulse_fastly_last_error != 0) return; const bodyIndex = __pulse_fastly_find(init, "body"); const jsonIndex = __pulse_fastly_find(init, "json"); if (bodyIndex >= 0) { const bodyValue = unchecked(init.values[bodyIndex]); const bodyObject = __pulse_fastly_value(bodyValue); body = bodyObject.kind == PULSE_VALUE_STRING ? bodyObject.text : __pulse_fastly_json(bodyValue, 0) } else if (jsonIndex >= 0) { body = __pulse_fastly_fetch_json_body(init, unchecked(init.values[jsonIndex])); if (__pulse_fastly_last_error != 0) return; headers = __pulse_fastly_fetch_json_headers(headers) } } __pulse_fastly_start_request(effectIndex, url, method, headers, body, __pulse_fastly_backend(effectIndex)) }
|
|
1948
1969
|
function __pulse_fastly_kv_get_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const store = __pulse_fastly_open_kv_store(effectIndex); if (__pulse_fastly_last_error != 0) return; const key = __pulse_fastly_string(__pulse_fastly_payload_field(payload, "key")); const keyBytes = __pulse_fastly_utf8(key); const lookupOut = __pulse_fastly_out_i32(); const config = new StaticArray<i32>(1); const status = fastly_kv_store_lookup(store, changetype<usize>(keyBytes), keyBytes.byteLength, 0, changetype<usize>(config), changetype<usize>(lookupOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 116, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(lookupOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
1949
1970
|
${assetBeginSource}
|
|
1950
1971
|
function __pulse_fastly_kv_put_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const store = __pulse_fastly_open_kv_store(effectIndex); if (__pulse_fastly_last_error != 0) return; const key = __pulse_fastly_string(__pulse_fastly_payload_field(payload, "key")); const value = __pulse_fastly_payload_field(payload, "value"); const bodyOut = __pulse_fastly_out_i32(); let status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 117, effectIndex); return } const body = __pulse_fastly_out_value(bodyOut); status = __pulse_fastly_write_body(body, "{\"__pulseKv\":1,\"value\":" + __pulse_fastly_json(value, 0) + "}"); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 118, effectIndex); return } const keyBytes = __pulse_fastly_utf8(key); const insertOut = __pulse_fastly_out_i32(); const config = new StaticArray<i64>(4); status = fastly_kv_store_insert(store, changetype<usize>(keyBytes), keyBytes.byteLength, body, 0, changetype<usize>(config), changetype<usize>(insertOut)); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 119, effectIndex); return } unchecked(__pulse_fastly_pending[effectIndex] = __pulse_fastly_out_value(insertOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_ASYNC) }
|
|
@@ -1963,7 +1984,7 @@ function __pulse_fastly_wait_kv_get(effectIndex: i32): i32 { if (unchecked(__pul
|
|
|
1963
1984
|
${assetResultSource}
|
|
1964
1985
|
function __pulse_fastly_wait_kv_put(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 126, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const kvError = __pulse_fastly_out_i32(); const status = fastly_kv_store_insert_wait(pending, changetype<usize>(kvError)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK || __pulse_fastly_out_value(kvError) != 1) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 127, effectIndex); return 0 } return host_value_boolean(1) }
|
|
1965
1986
|
function __pulse_fastly_write_body(bodyHandle: i32, value: string): i32 { const bytes = __pulse_fastly_utf8(value); let offset = 0; while (offset < bytes.byteLength) { const written = __pulse_fastly_out_i32(); const status = fastly_http_body_write(bodyHandle, changetype<usize>(bytes) + offset, bytes.byteLength - offset, 0, changetype<usize>(written)); if (status != FASTLY_STATUS_OK) return status; const count = __pulse_fastly_out_value(written); if (count <= 0) return 1; offset += count } return FASTLY_STATUS_OK }
|
|
1966
|
-
function __pulse_fastly_send_result(handle: i32): i32 { const value = __pulse_fastly_value(handle); ${bindings.s3.length ? 'if (value.s3Body) return __s3_send_body(value);' : ''} if (value.kind == PULSE_VALUE_FETCH) return fastly_http_resp_send_downstream(value.responseHandle, value.bodyHandle, 0); if (value.kind != PULSE_VALUE_RESPONSE) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 90, -1); return PULSE_ERROR_VALUE } const responseOut = __pulse_fastly_out_i32(); let status = fastly_http_resp_new(changetype<usize>(responseOut)); if (status != FASTLY_STATUS_OK) return status; const responseHandle = __pulse_fastly_out_value(responseOut); const bodyOut = __pulse_fastly_out_i32(); status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) return status; const bodyHandle = __pulse_fastly_out_value(bodyOut); status = fastly_http_resp_status_set(responseHandle, value.status); if (status != FASTLY_STATUS_OK) return status; for (let i = 0; i < value.headers.length; i += 1) { const header = unchecked(value.headers[i]); const nameBytes = __pulse_fastly_utf8(header.name); const valueBytes = __pulse_fastly_utf8(header.value); status = fastly_http_resp_header_append(responseHandle, changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(valueBytes), valueBytes.byteLength); if (status != FASTLY_STATUS_OK) return status } status = __pulse_fastly_write_body(bodyHandle, value.text); if (status != FASTLY_STATUS_OK) return status; return fastly_http_resp_send_downstream(responseHandle, bodyHandle, 0) }
|
|
1987
|
+
${lifecycle?.active ? `function __pulse_fastly_send_result(handle: i32): i32 {\n${lifecycle.site('response-send')}\n return __pulse_fastly_send_result_impl(handle)\n}\n` : ''}function ${lifecycle?.legacyName('__pulse_fastly_send_result') || '__pulse_fastly_send_result'}(handle: i32): i32 { const value = __pulse_fastly_value(handle); ${bindings.s3.length ? 'if (value.s3Body) return __s3_send_body(value);' : ''} if (value.kind == PULSE_VALUE_FETCH) return ${options.fetchAccounting?.selection === 'canonical-fetch-cohorts' ? '__fca_transfer(value.responseHandle, value.bodyHandle)' : 'fastly_http_resp_send_downstream(value.responseHandle, value.bodyHandle, 0)'}; if (value.kind != PULSE_VALUE_RESPONSE) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 90, -1); return PULSE_ERROR_VALUE } const responseOut = __pulse_fastly_out_i32(); let status = fastly_http_resp_new(changetype<usize>(responseOut)); if (status != FASTLY_STATUS_OK) return status; const responseHandle = __pulse_fastly_out_value(responseOut); const bodyOut = __pulse_fastly_out_i32(); status = fastly_http_body_new(changetype<usize>(bodyOut)); if (status != FASTLY_STATUS_OK) return status; const bodyHandle = __pulse_fastly_out_value(bodyOut); status = fastly_http_resp_status_set(responseHandle, value.status); if (status != FASTLY_STATUS_OK) return status; for (let i = 0; i < value.headers.length; i += 1) { const header = unchecked(value.headers[i]); const nameBytes = __pulse_fastly_utf8(header.name); const valueBytes = __pulse_fastly_utf8(header.value); status = fastly_http_resp_header_append(responseHandle, changetype<usize>(nameBytes), nameBytes.byteLength, changetype<usize>(valueBytes), valueBytes.byteLength); if (status != FASTLY_STATUS_OK) return status } status = __pulse_fastly_write_body(bodyHandle, value.text); if (status != FASTLY_STATUS_OK) return status; return fastly_http_resp_send_downstream(responseHandle, bodyHandle, 0) }
|
|
1967
1988
|
|
|
1968
1989
|
${jwtEvidenceSource}
|
|
1969
1990
|
${terminalErrorSource}
|
|
@@ -1980,6 +2001,7 @@ ${effectResultSource(plan, bindings)}
|
|
|
1980
2001
|
}
|
|
1981
2002
|
|
|
1982
2003
|
function driverSource(plan, options = {}) {
|
|
2004
|
+
const lifecycle = options.lifecycle;
|
|
1983
2005
|
const entryName = options.guestLinked === true
|
|
1984
2006
|
? 'pulse_fastly_handle'
|
|
1985
2007
|
: '_start';
|
|
@@ -1996,18 +2018,18 @@ export function ${entryName}(): void {
|
|
|
1996
2018
|
if (__pulse_invocation_started) { __pulse_fastly_fail(PULSE_ERROR_STATE, 171, -1); return }
|
|
1997
2019
|
__pulse_invocation_started = true
|
|
1998
2020
|
__pulse_fastly_run_invocation()
|
|
1999
|
-
${plan.effects.some(e => e.kind === 's3.getBody') ? '__s3_close_bodies()' : ''}
|
|
2021
|
+
${lifecycle?.site('invocation-end') ? lifecycle.site('invocation-end') + '\n' : ''} ${plan.effects.some(e => e.kind === 's3.getBody') ? '__s3_close_bodies()' : ''}
|
|
2000
2022
|
__pulse_invocation_close()
|
|
2001
2023
|
}
|
|
2002
2024
|
function __pulse_fastly_run_invocation(): void {
|
|
2003
2025
|
__pulse_fastly_last_error = fastly_abi_init(PULSE_FASTLY_EFFECTS_ABI_VERSION)
|
|
2004
2026
|
if (__pulse_fastly_last_error != FASTLY_STATUS_OK) { __pulse_fastly_jwt_send_error(); return }
|
|
2005
2027
|
const handles = new StaticArray<i32>(2)
|
|
2006
|
-
const downstreamStatus = fastly_http_req_body_downstream_get(changetype<usize>(handles), changetype<usize>(handles) + 4)
|
|
2028
|
+
const downstreamStatus = ${options.resourceAccounting ? '__fra_incoming' : 'fastly_http_req_body_downstream_get'}(changetype<usize>(handles), changetype<usize>(handles) + 4)
|
|
2007
2029
|
if (downstreamStatus != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 100, -1); __pulse_fastly_jwt_send_error(); return }
|
|
2008
2030
|
__pulse_fastly_request_handle = load<i32>(changetype<usize>(handles))
|
|
2009
2031
|
__pulse_fastly_request_body_handle = load<i32>(changetype<usize>(handles) + 4)
|
|
2010
|
-
__pulse_fastly_request_method = __pulse_fastly_read_req_string(0)
|
|
2032
|
+
${lifecycle?.site('request-acquired') ? lifecycle.site('request-acquired') + '\n' : ''} __pulse_fastly_request_method = __pulse_fastly_read_req_string(0)
|
|
2011
2033
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
2012
2034
|
__pulse_fastly_request_url = __pulse_fastly_read_req_string(1)
|
|
2013
2035
|
if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
|
|
@@ -2027,7 +2049,7 @@ ${packageApplication.enabled(plan) ? packageApplication.driverLoop() : applicati
|
|
|
2027
2049
|
if (runStatus != 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 102, -1); __pulse_fastly_jwt_send_error(); return }
|
|
2028
2050
|
const result = pulse_result_handle()
|
|
2029
2051
|
if (result <= 0) { __pulse_fastly_fail(PULSE_ERROR_STATE, 103, -1); __pulse_fastly_jwt_send_error(); return }
|
|
2030
|
-
__pulse_fastly_jwt_attach_evidence(result)
|
|
2052
|
+
${lifecycle?.site('result-claim') ? lifecycle.site('result-claim') + '\n' : ''} __pulse_fastly_jwt_attach_evidence(result)
|
|
2031
2053
|
const sendStatus = __pulse_fastly_send_result(result)
|
|
2032
2054
|
if (sendStatus != FASTLY_STATUS_OK) __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 104, -1)
|
|
2033
2055
|
}
|
|
@@ -2096,13 +2118,22 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
2096
2118
|
const packageDuration = plan.packages?.application?.requestLimits?.maxDurationMs;
|
|
2097
2119
|
if (packageDuration !== undefined) options = { ...options, maxDurationMs: Math.min(options.maxDurationMs ?? packageDuration, packageDuration) };
|
|
2098
2120
|
const bindings = resolveCapabilityBindings(plan, options);
|
|
2121
|
+
const selectedExtensions = hostExtensions.select(plan, options);
|
|
2122
|
+
const extensionPolicy = selectedExtensions.length ? hostExtensions.policy(selectedExtensions,
|
|
2123
|
+
(options.targetDescriptor?.finalWasmPolicy?.permittedImports || FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS.map(([module, name]) => ({ module, name, kind: 'function' })))) : undefined;
|
|
2124
|
+
const forwardingAccounting = require('./native-forwarding-accounting.js').profile(plan, bindings);
|
|
2125
|
+
const fetchAccounting = require('./native-fetch-accounting.js').profile(plan, bindings, FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES)
|
|
2126
|
+
|| require('./native-fetch-cohorts.js').profile(plan, bindings, FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES);
|
|
2127
|
+
const resourceAccounting = forwardingAccounting || fetchAccounting;
|
|
2128
|
+
// Trusted provider-internal services, never a project/package hook API.
|
|
2129
|
+
const lifecycle = lifecycleBindings.create(plan, bindings, [...require('./native-fetch-accounting.js').hooks(fetchAccounting), ...hostExtensions.hooks(selectedExtensions), ...(options.lifecycleServices || [])]);
|
|
2099
2130
|
// Keep final provider eligibility authoritative over the portable artifact
|
|
2100
2131
|
// and caller options. Only the selected crypto guest uses the linked policy;
|
|
2101
2132
|
// that policy excludes active codecs regardless of JSON arena configuration.
|
|
2102
2133
|
const numericRegions = facts.guestUnits.length === 0 ? 'fastly-numeric-regions-v2'
|
|
2103
2134
|
: jwtCrypto && jwtCrypto.guestUnitRequired ? 'fastly-linked-numeric-regions-v1' : undefined;
|
|
2104
2135
|
const portable = packageApplication.enabled(plan) ? packageApplication.portable(plan) : generateCanonicalNativeAssemblyScript(plan, {
|
|
2105
|
-
...options, numericRegions,
|
|
2136
|
+
...options, numericRegions, lifecycle,
|
|
2106
2137
|
handleOwnership: facts.guestUnits.length === 0 ? 'fastly-handle-tracing-v1' : undefined
|
|
2107
2138
|
});
|
|
2108
2139
|
nativeHandleOwnership.validateRoots(plan, portable.manifest);
|
|
@@ -2115,12 +2146,13 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
2115
2146
|
'export function __pulse_fastly_abort(message: string | null, fileName: string | null = null, line: u32 = 0, column: u32 = 0): void { unreachable() }',
|
|
2116
2147
|
'',
|
|
2117
2148
|
fastlyRuntimeSource(plan, bindings, {
|
|
2118
|
-
...options,
|
|
2149
|
+
...options, lifecycle, fetchAccounting,
|
|
2119
2150
|
handleTracing: portable.manifest.handleOwnership?.enabled === true,
|
|
2120
2151
|
jwtCrypto,
|
|
2121
2152
|
es256KeyRecords
|
|
2122
2153
|
}),
|
|
2123
2154
|
jwtSource ? jwtSource.source : '',
|
|
2155
|
+
...(selectedExtensions.length ? [hostExtensions.source(selectedExtensions)] : []),
|
|
2124
2156
|
((plan.effects || []).some(effect => ['fetch', 'grip.publish', 'grip.broadcast'].includes(effect.kind)) || bindings.s3.length > 0)
|
|
2125
2157
|
? fs.readFileSync(path.join(__dirname, 'native-send-error-detail.as.ts'), 'utf8') : '',
|
|
2126
2158
|
require('./s3-native.js').s3NativeSource(plan, bindings),
|
|
@@ -2133,29 +2165,39 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
2133
2165
|
require('./native-header-lookup.js').runtimeSource(),
|
|
2134
2166
|
fs.readFileSync(path.join(__dirname, 'native-transport-details.as.ts'), 'utf8'),
|
|
2135
2167
|
require('./native-fetch-errors.js').runtimeSource(dynamicBackends.active(bindings)),
|
|
2136
|
-
require('./native-fetch-deadlines.js').runtimeSource(plan),
|
|
2168
|
+
require('./native-fetch-deadlines.js').runtimeSource(plan, fetchAccounting),
|
|
2137
2169
|
applicationErrors.settlementEnabled(plan) ? applicationErrors.runtimeSource(plan) : '',
|
|
2138
|
-
effectInvocations.runtimeSource({ packageCompletion: packageApplication.enabled(plan) }),
|
|
2170
|
+
effectInvocations.runtimeSource({ packageCompletion: packageApplication.enabled(plan), lifecycle }),
|
|
2139
2171
|
...(portable.manifest.numericRegions?.accounting === 'fastly-read-loop-v1' && portable.manifest.numericRegions.regions.length > 0
|
|
2140
2172
|
? [nativeValueBudget.numericRegionSource()] : []),
|
|
2141
2173
|
portableSource,
|
|
2142
2174
|
nativeHandleOwnership.providerSource(plan, portable.manifest.handleOwnership?.enabled === true),
|
|
2143
|
-
driverSource(plan, { guestLinked: facts.guestUnits.length > 0 }),
|
|
2175
|
+
driverSource(plan, { guestLinked: facts.guestUnits.length > 0, lifecycle, resourceAccounting: forwardingAccounting }),
|
|
2176
|
+
...(forwardingAccounting ? [require('./native-forwarding-accounting.js').source(forwardingAccounting)] : []),
|
|
2177
|
+
...(fetchAccounting ? [require(fetchAccounting.selection === 'canonical-fetch-cohorts' ? './native-fetch-cohorts.js' : './native-fetch-accounting.js').source(fetchAccounting)] : []),
|
|
2144
2178
|
''
|
|
2145
2179
|
].join('\n');
|
|
2146
2180
|
if (applicationErrors.settlementEnabled(plan)) source = applicationErrors.instrument(source);
|
|
2147
2181
|
source = require('./native-request-headers.js').instrument(source, applicationErrors.enabled(plan), /\bhost_request_headers\(/.test(portableSource));
|
|
2148
|
-
source = require('./native-header-lookup.js').instrument(source);
|
|
2149
|
-
source = require('./native-fetch-errors.js').instrument(source);
|
|
2150
|
-
source = require('./native-fetch-deadlines.js').instrument(source, plan);
|
|
2151
|
-
source = require('./native-request-body.js').instrument(source, applicationErrors.enabled(plan));
|
|
2182
|
+
source = require('./native-header-lookup.js').instrument(source, lifecycle);
|
|
2183
|
+
source = require('./native-fetch-errors.js').instrument(source, lifecycle);
|
|
2184
|
+
source = require('./native-fetch-deadlines.js').instrument(source, plan, fetchAccounting);
|
|
2185
|
+
source = require('./native-request-body.js').instrument(source, applicationErrors.enabled(plan), lifecycle);
|
|
2152
2186
|
if (packageApplication.enabled(plan)) source = packageApplication.instrument(source);
|
|
2153
2187
|
source = effectInvocations.instrument(source);
|
|
2154
|
-
source = require('./request-budget.js').instrumentRequestBudget(source, bindings.maxDurationMs, plan);
|
|
2188
|
+
source = require('./request-budget.js').instrumentRequestBudget(source, bindings.maxDurationMs, plan, lifecycle, fetchAccounting);
|
|
2155
2189
|
source = packageApplication.terminalFailure(source, plan);
|
|
2156
2190
|
source = nativeValueBudget.instrument(source, plan);
|
|
2157
2191
|
source = require('./native-body-forwarding.js').instrument(source, plan, bindings);
|
|
2158
2192
|
source = dynamicBackends.instrument(source, bindings, true, (options.bindings || options).backends);
|
|
2193
|
+
// Forwarding owns a specialized terminal fetch implementation. Admission
|
|
2194
|
+
// above rejects outcome/timing options; generic fetch anchors do not exist here.
|
|
2195
|
+
if (!require('./native-body-forwarding.js').enabled(plan)) {
|
|
2196
|
+
source = require('./native-fetch-outcomes.js').instrument(source, plan);
|
|
2197
|
+
source = require('./native-fetch-timing.js').instrument(source, plan);
|
|
2198
|
+
}
|
|
2199
|
+
lifecycle.assertSites();
|
|
2200
|
+
if (lifecycle.supportSource) source += '\n' + lifecycle.supportSource;
|
|
2159
2201
|
const effectKinds = Object.freeze((plan.effects || []).reduce((output, effect) => {
|
|
2160
2202
|
output[effect.kind] = (output[effect.kind] || 0) + 1;
|
|
2161
2203
|
return output;
|
|
@@ -2170,6 +2212,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
2170
2212
|
imports: Object.freeze([...capabilityImports(effect), ...(bindings.backends.some(b => b.index === index && b.dynamic) ? ['fastly_http_req.register_dynamic_backend'] : [])])
|
|
2171
2213
|
})));
|
|
2172
2214
|
const manifest = Object.freeze({
|
|
2215
|
+
...(resourceAccounting ? { resourceAccounting } : {}),
|
|
2173
2216
|
version: FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION,
|
|
2174
2217
|
generatorVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_GENERATOR_VERSION,
|
|
2175
2218
|
abiVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION,
|
|
@@ -2215,6 +2258,8 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
|
|
|
2215
2258
|
: Object.freeze({ status: 'inactive' }),
|
|
2216
2259
|
effectCount: (plan.effects || []).length,
|
|
2217
2260
|
effectInvocations: effectInvocations.policy,
|
|
2261
|
+
...(lifecycle.active ? { lifecycle: lifecycle.plan } : {}),
|
|
2262
|
+
...(selectedExtensions.length ? { hostExtensions: hostExtensions.evidence(selectedExtensions, lifecycle, extensionPolicy) } : {}),
|
|
2218
2263
|
effectKinds,
|
|
2219
2264
|
continuationCount: (plan.continuations || []).length,
|
|
2220
2265
|
groupedContinuationCount: (plan.continuations || []).filter((entry) => (entry.effectIds || []).length > 1).length,
|
|
@@ -2293,7 +2338,9 @@ function inspectFastlyNativePlatformCapabilitiesWasm(input, options = {}) {
|
|
|
2293
2338
|
const module = new WebAssembly.Module(bytes);
|
|
2294
2339
|
const imports = WebAssembly.Module.imports(module).map((entry) => Object.freeze({ module: entry.module, name: entry.name, kind: entry.kind }));
|
|
2295
2340
|
const exports = WebAssembly.Module.exports(module).map((entry) => Object.freeze({ name: entry.name, kind: entry.kind }));
|
|
2296
|
-
const
|
|
2341
|
+
const permitted = options.extensionPolicy ? new Set(options.extensionPolicy.permittedImports.map(entry => `${entry.module}:${entry.name}:${entry.kind}`)) : null;
|
|
2342
|
+
if (options.extensionPolicy) require('@pulse-compute/wasm-contracts/provider/toolchain').hostExtensions.auditWasm(bytes, options.extensionPolicy);
|
|
2343
|
+
const unexpected = imports.filter((entry) => permitted ? !permitted.has(`${entry.module}:${entry.name}:${entry.kind}`) : entry.kind !== 'function' || !FASTLY_NATIVE_PLATFORM_CAPABILITIES_ALLOWED_IMPORTS.has(`${entry.module}:${entry.name}`));
|
|
2297
2344
|
if (unexpected.length > 0) fail('Generated Fastly native platform capabilities module imports functions outside the Pass98 ABI.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORT_UNSUPPORTED', { unexpected, imports });
|
|
2298
2345
|
const forbidden = imports.filter((entry) => /pulse_host|js[_-]?compute/i.test(`${entry.module}:${entry.name}`));
|
|
2299
2346
|
if (forbidden.length > 0) fail('Generated Fastly native platform capabilities module imports a forbidden runtime.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_RUNTIME_FORBIDDEN', { forbidden });
|
|
@@ -2549,13 +2596,16 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
|
|
|
2549
2596
|
fs.writeFileSync(wasmFile, wasm);
|
|
2550
2597
|
if (emitWat) fs.writeFileSync(watFile, wat, 'utf8');
|
|
2551
2598
|
}
|
|
2552
|
-
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm, { maxWasmBytes });
|
|
2599
|
+
const inspection = inspectFastlyNativePlatformCapabilitiesWasm(wasm, { maxWasmBytes, extensionPolicy: generated.manifest.hostExtensions?.importPolicy });
|
|
2553
2600
|
const imported = new Set(inspection.imports.map((entry) => `${entry.module}:${entry.name}`));
|
|
2554
2601
|
const requiredImports = requiredImportsForPlan(plan, generated.bindings);
|
|
2555
2602
|
const missingRequiredImports = requiredImports.filter((key) => !imported.has(key));
|
|
2556
2603
|
if (missingRequiredImports.length > 0) fail('Generated Fastly native platform capabilities module is missing plan-required host imports.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORT_MISSING', { missingRequiredImports, imports: inspection.imports, requiredImports });
|
|
2557
2604
|
const manifest = Object.freeze({
|
|
2558
2605
|
...generated.manifest,
|
|
2606
|
+
...(generated.manifest.hostExtensions ? { hostExtensions: Object.freeze({ ...generated.manifest.hostExtensions,
|
|
2607
|
+
finalWasmSha256: inspection.sha256,
|
|
2608
|
+
observedImports: require('@pulse-compute/wasm-contracts/provider/toolchain').hostExtensions.auditWasm(wasm, generated.manifest.hostExtensions.importPolicy).observedImports }) } : {}),
|
|
2559
2609
|
compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
|
|
2560
2610
|
assemblyScript: Object.freeze({ package: 'assemblyscript', version: require(path.join(asc.packageRoot, 'package.json')).version }),
|
|
2561
2611
|
jsonAs: jsonAs
|
|
@@ -4,15 +4,16 @@ function runtimeSource() {
|
|
|
4
4
|
return `
|
|
5
5
|
let __pulse_request_body_failure: i32 = 0
|
|
6
6
|
// Validate the bounded, assembled request bytes before any text projection.
|
|
7
|
-
function __pulse_request_utf8_valid(bytes: Uint8Array): bool {
|
|
8
|
-
|
|
7
|
+
function __pulse_request_utf8_valid(bytes: Uint8Array, length: i32 = bytes.length): bool {
|
|
8
|
+
if (length < 0 || length > bytes.length) return false;
|
|
9
|
+
for (let i = 0; i < length;) {
|
|
9
10
|
const lead = bytes[i++]; if (lead < 128) continue;
|
|
10
11
|
let count = 0, minimum = 128, maximum = 191;
|
|
11
12
|
if (lead >= 194 && lead <= 223) count = 1;
|
|
12
13
|
else if (lead >= 224 && lead <= 239) { count = 2; if (lead == 224) minimum = 160; if (lead == 237) maximum = 159; }
|
|
13
14
|
else if (lead >= 240 && lead <= 244) { count = 3; if (lead == 240) minimum = 144; if (lead == 244) maximum = 143; }
|
|
14
15
|
else return false;
|
|
15
|
-
if (i + count >
|
|
16
|
+
if (i + count > length || i32(bytes[i]) < minimum || i32(bytes[i]) > maximum) return false;
|
|
16
17
|
i++; for (let n = 1; n < count; n++, i++) if (bytes[i] < 128 || bytes[i] > 191) return false;
|
|
17
18
|
}
|
|
18
19
|
return true;
|
|
@@ -31,12 +32,12 @@ function __pulse_request_send_body_error(): bool {
|
|
|
31
32
|
`;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
function instrument(source, applicationErrors) {
|
|
35
|
+
function instrument(source, applicationErrors, lifecycle) {
|
|
35
36
|
if (!applicationErrors) source = source.replace(/function (host_\w+)\(([^)]*)\): (i32|void) \{/g,
|
|
36
37
|
(declaration, _name, _args, result) => `${declaration} if (__pulse_fastly_last_error == PULSE_ERROR_REQUEST_BODY) return${result === 'void' ? '' : ' 0'};`);
|
|
37
38
|
// The existing terminal failure path also covers applications without an
|
|
38
39
|
// error handler. Request-budget instrumentation subsequently takes priority.
|
|
39
|
-
const marker =
|
|
40
|
+
const marker = `function ${lifecycle?.legacyName('__pulse_fastly_jwt_send_error') || '__pulse_fastly_jwt_send_error'}(): void {`;
|
|
40
41
|
if (!source.includes(marker)) throw new Error('Fastly request-body error boundary drift.');
|
|
41
42
|
return source.replace(marker, marker + ' if (__pulse_request_send_body_error()) return;');
|
|
42
43
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function runtimeSource(lifecycle, forwarding = false, fetchAccounting = false) {
|
|
4
|
+
const name = lifecycle?.legacyName('__request_send_timeout') || '__request_send_timeout';
|
|
5
|
+
return `// Provider-owned request lifecycle. No wall time or caller header grants time.
|
|
2
6
|
@external('fastly_async_io', 'select') declare function __request_select(handles: usize, count: i32, timeout: i32, done: usize): i32
|
|
3
7
|
let __request_deadline: i64 = -1;
|
|
4
8
|
let __request_last_clock: i64 = -1;
|
|
@@ -27,18 +31,19 @@ function __request_check(index: i32 = -1): bool {
|
|
|
27
31
|
__pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 260, index);
|
|
28
32
|
return false;
|
|
29
33
|
}
|
|
34
|
+
${fetchAccounting ? 'const __request_wait_handles = new StaticArray<i32>(1), __request_wait_ready = new StaticArray<i32>(1);' : ''}
|
|
30
35
|
function __request_wait(handle: i32, index: i32): bool {
|
|
31
36
|
const remaining = __request_remaining();
|
|
32
37
|
if (remaining <= 0) return __request_check(index);
|
|
33
|
-
const handles = new StaticArray<i32>(1), ready = new StaticArray<i32>(1);
|
|
38
|
+
const handles = ${fetchAccounting ? '__request_wait_handles' : 'new StaticArray<i32>(1)'}, ready = ${fetchAccounting ? '__request_wait_ready' : 'new StaticArray<i32>(1)'};
|
|
34
39
|
handles[0] = handle;
|
|
35
40
|
const status = __request_select(changetype<usize>(handles), 1, remaining, changetype<usize>(ready));
|
|
36
41
|
if (status != 0) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 261, index); return false; }
|
|
37
42
|
if (ready[0] == -1) __request_expired = true;
|
|
38
43
|
return __request_check(index);
|
|
39
44
|
}
|
|
40
|
-
function __request_send_timeout(): void {
|
|
41
|
-
// A timeout says nothing about remote acceptance of dispatched writes.
|
|
45
|
+
${lifecycle?.active ? `function __request_send_timeout(): void {\n${lifecycle.site('response-timeout')}\n ${name}()\n}\n` : ''}function ${name}(): void {
|
|
46
|
+
${fetchAccounting ? ' if (__fca_error()) return;\n' : ''}${forwarding ? ' if (__fwd_ra !== null) { __fwd_fail(504); __fwd_error_response(); return; }\n' : ''} // A timeout says nothing about remote acceptance of dispatched writes.
|
|
42
47
|
__request_error_response = true;
|
|
43
48
|
__pulse_fastly_last_error = 0;
|
|
44
49
|
const options = host_value_object();
|
|
@@ -48,3 +53,6 @@ function __request_send_timeout(): void {
|
|
|
48
53
|
const status = __pulse_fastly_send_result(response);
|
|
49
54
|
if (status != 0) __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 262, -1);
|
|
50
55
|
}
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
module.exports = { runtimeSource };
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const fs = require('node:fs');
|
|
3
|
-
const path = require('node:path');
|
|
4
2
|
const {normalizeRequestDuration} = require('@pulse-compute/runtime/host');
|
|
5
3
|
|
|
6
|
-
function instrumentRequestBudget(input, duration, plan) {
|
|
4
|
+
function instrumentRequestBudget(input, duration, plan, lifecycle, fetchAccounting) {
|
|
7
5
|
const ms = normalizeRequestDuration(duration);
|
|
8
6
|
if (ms === undefined) return input;
|
|
9
7
|
let source = input;
|
|
@@ -26,14 +24,15 @@ function instrumentRequestBudget(input, duration, plan) {
|
|
|
26
24
|
if (unchecked(__pulse_fastly_pending_mode[effectIndex]) == PULSE_FASTLY_PENDING_ASYNC && !(kind == 1 && __pulse_fetch_has_deadlines()) && kind != 12 && kind != 13 && kind != 14 && kind != 21 && ${plan.effects.filter(e=>e.kind.startsWith('kv.')&&['kv.getVersioned','kv.insertIfAbsent','kv.compareAndSwap','kv.list','kv.delete'].includes(e.kind)).map(e=>'effectIndex != '+plan.effects.indexOf(e)).join(' && ')||'true'}) {
|
|
27
25
|
if (!__request_wait(unchecked(__pulse_fastly_pending[effectIndex]), effectIndex)) return 0;
|
|
28
26
|
}`);
|
|
29
|
-
replace('
|
|
27
|
+
replace('function __pulse_fastly_body_read_ready(handle: i32, effectIndex: i32): bool { return true }', 'function __pulse_fastly_body_read_ready(handle: i32, effectIndex: i32): bool { return __request_wait(handle, effectIndex) }');
|
|
30
28
|
replace('while (offset < bytes.byteLength) { const written', 'while (offset < bytes.byteLength) { if (!__request_check()) return 1; const written');
|
|
31
|
-
replace(
|
|
29
|
+
replace(`function ${lifecycle?.legacyName('__pulse_fastly_send_result') || '__pulse_fastly_send_result'}(handle: i32): i32 {`, `function ${lifecycle?.legacyName('__pulse_fastly_send_result') || '__pulse_fastly_send_result'}(handle: i32): i32 { if (!__request_check()) return 1;`);
|
|
32
30
|
replace('return fastly_http_resp_send_downstream(responseHandle, bodyHandle, 0)', 'if (!__request_check()) return 1; return fastly_http_resp_send_downstream(responseHandle, bodyHandle, 0)');
|
|
33
31
|
replace('if (sendStatus != FASTLY_STATUS_OK) __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 104, -1)', 'if (sendStatus != FASTLY_STATUS_OK) { if (__request_expired) { __request_send_timeout(); return; } __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 104, -1); }');
|
|
34
|
-
|
|
32
|
+
const send = fetchAccounting ? '__fca_dispatch' : 'fastly_http_req_send_async';
|
|
33
|
+
replace('status = ' + send + '(requestHandle', 'if (!__request_check(effectIndex)) return; status = ' + send + '(requestHandle');
|
|
35
34
|
replace('function __pulse_fastly_ready_effect(effectIndex: i32, handle: i32): void {', 'function __pulse_fastly_ready_effect(effectIndex: i32, handle: i32): void { if (!__request_check(effectIndex)) return;');
|
|
36
|
-
replace(
|
|
35
|
+
replace(`function ${lifecycle?.legacyName('__pulse_fastly_jwt_send_error') || '__pulse_fastly_jwt_send_error'}(): void {`, `function ${lifecycle?.legacyName('__pulse_fastly_jwt_send_error') || '__pulse_fastly_jwt_send_error'}(): void { if (__request_expired || !__request_check()) { __request_send_timeout(); return; }`);
|
|
37
36
|
source = source.replaceAll('runStatus = pulse_resume()', 'if (!__request_check()) { __request_send_timeout(); return; }\n runStatus = pulse_resume()');
|
|
38
37
|
replace(' const result = pulse_result_handle()', ' if (!__request_check()) { __request_send_timeout(); return; }\n const result = pulse_result_handle()');
|
|
39
38
|
if (source.includes('__kv_deadline[index] = start +')) replace('__kv_deadline[index] = start + i64(__KV_timeoutMs) * 1000000;', '__kv_deadline[index] = min<i64>(__request_deadline, start + i64(__KV_timeoutMs) * 1000000);');
|
|
@@ -42,6 +41,6 @@ function instrumentRequestBudget(input, duration, plan) {
|
|
|
42
41
|
replace('function __pulse_fetch_request_check(index: i32): bool { return true }', 'function __pulse_fetch_request_check(index: i32): bool { return __request_check(index) }');
|
|
43
42
|
replace('function __pulse_fetch_request_remaining(): i32 { return 2147483647 }', 'function __pulse_fetch_request_remaining(): i32 { return __request_remaining() }');
|
|
44
43
|
if (plan.callables?.length || plan.binaryResources || plan.numericWorkspaces?.length) replace('function __pulse_resource_request_check(): bool { return true }', 'function __pulse_resource_request_check(): bool { return __request_check() }');
|
|
45
|
-
return source + '\n' +
|
|
44
|
+
return source + '\n' + require('./request-budget-source.js').runtimeSource(lifecycle, plan.capabilities.includes('request.body.forward'), fetchAccounting);
|
|
46
45
|
}
|
|
47
46
|
module.exports = {instrumentRequestBudget};
|