@pulse-compute/provider-fastly 1.0.0-beta.7 → 1.0.0-beta.9

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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { scalarRecordProjectionLines } = require('./schema-scalar-record.js');
4
4
  const { hasJsonAdmission, tracksJsonDuplicates, schemaParserDepth, nestedJsonProjectionLines, schemaJsonAdmissionSource, schemaFetchJsonSource } = require('./schema-nested-json.js');
5
+ const { nativeStdioSource } = require('./native-stdio.js');
5
6
  const applicationErrors = require('./native-application-errors.js');
6
7
  const effectInvocations = require('./effect-invocations.js');
7
8
  const packageApplication = require('./package-native-application.js');
@@ -88,6 +89,7 @@ function loadGuestToolchain() {
88
89
 
89
90
  const { resolveAsc } = loadBuildSupport();
90
91
  const { generateCanonicalNativeAssemblyScript } = loadNativeGenerator();
92
+ const nativeHandleOwnership = require('./native-handle-ownership.js');
91
93
  const { pulseJwtAssemblyScriptSource } = loadJwtNativeSource();
92
94
  const {
93
95
  GUEST_LINK_STAGE_INVOCATION_VERSION,
@@ -108,6 +110,7 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_BUFFER_BYTES = 65536;
108
110
  const FASTLY_NATIVE_SIZE_OPTIMIZATION = EXPERIMENTAL_NATIVE_SIZE_OPTIMIZATION;
109
111
 
110
112
  const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
113
+ ...require('./native-body-forwarding.js').IMPORTS.map(key => Object.freeze(key.split(':'))),
111
114
  ...conditionalKv.KV_IMPORTS.map((key) => Object.freeze(key.split(':'))),
112
115
  ...require('./s3-native.js').S3_IMPORTS.map((key) => Object.freeze(key.split(':'))),
113
116
  Object.freeze(['fastly_abi', 'init']),
@@ -126,6 +129,7 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
126
129
  Object.freeze(['fastly_http_resp', 'new']),
127
130
  Object.freeze(['fastly_http_resp', 'header_append']),
128
131
  Object.freeze(['fastly_http_resp', 'header_value_get']),
132
+ Object.freeze(['fastly_http_resp', 'header_values_get']),
129
133
  Object.freeze(['fastly_http_resp', 'status_get']),
130
134
  Object.freeze(['fastly_http_resp', 'status_set']),
131
135
  Object.freeze(['fastly_http_resp', 'send_downstream']),
@@ -142,8 +146,7 @@ const FASTLY_NATIVE_PLATFORM_CAPABILITIES_IMPORTS = Object.freeze([
142
146
  Object.freeze(['fastly_kv_store', 'lookup_wait_v2']),
143
147
  Object.freeze(['fastly_kv_store', 'insert']),
144
148
  Object.freeze(['fastly_kv_store', 'insert_wait']),
145
- Object.freeze(['fastly_log', 'endpoint_get']),
146
- Object.freeze(['fastly_log', 'write']),
149
+ Object.freeze(['wasi_snapshot_preview1', 'fd_write']),
147
150
  Object.freeze(['wasi_snapshot_preview1', 'clock_time_get'])
148
151
  ].filter(([module, name], index, entries) => entries.findIndex((item) => item[0] === module && item[1] === name) === index));
149
152
 
@@ -662,6 +665,7 @@ function fastlyGuestLinkedStartWrapperWat() {
662
665
  }
663
666
 
664
667
  function validatePlanBoundary(plan, options = {}) {
668
+ require('./native-body-forwarding.js').validate(plan, options);
665
669
  if (!plan || typeof plan !== 'object') throw new TypeError('Fastly native platform capability realization requires a canonical native plan.');
666
670
  if (plan.version !== CANONICAL_NATIVE_PLAN_VERSION) {
667
671
  fail(`Fastly native platform capability realization requires ${CANONICAL_NATIVE_PLAN_VERSION}.`, 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_PLAN_VERSION_UNSUPPORTED', { version: plan.version });
@@ -929,6 +933,7 @@ function resolveCapabilityBindings(plan, options = {}) {
929
933
 
930
934
  return Object.freeze({
931
935
  maxDurationMs: require('@pulse-compute/runtime/host').normalizeRequestDuration(options.maxDurationMs),
936
+ bodyForwarding: require('../toolchain/body-forwarding.js').normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs),
932
937
  configStore,
933
938
  secretStore,
934
939
  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),
@@ -988,6 +993,7 @@ function requiredImportsForPlan(plan, bindings) {
988
993
  'fastly_http_req:header_insert', 'fastly_http_req:send_async', 'fastly_http_req:pending_req_wait'
989
994
  ]) keys.add(key);
990
995
  }
996
+ 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);
991
997
  if (planRequiresFetchBodyRead(plan)) keys.add('fastly_http_body:read');
992
998
  if (kinds.has('config.get')) for (const key of ['fastly_config_store:open', 'fastly_config_store:get']) keys.add(key);
993
999
  const gripAuthenticationRequired = kinds.has('grip.broadcast')
@@ -1007,10 +1013,14 @@ function requiredImportsForPlan(plan, bindings) {
1007
1013
  if (kinds.has('kv.put')) for (const key of ['fastly_kv_store:insert', 'fastly_kv_store:insert_wait']) keys.add(key);
1008
1014
  if (kinds.has('grip.hold')) keys.add('fastly_http_resp:header_append');
1009
1015
  if (plan.logging && plan.logging.enabledStatements > 0) {
1010
- keys.add('fastly_log:endpoint_get');
1011
- keys.add('fastly_log:write');
1016
+ keys.add('wasi_snapshot_preview1:fd_write');
1012
1017
  }
1013
1018
  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);
1019
+ if (require('./native-body-forwarding.js').enabled(plan)) {
1020
+ keys.delete('fastly_http_req:send_async'); keys.delete('fastly_http_req:pending_req_wait');
1021
+ for (const key of require('./native-body-forwarding.js').IMPORTS) keys.add(key);
1022
+ keys.add('fastly_http_body:read');
1023
+ }
1014
1024
  return Object.freeze([...keys].sort());
1015
1025
  }
1016
1026
 
@@ -1433,8 +1443,7 @@ function __pulse_fastly_jwt_send_error(): void {}
1433
1443
  @external("fastly_kv_store", "lookup_wait_v2") declare function fastly_kv_store_lookup_wait_v2(handle: i32, bodyOut: usize, metadata: usize, metadataLength: i32, writtenOut: usize, generationOut: usize, kvErrorOut: usize): i32
1434
1444
  @external("fastly_kv_store", "insert") declare function fastly_kv_store_insert(store: i32, key: usize, keyLength: i32, body: i32, configMask: i32, config: usize, insertOut: usize): i32
1435
1445
  @external("fastly_kv_store", "insert_wait") declare function fastly_kv_store_insert_wait(handle: i32, kvErrorOut: usize): i32
1436
- @external("fastly_log", "endpoint_get") declare function fastly_log_endpoint_get(name: usize, nameLength: i32, handleOut: usize): i32
1437
- @external("fastly_log", "write") declare function fastly_log_write(handle: i32, message: usize, messageLength: i32, writtenOut: usize): i32
1446
+ ${nativeStdioSource}
1438
1447
  @external("wasi_snapshot_preview1", "clock_time_get") declare function wasi_snapshot_preview1_clock_time_get(clockId: i32, precision: i64, timeOut: usize): i32
1439
1448
 
1440
1449
  const PULSE_FASTLY_EFFECTS_ABI_VERSION: i64 = ${FASTLY_NATIVE_PLATFORM_CAPABILITIES_ABI_VERSION}
@@ -1506,6 +1515,7 @@ ${nativeStringFields}
1506
1515
  }
1507
1516
  binaryBody: Uint8Array | null = null
1508
1517
  immutable: bool = false
1518
+ ${nativeHandleOwnership.fields(options.handleTracing)}
1509
1519
  }
