@interopio/gateway-server 0.5.1-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.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();
@@ -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,56 @@ 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
+ var serverHeader = (server) => {
1483
+ const enabled = typeof server === "string";
1484
+ return async ({ response }, next) => {
1485
+ if (server != false && !response.headers.has("server")) {
1486
+ response.headers.set("Server", server);
1344
1487
  }
1345
- responseHeaders.set("Vary", varyHeaders.join(", "));
1346
- }
1347
- try {
1348
- if (!isCorsRequest(request)) {
1488
+ await next();
1489
+ };
1490
+ };
1491
+ var server_header_default = (server = "gateway-server") => serverHeader(server);
1492
+
1493
+ // src/app/route.ts
1494
+ function findSocketRoute({ request }, { sockets: routes3 }) {
1495
+ const path = request.path ?? "/";
1496
+ const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
1497
+ if (path === "/" && route2.default === true) {
1349
1498
  return true;
1350
1499
  }
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;
1500
+ });
1501
+ return [route, path];
1371
1502
  }
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
- });
1503
+
1504
+ // src/server/security/http-headers.ts
1505
+ var staticServerHttpHeadersWriter = (headers2) => {
1506
+ return async (exchange) => {
1507
+ let containsNoHeaders = true;
1508
+ const { response } = exchange;
1509
+ for (const name of headers2.keys()) {
1510
+ if (response.headers.has(name)) {
1511
+ containsNoHeaders = false;
1512
+ }
1386
1513
  }
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();
1514
+ if (containsNoHeaders) {
1515
+ for (const [name, value] of headers2) {
1516
+ response.headers.set(name, value);
1517
+ }
1398
1518
  }
1399
1519
  };
1400
1520
  };
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";
1521
+ var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1522
+ new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
1523
+ );
1524
+ var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
1525
+ new MapHttpHeaders().add("x-content-type-options", "nosniff")
1526
+ );
1527
+ var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
1528
+ let headerValue = `max-age=${maxAgeInSeconds}`;
1529
+ if (includeSubDomains) {
1530
+ headerValue += " ; includeSubDomains";
1577
1531
  }
1578
1532
  if (preload) {
1579
1533
  headerValue += " ; preload";
@@ -1733,18 +1687,18 @@ var AuthorizationDecision = class {
1733
1687
  granted;
1734
1688
  };
1735
1689
  var DefaultAuthorizationManager = class {
1736
- check;
1690
+ #check;
1737
1691
  constructor(check) {
1738
- this.check = check;
1692
+ this.#check = check;
1739
1693
  }
1740
1694
  async verify(authentication, object) {
1741
- const decision = await this.check(authentication, object);
1695
+ const decision = await this.#check(authentication, object);
1742
1696
  if (!decision?.granted) {
1743
1697
  throw new AccessDeniedError("Access denied");
1744
1698
  }
1745
1699
  }
1746
1700
  async authorize(authentication, object) {
1747
- return await this.check(authentication, object);
1701
+ return await this.#check(authentication, object);
1748
1702
  }
1749
1703
  };
1750
1704
  var AuthenticationServiceError = class extends AuthenticationError {
@@ -1798,71 +1752,6 @@ var httpBasicAuthenticationConverter = (opts) => {
1798
1752
  };
1799
1753
  };
1800
1754
 
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
1755
  // src/server/security/security-context.ts
1867
1756
  import { AsyncLocalStorage } from "node:async_hooks";
1868
1757
  var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
@@ -2176,6 +2065,7 @@ var httpStatusEntryPoint = (opts) => {
2176
2065
  };
2177
2066
 
2178
2067
  // src/server/security/delegating-entry-point.ts
2068
+ var logger6 = getLogger("auth.entry-point");
2179
2069
  var delegatingEntryPoint = (opts) => {
2180
2070
  const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
2181
2071
  response.statusCode = 401;
@@ -2183,11 +2073,20 @@ var delegatingEntryPoint = (opts) => {
2183
2073
  });
2184
2074
  return async (exchange, error) => {
2185
2075
  for (const [matcher, entryPoint] of opts.entryPoints) {
2076
+ if (logger6.enabledFor("debug")) {
2077
+ logger6.debug(`trying to match using: ${matcher}`);
2078
+ }
2186
2079
  const match = await matcher(exchange);
2187
2080
  if (match.match) {
2081
+ if (logger6.enabledFor("debug")) {
2082
+ logger6.debug(`match found. using default entry point ${entryPoint}`);
2083
+ }
2188
2084
  return entryPoint(exchange, error);
2189
2085
  }
2190
2086
  }
2087
+ if (logger6.enabledFor("debug")) {
2088
+ logger6.debug(`no match found. using default entry point ${defaultEntryPoint}`);
2089
+ }
2191
2090
  return defaultEntryPoint(exchange, error);
2192
2091
  };
2193
2092
  };
