@pulse-compute/provider-fastly 1.0.0-beta.8 → 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.
package/README.md CHANGED
@@ -7,9 +7,9 @@
7
7
  > **Supported entry points:** `@pulse-compute/provider-fastly`, `@pulse-compute/provider-fastly/contract`, `@pulse-compute/provider-fastly/toolchain`, `@pulse-compute/provider-fastly/config-schema`, `@pulse-compute/provider-fastly/runtime/canonical-api-runtime`, `@pulse-compute/provider-fastly/build/canonical-target`, `@pulse-compute/provider-fastly/testing/fastly-cli`<br>
8
8
  > **Stability:** The listed entry points are supported for the Beta; all other exported subpaths are implementation-only.<br>
9
9
  > **npm:** [`@pulse-compute/provider-fastly`](https://www.npmjs.com/package/@pulse-compute/provider-fastly)<br>
10
- > **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.8/packages/provider-fastly/)
10
+ > **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.9/packages/provider-fastly/)
11
11
  >
12
- > This release-status block is generated from the synchronized `Pulse 1.0.0-beta.8` package policy.
12
+ > This release-status block is generated from the synchronized `Pulse 1.0.0-beta.9` package policy.
13
13
  <!-- pulse-package-status:end -->
14
14
 
15
15
  Fastly provider configuration, canonical capability lowering, local conformance execution, Compute target generation, and explicit external-tool boundaries.
@@ -49,7 +49,7 @@ trusted wall-clock authority. It does not fall back to Node execution.
49
49
 
50
50
  ## Documentation
51
51
 