1510
1520
  class __PulseFastlyHeader {
1511
1521
  constructor(public name: string, public value: string) {}
@@ -1638,7 +1648,7 @@ class __PulseJsonParser {
1638
1648
  const PULSE_FASTLY_PENDING_NONE: i32 = 0
1639
1649
  const PULSE_FASTLY_PENDING_ASYNC: i32 = 1
1640
1650
  const PULSE_FASTLY_PENDING_READY: i32 = 2
1641
- @lazy const __pulse_fastly_values = new Array<__PulseFastlyValue>()
1651
+ ${nativeHandleOwnership.tableSource(options.handleTracing)}
1642
1652
  // Primitive handles have value semantics. Bounded caches keep admitted scalar
1643
1653
  // validation loops from retaining a fresh boxed value for every iteration.
1644
1654
  @lazy const __pulse_fastly_scalar_handles = new StaticArray<i32>(4)
@@ -1651,6 +1661,7 @@ const PULSE_FASTLY_PENDING_READY: i32 = 2
1651
1661
  @lazy const __pulse_fastly_ready = new StaticArray<i32>(PULSE_FASTLY_EFFECT_COUNT)
1652
1662
  @lazy const __pulse_fastly_grip_channels = new Array<string>()
1653
1663
  @lazy const __pulse_fastly_redactions = new Array<string>()
1664
+ @lazy const __pulse_fastly_field_redactions = new Array<string>()
1654
1665
  let __pulse_fastly_request_handle: i32 = 0
1655
1666
  let __pulse_fastly_request_body_handle: i32 = 0
1656
1667
  let __pulse_fastly_request_method: string = ""
@@ -1661,7 +1672,6 @@ let __pulse_fastly_request_body_loaded: i32 = 0
1661
1672
  let __pulse_fastly_last_error: i32 = 0
1662
1673
  let __pulse_fastly_error_stage: i32 = 0
1663
1674
  let __pulse_fastly_error_effect: i32 = -1
1664
- let __pulse_fastly_log_handle: i32 = -1
1665
1675
  let __pulse_fastly_jwt_error: i32 = 0
1666
1676
  let __pulse_fastly_jwt_secret_calls: i32 = 0
1667
1677
  let __pulse_fastly_jwt_crypto_calls: i32 = 0
@@ -1678,8 +1688,6 @@ function __pulse_fastly_out_i32(): StaticArray<i32> { return new StaticArray<i32
1678
1688
  function __pulse_fastly_out_value(out: StaticArray<i32>): i32 { return load<i32>(changetype<usize>(out)) }
1679
1689
  function __pulse_fastly_utf8(value: string): ArrayBuffer { return String.UTF8.encode(value, false) }
1680
1690
  function __pulse_fastly_decode(buffer: Uint8Array, length: i32): string { return String.UTF8.decodeUnsafe(buffer.dataStart, length, false) }
1681
- function __pulse_fastly_put(value: __PulseFastlyValue): i32 { __pulse_fastly_values.push(value); return __pulse_fastly_values.length }
1682
- 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]) }
1683
1691
  ${nativeStringConcat}
1684
1692
  ${nativeStringTrim}
1685
1693
  ${nativeStringIndex}
@@ -1689,16 +1697,38 @@ function __pulse_fastly_string_value(value: string): i32 {
1689
1697
  if (index >= 0) { const cached = unchecked(__pulse_fastly_character_handles[index]); if (cached != 0) return cached }
1690
1698
  const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_STRING; item.text = value;
1691
1699
  const handle = __pulse_fastly_put(item);
1692
- if (index >= 0) unchecked(__pulse_fastly_character_handles[index] = handle);
1700
+ if (index >= 0) { unchecked(__pulse_fastly_character_handles[index] = handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} }
1693
1701
  return handle
1694
1702
  }
1695
1703
  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 }
1696
1704
  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() }
1697
1705
  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) }
