@interopio/gateway-server 0.5.2-beta → 0.6.1-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.js CHANGED
@@ -138,7 +138,7 @@ var HttpServerRequest = class {
138
138
  this._body ??= new Promise((resolve, reject) => {
139
139
  const chunks = [];
140
140
  this._req.on("error", (err) => reject(err)).on("data", (chunk) => chunks.push(chunk)).on("end", () => {
141
- resolve(new Blob(chunks));
141
+ resolve(new Blob(chunks, { type: this.headers.one("content-type") }));
142
142
  });
143
143
  });
144
144
  return this._body;
@@ -155,74 +155,79 @@ var HttpServerRequest = class {
155
155
  }
156
156
  get json() {
157
157
  return this.body.then(async (blob) => {
158
- const json = JSON.parse(await blob.text());
158
+ if (blob.size === 0) {
159
+ return void 0;
160
+ }
161
+ const text = await blob.text();
162
+ const json = JSON.parse(text);
159
163
  return json;
160
164
  });
161
165
  }
162
166
  };
163
167
  var IncomingMessageHeaders = class {
164
- constructor(_msg) {
165
- this._msg = _msg;
168
+ #msg;
169
+ constructor(msg) {
170
+ this.#msg = msg;
166
171
  }
167
172
  has(name) {
168
- return this._msg.headers[name] !== void 0;
173
+ return this.#msg.headers[name] !== void 0;
169
174
  }
170
175
  get(name) {
171
- return this._msg.headers[name];
176
+ return this.#msg.headers[name];
172
177
  }
173
178
  list(name) {
174
- return toList(this._msg.headers[name]);
179
+ return toList(this.#msg.headers[name]);
175
180
  }
176
181
  one(name) {
177
- const value = this._msg.headers[name];
182
+ const value = this.#msg.headers[name];
178
183
  if (Array.isArray(value)) {
179
184
  return value[0];
180
185
  }
181
186
  return value;
182
187
  }
183
188
  keys() {
184
- return Object.keys(this._msg.headers).values();
189
+ return Object.keys(this.#msg.headers).values();
185
190
  }
186
191
  };
187
192
  var OutgoingMessageHeaders = class {
188
- _msg;
193
+ #msg;
189
194
  constructor(msg) {
190
- this._msg = msg;
195
+ this.#msg = msg;
191
196
  }
192
197
  has(name) {
193
- return this._msg.hasHeader(name);
198
+ return this.#msg.hasHeader(name);
194
199
  }
195
200
  keys() {
196
- return this._msg.getHeaderNames().values();
201
+ return this.#msg.getHeaderNames().values();
197
202
  }
198
203
  get(name) {
199
- return this._msg.getHeader(name);
204
+ return this.#msg.getHeader(name);
200
205
  }
201
206
  one(name) {
202
- const value = this._msg.getHeader(name);
207
+ const value = this.#msg.getHeader(name);
203
208
  if (Array.isArray(value)) {
204
209
  return value[0];
205
210
  }
206
211
  return value;
207
212
  }
208
213
  set(name, value) {
209
- if (!this._msg.headersSent) {
214
+ if (!this.#msg.headersSent) {
210
215
  if (Array.isArray(value)) {
211
216
  value = value.map((v) => typeof v === "number" ? String(v) : v);
212
217
  } else if (typeof value === "number") {
213
218
  value = String(value);
214
219
  }
215
220
  if (value) {
216
- this._msg.setHeader(name, value);
221
+ this.#msg.setHeader(name, value);
217
222
  } else {
218
- this._msg.removeHeader(name);
223
+ this.#msg.removeHeader(name);
219
224
  }
220
225
  }
221
226
  return this;
222
227
  }
223
228
  add(name, value) {
224
- if (!this._msg.headersSent) {
225
- this._msg.appendHeader(name, value);
229
+ if (!this.#msg.headersSent) {
230
+ this.#msg.appendHeader(name, value);
226
231
  }
227
232
  return this;
228
233
  }
@@ -270,7 +275,7 @@ var HttpServerResponse = class {
270
275
  }
271
276
  end(chunk) {
272
277
  if (!this._res.headersSent) {
273
- return new Promise((resolve, reject) => {
278
+ return new Promise((resolve) => {
274
279
  if (chunk === void 0) {
275
280
  this._res.end(() => {
276
281
  resolve(true);
@@ -300,6 +305,8 @@ var HttpServerResponse = class {
300
305
  }
301
306
  };
302
307
  var DefaultWebExchange = class extends WebExchange {
308
+ request;
309
+ response;
303
310
  constructor(request, response) {
304
311
  super();
305
312
  this.request = request;
@@ -388,6 +395,92 @@ var MapHttpHeaders = class extends Map {
388
395
  return this;
389
396
  }
390
397
  };
398
+ var MockHttpRequest = class {
399
+ #url;
400
+ #body;
401
+ headers = new MapHttpHeaders();
402
+ constructor(url, method) {
403
+ if (typeof url === "string") {
404
+ if (URL.canParse(url)) {
405
+ url = new URL(url);
406
+ } else if (URL.canParse(url, "http://localhost")) {
407
+ url = new URL(url, "http://localhost");
408
+ } else {
409
+ throw new TypeError("URL cannot parse url");
410
+ }
411
+ }
412
+ this.#url = url;
413
+ this.method = method ?? "GET";
414
+ this.headers.set("Host", this.#url.hostname);
415
+ this.path = this.#url.pathname ?? "/";
416
+ }
417
+ method;
418
+ path;
419
+ get host() {
420
+ return requestToHost(this, this.#url.host);
421
+ }
422
+ get protocol() {
423
+ return requestToProtocol(this, this.#url.protocol.slice(0, -1));
424
+ }
425
+ get cookies() {
426
+ return parseCookies(this);
427
+ }
428
+ get body() {
429
+ const body = this.#body;
430
+ return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
431
+ }
432
+ get json() {
433
+ return this.body.then(async (blob) => JSON.parse(await blob.text()));
434
+ }
435
+ get text() {
436
+ return this.body.then(async (blob) => await blob.text());
437
+ }
438
+ set body(value) {
439
+ this.#body = value;
440
+ if (!this.headers.has("content-type")) {
441
+ this.headers.set("content-type", value.type || "application/octet-stream");
442
+ }
443
+ }
444
+ get formData() {
445
+ return this.body.then(async (b) => new URLSearchParams(await b.text()));
446
+ }
447
+ get URL() {
448
+ return new URL(this.path + this.#url.search + this.#url.hash, `${this.protocol}://${this.host}`);
449
+ }
450
+ };
451
+ var MockHttpResponse = class {
452
+ statusCode;
453
+ headers = new MapHttpHeaders();
454
+ cookies = [];
455
+ addCookie(cookie) {
456
+ this.cookies.push(cookie);
457
+ return this;
458
+ }
459
+ _body;
460
+ async end(chunk) {
461
+ if (this._body) {
462
+ return false;
463
+ }
464
+ switch (typeof chunk) {
465
+ case "string":
466
+ this._body = new Blob([chunk], { type: "text/plain" });
467
+ break;
468
+ case "object":
469
+ if (chunk instanceof Blob) {
470
+ this._body = chunk;
471
+ } else if (chunk !== null) {
472
+ this._body = new Blob([JSON.stringify(chunk)], { type: "application/json" });
473
+ }
474
+ break;
475
+ case "undefined":
476
+ this._body = new Blob([]);
477
+ break;
478
+ default:
479
+ throw new Error(`Unsupported chunk type: ${typeof chunk}`);
480
+ }
481
+ return true;
482
+ }
483
+ };
391
484
 
392
485
  // src/gateway/ws/core.ts
393
486
  import { IOGateway as IOGateway2 } from "@interopio/gateway";
@@ -558,22 +651,129 @@ var InMemoryNodeConnections = class {
558
651
  }
559
652
  };
560
653
 
654
+ // src/server/util/matchers.ts
655
+ var or = (matchers) => {
656
+ return async (exchange) => {
657
+ for (const matcher of matchers) {
658
+ const result = await matcher(exchange);
659
+ if (result.match) {
660
+ return { match: true };
661
+ }
662
+ }
663
+ return { match: false };
664
+ };
665
+ };
666
+ var and = (matchers) => {
667
+ const matcher = async (exchange) => {
668
+ for (const matcher2 of matchers) {
669
+ const result = await matcher2(exchange);
670
+ if (!result.match) {
671
+ return { match: false };
672
+ }
673
+ }
674
+ return { match: true };
675
+ };
676
+ matcher.toString = () => `and(${matchers.map((m) => m.toString()).join(", ")})`;
677
+ return matcher;
678
+ };
679
+ var not = (matcher) => {
680
+ return async (exchange) => {
681
+ const result = await matcher(exchange);
682
+ return { match: !result.match };
683
+ };
684
+ };
685
+ var anyExchange = async (_exchange) => {
686
+ return { match: true };
687
+ };
688
+ anyExchange.toString = () => "any-exchange";
689
+ var pattern = (pattern2, opts) => {
690
+ const method = opts?.method;
691
+ const matcher = async (exchange) => {
692
+ const request = exchange.request;
693
+ const path = request.path;
694
+ if (method !== void 0 && request.method !== method) {
695
+ return { match: false };
696
+ }
697
+ if (typeof pattern2 === "string") {
698
+ if (path === pattern2) {
699
+ return { match: true };
700
+ } else {
701
+ return { match: false };
702
+ }
703
+ } else {
704
+ const match = pattern2.exec(path);
705
+ if (match !== null) {
706
+ return { match: true };
707
+ } else {
708
+ return { match: false };
709
+ }
710
+ }
711
+ };
712
+ matcher.toString = () => {
713
+ return `pattern(${pattern2.toString()})${method ? ` method=${method}` : ""}`;
714
+ };
715
+ return matcher;
716
+ };
717
+ var mediaType = (opts) => {
718
+ const shouldIgnore = (requestedMediaType) => {
719
+ if (opts.ignoredMediaTypes !== void 0) {
720
+ for (const ignoredMediaType of opts.ignoredMediaTypes) {
721
+ if (requestedMediaType === ignoredMediaType || ignoredMediaType === "*/*") {
722
+ return true;
723
+ }
724
+ }
725
+ }
726
+ return false;
727
+ };
728
+ return async (exchange) => {
729
+ const request = exchange.request;
730
+ let requestMediaTypes;
731
+ try {
732
+ requestMediaTypes = request.headers.list("accept");
733
+ } catch (e) {
734
+ return { match: false };
735
+ }
736
+ for (const requestedMediaType of requestMediaTypes) {
737
+ if (shouldIgnore(requestedMediaType)) {
738
+ continue;
739
+ }
740
+ for (const mediaType2 of opts.mediaTypes) {
741
+ if (requestedMediaType.startsWith(mediaType2)) {
742
+ return { match: true };
743
+ }
744
+ }
745
+ }
746
+ return { match: false };
747
+ };
748
+ };
749
+ var upgradeMatcher = async ({ request }) => {
750
+ const match = request["_req"]?.["upgrade"] && request.headers.one("upgrade")?.toLowerCase() === "websocket";
751
+ return { match };
752
+ };
753
+ upgradeMatcher.toString = () => "websocket upgrade";
754
+
561
755
  // src/mesh/rest-directory/routes.ts
562
- function routes(connections) {
756
+ function routes(connections, config, authorize) {
757
+ config.cors.push(
758
+ [pattern("/api/nodes"), { allowMethods: ["GET", "POST"] }],
759
+ [pattern(/\/api\/nodes\/(?<nodeId>.*)/), { allowMethods: ["GET", "DELETE"] }]
760
+ );
761
+ if (authorize) {
762
+ config.authorize.push([pattern(/\/api\/nodes(\/.*)?/), authorize]);
763
+ }
563
764
  return [
564
765
  async (ctx, next) => {
565
766
  if (ctx.method === "POST" && ctx.path === "/api/nodes") {
566
767
  const json = await ctx.request.json;
567
768
  if (!Array.isArray(json)) {
568
769
  ctx.response.statusCode = 400;
569
- ctx.response._res.end();
770
+ await ctx.response.end();
570
771
  } else {
571
772
  const nodes = json;
572
773
  const result = connections.announce(nodes);
573
- const body = JSON.stringify(result);
574
- ctx.response.headers.set("content-type", "application/json");
774
+ const body = new Blob([JSON.stringify(result)], { type: "application/json" });
575
775
  ctx.response.statusCode = 200;
576
- ctx.response._res.end(body);
776
+ await ctx.response.end(body);
577
777
  }
578
778
  } else {
579
779
  await next();
@@ -584,7 +784,7 @@ function routes(connections) {
584
784
  const nodeId = path?.substring("/api/nodes/".length);
585
785
  connections.remove(nodeId);
586
786
  response.statusCode = 200;
587
- response._res.end();
787
+ await response.end();
588
788
  } else {
589
789
  await next();
590
790
  }
@@ -945,68 +1145,38 @@ var core_default4 = create4;
945
1145
 
946
1146
  // src/metrics/routes.ts
947
1147
  var logger5 = getLogger("metrics");
948
- var COOKIE_NAME = "GW_LOGIN";
949
- function loggedIn(auth, ctx) {
950
- if (auth) {
951
- const value = ctx.request.cookies.find((cookie) => cookie.name === COOKIE_NAME)?.value;
952
- return value && parseInt(value) > Date.now();
953
- }
954
- return true;
955
- }
956
- async function routes2(config) {
1148
+ async function routes2(config, { cors, authorize }) {
957
1149
  const { jsonFileAppender } = await import("@interopio/gateway/metrics/publisher/file");
958
1150
  const appender = jsonFileAppender(logger5);
959
1151
  await appender.open(config.file?.location ?? "metrics.ndjson", config.file?.append ?? true);
1152
+ cors.push([pattern("/api/metrics"), { allowMethods: ["GET", "POST"] }]);
1153
+ if (config.authorize) {
1154
+ authorize.push([pattern("/api/metrics"), config.authorize]);
1155
+ }
960
1156
  return [
961
1157
  async (ctx, next) => {
962
1158
  if (ctx.method === "GET" && ctx.path === "/api/metrics") {
963
- if (loggedIn(config.auth, ctx)) {
964
- ctx.response.statusCode = 200;
965
- } else {
966
- ctx.response.statusCode = 302;
967
- ctx.response.headers.set("location", "/api/login?redirectTo=/api/metrics");
968
- }
969
- ctx.response._res.end();
970
- } else {
971
- await next();
972
- }
973
- },
974
- async (ctx, next) => {
975
- if (ctx.method === "GET" && ctx.path === "/api/login") {
976
- const redirectTo = new URLSearchParams(ctx.request.query ?? void 0).get("redirectTo");
977
- const expires = Date.now() + 180 * 1e3;
978
- ctx.response.addCookie({ name: COOKIE_NAME, value: `${expires}`, maxAge: Infinity, path: "/api", sameSite: "strict" });
979
- if (redirectTo) {
980
- ctx.response.statusCode = 302;
981
- ctx.response.headers.set("location", redirectTo);
982
- } else {
983
- ctx.response.statusCode = 200;
984
- }
985
- ctx.response._res.end();
1159
+ ctx.response.statusCode = 200;
1160
+ await ctx.response.end();
986
1161
  } else {
987
1162
  await next();
988
1163
  }
989
1164
  },
990
- async (ctx, next) => {
991
- if (ctx.method === "POST" && ctx.path === "/api/metrics") {
992
- if (loggedIn(config.auth, ctx)) {
993
- ctx.response.statusCode = 202;
994
- ctx.response._res.end();
995
- try {
996
- const json = await ctx.request.json;
997
- const update = json;
998
- if (logger5.enabledFor("debug")) {
999
- logger5.debug(`${JSON.stringify(update)}`);
1000
- }
1001
- if ((config.file?.status ?? false) || update.status === void 0) {
1002
- await appender.write(update);
1003
- }
1004
- } catch (e) {
1005
- logger5.error(`error processing metrics`, e);
1165
+ async ({ request, response }, next) => {
1166
+ if (request.method === "POST" && request.path === "/api/metrics") {
1167
+ response.statusCode = 202;
1168
+ await response.end();
1169
+ try {
1170
+ const json = await request.json;
1171
+ const update = json;
1172
+ if (logger5.enabledFor("debug")) {
1173
+ logger5.debug(`${JSON.stringify(update)}`);
1006
1174
  }
1007
- } else {
1008
- ctx.response.statusCode = 401;
1009
- ctx.response._res.end();
1175
+ if ((config.file?.status ?? false) || update.status === void 0) {
1176
+ await appender.write(update);
1177
+ }
1178
+ } catch (e) {
1179
+ logger5.error(`error processing metrics`, e);
1010
1180
  }
1011
1181
  } else {
1012
1182
  await next();
@@ -1087,9 +1257,9 @@ var localIp = (() => {
1087
1257
  return a.length > 0 ? a[0] : void 0;
1088
1258
  }
1089
1259
  const addresses = Object.values(networkInterfaces()).flatMap((details) => {
1090
- return (details ?? []).filter((info) => info.family === "IPv4");
1091
- }).reduce((acc, info) => {
1092
- acc[info.internal ? "internal" : "external"].push(info);
1260
+ return (details ?? []).filter((info2) => info2.family === "IPv4");
1261
+ }).reduce((acc, info2) => {
1262
+ acc[info2.internal ? "internal" : "external"].push(info2);
1093
1263
  return acc;
1094
1264
  }, { internal: [], external: [] });
1095
1265
  return (first(addresses.internal) ?? first(addresses.external))?.address;
@@ -1236,7 +1406,7 @@ async function stop(m) {
1236
1406
  return await run(m, "stop");
1237
1407
  }
1238
1408
 
1239
- // src/server/ws-client-verify.ts
1409
+ // src/app/ws-client-verify.ts
1240
1410
  import { IOGateway as IOGateway5 } from "@interopio/gateway";
1241
1411
  var log3 = getLogger("gateway.ws.client-verify");
1242
1412
  function acceptsMissing(originFilters) {
@@ -1308,272 +1478,57 @@ function regexifyOriginFilters(originFilters) {
1308
1478
  }
1309
1479
  }
1310
1480
 
1311
- // src/server/cors.ts
1312
- import { IOGateway as IOGateway6 } from "@interopio/gateway";
1313
- function isSameOrigin(request) {
1314
- const origin = request.headers.one("origin");
1315
- if (origin === void 0) {
1316
- return true;
1317
- }
1318
- const url = request.URL;
1319
- const actualProtocol = url.protocol;
1320
- const actualHost = url.host;
1321
- const originUrl = new URL(origin);
1322
- const originHost = originUrl.host;
1323
- const originProtocol = originUrl.protocol;
1324
- return actualProtocol === originProtocol && actualHost === originHost;
1325
- }
1326
- function isCorsRequest(request) {
1327
- return request.headers.has("origin") && !isSameOrigin(request);
1328
- }
1329
- function isPreFlightRequest(request) {
1330
- return request.method === "OPTIONS" && request.headers.has("origin") && request.headers.has("access-control-request-method");
1331
- }
1332
- var VARY_HEADERS = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"];
1333
- function processRequest(exchange, config) {
1334
- const { request, response } = exchange;
1335
- const responseHeaders = response.headers;
1336
- if (!responseHeaders.has("Vary")) {
1337
- responseHeaders.set("Vary", VARY_HEADERS.join(", "));
1338
- } else {
1339
- const varyHeaders = responseHeaders.list("Vary");
1340
- for (const header of VARY_HEADERS) {
1341
- if (!varyHeaders.find((h) => h === header)) {
1342
- varyHeaders.push(header);
1343
- }
1481
+ // src/server/server-header.ts
1482
+ import info from "@interopio/gateway-server/package.json" with { type: "json" };
1483
+ var serverHeader = (server) => {
1484
+ server ??= `${info.name} - v${info.version}`;
1485
+ return async ({ response }, next) => {
1486
+ if (server !== false && !response.headers.has("server")) {
1487
+ response.headers.set("Server", server);
1344
1488
  }
1345
- responseHeaders.set("Vary", varyHeaders.join(", "));
1346
- }
1347
- try {
1348
- if (!isCorsRequest(request)) {
1489
+ await next();
1490
+ };
1491
+ };
1492
+ var server_header_default = (server) => serverHeader(server);
1493
+
1494
+ // src/app/route.ts
1495
+ function findSocketRoute({ request }, { sockets: routes3 }) {
1496
+ const path = request.path ?? "/";
1497
+ const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
1498
+ if (path === "/" && route2.default === true) {
1349
1499
  return true;
1350
1500
  }
1351
- } catch (e) {
1352
- if (logger6.enabledFor("debug")) {
1353
- logger6.debug(`reject: origin is malformed`);
1354
- }
1355
- rejectRequest(response);
1356
- return false;
1357
- }
1358
- if (responseHeaders.has("access-control-allow-origin")) {
1359
- logger6.trace(`skip: already contains "Access-Control-Allow-Origin"`);
1360
- return true;
1361
- }
1362
- const preFlightRequest = isPreFlightRequest(request);
1363
- if (config) {
1364
- return handleInternal(exchange, config, preFlightRequest);
1365
- }
1366
- if (preFlightRequest) {
1367
- rejectRequest(response);
1368
- return false;
1369
- }
1370
- return true;
1501
+ });
1502
+ return [route, path];
1371
1503
  }
1372
- function validateConfig(config) {
1373
- if (config) {
1374
- const headers2 = config.headers;
1375
- if (headers2?.allow && headers2.allow !== ALL) {
1376
- headers2.allow = headers2.allow.map((header) => header.toLowerCase());
1377
- }
1378
- const origins = config.origins;
1379
- if (origins?.allow && origins.allow !== ALL) {
1380
- origins.allow = origins.allow.map((origin) => {
1381
- if (typeof origin === "string") {
1382
- return origin.toLowerCase();
1383
- }
1384
- return origin;
1385
- });
1504
+
1505
+ // src/server/security/http-headers.ts
1506
+ var staticServerHttpHeadersWriter = (headers2) => {
1507
+ return async (exchange) => {
1508
+ let containsNoHeaders = true;
1509
+ const { response } = exchange;
1510
+ for (const name of headers2.keys()) {
1511
+ if (response.headers.has(name)) {
1512
+ containsNoHeaders = false;
1513
+ }
1386
1514
  }
1387
- return config;
1388
- }
1389
- }
1390
- var handler = (config) => {
1391
- validateConfig(config);
1392
- return async (ctx, next) => {
1393
- const isValid = processRequest(ctx, config);
1394
- if (!isValid || isPreFlightRequest(ctx.request)) {
1395
- await ctx.response.end();
1396
- } else {
1397
- await next();
1515
+ if (containsNoHeaders) {
1516
+ for (const [name, value] of headers2) {
1517
+ response.headers.set(name, value);
1518
+ }
1398
1519
  }
1399
1520
  };
1400
1521
  };
1401
- var cors_default = (config) => [handler(config)];
1402
- var logger6 = getLogger("cors");
1403
- function rejectRequest(response) {
1404
- response.statusCode = 403;
1405
- }
1406
- function handleInternal(exchange, config, preFlightRequest) {
1407
- const { request, response } = exchange;
1408
- const responseHeaders = response.headers;
1409
- const requestOrigin = request.headers.one("origin");
1410
- const allowOrigin = checkOrigin(config, requestOrigin);
1411
- if (allowOrigin === void 0) {
1412
- if (logger6.enabledFor("debug")) {
1413
- logger6.debug(`reject: '${requestOrigin}' origin is not allowed`);
1414
- }
1415
- rejectRequest(response);
1416
- return false;
1417
- }
1418
- const requestMethod = getMethodToUse(request, preFlightRequest);
1419
- const allowMethods = checkMethods(config, requestMethod);
1420
- if (allowMethods === void 0) {
1421
- if (logger6.enabledFor("debug")) {
1422
- logger6.debug(`reject: HTTP '${requestMethod}' is not allowed`);
1423
- }
1424
- rejectRequest(response);
1425
- return false;
1426
- }
1427
- const requestHeaders = getHeadersToUse(request, preFlightRequest);
1428
- const allowHeaders = checkHeaders(config, requestHeaders);
1429
- if (preFlightRequest && allowHeaders === void 0) {
1430
- if (logger6.enabledFor("debug")) {
1431
- logger6.debug(`reject: headers '${requestHeaders}' are not allowed`);
1432
- }
1433
- rejectRequest(response);
1434
- return false;
1435
- }
1436
- responseHeaders.set("access-control-allow-origin", allowOrigin);
1437
- if (preFlightRequest) {
1438
- responseHeaders.set("access-control-allow-methods", allowMethods.join(","));
1439
- }
1440
- if (preFlightRequest && allowHeaders !== void 0 && allowHeaders.length > 0) {
1441
- responseHeaders.set("access-control-allow-headers", allowHeaders.join(", "));
1442
- }
1443
- const exposeHeaders = config.headers?.expose;
1444
- if (exposeHeaders && exposeHeaders.length > 0) {
1445
- responseHeaders.set("access-control-expose-headers", exposeHeaders.join(", "));
1446
- }
1447
- if (config.credentials?.allow) {
1448
- responseHeaders.set("access-control-allow-credentials", "true");
1449
- }
1450
- if (config.privateNetwork?.allow && request.headers.one("access-control-request-private-network") === "true") {
1451
- responseHeaders.set("access-control-allow-private-network", "true");
1452
- }
1453
- return true;
1454
- }
1455
- var ALL = "*";
1456
- var DEFAULT_METHODS = ["GET", "HEAD"];
1457
- function validateAllowCredentials(config) {
1458
- if (config.credentials?.allow === true && config.origins?.allow === ALL) {
1459
- throw new Error(`when credentials.allow is true origins.allow cannot be "*"`);
1460
- }
1461
- }
1462
- function validateAllowPrivateNetwork(config) {
1463
- if (config.privateNetwork?.allow === true && config.origins?.allow === ALL) {
1464
- throw new Error(`when privateNetwork.allow is true origins.allow cannot be "*"`);
1465
- }
1466
- }
1467
- function checkOrigin(config, origin) {
1468
- if (origin) {
1469
- const allowedOrigins = config.origins?.allow;
1470
- if (allowedOrigins) {
1471
- if (allowedOrigins === ALL) {
1472
- validateAllowCredentials(config);
1473
- validateAllowPrivateNetwork(config);
1474
- return ALL;
1475
- }
1476
- const originToCheck = trimTrailingSlash(origin.toLowerCase());
1477
- for (const allowedOrigin of allowedOrigins) {
1478
- if (allowedOrigin === ALL || IOGateway6.Filtering.valueMatches(allowedOrigin, originToCheck)) {
1479
- return origin;
1480
- }
1481
- }
1482
- }
1483
- }
1484
- }
1485
- function checkMethods(config, requestMethod) {
1486
- if (requestMethod) {
1487
- const allowedMethods = config.methods?.allow ?? DEFAULT_METHODS;
1488
- if (allowedMethods === ALL) {
1489
- return [requestMethod];
1490
- }
1491
- if (IOGateway6.Filtering.valuesMatch(allowedMethods, requestMethod)) {
1492
- return allowedMethods;
1493
- }
1494
- }
1495
- }
1496
- function checkHeaders(config, requestHeaders) {
1497
- if (requestHeaders === void 0) {
1498
- return;
1499
- }
1500
- if (requestHeaders.length == 0) {
1501
- return [];
1502
- }
1503
- const allowedHeaders = config.headers?.allow;
1504
- if (allowedHeaders === void 0) {
1505
- return;
1506
- }
1507
- const allowAnyHeader = allowedHeaders === ALL;
1508
- const result = [];
1509
- for (const requestHeader of requestHeaders) {
1510
- const value = requestHeader?.trim();
1511
- if (value) {
1512
- if (allowAnyHeader) {
1513
- result.push(value);
1514
- } else {
1515
- for (const allowedHeader of allowedHeaders) {
1516
- if (value.toLowerCase() == allowedHeader) {
1517
- result.push(value);
1518
- break;
1519
- }
1520
- }
1521
- }
1522
- }
1523
- }
1524
- if (result.length > 0) {
1525
- return result;
1526
- }
1527
- }
1528
- function trimTrailingSlash(origin) {
1529
- return origin.endsWith("/") ? origin.slice(0, -1) : origin;
1530
- }
1531
- function getMethodToUse(request, isPreFlight) {
1532
- return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
1533
- }
1534
- function getHeadersToUse(request, isPreFlight) {
1535
- const headers2 = request.headers;
1536
- return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
1537
- }
1538
-
1539
- // src/server/server-header.ts
1540
- var serverHeader = (server) => {
1541
- return async ({ response }, next) => {
1542
- if (!response.headers.has("server")) {
1543
- response.headers.set("Server", server);
1544
- }
1545
- await next();
1546
- };
1547
- };
1548
- var server_header_default = (server = "gateway-server") => serverHeader(server);
1549
-
1550
- // src/server/security/http-headers.ts
1551
- var staticServerHttpHeadersWriter = (headers2) => {
1552
- return async (exchange) => {
1553
- let containsNoHeaders = true;
1554
- const { response } = exchange;
1555
- for (const name of headers2.keys()) {
1556
- if (response.headers.has(name)) {
1557
- containsNoHeaders = false;
1558
- }
1559
- }
1560
- if (containsNoHeaders) {
1561
- for (const [name, value] of headers2) {
1562
- response.headers.set(name, value);
1563
- }
1564
- }
1565
- };
1566
- };
1567
- var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1568
- new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
1569
- );
1570
- var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1571
- new MapHttpHeaders().add("x-content-type-options", "nosniff")
1572
- );
1573
- var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
1574
- let headerValue = `max-age=${maxAgeInSeconds}`;
1575
- if (includeSubDomains) {
1576
- headerValue += " ; includeSubDomains";
1522
+ var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1523
+ new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
1524
+ );
1525
+ var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1526
+ new MapHttpHeaders().add("x-content-type-options", "nosniff")
1527
+ );
1528
+ var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
1529
+ let headerValue = `max-age=${maxAgeInSeconds}`;
1530
+ if (includeSubDomains) {
1531
+ headerValue += " ; includeSubDomains";
1577
1532
  }
1578
1533
  if (preload) {
1579
1534
  headerValue += " ; preload";
@@ -1733,18 +1688,18 @@ var AuthorizationDecision = class {
1733
1688
  granted;
1734
1689
  };
1735
1690
  var DefaultAuthorizationManager = class {
1736
- check;
1691
+ #check;
1737
1692
  constructor(check) {
1738
- this.check = check;
1693
+ this.#check = check;
1739
1694
  }
1740
1695
  async verify(authentication, object) {
1741
- const decision = await this.check(authentication, object);
1696
+ const decision = await this.#check(authentication, object);
1742
1697
  if (!decision?.granted) {
1743
1698
  throw new AccessDeniedError("Access denied");
1744
1699
  }
1745
1700
  }
1746
1701
  async authorize(authentication, object) {
1747
- return await this.check(authentication, object);
1702
+ return await this.#check(authentication, object);
1748
1703
  }
1749
1704
  };
1750
1705
  var AuthenticationServiceError = class extends AuthenticationError {
@@ -1798,71 +1753,6 @@ var httpBasicAuthenticationConverter = (opts) => {
1798
1753
  };
1799
1754
  };
1800
1755
 
1801
- // src/server/util/matchers.ts
1802
- var or = (matchers) => {
1803
- return async (exchange) => {
1804
- for (const matcher of matchers) {
1805
- const result = await matcher(exchange);
1806
- if (result.match) {
1807
- return { match: true };
1808
- }
1809
- }
1810
- return { match: false };
1811
- };
1812
- };
1813
- var and = (matchers) => {
1814
- return async (exchange) => {
1815
- for (const matcher of matchers) {
1816
- const result = await matcher(exchange);
1817
- if (!result.match) {
1818
- return { match: false };
1819
- }
1820
- }
1821
- return { match: true };
1822
- };
1823
- };
1824
- var not = (matcher) => {
1825
- return async (exchange) => {
1826
- const result = await matcher(exchange);
1827
- return { match: !result.match };
1828
- };
1829
- };
1830
- var anyExchange = async (_exchange) => {
1831
- return { match: true };
1832
- };
1833
- var mediaType = (opts) => {
1834
- const shouldIgnore = (requestedMediaType) => {
1835
- if (opts.ignoredMediaTypes !== void 0) {
1836
- for (const ignoredMediaType of opts.ignoredMediaTypes) {
1837
- if (requestedMediaType === ignoredMediaType || ignoredMediaType === "*/*") {
1838
- return true;
1839
- }
1840
- }
1841
- }
1842
- return false;
1843
- };
1844
- return async (exchange) => {
1845
- const request = exchange.request;
1846
- let requestMediaTypes;
1847
- try {
1848
- requestMediaTypes = request.headers.list("accept");
1849
- } catch (e) {
1850
- return { match: false };
1851
- }
1852
- for (const requestedMediaType of requestMediaTypes) {
1853
- if (shouldIgnore(requestedMediaType)) {
1854
- continue;
1855
- }
1856
- for (const mediaType2 of opts.mediaTypes) {
1857
- if (requestedMediaType.startsWith(mediaType2)) {
1858
- return { match: true };
1859
- }
1860
- }
1861
- }
1862
- return { match: false };
1863
- };
1864
- };
1865
-
1866
1756
  // src/server/security/security-context.ts
1867
1757
  import { AsyncLocalStorage } from "node:async_hooks";
1868
1758
  var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
@@ -2176,6 +2066,7 @@ var httpStatusEntryPoint = (opts) => {
2176
2066
  };
2177
2067
 
2178
2068
  // src/server/security/delegating-entry-point.ts
2069
+ var logger6 = getLogger("auth.entry-point");
2179
2070
  var delegatingEntryPoint = (opts) => {
2180
2071
  const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
2181
2072
  response.statusCode = 401;
@@ -2183,11 +2074,20 @@ var delegatingEntryPoint = (opts) => {
2183
2074
  });
2184
2075
  return async (exchange, error) => {
2185
2076
  for (const [matcher, entryPoint] of opts.entryPoints) {
2077
+ if (logger6.enabledFor("debug")) {
2078
+ logger6.debug(`trying to match using: ${matcher}`);
2079
+ }
2186
2080
  const match = await matcher(exchange);
2187
2081
  if (match.match) {
2082
+ if (logger6.enabledFor("debug")) {
2083
+ logger6.debug(`match found. using default entry point ${entryPoint}`);
2084
+ }
2188
2085
  return entryPoint(exchange, error);
2189
2086
  }
2190
2087
  }
2088
+ if (logger6.enabledFor("debug")) {
2089
+ logger6.debug(`no match found. using default entry point ${defaultEntryPoint}`);
2090
+ }
2191
2091
  return defaultEntryPoint(exchange, error);
2192
2092
  };
2193
2093
  };
@@ -2195,8 +2095,8 @@ var delegatingEntryPoint = (opts) => {
2195
2095
  // src/server/security/delegating-success-handler.ts
2196
2096
  var delegatingSuccessHandler = (handlers) => {
2197
2097
  return async ({ exchange, next }, authentication) => {
2198
- for (const handler2 of handlers) {
2199
- await handler2({ exchange, next }, authentication);
2098
+ for (const handler of handlers) {
2099
+ await handler({ exchange, next }, authentication);
2200
2100
  }
2201
2101
  };
2202
2102
  };
@@ -2246,7 +2146,7 @@ function httpBasic(opts) {
2246
2146
  }
2247
2147
 
2248
2148
  // src/server/security/config.ts
2249
- import { jwtVerifier } from "@interopio/gateway/jose/jwt";
2149
+ import { jwtVerifier, JwtVerifyError } from "@interopio/gateway/jose/jwt";
2250
2150
 
2251
2151
  // src/server/security/error-filter.ts
2252
2152
  async function commenceAuthentication(exchange, authentication, entryPoint) {
@@ -2290,14 +2190,21 @@ var errorFilter = (opts) => {
2290
2190
  };
2291
2191
 
2292
2192
  // src/server/security/authorization-filter.ts
2193
+ var logger7 = getLogger("security.auth");
2293
2194
  function authorizationFilter(opts) {
2294
2195
  const { manager, storage } = opts;
2295
2196
  return async (exchange, next) => {
2296
2197
  const promise = AsyncStorageSecurityContextHolder.getContext(storage).then((c) => c?.authentication);
2297
2198
  try {
2298
2199
  await manager.verify(promise, exchange);
2200
+ if (logger7.enabledFor("debug")) {
2201
+ logger7.debug("authorization successful");
2202
+ }
2299
2203
  } catch (error) {
2300
2204
  if (error instanceof AccessDeniedError) {
2205
+ if (logger7.enabledFor("debug")) {
2206
+ logger7.debug(`authorization failed: ${error.message}`);
2207
+ }
2301
2208
  }
2302
2209
  throw error;
2303
2210
  }
@@ -2306,12 +2213,14 @@ function authorizationFilter(opts) {
2306
2213
  }
2307
2214
 
2308
2215
  // src/server/security/delegating-authorization-manager.ts
2216
+ var logger8 = getLogger("auth");
2309
2217
  function delegatingAuthorizationManager(opts) {
2310
2218
  const check = async (authentication, exchange) => {
2311
2219
  let decision;
2312
2220
  for (const [matcher, manager] of opts.mappings) {
2313
2221
  if ((await matcher(exchange))?.match) {
2314
- const checkResult = await manager.check(authentication, { exchange });
2222
+ logger8.debug(`checking authorization on '${exchange.path}' using [${matcher}, ${manager}]`);
2223
+ const checkResult = await manager.authorize(authentication, { exchange });
2315
2224
  if (checkResult !== void 0) {
2316
2225
  decision = checkResult;
2317
2226
  break;
@@ -2324,6 +2233,308 @@ function delegatingAuthorizationManager(opts) {
2324
2233
  return new DefaultAuthorizationManager(check);
2325
2234
  }
2326
2235
 
2236
+ // src/server/cors.ts
2237
+ import { IOGateway as IOGateway6 } from "@interopio/gateway";
2238
+ function isSameOrigin(request) {
2239
+ const origin = request.headers.one("origin");
2240
+ if (origin === void 0) {
2241
+ return true;
2242
+ }
2243
+ const url = request.URL;
2244
+ const actualProtocol = url.protocol;
2245
+ const actualHost = url.host;
2246
+ const originUrl = URL.parse(origin);
2247
+ const originHost = originUrl?.host;
2248
+ const originProtocol = originUrl?.protocol;
2249
+ return actualProtocol === originProtocol && actualHost === originHost;
2250
+ }
2251
+ function isCorsRequest(request) {
2252
+ return request.headers.has("origin") && !isSameOrigin(request);
2253
+ }
2254
+ function isPreFlightRequest(request) {
2255
+ return request.method === "OPTIONS" && request.headers.has("origin") && request.headers.has("access-control-request-method");
2256
+ }
2257
+ var VARY_HEADERS = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"];
2258
+ var processRequest = (exchange, config) => {
2259
+ const { request, response } = exchange;
2260
+ const responseHeaders = response.headers;
2261
+ if (!responseHeaders.has("Vary")) {
2262
+ responseHeaders.set("Vary", VARY_HEADERS.join(", "));
2263
+ } else {
2264
+ const varyHeaders = responseHeaders.list("Vary");
2265
+ for (const header of VARY_HEADERS) {
2266
+ if (!varyHeaders.find((h) => h === header)) {
2267
+ varyHeaders.push(header);
2268
+ }
2269
+ }
2270
+ responseHeaders.set("Vary", varyHeaders.join(", "));
2271
+ }
2272
+ try {
2273
+ if (!isCorsRequest(request)) {
2274
+ return true;
2275
+ }
2276
+ } catch (e) {
2277
+ if (logger9.enabledFor("debug")) {
2278
+ logger9.debug(`reject: origin is malformed`);
2279
+ }
2280
+ rejectRequest(response);
2281
+ return false;
2282
+ }
2283
+ if (responseHeaders.has("access-control-allow-origin")) {
2284
+ logger9.trace(`skip: already contains "Access-Control-Allow-Origin"`);
2285
+ return true;
2286
+ }
2287
+ const preFlightRequest = isPreFlightRequest(request);
2288
+ if (config) {
2289
+ return handleInternal(exchange, config, preFlightRequest);
2290
+ }
2291
+ if (preFlightRequest) {
2292
+ rejectRequest(response);
2293
+ return false;
2294
+ }
2295
+ return true;
2296
+ };
2297
+ var DEFAULT_PERMIT_ALL = ["*"];
2298
+ var DEFAULT_PERMIT_METHODS = ["GET", "HEAD", "POST"];
2299
+ var PERMIT_DEFAULT_CONFIG = {
2300
+ allowOrigins: DEFAULT_PERMIT_ALL,
2301
+ allowMethods: DEFAULT_PERMIT_METHODS,
2302
+ allowHeaders: DEFAULT_PERMIT_ALL,
2303
+ maxAge: 1800
2304
+ // 30 minutes
2305
+ };
2306
+ function validateCorsConfig(config) {
2307
+ if (config) {
2308
+ const allowHeaders = config.allowHeaders;
2309
+ if (allowHeaders && allowHeaders !== ALL) {
2310
+ config = {
2311
+ ...config,
2312
+ allowHeaders: allowHeaders.map((header) => header.toLowerCase())
2313
+ };
2314
+ }
2315
+ const allowOrigins = config.allowOrigins;
2316
+ if (allowOrigins) {
2317
+ if (allowOrigins === "*") {
2318
+ validateAllowCredentials(config);
2319
+ validateAllowPrivateNetwork(config);
2320
+ } else {
2321
+ config = {
2322
+ ...config,
2323
+ allowOrigins: allowOrigins.map((origin) => {
2324
+ if (typeof origin === "string" && origin !== ALL) {
2325
+ origin = IOGateway6.Filtering.regexify(origin);
2326
+ if (typeof origin === "string") {
2327
+ return trimTrailingSlash(origin).toLowerCase();
2328
+ }
2329
+ }
2330
+ return origin;
2331
+ })
2332
+ };
2333
+ }
2334
+ }
2335
+ return config;
2336
+ }
2337
+ }
2338
+ function combine(source, other) {
2339
+ if (other === void 0) {
2340
+ return source !== void 0 ? source === ALL ? [ALL] : source : [];
2341
+ }
2342
+ if (source === void 0) {
2343
+ return other === ALL ? [ALL] : other;
2344
+ }
2345
+ if (source == DEFAULT_PERMIT_ALL || source === DEFAULT_PERMIT_METHODS) {
2346
+ return other === ALL ? [ALL] : other;
2347
+ }
2348
+ if (other == DEFAULT_PERMIT_ALL || other === DEFAULT_PERMIT_METHODS) {
2349
+ return source === ALL ? [ALL] : source;
2350
+ }
2351
+ if (source === ALL || source.includes(ALL) || other === ALL || other.includes(ALL)) {
2352
+ return [ALL];
2353
+ }
2354
+ const combined = /* @__PURE__ */ new Set();
2355
+ source.forEach((v) => combined.add(v));
2356
+ other.forEach((v) => combined.add(v));
2357
+ return Array.from(combined);
2358
+ }
2359
+ var combineCorsConfig = (source, other) => {
2360
+ if (other === void 0) {
2361
+ return source;
2362
+ }
2363
+ const config = {
2364
+ allowOrigins: combine(source.allowOrigins, other?.allowOrigins),
2365
+ allowMethods: combine(source.allowMethods, other?.allowMethods),
2366
+ allowHeaders: combine(source.allowHeaders, other?.allowHeaders),
2367
+ exposeHeaders: combine(source.exposeHeaders, other?.exposeHeaders),
2368
+ allowCredentials: other?.allowCredentials ?? source.allowCredentials,
2369
+ allowPrivateNetwork: other?.allowPrivateNetwork ?? source.allowPrivateNetwork,
2370
+ maxAge: other?.maxAge ?? source.maxAge
2371
+ };
2372
+ return config;
2373
+ };
2374
+ var corsFilter = (opts) => {
2375
+ const source = opts.corsConfigSource;
2376
+ const processor = opts.corsProcessor ?? processRequest;
2377
+ return async (ctx, next) => {
2378
+ const config = await source(ctx);
2379
+ const isValid = processor(ctx, config);
2380
+ if (!isValid || isPreFlightRequest(ctx.request)) {
2381
+ return;
2382
+ } else {
2383
+ await next();
2384
+ }
2385
+ };
2386
+ };
2387
+ var cors_default = corsFilter;
2388
+ var logger9 = getLogger("cors");
2389
+ function rejectRequest(response) {
2390
+ response.statusCode = 403;
2391
+ }
2392
+ function handleInternal(exchange, config, preFlightRequest) {
2393
+ const { request, response } = exchange;
2394
+ const responseHeaders = response.headers;
2395
+ const requestOrigin = request.headers.one("origin");
2396
+ const allowOrigin = checkOrigin(config, requestOrigin);
2397
+ if (allowOrigin === void 0) {
2398
+ if (logger9.enabledFor("debug")) {
2399
+ logger9.debug(`reject: '${requestOrigin}' origin is not allowed`);
2400
+ }
2401
+ rejectRequest(response);
2402
+ return false;
2403
+ }
2404
+ const requestMethod = getMethodToUse(request, preFlightRequest);
2405
+ const allowMethods = checkMethods(config, requestMethod);
2406
+ if (allowMethods === void 0) {
2407
+ if (logger9.enabledFor("debug")) {
2408
+ logger9.debug(`reject: HTTP '${requestMethod}' is not allowed`);
2409
+ }
2410
+ rejectRequest(response);
2411
+ return false;
2412
+ }
2413
+ const requestHeaders = getHeadersToUse(request, preFlightRequest);
2414
+ const allowHeaders = checkHeaders(config, requestHeaders);
2415
+ if (preFlightRequest && allowHeaders === void 0) {
2416
+ if (logger9.enabledFor("debug")) {
2417
+ logger9.debug(`reject: headers '${requestHeaders}' are not allowed`);
2418
+ }
2419
+ rejectRequest(response);
2420
+ return false;
2421
+ }
2422
+ responseHeaders.set("Access-Control-Allow-Origin", allowOrigin);
2423
+ if (preFlightRequest) {
2424
+ responseHeaders.set("Access-Control-Allow-Methods", allowMethods.join(","));
2425
+ }
2426
+ if (preFlightRequest && allowHeaders !== void 0 && allowHeaders.length > 0) {
2427
+ responseHeaders.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
2428
+ }
2429
+ const exposeHeaders = config.exposeHeaders;
2430
+ if (exposeHeaders && exposeHeaders.length > 0) {
2431
+ responseHeaders.set("Access-Control-Expose-Headers", exposeHeaders.join(", "));
2432
+ }
2433
+ if (config.allowCredentials) {
2434
+ responseHeaders.set("Access-Control-Allow-Credentials", "true");
2435
+ }
2436
+ if (config.allowPrivateNetwork && request.headers.one("access-control-request-private-network") === "true") {
2437
+ responseHeaders.set("Access-Control-Allow-Private-Network", "true");
2438
+ }
2439
+ if (preFlightRequest && config.maxAge !== void 0) {
2440
+ responseHeaders.set("Access-Control-Max-Age", config.maxAge.toString());
2441
+ }
2442
+ return true;
2443
+ }
2444
+ var ALL = "*";
2445
+ var DEFAULT_METHODS = ["GET", "HEAD"];
2446
+ function validateAllowCredentials(config) {
2447
+ if (config.allowCredentials === true && config.allowOrigins === ALL) {
2448
+ throw new Error(`when allowCredentials is true allowOrigins cannot be "*"`);
2449
+ }
2450
+ }
2451
+ function validateAllowPrivateNetwork(config) {
2452
+ if (config.allowPrivateNetwork === true && config.allowOrigins === ALL) {
2453
+ throw new Error(`when allowPrivateNetwork is true allowOrigins cannot be "*"`);
2454
+ }
2455
+ }
2456
+ function checkOrigin(config, origin) {
2457
+ if (origin) {
2458
+ const allowedOrigins = config.allowOrigins;
2459
+ if (allowedOrigins) {
2460
+ if (allowedOrigins === ALL) {
2461
+ validateAllowCredentials(config);
2462
+ validateAllowPrivateNetwork(config);
2463
+ return ALL;
2464
+ }
2465
+ const originToCheck = trimTrailingSlash(origin.toLowerCase());
2466
+ for (const allowedOrigin of allowedOrigins) {
2467
+ if (allowedOrigin === ALL || IOGateway6.Filtering.valueMatches(allowedOrigin, originToCheck)) {
2468
+ return origin;
2469
+ }
2470
+ }
2471
+ }
2472
+ }
2473
+ }
2474
+ function checkMethods(config, requestMethod) {
2475
+ if (requestMethod) {
2476
+ const allowedMethods = config.allowMethods ?? DEFAULT_METHODS;
2477
+ if (allowedMethods === ALL) {
2478
+ return [requestMethod];
2479
+ }
2480
+ if (IOGateway6.Filtering.valuesMatch(allowedMethods, requestMethod)) {
2481
+ return allowedMethods;
2482
+ }
2483
+ }
2484
+ }
2485
+ function checkHeaders(config, requestHeaders) {
2486
+ if (requestHeaders === void 0) {
2487
+ return;
2488
+ }
2489
+ if (requestHeaders.length == 0) {
2490
+ return [];
2491
+ }
2492
+ const allowedHeaders = config.allowHeaders;
2493
+ if (allowedHeaders === void 0) {
2494
+ return;
2495
+ }
2496
+ const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
2497
+ const result = [];
2498
+ for (const requestHeader of requestHeaders) {
2499
+ const value = requestHeader?.trim();
2500
+ if (value) {
2501
+ if (allowAnyHeader) {
2502
+ result.push(value);
2503
+ } else {
2504
+ for (const allowedHeader of allowedHeaders) {
2505
+ if (value.toLowerCase() === allowedHeader) {
2506
+ result.push(value);
2507
+ break;
2508
+ }
2509
+ }
2510
+ }
2511
+ }
2512
+ }
2513
+ if (result.length > 0) {
2514
+ return result;
2515
+ }
2516
+ }
2517
+ function trimTrailingSlash(origin) {
2518
+ return origin.endsWith("/") ? origin.slice(0, -1) : origin;
2519
+ }
2520
+ function getMethodToUse(request, isPreFlight) {
2521
+ return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
2522
+ }
2523
+ function getHeadersToUse(request, isPreFlight) {
2524
+ const headers2 = request.headers;
2525
+ return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
2526
+ }
2527
+ var matchingCorsConfigSource = (opts) => {
2528
+ return async (exchange) => {
2529
+ for (const [matcher, config] of opts.mappings) {
2530
+ if ((await matcher(exchange)).match) {
2531
+ logger9.debug(`resolved cors config on '${exchange.path}' using ${matcher}: ${JSON.stringify(config)}`);
2532
+ return config;
2533
+ }
2534
+ }
2535
+ };
2536
+ };
2537
+
2327
2538
  // src/server/security/config.ts
2328
2539
  var filterOrder = {
2329
2540
  first: Number.MAX_SAFE_INTEGER,
@@ -2337,7 +2548,7 @@ var filterOrder = {
2337
2548
  last: Number.MAX_SAFE_INTEGER
2338
2549
  };
2339
2550
  var filterOrderSymbol = Symbol.for("filterOrder");
2340
- var config_default = (config, storage) => {
2551
+ var config_default = (config, context) => {
2341
2552
  const middleware = [];
2342
2553
  class ServerHttpSecurity {
2343
2554
  #authenticationEntryPoint;
@@ -2361,6 +2572,11 @@ var config_default = (config, storage) => {
2361
2572
  writer[filterOrderSymbol] = filterOrder.http_headers;
2362
2573
  middleware.push(writer);
2363
2574
  }
2575
+ if (config.cors?.disabled !== true && context.corsConfigSource !== void 0) {
2576
+ const filter = cors_default({ corsConfigSource: context.corsConfigSource });
2577
+ filter[filterOrderSymbol] = filterOrder.cors;
2578
+ middleware.push(filter);
2579
+ }
2364
2580
  if (config.basic !== void 0 && config.basic?.disabled !== true) {
2365
2581
  const username = config.basic.user?.name.toLowerCase();
2366
2582
  const password = config.basic.user?.password ?? "";
@@ -2379,7 +2595,7 @@ var config_default = (config, storage) => {
2379
2595
  }
2380
2596
  ];
2381
2597
  const filter = httpBasic({
2382
- storage,
2598
+ storage: context.storage,
2383
2599
  manager,
2384
2600
  defaultEntryPoints: this.#defaultEntryPoints,
2385
2601
  defaultSuccessHandlers
@@ -2389,20 +2605,43 @@ var config_default = (config, storage) => {
2389
2605
  }
2390
2606
  if (config.jwt !== void 0 && config.jwt.disabled !== true) {
2391
2607
  const verifier = jwtVerifier({
2392
- issuerBaseUri: config.jwt.issuerBaseUri,
2608
+ issuerBaseUri: config.jwt.issuerUri,
2393
2609
  issuer: config.jwt.issuer,
2394
2610
  audience: config.jwt.audience
2395
2611
  });
2396
2612
  const decoder = async (token) => {
2397
- const { payload } = await verifier(token);
2398
- return {
2399
- subject: payload.sub,
2400
- getClaimAsString(claim) {
2401
- return payload[claim];
2613
+ try {
2614
+ const { payload } = await verifier(token);
2615
+ return {
2616
+ subject: payload.sub,
2617
+ getClaimAsString(claim) {
2618
+ return payload[claim];
2619
+ }
2620
+ };
2621
+ } catch (e) {
2622
+ if (e instanceof JwtVerifyError) {
2623
+ throw new BadJwtError(e.message, { cause: e });
2402
2624
  }
2403
- };
2625
+ throw new JwtError("error occurred while attempting to decoding jwt", { cause: e });
2626
+ }
2404
2627
  };
2405
- const filter = resourceServer({ storage, jwt: { decoder } });
2628
+ const authenticationConverter = token_converter_default({ uriQueryParameter: true });
2629
+ const authenticationConverterMatcher = async (exchange) => {
2630
+ try {
2631
+ const a = await authenticationConverter(exchange);
2632
+ return { match: a !== void 0 };
2633
+ } catch (e) {
2634
+ return { match: false };
2635
+ }
2636
+ };
2637
+ const entryPoint = token_entry_point_default({});
2638
+ this.#defaultEntryPoints.push([authenticationConverterMatcher, entryPoint]);
2639
+ const filter = resourceServer({
2640
+ storage: context.storage,
2641
+ entryPoint,
2642
+ converter: authenticationConverter,
2643
+ jwt: { decoder }
2644
+ });
2406
2645
  filter[filterOrderSymbol] = filterOrder.authentication;
2407
2646
  middleware.push(filter);
2408
2647
  }
@@ -2424,10 +2663,12 @@ var config_default = (config, storage) => {
2424
2663
  serverMatcher = matcher;
2425
2664
  }
2426
2665
  let manager2;
2427
- if (access2.access === "permit-all") {
2666
+ if (access2.access === "permitted") {
2428
2667
  manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(true));
2429
- } else if (access2.access === "deny-all") {
2668
+ manager2.toString = () => "AuthorizationManager[permitted]";
2669
+ } else if (access2.access === "denied") {
2430
2670
  manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(false));
2671
+ manager2.toString = () => "AuthorizationManager[denied]";
2431
2672
  } else if (access2.access === "authenticated") {
2432
2673
  manager2 = new DefaultAuthorizationManager(async (p) => {
2433
2674
  const authentication = await p;
@@ -2436,6 +2677,7 @@ var config_default = (config, storage) => {
2436
2677
  }
2437
2678
  return new AuthorizationDecision(false);
2438
2679
  });
2680
+ manager2.toString = () => "AuthorizationManager[authenticated]";
2439
2681
  } else {
2440
2682
  throw new Error(`Unknown access type: ${JSON.stringify(access2)}`);
2441
2683
  }
@@ -2444,7 +2686,7 @@ var config_default = (config, storage) => {
2444
2686
  return delegatingAuthorizationManager({ mappings });
2445
2687
  };
2446
2688
  const manager = buildAuthorizationManager(config.authorize);
2447
- const filter = authorizationFilter({ manager, storage });
2689
+ const filter = authorizationFilter({ manager, storage: context.storage });
2448
2690
  filter[filterOrderSymbol] = filterOrder.authorization;
2449
2691
  middleware.push(filter);
2450
2692
  }
@@ -2460,8 +2702,80 @@ var config_default = (config, storage) => {
2460
2702
  return middleware;
2461
2703
  };
2462
2704
 
2705
+ // src/app/cors.ts
2706
+ function mockUpgradeExchange(path) {
2707
+ const request = new MockHttpRequest(path, "GET");
2708
+ request.headers.set("upgrade", "websocket");
2709
+ request["_req"] = { upgrade: true };
2710
+ return new DefaultWebExchange(request, new MockHttpResponse());
2711
+ }
2712
+ async function createCorsConfigSource(context) {
2713
+ const { sockets: routes3, cors } = context;
2714
+ const defaultCorsConfig = combineCorsConfig(PERMIT_DEFAULT_CONFIG, context.corsConfig);
2715
+ const validatedConfigs = [];
2716
+ for (const [path, route] of routes3) {
2717
+ let routeCorsConfig = defaultCorsConfig;
2718
+ const upgradeExchange = mockUpgradeExchange(path);
2719
+ for (const [matcher, config] of cors) {
2720
+ if ((await matcher(upgradeExchange)).match) {
2721
+ routeCorsConfig = combineCorsConfig(routeCorsConfig, config);
2722
+ }
2723
+ }
2724
+ routeCorsConfig = combineCorsConfig(routeCorsConfig, {
2725
+ allowOrigins: route.originFilters?.allow,
2726
+ allowMethods: ["GET", "CONNECT"],
2727
+ allowHeaders: ["upgrade", "connection", "origin", "sec-websocket-key", "sec-websocket-version", "sec-websocket-protocol", "sec-websocket-extensions"],
2728
+ exposeHeaders: ["sec-websocket-accept", "sec-websocket-protocol", "sec-websocket-extensions"],
2729
+ allowCredentials: route.authorize?.access !== "permitted"
2730
+ });
2731
+ validatedConfigs.push([and([upgradeMatcher, pattern(path)]), validateCorsConfig(routeCorsConfig)]);
2732
+ }
2733
+ for (const [matcher, config] of cors) {
2734
+ const routeCorsConfig = combineCorsConfig(defaultCorsConfig, config);
2735
+ validatedConfigs.push([matcher, validateCorsConfig(routeCorsConfig)]);
2736
+ }
2737
+ validatedConfigs.push([pattern(/\/api\/.*/), validateCorsConfig(defaultCorsConfig)]);
2738
+ return matchingCorsConfigSource({ mappings: validatedConfigs });
2739
+ }
2740
+
2741
+ // src/app/auth.ts
2742
+ function createSecurityConfig(context) {
2743
+ const authorize = [];
2744
+ const defaultAccess = { access: context.authConfig?.type !== "none" ? "authenticated" : "permitted" };
2745
+ for (const [path, route] of context.sockets) {
2746
+ const rule = route.authorize ?? defaultAccess;
2747
+ let matcher = pattern(path, { method: "GET" });
2748
+ matcher = and([upgradeMatcher, matcher]);
2749
+ authorize.push([matcher, rule]);
2750
+ }
2751
+ authorize.push([pattern("/", { method: "GET" }), { access: "permitted" }]);
2752
+ authorize.push([pattern("/favicon.ico", { method: "GET" }), { access: "permitted" }]);
2753
+ authorize.push([pattern("/health", { method: "GET" }), { access: "permitted" }]);
2754
+ if (context.authorize.length > 0) {
2755
+ authorize.push(...context.authorize);
2756
+ }
2757
+ authorize.push(["any-exchange", defaultAccess]);
2758
+ return {
2759
+ authorize,
2760
+ basic: {
2761
+ disabled: context.authConfig?.type !== "basic",
2762
+ ...context.authConfig?.basic
2763
+ },
2764
+ jwt: {
2765
+ disabled: context.authConfig?.type !== "oauth2",
2766
+ ...context.authConfig?.oauth2?.jwt
2767
+ }
2768
+ };
2769
+ }
2770
+ async function httpSecurity(context) {
2771
+ const corsConfigSource = await createCorsConfigSource(context);
2772
+ const config = createSecurityConfig(context);
2773
+ const { storage } = context;
2774
+ return config_default(config, { storage, corsConfigSource });
2775
+ }
2776
+
2463
2777
  // src/server.ts
2464
- var logger7 = getLogger("app");
2778
+ var logger10 = getLogger("app");
2465
2779
  function secureContextOptions(ssl) {
2466
2780
  const options = {};
2467
2781
  if (ssl.key) options.key = readFileSync(ssl.key);
@@ -2469,60 +2783,38 @@ function secureContextOptions(ssl) {
2469
2783
  if (ssl.ca) options.ca = readFileSync(ssl.ca);
2470
2784
  return options;
2471
2785
  }
2472
- function createListener(storage, middleware, routes3, onSocketError) {
2786
+ async function createListener(middleware, context, onSocketError) {
2787
+ const storage = context.storage;
2788
+ const security = await httpSecurity(context);
2473
2789
  const listener = compose(
2474
- server_header_default(),
2475
- ...config_default({
2476
- authorize: [
2477
- [async (exchange) => {
2478
- return { match: exchange.path === "/health" && exchange.method === "GET" };
2479
- }, { access: "permit-all" }],
2480
- // ['any-exchange', {access: 'authenticated'}],
2481
- ["any-exchange", { access: "permit-all" }]
2482
- ],
2483
- basic: {
2484
- disabled: true,
2485
- realm: "Gateway Server"
2486
- },
2487
- jwt: {
2488
- disabled: true
2489
- }
2490
- }, storage),
2491
- ...cors_default({
2492
- origins: { allow: [/http:\/\/localhost(:\d+)?/, /file:\//] },
2493
- methods: { allow: ["GET", "HEAD", "POST", "DELETE"] },
2494
- headers: { allow: "*" },
2495
- credentials: { allow: true }
2496
- }),
2497
- ...middleware,
2498
- async ({ request, response }, next) => {
2499
- const path = request.path ?? "/";
2500
- const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
2501
- if (path === "/" && route2.default === true) {
2502
- return true;
2503
- }
2504
- });
2505
- if (route) {
2506
- if (request.method === "GET" && request._req["upgrade"] && request.headers.one("upgrade")?.toLowerCase() === "websocket") {
2790
+ server_header_default(context.serverHeader),
2791
+ ...security,
2792
+ // websocket upgrade handler
2793
+ async (exchange, next) => {
2794
+ const [route, path] = findSocketRoute(exchange, context);
2795
+ if (route !== void 0) {
2796
+ const { request, response } = exchange;
2797
+ const upgradeMatchResult = await upgradeMatcher(exchange);
2798
+ if ((request.method === "GET" || request.method === "CONNECT") && upgradeMatchResult.match) {
2507
2799
  const socket = request.socket;
2508
2800
  const host = request.host;
2509
- const info = socketKey(request._req.socket);
2510
- if (route?.wss) {
2801
+ const info2 = socketKey(request._req.socket);
2802
+ if (route.wss) {
2511
2803
  socket.removeListener("error", onSocketError);
2512
2804
  const wss = route.wss;
2513
2805
  if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
2514
- logger7.warn(`${info} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
2806
+ logger10.warn(`${info2} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
2515
2807
  socket.destroy();
2516
2808
  return;
2517
2809
  }
2518
- const origin = request.headers["origin"];
2810
+ const origin = request.headers.one("origin");
2519
2811
  if (!acceptsOrigin(origin, route.originFilters)) {
2520
- logger7.info(`${info} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
2812
+ logger10.info(`${info2} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
2521
2813
  socket.destroy();
2522
2814
  return;
2523
2815
  }
2524
- if (logger7.enabledFor("debug")) {
2525
- logger7.debug(`${info} accepted new ws connection request from ${host} on ${path}`);
2816
+ if (logger10.enabledFor("debug")) {
2817
+ logger10.debug(`${info2} accepted new ws connection request from ${host} on ${path}`);
2526
2818
  }
2527
2819
  wss.handleUpgrade(request._req, socket, request._req["_upgradeHead"], (ws) => {
2528
2820
  response._res["_header"] = true;
@@ -2532,29 +2824,36 @@ function createListener(storage, middleware, routes3, onSocketError) {
2532
2824
  wss.emit("connection", ws, request._req);
2533
2825
  });
2534
2826
  } else {
2535
- logger7.warn(`${info} rejected upgrade request from ${host} on ${path}`);
2827
+ logger10.warn(`${info2} rejected upgrade request from ${host} on ${path}`);
2536
2828
  socket.destroy();
2537
2829
  }
2538
2830
  } else {
2539
- if (logger7.enabledFor("debug")) {
2540
- logger7.debug(`rejecting request for ${path} with method ${request.method} from ${request.socket.remoteAddress}:${request.socket.remotePort} with headers: ${JSON.stringify(request._req.rawHeaders)}`);
2831
+ if (route.default) {
2832
+ await next();
2833
+ return;
2834
+ }
2835
+ if (logger10.enabledFor("debug")) {
2836
+ logger10.debug(`rejecting request for ${path} with method ${request.method} from ${request.socket.remoteAddress}:${request.socket.remotePort} with headers: ${JSON.stringify(request._req.rawHeaders)}`);
2541
2837
  }
2542
2838
  response.statusCode = 426;
2543
- response._res.appendHeader("Upgrade", "websocket").appendHeader("Connection", "Upgrade").appendHeader("Content-Type", "text/plain");
2839
+ response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
2544
2840
  await response.end(`This service [${request.path}] requires use of the websocket protocol.`);
2545
2841
  }
2546
2842
  } else {
2547
2843
  await next();
2548
2844
  }
2549
2845
  },
2846
+ ...middleware,
2847
+ // helth check
2550
2848
  async ({ request, response }, next) => {
2551
2849
  if (request.method === "GET" && request.path === "/health") {
2552
2850
  response.statusCode = 200;
2553
- response._res.end(http.STATUS_CODES[200]);
2851
+ await response.end(http.STATUS_CODES[200]);
2554
2852
  } else {
2555
2853
  await next();
2556
2854
  }
2557
2855
  },
2856
+ // home page
2558
2857
  async ({ request, response }, next) => {
2559
2858
  if (request.method === "GET" && request.path === "/") {
2560
2859
  await response.end(`io.Gateway Server`);
@@ -2562,7 +2861,8 @@ function createListener(storage, middleware, routes3, onSocketError) {
2562
2861
  await next();
2563
2862
  }
2564
2863
  },
2565
- async ({ request, response }, _next) => {
2864
+ // not found
2865
+ async ({ response }, _next) => {
2566
2866
  response.statusCode = 404;
2567
2867
  await response.end(http.STATUS_CODES[404]);
2568
2868
  }
@@ -2571,17 +2871,17 @@ function createListener(storage, middleware, routes3, onSocketError) {
2571
2871
  request.socket.addListener("error", onSocketError);
2572
2872
  const exchange = new DefaultWebExchange(new HttpServerRequest(request), new HttpServerResponse(response));
2573
2873
  return storage.run({ exchange }, async () => {
2574
- if (logger7.enabledFor("debug")) {
2874
+ if (logger10.enabledFor("debug")) {
2575
2875
  const socket = exchange.request._req.socket;
2576
- if (logger7.enabledFor("debug")) {
2577
- logger7.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
2876
+ if (logger10.enabledFor("debug")) {
2877
+ logger10.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
2578
2878
  }
2579
2879
  }
2580
2880
  try {
2581
2881
  return await listener(exchange);
2582
2882
  } catch (e) {
2583
- if (logger7.enabledFor("warn")) {
2584
- logger7.warn(`error processing request for ${exchange.path}`, e);
2883
+ if (logger10.enabledFor("warn")) {
2884
+ logger10.warn(`error processing request for ${exchange.path}`, e);
2585
2885
  }
2586
2886
  } finally {
2587
2887
  await exchange.response.end();
@@ -2616,65 +2916,75 @@ function regexAwareReplacer(_key, value) {
2616
2916
  }
2617
2917
  var Factory = async (options) => {
2618
2918
  const ssl = options.ssl;
2619
- const createServer = ssl ? (options2, handler2) => https.createServer({ ...options2, ...secureContextOptions(ssl) }, handler2) : (options2, handler2) => http.createServer(options2, handler2);
2919
+ const createServer = ssl ? (options2, handler) => https.createServer({ ...options2, ...secureContextOptions(ssl) }, handler) : (options2, handler) => http.createServer(options2, handler);
2620
2920
  const monitor = memoryMonitor(options.memory);
2621
2921
  const middleware = [];
2622
- const routes3 = /* @__PURE__ */ new Map();
2922
+ const context = {
2923
+ corsConfig: options.cors,
2924
+ cors: [],
2925
+ authConfig: options.auth,
2926
+ authorize: [],
2927
+ storage: new AsyncLocalStorage2(),
2928
+ sockets: /* @__PURE__ */ new Map()
2929
+ };
2623
2930
  const gw = IOGateway7.Factory({ ...options.gateway });
2624
2931
  if (options.gateway) {
2625
2932
  const config = options.gateway;
2626
- routes3.set(config.route ?? "/", {
2933
+ const route = config.route ?? "/";
2934
+ context.sockets.set(route, {
2627
2935
  default: config.route === void 0,
2628
2936
  ping: options.gateway.ping,
2629
2937
  factory: core_default.bind(gw),
2630
2938
  maxConnections: config.limits?.max_connections,
2939
+ authorize: config.authorize,
2631
2940
  originFilters: regexifyOriginFilters(config.origins)
2632
2941
  });
2633
2942
  }
2634
2943
  if (options.mesh) {
2635
2944
  const connections = new InMemoryNodeConnections(options.mesh.timeout ?? 6e4);
2636
- middleware.push(...routes_default(connections));
2945
+ const authorize = options.mesh.authorize;
2946
+ middleware.push(...routes_default(connections, context, authorize));
2637
2947
  const ping = options.mesh.ping ?? 3e4;
2638
- routes3.set("/broker", { factory: core_default2, ping });
2639
- routes3.set("/cluster", { factory: core_default4, ping });
2640
- routes3.set("/relays", { factory: core_default3, ping });
2948
+ const originFilters = regexifyOriginFilters(options.mesh.origins);
2949
+ context.sockets.set("/broker", { factory: core_default2, ping, originFilters, authorize });
2950
+ context.sockets.set("/cluster", { factory: core_default4, ping, originFilters, authorize });
2951
+ context.sockets.set("/relays", { factory: core_default3, ping, originFilters, authorize });
2641
2952
  }
2642
2953
  if (options.metrics) {
2643
- middleware.push(...await routes_default2(options.metrics));
2954
+ middleware.push(...await routes_default2(options.metrics, context));
2644
2955
  }
2645
2956
  const ports = portRange(options.port ?? 0);
2646
2957
  const host = options.host;
2647
- const storage = new AsyncLocalStorage2();
2958
+ const onSocketError = (err) => logger10.error(`socket error: ${err}`, err);
2959
+ const listener = await createListener(middleware, context, onSocketError);
2648
2960
  const serverP = new Promise((resolve, reject) => {
2649
- const onSocketError = (err) => logger7.error(`socket error: ${err}`, err);
2650
- const listener = createListener(storage, middleware, routes3, onSocketError);
2651
2961
  const server2 = createServer({}, listener);
2652
2962
  server2.on("error", (e) => {
2653
2963
  if (e["code"] === "EADDRINUSE") {
2654
- logger7.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
2964
+ logger10.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
2655
2965
  const { value: port } = ports.next();
2656
2966
  if (port) {
2657
- logger7.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
2967
+ logger10.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
2658
2968
  server2.close();
2659
2969
  server2.listen(port, host);
2660
2970
  } else {
2661
- logger7.warn(`all configured port(s) ${options.port} are in use. closing...`);
2971
+ logger10.warn(`all configured port(s) ${options.port} are in use. closing...`);
2662
2972
  server2.close();
2663
2973
  reject(e);
2664
2974
  }
2665
2975
  } else {
2666
- logger7.error(`server error: ${e.message}`, e);
2976
+ logger10.error(`server error: ${e.message}`, e);
2667
2977
  reject(e);
2668
2978
  }
2669
2979
  });
2670
2980
  server2.on("listening", async () => {
2671
- const info = server2.address();
2672
- for (const [path, route] of routes3) {
2981
+ const info2 = server2.address();
2982
+ for (const [path, route] of context.sockets) {
2673
2983
  try {
2674
- logger7.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
2984
+ logger10.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
2675
2985
  const wss = new WebSocketServer({ noServer: true });
2676
- const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info.port}${path}`;
2677
- const handler2 = await route.factory({ endpoint, wss, storage });
2986
+ const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info2.port}${path}`;
2987
+ const handler = await route.factory({ endpoint, wss, storage: context.storage });
2678
2988
  const pingInterval = route.ping;
2679
2989
  if (pingInterval) {
2680
2990
  const pingIntervalId = setInterval(() => {
@@ -2691,12 +3001,12 @@ var Factory = async (options) => {
2691
3001
  });
2692
3002
  }
2693
3003
  route.wss = wss;
2694
- route.close = handler2.close?.bind(handler2);
3004
+ route.close = handler.close?.bind(handler);
2695
3005
  } catch (e) {
2696
- logger7.warn(`failed to init route ${path}`, e);
3006
+ logger10.warn(`failed to init route ${path}`, e);
2697
3007
  }
2698
3008
  }
2699
- logger7.info(`http server listening on ${info.address}:${info.port}`);
3009
+ logger10.info(`http server listening on ${info2.address}:${info2.port}`);
2700
3010
  resolve(server2);
2701
3011
  });
2702
3012
  server2.on("upgrade", (req, socket, head) => {
@@ -2707,16 +3017,16 @@ var Factory = async (options) => {
2707
3017
  res.assignSocket(socket);
2708
3018
  listener(req, res);
2709
3019
  } catch (err) {
2710
- logger7.error(`upgrade error: ${err}`, err);
3020
+ logger10.error(`upgrade error: ${err}`, err);
2711
3021
  }
2712
3022
  }).on("close", async () => {
2713
- logger7.info(`http server closed.`);
3023
+ logger10.info(`http server closed.`);
2714
3024
  });
2715
3025
  try {
2716
3026
  const { value: port } = ports.next();
2717
3027
  server2.listen(port, host);
2718
3028
  } catch (e) {
2719
- logger7.error(`error starting web socket server`, e);
3029
+ logger10.error(`error starting web socket server`, e);
2720
3030
  reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
2721
3031
  }
2722
3032
  });
@@ -2724,18 +3034,18 @@ var Factory = async (options) => {
2724
3034
  return new class {
2725
3035
  gateway = gw;
2726
3036
  async close() {
2727
- for (const [path, route] of routes3) {
3037
+ for (const [path, route] of context.sockets) {
2728
3038
  try {
2729
3039
  if (route.close) {
2730
3040
  await route.close();
2731
3041
  }
2732
- logger7.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
3042
+ logger10.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
2733
3043
  route.wss?.clients?.forEach((client) => {
2734
3044
  client.terminate();
2735
3045
  });
2736
3046
  route.wss?.close();
2737
3047
  } catch (e) {
2738
- logger7.warn(`error closing route ${path}`, e);
3048
+ logger10.warn(`error closing route ${path}`, e);
2739
3049
  }
2740
3050
  }
2741
3051
  await promisify((cb) => {