@@ -2195,8 +2094,8 @@ var delegatingEntryPoint = (opts) => {
2195
2094
  // src/server/security/delegating-success-handler.ts
2196
2095
  var delegatingSuccessHandler = (handlers) => {
2197
2096
  return async ({ exchange, next }, authentication) => {
2198
- for (const handler2 of handlers) {
2199
- await handler2({ exchange, next }, authentication);
2097
+ for (const handler of handlers) {
2098
+ await handler({ exchange, next }, authentication);
2200
2099
  }
2201
2100
  };
2202
2101
  };
@@ -2246,7 +2145,7 @@ function httpBasic(opts) {
2246
2145
  }
2247
2146
 
2248
2147
  // src/server/security/config.ts
2249
- import { jwtVerifier } from "@interopio/gateway/jose/jwt";
2148
+ import { jwtVerifier, JwtVerifyError } from "@interopio/gateway/jose/jwt";
2250
2149
 
2251
2150
  // src/server/security/error-filter.ts
2252
2151
  async function commenceAuthentication(exchange, authentication, entryPoint) {
@@ -2290,14 +2189,21 @@ var errorFilter = (opts) => {
2290
2189
  };
2291
2190
 
2292
2191
  // src/server/security/authorization-filter.ts
2192
+ var logger7 = getLogger("security.auth");
2293
2193
  function authorizationFilter(opts) {
2294
2194
  const { manager, storage } = opts;
2295
2195
  return async (exchange, next) => {
2296
2196
  const promise = AsyncStorageSecurityContextHolder.getContext(storage).then((c) => c?.authentication);
2297
2197
  try {
2298
2198
  await manager.verify(promise, exchange);
2199
+ if (logger7.enabledFor("debug")) {
2200
+ logger7.debug("authorization successful");
2201
+ }
2299
2202
  } catch (error) {
2300
2203
  if (error instanceof AccessDeniedError) {
2204
+ if (logger7.enabledFor("debug")) {
2205
+ logger7.debug(`authorization failed: ${error.message}`);
2206
+ }
2301
2207
  }
2302
2208
  throw error;
2303
2209
  }
@@ -2306,12 +2212,14 @@ function authorizationFilter(opts) {
2306
2212
  }
2307
2213
 
2308
2214
  // src/server/security/delegating-authorization-manager.ts
2215
+ var logger8 = getLogger("auth");
2309
2216
  function delegatingAuthorizationManager(opts) {
2310
2217
  const check = async (authentication, exchange) => {
2311
2218
  let decision;
2312
2219
  for (const [matcher, manager] of opts.mappings) {
2313
2220
  if ((await matcher(exchange))?.match) {
2314
- const checkResult = await manager.check(authentication, { exchange });
2221
+ logger8.debug(`checking authorization on '${exchange.path}' using [${matcher}, ${manager}]`);
2222
+ const checkResult = await manager.authorize(authentication, { exchange });
2315
2223
  if (checkResult !== void 0) {
2316
2224
  decision = checkResult;
2317
2225
  break;
@@ -2324,6 +2232,308 @@ function delegatingAuthorizationManager(opts) {
2324
2232
  return new DefaultAuthorizationManager(check);
2325
2233
  }
2326
2234
 
2235
+ // src/server/cors.ts
2236
+ import { IOGateway as IOGateway6 } from "@interopio/gateway";
2237
+ function isSameOrigin(request) {
2238
+ const origin = request.headers.one("origin");
2239
+ if (origin === void 0) {
2240
+ return true;
2241
+ }
2242
+ const url = request.URL;
2243
+ const actualProtocol = url.protocol;
2244
+ const actualHost = url.host;
2245
+ const originUrl = URL.parse(origin);
2246
+ const originHost = originUrl?.host;
2247
+ const originProtocol = originUrl?.protocol;
2248
+ return actualProtocol === originProtocol && actualHost === originHost;
2249
+ }
2250
+ function isCorsRequest(request) {
2251
+ return request.headers.has("origin") && !isSameOrigin(request);
2252
+ }
2253
+ function isPreFlightRequest(request) {
2254
+ return request.method === "OPTIONS" && request.headers.has("origin") && request.headers.has("access-control-request-method");
2255
+ }
2256
+ var VARY_HEADERS = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"];
2257
+ var processRequest = (exchange, config) => {
2258
+ const { request, response } = exchange;
2259
+ const responseHeaders = response.headers;
2260
+ if (!responseHeaders.has("Vary")) {
2261
+ responseHeaders.set("Vary", VARY_HEADERS.join(", "));
2262
+ } else {
2263
+ const varyHeaders = responseHeaders.list("Vary");
2264
+ for (const header of VARY_HEADERS) {
2265
+ if (!varyHeaders.find((h) => h === header)) {
2266
+ varyHeaders.push(header);
2267
+ }
2268
+ }
2269
+ responseHeaders.set("Vary", varyHeaders.join(", "));
2270
+ }
2271
+ try {
2272
+ if (!isCorsRequest(request)) {
2273
+ return true;
2274
+ }
2275
+ } catch (e) {
2276
+ if (logger9.enabledFor("debug")) {
2277
+ logger9.debug(`reject: origin is malformed`);
2278
+ }
2279
+ rejectRequest(response);
2280
+ return false;
2281
+ }
2282
+ if (responseHeaders.has("access-control-allow-origin")) {
2283
+ logger9.trace(`skip: already contains "Access-Control-Allow-Origin"`);
2284
+ return true;
2285
+ }
2286
+ const preFlightRequest = isPreFlightRequest(request);
2287
+ if (config) {
2288
+ return handleInternal(exchange, config, preFlightRequest);
2289
+ }
2290
+ if (preFlightRequest) {
2291
+ rejectRequest(response);
2292
+ return false;
2293
+ }
2294
+ return true;
2295
+ };
2296
+ var DEFAULT_PERMIT_ALL = ["*"];
2297
+ var DEFAULT_PERMIT_METHODS = ["GET", "HEAD", "POST"];
2298
+ var PERMIT_DEFAULT_CONFIG = {
2299
+ allowOrigins: DEFAULT_PERMIT_ALL,
2300
+ allowMethods: DEFAULT_PERMIT_METHODS,
2301
+ allowHeaders: DEFAULT_PERMIT_ALL,
2302
+ maxAge: 1800
2303
+ // 30 minutes
2304
+ };
2305
+ function validateCorsConfig(config) {
2306
+ if (config) {
2307
+ const allowHeaders = config.allowHeaders;
2308
+ if (allowHeaders && allowHeaders !== ALL) {
2309
+ config = {
2310
+ ...config,
2311
+ allowHeaders: allowHeaders.map((header) => header.toLowerCase())
2312
+ };
2313
+ }
2314
+ const allowOrigins = config.allowOrigins;
2315
+ if (allowOrigins) {
2316
+ if (allowOrigins === "*") {
2317
+ validateAllowCredentials(config);
2318
+ validateAllowPrivateNetwork(config);
2319
+ } else {
2320
+ config = {
2321
+ ...config,
2322
+ allowOrigins: allowOrigins.map((origin) => {
2323
+ if (typeof origin === "string" && origin !== ALL) {
2324
+ origin = IOGateway6.Filtering.regexify(origin);
2325
+ if (typeof origin === "string") {
2326
+ return trimTrailingSlash(origin).toLowerCase();
2327
+ }
2328
+ }
2329
+ return origin;
2330
+ })
2331
+ };
2332
+ }
2333
+ }
2334
+ return config;
2335
+ }
2336
+ }
2337
+ function combine(source, other) {
2338
+ if (other === void 0) {
2339
+ return source !== void 0 ? source === ALL ? [ALL] : source : [];
2340
+ }
2341
+ if (source === void 0) {
2342
+ return other === ALL ? [ALL] : other;
2343
+ }
2344
+ if (source == DEFAULT_PERMIT_ALL || source === DEFAULT_PERMIT_METHODS) {
2345
+ return other === ALL ? [ALL] : other;
2346
+ }
2347
+ if (other == DEFAULT_PERMIT_ALL || other === DEFAULT_PERMIT_METHODS) {
2348
+ return source === ALL ? [ALL] : source;
2349
+ }
2350
+ if (source === ALL || source.includes(ALL) || other === ALL || other.includes(ALL)) {
2351
+ return [ALL];
2352
+ }
2353
+ const combined = /* @__PURE__ */ new Set();
2354
+ source.forEach((v) => combined.add(v));
2355
+ other.forEach((v) => combined.add(v));
2356
+ return Array.from(combined);
2357
+ }
2358
+ var combineCorsConfig = (source, other) => {
2359
+ if (other === void 0) {
2360
+ return source;
2361
+ }
2362
+ const config = {
2363
+ allowOrigins: combine(source.allowOrigins, other?.allowOrigins),
2364
+ allowMethods: combine(source.allowMethods, other?.allowMethods),
2365
+ allowHeaders: combine(source.allowHeaders, other?.allowHeaders),
2366
+ exposeHeaders: combine(source.exposeHeaders, other?.exposeHeaders),
2367
+ allowCredentials: other?.allowCredentials ?? source.allowCredentials,
2368
+ allowPrivateNetwork: other?.allowPrivateNetwork ?? source.allowPrivateNetwork,
2369
+ maxAge: other?.maxAge ?? source.maxAge
2370
+ };
2371
+ return config;
2372
+ };
2373
+ var corsFilter = (opts) => {
2374
+ const source = opts.corsConfigSource;
2375
+ const processor = opts.corsProcessor ?? processRequest;
2376
+ return async (ctx, next) => {
2377
+ const config = await source(ctx);
2378
+ const isValid = processor(ctx, config);
2379
+ if (!isValid || isPreFlightRequest(ctx.request)) {
2380
+ return;
2381
+ } else {
2382
+ await next();
2383
+ }
2384
+ };
2385
+ };
2386
+ var cors_default = corsFilter;
2387
+ var logger9 = getLogger("cors");
2388
+ function rejectRequest(response) {
2389
+ response.statusCode = 403;
2390
+ }
2391
+ function handleInternal(exchange, config, preFlightRequest) {
2392
+ const { request, response } = exchange;
2393
+ const responseHeaders = response.headers;
2394
+ const requestOrigin = request.headers.one("origin");
2395
+ const allowOrigin = checkOrigin(config, requestOrigin);
2396
+ if (allowOrigin === void 0) {
2397
+ if (logger9.enabledFor("debug")) {
2398
+ logger9.debug(`reject: '${requestOrigin}' origin is not allowed`);
2399
+ }
2400
+ rejectRequest(response);
2401
+ return false;
2402
+ }
2403
+ const requestMethod = getMethodToUse(request, preFlightRequest);
2404
+ const allowMethods = checkMethods(config, requestMethod);
2405
+ if (allowMethods === void 0) {
2406
+ if (logger9.enabledFor("debug")) {
2407
+ logger9.debug(`reject: HTTP '${requestMethod}' is not allowed`);
2408
+ }
2409
+ rejectRequest(response);
2410
+ return false;
2411
+ }
2412
+ const requestHeaders = getHeadersToUse(request, preFlightRequest);
2413
+ const allowHeaders = checkHeaders(config, requestHeaders);
2414
+ if (preFlightRequest && allowHeaders === void 0) {
2415
+ if (logger9.enabledFor("debug")) {
2416
+ logger9.debug(`reject: headers '${requestHeaders}' are not allowed`);
2417
+ }
2418
+ rejectRequest(response);
2419
+ return false;
2420
+ }
2421
+ responseHeaders.set("Access-Control-Allow-Origin", allowOrigin);
2422
+ if (preFlightRequest) {
2423
+ responseHeaders.set("Access-Control-Allow-Methods", allowMethods.join(","));
2424
+ }
2425
+ if (preFlightRequest && allowHeaders !== void 0 && allowHeaders.length > 0) {
2426
+ responseHeaders.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
2427
+ }
2428
+ const exposeHeaders = config.exposeHeaders;
2429
+ if (exposeHeaders && exposeHeaders.length > 0) {
2430
+ responseHeaders.set("Access-Control-Expose-Headers", exposeHeaders.join(", "));
2431
+ }
2432
+ if (config.allowCredentials) {
2433
+ responseHeaders.set("Access-Control-Allow-Credentials", "true");
2434
+ }
2435
+ if (config.allowPrivateNetwork && request.headers.one("access-control-request-private-network") === "true") {
2436
+ responseHeaders.set("Access-Control-Allow-Private-Network", "true");
2437
+ }
2438
+ if (preFlightRequest && config.maxAge !== void 0) {
2439
+ responseHeaders.set("Access-Control-Max-Age", config.maxAge.toString());
2440
+ }
2441
+ return true;
2442
+ }
2443
+ var ALL = "*";
2444
+ var DEFAULT_METHODS = ["GET", "HEAD"];
2445
+ function validateAllowCredentials(config) {
2446
+ if (config.allowCredentials === true && config.allowOrigins === ALL) {
2447
+ throw new Error(`when allowCredentials is true allowOrigins cannot be "*"`);
2448
+ }
2449
+ }
2450
+ function validateAllowPrivateNetwork(config) {
2451
+ if (config.allowPrivateNetwork === true && config.allowOrigins === ALL) {
2452
+ throw new Error(`when allowPrivateNetwork is true allowOrigins cannot be "*"`);
2453
+ }
2454
+ }
2455
+ function checkOrigin(config, origin) {
2456
+ if (origin) {
2457
+ const allowedOrigins = config.allowOrigins;
2458
+ if (allowedOrigins) {
2459
+ if (allowedOrigins === ALL) {
2460
+ validateAllowCredentials(config);
2461
+ validateAllowPrivateNetwork(config);
2462
+ return ALL;
2463
+ }
2464
+ const originToCheck = trimTrailingSlash(origin.toLowerCase());
2465
+ for (const allowedOrigin of allowedOrigins) {
2466
+ if (allowedOrigin === ALL || IOGateway6.Filtering.valueMatches(allowedOrigin, originToCheck)) {
2467
+ return origin;
2468
+ }
2469
+ }
2470
+ }
2471
+ }
2472
+ }
2473
+ function checkMethods(config, requestMethod) {
2474
+ if (requestMethod) {
2475
+ const allowedMethods = config.allowMethods ?? DEFAULT_METHODS;
2476
+ if (allowedMethods === ALL) {
2477
+ return [requestMethod];
2478
+ }
2479
+ if (IOGateway6.Filtering.valuesMatch(allowedMethods, requestMethod)) {
2480
+ return allowedMethods;
2481
+ }
2482
+ }
2483
+ }
2484
+ function checkHeaders(config, requestHeaders) {
2485
+ if (requestHeaders === void 0) {
2486
+ return;
2487
+ }
2488
+ if (requestHeaders.length == 0) {
2489
+ return [];
2490
+ }
2491
+ const allowedHeaders = config.allowHeaders;
2492
+ if (allowedHeaders === void 0) {
2493
+ return;
2494
+ }
2495
+ const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
2496
+ const result = [];
2497
+ for (const requestHeader of requestHeaders) {
2498
+ const value = requestHeader?.trim();
2499
+ if (value) {
2500
+ if (allowAnyHeader) {
2501
+ result.push(value);
2502
+ } else {
2503
+ for (const allowedHeader of allowedHeaders) {
2504
+ if (value.toLowerCase() === allowedHeader) {
2505
+ result.push(value);
2506
+ break;
2507
+ }
2508
+ }
2509
+ }
2510
+ }
2511
+ }
2512
+ if (result.length > 0) {
2513
+ return result;
2514
+ }
2515
+ }
2516
+ function trimTrailingSlash(origin) {
2517
+ return origin.endsWith("/") ? origin.slice(0, -1) : origin;
2518
+ }
2519
+ function getMethodToUse(request, isPreFlight) {
2520
+ return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
2521
+ }
2522
+ function getHeadersToUse(request, isPreFlight) {
2523
+ const headers2 = request.headers;
2524
+ return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
2525
+ }
2526
+ var matchingCorsConfigSource = (opts) => {
2527
+ return async (exchange) => {
2528
+ for (const [matcher, config] of opts.mappings) {
2529
+ if ((await matcher(exchange)).match) {
2530
+ logger9.debug(`resolved cors config on '${exchange.path}' using ${matcher}: ${JSON.stringify(config)}`);
2531
+ return config;
2532
+ }
2533
+ }
2534
+ };
2535
+ };
2536
+
2327
2537
  // src/server/security/config.ts
2328
2538
  var filterOrder = {
2329
2539
  first: Number.MAX_SAFE_INTEGER,
@@ -2337,7 +2547,7 @@ var filterOrder = {
2337
2547
  last: Number.MAX_SAFE_INTEGER
2338
2548
  };
2339
2549
  var filterOrderSymbol = Symbol.for("filterOrder");
2340
- var config_default = (config, storage) => {
2550
+ var config_default = (config, context) => {
2341
2551
  const middleware = [];
2342
2552
  class ServerHttpSecurity {
2343
2553
  #authenticationEntryPoint;
@@ -2361,6 +2571,11 @@ var config_default = (config, storage) => {
2361
2571
  writer[filterOrderSymbol] = filterOrder.http_headers;
2362
2572
  middleware.push(writer);
2363
2573
  }
2574
+ if (config.cors?.disabled !== true && context.corsConfigSource !== void 0) {
2575
+ const filter = cors_default({ corsConfigSource: context.corsConfigSource });
2576
+ filter[filterOrderSymbol] = filterOrder.cors;
2577
+ middleware.push(filter);
2578
+ }
2364
2579
  if (config.basic !== void 0 && config.basic?.disabled !== true) {
2365
2580
  const username = config.basic.user?.name.toLowerCase();
2366
2581
  const password = config.basic.user?.password ?? "";
@@ -2379,7 +2594,7 @@ var config_default = (config, storage) => {
2379
2594
  }
2380
2595
  ];
2381
2596
  const filter = httpBasic({
2382
- storage,
2597
+ storage: context.storage,
2383
2598
  manager,
2384
2599
  defaultEntryPoints: this.#defaultEntryPoints,
2385
2600
  defaultSuccessHandlers
@@ -2389,20 +2604,43 @@ var config_default = (config, storage) => {
2389
2604
  }
2390
2605
  if (config.jwt !== void 0 && config.jwt.disabled !== true) {
2391
2606
  const verifier = jwtVerifier({
2392
- issuerBaseUri: config.jwt.issuerBaseUri,
2607
+ issuerBaseUri: config.jwt.issuerUri,
2393
2608
  issuer: config.jwt.issuer,
2394
2609
  audience: config.jwt.audience
2395
2610
  });
2396
2611
  const decoder = async (token) => {
2397
- const { payload } = await verifier(token);
2398
- return {
2399
- subject: payload.sub,
2400
- getClaimAsString(claim) {
2401
- return payload[claim];
2612
+ try {
2613
+ const { payload } = await verifier(token);
2614
+ return {
2615
+ subject: payload.sub,
2616
+ getClaimAsString(claim) {
2617
+ return payload[claim];
2618
+ }
2619
+ };
2620
+ } catch (e) {
2621
+ if (e instanceof JwtVerifyError) {
2622
+ throw new BadJwtError(e.message, { cause: e });
2402
2623
  }
2403
- };
2624
+ throw new JwtError("error occurred while attempting to decoding jwt", { cause: e });
2625
+ }
2404
2626
  };
2405
- const filter = resourceServer({ storage, jwt: { decoder } });
2627
+ const authenticationConverter = token_converter_default({ uriQueryParameter: true });
2628
+ const authenticationConverterMatcher = async (exchange) => {
2629
+ try {
2630
+ const a = await authenticationConverter(exchange);
2631
+ return { match: a !== void 0 };
2632
+ } catch (e) {
2633
+ return { match: false };
2634
+ }
2635
+ };
2636
+ const entryPoint = token_entry_point_default({});
2637
+ this.#defaultEntryPoints.push([authenticationConverterMatcher, entryPoint]);
2638
+ const filter = resourceServer({
2639
+ storage: context.storage,
2640
+ entryPoint,
2641
+ converter: authenticationConverter,
2642
+ jwt: { decoder }
2643
+ });
2406
2644
  filter[filterOrderSymbol] = filterOrder.authentication;
2407
2645
  middleware.push(filter);
2408
2646
  }
@@ -2424,10 +2662,12 @@ var config_default = (config, storage) => {
2424
2662
  serverMatcher = matcher;
2425
2663
  }
2426
2664
  let manager2;
2427
- if (access2.access === "permit-all") {
2665
+ if (access2.access === "permitted") {
2428
2666
  manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(true));
2429
- } else if (access2.access === "deny-all") {
2667
+ manager2.toString = () => "AuthorizationManager[permitted]";
2668
+ } else if (access2.access === "denied") {
2430
2669
  manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(false));
2670
+ manager2.toString = () => "AuthorizationManager[denied]";
2431
2671
  } else if (access2.access === "authenticated") {
2432
2672
  manager2 = new DefaultAuthorizationManager(async (p) => {
2433
2673
  const authentication = await p;
@@ -2436,6 +2676,7 @@ var config_default = (config, storage) => {
2436
2676
  }
2437
2677
  return new AuthorizationDecision(false);
2438
2678
  });
2679
+ manager2.toString = () => "AuthorizationManager[authenticated]";
2439
2680
  } else {
2440
2681
  throw new Error(`Unknown access type: ${JSON.stringify(access2)}`);
2441
2682
  }
@@ -2444,7 +2685,7 @@ var config_default = (config, storage) => {
2444
2685
  return delegatingAuthorizationManager({ mappings });
2445
2686
  };
2446
2687
  const manager = buildAuthorizationManager(config.authorize);
2447
- const filter = authorizationFilter({ manager, storage });
2688
+ const filter = authorizationFilter({ manager, storage: context.storage });
2448
2689
  filter[filterOrderSymbol] = filterOrder.authorization;
2449
2690
  middleware.push(filter);
2450
2691
  }
@@ -2460,8 +2701,79 @@ var config_default = (config, storage) => {
2460
2701
  return middleware;
2461
2702
  };
2462
2703
 
2704
+ // src/app/cors.ts
2705
+ function mockUpgradeExchange(path) {
2706
+ const request = new MockHttpRequest(path, "GET");
2707
+ request.headers.set("upgrade", "websocket");
2708
+ request["_req"] = { upgrade: true };
2709
+ return new DefaultWebExchange(request, new MockHttpResponse());
2710
+ }
2711
+ async function createCorsConfigSource(context) {
2712
+ const { sockets: routes3, cors } = context;
2713
+ const defaultCorsConfig = combineCorsConfig(PERMIT_DEFAULT_CONFIG, context.corsConfig);
2714
+ const validatedConfigs = [];
2715
+ for (const [path, route] of routes3) {
2716
+ let routeCorsConfig = defaultCorsConfig;
2717
+ const upgradeExchange = mockUpgradeExchange(path);
2718
+ for (const [matcher, config] of cors) {
2719
+ if ((await matcher(upgradeExchange)).match) {
2720
+ routeCorsConfig = combineCorsConfig(routeCorsConfig, config);
2721
+ }
2722
+ }
2723
+ routeCorsConfig = combineCorsConfig(routeCorsConfig, {
2724
+ allowOrigins: route.originFilters?.allow,
2725
+ allowMethods: ["GET", "CONNECT"],
2726
+ allowHeaders: ["upgrade", "connection", "origin", "sec-websocket-key", "sec-websocket-version", "sec-websocket-protocol", "sec-websocket-extensions"],
2727
+ exposeHeaders: ["sec-websocket-accept", "sec-websocket-protocol", "sec-websocket-extensions"],
2728
+ allowCredentials: route.authorize?.access !== "permitted"
2729
+ });
2730
+ validatedConfigs.push([and([upgradeMatcher, pattern(path)]), validateCorsConfig(routeCorsConfig)]);
2731
+ }
2732
+ for (const [matcher, config] of cors) {
2733
+ const routeCorsConfig = combineCorsConfig(defaultCorsConfig, config);
2734
+ validatedConfigs.push([matcher, validateCorsConfig(routeCorsConfig)]);
2735
+ }
2736
+ validatedConfigs.push([pattern(/\/api\/.*/), validateCorsConfig(defaultCorsConfig)]);
2737
+ return matchingCorsConfigSource({ mappings: validatedConfigs });
2738
+ }
2739
+
2740
+ // src/app/auth.ts
2741
+ function createSecurityConfig(context) {
2742
+ const authorize = [];
2743
+ for (const [path, route] of context.sockets) {
2744
+ const rule = route.authorize ?? { access: "authenticated" };
2745
+ let matcher = pattern(path, { method: "GET" });
2746
+ matcher = and([upgradeMatcher, matcher]);
2747
+ authorize.push([matcher, rule]);
2748
+ }
2749
+ authorize.push([pattern("/", { method: "GET" }), { access: "permitted" }]);
2750
+ authorize.push([pattern("/favicon.ico", { method: "GET" }), { access: "permitted" }]);
2751
+ authorize.push([pattern("/health", { method: "GET" }), { access: "permitted" }]);
2752
+ if (context.authorize.length > 0) {
2753
+ authorize.push(...context.authorize);
2754
+ }
2755
+ authorize.push(["any-exchange", { access: "authenticated" }]);
2756
+ return {
2757
+ authorize,
2758
+ basic: {
2759
+ disabled: context.authConfig?.type !== "basic",
2760
+ ...context.authConfig?.basic
2761
+ },
2762
+ jwt: {
2763
+ disabled: context.authConfig?.type !== "oauth2",
2764
+ ...context.authConfig?.oauth2?.jwt
2765
+ }
2766
+ };
2767
+ }
2768
+ async function httpSecurity(context) {
2769
+ const corsConfigSource = await createCorsConfigSource(context);
2770
+ const config = createSecurityConfig(context);
2771
+ const { storage } = context;
2772
+ return config_default(config, { storage, corsConfigSource });
2773
+ }
2774
+
2463
2775
  // src/server.ts
2464
- var logger7 = getLogger("app");
2776
+ var logger10 = getLogger("app");
2465
2777
  function secureContextOptions(ssl) {
2466
2778
  const options = {};
2467
2779
  if (ssl.key) options.key = readFileSync(ssl.key);
@@ -2469,60 +2781,38 @@ function secureContextOptions(ssl) {
2469
2781
  if (ssl.ca) options.ca = readFileSync(ssl.ca);
2470
2782
  return options;
2471
2783
  }
2472
- function createListener(storage, middleware, routes3, onSocketError) {
2784
+ async function createListener(middleware, context, onSocketError) {
2785
+ const storage = context.storage;
2786
+ const security = await httpSecurity(context);
2473
2787
  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.headers.one("connection") === "Upgrade" && request.headers.one("upgrade")?.toLowerCase() === "websocket") {
2788
+ server_header_default(context.serverHeader),
2789
+ ...security,
2790
+ // websocket upgrade handler
2791
+ async (exchange, next) => {
2792
+ const [route, path] = findSocketRoute(exchange, context);
2793
+ if (route !== void 0) {
2794
+ const { request, response } = exchange;
2795
+ const upgradeMatchResult = await upgradeMatcher(exchange);
2796
+ if ((request.method === "GET" || request.method === "CONNECT") && upgradeMatchResult.match) {
2507
2797
  const socket = request.socket;
2508
2798
  const host = request.host;
2509
2799
  const info = socketKey(request._req.socket);
2510
- if (route?.wss) {
2800
+ if (route.wss) {
2511
2801
  socket.removeListener("error", onSocketError);
2512
2802
  const wss = route.wss;
2513
2803
  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.`);
2804
+ logger10.warn(`${info} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
2515
2805
  socket.destroy();
2516
2806
  return;
2517
2807
  }
2518
- const origin = request.headers["origin"];
2808
+ const origin = request.headers.one("origin");
2519
2809
  if (!acceptsOrigin(origin, route.originFilters)) {
2520
- logger7.info(`${info} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
2810
+ logger10.info(`${info} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
2521
2811
  socket.destroy();
2522
2812
  return;
2523
2813
  }
2524
- if (logger7.enabledFor("debug")) {
2525
- logger7.debug(`${info} accepted new ws connection request from ${host} on ${path}`);
2814
+ if (logger10.enabledFor("debug")) {
2815
+ logger10.debug(`${info} accepted new ws connection request from ${host} on ${path}`);
2526
2816
  }
2527
2817
  wss.handleUpgrade(request._req, socket, request._req["_upgradeHead"], (ws) => {
2528
2818
  response._res["_header"] = true;
@@ -2532,29 +2822,36 @@ function createListener(storage, middleware, routes3, onSocketError) {
2532
2822
  wss.emit("connection", ws, request._req);
2533
2823
  });
2534
2824
  } else {
2535
- logger7.warn(`${info} rejected upgrade request from ${host} on ${path}`);
2825
+ logger10.warn(`${info} rejected upgrade request from ${host} on ${path}`);
2536
2826
  socket.destroy();
2537
2827
  }
2538
2828
  } 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)}`);
2829
+ if (route.default) {
2830
+ await next();
2831
+ return;
2832
+ }
2833
+ if (logger10.enabledFor("debug")) {
2834
+ 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
2835
  }
2542
2836
  response.statusCode = 426;
2543
- response._res.appendHeader("Upgrade", "websocket").appendHeader("Connection", "Upgrade").appendHeader("Content-Type", "text/plain");
2837
+ response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
2544
2838
  await response.end(`This service [${request.path}] requires use of the websocket protocol.`);
2545
2839
  }
2546
2840
  } else {
2547
2841
  await next();
2548
2842
  }
2549
2843
  },
2844
+ ...middleware,
2845
+ // helth check
2550
2846
  async ({ request, response }, next) => {
2551
2847
  if (request.method === "GET" && request.path === "/health") {
2552
2848
  response.statusCode = 200;
2553
- response._res.end(http.STATUS_CODES[200]);
2849
+ await response.end(http.STATUS_CODES[200]);
2554
2850
  } else {
2555
2851
  await next();
2556
2852
  }
2557
2853
  },
2854
+ // home page
2558
2855
  async ({ request, response }, next) => {
2559
2856
  if (request.method === "GET" && request.path === "/") {
2560
2857
  await response.end(`io.Gateway Server`);
@@ -2562,7 +2859,8 @@ function createListener(storage, middleware, routes3, onSocketError) {
2562
2859
  await next();
2563
2860
  }
2564
2861
  },
2565
- async ({ request, response }, _next) => {
2862
+ // not found
2863
+ async ({ response }, _next) => {
2566
2864
  response.statusCode = 404;
2567
2865
  await response.end(http.STATUS_CODES[404]);
2568
2866
  }
@@ -2571,17 +2869,17 @@ function createListener(storage, middleware, routes3, onSocketError) {
2571
2869
  request.socket.addListener("error", onSocketError);
2572
2870
  const exchange = new DefaultWebExchange(new HttpServerRequest(request), new HttpServerResponse(response));
2573
2871
  return storage.run({ exchange }, async () => {
2574
- if (logger7.enabledFor("debug")) {
2872
+ if (logger10.enabledFor("debug")) {
2575
2873
  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}`);
2874
+ if (logger10.enabledFor("debug")) {
2875
+ logger10.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
2578
2876
  }
2579
2877
  }
2580
2878
  try {
2581
2879
  return await listener(exchange);
2582
2880
  } catch (e) {
2583
- if (logger7.enabledFor("warn")) {
2584
- logger7.warn(`error processing request for ${exchange.path}`, e);
2881
+ if (logger10.enabledFor("warn")) {
2882
+ logger10.warn(`error processing request for ${exchange.path}`, e);
2585
2883
  }
2586
2884
  } finally {
2587
2885
  await exchange.response.end();
@@ -2616,65 +2914,75 @@ function regexAwareReplacer(_key, value) {
2616
2914
  }
2617
2915
  var Factory = async (options) => {
2618
2916
  const ssl = options.ssl;
2619
- const createServer = ssl ? (options2, handler2) => https.createServer({ ...options2, ...secureContextOptions(ssl) }, handler2) : (options2, handler2) => http.createServer(options2, handler2);
2917
+ const createServer = ssl ? (options2, handler) => https.createServer({ ...options2, ...secureContextOptions(ssl) }, handler) : (options2, handler) => http.createServer(options2, handler);
2620
2918
  const monitor = memoryMonitor(options.memory);
2621
2919
  const middleware = [];
2622
- const routes3 = /* @__PURE__ */ new Map();
2920
+ const context = {
2921
+ corsConfig: options.cors,
2922
+ cors: [],
2923
+ authConfig: options.auth,
2924
+ authorize: [],
2925
+ storage: new AsyncLocalStorage2(),
2926
+ sockets: /* @__PURE__ */ new Map()
2927
+ };
2623
2928
  const gw = IOGateway7.Factory({ ...options.gateway });
2624
2929
  if (options.gateway) {
2625
2930
  const config = options.gateway;
2626
- routes3.set(config.route ?? "/", {
2931
+ const route = config.route ?? "/";
2932
+ context.sockets.set(route, {
2627
2933
  default: config.route === void 0,
2628
2934
  ping: options.gateway.ping,
2629
2935
  factory: core_default.bind(gw),
2630
2936
  maxConnections: config.limits?.max_connections,
2937
+ authorize: config.authorize,
2631
2938
  originFilters: regexifyOriginFilters(config.origins)
2632
2939
  });
2633
2940
  }
2634
2941
  if (options.mesh) {
2635
2942
  const connections = new InMemoryNodeConnections(options.mesh.timeout ?? 6e4);
2636
- middleware.push(...routes_default(connections));
2943
+ const authorize = options.mesh.authorize;
2944
+ middleware.push(...routes_default(connections, context, authorize));
2637
2945
  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 });
2946
+ const originFilters = regexifyOriginFilters(options.mesh.origins);
2947
+ context.sockets.set("/broker", { factory: core_default2, ping, originFilters, authorize });
2948
+ context.sockets.set("/cluster", { factory: core_default4, ping, originFilters, authorize });
2949
+ context.sockets.set("/relays", { factory: core_default3, ping, originFilters, authorize });
2641
2950
  }
2642
2951
  if (options.metrics) {
2643
- middleware.push(...await routes_default2(options.metrics));
2952
+ middleware.push(...await routes_default2(options.metrics, context));
2644
2953
  }
2645
2954
  const ports = portRange(options.port ?? 0);
2646
2955
  const host = options.host;
2647
- const storage = new AsyncLocalStorage2();
2956
+ const onSocketError = (err) => logger10.error(`socket error: ${err}`, err);
2957
+ const listener = await createListener(middleware, context, onSocketError);
2648
2958
  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
2959
  const server2 = createServer({}, listener);
2652
2960
  server2.on("error", (e) => {
2653
2961
  if (e["code"] === "EADDRINUSE") {
2654
- logger7.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
2962
+ logger10.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
2655
2963
  const { value: port } = ports.next();
2656
2964
  if (port) {
2657
- logger7.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
2965
+ logger10.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
2658
2966
  server2.close();
2659
2967
  server2.listen(port, host);
2660
2968
  } else {
2661
- logger7.warn(`all configured port(s) ${options.port} are in use. closing...`);
2969
+ logger10.warn(`all configured port(s) ${options.port} are in use. closing...`);
2662
2970
  server2.close();
2663
2971
  reject(e);
2664
2972
  }
2665
2973
  } else {
2666
- logger7.error(`server error: ${e.message}`, e);
2974
+ logger10.error(`server error: ${e.message}`, e);
2667
2975
  reject(e);
2668
2976
  }
2669
2977
  });
2670
2978
  server2.on("listening", async () => {
2671
2979
  const info = server2.address();
2672
- for (const [path, route] of routes3) {
2980
+ for (const [path, route] of context.sockets) {
2673
2981
  try {
2674
- logger7.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
2982
+ logger10.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
2675
2983
  const wss = new WebSocketServer({ noServer: true });
2676
2984
  const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info.port}${path}`;
2677
- const handler2 = await route.factory({ endpoint, wss, storage });
2985
+ const handler = await route.factory({ endpoint, wss, storage: context.storage });
2678
2986
  const pingInterval = route.ping;
2679
2987
  if (pingInterval) {
2680
2988
  const pingIntervalId = setInterval(() => {
@@ -2691,12 +2999,12 @@ var Factory = async (options) => {
2691
2999
  });
2692
3000
  }
2693
3001
  route.wss = wss;
2694
- route.close = handler2.close?.bind(handler2);
3002
+ route.close = handler.close?.bind(handler);
2695
3003
  } catch (e) {
2696
- logger7.warn(`failed to init route ${path}`, e);
3004
+ logger10.warn(`failed to init route ${path}`, e);
2697
3005
  }
2698
3006
  }
2699
- logger7.info(`http server listening on ${info.address}:${info.port}`);
3007
+ logger10.info(`http server listening on ${info.address}:${info.port}`);
2700
3008
  resolve(server2);
2701
3009
  });
2702
3010
  server2.on("upgrade", (req, socket, head) => {
@@ -2707,16 +3015,16 @@ var Factory = async (options) => {
2707
3015
  res.assignSocket(socket);
2708
3016
  listener(req, res);
2709
3017
  } catch (err) {
2710
- logger7.error(`upgrade error: ${err}`, err);
3018
+ logger10.error(`upgrade error: ${err}`, err);
2711
3019
  }
2712
3020
  }).on("close", async () => {
2713
- logger7.info(`http server closed.`);
3021
+ logger10.info(`http server closed.`);
2714
3022
  });
2715
3023
  try {
2716
3024
  const { value: port } = ports.next();
2717
3025
  server2.listen(port, host);
2718
3026
  } catch (e) {
2719
- logger7.error(`error starting web socket server`, e);
3027
+ logger10.error(`error starting web socket server`, e);
2720
3028
  reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
2721
3029
  }
2722
3030
  });
@@ -2724,18 +3032,18 @@ var Factory = async (options) => {
2724
3032
  return new class {
2725
3033
  gateway = gw;
2726
3034
  async close() {
2727
- for (const [path, route] of routes3) {
3035
+ for (const [path, route] of context.sockets) {
2728
3036
  try {
2729
3037
  if (route.close) {
2730
3038
  await route.close();
2731
3039
  }
2732
- logger7.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
3040
+ logger10.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
2733
3041
  route.wss?.clients?.forEach((client) => {
2734
3042
  client.terminate();
2735
3043
  });
2736
3044
  route.wss?.close();
2737
3045
  } catch (e) {
2738
- logger7.warn(`error closing route ${path}`, e);
3046
+ logger10.warn(`error closing route ${path}`, e);
2739
3047
  }
2740
3048
  }
2741
3049
  await promisify((cb) => {