1698
- function __pulse_fastly_remember_secret(value: string): string { if (value.length == 0) return value; for (let i = 0; i < __pulse_fastly_redactions.length; i += 1) if (unchecked(__pulse_fastly_redactions[i]) == value) return value; __pulse_fastly_redactions.push(value); return value }
1706
+ function __pulse_fastly_remember_secret(value: string, field: bool = false): string { if (value.length == 0) return value; const values = field ? __pulse_fastly_field_redactions : __pulse_fastly_redactions; for (let i = 0; i < values.length; i += 1) if (unchecked(values[i]) == value) return value; values.push(value); return value }
1699
1707
  function __pulse_fastly_replace_secret(input: string, secret: string): string { if (secret.length == 0) return input; let source = input; let output = ""; while (true) { const index = source.indexOf(secret); if (index < 0) return output + source; output += source.substring(0, index) + "<redacted>"; source = source.substring(index + secret.length) } }
1700
- function __pulse_fastly_redact(input: string): string { let output = input; for (let i = 0; i < __pulse_fastly_redactions.length; i += 1) output = __pulse_fastly_replace_secret(output, unchecked(__pulse_fastly_redactions[i])); return output }
1701
- function host_log(level: i32, messageHandle: i32): void { let endpoint = __pulse_fastly_log_handle; if (endpoint == -2) return; if (endpoint == -1) { const name = __pulse_fastly_utf8("stdout"); const out = __pulse_fastly_out_i32(); if (fastly_log_endpoint_get(changetype<usize>(name), name.byteLength, changetype<usize>(out)) != FASTLY_STATUS_OK) { __pulse_fastly_log_handle = -2; return } endpoint = __pulse_fastly_out_value(out); __pulse_fastly_log_handle = endpoint } let label = "unknown"; if (level == 1) label = "error"; else if (level == 2) label = "warn"; else if (level == 3) label = "info"; else if (level == 4) label = "debug"; const bytes = __pulse_fastly_utf8("[pulse:" + label + "] " + __pulse_fastly_redact(__pulse_fastly_string(messageHandle)) + "\n"); const written = __pulse_fastly_out_i32(); fastly_log_write(endpoint, changetype<usize>(bytes), bytes.byteLength, changetype<usize>(written)) }
1708
+ // Walk complete JSON string tokens, including escaped quotes. Substring matching
1709
+ // a quoted field would still corrupt an unrelated token such as "prefix\\\"id".
1710
+ function __pulse_fastly_redact_fields(input: string): string {
1711
+ if (__pulse_fastly_field_redactions.length == 0) return input;
1712
+ let output = "", start = 0, cursor = 0;
1713
+ while (cursor < input.length) {
1714
+ if (input.charCodeAt(cursor) != 34) { cursor += 1; continue; }
1715
+ const begin = cursor++;
1716
+ while (cursor < input.length) {
1717
+ const code = input.charCodeAt(cursor++);
1718
+ if (code == 92 && cursor < input.length) cursor += 1;
1719
+ else if (code == 34) break;
1720
+ }
1721
+ const token = input.substring(begin, cursor);
1722
+ for (let i = 0; i < __pulse_fastly_field_redactions.length; i += 1) {
1723
+ if (token != unchecked(__pulse_fastly_field_redactions[i])) continue;
1724
+ output += input.substring(start, begin) + __pulse_fastly_quote("<redacted>");
1725
+ start = cursor; break;
1726
+ }
1727
+ }
1728
+ return output + input.substring(start);
1729
+ }
1730
+ function __pulse_fastly_redact(input: string): string { let output = __pulse_fastly_redact_fields(input); for (let i = 0; i < __pulse_fastly_redactions.length; i += 1) output = __pulse_fastly_replace_secret(output, unchecked(__pulse_fastly_redactions[i])); return output }
1731
+ function host_log(level: i32, messageHandle: i32): void { let label = "unknown"; if (level == 1) label = "error"; else if (level == 2) label = "warn"; else if (level == 3) label = "info"; else if (level == 4) label = "debug"; const bytes = __pulse_fastly_utf8("[pulse:" + label + "] " + __pulse_fastly_redact(__pulse_fastly_string(messageHandle)) + "\n"); __pulse_fastly_write_stdout(bytes) }
1702
1732
  function __pulse_fastly_number(handle: i32): f64 { const value = __pulse_fastly_value(handle); if (value.kind == PULSE_VALUE_NUMBER) return value.number; if (value.kind == PULSE_VALUE_BOOLEAN) return value.boolean != 0 ? 1.0 : 0.0; if (value.kind == PULSE_VALUE_STRING) return F64.parseFloat(value.text); return 0.0 }
1703
1733
  function __pulse_fastly_hex(value: i32): string { return String.fromCharCode(value < 10 ? 48 + value : 87 + value) }
1704
1734
  function __pulse_fastly_is_unpaired(value: string, i: i32): bool {
@@ -1768,14 +1798,14 @@ function __pulse_fastly_read_body(handle: i32, effectIndex: i32, requestText: bo
1768
1798
  if (requestText && !__pulse_request_utf8_valid(bytes)) { __pulse_fastly_fail(PULSE_ERROR_REQUEST_BODY, 24, effectIndex); return "" }
1769
1799
  return __pulse_fastly_decode(bytes, total);
1770
1800
  }
1771
- 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)) }
1772
- 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)) }
1801
+ function __pulse_fastly_request_header_text(name: string): string { const value = __pulse_header_lookup(__pulse_fastly_request_handle, name, false); return value === null ? "" : value }
1802
+ function __pulse_fastly_fetch_header_text(responseHandle: i32, name: string): string { const value = __pulse_header_lookup(responseHandle, name, true); return value === null ? "" : value }
1773
1803
  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 }
1774
1804
 
1775
1805
  function __pulse_fastly_scalar(kind: i32, boolean: i32, slot: i32): i32 {
1776
1806
  const cached = unchecked(__pulse_fastly_scalar_handles[slot]); if (cached != 0) return cached;
1777
1807
  const item = new __PulseFastlyValue(); item.kind = kind; item.boolean = boolean;
1778
- const handle = __pulse_fastly_put(item); unchecked(__pulse_fastly_scalar_handles[slot] = handle); return handle
1808
+ const handle = __pulse_fastly_put(item); unchecked(__pulse_fastly_scalar_handles[slot] = handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} return handle
1779
1809
  }
