@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.
@@ -18410,13 +18410,11 @@ import { WASI } from "node:wasi";
18410
18410
  var textEncoder = new TextEncoder();
18411
18411
  var textDecoder = new TextDecoder();
18412
18412
  var MemoryManager = class {
18413
- constructor() {
18414
- this.memory = null;
18415
- this.instance = null;
18416
- this.hostAllocOffset = 0;
18417
- this.logCallback = null;
18418
- this.isInitializing = false;
18419
- }
18413
+ memory = null;
18414
+ instance = null;
18415
+ hostAllocOffset = 0;
18416
+ logCallback = null;
18417
+ isInitializing = false;
18420
18418
  setMemory(memory) {
18421
18419
  this.memory = memory;
18422
18420
  }
@@ -18579,7 +18577,7 @@ var MemoryManager = class {
18579
18577
 
18580
18578
  // server/runner/HeaderManager.ts
18581
18579
  var textEncoder2 = new TextEncoder();
18582
- var HeaderManager = class {
18580
+ var HeaderManager = class _HeaderManager {
18583
18581
  // Read a header value as a single string. For multi-valued headers (string[]) returns the first.
18584
18582
  // Use when callers know the header is conventionally single-valued (content-type, host, location, etc.)
18585
18583
  // and need to satisfy APIs that take a string.
@@ -18602,7 +18600,7 @@ var HeaderManager = class {
18602
18600
  return flat;
18603
18601
  }
18604
18602
  static normalize(headers) {
18605
- const normalized = {};
18603
+ const normalized = /* @__PURE__ */ Object.create(null);
18606
18604
  for (const [key, value] of Object.entries(headers)) {
18607
18605
  const k = key.toLowerCase();
18608
18606
  if (Array.isArray(value)) {
@@ -18613,6 +18611,30 @@ var HeaderManager = class {
18613
18611
  }
18614
18612
  return normalized;
18615
18613
  }
18614
+ // Append-merge two header records: for keys present in both, values are
18615
+ // concatenated into a string[] rather than the right-hand side overwriting
18616
+ // the left. Keys are lowercased on the way in (consistent with `normalize`).
18617
+ //
18618
+ // Used by the runner to combine request-phase response-header state with
18619
+ // the origin's response headers, mirroring how Envoy serves
18620
+ // `add_http_response_header` calls issued during onRequestHeaders /
18621
+ // onRequestBody against the actual upstream response — both values survive
18622
+ // as a multi-value list (preserving the proxy-wasm cross-phase pattern).
18623
+ static appendMerge(left, right) {
18624
+ const result = _HeaderManager.normalize(left);
18625
+ for (const [key, value] of Object.entries(right)) {
18626
+ const k = key.toLowerCase();
18627
+ const incoming = Array.isArray(value) ? value.map(String) : [String(value)];
18628
+ if (Object.hasOwn(result, k)) {
18629
+ const existing = result[k];
18630
+ const existingArr = Array.isArray(existing) ? existing : [existing];
18631
+ result[k] = [...existingArr, ...incoming];
18632
+ } else {
18633
+ result[k] = incoming.length === 1 ? incoming[0] : incoming;
18634
+ }
18635
+ }
18636
+ return result;
18637
+ }
18616
18638
  static serialize(headers) {
18617
18639
  const pairs = Object.entries(headers);
18618
18640
  const numPairs = pairs.length;
@@ -18791,20 +18813,18 @@ var HeaderManager = class {
18791
18813
 
18792
18814
  // server/runner/PropertyResolver.ts
18793
18815
  var PropertyResolver = class {
18794
- constructor() {
18795
- this.properties = {};
18796
- this.requestHeaders = {};
18797
- this.requestMethod = "GET";
18798
- this.requestPath = "/";
18799
- this.requestScheme = "https";
18800
- this.requestUrl = "";
18801
- this.requestHost = "";
18802
- this.requestQuery = "";
18803
- this.requestExtension = "";
18804
- this.responseHeaders = {};
18805
- this.responseStatus = 200;
18806
- this.responseStatusText = "OK";
18807
- }
18816
+ properties = {};
18817
+ requestHeaders = {};
18818
+ requestMethod = "GET";
18819
+ requestPath = "/";
18820
+ requestScheme = "https";
18821
+ requestUrl = "";
18822
+ requestHost = "";
18823
+ requestQuery = "";
18824
+ requestExtension = "";
18825
+ responseHeaders = {};
18826
+ responseStatus = 200;
18827
+ responseStatusText = "OK";
18808
18828
  setProperties(properties) {
18809
18829
  this.properties = properties;
18810
18830
  }
@@ -18996,6 +19016,7 @@ var PropertyResolver = class {
18996
19016
 
18997
19017
  // server/fastedge-host/SecretStore.ts
18998
19018
  var SecretStore = class {
19019
+ secrets;
18999
19020
  constructor(initialSecrets) {
19000
19021
  this.secrets = /* @__PURE__ */ new Map();
19001
19022
  if (initialSecrets) {
@@ -19078,6 +19099,7 @@ var SecretStore = class {
19078
19099
 
19079
19100
  // server/fastedge-host/Dictionary.ts
19080
19101
  var Dictionary = class {
19102
+ data;
19081
19103
  constructor(initialData) {
19082
19104
  this.data = /* @__PURE__ */ new Map();
19083
19105
  if (initialData) {
@@ -19245,26 +19267,39 @@ function createFastEdgeHostFunctions(memory, secretStore, dictionary, logDebug)
19245
19267
  // server/runner/HostFunctions.ts
19246
19268
  var textEncoder3 = new TextEncoder();
19247
19269
  var HostFunctions = class {
19270
+ memory;
19271
+ propertyResolver;
19272
+ propertyAccessControl;
19273
+ getCurrentHook;
19274
+ logs = [];
19275
+ requestHeaders = [];
19276
+ responseHeaders = [];
19277
+ requestBody = "";
19278
+ responseBody = "";
19279
+ vmConfig = "";
19280
+ pluginConfig = "";
19281
+ currentContextId = 1;
19282
+ lastHostCall = null;
19283
+ debug = false;
19284
+ currentLogLevel = 0 /* Trace */;
19285
+ // Default to show all logs
19286
+ // http_call state
19287
+ nextTokenId = 0;
19288
+ pendingHttpCall = null;
19289
+ httpCallResponse = null;
19290
+ streamClosed = false;
19291
+ // Local response state (from proxy_send_local_response / send_http_response).
19292
+ // `headers` carries the additional headers passed as the 4th argument of
19293
+ // `send_http_response`; ProxyWasmRunner merges these into finalResponse at
19294
+ // the short-circuit points so they reach the caller alongside any headers
19295
+ // accumulated via stream_context.headers.response.add() in the same hook.
19296
+ // Stored as tuples to preserve order and duplicate-name semantics (e.g. for
19297
+ // multi-value Set-Cookie additions).
19298
+ localResponse = null;
19299
+ // FastEdge extensions
19300
+ secretStore;
19301
+ dictionary;
19248
19302
  constructor(memory, propertyResolver, propertyAccessControl, getCurrentHook, debug = false, secretStore, dictionary) {
19249
- this.logs = [];
19250
- this.requestHeaders = [];
19251
- this.responseHeaders = [];
19252
- this.requestBody = "";
19253
- this.responseBody = "";
19254
- this.vmConfig = "";
19255
- this.pluginConfig = "";
19256
- this.currentContextId = 1;
19257
- this.lastHostCall = null;
19258
- this.debug = false;
19259
- this.currentLogLevel = 0 /* Trace */;
19260
- // Default to show all logs
19261
- // http_call state
19262
- this.nextTokenId = 0;
19263
- this.pendingHttpCall = null;
19264
- this.httpCallResponse = null;
19265
- this.streamClosed = false;
19266
- // Local response state (from proxy_send_local_response / send_http_response)
19267
- this.localResponse = null;
19268
19303
  this.memory = memory;
19269
19304
  this.propertyResolver = propertyResolver;
19270
19305
  this.propertyAccessControl = propertyAccessControl;
@@ -19609,12 +19644,13 @@ var HostFunctions = class {
19609
19644
  );
19610
19645
  const statusText = this.memory.readString(statusCodePtr, statusCodeLen);
19611
19646
  const body = this.memory.readBytes(bodyPtr, bodyLen);
19647
+ let headers = [];
19612
19648
  if (headerPairsLen > 0) {
19613
19649
  const headerBytes = this.memory.readBytes(headerPairsPtr, headerPairsLen);
19614
- const headers = HeaderManager.deserializeBinary(headerBytes);
19615
- this.logDebug(`send_local_response headers (not merged): ${JSON.stringify(headers)}`);
19650
+ headers = HeaderManager.deserializeBinaryToTuples(headerBytes);
19651
+ this.logDebug(`send_local_response headers: ${JSON.stringify(headers)}`);
19616
19652
  }
19617
- this.localResponse = { statusCode, statusText, body };
19653
+ this.localResponse = { statusCode, statusText, body, headers };
19618
19654
  this.logs.push({
19619
19655
  level: 1,
19620
19656
  message: `local_response status=${statusCode} ${statusText} bodyLen=${body.byteLength} grpc=${grpcStatus}`
@@ -19995,6 +20031,8 @@ var BUILT_IN_PROPERTIES = [
19995
20031
  }
19996
20032
  ];
19997
20033
  var PropertyAccessControl = class {
20034
+ builtInProperties;
20035
+ customProperties;
19998
20036
  constructor() {
19999
20037
  this.builtInProperties = /* @__PURE__ */ new Map();
20000
20038
  this.customProperties = /* @__PURE__ */ new Map();
@@ -20212,21 +20250,28 @@ var textEncoder4 = new TextEncoder();
20212
20250
  var BUILTIN_URL = "http://fastedge-builtin.debug";
20213
20251
  var BUILTIN_SHORTHAND = "built-in";
20214
20252
  var ProxyWasmRunner = class {
20253
+ module = null;
20254
+ // Compiled module (reused)
20255
+ instance = null;
20256
+ // Current instance (transient per hook)
20257
+ memory;
20258
+ propertyResolver;
20259
+ propertyAccessControl;
20260
+ currentHook = null;
20261
+ hostFunctions;
20262
+ logs = [];
20263
+ rootContextId = 1;
20264
+ nextContextId = 2;
20265
+ currentContextId = 1;
20266
+ isInitializing = false;
20267
+ debug = process.env.PROXY_RUNNER_DEBUG === "1";
20268
+ stateManager = null;
20269
+ secretStore;
20270
+ dictionary;
20271
+ dotenvEnabled = true;
20272
+ // Default to enabled
20273
+ dotenvPath = ".";
20215
20274
  constructor(fastEdgeConfig, dotenvEnabled = true) {
20216
- this.module = null;
20217
- // Compiled module (reused)
20218
- this.instance = null;
20219
- this.currentHook = null;
20220
- this.logs = [];
20221
- this.rootContextId = 1;
20222
- this.nextContextId = 2;
20223
- this.currentContextId = 1;
20224
- this.isInitializing = false;
20225
- this.debug = process.env.PROXY_RUNNER_DEBUG === "1";
20226
- this.stateManager = null;
20227
- this.dotenvEnabled = true;
20228
- // Default to enabled
20229
- this.dotenvPath = ".";
20230
20275
  this.memory = new MemoryManager();
20231
20276
  this.propertyResolver = new PropertyResolver();
20232
20277
  this.propertyAccessControl = new PropertyAccessControl();
@@ -20399,14 +20444,15 @@ var ProxyWasmRunner = class {
20399
20444
  const local = this.hostFunctions.getLocalResponse();
20400
20445
  const responseHeaders2 = results.onRequestHeaders.output.response.headers;
20401
20446
  this.hostFunctions.resetLocalResponse();
20402
- const contentType2 = HeaderManager.firstValue(responseHeaders2["content-type"]) || "text/plain";
20447
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20448
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20403
20449
  const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20404
20450
  return {
20405
20451
  hookResults: results,
20406
20452
  finalResponse: {
20407
20453
  status: local.statusCode,
20408
20454
  statusText: local.statusText,
20409
- headers: responseHeaders2,
20455
+ headers: mergedHeaders,
20410
20456
  body,
20411
20457
  contentType: contentType2,
20412
20458
  isBase64: isBase642
@@ -20442,14 +20488,15 @@ var ProxyWasmRunner = class {
20442
20488
  const local = this.hostFunctions.getLocalResponse();
20443
20489
  const responseHeaders2 = results.onRequestBody.output.response.headers;
20444
20490
  this.hostFunctions.resetLocalResponse();
20445
- const contentType2 = HeaderManager.firstValue(responseHeaders2["content-type"]) || "text/plain";
20491
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20492
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20446
20493
  const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20447
20494
  return {
20448
20495
  hookResults: results,
20449
20496
  finalResponse: {
20450
20497
  status: local.statusCode,
20451
20498
  statusText: local.statusText,
20452
- headers: responseHeaders2,
20499
+ headers: mergedHeaders,
20453
20500
  body,
20454
20501
  contentType: contentType2,
20455
20502
  isBase64: isBase642
@@ -20579,6 +20626,14 @@ var ProxyWasmRunner = class {
20579
20626
  `Fetch completed: ${responseStatus} ${responseStatusText}`
20580
20627
  );
20581
20628
  }
20629
+ const requestPhaseResponseHeaders = {
20630
+ ...results.onRequestHeaders.output.response.headers ?? {},
20631
+ ...results.onRequestBody.output.response.headers ?? {}
20632
+ };
20633
+ const mergedResponseHeaders = HeaderManager.appendMerge(
20634
+ requestPhaseResponseHeaders,
20635
+ responseHeaders
20636
+ );
20582
20637
  const responseCall = {
20583
20638
  ...call,
20584
20639
  request: {
@@ -20587,7 +20642,7 @@ var ProxyWasmRunner = class {
20587
20642
  body: modifiedRequestBody
20588
20643
  },
20589
20644
  response: {
20590
- headers: responseHeaders,
20645
+ headers: mergedResponseHeaders,
20591
20646
  body: responseBody,
20592
20647
  status: responseStatus,
20593
20648
  statusText: responseStatusText
@@ -20609,6 +20664,26 @@ var ProxyWasmRunner = class {
20609
20664
  "system"
20610
20665
  );
20611
20666
  }
20667
+ if (results.onResponseHeaders.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
20668
+ const local = this.hostFunctions.getLocalResponse();
20669
+ const responseHeaders2 = results.onResponseHeaders.output.response.headers;
20670
+ this.hostFunctions.resetLocalResponse();
20671
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20672
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20673
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20674
+ return {
20675
+ hookResults: results,
20676
+ finalResponse: {
20677
+ status: local.statusCode,
20678
+ statusText: local.statusText,
20679
+ headers: mergedHeaders,
20680
+ body,
20681
+ contentType: contentType2,
20682
+ isBase64: isBase642
20683
+ },
20684
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
20685
+ };
20686
+ }
20612
20687
  const headersAfterResponseHeaders = results.onResponseHeaders.output.response.headers;
20613
20688
  const propertiesAfterResponseHeaders = results.onResponseHeaders.properties;
20614
20689
  this.logDebug(
@@ -20633,6 +20708,26 @@ var ProxyWasmRunner = class {
20633
20708
  "system"
20634
20709
  );
20635
20710
  }
20711
+ if (results.onResponseBody.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
20712
+ const local = this.hostFunctions.getLocalResponse();
20713
+ const responseHeaders2 = results.onResponseBody.output.response.headers;
20714
+ this.hostFunctions.resetLocalResponse();
20715
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20716
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20717
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20718
+ return {
20719
+ hookResults: results,
20720
+ finalResponse: {
20721
+ status: local.statusCode,
20722
+ statusText: local.statusText,
20723
+ headers: mergedHeaders,
20724
+ body,
20725
+ contentType: contentType2,
20726
+ isBase64: isBase642
20727
+ },
20728
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
20729
+ };
20730
+ }
20636
20731
  const finalHeaders = results.onResponseBody.output.response.headers;
20637
20732
  const finalBody = results.onResponseBody.output.response.body;
20638
20733
  this.logDebug(`Final response body length: ${finalBody.length}`);
@@ -21000,7 +21095,9 @@ var ProxyWasmRunner = class {
21000
21095
  }
21001
21096
  }
21002
21097
  createImports() {
21003
- const wasi = new WASI({ version: "preview1", env: this.dictionary.getAll() });
21098
+ const dictEnv = this.dictionary.getAll();
21099
+ const wasiEnv = Object.keys(dictEnv).length === 0 ? { __FASTEDGE_RUNNER__: "1" } : dictEnv;
21100
+ const wasi = new WASI({ version: "preview1", env: wasiEnv });
21004
21101
  const wasiImport = wasi.wasiImport;
21005
21102
  return {
21006
21103
  env: this.hostFunctions.createImports(),
@@ -21109,7 +21206,7 @@ var ProxyWasmRunner = class {
21109
21206
  /**
21110
21207
  * Not supported for Proxy-WASM (HTTP WASM only)
21111
21208
  */
21112
- async execute(request) {
21209
+ async execute(_request) {
21113
21210
  throw new Error(
21114
21211
  "execute() is not supported for Proxy-WASM. Use callHook() or callFullFlow() instead."
21115
21212
  );
@@ -21273,21 +21370,22 @@ async function isLegacySyncWasm(bufferOrPath) {
21273
21370
 
21274
21371
  // server/runner/HttpWasmRunner.ts
21275
21372
  var HttpWasmRunner = class {
21373
+ process = null;
21374
+ port = null;
21375
+ cliPath = null;
21376
+ tempWasmPath = null;
21377
+ currentWasmPath = null;
21378
+ // resolved path used when spawning
21379
+ logs = [];
21380
+ stateManager = null;
21381
+ portManager;
21382
+ dotenvEnabled = true;
21383
+ dotenvPath = null;
21384
+ /** Pinned ports bypass PortManager allocation and must not be released back to it. */
21385
+ isPinnedPort = false;
21386
+ /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
21387
+ isLegacySync = false;
21276
21388
  constructor(portManager, dotenvEnabled = true) {
21277
- this.process = null;
21278
- this.port = null;
21279
- this.cliPath = null;
21280
- this.tempWasmPath = null;
21281
- this.currentWasmPath = null;
21282
- // resolved path used when spawning
21283
- this.logs = [];
21284
- this.stateManager = null;
21285
- this.dotenvEnabled = true;
21286
- this.dotenvPath = null;
21287
- /** Pinned ports bypass PortManager allocation and must not be released back to it. */
21288
- this.isPinnedPort = false;
21289
- /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
21290
- this.isLegacySync = false;
21291
21389
  this.portManager = portManager;
21292
21390
  this.dotenvEnabled = dotenvEnabled;
21293
21391
  }
@@ -21687,12 +21785,10 @@ function parseFetchHeaders(headers) {
21687
21785
  // server/runner/PortManager.ts
21688
21786
  import { createServer } from "net";
21689
21787
  var PortManager = class {
21690
- constructor() {
21691
- this.minPort = 8100;
21692
- this.maxPort = 8199;
21693
- this.allocatedPorts = /* @__PURE__ */ new Set();
21694
- this.lastAllocatedPort = this.minPort - 1;
21695
- }
21788
+ minPort = 8100;
21789
+ maxPort = 8199;
21790
+ allocatedPorts = /* @__PURE__ */ new Set();
21791
+ lastAllocatedPort = this.minPort - 1;
21696
21792
  /**
21697
21793
  * Check whether a port is actually free at the OS level.
21698
21794
  * This is necessary when multiple server processes run simultaneously —