52
- - [Fastly package guide](https://pulsecompute.io/v1.0.0-beta.8/packages/provider-fastly/)
53
- - [Project configuration](https://pulsecompute.io/v1.0.0-beta.8/reference/project-config/#fastly-provider-options)
54
- - [Fastly capabilities guide](https://pulsecompute.io/v1.0.0-beta.8/guides/fastly-capabilities/)
55
- - [Environment variables](https://pulsecompute.io/v1.0.0-beta.8/reference/environment/#pulse-fastly-bin)
52
+ - [Fastly package guide](https://pulsecompute.io/v1.0.0-beta.9/packages/provider-fastly/)
53
+ - [Project configuration](https://pulsecompute.io/v1.0.0-beta.9/reference/project-config/#fastly-provider-options)
54
+ - [Fastly capabilities guide](https://pulsecompute.io/v1.0.0-beta.9/guides/fastly-capabilities/)
55
+ - [Environment variables](https://pulsecompute.io/v1.0.0-beta.9/reference/environment/#pulse-fastly-bin)
@@ -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
  }
package/dist/index.d.ts CHANGED
@@ -13,10 +13,16 @@ export interface PulseFastlyGripOptions {
13
13
  export interface PulseFastlyLocalOptions {
14
14
  readonly networkFetch?: boolean;
15
15
  }
16
+ export interface PulseFastlyBodyForwardingOptions {
17
+ readonly requestMaxBytes: number;
18
+ readonly responseMaxBytes: number;
19
+ }
16
20
  export interface PulseFastlyProviderOptions {
17
21
  /** Inclusive final Native Wasm byte budget; default 4 MiB. */
18
22
  readonly maxWasmBytes?: number;
19
23
  readonly maxDurationMs?: number;
24
+ /** Experimental Native metered pump; final close hands delivery to Fastly. */
25
+ readonly bodyForwarding?: PulseFastlyBodyForwardingOptions;
20
26
  readonly configStore?: string;
21
27
  readonly secretStore?: string;
22
28
  readonly kv?: Readonly<Record<string, string>>;
@@ -30,6 +36,8 @@ export interface PulseFastlyProviderOptions {
30
36
  }
31
37
  export interface PulseFastlyProviderConfig {
32
38
  readonly maxDurationMs?: number;
39
+ /** Experimental Native metered pump; final close hands delivery to Fastly. */
40
+ readonly bodyForwarding?: PulseFastlyBodyForwardingOptions;
33
41
  readonly kind: 'fastly';
34
42
  readonly version: typeof FASTLY_PROVIDER_API_VERSION;
35
43
  readonly bindings: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,2BAA2B,EAAG,8BAAuC,CAAC;AAEnF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,0BAA0B;IACzC,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,gCAAgC,CAAC;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,sBAAsB,CAAC;IACvC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,uBAAuB,CAAC;CAC1C;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,gCAAgC,CAAC;IAC3D,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,2BAA2B,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,EAAE;YACd,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;YAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;YAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;YAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;YACjC,QAAQ,CAAC,cAAc,CAAC,EAAE;gBACxB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;gBAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;aAC5B,CAAC;YACF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;SAC9B,CAAC;QACF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;KACnC,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QACpC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;KAChC,CAAC;IACF,QAAQ,CAAC,wBAAwB,EAAE,KAAK,CAAC;CAC1C;AA2BD,wBAAgB,MAAM,CAAC,OAAO,GAAE,0BAA+B,GAAG,yBAAyB,CAyB1F"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.FASTLY_PROVIDER_API_VERSION = void 0;
7
7
  exports.fastly = fastly;
8
+ const { normalizeBodyForwarding } = require('../src/toolchain/body-forwarding.js');
8
9
  const { normalizeFastlyMaxWasmBytes } = require('../src/build/wasm-budget.js');
9
10
  const { normalizeRequestDuration } = require('@pulse-compute/runtime/host');
10
11
  const config_schema_json_1 = __importDefault(require("./config-schema.json"));
@@ -40,6 +41,7 @@ function fastly(options = {}) {
40
41
  return Object.freeze({
41
42
  kind: 'fastly',
42
43
  maxDurationMs: normalizeRequestDuration(options.maxDurationMs),
44
+ bodyForwarding: normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs),
43
45
  version: exports.FASTLY_PROVIDER_API_VERSION,
44
46
  bindings: Object.freeze({
45
47
  configStore: String(options.configStore ?? fastlyDefaults['fastly.configStore']),
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulse-compute/provider-fastly",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0-beta.9",
4
4
  "license": "Apache-2.0",
5
5
  "engines": {
6
6
  "node": "^22.14.0 || ^24.0.0"
@@ -59,15 +59,15 @@
59
59
  }
60
60
  },
61
61
  "dependencies": {
62
- "@pulse-compute/crypto": "1.0.0-beta.8",
63
- "@pulse-compute/jwt": "1.0.0-beta.8",
64
- "@pulse-compute/runtime": "1.0.0-beta.8",
65
- "@pulse-compute/s3": "1.0.0-beta.8",
66
- "@pulse-compute/wasm-build-support": "1.0.0-beta.8",
67
- "@pulse-compute/wasm-contracts": "1.0.0-beta.8",
68
- "@pulse-compute/wasm-guest-link": "1.0.0-beta.8",
69
- "@pulse-compute/wasm-host-runtime": "1.0.0-beta.8",
70
- "@pulse-compute/wasm-runtime-core-as": "1.0.0-beta.8",
62
+ "@pulse-compute/crypto": "1.0.0-beta.9",
63
+ "@pulse-compute/jwt": "1.0.0-beta.9",
64
+ "@pulse-compute/runtime": "1.0.0-beta.9",
65
+ "@pulse-compute/s3": "1.0.0-beta.9",
66
+ "@pulse-compute/wasm-build-support": "1.0.0-beta.9",
67
+ "@pulse-compute/wasm-contracts": "1.0.0-beta.9",
68
+ "@pulse-compute/wasm-guest-link": "1.0.0-beta.9",
69
+ "@pulse-compute/wasm-host-runtime": "1.0.0-beta.9",
70
+ "@pulse-compute/wasm-runtime-core-as": "1.0.0-beta.9",
71
71
  "esbuild": "0.28.1"
72
72
  },
73
73
  "types": "./dist/index.d.ts",
@@ -87,7 +87,7 @@
87
87
  "url": "git+https://github.com/pulse-compute/pulse.git",
88
88
  "directory": "packages/provider-fastly"
89
89
  },
90
- "homepage": "https://pulsecompute.io/v1.0.0-beta.8/packages/provider-fastly/",
90
+ "homepage": "https://pulsecompute.io/v1.0.0-beta.9/packages/provider-fastly/",
91
91
  "bugs": {
92
92
  "url": "https://github.com/pulse-compute/pulse/issues"
93
93
  },
@@ -233,6 +233,7 @@ function inspectFastlyCanonicalTarget(options = {}) {
233
233
  profile: options.profile,
234
234
  bindings,
235
235
  maxDurationMs: providerConfig.maxDurationMs,
236
+ bodyForwarding: providerConfig.bodyForwarding,
236
237
  maxWasmBytes,
237
238
  requirePlatformCapability: false,
238
239
  canonicalBuild: true,
@@ -252,6 +253,10 @@ function inspectFastlyCanonicalTarget(options = {}) {
252
253
  if (native.manifest.bindings?.maxDurationMs !== duration) {
253
254
  throw providerBuildError('PULSE_REQUEST_DURATION_ARTIFACT_MISMATCH', 'Fastly artifact request deadline does not match the selected provider profile.');
254
255
  }
256
+ const forwarding = require('../toolchain/body-forwarding.js').normalizeBodyForwarding(providerConfig.bodyForwarding, duration);
257
+ if (JSON.stringify(native.manifest.bindings?.bodyForwarding) !== JSON.stringify(forwarding)) {
258
+ throw providerBuildError('PULSE_FASTLY_BODY_FORWARDING_ARTIFACT_MISMATCH', 'Fastly artifact forwarding bounds do not match the selected provider profile.');
259
+ }
255
260
  return Object.freeze({
256
261
  target: FASTLY_CANONICAL_TARGET,
257
262
  provider: 'fastly',
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { nativeStdioSource } = require('./native-stdio.js');
3
4
  const crypto = require('node:crypto');
4
5
  const fs = require('node:fs');
5
6
  const os = require('node:os');
@@ -41,8 +42,7 @@ const ALLOWED_IMPORTS = new Set([
41
42
  'fastly_http_resp:new',
42
43
  'fastly_http_resp:send_downstream',
43
44
  'fastly_http_resp:status_set',
44
- 'fastly_log:endpoint_get',
45
- 'fastly_log:write',
45
+ 'wasi_snapshot_preview1:fd_write',
46
46
  'fastly_secret_store:get',
47
47
  'fastly_secret_store:open',
48
48
  'fastly_secret_store:plaintext'
@@ -178,8 +178,7 @@ function providerRuntimeSource(bindings, options) {
178
178
  @external("fastly_secret_store", "open") declare function fastly_secret_store_open(name: usize, nameLength: i32, handleOut: usize): i32
179
179
  @external("fastly_secret_store", "get") declare function fastly_secret_store_get(handle: i32, key: usize, keyLength: i32, secretOut: usize): i32
180
180
  @external("fastly_secret_store", "plaintext") declare function fastly_secret_store_plaintext(secret: i32, value: usize, valueLength: i32, writtenOut: usize): i32
181
- @external("fastly_log", "endpoint_get") declare function fastly_log_endpoint_get(name: usize, nameLength: i32, handleOut: usize): i32
182
- @external("fastly_log", "write") declare function fastly_log_write(handle: i32, message: usize, messageLength: i32, writtenOut: usize): i32
181
+ ${nativeStdioSource}
183
182
 
184
183
  const __PULSE_FASTLY_ENTITIES_ABI: i64 = 1
185
184
  const __PULSE_FASTLY_ENTITIES_BUFFER: i32 = ${BUFFER_BYTES}
@@ -191,7 +190,6 @@ let __pulse_fastly_entities_results = new Array<string>()
191
190
  let __pulse_fastly_entities_ready = new Array<i32>()
192
191
  let __pulse_fastly_entities_ok = new Array<i32>()
193
192
  let __pulse_fastly_entities_redactions = new Array<string>()
194
- let __pulse_fastly_entities_log_handle: i32 = -1
195
193
 
196
194
  function __pulse_fastly_entities_out(): StaticArray<i32> { return new StaticArray<i32>(1) }
197
195
  function __pulse_fastly_entities_out_value(value: StaticArray<i32>): i32 { return load<i32>(changetype<usize>(value)) }
@@ -327,15 +325,8 @@ function pulse_entities_host_effect_begin(effectIndex: i32, payloadPointer: i32,
327
325
  }
328
326
  function pulse_entities_host_log(level: i32, payloadPointer: i32, payloadLength: i32): void {
329
327
  if (payloadPointer <= 0 || payloadLength < 0) return
330
- if (__pulse_fastly_entities_log_handle < 0) {
331
- const endpoint = __pulse_fastly_entities_utf8("stdout")
332
- const handleOut = __pulse_fastly_entities_out()
333
- if (fastly_log_endpoint_get(changetype<usize>(endpoint), endpoint.byteLength, changetype<usize>(handleOut)) != __PULSE_FASTLY_OK) return
334
- __pulse_fastly_entities_log_handle = __pulse_fastly_entities_out_value(handleOut)
335
- }
336
328
  const message = __pulse_fastly_entities_utf8("[pulse:entities:" + level.toString() + "] " + __pulse_fastly_entities_redact(changetype<string>(payloadPointer)) + "\n")
337
- const written = __pulse_fastly_entities_out()
338
- fastly_log_write(__pulse_fastly_entities_log_handle, changetype<usize>(message), message.byteLength, changetype<usize>(written))
329
+ __pulse_fastly_write_stdout(message)
339
330
  }
340
331
  function __pulse_fastly_entities_write_body(handle: i32, value: string): i32 {
341
332
  const bytes = __pulse_fastly_entities_utf8(value)
@@ -0,0 +1,334 @@
1
+ // FWD9-02: sequential upload/response pumps. Payload bytes never become Pulse
2
+ // values. Fixed scratch and ABI outputs are reused, including the clock slot.
3
+ // close accepts EOF into Fastly; it is not a delivery/drain receipt.
4
+ @external('fastly_http_req', 'send_async_streaming')
5
+ declare function __fwd_dispatch(request: i32, body: i32, backend: usize, length: i32, pending: usize): i32
6
+ @external('fastly_http_body', 'abandon') declare function __fwd_abandon(body: i32): i32
7
+ @external('fastly_async_io', 'is_ready') declare function __fwd_ready(handle: i32, ready: usize): i32
8
+ @external('fastly_http_resp', 'header_names_get')
9
+ declare function __fwd_names(handle: i32, buffer: usize, size: i32, cursor: i32, end: usize, written: usize): i32
10
+ @external('fastly_http_resp', 'header_values_get')
11
+ declare function __fwd_values(handle: i32, name: usize, length: i32, buffer: usize, size: i32, cursor: i32, end: usize, written: usize): i32
12
+ const __fwd_buffer: usize = memory.data(16384)
13
+ const __fwd_headers: usize = memory.data(16384)
14
+ const __fwd_pairs: usize = memory.data(1024) // 64 x (name ptr/size, value ptr/size)
15
+ const __fwd_io: usize = memory.data(128) // aligned reusable ABI outputs
16
+ let __fwd_source: i32 = -1
17
+ let __fwd_upload: i32 = -1
18
+ let __fwd_request: i32 = -1
19
+ let __fwd_pending: i32 = -1
20
+ let __fwd_response: i32 = -1
21
+ let __fwd_body: i32 = -1
22
+ let __fwd_downstream: i32 = -1
23
+ let __fwd_output: i32 = -1
24
+ let __fwd_index: i32 = -1
25
+ let __fwd_result: i32 = 0
26
+ let __fwd_marker: i32 = 0
27
+ let __fwd_failure: i32 = 0
28
+ let __fwd_committed: bool = false
29
+ let __fwd_reserved: bool = false
30
+ let __fwd_header_count: i32 = 0
31
+ let __fwd_request_length: i32 = -1
32
+ let __fwd_response_length: i32 = -1
33
+ let __fwd_status: i32 = 200
34
+ function host_request_body(): i32 {
35
+ if (__fwd_marker != 0 || __fwd_reserved) { __fwd_fail(400); return 0; }
36
+ const marker = new __PulseFastlyValue(); marker.kind = 90;
37
+ __fwd_marker = __pulse_fastly_put(marker); return __fwd_marker;
38
+ }
39
+ function __fwd_fail(status: i32): void {
40
+ if (__fwd_failure == 0) __fwd_failure = status;
41
+ __pulse_fastly_fail(PULSE_ERROR_HOSTCALL, 280, __fwd_index);
42
+ }
43
+ function __fwd_remaining(): i32 {
44
+ if (__fwd_failure != 0) return 0;
45
+ if (wasi_snapshot_preview1_clock_time_get(1, 1, __fwd_io) != 0) { __fwd_fail(504); return 0; }
46
+ const now = load<i64>(__fwd_io);
47
+ if (now < 0 || now < __request_last_clock || now < __pulse_fetch_last_clock) { __fwd_fail(504); return 0; }
48
+ __request_last_clock = now; __pulse_fetch_last_clock = now;
49
+ let remaining = f64(__request_deadline - now) / 1000000.0;
50
+ if (__request_expired || remaining <= 0) { __request_expired = true; __fwd_fail(504); return 0; }
51
+ if (__fwd_index >= 0) remaining = Math.min(remaining, __pulse_fetch_remaining(__fwd_index, now));
52
+ if (remaining <= 0) { __fwd_fail(504); return 0; }
53
+ return i32(Math.ceil(remaining)); // 1..30000; zero never reaches async.select
54
+ }
55
+ function __fwd_cleanup(): void {
56
+ if (__fwd_source >= 0) { __pulse_fetch_close_body(__fwd_source); __fwd_source = -1; }
57
+ if (__fwd_upload >= 0) { __fwd_abandon(__fwd_upload); __fwd_upload = -1; }
58
+ if (__fwd_request >= 0) { __pulse_fetch_close_request(__fwd_request); __fwd_request = -1; }
59
+ if (__fwd_body >= 0) { __pulse_fetch_close_body(__fwd_body); __fwd_body = -1; }
60
+ if (__fwd_response >= 0) { __pulse_fetch_close_response(__fwd_response); __fwd_response = -1; }
61
+ if (__fwd_output >= 0) { __pulse_fetch_close_response(__fwd_output); __fwd_output = -1; }
62
+ if (__fwd_downstream >= 0) { __fwd_abandon(__fwd_downstream); __fwd_downstream = -1; }
63
+ if (__fwd_pending >= 0) {
64
+ // No pending-request cancel ABI. Poll once without waiting and discard any
65
+ // late handles; an unresolved host operation ends at invocation teardown.
66
+ store<i32>(__fwd_io + 24, 0);
67
+ const status = __pulse_fetch_poll(__fwd_pending, __fwd_io + 8, __fwd_io + 24, __fwd_io + 28, __fwd_io + 32);
68
+ if (status == 0 && load<i32>(__fwd_io + 24) == 1) {
69
+ __pulse_fetch_close_response(load<i32>(__fwd_io + 28)); __pulse_fetch_close_body(load<i32>(__fwd_io + 32));
70
+ }
71
+ __fwd_pending = -1;
72
+ }
73
+ }
74
+ function __fwd_error_response(): bool {
75
+ if (__fwd_committed) { __fwd_cleanup(); return true; }
76
+ if (__fwd_failure == 0) return false;
77
+ const status = __fwd_failure;
78
+ __fwd_cleanup(); __request_error_response = true;
79
+ __pulse_fastly_last_error = 0;
80
+ const value = new __PulseFastlyValue(); value.kind = PULSE_VALUE_RESPONSE; value.status = status;
81
+ value.text = status == 413 ? 'Payload Too Large' : status == 504 ? 'Gateway Timeout' : status == 400 ? 'Bad Request' : 'Bad Gateway';
82
+ __fwd_committed = true;
83
+ __pulse_fastly_send_result(__pulse_fastly_put(value)); return true;
84
+ }
85
+ function __fwd_lower(byte: u8): u8 { return byte >= 65 && byte <= 90 ? byte + 32 : byte; }
86
+ function __fwd_equal(ptr: usize, size: i32, text: string): bool {
87
+ if (size != text.length) return false;
88
+ for (let i = 0; i < size; i++) if (__fwd_lower(load<u8>(ptr + i)) != text.charCodeAt(i)) return false;
89
+ return true;
90
+ }
91
+ function __fwd_token(byte: u8): bool {
92
+ return (byte >= 48 && byte <= 57) || (byte >= 65 && byte <= 90) || (byte >= 97 && byte <= 122)
93
+ || byte == 33 || byte == 35 || byte == 36 || byte == 37 || byte == 38 || byte == 39 || byte == 42
94
+ || byte == 43 || byte == 45 || byte == 46 || byte == 94 || byte == 95 || byte == 96 || byte == 124 || byte == 126;
95
+ }
96
+ function __fwd_hop(ptr: usize, size: i32): bool {
97
+ return __fwd_equal(ptr,size,'connection') || __fwd_equal(ptr,size,'keep-alive') || __fwd_equal(ptr,size,'proxy-connection')
98
+ || __fwd_equal(ptr,size,'transfer-encoding') || __fwd_equal(ptr,size,'te') || __fwd_equal(ptr,size,'trailer')
99
+ || __fwd_equal(ptr,size,'upgrade') || __fwd_equal(ptr,size,'content-length') || __fwd_equal(ptr,size,'host')
100
+ || __fwd_equal(ptr,size,'proxy-authenticate') || __fwd_equal(ptr,size,'proxy-authorization');
101
+ }
102
+ // Decimal parsing stops at cap+1: huge declared sizes cannot overflow or wrap.
103
+ function __fwd_length(ptr: usize, size: i32, cap: i32): i32 {
104
+ if (size == 0) return -2;
105
+ let value = 0;
106
+ for (let i = 0; i < size; i++) {
107
+ const byte = load<u8>(ptr + i); if (byte < 48 || byte > 57) return -2;
108
+ value = min(cap + 1, value * 10 + byte - 48);
109
+ }
110
+ return value;
111
+ }
112
+ function __fwd_declared_request(): bool {
113
+ const name = memory.data<u8>([99,111,110,116,101,110,116,45,108,101,110,103,116,104]);
114
+ store<i64>(__fwd_io + 40, -1); store<i32>(__fwd_io + 48, 0);
115
+ const status = __pulse_request_header_values(__pulse_fastly_request_handle, name, 14, __fwd_headers, 16384, 0, __fwd_io + 40, __fwd_io + 48);
116
+ const count = load<i32>(__fwd_io + 48);
117
+ if (status != 0 || count < 0 || count > 16384 || (count > 0 && load<i64>(__fwd_io + 40) != -1)) { __fwd_fail(400); return false; }
118
+ if (count == 0) return true;
119
+ if (load<u8>(__fwd_headers + count - 1) != 0) { __fwd_fail(400); return false; }
120
+ __fwd_request_length = __fwd_length(__fwd_headers, count - 1, __FWD_REQUEST_MAX);
121
+ if (__fwd_request_length < 0) { __fwd_fail(400); return false; }
122
+ if (__fwd_request_length > __FWD_REQUEST_MAX) { __fwd_fail(413); return false; }
123
+ return true;
124
+ }
125
+ function __fwd_begin(index: i32, payload: __PulseFastlyValue): void {
126
+ __fwd_index = index;
127
+ const init = __pulse_fastly_value(__pulse_fastly_payload_field(payload, 'init'));
128
+ if (__fwd_reserved || __fwd_source < 0 || __pulse_fastly_payload_field(init, 'body') != __fwd_marker || __fwd_marker == 0) { __fwd_fail(400); return; }
129
+ __fwd_reserved = true;
130
+ __pulse_fetch_begin_deadline(index, init);
131
+ if (__pulse_fastly_last_error != 0 || !__fwd_remaining() || !__fwd_declared_request()) return;
132
+ const url = __pulse_fastly_utf8(__pulse_fastly_string(__pulse_fastly_payload_field(payload, 'url')));
133
+ const backend = __pulse_fastly_utf8(__pulse_fastly_backend(index));
134
+ if (fastly_http_req_new(__fwd_io + 52) != 0) { __fwd_fail(502); return; }
135
+ __fwd_request = load<i32>(__fwd_io + 52);
136
+ if (fastly_http_body_new(__fwd_io + 52) != 0) { __fwd_fail(502); return; }
137
+ __fwd_upload = load<i32>(__fwd_io + 52);
138
+ const method = memory.data<u8>([80,79,83,84]);
139
+ if (fastly_http_req_method_set(__fwd_request, method, 4) != 0 || fastly_http_req_uri_set(__fwd_request, changetype<usize>(url), url.byteLength) != 0) { __fwd_fail(502); return; }
140
+ const headers = __pulse_fastly_value(__pulse_fastly_payload_field(init, 'headers'));
141
+ let bytes = 0;
142
+ if (headers.kind == PULSE_VALUE_OBJECT) for (let i = 0; i < headers.keys.length; i++) {
143
+ const name = __pulse_fastly_utf8(headers.keys[i]), value = __pulse_fastly_utf8(__pulse_fastly_string(headers.values[i]));
144
+ bytes += name.byteLength + value.byteLength + 4;
145
+ if (i >= 64 || bytes > 16384 || __fwd_hop(changetype<usize>(name), name.byteLength)) { __fwd_fail(400); return; }
146
+ if (fastly_http_req_header_insert(__fwd_request, changetype<usize>(name), name.byteLength, changetype<usize>(value), value.byteLength) != 0) { __fwd_fail(400); return; }
147
+ }
148
+ if (!__fwd_remaining()) return;
149
+ const status = __fwd_dispatch(__fwd_request, __fwd_upload, changetype<usize>(backend), backend.byteLength, __fwd_io + 52);
150
+ // send may consume the request even on failure. Cleanup makes only a best-effort
151
+ // close on that error path before any handle allocation/reuse can occur.
152
+ if (status != 0) { __fwd_fail(502); return; }
153
+ __fwd_request = -1; __fwd_pending = load<i32>(__fwd_io + 52);
154
+ __pulse_fastly_pending[index] = __fwd_pending; __pulse_fastly_pending_mode[index] = PULSE_FASTLY_PENDING_ASYNC;
155
+ __fwd_remaining();
156
+ }
157
+ // Adopt early headers before considering readiness of the source or writer.
158
+ function __fwd_poll(): bool {
159
+ if (__fwd_pending < 0 || !__fwd_remaining()) return __fwd_response >= 0;
160
+ store<i32>(__fwd_io + 24, 0);
161
+ const status = __pulse_fetch_poll(__fwd_pending, __fwd_io + 8, __fwd_io + 24, __fwd_io + 28, __fwd_io + 32);
162
+ const done = load<i32>(__fwd_io + 24);
163
+ if (status != 0) { __fwd_pending = -1; __fwd_fail(502); return false; }
164
+ if (done != 0 && done != 1) { __fwd_fail(502); return false; }
165
+ if (done == 1) {
166
+ __fwd_response = load<i32>(__fwd_io + 28); __fwd_body = load<i32>(__fwd_io + 32); __fwd_pending = -1;
167
+ }
168
+ if (!__fwd_remaining()) return false;
169
+ return done == 1;
170
+ }
171
+ function __fwd_wait_io(handle: i32, upload: bool): bool {
172
+ while (__fwd_remaining() > 0) {
173
+ if (upload && __fwd_poll()) return false;
174
+ if (__fwd_failure != 0) return false;
175
+ store<i32>(__fwd_io + 52, -1);
176
+ if (__fwd_ready(handle, __fwd_io + 52) != 0) { __fwd_fail(502); return false; }
177
+ const ready = load<i32>(__fwd_io + 52);
178
+ if (ready == 1) return __fwd_remaining() > 0;
179
+ if (ready != 0) { __fwd_fail(502); return false; }
180
+ let count = 0;
181
+ if (upload && __fwd_pending >= 0) { store<i32>(__fwd_io + 56, __fwd_pending); count++; }
182
+ store<i32>(__fwd_io + 56 + count * 4, handle); count++;
183
+ const remaining = __fwd_remaining(); if (remaining == 0) return false;
184
+ const status = __pulse_fetch_select(__fwd_io + 56, count, remaining, __fwd_io + 64);
185
+ if (status != 0) { __fwd_fail(502); return false; }
186
+ const selected = load<i32>(__fwd_io + 64);
187
+ if (selected == -1) { __fwd_fail(504); return false; }
188
+ if (selected < 0 || selected >= count) { __fwd_fail(502); return false; }
189
+ }
190
+ return false;
191
+ }
192
+ function __fwd_copy(source: i32, sink: i32, cap: i32, declared: i32, upload: bool): bool {
193
+ let total = 0;
194
+ while (true) {
195
+ // Demand first; at most one raw chunk can be read ahead of the writer.
196
+ if (!__fwd_wait_io(sink, upload) || !__fwd_wait_io(source, upload)) return false;
197
+ const size = min(16384, cap - total + 1);
198
+ store<i32>(__fwd_io + 68, -1);
199
+ if (fastly_http_body_read(source, __fwd_buffer, size, __fwd_io + 68) != 0) { __fwd_fail(upload ? 400 : 502); return false; }
200
+ const read = load<i32>(__fwd_io + 68);
201
+ if (!__fwd_remaining()) return false;
202
+ if (read < 0 || read > size) { __fwd_fail(502); return false; }
203
+ if (read == 0) {
204
+ if (declared >= 0 && total != declared) { __fwd_fail(upload ? 400 : 502); return false; }
205
+ return true;
206
+ }
207
+ total += read;
208
+ if (total > cap) { __fwd_fail(upload ? 413 : 502); return false; }
209
+ if (declared >= 0 && total > declared) { __fwd_fail(upload ? 400 : 502); return false; }
210
+ let offset = 0;
211
+ while (offset < read) {
212
+ if (!__fwd_wait_io(sink, upload)) return false;
213
+ store<i32>(__fwd_io + 72, -1);
214
+ if (fastly_http_body_write(sink, __fwd_buffer + offset, read - offset, 0, __fwd_io + 72) != 0) { __fwd_fail(502); return false; }
215
+ const written = load<i32>(__fwd_io + 72);
216
+ if (!__fwd_remaining()) return false;
217
+ if (written <= 0 || written > read - offset) { __fwd_fail(502); return false; }
218
+ offset += written;
219
+ }
220
+ }
221
+ }
222
+ function __fwd_wait(): i32 {
223
+ if (__fwd_failure != 0 || __fwd_pending < 0) return 0;
224
+ const eof = __fwd_copy(__fwd_source, __fwd_upload, __FWD_REQUEST_MAX, __fwd_request_length, true);
225
+ __pulse_fetch_close_body(__fwd_source); __fwd_source = -1;
226
+ if (eof && __fwd_remaining()) {
227
+ const upload = __fwd_upload; __fwd_upload = -1;
228
+ if (__pulse_fetch_close_body(upload) != 0) __fwd_fail(502);
229
+ } else if (__fwd_upload >= 0) { __fwd_abandon(__fwd_upload); __fwd_upload = -1; }
230
+ while (__fwd_failure == 0 && __fwd_response < 0) {
231
+ if (__fwd_poll()) break;
232
+ if (__fwd_failure != 0 || !__fwd_wait_io(__fwd_pending, false)) break;
233
+ }
234
+ __pulse_fastly_pending_mode[__fwd_index] = PULSE_FASTLY_PENDING_NONE; __pulse_fastly_pending[__fwd_index] = 0;
235
+ if (__fwd_failure != 0 || !__fwd_remaining()) return 0;
236
+ const value = new __PulseFastlyValue(); value.kind = PULSE_VALUE_FETCH;
237
+ value.responseHandle = __fwd_response; value.bodyHandle = __fwd_body;
238
+ __fwd_result = __pulse_fastly_put(value); return __fwd_result;
239
+ }
240
+ function __fwd_response_headers(): bool {
241
+ store<i32>(__fwd_io + 52, 0);
242
+ if (fastly_http_resp_status_get(__fwd_response, __fwd_io + 52) != 0) { __fwd_fail(502); return false; }
243
+ __fwd_status = load<u16>(__fwd_io + 52);
244
+ if (__fwd_status < 200 || __fwd_status > 599) { __fwd_fail(502); return false; }
245
+ store<i64>(__fwd_io + 40, -1); store<i32>(__fwd_io + 48, -1);
246
+ if (__fwd_names(__fwd_response, __fwd_headers, 16384, 0, __fwd_io + 40, __fwd_io + 48) != 0) { __fwd_fail(502); return false; }
247
+ const names = load<i32>(__fwd_io + 48);
248
+ if (names < 0 || names > 16384 || (names > 0 && load<i64>(__fwd_io + 40) != -1)) { __fwd_fail(502); return false; }
249
+ let used = names, start = 0, total = 0, nameCount = 0;
250
+ for (let i = 0; i < names; i++) {
251
+ const byte = load<u8>(__fwd_headers + i);
252
+ if (byte != 0) { if (!__fwd_token(byte)) { __fwd_fail(502); return false; } continue; }
253
+ const name = __fwd_headers + start, length = i - start; start = i + 1;
254
+ if (length == 0 || ++nameCount > 64 || used >= 16384) { __fwd_fail(502); return false; }
255
+ store<i64>(__fwd_io + 40, -1); store<i32>(__fwd_io + 48, -1);
256
+ if (__fwd_values(__fwd_response, name, length, __fwd_headers + used, 16384 - used, 0, __fwd_io + 40, __fwd_io + 48) != 0) { __fwd_fail(502); return false; }
257
+ const size = load<i32>(__fwd_io + 48);
258
+ if (size <= 0 || size > 16384 - used || load<i64>(__fwd_io + 40) != -1) { __fwd_fail(502); return false; }
259
+ let valueStart = 0;
260
+ for (let j = 0; j < size; j++) {
261
+ const item = load<u8>(__fwd_headers + used + j);
262
+ if (item != 0) { if ((item < 32 && item != 9) || item == 127) { __fwd_fail(502); return false; } continue; }
263
+ const value = __fwd_headers + used + valueStart, valueSize = j - valueStart; valueStart = j + 1;
264
+ total += length + valueSize + 4;
265
+ if (__fwd_header_count >= 64 || total > 16384) { __fwd_fail(502); return false; }
266
+ const pair = __fwd_pairs + __fwd_header_count * 16; __fwd_header_count++;
267
+ store<usize>(pair, name); store<i32>(pair + 4, length); store<usize>(pair + 8, value); store<i32>(pair + 12, valueSize);
268
+ if (__fwd_equal(name, length, 'content-length')) {
269
+ if (__fwd_response_length != -1) { __fwd_fail(502); return false; }
270
+ __fwd_response_length = __fwd_length(value, valueSize, __FWD_RESPONSE_MAX);
271
+ if (__fwd_response_length < 0 || (__fwd_status != 304 && __fwd_response_length > __FWD_RESPONSE_MAX)) { __fwd_fail(502); return false; }
272
+ }
273
+ }
274
+ if (valueStart != size) { __fwd_fail(502); return false; }
275
+ used += size;
276
+ }
277
+ if (start != names || ((__fwd_status == 204 || __fwd_status == 205) && __fwd_response_length > 0)) { __fwd_fail(502); return false; }
278
+ return __fwd_remaining() > 0;
279
+ }
280
+ function __fwd_nominated(name: usize, length: i32): bool {
281
+ let nominated = false;
282
+ for (let i = 0; i < __fwd_header_count; i++) {
283
+ const pair = __fwd_pairs + i * 16;
284
+ if (!__fwd_equal(load<usize>(pair), load<i32>(pair + 4), 'connection')) continue;
285
+ const value = load<usize>(pair + 8), size = load<i32>(pair + 12);
286
+ let offset = 0;
287
+ while (offset < size) {
288
+ while (offset < size && (load<u8>(value + offset) == 32 || load<u8>(value + offset) == 9)) offset++;
289
+ const start = offset;
290
+ while (offset < size && __fwd_token(load<u8>(value + offset))) offset++;
291
+ const count = offset - start;
292
+ if (count == 0) { __fwd_fail(502); return false; }
293
+ if (count == length) {
294
+ let same = true;
295
+ for (let c = 0; c < count; c++) if (__fwd_lower(load<u8>(value + start + c)) != __fwd_lower(load<u8>(name + c))) same = false;
296
+ if (same) nominated = true;
297
+ }
298
+ while (offset < size && (load<u8>(value + offset) == 32 || load<u8>(value + offset) == 9)) offset++;
299
+ if (offset < size && (load<u8>(value + offset++) != 44 || offset == size)) { __fwd_fail(502); return false; }
300
+ }
301
+ }
302
+ return nominated;
303
+ }
304
+ function __fwd_send(): i32 {
305
+ if (!__fwd_response_headers()) { __fwd_error_response(); return 0; }
306
+ if (fastly_http_resp_new(__fwd_io + 52) != 0) { __fwd_fail(502); __fwd_error_response(); return 0; }
307
+ __fwd_output = load<i32>(__fwd_io + 52);
308
+ if (fastly_http_resp_status_set(__fwd_output, __fwd_status) != 0 || fastly_http_body_new(__fwd_io + 52) != 0) { __fwd_fail(502); __fwd_error_response(); return 0; }
309
+ __fwd_downstream = load<i32>(__fwd_io + 52);
310
+ for (let i = 0; i < __fwd_header_count; i++) {
311
+ const pair = __fwd_pairs + i * 16, name = load<usize>(pair), length = load<i32>(pair + 4);
312
+ const nominated = __fwd_nominated(name, length);
313
+ if (__fwd_failure != 0) break;
314
+ if (__fwd_hop(name, length) || nominated) continue;
315
+ if (fastly_http_resp_header_append(__fwd_output, name, length, load<usize>(pair + 8), load<i32>(pair + 12)) != 0) { __fwd_fail(502); break; }
316
+ }
317
+ __pulse_fetch_close_response(__fwd_response); __fwd_response = -1;
318
+ if (!__fwd_remaining()) { __fwd_error_response(); return 0; }
319
+ // Treat even an ambiguous send failure as committed: never emit a second
320
+ // response. The writable body remains ours until explicit EOF or abandon.
321
+ __fwd_committed = true;
322
+ const output = __fwd_output; __fwd_output = -1;
323
+ if (fastly_http_resp_send_downstream(output, __fwd_downstream, 1) != 0) { __pulse_fetch_close_response(output); __fwd_fail(502); __fwd_cleanup(); return 0; }
324
+ const bodyless = __fwd_status == 204 || __fwd_status == 205 || __fwd_status == 304;
325
+ const eof = __fwd_copy(__fwd_body, __fwd_downstream, bodyless ? 0 : __FWD_RESPONSE_MAX, bodyless ? 0 : __fwd_response_length, false);
326
+ __pulse_fetch_close_body(__fwd_body); __fwd_body = -1;
327
+ if (eof && __fwd_remaining()) {
328
+ const downstream = __fwd_downstream; __fwd_downstream = -1;
329
+ if (__pulse_fetch_close_body(downstream) != 0) __fwd_fail(502);
330
+ // Success is the host-handoff boundary. The handle is gone; there is no
331
+ // final-writer wait, delivery receipt, or post-handoff timeout guarantee.
332
+ }
333
+ __fwd_cleanup(); return 0;
334
+ }
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const {normalizeBodyForwarding, forwardingError} = require('../toolchain/body-forwarding.js');
5
+ const IMPORTS = ['fastly_http_req:send_async_streaming', 'fastly_http_body:abandon',
6
+ 'fastly_async_io:is_ready', 'fastly_http_resp:header_names_get'];
7
+ const enabled = plan => (plan?.capabilities || []).includes('request.body.forward');
8
+ function field(value, name) { return value?.entries?.find(entry => entry.key?.value === name)?.value; }
9
+ function validate(plan, options) {
10
+ const config = normalizeBodyForwarding(options.bodyForwarding, options.maxDurationMs);
11
+ if (!enabled(plan)) return;
12
+ if (!config) throw forwardingError('Fastly Native incoming forwarding requires fastly.bodyForwarding limits and maxDurationMs.');
13
+ const effects = plan.effects || [], effect = effects[0];
14
+ const init = effect?.inputs?.find(input => input.name === 'init')?.value;
15
+ const body = field(init, 'body'), method = field(init, 'method'), headers = field(init, 'headers');
16
+ if (effects.length !== 1 || effect.kind !== 'fetch' || effect.grouped
17
+ || effect.result?.mode !== 'return' || effect.result?.valueKind !== 'opaque-response'
18
+ || effect.resource?.kind !== 'literal' || options.bindings?.dynamicBackends
19
+ || body?.name !== 'request.body.forward-marker' || method?.kind !== 'literal' || method.value !== 'POST'
20
+ || init.entries.some(entry => !['method', 'body', 'headers', 'timeoutMs'].includes(entry.key?.value))
21
+ || (headers && (headers.kind !== 'object' || headers.entries.some(entry => entry.key?.kind !== 'literal' || entry.value?.kind !== 'literal' || typeof entry.value.value !== 'string')))) {
22
+ throw forwardingError('Fastly Native forwarding admits one terminal opaque POST fetch with inline req.body(), a static backend, and optional literal headers/timeoutMs; groups, decoding, reuse, and dynamic backends are unsupported.');
23
+ }
24
+ const entries = headers?.entries || [];
25
+ const hop = /^(connection|keep-alive|proxy-connection|transfer-encoding|te|trailer|upgrade|content-length|host|proxy-authenticate|proxy-authorization)$/i;
26
+ if (entries.length > 64 || entries.reduce((sum, entry) => sum + Buffer.byteLength(entry.key.value) + Buffer.byteLength(entry.value.value) + 4, 0) > 16384
27
+ || entries.some(entry => hop.test(entry.key.value) || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(entry.key.value) || /[\x00-\x08\x0a-\x1f\x7f]/.test(entry.value.value))) {
28
+ throw forwardingError('Forwarding request headers must fit 64 pairs/16 KiB and exclude hop-by-hop, framing and Host fields.');
29
+ }
30
+ // One marker evaluation owns the incoming body. Reject unused/duplicate markers
31
+ // too: they must not be dispatched later through another continuation.
32
+ let markers = 0;
33
+ function walk(value) { if (!value || typeof value !== 'object') return; if (value.name === 'request.body.forward-marker') markers++; for (const item of Object.values(value)) walk(item); }
34
+ walk(plan);
35
+ if (markers !== 1) throw forwardingError('Fastly Native forwarding requires exactly one incoming body consumer.');
36
+ }
37
+ function instrument(input, plan, bindings) {
38
+ if (!enabled(plan)) return input;
39
+ let source = input;
40
+ const replace = (before, after) => {
41
+ if (!source.includes(before)) throw new Error('Fastly body-forwarding boundary drift: ' + before.slice(0, 100));
42
+ source = source.replace(before, after);
43
+ };
44
+ for (const [name, args, result, body] of [
45
+ ['__pulse_fastly_fetch_begin', 'effectIndex: i32, payload: __PulseFastlyValue', 'void', '__fwd_begin(effectIndex, payload);'],
46
+ ['__pulse_fastly_wait_fetch', 'effectIndex: i32', 'i32', 'return __fwd_wait();']
47
+ ]) {
48
+ const line = source.split('\n').find(line => line.startsWith(`function ${name}(`));
49
+ if (!line || !line.endsWith('}')) throw new Error('Fastly forwarding function boundary drift: ' + name);
50
+ replace(line, `function ${name}(${args}): ${result} { ${body} }`);
51
+ }
52
+ replace('function __pulse_fastly_send_result(handle: i32): i32 {',
53
+ 'function __pulse_fastly_send_result(handle: i32): i32 { if (handle == __fwd_result && handle != 0) return __fwd_send();');
54
+ replace('function __pulse_fastly_jwt_send_error(): void {',
55
+ 'function __pulse_fastly_jwt_send_error(): void { if (__fwd_error_response()) return;');
56
+ replace('function __request_send_timeout(): void {',
57
+ 'function __request_send_timeout(): void { if (__fwd_committed) { __fwd_cleanup(); return; }');
58
+ replace(' __pulse_fastly_run_invocation()\n', ' __pulse_fastly_run_invocation()\n __fwd_cleanup()\n');
59
+ replace(' __pulse_fastly_request_body_handle = load<i32>(changetype<usize>(handles) + 4)',
60
+ ' __pulse_fastly_request_body_handle = load<i32>(changetype<usize>(handles) + 4)\n __fwd_source = __pulse_fastly_request_body_handle');
61
+ // Generic request-budget code must not wait for headers before driving upload.
62
+ const begin = source.indexOf('function __pulse_fastly_resolve_effect(');
63
+ const end = source.indexOf('\n}', begin);
64
+ if (begin < 0 || end < 0) throw new Error('Fastly forwarding effect boundary drift');
65
+ source = source.slice(0, begin) + 'function __pulse_fastly_resolve_effect(effectIndex: i32): i32 { return __fwd_wait(); }' + source.slice(end + 2);
66
+ return source + '\n' + `const __FWD_REQUEST_MAX: i32 = ${bindings.bodyForwarding.requestMaxBytes}\nconst __FWD_RESPONSE_MAX: i32 = ${bindings.bodyForwarding.responseMaxBytes}\n`
67
+ + fs.readFileSync(path.join(__dirname, 'native-body-forwarding.as.ts'), 'utf8');
68
+ }
69
+ module.exports = {IMPORTS, enabled, validate, instrument};