1780
1810
  function host_value_undefined(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_UNDEFINED, 0, 0) }
1781
1811
  function host_value_null(): i32 { return __pulse_fastly_scalar(PULSE_VALUE_NULL, 0, 1) }
@@ -1786,13 +1816,13 @@ function host_value_number(value: f64): i32 {
1786
1816
  const index = cacheable ? i32(value) : -1;
1787
1817
  if (index >= 0) { const cached = unchecked(__pulse_fastly_integer_handles[index]); if (cached != 0) return cached }
1788
1818
  const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_NUMBER; item.number = value;
1789
- const handle = __pulse_fastly_put(item); if (index >= 0) unchecked(__pulse_fastly_integer_handles[index] = handle); return handle
1819
+ const handle = __pulse_fastly_put(item); if (index >= 0) { unchecked(__pulse_fastly_integer_handles[index] = handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} } return handle
1790
1820
  }
1791
1821
  function host_value_string(pointer: i32, length: i32): i32 {
1792
1822
  const text = changetype<string>(pointer);
1793
1823
  if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.has(text)) return __pulse_fastly_literal_handles.get(text);
1794
1824
  const handle = __pulse_fastly_string_value(text);
1795
- if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.size < 1024) __pulse_fastly_literal_handles.set(text, handle);
1825
+ if (text.length > 1 && text.length <= 128 && __pulse_fastly_literal_handles.size < 1024) { __pulse_fastly_literal_handles.set(text, handle); ${options.handleTracing ? 'if (handle != 0) __pulse_handle_cache_roots.push(handle);' : ''} }
1796
1826
  return handle
1797
1827
  }
1798
1828
  function host_value_array(): i32 { const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_ARRAY; return __pulse_fastly_put(item) }
@@ -1819,7 +1849,7 @@ function host_router_match(path: i32, pattern: i32): i32 { return host_value_boo
1819
1849
  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) }
1820
1850
  function host_request_path(): i32 { return __pulse_fastly_string_value(__pulse_fastly_request_path) }
1821
1851
  function host_request_headers(): i32 { return __pulse_request_headers_read() }
1822
- 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) }
1852
+ function host_request_header(name: i32): i32 { const value = __pulse_header_lookup(__pulse_fastly_request_handle, __pulse_fastly_string(name), false); return value === null ? host_value_undefined() : __pulse_fastly_string_value(value) }
1823
1853
  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) }
1824
1854
  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 }
1825
1855
  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])))) } } }
@@ -1858,7 +1888,7 @@ function host_grip_handoff(optionsHandle: i32): i32 { const options = __pulse_fa
1858
1888
  function host_kv_namespace(name: i32): i32 { return name }
1859
1889
  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)) }
1860
1890
  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 }
1861
- 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) }
1891
+ 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) }
1862
1892
  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]) }
1863
1893
  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) }
1864
1894
  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) }
@@ -1880,7 +1910,7 @@ function __pulse_fastly_jwt_capture_clock(effectIndex: i32): f64 { __pulse_fastl
1880
1910
  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) }
1881
1911
  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) }
1882
1912
  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) }
1883
- 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) }
1913
+ 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_fetch_close_request(requestHandle); __pulse_fetch_close_body(bodyHandle); __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) }
1884
1914
  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)) }
1885
1915
  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) }
1886
1916
  ${assetBeginSource}
@@ -1892,7 +1922,7 @@ function __pulse_fastly_grip_hold_begin(effectIndex: i32, payload: __PulseFastly
1892
1922
  ${gripSecretSource}
1893
1923
  function __pulse_fastly_grip_publish_begin(effectIndex: i32, payload: __PulseFastlyValue): void { const valueHandle = __pulse_fastly_payload_field(payload, "payload"); const value = __pulse_fastly_value(valueHandle); if (value.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 122, effectIndex); return } const headers = new __PulseFastlyValue(); headers.kind = PULSE_VALUE_OBJECT; headers.keys.push("content-type"); headers.values.push(__pulse_fastly_string_value("application/json; charset=utf-8")); headers.keys.push("accept"); headers.values.push(__pulse_fastly_string_value("application/json")); ${gripAuthorizationSource} const body = __pulse_fastly_json(valueHandle, 0); if (String.UTF8.encode(body, false).byteLength > PULSE_FASTLY_BUFFER_BYTES) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 145, effectIndex); return } __pulse_fastly_start_request(effectIndex, __pulse_fastly_grip_publish_url(effectIndex), "POST", headers, body, __pulse_fastly_backend(effectIndex)) }
1894
1924
  ${effectDispatchSource(plan)}
