@gcoredev/fastedge-test 0.2.0 → 0.2.1

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/dist/lib/index.js CHANGED
@@ -5,13 +5,11 @@ import { WASI } from "node:wasi";
5
5
  var textEncoder = new TextEncoder();
6
6
  var textDecoder = new TextDecoder();
7
7
  var MemoryManager = class {
8
- constructor() {
9
- this.memory = null;
10
- this.instance = null;
11
- this.hostAllocOffset = 0;
12
- this.logCallback = null;
13
- this.isInitializing = false;
14
- }
8
+ memory = null;
9
+ instance = null;
10
+ hostAllocOffset = 0;
11
+ logCallback = null;
12
+ isInitializing = false;
15
13
  setMemory(memory) {
16
14
  this.memory = memory;
17
15
  }
@@ -174,7 +172,7 @@ var MemoryManager = class {
174
172
 
175
173
  // server/runner/HeaderManager.ts
176
174
  var textEncoder2 = new TextEncoder();
177
- var HeaderManager = class {
175
+ var HeaderManager = class _HeaderManager {
178
176
  // Read a header value as a single string. For multi-valued headers (string[]) returns the first.
179
177
  // Use when callers know the header is conventionally single-valued (content-type, host, location, etc.)
180
178
  // and need to satisfy APIs that take a string.
@@ -197,7 +195,7 @@ var HeaderManager = class {
197
195
  return flat;
198
196
  }
199
197
  static normalize(headers) {
200
- const normalized = {};
198
+ const normalized = /* @__PURE__ */ Object.create(null);
201
199
  for (const [key, value] of Object.entries(headers)) {
202
200
  const k = key.toLowerCase();
203
201
  if (Array.isArray(value)) {
@@ -208,6 +206,30 @@ var HeaderManager = class {
208
206
  }
209
207
  return normalized;
210
208
  }
209
+ // Append-merge two header records: for keys present in both, values are
210
+ // concatenated into a string[] rather than the right-hand side overwriting
211
+ // the left. Keys are lowercased on the way in (consistent with `normalize`).
212
+ //
213
+ // Used by the runner to combine request-phase response-header state with
214
+ // the origin's response headers, mirroring how Envoy serves
215
+ // `add_http_response_header` calls issued during onRequestHeaders /
216
+ // onRequestBody against the actual upstream response — both values survive
217
+ // as a multi-value list (preserving the proxy-wasm cross-phase pattern).
218
+ static appendMerge(left, right) {
219
+ const result = _HeaderManager.normalize(left);
220
+ for (const [key, value] of Object.entries(right)) {
221
+ const k = key.toLowerCase();
222
+ const incoming = Array.isArray(value) ? value.map(String) : [String(value)];
223
+ if (Object.hasOwn(result, k)) {
224
+ const existing = result[k];
225
+ const existingArr = Array.isArray(existing) ? existing : [existing];
226
+ result[k] = [...existingArr, ...incoming];
227
+ } else {
228
+ result[k] = incoming.length === 1 ? incoming[0] : incoming;
229
+ }
230
+ }
231
+ return result;
232
+ }
211
233
  static serialize(headers) {
212
234
  const pairs = Object.entries(headers);
213
235
  const numPairs = pairs.length;
@@ -386,20 +408,18 @@ var HeaderManager = class {
386
408
 
387
409
  // server/runner/PropertyResolver.ts
388
410
  var PropertyResolver = class {
389
- constructor() {
390
- this.properties = {};
391
- this.requestHeaders = {};
392
- this.requestMethod = "GET";
393
- this.requestPath = "/";
394
- this.requestScheme = "https";
395
- this.requestUrl = "";
396
- this.requestHost = "";
397
- this.requestQuery = "";
398
- this.requestExtension = "";
399
- this.responseHeaders = {};
400
- this.responseStatus = 200;
401
- this.responseStatusText = "OK";
402
- }
411
+ properties = {};
412
+ requestHeaders = {};
413
+ requestMethod = "GET";
414
+ requestPath = "/";
415
+ requestScheme = "https";
416
+ requestUrl = "";
417
+ requestHost = "";
418
+ requestQuery = "";
419
+ requestExtension = "";
420
+ responseHeaders = {};
421
+ responseStatus = 200;
422
+ responseStatusText = "OK";
403
423
  setProperties(properties) {
404
424
  this.properties = properties;
405
425
  }
@@ -591,6 +611,7 @@ var PropertyResolver = class {
591
611
 
592
612
  // server/fastedge-host/SecretStore.ts
593
613
  var SecretStore = class {
614
+ secrets;
594
615
  constructor(initialSecrets) {
595
616
  this.secrets = /* @__PURE__ */ new Map();
596
617
  if (initialSecrets) {
@@ -673,6 +694,7 @@ var SecretStore = class {
673
694
 
674
695
  // server/fastedge-host/Dictionary.ts
675
696
  var Dictionary = class {
697
+ data;
676
698
  constructor(initialData) {
677
699
  this.data = /* @__PURE__ */ new Map();
678
700
  if (initialData) {
@@ -840,26 +862,33 @@ function createFastEdgeHostFunctions(memory, secretStore, dictionary, logDebug)
840
862
  // server/runner/HostFunctions.ts
841
863
  var textEncoder3 = new TextEncoder();
842
864
  var HostFunctions = class {
865
+ memory;
866
+ propertyResolver;
867
+ propertyAccessControl;
868
+ getCurrentHook;
869
+ logs = [];
870
+ requestHeaders = [];
871
+ responseHeaders = [];
872
+ requestBody = "";
873
+ responseBody = "";
874
+ vmConfig = "";
875
+ pluginConfig = "";
876
+ currentContextId = 1;
877
+ lastHostCall = null;
878
+ debug = false;
879
+ currentLogLevel = 0 /* Trace */;
880
+ // Default to show all logs
881
+ // http_call state
882
+ nextTokenId = 0;
883
+ pendingHttpCall = null;
884
+ httpCallResponse = null;
885
+ streamClosed = false;
886
+ // Local response state (from proxy_send_local_response / send_http_response)
887
+ localResponse = null;
888
+ // FastEdge extensions
889
+ secretStore;
890
+ dictionary;
843
891
  constructor(memory, propertyResolver, propertyAccessControl, getCurrentHook, debug = false, secretStore, dictionary) {
844
- this.logs = [];
845
- this.requestHeaders = [];
846
- this.responseHeaders = [];
847
- this.requestBody = "";
848
- this.responseBody = "";
849
- this.vmConfig = "";
850
- this.pluginConfig = "";
851
- this.currentContextId = 1;
852
- this.lastHostCall = null;
853
- this.debug = false;
854
- this.currentLogLevel = 0 /* Trace */;
855
- // Default to show all logs
856
- // http_call state
857
- this.nextTokenId = 0;
858
- this.pendingHttpCall = null;
859
- this.httpCallResponse = null;
860
- this.streamClosed = false;
861
- // Local response state (from proxy_send_local_response / send_http_response)
862
- this.localResponse = null;
863
892
  this.memory = memory;
864
893
  this.propertyResolver = propertyResolver;
865
894
  this.propertyAccessControl = propertyAccessControl;
@@ -1590,6 +1619,8 @@ var BUILT_IN_PROPERTIES = [
1590
1619
  }
1591
1620
  ];
1592
1621
  var PropertyAccessControl = class {
1622
+ builtInProperties;
1623
+ customProperties;
1593
1624
  constructor() {
1594
1625
  this.builtInProperties = /* @__PURE__ */ new Map();
1595
1626
  this.customProperties = /* @__PURE__ */ new Map();
@@ -1807,21 +1838,28 @@ var textEncoder4 = new TextEncoder();
1807
1838
  var BUILTIN_URL = "http://fastedge-builtin.debug";
1808
1839
  var BUILTIN_SHORTHAND = "built-in";
1809
1840
  var ProxyWasmRunner = class {
1841
+ module = null;
1842
+ // Compiled module (reused)
1843
+ instance = null;
1844
+ // Current instance (transient per hook)
1845
+ memory;
1846
+ propertyResolver;
1847
+ propertyAccessControl;
1848
+ currentHook = null;
1849
+ hostFunctions;
1850
+ logs = [];
1851
+ rootContextId = 1;
1852
+ nextContextId = 2;
1853
+ currentContextId = 1;
1854
+ isInitializing = false;
1855
+ debug = process.env.PROXY_RUNNER_DEBUG === "1";
1856
+ stateManager = null;
1857
+ secretStore;
1858
+ dictionary;
1859
+ dotenvEnabled = true;
1860
+ // Default to enabled
1861
+ dotenvPath = ".";
1810
1862
  constructor(fastEdgeConfig, dotenvEnabled = true) {
1811
- this.module = null;
1812
- // Compiled module (reused)
1813
- this.instance = null;
1814
- this.currentHook = null;
1815
- this.logs = [];
1816
- this.rootContextId = 1;
1817
- this.nextContextId = 2;
1818
- this.currentContextId = 1;
1819
- this.isInitializing = false;
1820
- this.debug = process.env.PROXY_RUNNER_DEBUG === "1";
1821
- this.stateManager = null;
1822
- this.dotenvEnabled = true;
1823
- // Default to enabled
1824
- this.dotenvPath = ".";
1825
1863
  this.memory = new MemoryManager();
1826
1864
  this.propertyResolver = new PropertyResolver();
1827
1865
  this.propertyAccessControl = new PropertyAccessControl();
@@ -2174,6 +2212,14 @@ var ProxyWasmRunner = class {
2174
2212
  `Fetch completed: ${responseStatus} ${responseStatusText}`
2175
2213
  );
2176
2214
  }
2215
+ const requestPhaseResponseHeaders = {
2216
+ ...results.onRequestHeaders.output.response.headers ?? {},
2217
+ ...results.onRequestBody.output.response.headers ?? {}
2218
+ };
2219
+ const mergedResponseHeaders = HeaderManager.appendMerge(
2220
+ requestPhaseResponseHeaders,
2221
+ responseHeaders
2222
+ );
2177
2223
  const responseCall = {
2178
2224
  ...call,
2179
2225
  request: {
@@ -2182,7 +2228,7 @@ var ProxyWasmRunner = class {
2182
2228
  body: modifiedRequestBody
2183
2229
  },
2184
2230
  response: {
2185
- headers: responseHeaders,
2231
+ headers: mergedResponseHeaders,
2186
2232
  body: responseBody,
2187
2233
  status: responseStatus,
2188
2234
  statusText: responseStatusText
@@ -2204,6 +2250,25 @@ var ProxyWasmRunner = class {
2204
2250
  "system"
2205
2251
  );
2206
2252
  }
2253
+ if (results.onResponseHeaders.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
2254
+ const local = this.hostFunctions.getLocalResponse();
2255
+ const headers = results.onResponseHeaders.output.response.headers;
2256
+ this.hostFunctions.resetLocalResponse();
2257
+ const contentType2 = HeaderManager.firstValue(headers["content-type"]) || "text/plain";
2258
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
2259
+ return {
2260
+ hookResults: results,
2261
+ finalResponse: {
2262
+ status: local.statusCode,
2263
+ statusText: local.statusText,
2264
+ headers,
2265
+ body,
2266
+ contentType: contentType2,
2267
+ isBase64: isBase642
2268
+ },
2269
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
2270
+ };
2271
+ }
2207
2272
  const headersAfterResponseHeaders = results.onResponseHeaders.output.response.headers;
2208
2273
  const propertiesAfterResponseHeaders = results.onResponseHeaders.properties;
2209
2274
  this.logDebug(
@@ -2228,6 +2293,25 @@ var ProxyWasmRunner = class {
2228
2293
  "system"
2229
2294
  );
2230
2295
  }
2296
+ if (results.onResponseBody.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
2297
+ const local = this.hostFunctions.getLocalResponse();
2298
+ const headers = results.onResponseBody.output.response.headers;
2299
+ this.hostFunctions.resetLocalResponse();
2300
+ const contentType2 = HeaderManager.firstValue(headers["content-type"]) || "text/plain";
2301
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
2302
+ return {
2303
+ hookResults: results,
2304
+ finalResponse: {
2305
+ status: local.statusCode,
2306
+ statusText: local.statusText,
2307
+ headers,
2308
+ body,
2309
+ contentType: contentType2,
2310
+ isBase64: isBase642
2311
+ },
2312
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
2313
+ };
2314
+ }
2231
2315
  const finalHeaders = results.onResponseBody.output.response.headers;
2232
2316
  const finalBody = results.onResponseBody.output.response.body;
2233
2317
  this.logDebug(`Final response body length: ${finalBody.length}`);
@@ -2704,7 +2788,7 @@ var ProxyWasmRunner = class {
2704
2788
  /**
2705
2789
  * Not supported for Proxy-WASM (HTTP WASM only)
2706
2790
  */
2707
- async execute(request) {
2791
+ async execute(_request) {
2708
2792
  throw new Error(
2709
2793
  "execute() is not supported for Proxy-WASM. Use callHook() or callFullFlow() instead."
2710
2794
  );
@@ -2868,21 +2952,22 @@ async function isLegacySyncWasm(bufferOrPath) {
2868
2952
 
2869
2953
  // server/runner/HttpWasmRunner.ts
2870
2954
  var HttpWasmRunner = class {
2955
+ process = null;
2956
+ port = null;
2957
+ cliPath = null;
2958
+ tempWasmPath = null;
2959
+ currentWasmPath = null;
2960
+ // resolved path used when spawning
2961
+ logs = [];
2962
+ stateManager = null;
2963
+ portManager;
2964
+ dotenvEnabled = true;
2965
+ dotenvPath = null;
2966
+ /** Pinned ports bypass PortManager allocation and must not be released back to it. */
2967
+ isPinnedPort = false;
2968
+ /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
2969
+ isLegacySync = false;
2871
2970
  constructor(portManager, dotenvEnabled = true) {
2872
- this.process = null;
2873
- this.port = null;
2874
- this.cliPath = null;
2875
- this.tempWasmPath = null;
2876
- this.currentWasmPath = null;
2877
- // resolved path used when spawning
2878
- this.logs = [];
2879
- this.stateManager = null;
2880
- this.dotenvEnabled = true;
2881
- this.dotenvPath = null;
2882
- /** Pinned ports bypass PortManager allocation and must not be released back to it. */
2883
- this.isPinnedPort = false;
2884
- /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
2885
- this.isLegacySync = false;
2886
2971
  this.portManager = portManager;
2887
2972
  this.dotenvEnabled = dotenvEnabled;
2888
2973
  }
@@ -3282,12 +3367,10 @@ function parseFetchHeaders(headers) {
3282
3367
  // server/runner/PortManager.ts
3283
3368
  import { createServer } from "net";
3284
3369
  var PortManager = class {
3285
- constructor() {
3286
- this.minPort = 8100;
3287
- this.maxPort = 8199;
3288
- this.allocatedPorts = /* @__PURE__ */ new Set();
3289
- this.lastAllocatedPort = this.minPort - 1;
3290
- }
3370
+ minPort = 8100;
3371
+ maxPort = 8199;
3372
+ allocatedPorts = /* @__PURE__ */ new Set();
3373
+ lastAllocatedPort = this.minPort - 1;
3291
3374
  /**
3292
3375
  * Check whether a port is actually free at the OS level.
3293
3376
  * This is necessary when multiple server processes run simultaneously —
@@ -3347,6 +3430,7 @@ var PortManager = class {
3347
3430
 
3348
3431
  // server/runner/WasmRunnerFactory.ts
3349
3432
  var WasmRunnerFactory = class {
3433
+ portManager;
3350
3434
  constructor() {
3351
3435
  this.portManager = new PortManager();
3352
3436
  }
@@ -3,6 +3,7 @@ export declare class HeaderManager {
3
3
  static firstValue(v: string | string[] | undefined): string | undefined;
4
4
  static flattenToMap(headers: HeaderMap | HeaderRecord): HeaderMap;
5
5
  static normalize(headers: HeaderMap | HeaderRecord): HeaderRecord;
6
+ static appendMerge(left: HeaderMap | HeaderRecord, right: HeaderMap | HeaderRecord): HeaderRecord;
6
7
  static serialize(headers: HeaderMap): Uint8Array;
7
8
  /**
8
9
  * Deserializes a proxy-wasm binary header map format:
@@ -68,7 +68,7 @@ export declare class ProxyWasmRunner implements IWasmRunner {
68
68
  /**
69
69
  * Not supported for Proxy-WASM (HTTP WASM only)
70
70
  */
71
- execute(request: HttpRequest): Promise<HttpResponse>;
71
+ execute(_request: HttpRequest): Promise<HttpResponse>;
72
72
  /**
73
73
  * Clean up resources (no-op for Proxy-WASM, but required by interface)
74
74
  */