@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.
@@ -18446,13 +18446,11 @@ var import_node_wasi = require("node:wasi");
18446
18446
  var textEncoder = new TextEncoder();
18447
18447
  var textDecoder = new TextDecoder();
18448
18448
  var MemoryManager = class {
18449
- constructor() {
18450
- this.memory = null;
18451
- this.instance = null;
18452
- this.hostAllocOffset = 0;
18453
- this.logCallback = null;
18454
- this.isInitializing = false;
18455
- }
18449
+ memory = null;
18450
+ instance = null;
18451
+ hostAllocOffset = 0;
18452
+ logCallback = null;
18453
+ isInitializing = false;
18456
18454
  setMemory(memory) {
18457
18455
  this.memory = memory;
18458
18456
  }
@@ -18615,7 +18613,7 @@ var MemoryManager = class {
18615
18613
 
18616
18614
  // server/runner/HeaderManager.ts
18617
18615
  var textEncoder2 = new TextEncoder();
18618
- var HeaderManager = class {
18616
+ var HeaderManager = class _HeaderManager {
18619
18617
  // Read a header value as a single string. For multi-valued headers (string[]) returns the first.
18620
18618
  // Use when callers know the header is conventionally single-valued (content-type, host, location, etc.)
18621
18619
  // and need to satisfy APIs that take a string.
@@ -18638,7 +18636,7 @@ var HeaderManager = class {
18638
18636
  return flat;
18639
18637
  }
18640
18638
  static normalize(headers) {
18641
- const normalized = {};
18639
+ const normalized = /* @__PURE__ */ Object.create(null);
18642
18640
  for (const [key, value] of Object.entries(headers)) {
18643
18641
  const k = key.toLowerCase();
18644
18642
  if (Array.isArray(value)) {
@@ -18649,6 +18647,30 @@ var HeaderManager = class {
18649
18647
  }
18650
18648
  return normalized;
18651
18649
  }
18650
+ // Append-merge two header records: for keys present in both, values are
18651
+ // concatenated into a string[] rather than the right-hand side overwriting
18652
+ // the left. Keys are lowercased on the way in (consistent with `normalize`).
18653
+ //
18654
+ // Used by the runner to combine request-phase response-header state with
18655
+ // the origin's response headers, mirroring how Envoy serves
18656
+ // `add_http_response_header` calls issued during onRequestHeaders /
18657
+ // onRequestBody against the actual upstream response — both values survive
18658
+ // as a multi-value list (preserving the proxy-wasm cross-phase pattern).
18659
+ static appendMerge(left, right) {
18660
+ const result = _HeaderManager.normalize(left);
18661
+ for (const [key, value] of Object.entries(right)) {
18662
+ const k = key.toLowerCase();
18663
+ const incoming = Array.isArray(value) ? value.map(String) : [String(value)];
18664
+ if (Object.hasOwn(result, k)) {
18665
+ const existing = result[k];
18666
+ const existingArr = Array.isArray(existing) ? existing : [existing];
18667
+ result[k] = [...existingArr, ...incoming];
18668
+ } else {
18669
+ result[k] = incoming.length === 1 ? incoming[0] : incoming;
18670
+ }
18671
+ }
18672
+ return result;
18673
+ }
18652
18674
  static serialize(headers) {
18653
18675
  const pairs = Object.entries(headers);
18654
18676
  const numPairs = pairs.length;
@@ -18827,20 +18849,18 @@ var HeaderManager = class {
18827
18849
 
18828
18850
  // server/runner/PropertyResolver.ts
18829
18851
  var PropertyResolver = class {
18830
- constructor() {
18831
- this.properties = {};
18832
- this.requestHeaders = {};
18833
- this.requestMethod = "GET";
18834
- this.requestPath = "/";
18835
- this.requestScheme = "https";
18836
- this.requestUrl = "";
18837
- this.requestHost = "";
18838
- this.requestQuery = "";
18839
- this.requestExtension = "";
18840
- this.responseHeaders = {};
18841
- this.responseStatus = 200;
18842
- this.responseStatusText = "OK";
18843
- }
18852
+ properties = {};
18853
+ requestHeaders = {};
18854
+ requestMethod = "GET";
18855
+ requestPath = "/";
18856
+ requestScheme = "https";
18857
+ requestUrl = "";
18858
+ requestHost = "";
18859
+ requestQuery = "";
18860
+ requestExtension = "";
18861
+ responseHeaders = {};
18862
+ responseStatus = 200;
18863
+ responseStatusText = "OK";
18844
18864
  setProperties(properties) {
18845
18865
  this.properties = properties;
18846
18866
  }
@@ -19032,6 +19052,7 @@ var PropertyResolver = class {
19032
19052
 
19033
19053
  // server/fastedge-host/SecretStore.ts
19034
19054
  var SecretStore = class {
19055
+ secrets;
19035
19056
  constructor(initialSecrets) {
19036
19057
  this.secrets = /* @__PURE__ */ new Map();
19037
19058
  if (initialSecrets) {
@@ -19114,6 +19135,7 @@ var SecretStore = class {
19114
19135
 
19115
19136
  // server/fastedge-host/Dictionary.ts
19116
19137
  var Dictionary = class {
19138
+ data;
19117
19139
  constructor(initialData) {
19118
19140
  this.data = /* @__PURE__ */ new Map();
19119
19141
  if (initialData) {
@@ -19281,26 +19303,39 @@ function createFastEdgeHostFunctions(memory, secretStore, dictionary, logDebug)
19281
19303
  // server/runner/HostFunctions.ts
19282
19304
  var textEncoder3 = new TextEncoder();
19283
19305
  var HostFunctions = class {
19306
+ memory;
19307
+ propertyResolver;
19308
+ propertyAccessControl;
19309
+ getCurrentHook;
19310
+ logs = [];
19311
+ requestHeaders = [];
19312
+ responseHeaders = [];
19313
+ requestBody = "";
19314
+ responseBody = "";
19315
+ vmConfig = "";
19316
+ pluginConfig = "";
19317
+ currentContextId = 1;
19318
+ lastHostCall = null;
19319
+ debug = false;
19320
+ currentLogLevel = 0 /* Trace */;
19321
+ // Default to show all logs
19322
+ // http_call state
19323
+ nextTokenId = 0;
19324
+ pendingHttpCall = null;
19325
+ httpCallResponse = null;
19326
+ streamClosed = false;
19327
+ // Local response state (from proxy_send_local_response / send_http_response).
19328
+ // `headers` carries the additional headers passed as the 4th argument of
19329
+ // `send_http_response`; ProxyWasmRunner merges these into finalResponse at
19330
+ // the short-circuit points so they reach the caller alongside any headers
19331
+ // accumulated via stream_context.headers.response.add() in the same hook.
19332
+ // Stored as tuples to preserve order and duplicate-name semantics (e.g. for
19333
+ // multi-value Set-Cookie additions).
19334
+ localResponse = null;
19335
+ // FastEdge extensions
19336
+ secretStore;
19337
+ dictionary;
19284
19338
  constructor(memory, propertyResolver, propertyAccessControl, getCurrentHook, debug = false, secretStore, dictionary) {
19285
- this.logs = [];
19286
- this.requestHeaders = [];
19287
- this.responseHeaders = [];
19288
- this.requestBody = "";
19289
- this.responseBody = "";
19290
- this.vmConfig = "";
19291
- this.pluginConfig = "";
19292
- this.currentContextId = 1;
19293
- this.lastHostCall = null;
19294
- this.debug = false;
19295
- this.currentLogLevel = 0 /* Trace */;
19296
- // Default to show all logs
19297
- // http_call state
19298
- this.nextTokenId = 0;
19299
- this.pendingHttpCall = null;
19300
- this.httpCallResponse = null;
19301
- this.streamClosed = false;
19302
- // Local response state (from proxy_send_local_response / send_http_response)
19303
- this.localResponse = null;
19304
19339
  this.memory = memory;
19305
19340
  this.propertyResolver = propertyResolver;
19306
19341
  this.propertyAccessControl = propertyAccessControl;
@@ -19645,12 +19680,13 @@ var HostFunctions = class {
19645
19680
  );
19646
19681
  const statusText = this.memory.readString(statusCodePtr, statusCodeLen);
19647
19682
  const body = this.memory.readBytes(bodyPtr, bodyLen);
19683
+ let headers = [];
19648
19684
  if (headerPairsLen > 0) {
19649
19685
  const headerBytes = this.memory.readBytes(headerPairsPtr, headerPairsLen);
19650
- const headers = HeaderManager.deserializeBinary(headerBytes);
19651
- this.logDebug(`send_local_response headers (not merged): ${JSON.stringify(headers)}`);
19686
+ headers = HeaderManager.deserializeBinaryToTuples(headerBytes);
19687
+ this.logDebug(`send_local_response headers: ${JSON.stringify(headers)}`);
19652
19688
  }
19653
- this.localResponse = { statusCode, statusText, body };
19689
+ this.localResponse = { statusCode, statusText, body, headers };
19654
19690
  this.logs.push({
19655
19691
  level: 1,
19656
19692
  message: `local_response status=${statusCode} ${statusText} bodyLen=${body.byteLength} grpc=${grpcStatus}`
@@ -20031,6 +20067,8 @@ var BUILT_IN_PROPERTIES = [
20031
20067
  }
20032
20068
  ];
20033
20069
  var PropertyAccessControl = class {
20070
+ builtInProperties;
20071
+ customProperties;
20034
20072
  constructor() {
20035
20073
  this.builtInProperties = /* @__PURE__ */ new Map();
20036
20074
  this.customProperties = /* @__PURE__ */ new Map();
@@ -20248,21 +20286,28 @@ var textEncoder4 = new TextEncoder();
20248
20286
  var BUILTIN_URL = "http://fastedge-builtin.debug";
20249
20287
  var BUILTIN_SHORTHAND = "built-in";
20250
20288
  var ProxyWasmRunner = class {
20289
+ module = null;
20290
+ // Compiled module (reused)
20291
+ instance = null;
20292
+ // Current instance (transient per hook)
20293
+ memory;
20294
+ propertyResolver;
20295
+ propertyAccessControl;
20296
+ currentHook = null;
20297
+ hostFunctions;
20298
+ logs = [];
20299
+ rootContextId = 1;
20300
+ nextContextId = 2;
20301
+ currentContextId = 1;
20302
+ isInitializing = false;
20303
+ debug = process.env.PROXY_RUNNER_DEBUG === "1";
20304
+ stateManager = null;
20305
+ secretStore;
20306
+ dictionary;
20307
+ dotenvEnabled = true;
20308
+ // Default to enabled
20309
+ dotenvPath = ".";
20251
20310
  constructor(fastEdgeConfig, dotenvEnabled = true) {
20252
- this.module = null;
20253
- // Compiled module (reused)
20254
- this.instance = null;
20255
- this.currentHook = null;
20256
- this.logs = [];
20257
- this.rootContextId = 1;
20258
- this.nextContextId = 2;
20259
- this.currentContextId = 1;
20260
- this.isInitializing = false;
20261
- this.debug = process.env.PROXY_RUNNER_DEBUG === "1";
20262
- this.stateManager = null;
20263
- this.dotenvEnabled = true;
20264
- // Default to enabled
20265
- this.dotenvPath = ".";
20266
20311
  this.memory = new MemoryManager();
20267
20312
  this.propertyResolver = new PropertyResolver();
20268
20313
  this.propertyAccessControl = new PropertyAccessControl();
@@ -20435,14 +20480,15 @@ var ProxyWasmRunner = class {
20435
20480
  const local = this.hostFunctions.getLocalResponse();
20436
20481
  const responseHeaders2 = results.onRequestHeaders.output.response.headers;
20437
20482
  this.hostFunctions.resetLocalResponse();
20438
- const contentType2 = HeaderManager.firstValue(responseHeaders2["content-type"]) || "text/plain";
20483
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20484
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20439
20485
  const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20440
20486
  return {
20441
20487
  hookResults: results,
20442
20488
  finalResponse: {
20443
20489
  status: local.statusCode,
20444
20490
  statusText: local.statusText,
20445
- headers: responseHeaders2,
20491
+ headers: mergedHeaders,
20446
20492
  body,
20447
20493
  contentType: contentType2,
20448
20494
  isBase64: isBase642
@@ -20478,14 +20524,15 @@ var ProxyWasmRunner = class {
20478
20524
  const local = this.hostFunctions.getLocalResponse();
20479
20525
  const responseHeaders2 = results.onRequestBody.output.response.headers;
20480
20526
  this.hostFunctions.resetLocalResponse();
20481
- const contentType2 = HeaderManager.firstValue(responseHeaders2["content-type"]) || "text/plain";
20527
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20528
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20482
20529
  const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20483
20530
  return {
20484
20531
  hookResults: results,
20485
20532
  finalResponse: {
20486
20533
  status: local.statusCode,
20487
20534
  statusText: local.statusText,
20488
- headers: responseHeaders2,
20535
+ headers: mergedHeaders,
20489
20536
  body,
20490
20537
  contentType: contentType2,
20491
20538
  isBase64: isBase642
@@ -20615,6 +20662,14 @@ var ProxyWasmRunner = class {
20615
20662
  `Fetch completed: ${responseStatus} ${responseStatusText}`
20616
20663
  );
20617
20664
  }
20665
+ const requestPhaseResponseHeaders = {
20666
+ ...results.onRequestHeaders.output.response.headers ?? {},
20667
+ ...results.onRequestBody.output.response.headers ?? {}
20668
+ };
20669
+ const mergedResponseHeaders = HeaderManager.appendMerge(
20670
+ requestPhaseResponseHeaders,
20671
+ responseHeaders
20672
+ );
20618
20673
  const responseCall = {
20619
20674
  ...call,
20620
20675
  request: {
@@ -20623,7 +20678,7 @@ var ProxyWasmRunner = class {
20623
20678
  body: modifiedRequestBody
20624
20679
  },
20625
20680
  response: {
20626
- headers: responseHeaders,
20681
+ headers: mergedResponseHeaders,
20627
20682
  body: responseBody,
20628
20683
  status: responseStatus,
20629
20684
  statusText: responseStatusText
@@ -20645,6 +20700,26 @@ var ProxyWasmRunner = class {
20645
20700
  "system"
20646
20701
  );
20647
20702
  }
20703
+ if (results.onResponseHeaders.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
20704
+ const local = this.hostFunctions.getLocalResponse();
20705
+ const responseHeaders2 = results.onResponseHeaders.output.response.headers;
20706
+ this.hostFunctions.resetLocalResponse();
20707
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20708
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20709
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20710
+ return {
20711
+ hookResults: results,
20712
+ finalResponse: {
20713
+ status: local.statusCode,
20714
+ statusText: local.statusText,
20715
+ headers: mergedHeaders,
20716
+ body,
20717
+ contentType: contentType2,
20718
+ isBase64: isBase642
20719
+ },
20720
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
20721
+ };
20722
+ }
20648
20723
  const headersAfterResponseHeaders = results.onResponseHeaders.output.response.headers;
20649
20724
  const propertiesAfterResponseHeaders = results.onResponseHeaders.properties;
20650
20725
  this.logDebug(
@@ -20669,6 +20744,26 @@ var ProxyWasmRunner = class {
20669
20744
  "system"
20670
20745
  );
20671
20746
  }
20747
+ if (results.onResponseBody.returnCode === 1 && this.hostFunctions.hasLocalResponse()) {
20748
+ const local = this.hostFunctions.getLocalResponse();
20749
+ const responseHeaders2 = results.onResponseBody.output.response.headers;
20750
+ this.hostFunctions.resetLocalResponse();
20751
+ const mergedHeaders = local.headers.length > 0 ? HeaderManager.appendMerge(responseHeaders2, HeaderManager.tuplesToRecord(local.headers)) : responseHeaders2;
20752
+ const contentType2 = HeaderManager.firstValue(mergedHeaders["content-type"]) || "text/plain";
20753
+ const { body, isBase64: isBase642 } = encodeLocalResponseBody(local.body, contentType2);
20754
+ return {
20755
+ hookResults: results,
20756
+ finalResponse: {
20757
+ status: local.statusCode,
20758
+ statusText: local.statusText,
20759
+ headers: mergedHeaders,
20760
+ body,
20761
+ contentType: contentType2,
20762
+ isBase64: isBase642
20763
+ },
20764
+ calculatedProperties: this.propertyResolver.getCalculatedProperties()
20765
+ };
20766
+ }
20672
20767
  const finalHeaders = results.onResponseBody.output.response.headers;
20673
20768
  const finalBody = results.onResponseBody.output.response.body;
20674
20769
  this.logDebug(`Final response body length: ${finalBody.length}`);
@@ -21036,7 +21131,9 @@ var ProxyWasmRunner = class {
21036
21131
  }
21037
21132
  }
21038
21133
  createImports() {
21039
- const wasi = new import_node_wasi.WASI({ version: "preview1", env: this.dictionary.getAll() });
21134
+ const dictEnv = this.dictionary.getAll();
21135
+ const wasiEnv = Object.keys(dictEnv).length === 0 ? { __FASTEDGE_RUNNER__: "1" } : dictEnv;
21136
+ const wasi = new import_node_wasi.WASI({ version: "preview1", env: wasiEnv });
21040
21137
  const wasiImport = wasi.wasiImport;
21041
21138
  return {
21042
21139
  env: this.hostFunctions.createImports(),
@@ -21145,7 +21242,7 @@ var ProxyWasmRunner = class {
21145
21242
  /**
21146
21243
  * Not supported for Proxy-WASM (HTTP WASM only)
21147
21244
  */
21148
- async execute(request) {
21245
+ async execute(_request) {
21149
21246
  throw new Error(
21150
21247
  "execute() is not supported for Proxy-WASM. Use callHook() or callFullFlow() instead."
21151
21248
  );
@@ -21309,21 +21406,22 @@ async function isLegacySyncWasm(bufferOrPath) {
21309
21406
 
21310
21407
  // server/runner/HttpWasmRunner.ts
21311
21408
  var HttpWasmRunner = class {
21409
+ process = null;
21410
+ port = null;
21411
+ cliPath = null;
21412
+ tempWasmPath = null;
21413
+ currentWasmPath = null;
21414
+ // resolved path used when spawning
21415
+ logs = [];
21416
+ stateManager = null;
21417
+ portManager;
21418
+ dotenvEnabled = true;
21419
+ dotenvPath = null;
21420
+ /** Pinned ports bypass PortManager allocation and must not be released back to it. */
21421
+ isPinnedPort = false;
21422
+ /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
21423
+ isLegacySync = false;
21312
21424
  constructor(portManager, dotenvEnabled = true) {
21313
- this.process = null;
21314
- this.port = null;
21315
- this.cliPath = null;
21316
- this.tempWasmPath = null;
21317
- this.currentWasmPath = null;
21318
- // resolved path used when spawning
21319
- this.logs = [];
21320
- this.stateManager = null;
21321
- this.dotenvEnabled = true;
21322
- this.dotenvPath = null;
21323
- /** Pinned ports bypass PortManager allocation and must not be released back to it. */
21324
- this.isPinnedPort = false;
21325
- /** @deprecated Legacy sync support — remove when #[fastedge::http] is retired */
21326
- this.isLegacySync = false;
21327
21425
  this.portManager = portManager;
21328
21426
  this.dotenvEnabled = dotenvEnabled;
21329
21427
  }
@@ -21723,12 +21821,10 @@ function parseFetchHeaders(headers) {
21723
21821
  // server/runner/PortManager.ts
21724
21822
  var import_net = require("net");
21725
21823
  var PortManager = class {
21726
- constructor() {
21727
- this.minPort = 8100;
21728
- this.maxPort = 8199;
21729
- this.allocatedPorts = /* @__PURE__ */ new Set();
21730
- this.lastAllocatedPort = this.minPort - 1;
21731
- }
21824
+ minPort = 8100;
21825
+ maxPort = 8199;
21826
+ allocatedPorts = /* @__PURE__ */ new Set();
21827
+ lastAllocatedPort = this.minPort - 1;
21732
21828
  /**
21733
21829
  * Check whether a port is actually free at the OS level.
21734
21830
  * This is necessary when multiple server processes run simultaneously —