1895
- function __pulse_fastly_wait_fetch(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 80, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const responseOut = __pulse_fastly_out_i32(); const bodyOut = __pulse_fastly_out_i32(); const status = fastly_http_req_pending_req_wait(pending, changetype<usize>(responseOut), changetype<usize>(bodyOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_TRANSPORT, 81, effectIndex); return 0 } const value = new __PulseFastlyValue(); value.kind = PULSE_VALUE_FETCH; value.responseHandle = __pulse_fastly_out_value(responseOut); value.bodyHandle = __pulse_fastly_out_value(bodyOut); return __pulse_fastly_put(value) }
1925
+ function __pulse_fastly_wait_fetch(effectIndex: i32): i32 { if (unchecked(__pulse_fastly_pending_mode[effectIndex]) != PULSE_FASTLY_PENDING_ASYNC) { __pulse_fastly_fail(PULSE_ERROR_STATE, 80, effectIndex); return 0 } const pending = unchecked(__pulse_fastly_pending[effectIndex]); const responseOut = __pulse_fastly_out_i32(); const bodyOut = __pulse_fastly_out_i32(); const status = fastly_http_req_pending_req_wait(pending, changetype<usize>(responseOut), changetype<usize>(bodyOut)); unchecked(__pulse_fastly_pending_mode[effectIndex] = PULSE_FASTLY_PENDING_NONE); unchecked(__pulse_fastly_pending[effectIndex] = 0); if (status != FASTLY_STATUS_OK) { __pulse_fetch_fail(status, 81, effectIndex); return 0 } const value = new __PulseFastlyValue(); value.kind = PULSE_VALUE_FETCH; value.responseHandle = __pulse_fastly_out_value(responseOut); value.bodyHandle = __pulse_fastly_out_value(bodyOut); return __pulse_fastly_put(value) }
1896
1926
  function __pulse_fastly_wait_grip_publish(effectIndex: i32): i32 { const responseHandle = __pulse_fastly_wait_fetch(effectIndex); if (responseHandle <= 0) return 0; const response = __pulse_fastly_value(responseHandle); if (response.kind != PULSE_VALUE_FETCH) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 129, effectIndex); return 0 } const statusOut = __pulse_fastly_out_i32(); const statusCall = fastly_http_resp_status_get(response.responseHandle, changetype<usize>(statusOut)); if (statusCall != FASTLY_STATUS_OK) { __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 130, effectIndex); return 0 } const originStatus = __pulse_fastly_out_value(statusOut); if (originStatus < 200 || originStatus > 299) { __pulse_fastly_fail(PULSE_ERROR_TRANSPORT, 131, effectIndex); return 0 } const outer = __pulse_fastly_value(unchecked(__pulse_fastly_payloads[effectIndex])); const publicationHandle = __pulse_fastly_payload_field(outer, "payload"); const publication = __pulse_fastly_value(publicationHandle); if (publication.kind != PULSE_VALUE_OBJECT) { __pulse_fastly_fail(PULSE_ERROR_VALUE, 132, effectIndex); return 0 } const receipt = host_value_object(); host_value_object_set(receipt, __pulse_fastly_string_value("published"), host_value_boolean(1)); for (let i = 0; i < publication.keys.length; i += 1) host_value_object_set(receipt, __pulse_fastly_string_value(unchecked(publication.keys[i])), unchecked(publication.values[i])); const options = host_value_object(); host_value_object_set(options, __pulse_fastly_string_value("status"), host_value_number(202.0)); return host_response_json(receipt, options) }
1897
1927
  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 }
1898
1928
  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 }
