@gcoredev/fastedge-test 0.2.0 → 0.2.2

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.
@@ -1 +1 @@
1
- {"fastedge_run_version": "v0.15.0"}
1
+ {"fastedge_run_version": "v0.16.7"}
Binary file
@@ -49,13 +49,11 @@ var import_node_wasi = require("node:wasi");
49
49
  var textEncoder = new TextEncoder();
50
50
  var textDecoder = new TextDecoder();
51
51
  var MemoryManager = class {
52
- constructor() {
53
- this.memory = null;
54
- this.instance = null;
55
- this.hostAllocOffset = 0;
56
- this.logCallback = null;
57
- this.isInitializing = false;
58
- }
52
+ memory = null;
53
+ instance = null;
54
+ hostAllocOffset = 0;
55
+ logCallback = null;
56
+ isInitializing = false;
59
57
  setMemory(memory) {
60
58
  this.memory = memory;
61
59
  }
@@ -218,7 +216,7 @@ var MemoryManager = class {
218
216
 
219
217
  // server/runner/HeaderManager.ts
220
218
  var textEncoder2 = new TextEncoder();
221
- var HeaderManager = class {
219
+ var HeaderManager = class _HeaderManager {
222
220
  // Read a header value as a single string. For multi-valued headers (string[]) returns the first.
223
221
  // Use when callers know the header is conventionally single-valued (content-type, host, location, etc.)
224
222
  // and need to satisfy APIs that take a string.
@@ -241,7 +239,7 @@ var HeaderManager = class {
241
239
  return flat;
242
240
  }
243
241
  static normalize(headers) {
244
- const normalized = {};
242
+ const normalized = /* @__PURE__ */ Object.create(null);
245
243
  for (const [key, value] of Object.entries(headers)) {
246
244
  const k = key.toLowerCase();
247
245
  if (Array.isArray(value)) {
@@ -252,6 +250,30 @@ var HeaderManager = class {
252
250
  }
253
251
  return normalized;
254
252
  }
253
+ // Append-merge two header records: for keys present in both, values are
254
+ // concatenated into a string[] rather than the right-hand side overwriting
255
+ // the left. Keys are lowercased on the way in (consistent with `normalize`).
256
+ //
257
+ // Used by the runner to combine request-phase response-header state with
258
+ // the origin's response headers, mirroring how Envoy serves
259
+ // `add_http_response_header` calls issued during onRequestHeaders /
260
+ // onRequestBody against the actual upstream response — both values survive
261
+ // as a multi-value list (preserving the proxy-wasm cross-phase pattern).
262
+ static appendMerge(left, right) {
263
+ const result = _HeaderManager.normalize(left);
264
+ for (const [key, value] of Object.entries(right)) {
265
+ const k = key.toLowerCase();
266
+ const incoming = Array.isArray(value) ? value.map(String) : [String(value)];
267
+ if (Object.hasOwn(result, k)) {
268
+ const existing = result[k];
269
+ const existingArr = Array.isArray(existing) ? existing : [existing];
270
+ result[k] = [...existingArr, ...incoming];
271
+ } else {
272
+ result[k] = incoming.length === 1 ? incoming[0] : incoming;
273
+ }
274
+ }
275
+ return result;
276
+ }
255
277
  static serialize(headers) {
256
278
  const pairs = Object.entries(headers);
257
279
  const numPairs = pairs.length;
@@ -430,20 +452,18 @@ var HeaderManager = class {
430
452
 
431
453
  // server/runner/PropertyResolver.ts
432
454
  var PropertyResolver = class {
433
- constructor() {
434
- this.properties = {};
435
- this.requestHeaders = {};
436
- this.requestMethod = "GET";
437
- this.requestPath = "/";
438
- this.requestScheme = "https";
439
- this.requestUrl = "";
440
- this.requestHost = "";
441
- this.requestQuery = "";
442
- this.requestExtension = "";
443
- this.responseHeaders = {};
444
- this.responseStatus = 200;
445
- this.responseStatusText = "OK";
446
- }
455
+ properties = {};
456
+ requestHeaders = {};
457
+ requestMethod = "GET";
458
+ requestPath = "/";
459
+ requestScheme = "https";
460
+ requestUrl = "";
461
+ requestHost = "";
462
+ requestQuery = "";
463
+ requestExtension = "";
464
+ responseHeaders = {};
465
+ responseStatus = 200;
466
+ responseStatusText = "OK";
447
467
  setProperties(properties) {
448
468
  this.properties = properties;
449
469
  }
@@ -635,6 +655,7 @@ var PropertyResolver = class {
635
655
 
636
656
  // server/fastedge-host/SecretStore.ts
637
657
  var SecretStore = class {
658
+ secrets;
638
659
  constructor(initialSecrets) {
639
660
  this.secrets = /* @__PURE__ */ new Map();
640
661
  if (initialSecrets) {
@@ -717,6 +738,7 @@ var SecretStore = class {
717
738
 
718
739
  // server/fastedge-host/Dictionary.ts
719
740
  var Dictionary = class {
741
+ data;
720
742
  constructor(initialData) {
721
743
  this.data = /* @__PURE__ */ new Map();
722
744
  if (initialData) {
@@ -884,26 +906,39 @@ function createFastEdgeHostFunctions(memory, secretStore, dictionary, logDebug)
884
906
  // server/runner/HostFunctions.ts
885
907
  var textEncoder3 = new TextEncoder();
886
908
  var HostFunctions = class {
909
+ memory;
910
+ propertyResolver;
911
+ propertyAccessControl;
912
+ getCurrentHook;
913
+ logs = [];
914
+ requestHeaders = [];
915
+ responseHeaders = [];
916
+ requestBody = "";
917
+ responseBody = "";
918
+ vmConfig = "";
919
+ pluginConfig = "";
920
+ currentContextId = 1;
921
+ lastHostCall = null;
922
+ debug = false;
923
+ currentLogLevel = 0 /* Trace */;
924
+ // Default to show all logs
925
+ // http_call state
926
+ nextTokenId = 0;
927
+ pendingHttpCall = null;
928
+ httpCallResponse = null;
929
+ streamClosed = false;
930
+ // Local response state (from proxy_send_local_response / send_http_response).
931
+ // `headers` carries the additional headers passed as the 4th argument of
932
+ // `send_http_response`; ProxyWasmRunner merges these into finalResponse at
933
+ // the short-circuit points so they reach the caller alongside any headers
934
+ // accumulated via stream_context.headers.response.add() in the same hook.
935
+ // Stored as tuples to preserve order and duplicate-name semantics (e.g. for
936
+ // multi-value Set-Cookie additions).
937
+ localResponse = null;
938
+ // FastEdge extensions
939
+ secretStore;
940
+ dictionary;
887
941
  constructor(memory, propertyResolver, propertyAccessControl, getCurrentHook, debug = false, secretStore, dictionary) {
888
- this.logs = [];
889
- this.requestHeaders = [];
890
- this.responseHeaders = [];
891
- this.requestBody = "";
892
- this.responseBody = "";
893
- this.vmConfig = "";
894
- this.pluginConfig = "";
895
- this.currentContextId = 1;
896
- this.lastHostCall = null;
897
- this.debug = false;
898
- this.currentLogLevel = 0 /* Trace */;
899
- // Default to show all logs
900
- // http_call state
901
- this.nextTokenId = 0;
902
- this.pendingHttpCall = null;
903
- this.httpCallResponse = null;
904
- this.streamClosed = false;
905
- // Local response state (from proxy_send_local_response / send_http_response)
906
- this.localResponse = null;
907
942
  this.memory = memory;
908
943
  this.propertyResolver = propertyResolver;
909
944
  this.propertyAccessControl = propertyAccessControl;
@@ -1248,12 +1283,13 @@ var HostFunctions = class {
1248
1283
  );
1249
1284
  const statusText = this.memory.readString(statusCodePtr, statusCodeLen);
1250
1285
  const body = this.memory.readBytes(bodyPtr, bodyLen);
1286
+ let headers = [];
1251
1287
  if (headerPairsLen > 0) {
1252
1288
  const headerBytes = this.memory.readBytes(headerPairsPtr, headerPairsLen);
1253
- const headers = HeaderManager.deserializeBinary(headerBytes);
1254
- this.logDebug(`send_local_response headers (not merged): ${JSON.stringify(headers)}`);
1289
+ headers = HeaderManager.deserializeBinaryToTuples(headerBytes);
1290
+ this.logDebug(`send_local_response headers: ${JSON.stringify(headers)}`);
1255
1291
  }
1256
- this.localResponse = { statusCode, statusText, body };
1292
+ this.localResponse = { statusCode, statusText, body, headers };
1257
1293
  this.logs.push({
1258
1294
  level: 1,
1259
1295
  message: `local_response status=${statusCode} ${statusText} bodyLen=${body.byteLength} grpc=${grpcStatus}`
@@ -1634,6 +1670,8 @@ var BUILT_IN_PROPERTIES = [
1634
1670
  }
1635
1671
  ];
1636
1672
  var PropertyAccessControl = class {
1673
+ builtInProperties;
1674
+ customProperties;
1637
1675
  constructor() {
1638
1676
  this.builtInProperties = /* @__PURE__ */ new Map();
1639
1677
  this.customProperties = /* @__PURE__ */ new Map();
@@ -1851,21 +1889,28 @@ var textEncoder4 = new TextEncoder();
1851
1889
  var BUILTIN_URL = "http://fastedge-builtin.debug";
1852
1890
  var BUILTIN_SHORTHAND = "built-in";
1853
1891
  var ProxyWasmRunner = class {
1892
+ module = null;
1893
+ // Compiled module (reused)
1894
+ instance = null;
1895
+ // Current instance (transient per hook)
1896
+ memory;
1897
+ propertyResolver;
1898
+ propertyAccessControl;
1899
+ currentHook = null;
1900
+ hostFunctions;
1901
+ logs = [];
1902
+ rootContextId = 1;
1903
+ nextContextId = 2;
1904
+ currentContextId = 1;
1905
+ isInitializing = false;
1906
+ debug = process.env.PROXY_RUNNER_DEBUG === "1";
1907
+ stateManager = null;
1908
+ secretStore;
1909
+ dictionary;
1910
+ dotenvEnabled = true;
1911
+ // Default to enabled
1912
+ dotenvPath = ".";
1854
1913
  constructor(fastEdgeConfig, dotenvEnabled = true) {
1855
- this.module = null;
1856
- // Compiled module (reused)
1857
- this.instance = null;
1858
- this.currentHook = null;
1859
- this.logs = [];
1860
- this.rootContextId = 1;
1861
- this.nextContextId = 2;
1862
- this.currentContextId = 1;
1863
- this.isInitializing = false;
1864
- this.debug = process.env.PROXY_RUNNER_DEBUG === "1";
1865
- this.stateManager = null;
1866
- this.dotenvEnabled = true;
1867
- // Default to enabled
1868
- this.dotenvPath = ".";
1869
1914
  this.memory = new MemoryManager();
1870
1915
  this.propertyResolver = new PropertyResolver();
1871
1916
  this.propertyAccessControl = new PropertyAccessControl();
@@ -2038,14 +2083,15 @@ var ProxyWasmRunner = class {
2038
2083
  const local = this.hostFunctions.getLocalResponse();
2039
2084
  const responseHeaders2 = results.onRequestHeaders.output.response.headers;
2040
2085
  this.hostFunctions.resetLocalResponse();
2041
- const contentType2 = HeaderManager.firstValue(responseHeaders2["content-type"]) || "text/plain";
2086
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
2087
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
2042
2088
  const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
2043
2089
  return {
2044
2090
  hookResults: results,
2045
2091
  finalResponse: {
2046
2092
  status: local.statusCode,
2047
2093
  statusText: local.statusText,
2048
- headers: responseHeaders2,
2094
+ headers: mergedHeaders,
2049
2095
  body,
2050
2096
  contentType: contentType2,
2051
2097
  isBase64: isBase642
@@ -2081,14 +2127,15 @@ var ProxyWasmRunner = class {
2081
2127
  const local = this.hostFunctions.getLocalResponse();
2082
2128
  const responseHeaders2 = results.onRequestBody.output.response.headers;
2083
2129
  this.hostFunctions.resetLocalResponse();
2084
- const contentType2 = HeaderManager.firstValue(responseHeaders2["content-type"]) || "text/plain";
2130
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
2131
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
2085
2132
  const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
2086
2133
  return {
2087
2134
  hookResults: results,
2088
2135
  finalResponse: {
2089
2136
  status: local.statusCode,
2090
2137
  statusText: local.statusText,
2091
- headers: responseHeaders2,
2138
+ headers: mergedHeaders,
2092
2139
  body,
2093
2140
  contentType: contentType2,
2094
2141
  isBase64: isBase642
@@ -2218,6 +2265,14 @@ var ProxyWasmRunner = class {
2218
2265
  `Fetch completed: ${responseStatus} ${responseStatusText}`
2219
2266
  );
2220
2267
  }
2268
+ const requestPhaseResponseHeaders = {
2269
+ ...results.onRequestHeaders.output.response.headers ?? {},
2270
+ ...results.onRequestBody.output.response.headers ?? {}
2271
+ };
2272
+ const mergedResponseHeaders = HeaderManager.appendMerge(
2273
+ requestPhaseResponseHeaders,
2274
+ responseHeaders
2275
+ );
2221
2276
  const responseCall = {
2222
2277
  ...call,
2223
2278
  request: {
@@ -2226,7 +2281,7 @@ var ProxyWasmRunner = class {
2226
2281
  body: modifiedRequestBody
2227
2282
  },
2228
2283
  response: {
2229
- headers: responseHeaders,
2284
+ headers: mergedResponseHeaders,
2230
2285
  body: responseBody,
2231
2286
  status: responseStatus,
2232
2287
  statusText: responseStatusText
@@ -2248,6 +2303,26 @@ var ProxyWasmRunner = class {
2248
2303
  "system"
2249
2304
  );
2250
2305
  }
2306
+ if (results.onResponseHeaders.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
2307
+ const local = this.hostFunctions.getLocalResponse();
2308
+ const responseHeaders2 = results.onResponseHeaders.output.response.headers;
2309
+ this.hostFunctions.resetLocalResponse();
2310
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
2311
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
2312
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
2313
+ return {
2314
+ hookResults: results,
2315
+ finalResponse: {
2316
+ status: local.statusCode,
2317
+ statusText: local.statusText,
2318
+ headers: mergedHeaders,
2319
+ body,
2320
+ contentType: contentType2,
2321
+ isBase64: isBase642
2322
+ },
2323
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
2324
+ };
2325
+ }
2251
2326
  const headersAfterResponseHeaders = results.onResponseHeaders.output.response.headers;
2252
2327
  const propertiesAfterResponseHeaders = results.onResponseHeaders.properties;
2253
2328
  this.logDebug(
@@ -2272,6 +2347,26 @@ var ProxyWasmRunner = class {
2272
2347
  "system"
2273
2348
  );
2274
2349
  }
2350
+ if (results.onResponseBody.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
2351
+ const local = this.hostFunctions.getLocalResponse();
2352
+ const responseHeaders2 = results.onResponseBody.output.response.headers;
2353
+ this.hostFunctions.resetLocalResponse();
2354
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
2355
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
2356
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
2357
+ return {
2358
+ hookResults: results,
2359
+ finalResponse: {
2360
+ status: local.statusCode,
2361
+ statusText: local.statusText,
2362
+ headers: mergedHeaders,
2363
+ body,
2364
+ contentType: contentType2,
2365
+ isBase64: isBase642
2366
+ },
2367
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
2368
+ };
2369
+ }
2275
2370
  const finalHeaders = results.onResponseBody.output.response.headers;
2276
2371
  const finalBody = results.onResponseBody.output.response.body;
2277
2372
  this.logDebug(`Final response body length: ${finalBody.length}`);
@@ -2639,7 +2734,9 @@ var ProxyWasmRunner = class {
2639
2734
  }
2640
2735
  }
2641
2736
  createImports() {
2642
- const wasi = new import_node_wasi.WASI({ version: "preview1", env: this.dictionary.getAll() });
2737
+ const dictEnv = this.dictionary.getAll();
2738
+ const wasiEnv = Object.keys(dictEnv).length === 0 ? { __FASTEDGE_RUNNER__: "1" } : dictEnv;
2739
+ const wasi = new import_node_wasi.WASI({ version: "preview1", env: wasiEnv });
2643
2740
  const wasiImport = wasi.wasiImport;
2644
2741
  return {
2645
2742
  env: this.hostFunctions.createImports(),
@@ -2748,7 +2845,7 @@ var ProxyWasmRunner = class {
2748
2845
  /**
2749
2846
  * Not supported for Proxy-WASM (HTTP WASM only)
2750
2847
  */
2751
- async execute(request) {
2848
+ async execute(_request) {
2752
2849
  throw new Error(
2753
2850
  "execute() is not supported for Proxy-WASM. Use callHook() or callFullFlow() instead."
2754
2851
  );
@@ -2912,21 +3009,22 @@ async function isLegacySyncWasm(bufferOrPath) {
2912
3009
 
2913
3010
  // server/runner/HttpWasmRunner.ts
2914
3011
  var HttpWasmRunner = class {
3012
+ process = null;
3013
+ port = null;
3014
+ cliPath = null;
3015
+ tempWasmPath = null;
3016
+ currentWasmPath = null;
3017
+ // resolved path used when spawning
3018
+ logs = [];
3019
+ stateManager = null;
3020
+ portManager;
3021
+ dotenvEnabled = true;
3022
+ dotenvPath = null;
3023
+ /** Pinned ports bypass PortManager allocation and must not be released back to it. */
3024
+ isPinnedPort = false;
3025
+ /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
3026
+ isLegacySync = false;
2915
3027
  constructor(portManager, dotenvEnabled = true) {
2916
- this.process = null;
2917
- this.port = null;
2918
- this.cliPath = null;
2919
- this.tempWasmPath = null;
2920
- this.currentWasmPath = null;
2921
- // resolved path used when spawning
2922
- this.logs = [];
2923
- this.stateManager = null;
2924
- this.dotenvEnabled = true;
2925
- this.dotenvPath = null;
2926
- /** Pinned ports bypass PortManager allocation and must not be released back to it. */
2927
- this.isPinnedPort = false;
2928
- /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
2929
- this.isLegacySync = false;
2930
3028
  this.portManager = portManager;
2931
3029
  this.dotenvEnabled = dotenvEnabled;
2932
3030
  }
@@ -3326,12 +3424,10 @@ function parseFetchHeaders(headers) {
3326
3424
  // server/runner/PortManager.ts
3327
3425
  var import_net = require("net");
3328
3426
  var PortManager = class {
3329
- constructor() {
3330
- this.minPort = 8100;
3331
- this.maxPort = 8199;
3332
- this.allocatedPorts = /* @__PURE__ */ new Set();
3333
- this.lastAllocatedPort = this.minPort - 1;
3334
- }
3427
+ minPort = 8100;
3428
+ maxPort = 8199;
3429
+ allocatedPorts = /* @__PURE__ */ new Set();
3430
+ lastAllocatedPort = this.minPort - 1;
3335
3431
  /**
3336
3432
  * Check whether a port is actually free at the OS level.
3337
3433
  * This is necessary when multiple server processes run simultaneously —
@@ -3391,6 +3487,7 @@ var PortManager = class {
3391
3487
 
3392
3488
  // server/runner/WasmRunnerFactory.ts
3393
3489
  var WasmRunnerFactory = class {
3490
+ portManager;
3394
3491
  constructor() {
3395
3492
  this.portManager = new PortManager();
3396
3493
  }