@interopio/gateway-server 0.5.2-beta → 0.6.0-beta

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/index.cjs CHANGED
@@ -169,7 +169,7 @@ var HttpServerRequest = class {
169
169
  this._body ??= new Promise((resolve, reject) => {
170
170
  const chunks = [];
171
171
  this._req.on("error", (err) => reject(err)).on("data", (chunk) => chunks.push(chunk)).on("end", () => {
172
- resolve(new Blob(chunks));
172
+ resolve(new Blob(chunks, { type: this.headers.one("content-type") }));
173
173
  });
174
174
  });
175
175
  return this._body;
@@ -186,74 +186,79 @@ var HttpServerRequest = class {
186
186
  }
187
187
  get json() {
188
188
  return this.body.then(async (blob) => {
189
- const json = JSON.parse(await blob.text());
189
+ if (blob.size === 0) {
190
+ return void 0;
191
+ }
192
+ const text = await blob.text();
193
+ const json = JSON.parse(text);
190
194
  return json;
191
195
  });
192
196
  }
193
197
  };
194
198
  var IncomingMessageHeaders = class {
195
- constructor(_msg) {
196
- this._msg = _msg;
199
+ #msg;
200
+ constructor(msg) {
201
+ this.#msg = msg;
197
202
  }
198
203
  has(name) {
199
- return this._msg.headers[name] !== void 0;
204
+ return this.#msg.headers[name] !== void 0;
200
205
  }
201
206
  get(name) {
202
- return this._msg.headers[name];
207
+ return this.#msg.headers[name];
203
208
  }
204
209
  list(name) {
205
- return toList(this._msg.headers[name]);
210
+ return toList(this.#msg.headers[name]);
206
211
  }
207
212
  one(name) {
208
- const value = this._msg.headers[name];
213
+ const value = this.#msg.headers[name];
209
214
  if (Array.isArray(value)) {
210
215
  return value[0];
211
216
  }
212
217
  return value;
213
218
  }
214
219
  keys() {
215
- return Object.keys(this._msg.headers).values();
220
+ return Object.keys(this.#msg.headers).values();
216
221
  }
217
222
  };
218
223
  var OutgoingMessageHeaders = class {
219
- _msg;
224
+ #msg;
220
225
  constructor(msg) {
221
- this._msg = msg;
226
+ this.#msg = msg;
222
227
  }
223
228
  has(name) {
224
- return this._msg.hasHeader(name);
229
+ return this.#msg.hasHeader(name);
225
230
  }
226
231
  keys() {
227
- return this._msg.getHeaderNames().values();
232
+ return this.#msg.getHeaderNames().values();
228
233
  }
229
234
  get(name) {
230
- return this._msg.getHeader(name);
235
+ return this.#msg.getHeader(name);
231
236
  }
232
237
  one(name) {
233
- const value = this._msg.getHeader(name);
238
+ const value = this.#msg.getHeader(name);
234
239
  if (Array.isArray(value)) {
235
240
  return value[0];
236
241
  }
237
242
  return value;
238
243
  }
239
244
  set(name, value) {
240
- if (!this._msg.headersSent) {
245
+ if (!this.#msg.headersSent) {
241
246
  if (Array.isArray(value)) {
242
247
  value = value.map((v) => typeof v === "number" ? String(v) : v);
243
248
  } else if (typeof value === "number") {
244
249
  value = String(value);
245
250
  }
246
251
  if (value) {
247
- this._msg.setHeader(name, value);
252
+ this.#msg.setHeader(name, value);
248
253
  } else {
249
- this._msg.removeHeader(name);
254
+ this.#msg.removeHeader(name);
250
255
  }
251
256
  }
252
257
  return this;
253
258
  }
254
259
  add(name, value) {
255
- if (!this._msg.headersSent) {
256
- this._msg.appendHeader(name, value);
260
+ if (!this.#msg.headersSent) {
261
+ this.#msg.appendHeader(name, value);
257
262
  }
258
263
  return this;
259
264
  }
@@ -301,7 +306,7 @@ var HttpServerResponse = class {
301
306
  }
302
307
  end(chunk) {
303
308
  if (!this._res.headersSent) {
304
- return new Promise((resolve, reject) => {
309
+ return new Promise((resolve) => {
305
310
  if (chunk === void 0) {
306
311
  this._res.end(() => {
307
312
  resolve(true);
@@ -331,6 +336,8 @@ var HttpServerResponse = class {
331
336
  }
332
337
  };
333
338
  var DefaultWebExchange = class extends WebExchange {
339
+ request;
340
+ response;
334
341
  constructor(request, response) {
335
342
  super();
336
343
  this.request = request;
@@ -419,6 +426,92 @@ var MapHttpHeaders = class extends Map {
419
426
  return this;
420
427
  }
421
428
  };
429
+ var MockHttpRequest = class {
430
+ #url;
431
+ #body;
432
+ headers = new MapHttpHeaders();
433
+ constructor(url, method) {
434
+ if (typeof url === "string") {
435
+ if (URL.canParse(url)) {
436
+ url = new URL(url);
437
+ } else if (URL.canParse(url, "http://localhost")) {
438
+ url = new URL(url, "http://localhost");
439
+ } else {
440
+ throw new TypeError("URL cannot parse url");
441
+ }
442
+ }
443
+ this.#url = url;
444
+ this.method = method ?? "GET";
445
+ this.headers.set("Host", this.#url.hostname);
446
+ this.path = this.#url.pathname ?? "/";
447
+ }
448
+ method;
449
+ path;
450
+ get host() {
451
+ return requestToHost(this, this.#url.host);
452
+ }
453
+ get protocol() {
454
+ return requestToProtocol(this, this.#url.protocol.slice(0, -1));
455
+ }
456
+ get cookies() {
457
+ return parseCookies(this);
458
+ }
459
+ get body() {
460
+ const body = this.#body;
461
+ return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
462
+ }
463
+ get json() {
464
+ return this.body.then(async (blob) => JSON.parse(await blob.text()));
465
+ }
466
+ get text() {
467
+ return this.body.then(async (blob) => await blob.text());
468
+ }
469
+ set body(value) {
470
+ this.#body = value;
471
+ if (!this.headers.has("content-type")) {
472
+ this.headers.set("content-type", value.type || "application/octet-stream");
473
+ }
474
+ }
475
+ get formData() {
476
+ return this.body.then(async (b) => new URLSearchParams(await b.text()));
477
+ }
478
+ get URL() {
479
+ return new URL(this.path + this.#url.search + this.#url.hash, `${this.protocol}://${this.host}`);
480
+ }
481
+ };
482
+ var MockHttpResponse = class {
483
+ statusCode;
484
+ headers = new MapHttpHeaders();
485
+ cookies = [];
486
+ addCookie(cookie) {
487
+ this.cookies.push(cookie);
488
+ return this;
489
+ }
490
+ _body;
491
+ async end(chunk) {
492
+ if (this._body) {
493
+ return false;
494
+ }
495
+ switch (typeof chunk) {
496
+ case "string":
497
+ this._body = new Blob([chunk], { type: "text/plain" });
498
+ break;
499
+ case "object":
500
+ if (chunk instanceof Blob) {
501
+ this._body = chunk;
502
+ } else if (chunk !== null) {
503
+ this._body = new Blob([JSON.stringify(chunk)], { type: "application/json" });
504
+ }
505
+ break;
506
+ case "undefined":
507
+ this._body = new Blob([]);
508
+ break;
509
+ default:
510
+ throw new Error(`Unsupported chunk type: ${typeof chunk}`);
511
+ }
512
+ return true;
513
+ }
514
+ };
422
515
 
423
516
  // src/gateway/ws/core.ts
424
517
  var import_gateway2 = require("@interopio/gateway");
@@ -589,22 +682,129 @@ var InMemoryNodeConnections = class {
589
682
  }
590
683
  };
591
684
 
685
+ // src/server/util/matchers.ts
686
+ var or = (matchers) => {
687
+ return async (exchange) => {
688
+ for (const matcher of matchers) {
689
+ const result = await matcher(exchange);
690
+ if (result.match) {
691
+ return { match: true };
692
+ }
693
+ }
694
+ return { match: false };
695
+ };
696
+ };
697
+ var and = (matchers) => {
698
+ const matcher = async (exchange) => {
699
+ for (const matcher2 of matchers) {
700
+ const result = await matcher2(exchange);
701
+ if (!result.match) {
702
+ return { match: false };
703
+ }
704
+ }
705
+ return { match: true };
706
+ };
707
+ matcher.toString = () => `and(${matchers.map((m) => m.toString()).join(", ")})`;
708
+ return matcher;
709
+ };
710
+ var not = (matcher) => {
711
+ return async (exchange) => {
712
+ const result = await matcher(exchange);
713
+ return { match: !result.match };
714
+ };
715
+ };
716
+ var anyExchange = async (_exchange) => {
717
+ return { match: true };
718
+ };
719
+ anyExchange.toString = () => "any-exchange";
720
+ var pattern = (pattern2, opts) => {
721
+ const method = opts?.method;
722
+ const matcher = async (exchange) => {
723
+ const request = exchange.request;
724
+ const path = request.path;
725
+ if (method !== void 0 && request.method !== method) {
726
+ return { match: false };
727
+ }
728
+ if (typeof pattern2 === "string") {
729
+ if (path === pattern2) {
730
+ return { match: true };
731
+ } else {
732
+ return { match: false };
733
+ }
734
+ } else {
735
+ const match = pattern2.exec(path);
736
+ if (match !== null) {
737
+ return { match: true };
738
+ } else {
739
+ return { match: false };
740
+ }
741
+ }
742
+ };
743
+ matcher.toString = () => {
744
+ return `pattern(${pattern2.toString()})${method ? ` method=${method}` : ""}`;
745
+ };
746
+ return matcher;
747
+ };
748
+ var mediaType = (opts) => {
749
+ const shouldIgnore = (requestedMediaType) => {
750
+ if (opts.ignoredMediaTypes !== void 0) {
751
+ for (const ignoredMediaType of opts.ignoredMediaTypes) {
752
+ if (requestedMediaType === ignoredMediaType || ignoredMediaType === "*/*") {
753
+ return true;
754
+ }
755
+ }
756
+ }
757
+ return false;
758
+ };
759
+ return async (exchange) => {
760
+ const request = exchange.request;
761
+ let requestMediaTypes;
762
+ try {
763
+ requestMediaTypes = request.headers.list("accept");
764
+ } catch (e) {
765
+ return { match: false };
766
+ }
767
+ for (const requestedMediaType of requestMediaTypes) {
768
+ if (shouldIgnore(requestedMediaType)) {
769
+ continue;
770
+ }
771
+ for (const mediaType2 of opts.mediaTypes) {
772
+ if (requestedMediaType.startsWith(mediaType2)) {
773
+ return { match: true };
774
+ }
775
+ }
776
+ }
777
+ return { match: false };
778
+ };
779
+ };
780
+ var upgradeMatcher = async ({ request }) => {
781
+ const match = request["_req"]?.["upgrade"] && request.headers.one("upgrade")?.toLowerCase() === "websocket";
782
+ return { match };
783
+ };
784
+ upgradeMatcher.toString = () => "websocket upgrade";
785
+
592
786
  // src/mesh/rest-directory/routes.ts
593
- function routes(connections) {
787
+ function routes(connections, config, authorize) {
788
+ config.cors.push(
789
+ [pattern("/api/nodes"), { allowMethods: ["GET", "POST"] }],
790
+ [pattern(/\/api\/nodes\/(?<nodeId>.*)/), { allowMethods: ["GET", "DELETE"] }]
791
+ );
792
+ if (authorize) {
793
+ config.authorize.push([pattern(/\/api\/nodes(\/.*)?/), authorize]);
794
+ }
594
795
  return [
595
796
  async (ctx, next) => {
596
797
  if (ctx.method === "POST" && ctx.path === "/api/nodes") {
597
798
  const json = await ctx.request.json;
598
799
  if (!Array.isArray(json)) {
599
800
  ctx.response.statusCode = 400;
600
- ctx.response._res.end();
801
+ await ctx.response.end();
601
802
  } else {
602
803
  const nodes = json;
603
804
  const result = connections.announce(nodes);
604
- const body = JSON.stringify(result);
605
- ctx.response.headers.set("content-type", "application/json");
805
+ const body = new Blob([JSON.stringify(result)], { type: "application/json" });
606
806
  ctx.response.statusCode = 200;
607
- ctx.response._res.end(body);
807
+ await ctx.response.end(body);
608
808
  }
609
809
  } else {
610
810
  await next();
@@ -615,7 +815,7 @@ function routes(connections) {
615
815
  const nodeId = path?.substring("/api/nodes/".length);
616
816
  connections.remove(nodeId);
617
817
  response.statusCode = 200;
618
- response._res.end();
818
+ await response.end();
619
819
  } else {
620
820
  await next();
621
821
  }
@@ -976,68 +1176,38 @@ var core_default4 = create4;
976
1176
 
977
1177
  // src/metrics/routes.ts
978
1178
  var logger5 = getLogger("metrics");
979
- var COOKIE_NAME = "GW_LOGIN";
980
- function loggedIn(auth, ctx) {
981
- if (auth) {
982
- const value = ctx.request.cookies.find((cookie) => cookie.name === COOKIE_NAME)?.value;
983
- return value && parseInt(value) > Date.now();
984
- }
985
- return true;
986
- }
987
- async function routes2(config) {
1179
+ async function routes2(config, { cors, authorize }) {
988
1180
  const { jsonFileAppender } = await import("@interopio/gateway/metrics/publisher/file");
989
1181
  const appender = jsonFileAppender(logger5);
990
1182
  await appender.open(config.file?.location ?? "metrics.ndjson", config.file?.append ?? true);
1183
+ cors.push([pattern("/api/metrics"), { allowMethods: ["GET", "POST"] }]);
1184
+ if (config.authorize) {
1185
+ authorize.push([pattern("/api/metrics"), config.authorize]);
1186
+ }
991
1187
  return [
992
1188
  async (ctx, next) => {
993
1189
  if (ctx.method === "GET" && ctx.path === "/api/metrics") {
994
- if (loggedIn(config.auth, ctx)) {
995
- ctx.response.statusCode = 200;
996
- } else {
997
- ctx.response.statusCode = 302;
998
- ctx.response.headers.set("location", "/api/login?redirectTo=/api/metrics");
999
- }
1000
- ctx.response._res.end();
1001
- } else {
1002
- await next();
1003
- }
1004
- },
1005
- async (ctx, next) => {
1006
- if (ctx.method === "GET" && ctx.path === "/api/login") {
1007
- const redirectTo = new URLSearchParams(ctx.request.query ?? void 0).get("redirectTo");
1008
- const expires = Date.now() + 180 * 1e3;
1009
- ctx.response.addCookie({ name: COOKIE_NAME, value: `${expires}`, maxAge: Infinity, path: "/api", sameSite: "strict" });
1010
- if (redirectTo) {
1011
- ctx.response.statusCode = 302;
1012
- ctx.response.headers.set("location", redirectTo);
1013
- } else {
1014
- ctx.response.statusCode = 200;
1015
- }
1016
- ctx.response._res.end();
1190
+ ctx.response.statusCode = 200;
1191
+ await ctx.response.end();
1017
1192
  } else {
1018
1193
  await next();
1019
1194
  }
1020
1195
  },
1021
- async (ctx, next) => {
1022
- if (ctx.method === "POST" && ctx.path === "/api/metrics") {
1023
- if (loggedIn(config.auth, ctx)) {
1024
- ctx.response.statusCode = 202;
1025
- ctx.response._res.end();
1026
- try {
1027
- const json = await ctx.request.json;
1028
- const update = json;
1029
- if (logger5.enabledFor("debug")) {
1030
- logger5.debug(`${JSON.stringify(update)}`);
1031
- }
1032
- if ((config.file?.status ?? false) || update.status === void 0) {
1033
- await appender.write(update);
1034
- }
1035
- } catch (e) {
1036
- logger5.error(`error processing metrics`, e);
1196
+ async ({ request, response }, next) => {
1197
+ if (request.method === "POST" && request.path === "/api/metrics") {
1198
+ response.statusCode = 202;
1199
+ await response.end();
1200
+ try {
1201
+ const json = await request.json;
1202
+ const update = json;
1203
+ if (logger5.enabledFor("debug")) {
1204
+ logger5.debug(`${JSON.stringify(update)}`);
1037
1205
  }
1038
- } else {
1039
- ctx.response.statusCode = 401;
1040
- ctx.response._res.end();
1206
+ if ((config.file?.status ?? false) || update.status === void 0) {
1207
+ await appender.write(update);
1208
+ }
1209
+ } catch (e) {
1210
+ logger5.error(`error processing metrics`, e);
1041
1211
  }
1042
1212
  } else {
1043
1213
  await next();
@@ -1267,7 +1437,7 @@ async function stop(m) {
1267
1437
  return await run(m, "stop");
1268
1438
  }
1269
1439
 
1270
- // src/server/ws-client-verify.ts
1440
+ // src/app/ws-client-verify.ts
1271
1441
  var import_gateway5 = require("@interopio/gateway");
1272
1442
  var log3 = getLogger("gateway.ws.client-verify");
1273
1443
  function acceptsMissing(originFilters) {
@@ -1339,272 +1509,56 @@ function regexifyOriginFilters(originFilters) {
1339
1509
  }
1340
1510
  }
1341
1511
 
1342
- // src/server/cors.ts
1343
- var import_gateway6 = require("@interopio/gateway");
1344
- function isSameOrigin(request) {
1345
- const origin = request.headers.one("origin");
1346
- if (origin === void 0) {
1347
- return true;
1348
- }
1349
- const url = request.URL;
1350
- const actualProtocol = url.protocol;
1351
- const actualHost = url.host;
1352
- const originUrl = new URL(origin);
1353
- const originHost = originUrl.host;
1354
- const originProtocol = originUrl.protocol;
1355
- return actualProtocol === originProtocol && actualHost === originHost;
1356
- }
1357
- function isCorsRequest(request) {
1358
- return request.headers.has("origin") && !isSameOrigin(request);
1359
- }
1360
- function isPreFlightRequest(request) {
1361
- return request.method === "OPTIONS" && request.headers.has("origin") && request.headers.has("access-control-request-method");
1362
- }
1363
- var VARY_HEADERS = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"];
1364
- function processRequest(exchange, config) {
1365
- const { request, response } = exchange;
1366
- const responseHeaders = response.headers;
1367
- if (!responseHeaders.has("Vary")) {
1368
- responseHeaders.set("Vary", VARY_HEADERS.join(", "));
1369
- } else {
1370
- const varyHeaders = responseHeaders.list("Vary");
1371
- for (const header of VARY_HEADERS) {
1372
- if (!varyHeaders.find((h) => h === header)) {
1373
- varyHeaders.push(header);
1374
- }
1512
+ // src/server/server-header.ts
1513
+ var serverHeader = (server) => {
1514
+ const enabled = typeof server === "string";
1515
+ return async ({ response }, next) => {
1516
+ if (server != false && !response.headers.has("server")) {
1517
+ response.headers.set("Server", server);
1375
1518
  }
1376
- responseHeaders.set("Vary", varyHeaders.join(", "));
1377
- }
1378
- try {
1379
- if (!isCorsRequest(request)) {
1519
+ await next();
1520
+ };
1521
+ };
1522
+ var server_header_default = (server = "gateway-server") => serverHeader(server);
1523
+
1524
+ // src/app/route.ts
1525
+ function findSocketRoute({ request }, { sockets: routes3 }) {
1526
+ const path = request.path ?? "/";
1527
+ const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
1528
+ if (path === "/" && route2.default === true) {
1380
1529
  return true;
1381
1530
  }
1382
- } catch (e) {
1383
- if (logger6.enabledFor("debug")) {
1384
- logger6.debug(`reject: origin is malformed`);
1385
- }
1386
- rejectRequest(response);
1387
- return false;
1388
- }
1389
- if (responseHeaders.has("access-control-allow-origin")) {
1390
- logger6.trace(`skip: already contains "Access-Control-Allow-Origin"`);
1391
- return true;
1392
- }
1393
- const preFlightRequest = isPreFlightRequest(request);
1394
- if (config) {
1395
- return handleInternal(exchange, config, preFlightRequest);
1396
- }
1397
- if (preFlightRequest) {
1398
- rejectRequest(response);
1399
- return false;
1400
- }
1401
- return true;
1531
+ });
1532
+ return [route, path];
1402
1533
  }
1403
- function validateConfig(config) {
1404
- if (config) {
1405
- const headers2 = config.headers;
1406
- if (headers2?.allow && headers2.allow !== ALL) {
1407
- headers2.allow = headers2.allow.map((header) => header.toLowerCase());
1408
- }
1409
- const origins = config.origins;
1410
- if (origins?.allow && origins.allow !== ALL) {
1411
- origins.allow = origins.allow.map((origin) => {
1412
- if (typeof origin === "string") {
1413
- return origin.toLowerCase();
1414
- }
1415
- return origin;
1416
- });
1534
+
1535
+ // src/server/security/http-headers.ts
1536
+ var staticServerHttpHeadersWriter = (headers2) => {
1537
+ return async (exchange) => {
1538
+ let containsNoHeaders = true;
1539
+ const { response } = exchange;
1540
+ for (const name of headers2.keys()) {
1541
+ if (response.headers.has(name)) {
1542
+ containsNoHeaders = false;
1543
+ }
1417
1544
  }
1418
- return config;
1419
- }
1420
- }
1421
- var handler = (config) => {
1422
- validateConfig(config);
1423
- return async (ctx, next) => {
1424
- const isValid = processRequest(ctx, config);
1425
- if (!isValid || isPreFlightRequest(ctx.request)) {
1426
- await ctx.response.end();
1427
- } else {
1428
- await next();
1545
+ if (containsNoHeaders) {
1546
+ for (const [name, value] of headers2) {
1547
+ response.headers.set(name, value);
1548
+ }
1429
1549
  }
1430
1550
  };
1431
1551
  };
1432
- var cors_default = (config) => [handler(config)];
1433
- var logger6 = getLogger("cors");
1434
- function rejectRequest(response) {
1435
- response.statusCode = 403;
1436
- }
1437
- function handleInternal(exchange, config, preFlightRequest) {
1438
- const { request, response } = exchange;
1439
- const responseHeaders = response.headers;
1440
- const requestOrigin = request.headers.one("origin");
1441
- const allowOrigin = checkOrigin(config, requestOrigin);
1442
- if (allowOrigin === void 0) {
1443
- if (logger6.enabledFor("debug")) {
1444
- logger6.debug(`reject: '${requestOrigin}' origin is not allowed`);
1445
- }
1446
- rejectRequest(response);
1447
- return false;
1448
- }
1449
- const requestMethod = getMethodToUse(request, preFlightRequest);
1450
- const allowMethods = checkMethods(config, requestMethod);
1451
- if (allowMethods === void 0) {
1452
- if (logger6.enabledFor("debug")) {
1453
- logger6.debug(`reject: HTTP '${requestMethod}' is not allowed`);
1454
- }
1455
- rejectRequest(response);
1456
- return false;
1457
- }
1458
- const requestHeaders = getHeadersToUse(request, preFlightRequest);
1459
- const allowHeaders = checkHeaders(config, requestHeaders);
1460
- if (preFlightRequest && allowHeaders === void 0) {
1461
- if (logger6.enabledFor("debug")) {
1462
- logger6.debug(`reject: headers '${requestHeaders}' are not allowed`);
1463
- }
1464
- rejectRequest(response);
1465
- return false;
1466
- }
1467
- responseHeaders.set("access-control-allow-origin", allowOrigin);
1468
- if (preFlightRequest) {
1469
- responseHeaders.set("access-control-allow-methods", allowMethods.join(","));
1470
- }
1471
- if (preFlightRequest && allowHeaders !== void 0 && allowHeaders.length > 0) {
1472
- responseHeaders.set("access-control-allow-headers", allowHeaders.join(", "));
1473
- }
1474
- const exposeHeaders = config.headers?.expose;
1475
- if (exposeHeaders && exposeHeaders.length > 0) {
1476
- responseHeaders.set("access-control-expose-headers", exposeHeaders.join(", "));
1477
- }
1478
- if (config.credentials?.allow) {
1479
- responseHeaders.set("access-control-allow-credentials", "true");
1480
- }
1481
- if (config.privateNetwork?.allow && request.headers.one("access-control-request-private-network") === "true") {
1482
- responseHeaders.set("access-control-allow-private-network", "true");
1483
- }
1484
- return true;
1485
- }
1486
- var ALL = "*";
1487
- var DEFAULT_METHODS = ["GET", "HEAD"];
1488
- function validateAllowCredentials(config) {
1489
- if (config.credentials?.allow === true && config.origins?.allow === ALL) {
1490
- throw new Error(`when credentials.allow is true origins.allow cannot be "*"`);
1491
- }
1492
- }
1493
- function validateAllowPrivateNetwork(config) {
1494
- if (config.privateNetwork?.allow === true && config.origins?.allow === ALL) {
1495
- throw new Error(`when privateNetwork.allow is true origins.allow cannot be "*"`);
1496
- }
1497
- }
1498
- function checkOrigin(config, origin) {
1499
- if (origin) {
1500
- const allowedOrigins = config.origins?.allow;
1501
- if (allowedOrigins) {
1502
- if (allowedOrigins === ALL) {
1503
- validateAllowCredentials(config);
1504
- validateAllowPrivateNetwork(config);
1505
- return ALL;
1506
- }
1507
- const originToCheck = trimTrailingSlash(origin.toLowerCase());
1508
- for (const allowedOrigin of allowedOrigins) {
1509
- if (allowedOrigin === ALL || import_gateway6.IOGateway.Filtering.valueMatches(allowedOrigin, originToCheck)) {
1510
- return origin;
1511
- }
1512
- }
1513
- }
1514
- }
1515
- }
1516
- function checkMethods(config, requestMethod) {
1517
- if (requestMethod) {
1518
- const allowedMethods = config.methods?.allow ?? DEFAULT_METHODS;
1519
- if (allowedMethods === ALL) {
1520
- return [requestMethod];
1521
- }
1522
- if (import_gateway6.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
1523
- return allowedMethods;
1524
- }
1525
- }
1526
- }
1527
- function checkHeaders(config, requestHeaders) {
1528
- if (requestHeaders === void 0) {
1529
- return;
1530
- }
1531
- if (requestHeaders.length == 0) {
1532
- return [];
1533
- }
1534
- const allowedHeaders = config.headers?.allow;
1535
- if (allowedHeaders === void 0) {
1536
- return;
1537
- }
1538
- const allowAnyHeader = allowedHeaders === ALL;
1539
- const result = [];
1540
- for (const requestHeader of requestHeaders) {
1541
- const value = requestHeader?.trim();
1542
- if (value) {
1543
- if (allowAnyHeader) {
1544
- result.push(value);
1545
- } else {
1546
- for (const allowedHeader of allowedHeaders) {
1547
- if (value.toLowerCase() == allowedHeader) {
1548
- result.push(value);
1549
- break;
1550
- }
1551
- }
1552
- }
1553
- }
1554
- }
1555
- if (result.length > 0) {
1556
- return result;
1557
- }
1558
- }
1559
- function trimTrailingSlash(origin) {
1560
- return origin.endsWith("/") ? origin.slice(0, -1) : origin;
1561
- }
1562
- function getMethodToUse(request, isPreFlight) {
1563
- return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
1564
- }
1565
- function getHeadersToUse(request, isPreFlight) {
1566
- const headers2 = request.headers;
1567
- return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
1568
- }
1569
-
1570
- // src/server/server-header.ts
1571
- var serverHeader = (server) => {
1572
- return async ({ response }, next) => {
1573
- if (!response.headers.has("server")) {
1574
- response.headers.set("Server", server);
1575
- }
1576
- await next();
1577
- };
1578
- };
1579
- var server_header_default = (server = "gateway-server") => serverHeader(server);
1580
-
1581
- // src/server/security/http-headers.ts
1582
- var staticServerHttpHeadersWriter = (headers2) => {
1583
- return async (exchange) => {
1584
- let containsNoHeaders = true;
1585
- const { response } = exchange;
1586
- for (const name of headers2.keys()) {
1587
- if (response.headers.has(name)) {
1588
- containsNoHeaders = false;
1589
- }
1590
- }
1591
- if (containsNoHeaders) {
1592
- for (const [name, value] of headers2) {
1593
- response.headers.set(name, value);
1594
- }
1595
- }
1596
- };
1597
- };
1598
- var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1599
- new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
1600
- );
1601
- var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1602
- new MapHttpHeaders().add("x-content-type-options", "nosniff")
1603
- );
1604
- var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
1605
- let headerValue = `max-age=${maxAgeInSeconds}`;
1606
- if (includeSubDomains) {
1607
- headerValue += " ; includeSubDomains";
1552
+ var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1553
+ new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
1554
+ );
1555
+ var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1556
+ new MapHttpHeaders().add("x-content-type-options", "nosniff")
1557
+ );
1558
+ var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
1559
+ let headerValue = `max-age=${maxAgeInSeconds}`;
1560
+ if (includeSubDomains) {
1561
+ headerValue += " ; includeSubDomains";
1608
1562
  }
1609
1563
  if (preload) {
1610
1564
  headerValue += " ; preload";
@@ -1764,18 +1718,18 @@ var AuthorizationDecision = class {
1764
1718
  granted;
1765
1719
  };
1766
1720
  var DefaultAuthorizationManager = class {
1767
- check;
1721
+ #check;
1768
1722
  constructor(check) {
1769
- this.check = check;
1723
+ this.#check = check;
1770
1724
  }
1771
1725
  async verify(authentication, object) {
1772
- const decision = await this.check(authentication, object);
1726
+ const decision = await this.#check(authentication, object);
1773
1727
  if (!decision?.granted) {
1774
1728
  throw new AccessDeniedError("Access denied");
1775
1729
  }
1776
1730
  }
1777
1731
  async authorize(authentication, object) {
1778
- return await this.check(authentication, object);
1732
+ return await this.#check(authentication, object);
1779
1733
  }
1780
1734
  };
1781
1735
  var AuthenticationServiceError = class extends AuthenticationError {
@@ -1829,71 +1783,6 @@ var httpBasicAuthenticationConverter = (opts) => {
1829
1783
  };
1830
1784
  };
1831
1785
 
1832
- // src/server/util/matchers.ts
1833
- var or = (matchers) => {
1834
- return async (exchange) => {
1835
- for (const matcher of matchers) {
1836
- const result = await matcher(exchange);
1837
- if (result.match) {
1838
- return { match: true };
1839
- }
1840
- }
1841
- return { match: false };
1842
- };
1843
- };
1844
- var and = (matchers) => {
1845
- return async (exchange) => {
1846
- for (const matcher of matchers) {
1847
- const result = await matcher(exchange);
1848
- if (!result.match) {
1849
- return { match: false };
1850
- }
1851
- }
1852
- return { match: true };
1853
- };
1854
- };
1855
- var not = (matcher) => {
1856
- return async (exchange) => {
1857
- const result = await matcher(exchange);
1858
- return { match: !result.match };
1859
- };
1860
- };
1861
- var anyExchange = async (_exchange) => {
1862
- return { match: true };
1863
- };
1864
- var mediaType = (opts) => {
1865
- const shouldIgnore = (requestedMediaType) => {
1866
- if (opts.ignoredMediaTypes !== void 0) {
1867
- for (const ignoredMediaType of opts.ignoredMediaTypes) {
1868
- if (requestedMediaType === ignoredMediaType || ignoredMediaType === "*/*") {
1869
- return true;
1870
- }
1871
- }
1872
- }
1873
- return false;
1874
- };
1875
- return async (exchange) => {
1876
- const request = exchange.request;
1877
- let requestMediaTypes;
1878
- try {
1879
- requestMediaTypes = request.headers.list("accept");
1880
- } catch (e) {
1881
- return { match: false };
1882
- }
1883
- for (const requestedMediaType of requestMediaTypes) {
1884
- if (shouldIgnore(requestedMediaType)) {
1885
- continue;
1886
- }
1887
- for (const mediaType2 of opts.mediaTypes) {
1888
- if (requestedMediaType.startsWith(mediaType2)) {
1889
- return { match: true };
1890
- }
1891
- }
1892
- }
1893
- return { match: false };
1894
- };
1895
- };
1896
-
1897
1786
  // src/server/security/security-context.ts
1898
1787
  var import_node_async_hooks = require("node:async_hooks");
1899
1788
  var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
@@ -2207,6 +2096,7 @@ var httpStatusEntryPoint = (opts) => {
2207
2096
  };
2208
2097
 
2209
2098
  // src/server/security/delegating-entry-point.ts
2099
+ var logger6 = getLogger("auth.entry-point");
2210
2100
  var delegatingEntryPoint = (opts) => {
2211
2101
  const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
2212
2102
  response.statusCode = 401;
@@ -2214,11 +2104,20 @@ var delegatingEntryPoint = (opts) => {
2214
2104
  });
2215
2105
  return async (exchange, error) => {
2216
2106
  for (const [matcher, entryPoint] of opts.entryPoints) {
2107
+ if (logger6.enabledFor("debug")) {
2108
+ logger6.debug(`trying to match using: ${matcher}`);
2109
+ }
2217
2110
  const match = await matcher(exchange);
2218
2111
  if (match.match) {
2112
+ if (logger6.enabledFor("debug")) {
2113
+ logger6.debug(`match found. using default entry point ${entryPoint}`);
2114
+ }
2219
2115
  return entryPoint(exchange, error);
2220
2116
  }
2221
2117
  }
2118
+ if (logger6.enabledFor("debug")) {
2119
+ logger6.debug(`no match found. using default entry point ${defaultEntryPoint}`);
2120
+ }
2222
2121
  return defaultEntryPoint(exchange, error);
2223
2122
  };
2224
2123
  };
@@ -2226,8 +2125,8 @@ var delegatingEntryPoint = (opts) => {
2226
2125
  // src/server/security/delegating-success-handler.ts
2227
2126
  var delegatingSuccessHandler = (handlers) => {
2228
2127
  return async ({ exchange, next }, authentication) => {
2229
- for (const handler2 of handlers) {
2230
- await handler2({ exchange, next }, authentication);
2128
+ for (const handler of handlers) {
2129
+ await handler({ exchange, next }, authentication);
2231
2130
  }
2232
2131
  };
2233
2132
  };
@@ -2321,14 +2220,21 @@ var errorFilter = (opts) => {
2321
2220
  };
2322
2221
 
2323
2222
  // src/server/security/authorization-filter.ts
2223
+ var logger7 = getLogger("security.auth");
2324
2224
  function authorizationFilter(opts) {
2325
2225
  const { manager, storage } = opts;
2326
2226
  return async (exchange, next) => {
2327
2227
  const promise = AsyncStorageSecurityContextHolder.getContext(storage).then((c) => c?.authentication);
2328
2228
  try {
2329
2229
  await manager.verify(promise, exchange);
2230
+ if (logger7.enabledFor("debug")) {
2231
+ logger7.debug("authorization successful");
2232
+ }
2330
2233
  } catch (error) {
2331
2234
  if (error instanceof AccessDeniedError) {
2235
+ if (logger7.enabledFor("debug")) {
2236
+ logger7.debug(`authorization failed: ${error.message}`);
2237
+ }
2332
2238
  }
2333
2239
  throw error;
2334
2240
  }
@@ -2337,12 +2243,14 @@ function authorizationFilter(opts) {
2337
2243
  }
2338
2244
 
2339
2245
  // src/server/security/delegating-authorization-manager.ts
2246
+ var logger8 = getLogger("auth");
2340
2247
  function delegatingAuthorizationManager(opts) {
2341
2248
  const check = async (authentication, exchange) => {
2342
2249
  let decision;
2343
2250
  for (const [matcher, manager] of opts.mappings) {
2344
2251
  if ((await matcher(exchange))?.match) {
2345
- const checkResult = await manager.check(authentication, { exchange });
2252
+ logger8.debug(`checking authorization on '${exchange.path}' using [${matcher}, ${manager}]`);
2253
+ const checkResult = await manager.authorize(authentication, { exchange });
2346
2254
  if (checkResult !== void 0) {
2347
2255
  decision = checkResult;
2348
2256
  break;
@@ -2355,6 +2263,308 @@ function delegatingAuthorizationManager(opts) {
2355
2263
  return new DefaultAuthorizationManager(check);
2356
2264
  }
2357
2265
 
2266
+ // src/server/cors.ts
2267
+ var import_gateway6 = require("@interopio/gateway");
2268
+ function isSameOrigin(request) {
2269
+ const origin = request.headers.one("origin");
2270
+ if (origin === void 0) {
2271
+ return true;
2272
+ }
2273
+ const url = request.URL;
2274
+ const actualProtocol = url.protocol;
2275
+ const actualHost = url.host;
2276
+ const originUrl = URL.parse(origin);
2277
+ const originHost = originUrl?.host;
2278
+ const originProtocol = originUrl?.protocol;
2279
+ return actualProtocol === originProtocol && actualHost === originHost;
2280
+ }
2281
+ function isCorsRequest(request) {
2282
+ return request.headers.has("origin") && !isSameOrigin(request);
2283
+ }
2284
+ function isPreFlightRequest(request) {
2285
+ return request.method === "OPTIONS" && request.headers.has("origin") && request.headers.has("access-control-request-method");
2286
+ }
2287
+ var VARY_HEADERS = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"];
2288
+ var processRequest = (exchange, config) => {
2289
+ const { request, response } = exchange;
2290
+ const responseHeaders = response.headers;
2291
+ if (!responseHeaders.has("Vary")) {
2292
+ responseHeaders.set("Vary", VARY_HEADERS.join(", "));
2293
+ } else {
2294
+ const varyHeaders = responseHeaders.list("Vary");
2295
+ for (const header of VARY_HEADERS) {
2296
+ if (!varyHeaders.find((h) => h === header)) {
2297
+ varyHeaders.push(header);
2298
+ }
2299
+ }
2300
+ responseHeaders.set("Vary", varyHeaders.join(", "));
2301
+ }
2302
+ try {
2303
+ if (!isCorsRequest(request)) {
2304
+ return true;
2305
+ }
2306
+ } catch (e) {
2307
+ if (logger9.enabledFor("debug")) {
2308
+ logger9.debug(`reject: origin is malformed`);
2309
+ }
2310
+ rejectRequest(response);
2311
+ return false;
2312
+ }
2313
+ if (responseHeaders.has("access-control-allow-origin")) {
2314
+ logger9.trace(`skip: already contains "Access-Control-Allow-Origin"`);
2315
+ return true;
2316
+ }
2317
+ const preFlightRequest = isPreFlightRequest(request);
2318
+ if (config) {
2319
+ return handleInternal(exchange, config, preFlightRequest);
2320
+ }
2321
+ if (preFlightRequest) {
2322
+ rejectRequest(response);
2323
+ return false;
2324
+ }
2325
+ return true;
2326
+ };
2327
+ var DEFAULT_PERMIT_ALL = ["*"];
2328
+ var DEFAULT_PERMIT_METHODS = ["GET", "HEAD", "POST"];
2329
+ var PERMIT_DEFAULT_CONFIG = {
2330
+ allowOrigins: DEFAULT_PERMIT_ALL,
2331
+ allowMethods: DEFAULT_PERMIT_METHODS,
2332
+ allowHeaders: DEFAULT_PERMIT_ALL,
2333
+ maxAge: 1800
2334
+ // 30 minutes
2335
+ };
2336
+ function validateCorsConfig(config) {
2337
+ if (config) {
2338
+ const allowHeaders = config.allowHeaders;
2339
+ if (allowHeaders && allowHeaders !== ALL) {
2340
+ config = {
2341
+ ...config,
2342
+ allowHeaders: allowHeaders.map((header) => header.toLowerCase())
2343
+ };
2344
+ }
2345
+ const allowOrigins = config.allowOrigins;
2346
+ if (allowOrigins) {
2347
+ if (allowOrigins === "*") {
2348
+ validateAllowCredentials(config);
2349
+ validateAllowPrivateNetwork(config);
2350
+ } else {
2351
+ config = {
2352
+ ...config,
2353
+ allowOrigins: allowOrigins.map((origin) => {
2354
+ if (typeof origin === "string" && origin !== ALL) {
2355
+ origin = import_gateway6.IOGateway.Filtering.regexify(origin);
2356
+ if (typeof origin === "string") {
2357
+ return trimTrailingSlash(origin).toLowerCase();
2358
+ }
2359
+ }
2360
+ return origin;
2361
+ })
2362
+ };
2363
+ }
2364
+ }
2365
+ return config;
2366
+ }
2367
+ }
2368
+ function combine(source, other) {
2369
+ if (other === void 0) {
2370
+ return source !== void 0 ? source === ALL ? [ALL] : source : [];
2371
+ }
2372
+ if (source === void 0) {
2373
+ return other === ALL ? [ALL] : other;
2374
+ }
2375
+ if (source == DEFAULT_PERMIT_ALL || source === DEFAULT_PERMIT_METHODS) {
2376
+ return other === ALL ? [ALL] : other;
2377
+ }
2378
+ if (other == DEFAULT_PERMIT_ALL || other === DEFAULT_PERMIT_METHODS) {
2379
+ return source === ALL ? [ALL] : source;
2380
+ }
2381
+ if (source === ALL || source.includes(ALL) || other === ALL || other.includes(ALL)) {
2382
+ return [ALL];
2383
+ }
2384
+ const combined = /* @__PURE__ */ new Set();
2385
+ source.forEach((v) => combined.add(v));
2386
+ other.forEach((v) => combined.add(v));
2387
+ return Array.from(combined);
2388
+ }
2389
+ var combineCorsConfig = (source, other) => {
2390
+ if (other === void 0) {
2391
+ return source;
2392
+ }
2393
+ const config = {
2394
+ allowOrigins: combine(source.allowOrigins, other?.allowOrigins),
2395
+ allowMethods: combine(source.allowMethods, other?.allowMethods),
2396
+ allowHeaders: combine(source.allowHeaders, other?.allowHeaders),
2397
+ exposeHeaders: combine(source.exposeHeaders, other?.exposeHeaders),
2398
+ allowCredentials: other?.allowCredentials ?? source.allowCredentials,
2399
+ allowPrivateNetwork: other?.allowPrivateNetwork ?? source.allowPrivateNetwork,
2400
+ maxAge: other?.maxAge ?? source.maxAge
2401
+ };
2402
+ return config;
2403
+ };
2404
+ var corsFilter = (opts) => {
2405
+ const source = opts.corsConfigSource;
2406
+ const processor = opts.corsProcessor ?? processRequest;
2407
+ return async (ctx, next) => {
2408
+ const config = await source(ctx);
2409
+ const isValid = processor(ctx, config);
2410
+ if (!isValid || isPreFlightRequest(ctx.request)) {
2411
+ return;
2412
+ } else {
2413
+ await next();
2414
+ }
2415
+ };
2416
+ };
2417
+ var cors_default = corsFilter;
2418
+ var logger9 = getLogger("cors");
2419
+ function rejectRequest(response) {
2420
+ response.statusCode = 403;
2421
+ }
2422
+ function handleInternal(exchange, config, preFlightRequest) {
2423
+ const { request, response } = exchange;
2424
+ const responseHeaders = response.headers;
2425
+ const requestOrigin = request.headers.one("origin");
2426
+ const allowOrigin = checkOrigin(config, requestOrigin);
2427
+ if (allowOrigin === void 0) {
2428
+ if (logger9.enabledFor("debug")) {
2429
+ logger9.debug(`reject: '${requestOrigin}' origin is not allowed`);
2430
+ }
2431
+ rejectRequest(response);
2432
+ return false;
2433
+ }
2434
+ const requestMethod = getMethodToUse(request, preFlightRequest);
2435
+ const allowMethods = checkMethods(config, requestMethod);
2436
+ if (allowMethods === void 0) {
2437
+ if (logger9.enabledFor("debug")) {
2438
+ logger9.debug(`reject: HTTP '${requestMethod}' is not allowed`);
2439
+ }
2440
+ rejectRequest(response);
2441
+ return false;
2442
+ }
2443
+ const requestHeaders = getHeadersToUse(request, preFlightRequest);
2444
+ const allowHeaders = checkHeaders(config, requestHeaders);
2445
+ if (preFlightRequest && allowHeaders === void 0) {
2446
+ if (logger9.enabledFor("debug")) {
2447
+ logger9.debug(`reject: headers '${requestHeaders}' are not allowed`);
2448
+ }
2449
+ rejectRequest(response);
2450
+ return false;
2451
+ }
2452
+ responseHeaders.set("Access-Control-Allow-Origin", allowOrigin);
2453
+ if (preFlightRequest) {
2454
+ responseHeaders.set("Access-Control-Allow-Methods", allowMethods.join(","));
2455
+ }
2456
+ if (preFlightRequest && allowHeaders !== void 0 && allowHeaders.length > 0) {
2457
+ responseHeaders.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
2458
+ }
2459
+ const exposeHeaders = config.exposeHeaders;
2460
+ if (exposeHeaders && exposeHeaders.length > 0) {
2461
+ responseHeaders.set("Access-Control-Expose-Headers", exposeHeaders.join(", "));
2462
+ }
2463
+ if (config.allowCredentials) {
2464
+ responseHeaders.set("Access-Control-Allow-Credentials", "true");
2465
+ }
2466
+ if (config.allowPrivateNetwork && request.headers.one("access-control-request-private-network") === "true") {
2467
+ responseHeaders.set("Access-Control-Allow-Private-Network", "true");
2468
+ }
2469
+ if (preFlightRequest && config.maxAge !== void 0) {
2470
+ responseHeaders.set("Access-Control-Max-Age", config.maxAge.toString());
2471
+ }
2472
+ return true;
2473
+ }
2474
+ var ALL = "*";
2475
+ var DEFAULT_METHODS = ["GET", "HEAD"];
2476
+ function validateAllowCredentials(config) {
2477
+ if (config.allowCredentials === true && config.allowOrigins === ALL) {
2478
+ throw new Error(`when allowCredentials is true allowOrigins cannot be "*"`);
2479
+ }
2480
+ }
2481
+ function validateAllowPrivateNetwork(config) {
2482
+ if (config.allowPrivateNetwork === true && config.allowOrigins === ALL) {
2483
+ throw new Error(`when allowPrivateNetwork is true allowOrigins cannot be "*"`);
2484
+ }
2485
+ }
2486
+ function checkOrigin(config, origin) {
2487
+ if (origin) {
2488
+ const allowedOrigins = config.allowOrigins;
2489
+ if (allowedOrigins) {
2490
+ if (allowedOrigins === ALL) {
2491
+ validateAllowCredentials(config);
2492
+ validateAllowPrivateNetwork(config);
2493
+ return ALL;
2494
+ }
2495
+ const originToCheck = trimTrailingSlash(origin.toLowerCase());
2496
+ for (const allowedOrigin of allowedOrigins) {
2497
+ if (allowedOrigin === ALL || import_gateway6.IOGateway.Filtering.valueMatches(allowedOrigin, originToCheck)) {
2498
+ return origin;
2499
+ }
2500
+ }
2501
+ }
2502
+ }
2503
+ }
2504
+ function checkMethods(config, requestMethod) {
2505
+ if (requestMethod) {
2506
+ const allowedMethods = config.allowMethods ?? DEFAULT_METHODS;
2507
+ if (allowedMethods === ALL) {
2508
+ return [requestMethod];
2509
+ }
2510
+ if (import_gateway6.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
2511
+ return allowedMethods;
2512
+ }
2513
+ }
2514
+ }
2515
+ function checkHeaders(config, requestHeaders) {
2516
+ if (requestHeaders === void 0) {
2517
+ return;
2518
+ }
2519
+ if (requestHeaders.length == 0) {
2520
+ return [];
2521
+ }
2522
+ const allowedHeaders = config.allowHeaders;
2523
+ if (allowedHeaders === void 0) {
2524
+ return;
2525
+ }
2526
+ const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
2527
+ const result = [];
2528
+ for (const requestHeader of requestHeaders) {
2529
+ const value = requestHeader?.trim();
2530
+ if (value) {
2531
+ if (allowAnyHeader) {
2532
+ result.push(value);
2533
+ } else {
2534
+ for (const allowedHeader of allowedHeaders) {
2535
+ if (value.toLowerCase() === allowedHeader) {
2536
+ result.push(value);
2537
+ break;
2538
+ }
2539
+ }
2540
+ }
2541
+ }
2542
+ }
2543
+ if (result.length > 0) {
2544
+ return result;
2545
+ }
2546
+ }
2547
+ function trimTrailingSlash(origin) {
2548
+ return origin.endsWith("/") ? origin.slice(0, -1) : origin;
2549
+ }
2550
+ function getMethodToUse(request, isPreFlight) {
2551
+ return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
2552
+ }
2553
+ function getHeadersToUse(request, isPreFlight) {
2554
+ const headers2 = request.headers;
2555
+ return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
2556
+ }
2557
+ var matchingCorsConfigSource = (opts) => {
2558
+ return async (exchange) => {
2559
+ for (const [matcher, config] of opts.mappings) {
2560
+ if ((await matcher(exchange)).match) {
2561
+ logger9.debug(`resolved cors config on '${exchange.path}' using ${matcher}: ${JSON.stringify(config)}`);
2562
+ return config;
2563
+ }
2564
+ }
2565
+ };
2566
+ };
2567
+
2358
2568
  // src/server/security/config.ts
2359
2569
  var filterOrder = {
2360
2570
  first: Number.MAX_SAFE_INTEGER,
@@ -2368,7 +2578,7 @@ var filterOrder = {
2368
2578
  last: Number.MAX_SAFE_INTEGER
2369
2579
  };
2370
2580
  var filterOrderSymbol = Symbol.for("filterOrder");
2371
- var config_default = (config, storage) => {
2581
+ var config_default = (config, context) => {
2372
2582
  const middleware = [];
2373
2583
  class ServerHttpSecurity {
2374
2584
  #authenticationEntryPoint;
@@ -2392,6 +2602,11 @@ var config_default = (config, storage) => {
2392
2602
  writer[filterOrderSymbol] = filterOrder.http_headers;
2393
2603
  middleware.push(writer);
2394
2604
  }
2605
+ if (config.cors?.disabled !== true && context.corsConfigSource !== void 0) {
2606
+ const filter = cors_default({ corsConfigSource: context.corsConfigSource });
2607
+ filter[filterOrderSymbol] = filterOrder.cors;
2608
+ middleware.push(filter);
2609
+ }
2395
2610
  if (config.basic !== void 0 && config.basic?.disabled !== true) {
2396
2611
  const username = config.basic.user?.name.toLowerCase();
2397
2612
  const password = config.basic.user?.password ?? "";
@@ -2410,7 +2625,7 @@ var config_default = (config, storage) => {
2410
2625
  }
2411
2626
  ];
2412
2627
  const filter = httpBasic({
2413
- storage,
2628
+ storage: context.storage,
2414
2629
  manager,
2415
2630
  defaultEntryPoints: this.#defaultEntryPoints,
2416
2631
  defaultSuccessHandlers
@@ -2420,20 +2635,43 @@ var config_default = (config, storage) => {
2420
2635
  }
2421
2636
  if (config.jwt !== void 0 && config.jwt.disabled !== true) {
2422
2637
  const verifier = (0, import_jwt.jwtVerifier)({
2423
- issuerBaseUri: config.jwt.issuerBaseUri,
2638
+ issuerBaseUri: config.jwt.issuerUri,
2424
2639
  issuer: config.jwt.issuer,
2425
2640
  audience: config.jwt.audience
2426
2641
  });
2427
2642
  const decoder = async (token) => {
2428
- const { payload } = await verifier(token);
2429
- return {
2430
- subject: payload.sub,
2431
- getClaimAsString(claim) {
2432
- return payload[claim];
2643
+ try {
2644
+ const { payload } = await verifier(token);
2645
+ return {
2646
+ subject: payload.sub,
2647
+ getClaimAsString(claim) {
2648
+ return payload[claim];
2649
+ }
2650
+ };
2651
+ } catch (e) {
2652
+ if (e instanceof import_jwt.JwtVerifyError) {
2653
+ throw new BadJwtError(e.message, { cause: e });
2433
2654
  }
2434
- };
2655
+ throw new JwtError("error occurred while attempting to decoding jwt", { cause: e });
2656
+ }
2435
2657
  };
2436
- const filter = resourceServer({ storage, jwt: { decoder } });
2658
+ const authenticationConverter = token_converter_default({ uriQueryParameter: true });
2659
+ const authenticationConverterMatcher = async (exchange) => {
2660
+ try {
2661
+ const a = await authenticationConverter(exchange);
2662
+ return { match: a !== void 0 };
2663
+ } catch (e) {
2664
+ return { match: false };
2665
+ }
2666
+ };
2667
+ const entryPoint = token_entry_point_default({});
2668
+ this.#defaultEntryPoints.push([authenticationConverterMatcher, entryPoint]);
2669
+ const filter = resourceServer({
2670
+ storage: context.storage,
2671
+ entryPoint,
2672
+ converter: authenticationConverter,
2673
+ jwt: { decoder }
2674
+ });
2437
2675
  filter[filterOrderSymbol] = filterOrder.authentication;
2438
2676
  middleware.push(filter);
2439
2677
  }
@@ -2455,10 +2693,12 @@ var config_default = (config, storage) => {
2455
2693
  serverMatcher = matcher;
2456
2694
  }
2457
2695
  let manager2;
2458
- if (access2.access === "permit-all") {
2696
+ if (access2.access === "permitted") {
2459
2697
  manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(true));
2460
- } else if (access2.access === "deny-all") {
2698
+ manager2.toString = () => "AuthorizationManager[permitted]";
2699
+ } else if (access2.access === "denied") {
2461
2700
  manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(false));
2701
+ manager2.toString = () => "AuthorizationManager[denied]";
2462
2702
  } else if (access2.access === "authenticated") {
2463
2703
  manager2 = new DefaultAuthorizationManager(async (p) => {
2464
2704
  const authentication = await p;
@@ -2467,6 +2707,7 @@ var config_default = (config, storage) => {
2467
2707
  }
2468
2708
  return new AuthorizationDecision(false);
2469
2709
  });
2710
+ manager2.toString = () => "AuthorizationManager[authenticated]";
2470
2711
  } else {
2471
2712
  throw new Error(`Unknown access type: ${JSON.stringify(access2)}`);
2472
2713
  }
@@ -2475,7 +2716,7 @@ var config_default = (config, storage) => {
2475
2716
  return delegatingAuthorizationManager({ mappings });
2476
2717
  };
2477
2718
  const manager = buildAuthorizationManager(config.authorize);
2478
- const filter = authorizationFilter({ manager, storage });
2719
+ const filter = authorizationFilter({ manager, storage: context.storage });
2479
2720
  filter[filterOrderSymbol] = filterOrder.authorization;
2480
2721
  middleware.push(filter);
2481
2722
  }
@@ -2491,8 +2732,79 @@ var config_default = (config, storage) => {
2491
2732
  return middleware;
2492
2733
  };
2493
2734
 
2735
+ // src/app/cors.ts
2736
+ function mockUpgradeExchange(path) {
2737
+ const request = new MockHttpRequest(path, "GET");
2738
+ request.headers.set("upgrade", "websocket");
2739
+ request["_req"] = { upgrade: true };
2740
+ return new DefaultWebExchange(request, new MockHttpResponse());
2741
+ }
2742
+ async function createCorsConfigSource(context) {
2743
+ const { sockets: routes3, cors } = context;
2744
+ const defaultCorsConfig = combineCorsConfig(PERMIT_DEFAULT_CONFIG, context.corsConfig);
2745
+ const validatedConfigs = [];
2746
+ for (const [path, route] of routes3) {
2747
+ let routeCorsConfig = defaultCorsConfig;
2748
+ const upgradeExchange = mockUpgradeExchange(path);
2749
+ for (const [matcher, config] of cors) {
2750
+ if ((await matcher(upgradeExchange)).match) {
2751
+ routeCorsConfig = combineCorsConfig(routeCorsConfig, config);
2752
+ }
2753
+ }
2754
+ routeCorsConfig = combineCorsConfig(routeCorsConfig, {
2755
+ allowOrigins: route.originFilters?.allow,
2756
+ allowMethods: ["GET", "CONNECT"],
2757
+ allowHeaders: ["upgrade", "connection", "origin", "sec-websocket-key", "sec-websocket-version", "sec-websocket-protocol", "sec-websocket-extensions"],
2758
+ exposeHeaders: ["sec-websocket-accept", "sec-websocket-protocol", "sec-websocket-extensions"],
2759
+ allowCredentials: route.authorize?.access !== "permitted"
2760
+ });
2761
+ validatedConfigs.push([and([upgradeMatcher, pattern(path)]), validateCorsConfig(routeCorsConfig)]);
2762
+ }
2763
+ for (const [matcher, config] of cors) {
2764
+ const routeCorsConfig = combineCorsConfig(defaultCorsConfig, config);
2765
+ validatedConfigs.push([matcher, validateCorsConfig(routeCorsConfig)]);
2766
+ }
2767
+ validatedConfigs.push([pattern(/\/api\/.*/), validateCorsConfig(defaultCorsConfig)]);
2768
+ return matchingCorsConfigSource({ mappings: validatedConfigs });
2769
+ }
2770
+
2771
+ // src/app/auth.ts
2772
+ function createSecurityConfig(context) {
2773
+ const authorize = [];
2774
+ for (const [path, route] of context.sockets) {
2775
+ const rule = route.authorize ?? { access: "authenticated" };
2776
+ let matcher = pattern(path, { method: "GET" });
2777
+ matcher = and([upgradeMatcher, matcher]);
2778
+ authorize.push([matcher, rule]);
2779
+ }
2780
+ authorize.push([pattern("/", { method: "GET" }), { access: "permitted" }]);
2781
+ authorize.push([pattern("/favicon.ico", { method: "GET" }), { access: "permitted" }]);
2782
+ authorize.push([pattern("/health", { method: "GET" }), { access: "permitted" }]);
2783
+ if (context.authorize.length > 0) {
2784
+ authorize.push(...context.authorize);
2785
+ }
2786
+ authorize.push(["any-exchange", { access: "authenticated" }]);
2787
+ return {
2788
+ authorize,
2789
+ basic: {
2790
+ disabled: context.authConfig?.type !== "basic",
2791
+ ...context.authConfig?.basic
2792
+ },
2793
+ jwt: {
2794
+ disabled: context.authConfig?.type !== "oauth2",
2795
+ ...context.authConfig?.oauth2?.jwt
2796
+ }
2797
+ };
2798
+ }
2799
+ async function httpSecurity(context) {
2800
+ const corsConfigSource = await createCorsConfigSource(context);
2801
+ const config = createSecurityConfig(context);
2802
+ const { storage } = context;
2803
+ return config_default(config, { storage, corsConfigSource });
2804
+ }
2805
+
2494
2806
  // src/server.ts
2495
- var logger7 = getLogger("app");
2807
+ var logger10 = getLogger("app");
2496
2808
  function secureContextOptions(ssl) {
2497
2809
  const options = {};
2498
2810
  if (ssl.key) options.key = (0, import_node_fs.readFileSync)(ssl.key);
@@ -2500,60 +2812,38 @@ function secureContextOptions(ssl) {
2500
2812
  if (ssl.ca) options.ca = (0, import_node_fs.readFileSync)(ssl.ca);
2501
2813
  return options;
2502
2814
  }
2503
- function createListener(storage, middleware, routes3, onSocketError) {
2815
+ async function createListener(middleware, context, onSocketError) {
2816
+ const storage = context.storage;
2817
+ const security = await httpSecurity(context);
2504
2818
  const listener = compose(
2505
- server_header_default(),
2506
- ...config_default({
2507
- authorize: [
2508
- [async (exchange) => {
2509
- return { match: exchange.path === "/health" && exchange.method === "GET" };
2510
- }, { access: "permit-all" }],
2511
- // ['any-exchange', {access: 'authenticated'}],
2512
- ["any-exchange", { access: "permit-all" }]
2513
- ],
2514
- basic: {
2515
- disabled: true,
2516
- realm: "Gateway Server"
2517
- },
2518
- jwt: {
2519
- disabled: true
2520
- }
2521
- }, storage),
2522
- ...cors_default({
2523
- origins: { allow: [/http:\/\/localhost(:\d+)?/, /file:\//] },
2524
- methods: { allow: ["GET", "HEAD", "POST", "DELETE"] },
2525
- headers: { allow: "*" },
2526
- credentials: { allow: true }
2527
- }),
2528
- ...middleware,
2529
- async ({ request, response }, next) => {
2530
- const path = request.path ?? "/";
2531
- const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
2532
- if (path === "/" && route2.default === true) {
2533
- return true;
2534
- }
2535
- });
2536
- if (route) {
2537
- if (request.method === "GET" && request._req["upgrade"] && request.headers.one("upgrade")?.toLowerCase() === "websocket") {
2819
+ server_header_default(context.serverHeader),
2820
+ ...security,
2821
+ // websocket upgrade handler
2822
+ async (exchange, next) => {
2823
+ const [route, path] = findSocketRoute(exchange, context);
2824
+ if (route !== void 0) {
2825
+ const { request, response } = exchange;
2826
+ const upgradeMatchResult = await upgradeMatcher(exchange);
2827
+ if ((request.method === "GET" || request.method === "CONNECT") && upgradeMatchResult.match) {
2538
2828
  const socket = request.socket;
2539
2829
  const host = request.host;
2540
2830
  const info = socketKey(request._req.socket);
2541
- if (route?.wss) {
2831
+ if (route.wss) {
2542
2832
  socket.removeListener("error", onSocketError);
2543
2833
  const wss = route.wss;
2544
2834
  if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
2545
- logger7.warn(`${info} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
2835
+ logger10.warn(`${info} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
2546
2836
  socket.destroy();
2547
2837
  return;
2548
2838
  }
2549
- const origin = request.headers["origin"];
2839
+ const origin = request.headers.one("origin");
2550
2840
  if (!acceptsOrigin(origin, route.originFilters)) {
2551
- logger7.info(`${info} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
2841
+ logger10.info(`${info} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
2552
2842
  socket.destroy();
2553
2843
  return;
2554
2844
  }
2555
- if (logger7.enabledFor("debug")) {
2556
- logger7.debug(`${info} accepted new ws connection request from ${host} on ${path}`);
2845
+ if (logger10.enabledFor("debug")) {
2846
+ logger10.debug(`${info} accepted new ws connection request from ${host} on ${path}`);
2557
2847
  }
2558
2848
  wss.handleUpgrade(request._req, socket, request._req["_upgradeHead"], (ws) => {
2559
2849
  response._res["_header"] = true;
@@ -2563,29 +2853,36 @@ function createListener(storage, middleware, routes3, onSocketError) {
2563
2853
  wss.emit("connection", ws, request._req);
2564
2854
  });
2565
2855
  } else {
2566
- logger7.warn(`${info} rejected upgrade request from ${host} on ${path}`);
2856
+ logger10.warn(`${info} rejected upgrade request from ${host} on ${path}`);
2567
2857
  socket.destroy();
2568
2858
  }
2569
2859
  } else {
2570
- if (logger7.enabledFor("debug")) {
2571
- logger7.debug(`rejecting request for ${path} with method ${request.method} from ${request.socket.remoteAddress}:${request.socket.remotePort} with headers: ${JSON.stringify(request._req.rawHeaders)}`);
2860
+ if (route.default) {
2861
+ await next();
2862
+ return;
2863
+ }
2864
+ if (logger10.enabledFor("debug")) {
2865
+ logger10.debug(`rejecting request for ${path} with method ${request.method} from ${request.socket.remoteAddress}:${request.socket.remotePort} with headers: ${JSON.stringify(request._req.rawHeaders)}`);
2572
2866
  }
2573
2867
  response.statusCode = 426;
2574
- response._res.appendHeader("Upgrade", "websocket").appendHeader("Connection", "Upgrade").appendHeader("Content-Type", "text/plain");
2868
+ response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
2575
2869
  await response.end(`This service [${request.path}] requires use of the websocket protocol.`);
2576
2870
  }
2577
2871
  } else {
2578
2872
  await next();
2579
2873
  }
2580
2874
  },
2875
+ ...middleware,
2876
+ // helth check
2581
2877
  async ({ request, response }, next) => {
2582
2878
  if (request.method === "GET" && request.path === "/health") {
2583
2879
  response.statusCode = 200;
2584
- response._res.end(import_node_http.default.STATUS_CODES[200]);
2880
+ await response.end(import_node_http.default.STATUS_CODES[200]);
2585
2881
  } else {
2586
2882
  await next();
2587
2883
  }
2588
2884
  },
2885
+ // home page
2589
2886
  async ({ request, response }, next) => {
2590
2887
  if (request.method === "GET" && request.path === "/") {
2591
2888
  await response.end(`io.Gateway Server`);
@@ -2593,7 +2890,8 @@ function createListener(storage, middleware, routes3, onSocketError) {
2593
2890
  await next();
2594
2891
  }
2595
2892
  },
2596
- async ({ request, response }, _next) => {
2893
+ // not found
2894
+ async ({ response }, _next) => {
2597
2895
  response.statusCode = 404;
2598
2896
  await response.end(import_node_http.default.STATUS_CODES[404]);
2599
2897
  }
@@ -2602,17 +2900,17 @@ function createListener(storage, middleware, routes3, onSocketError) {
2602
2900
  request.socket.addListener("error", onSocketError);
2603
2901
  const exchange = new DefaultWebExchange(new HttpServerRequest(request), new HttpServerResponse(response));
2604
2902
  return storage.run({ exchange }, async () => {
2605
- if (logger7.enabledFor("debug")) {
2903
+ if (logger10.enabledFor("debug")) {
2606
2904
  const socket = exchange.request._req.socket;
2607
- if (logger7.enabledFor("debug")) {
2608
- logger7.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
2905
+ if (logger10.enabledFor("debug")) {
2906
+ logger10.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
2609
2907
  }
2610
2908
  }
2611
2909
  try {
2612
2910
  return await listener(exchange);
2613
2911
  } catch (e) {
2614
- if (logger7.enabledFor("warn")) {
2615
- logger7.warn(`error processing request for ${exchange.path}`, e);
2912
+ if (logger10.enabledFor("warn")) {
2913
+ logger10.warn(`error processing request for ${exchange.path}`, e);
2616
2914
  }
2617
2915
  } finally {
2618
2916
  await exchange.response.end();
@@ -2647,65 +2945,75 @@ function regexAwareReplacer(_key, value) {
2647
2945
  }
2648
2946
  var Factory = async (options) => {
2649
2947
  const ssl = options.ssl;
2650
- const createServer = ssl ? (options2, handler2) => import_node_https.default.createServer({ ...options2, ...secureContextOptions(ssl) }, handler2) : (options2, handler2) => import_node_http.default.createServer(options2, handler2);
2948
+ const createServer = ssl ? (options2, handler) => import_node_https.default.createServer({ ...options2, ...secureContextOptions(ssl) }, handler) : (options2, handler) => import_node_http.default.createServer(options2, handler);
2651
2949
  const monitor = memoryMonitor(options.memory);
2652
2950
  const middleware = [];
2653
- const routes3 = /* @__PURE__ */ new Map();
2951
+ const context = {
2952
+ corsConfig: options.cors,
2953
+ cors: [],
2954
+ authConfig: options.auth,
2955
+ authorize: [],
2956
+ storage: new import_node_async_hooks2.AsyncLocalStorage(),
2957
+ sockets: /* @__PURE__ */ new Map()
2958
+ };
2654
2959
  const gw = import_gateway7.IOGateway.Factory({ ...options.gateway });
2655
2960
  if (options.gateway) {
2656
2961
  const config = options.gateway;
2657
- routes3.set(config.route ?? "/", {
2962
+ const route = config.route ?? "/";
2963
+ context.sockets.set(route, {
2658
2964
  default: config.route === void 0,
2659
2965
  ping: options.gateway.ping,
2660
2966
  factory: core_default.bind(gw),
2661
2967
  maxConnections: config.limits?.max_connections,
2968
+ authorize: config.authorize,
2662
2969
  originFilters: regexifyOriginFilters(config.origins)
2663
2970
  });
2664
2971
  }
2665
2972
  if (options.mesh) {
2666
2973
  const connections = new InMemoryNodeConnections(options.mesh.timeout ?? 6e4);
2667
- middleware.push(...routes_default(connections));
2974
+ const authorize = options.mesh.authorize;
2975
+ middleware.push(...routes_default(connections, context, authorize));
2668
2976
  const ping = options.mesh.ping ?? 3e4;
2669
- routes3.set("/broker", { factory: core_default2, ping });
2670
- routes3.set("/cluster", { factory: core_default4, ping });
2671
- routes3.set("/relays", { factory: core_default3, ping });
2977
+ const originFilters = regexifyOriginFilters(options.mesh.origins);
2978
+ context.sockets.set("/broker", { factory: core_default2, ping, originFilters, authorize });
2979
+ context.sockets.set("/cluster", { factory: core_default4, ping, originFilters, authorize });
2980
+ context.sockets.set("/relays", { factory: core_default3, ping, originFilters, authorize });
2672
2981
  }
2673
2982
  if (options.metrics) {
2674
- middleware.push(...await routes_default2(options.metrics));
2983
+ middleware.push(...await routes_default2(options.metrics, context));
2675
2984
  }
2676
2985
  const ports = portRange(options.port ?? 0);
2677
2986
  const host = options.host;
2678
- const storage = new import_node_async_hooks2.AsyncLocalStorage();
2987
+ const onSocketError = (err) => logger10.error(`socket error: ${err}`, err);
2988
+ const listener = await createListener(middleware, context, onSocketError);
2679
2989
  const serverP = new Promise((resolve, reject) => {
2680
- const onSocketError = (err) => logger7.error(`socket error: ${err}`, err);
2681
- const listener = createListener(storage, middleware, routes3, onSocketError);
2682
2990
  const server2 = createServer({}, listener);
2683
2991
  server2.on("error", (e) => {
2684
2992
  if (e["code"] === "EADDRINUSE") {
2685
- logger7.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
2993
+ logger10.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
2686
2994
  const { value: port } = ports.next();
2687
2995
  if (port) {
2688
- logger7.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
2996
+ logger10.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
2689
2997
  server2.close();
2690
2998
  server2.listen(port, host);
2691
2999
  } else {
2692
- logger7.warn(`all configured port(s) ${options.port} are in use. closing...`);
3000
+ logger10.warn(`all configured port(s) ${options.port} are in use. closing...`);
2693
3001
  server2.close();
2694
3002
  reject(e);
2695
3003
  }
2696
3004
  } else {
2697
- logger7.error(`server error: ${e.message}`, e);
3005
+ logger10.error(`server error: ${e.message}`, e);
2698
3006
  reject(e);
2699
3007
  }
2700
3008
  });
2701
3009
  server2.on("listening", async () => {
2702
3010
  const info = server2.address();
2703
- for (const [path, route] of routes3) {
3011
+ for (const [path, route] of context.sockets) {
2704
3012
  try {
2705
- logger7.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
3013
+ logger10.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
2706
3014
  const wss = new import_ws.WebSocketServer({ noServer: true });
2707
3015
  const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info.port}${path}`;
2708
- const handler2 = await route.factory({ endpoint, wss, storage });
3016
+ const handler = await route.factory({ endpoint, wss, storage: context.storage });
2709
3017
  const pingInterval = route.ping;
2710
3018
  if (pingInterval) {
2711
3019
  const pingIntervalId = setInterval(() => {
@@ -2722,12 +3030,12 @@ var Factory = async (options) => {
2722
3030
  });
2723
3031
  }
2724
3032
  route.wss = wss;
2725
- route.close = handler2.close?.bind(handler2);
3033
+ route.close = handler.close?.bind(handler);
2726
3034
  } catch (e) {
2727
- logger7.warn(`failed to init route ${path}`, e);
3035
+ logger10.warn(`failed to init route ${path}`, e);
2728
3036
  }
2729
3037
  }
2730
- logger7.info(`http server listening on ${info.address}:${info.port}`);
3038
+ logger10.info(`http server listening on ${info.address}:${info.port}`);
2731
3039
  resolve(server2);
2732
3040
  });
2733
3041
  server2.on("upgrade", (req, socket, head) => {
@@ -2738,16 +3046,16 @@ var Factory = async (options) => {
2738
3046
  res.assignSocket(socket);
2739
3047
  listener(req, res);
2740
3048
  } catch (err) {
2741
- logger7.error(`upgrade error: ${err}`, err);
3049
+ logger10.error(`upgrade error: ${err}`, err);
2742
3050
  }
2743
3051
  }).on("close", async () => {
2744
- logger7.info(`http server closed.`);
3052
+ logger10.info(`http server closed.`);
2745
3053
  });
2746
3054
  try {
2747
3055
  const { value: port } = ports.next();
2748
3056
  server2.listen(port, host);
2749
3057
  } catch (e) {
2750
- logger7.error(`error starting web socket server`, e);
3058
+ logger10.error(`error starting web socket server`, e);
2751
3059
  reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
2752
3060
  }
2753
3061
  });
@@ -2755,18 +3063,18 @@ var Factory = async (options) => {
2755
3063
  return new class {
2756
3064
  gateway = gw;
2757
3065
  async close() {
2758
- for (const [path, route] of routes3) {
3066
+ for (const [path, route] of context.sockets) {
2759
3067
  try {
2760
3068
  if (route.close) {
2761
3069
  await route.close();
2762
3070
  }
2763
- logger7.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
3071
+ logger10.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
2764
3072
  route.wss?.clients?.forEach((client) => {
2765
3073
  client.terminate();
2766
3074
  });
2767
3075
  route.wss?.close();
2768
3076
  } catch (e) {
2769
- logger7.warn(`error closing route ${path}`, e);
3077
+ logger10.warn(`error closing route ${path}`, e);
2770
3078
  }
2771
3079
  }
2772
3080
  await promisify((cb) => {