@@ -1949,7 +1979,7 @@ function __pulse_fastly_run_invocation(): void {
1949
1979
  if (__pulse_fastly_last_error != 0) { __pulse_fastly_jwt_send_error(); return }
1950
1980
  __pulse_fastly_request_path = __pulse_fastly_path(__pulse_fastly_request_url)
1951
1981
  let runStatus = pulse_start()
1952
- ${packageApplication.enabled(plan) ? packageApplication.driverLoop() : applicationErrors.enabled(plan) ? applicationErrors.driverLoop(plan) : ` while (runStatus == 1 && __pulse_fastly_last_error == 0) {
1982
+ ${packageApplication.enabled(plan) ? packageApplication.driverLoop() : applicationErrors.settlementEnabled(plan) ? applicationErrors.driverLoop(plan) : ` while (runStatus == 1 && __pulse_fastly_last_error == 0) {
1953
1983
  for (let effectIndex = 0; effectIndex < PULSE_FASTLY_EFFECT_COUNT; effectIndex += 1) {
1954
1984
  if (unchecked(__pulse_fastly_pending_mode[effectIndex]) == PULSE_FASTLY_PENDING_NONE) continue
1955
1985
  const invocation = unchecked(__pulse_invocation_tickets[effectIndex])
@@ -2030,7 +2060,16 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
2030
2060
  );
2031
2061
  }
2032
2062
  const bindings = resolveCapabilityBindings(plan, options);
2033
- const portable = packageApplication.enabled(plan) ? packageApplication.portable(plan) : generateCanonicalNativeAssemblyScript(plan, options);
2063
+ // Keep final provider eligibility authoritative over the portable artifact
2064
+ // and caller options. Only the selected crypto guest uses the linked policy;
2065
+ // that policy excludes active codecs regardless of JSON arena configuration.
2066
+ const numericRegions = facts.guestUnits.length === 0 ? 'fastly-numeric-regions-v2'
2067
+ : jwtCrypto && jwtCrypto.guestUnitRequired ? 'fastly-linked-numeric-regions-v1' : undefined;
2068
+ const portable = packageApplication.enabled(plan) ? packageApplication.portable(plan) : generateCanonicalNativeAssemblyScript(plan, {
2069
+ ...options, numericRegions,
2070
+ handleOwnership: facts.guestUnits.length === 0 ? 'fastly-handle-tracing-v1' : undefined
2071
+ });
2072
+ nativeHandleOwnership.validateRoots(plan, portable.manifest);
2034
2073
  const portableSource = stripPulseHostImports(portable.source);
2035
2074
  const hasJwt = (plan.effects || []).some((effect) => effect.kind === 'jwt.verify');
2036
2075
  const hasSign = plan.effects.some(effect => effect.kind === 'jwt.sign');
@@ -2041,6 +2080,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
2041
2080
  '',
2042
2081
  fastlyRuntimeSource(plan, bindings, {
2043
2082
  ...options,
2083
+ handleTracing: portable.manifest.handleOwnership?.enabled === true,
2044
2084
  jwtCrypto,
2045
2085
  es256KeyRecords
2046
2086
  }),
@@ -2052,19 +2092,29 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
2052
2092
  conditionalKv.kvNativeSource(plan),
2053
2093
  require('./native-request-body.js').runtimeSource(),
2054
2094
  require('./native-request-headers.js').runtimeSource(),
2055
- applicationErrors.enabled(plan) ? applicationErrors.runtimeSource(plan) : '',
2095
+ require('./native-header-lookup.js').runtimeSource(),
2096
+ require('./native-fetch-errors.js').runtimeSource(),
2097
+ require('./native-fetch-deadlines.js').runtimeSource(plan),
2098
+ applicationErrors.settlementEnabled(plan) ? applicationErrors.runtimeSource(plan) : '',
2056
2099
  effectInvocations.runtimeSource({ packageCompletion: packageApplication.enabled(plan) }),
2100
+ ...(portable.manifest.numericRegions?.accounting === 'fastly-read-loop-v1' && portable.manifest.numericRegions.regions.length > 0
2101
+ ? [nativeValueBudget.numericRegionSource()] : []),
2057
2102
  portableSource,
2103
+ nativeHandleOwnership.providerSource(plan, portable.manifest.handleOwnership?.enabled === true),
2058
2104
  driverSource(plan, { guestLinked: facts.guestUnits.length > 0 }),
2059
2105
  ''
2060
2106
  ].join('\n');
2061
- if (applicationErrors.enabled(plan)) source = applicationErrors.instrument(source);
2107
+ if (applicationErrors.settlementEnabled(plan)) source = applicationErrors.instrument(source);
2062
2108
  source = require('./native-request-headers.js').instrument(source, applicationErrors.enabled(plan), /\bhost_request_headers\(/.test(portableSource));
2109
+ source = require('./native-header-lookup.js').instrument(source);
2110
+ source = require('./native-fetch-errors.js').instrument(source);
2111
+ source = require('./native-fetch-deadlines.js').instrument(source, plan);
2063
2112
  source = require('./native-request-body.js').instrument(source, applicationErrors.enabled(plan));
2064
2113
  if (packageApplication.enabled(plan)) source = packageApplication.instrument(source);
2065
2114
  source = effectInvocations.instrument(source);
2066
2115
  source = require('./request-budget.js').instrumentRequestBudget(source, bindings.maxDurationMs, plan);
2067
2116
  source = nativeValueBudget.instrument(source, plan);
2117
+ source = require('./native-body-forwarding.js').instrument(source, plan, bindings);
2068
2118
  const effectKinds = Object.freeze((plan.effects || []).reduce((output, effect) => {
2069
2119
  output[effect.kind] = (output[effect.kind] || 0) + 1;
2070
2120
  return output;
@@ -2088,7 +2138,16 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
2088
2138
  portableGeneratorVersion: portable.version,
2089
2139
  portableSourceHash: portable.sourceHash,
2090
2140
  schemaCodecs: portable.manifest.schemaCodecs,
2141
+ ...(portable.manifest.numericRegions ? { numericRegions: portable.manifest.numericRegions } : {}),
2142
+ handleOwnership: portable.manifest.handleOwnership ? Object.freeze({ ...portable.manifest.handleOwnership,
2143
+ providerRoots: portable.manifest.handleOwnership.enabled ? Object.freeze(nativeHandleOwnership.providerRoots(plan)) : Object.freeze([])
2144
+ }) : Object.freeze({
2145
+ policy: null, enabled: false,
2146
+ reason: packageApplication.enabled(plan) ? 'package-owned-emitter' : 'linked-runtime',
2147
+ roots: Object.freeze([]), checkpoints: 'none'
2148
+ }),
2091
2149
  handlerBodies: portable.manifest.handlerBodies,
2150
+ ...(portable.manifest.reportOwnership ? { reportOwnership: portable.manifest.reportOwnership } : {}),
2092
2151
  dispatcher: portable.manifest.dispatcher,
2093
2152
  stages: portable.manifest.stages,
2094
2153
  helperBodies: portable.manifest.helperBodies,
@@ -2139,7 +2198,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
2139
2198
  providerNeutralInput: true,
2140
2199
  javascriptRuntime: false,
2141
2200
  jsComputeRuntime: false,
2142
- 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,
2201
+ wasi: requiredImportsForPlan(plan, bindings).filter(key => key.startsWith('wasi_snapshot_preview1:')).map(key => key.split(':')[1]).join(', ').replace(/(.+)/, '$1 only') || false,
2143
2202
  effects: FASTLY_NATIVE_PLATFORM_EFFECT_KINDS.join(', '),
2144
2203
  continuations: true,
2145
2204
  groupedEffects: 'existing fetch start-before-wait ordering remains intact',
@@ -2151,7 +2210,7 @@ function generateFastlyNativePlatformCapabilitiesAssemblyScript(plan, options =
2151
2210
  kv: 'Fastly KV Store async lookup/insert; conditional operations preserve u64 generations, bounded readiness, strict Pulse envelopes and uncertain dispatch',
2152
2211
  assets: 'Fastly KV Store lookup with provider-owned content type, cache headers, and GET/HEAD body ownership',
2153
2212
  grip: 'canonical stateless framing plus bound, secret-referenced broadcast HTTP realization and stable acknowledgement',
2154
- logging: 'best-effort redacted writes to the provider-owned stdout logging endpoint',
2213
+ logging: 'best-effort redacted WASI fd_write to stdout (descriptor 1), one attempt per statement',
2155
2214
  schemas: 'fetch JSON decode and structured response encode',
2156
2215
  opaqueResponse: 'origin fetch response/body handles and direct GRIP hold responses pass through without JavaScript runtime ownership',
2157
2216
  transportErrors: 'host status failures remain distinct from HTTP response status',
@@ -2304,9 +2363,10 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
2304
2363
  '--path', jsonAs.dependencyRoot
2305
2364
  );
2306
2365
  }
2366
+ require('@pulse-compute/wasm-build-support/native-optimization').appendJsonCacheArenaArgs(args, options.elideJsonCacheArena, schemaCodecsActive);
2307
2367
  const readReportCapture = options.reportCapture === false ? () => null : prepareReportCapture(args, stagingDir, generated.manifest,
2308
2368
  require(path.join(asc.packageRoot, 'package.json')).version, 'fastly-native-platform-capabilities.as/');
2309
- const startedAt = Date.now();
2369
+ const startedAt = process.hrtime.bigint();
2310
2370
  const result = spawnSync(asc.executable, args, {
2311
2371
  cwd: stagingDir,
2312
2372
  encoding: 'utf8',
@@ -2316,7 +2376,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
2316
2376
  ? { ...process.env, JSON_STRICT: 'true', JSON_USE_FAST_PATH: '0', JSON_MODE: 'NAIVE' }
2317
2377
  : process.env
2318
2378
  });
2319
- const assemblyScriptDurationMs = Date.now() - startedAt;
2379
+ const assemblyScriptDurationMs = Math.round(Number(process.hrtime.bigint() - startedAt) / 1e6);
2320
2380
  if (result.error || result.status !== 0 || !fs.existsSync(assemblyScriptWasmFile)) {
2321
2381
  fail('AssemblyScript failed to compile the Fastly native platform capabilities module.', 'PULSE_FASTLY_NATIVE_PLATFORM_CAPABILITIES_ASC_FAILED', {
2322
2382
  status: result.status,
@@ -2454,7 +2514,7 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
2454
2514
  compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
2455
2515
  assemblyScript: Object.freeze({ package: 'assemblyscript', version: require(path.join(asc.packageRoot, 'package.json')).version }),
2456
2516
  jsonAs: jsonAs
2457
- ? Object.freeze({ package: 'json-as', version: jsonAs.version, transform: true, strict: true, mode: 'NAIVE', fastPath: false })
2517
+ ? Object.freeze({ package: 'json-as', version: jsonAs.version, transform: true, strict: true, mode: 'NAIVE', fastPath: false, elideDisabledCacheArena: options.elideJsonCacheArena === true })
2458
2518
  : undefined,
2459
2519
  optimization,
2460
2520
  wasm: Object.freeze({ bytes: inspection.bytes, sha256: inspection.sha256, magic: inspection.magic }),
@@ -2474,11 +2534,17 @@ function compileFastlyNativePlatformCapabilitiesPlan(plan, options = {}) {
2474
2534
  providerPackaging: realization.guestLink.providerPackaging
2475
2535
  } : {})
2476
2536
  });
2477
- const durationMs = Date.now() - startedAt;
2537
+ const durationMs = Math.round(Number(process.hrtime.bigint() - startedAt) / 1e6);
2478
2538
  return Object.freeze({
2479
2539
  version: FASTLY_NATIVE_PLATFORM_CAPABILITIES_VERSION,
2480
2540
  compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
2481
2541
  reportAttribution: readReportCapture(wasm),
2542
+ buildObservation: require('@pulse-compute/wasm-build-support/native-optimization').observeAssemblyScript(args, {
2543
+ version: require(path.join(asc.packageRoot, 'package.json')).version,
2544
+ assemblyScriptRoot: asc.packageRoot, timeoutMs: Number(options.timeoutMs || 180000),
2545
+ compilerVersion: FASTLY_NATIVE_PLATFORM_CAPABILITIES_COMPILER_VERSION,
2546
+ durationMs: assemblyScriptDurationMs, jsonAsVersion: jsonAs && jsonAs.version
2547
+ }),
2482
2548
  plan,
2483
2549
  generated,
2484
2550
  source: generated.source,
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ // Shared by both Native emitters. Fastly's named logging endpoint ABI rejects
4
+ // stdout/stderr; Compute stdio uses WASI fd_write with stdout descriptor 1.
5
+ const nativeStdioSource = String.raw`
6
+ @external("wasi_snapshot_preview1", "fd_write")
7
+ declare function __pulse_fastly_fd_write(fd: i32, iovs: usize, count: i32, written: usize): i32
8
+
9
+ function __pulse_fastly_write_stdout(bytes: ArrayBuffer): void {
10
+ const iov = new StaticArray<usize>(2)
11
+ unchecked(iov[0] = changetype<usize>(bytes))
12
+ unchecked(iov[1] = bytes.byteLength)
13
+ const written = new StaticArray<i32>(1)
14
+ // One best-effort attempt: errors, zero progress, and short writes never
15
+ // retry, spin, change the response, or disable subsequent log statements.
16
+ __pulse_fastly_fd_write(1, changetype<usize>(iov), 1, changetype<usize>(written))
17
+ }
18
+ `;
19
+
20
+ module.exports = { nativeStdioSource };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- // Host value handles live for the invocation. Store concatenation as shared,
4
- // immutable prefixes instead of retaining a flat copy for every intermediate.
3
+ // Store concatenation as shared, immutable prefixes instead of retaining a flat
4
+ // copy for every intermediate. Direct object edges preserve live rope children.
5
5
  // This changes representation only: coercion happens at the original operation,
6
6
  // and aliases keep their original string even after a later append.
7
7
  const nativeStringFields = `
@@ -35,6 +35,38 @@ export function pulse_fastly_memory_values(): i64 { return __pulse_memory_values
35
35
  `;
36
36
  }
37
37
 
38
+ // These helpers are private to proven regions in an unlinked read-loop plan.
39
+ // Cache admissions use the original operations, so later boxed code sees the
40
+ // same cache and charges. Uncached numbers pay at each original evaluation;
41
+ // only their physical materialization is delayed until a region exit.
42
+ function numericRegionSource() {
43
+ return `
44
+ function __pulse_numeric_cacheable(value: f64): bool {
45
+ return value >= 0 && value < 65536 && Math.floor(value) == value && (value != 0 || 1.0 / value > 0)
46
+ }
47
+ function __pulse_numeric_charge_number(value: f64): f64 {
48
+ __pulse_memory_charge(0, 0)
49
+ if (__pulse_numeric_cacheable(value)) host_value_number(value)
50
+ else __pulse_memory_charge(1, ${policy.valueBytes})
51
+ return value
52
+ }
53
+ function __pulse_numeric_charge_boolean(value: bool): bool {
54
+ __pulse_memory_charge(0, 0)
55
+ host_value_boolean(value ? 1 : 0)
56
+ return value
57
+ }
58
+ function __pulse_numeric_truthy(value: f64): bool { return value != 0.0 && !isNaN(value) }
59
+ function __pulse_numeric_box(value: f64): i32 {
60
+ __pulse_memory_charge(0, 0)
61
+ if (__pulse_numeric_cacheable(value)) return host_value_number(value)
62
+ const item = new __PulseFastlyValue(); item.kind = PULSE_VALUE_NUMBER; item.number = value
63
+ // Already charged at the original expression, never a general put bypass.
64
+ __pulse_fastly_values.push(item)
65
+ return __pulse_fastly_values.length
66
+ }
67
+ `;
68
+ }
69
+
38
70
  function instrument(source, plan) {
39
71
  if (!hasBoundedReadLoop(plan)) return source;
40
72
  function insert(needle, extra) {
@@ -55,4 +87,4 @@ function instrument(source, plan) {
55
87
  return source + runtimeSource();
56
88
  }
57
89
 
58
- module.exports = { policy, hasBoundedReadLoop, runtimeSource, instrument };
90
+ module.exports = { policy, hasBoundedReadLoop, runtimeSource, numericRegionSource, instrument };
@@ -23,7 +23,7 @@ function instrumentRequestBudget(input, duration, plan) {
23
23
  replace('function __pulse_fastly_resolve_effect(effectIndex: i32): i32 {', `function __pulse_fastly_resolve_effect(effectIndex: i32): i32 {
24
24
  if (!__request_check(effectIndex)) return 0;
25
25
  const kind = __pulse_fastly_effect_kind(effectIndex);
26
- if (unchecked(__pulse_fastly_pending_mode[effectIndex]) == PULSE_FASTLY_PENDING_ASYNC && kind != 12 && kind != 13 && kind != 14 && kind != 21 && ${plan.effects.filter(e=>e.kind.startsWith('kv.')&&['kv.getVersioned','kv.insertIfAbsent','kv.compareAndSwap'].includes(e.kind)).map(e=>'effectIndex != '+plan.effects.indexOf(e)).join(' && ')||'true'}) {
26
+ 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'].includes(e.kind)).map(e=>'effectIndex != '+plan.effects.indexOf(e)).join(' && ')||'true'}) {
27
27
  if (!__request_wait(unchecked(__pulse_fastly_pending[effectIndex]), effectIndex)) return 0;
28
28
  }`);
29
29
  replace(' const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES), read = __pulse_fastly_out_i32();', ' if (!__request_wait(handle, effectIndex)) return "";\n const buffer = new Uint8Array(PULSE_FASTLY_BUFFER_BYTES), read = __pulse_fastly_out_i32();');
@@ -40,6 +40,8 @@ function instrumentRequestBudget(input, duration, plan) {
40
40
  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);');
41
41
  if (source.includes('__s3_deadlines[index] = start +')) replace('__s3_deadlines[index] = start + i64(binding.timeout) * 1000000;', '__s3_deadlines[index] = min<i64>(__request_deadline, start + i64(binding.timeout) * 1000000);');
42
42
  if (source.includes('function __pulse_fastly_write_binary(')) replace('const written = new StaticArray<i32>(1); let offset = 0;\n while (offset < data.length) {', 'const written = new StaticArray<i32>(1); let offset = 0;\n while (offset < data.length) { if (!__request_check()) return 1;');
43
+ replace('function __pulse_fetch_request_check(index: i32): bool { return true }', 'function __pulse_fetch_request_check(index: i32): bool { return __request_check(index) }');
44
+ replace('function __pulse_fetch_request_remaining(): i32 { return 2147483647 }', 'function __pulse_fetch_request_remaining(): i32 { return __request_remaining() }');
43
45
  return source + '\n' + fs.readFileSync(path.join(__dirname,'request-budget.as.ts'),'utf8');
44
46
  }
45
47
  module.exports = {instrumentRequestBudget};
@@ -10,8 +10,8 @@ class __PulseFastlyAdmissionFrame {
10
10
  }
11
11
  function __pulse_fastly_admission_enter(handle: i32, budget: __PulseJsonAdmissionBudget,
12
12
  stack: Array<__PulseFastlyAdmissionFrame>, active: Set<i32>): bool {
13
- if (handle <= 0 || handle > __pulse_fastly_values.length) return budget.fail(12)
14
- const value = __pulse_fastly_values[handle - 1]
13
+ if (!__pulse_fastly_has_value(handle)) return budget.fail(12)
14
+ const value = __pulse_fastly_value(handle)
15
15
  const array = value.kind == PULSE_VALUE_ARRAY
16
16
  const object = value.kind == PULSE_VALUE_OBJECT
17
17
  if (!budget.node(stack.length + (array || object ? 1 : 0))) return false
@@ -36,7 +36,7 @@ function __pulse_fastly_admit_bounded_value(handle: i32, limits: __PulseJsonAdmi
36
36
  if (!__pulse_fastly_admission_enter(handle, budget, stack, active)) return budget
37
37
  while (stack.length > 0) {
38
38
  const frame = stack[stack.length - 1]
39
- const value = __pulse_fastly_values[frame.handle - 1]
39
+ const value = __pulse_fastly_value(frame.handle)
40
40
  if (frame.index == value.values.length) { active.delete(frame.handle); stack.pop(); continue }
41
41
  const index = frame.index++
42
42
  if (index > 0 && !budget.addBytes(1)) return budget
package/src/config-api.js CHANGED
@@ -39,6 +39,7 @@ function fastly(options = {}) {
39
39
  const normalizedGrip = options.grip === undefined ? undefined : gripBindings(options.grip);
40
40
  return Object.freeze({
41
41
  kind: 'fastly',
42
+ ...(options.bodyForwarding === undefined ? {} : {bodyForwarding: require('./toolchain/body-forwarding.js').normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs)}),
42
43
  ...(options.maxDurationMs === undefined ? {} : { maxDurationMs: require('@pulse-compute/runtime/host').normalizeRequestDuration(options.maxDurationMs) }),
43
44
  version: FASTLY_PROVIDER_API_VERSION,
44
45
  bindings: Object.freeze({
@@ -82,6 +82,19 @@
82
82
  },
83
83
  "fastly.maxWasmBytes": {
84
84
  "kind": "positive-safe-integer"
85
+ },
86
+ "fastly.bodyForwarding": {
87
+ "kind": "object"
88
+ },
89
+ "fastly.bodyForwarding.requestMaxBytes": {
90
+ "kind": "integer",
91
+ "minimum": 1,
92
+ "maximum": 1048576
93
+ },
94
+ "fastly.bodyForwarding.responseMaxBytes": {
95
+ "kind": "integer",
96
+ "minimum": 1,
97
+ "maximum": 1048576
85
98
  }
86
99
  }
87
100
  }