@conduitplatform/module-tools 0.17.0-alpha.7 → 0.17.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AUTHENTICATION_CONFIG_UPDATE_CHANNEL: () => AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
34
+ AccessTokenVerifier: () => AccessTokenVerifier,
33
35
  ConduitActiveSchema: () => ConduitActiveSchema,
34
36
  ConduitBoolean: () => ConduitBoolean,
35
37
  ConduitDate: () => ConduitDate,
@@ -40,6 +42,7 @@ __export(index_exports, {
40
42
  ConduitServiceModule: () => ConduitServiceModule,
41
43
  ConduitString: () => ConduitString,
42
44
  ConfigController: () => ConfigController,
45
+ DEFAULT_LEEWAY_SECONDS: () => DEFAULT_LEEWAY_SECONDS,
43
46
  DEFAULT_STRIP_FIELDS: () => DEFAULT_STRIP_FIELDS,
44
47
  GrpcServer: () => GrpcServer,
45
48
  ManagedModule: () => ManagedModule,
@@ -52,20 +55,27 @@ __export(index_exports, {
52
55
  convictConfigParser: () => convictConfigParser,
53
56
  createParsedRouterRequest: () => createParsedRouterRequest,
54
57
  createServer: () => createServer,
58
+ createUserHydrator: () => createUserHydrator,
55
59
  exportModel: () => exportModel,
60
+ getAuthMiddlewareKind: () => getAuthMiddlewareKind,
61
+ hydrateContextUser: () => hydrateContextUser,
56
62
  importModel: () => importModel,
57
63
  initializeSdk: () => initializeSdk,
58
64
  linearBackoffTimeout: () => linearBackoffTimeout,
59
65
  linearBackoffTimeoutAsync: () => linearBackoffTimeoutAsync,
66
+ mapHydrationErrorCode: () => mapHydrationErrorCode,
60
67
  merge: () => merge,
61
68
  readConduitPeersManifest: () => readConduitPeersManifest,
62
69
  reconcileStoredModuleConfig: () => reconcileStoredModuleConfig,
63
70
  redactSensitiveConfig: () => redactSensitiveConfig,
64
71
  restoreRedactedSecrets: () => restoreRedactedSecrets,
65
72
  sanitizeDocumentsForExport: () => sanitizeDocumentsForExport,
73
+ selectVerificationKey: () => selectVerificationKey,
74
+ shouldHydrateUser: () => shouldHydrateUser,
66
75
  stableConfigJson: () => stableConfigJson,
67
76
  storedConfigsEquivalent: () => storedConfigsEquivalent,
68
77
  stripInternalFields: () => stripInternalFields,
78
+ verifyAccessToken: () => verifyAccessToken,
69
79
  wrapFunctionsAsync: () => wrapFunctionsAsync,
70
80
  wrapGrpcFunctions: () => wrapGrpcFunctions,
71
81
  wrapRouterGrpcFunction: () => wrapRouterGrpcFunction
@@ -461,8 +471,8 @@ var GrpcServer = class {
461
471
  var import_path = __toESM(require("path"), 1);
462
472
  var import_events = require("events");
463
473
  var import_lodash_es2 = require("lodash-es");
464
- var import_grpc_js4 = require("@grpc/grpc-js");
465
- var import_grpc_sdk8 = require("@conduitplatform/grpc-sdk");
474
+ var import_grpc_js6 = require("@grpc/grpc-js");
475
+ var import_grpc_sdk9 = require("@conduitplatform/grpc-sdk");
466
476
 
467
477
  // src/routing/RouteBuilder.ts
468
478
  var import_grpc_sdk5 = require("@conduitplatform/grpc-sdk");
@@ -540,6 +550,16 @@ var RouteBuilder = class {
540
550
  this._options.captureRawHeaders = capture;
541
551
  return this;
542
552
  }
553
+ /**
554
+ * Whether module-tools hydrates `context.user` from `context.userId` on routes with
555
+ * `authMiddleware` / `authMiddleware?` (default true).
556
+ * @deprecated The default flips to false in the next major; call
557
+ * `grpcSdk.authentication.getUser(userId)` explicitly instead.
558
+ */
559
+ hydrateUser(hydrate) {
560
+ this._options.hydrateUser = hydrate;
561
+ return this;
562
+ }
543
563
  return(name, fields) {
544
564
  this._returns = new import_grpc_sdk5.ConduitRouteReturnDefinition(name, fields);
545
565
  return this;
@@ -572,44 +592,104 @@ var RouteBuilder = class {
572
592
  };
573
593
 
574
594
  // src/routing/wrapRouterFunctions.ts
595
+ var import_grpc_sdk7 = require("@conduitplatform/grpc-sdk");
596
+ var import_grpc_js4 = require("@grpc/grpc-js");
597
+
598
+ // src/routing/hydrateUser.ts
575
599
  var import_grpc_sdk6 = require("@conduitplatform/grpc-sdk");
576
600
  var import_grpc_js3 = require("@grpc/grpc-js");
601
+ function middlewareList(routeOptions) {
602
+ const m = routeOptions?.middlewares;
603
+ if (Array.isArray(m)) return m.filter((x) => typeof x === "string");
604
+ if (typeof m === "string") return [m];
605
+ return [];
606
+ }
607
+ function getAuthMiddlewareKind(routeOptions) {
608
+ const list = middlewareList(routeOptions);
609
+ if (list.includes("authMiddleware")) return "required";
610
+ if (list.includes("authMiddleware?")) return "optional";
611
+ return void 0;
612
+ }
613
+ function shouldHydrateUser(routeOptions, context) {
614
+ if (!routeOptions || !context) return false;
615
+ if (routeOptions.hydrateUser === false) return false;
616
+ if (!getAuthMiddlewareKind(routeOptions)) return false;
617
+ return typeof context.userId === "string" && !!context.userId && !context.user;
618
+ }
619
+ function createUserHydrator(routeOptions, resolve) {
620
+ if (!routeOptions) return void 0;
621
+ const kind = getAuthMiddlewareKind(routeOptions);
622
+ if (!kind || routeOptions.hydrateUser === false) return void 0;
623
+ return {
624
+ optional: kind === "optional",
625
+ getUser: async (userId) => {
626
+ const resolver = resolve();
627
+ if (!resolver) throw new import_grpc_sdk6.GrpcError(import_grpc_js3.status.UNAVAILABLE, "User lookup unavailable");
628
+ return resolver(userId);
629
+ }
630
+ };
631
+ }
632
+ function mapHydrationErrorCode(error) {
633
+ const code = error?.code;
634
+ if (typeof code !== "number" || code === import_grpc_js3.status.OK) return import_grpc_js3.status.INTERNAL;
635
+ if (code === import_grpc_js3.status.NOT_FOUND) return import_grpc_js3.status.UNAUTHENTICATED;
636
+ return code;
637
+ }
638
+ async function hydrateContextUser(routeOptions, context, hydrator) {
639
+ if (!shouldHydrateUser(routeOptions, context)) return;
640
+ try {
641
+ context.user = await hydrator.getUser(context.userId);
642
+ } catch (e) {
643
+ import_grpc_sdk6.ConduitGrpcSdk.Logger.warn(
644
+ `User hydration failed: ${e?.message ?? String(e)}`
645
+ );
646
+ if (!hydrator.optional) {
647
+ const code = mapHydrationErrorCode(e);
648
+ throw new import_grpc_sdk6.GrpcError(
649
+ code,
650
+ code === import_grpc_js3.status.UNAUTHENTICATED ? "Unauthorized" : "User lookup failed"
651
+ );
652
+ }
653
+ }
654
+ }
655
+
656
+ // src/routing/wrapRouterFunctions.ts
577
657
  var mapGrpcStatusToHttp = (code) => {
578
658
  if (!code) return "200 (OK)";
579
659
  switch (code) {
580
- case import_grpc_js3.status.OK:
660
+ case import_grpc_js4.status.OK:
581
661
  return `200 (OK)`;
582
- case import_grpc_js3.status.CANCELLED:
662
+ case import_grpc_js4.status.CANCELLED:
583
663
  return `499 (Cancelled)`;
584
- case import_grpc_js3.status.INVALID_ARGUMENT:
664
+ case import_grpc_js4.status.INVALID_ARGUMENT:
585
665
  return `400 (Invalid Argument)`;
586
- case import_grpc_js3.status.DEADLINE_EXCEEDED:
666
+ case import_grpc_js4.status.DEADLINE_EXCEEDED:
587
667
  return `504 (Deadline Exceeded)`;
588
- case import_grpc_js3.status.NOT_FOUND:
668
+ case import_grpc_js4.status.NOT_FOUND:
589
669
  return `404 (Not Found)`;
590
- case import_grpc_js3.status.ALREADY_EXISTS:
670
+ case import_grpc_js4.status.ALREADY_EXISTS:
591
671
  return `409 (Already Exists)`;
592
- case import_grpc_js3.status.PERMISSION_DENIED:
672
+ case import_grpc_js4.status.PERMISSION_DENIED:
593
673
  return `403 (Permission Denied)`;
594
- case import_grpc_js3.status.UNAUTHENTICATED:
674
+ case import_grpc_js4.status.UNAUTHENTICATED:
595
675
  return `401 (Unauthenticated)`;
596
- case import_grpc_js3.status.OUT_OF_RANGE:
676
+ case import_grpc_js4.status.OUT_OF_RANGE:
597
677
  return `416 (Out of Range)`;
598
- case import_grpc_js3.status.FAILED_PRECONDITION:
678
+ case import_grpc_js4.status.FAILED_PRECONDITION:
599
679
  return `412 (Failed Precondition)`;
600
- case import_grpc_js3.status.ABORTED:
680
+ case import_grpc_js4.status.ABORTED:
601
681
  return `409 (Aborted)`;
602
- case import_grpc_js3.status.INTERNAL:
682
+ case import_grpc_js4.status.INTERNAL:
603
683
  return `416 (Internal)`;
604
- case import_grpc_js3.status.UNAVAILABLE:
684
+ case import_grpc_js4.status.UNAVAILABLE:
605
685
  return `503 (Service Unavailable)`;
606
- case import_grpc_js3.status.DATA_LOSS:
686
+ case import_grpc_js4.status.DATA_LOSS:
607
687
  return `500 (Data Loss)`;
608
688
  default:
609
689
  return `500 (Unknown)`;
610
690
  }
611
691
  };
612
- function generateLog(routerType, routerRequest, requestReceive, call, status6) {
692
+ function generateLog(routerType, routerRequest, requestReceive, call, status10) {
613
693
  let log = "";
614
694
  const latency = Date.now() - requestReceive;
615
695
  if (routerRequest) {
@@ -619,15 +699,15 @@ function generateLog(routerType, routerRequest, requestReceive, call, status6) {
619
699
  const socketLabel = socketReq.socketId ?? socketReq.socket;
620
700
  log += `(${routerType}) Socket: ${socketReq.event} socket: ${socketLabel}`;
621
701
  }
622
- log += ` ${mapGrpcStatusToHttp(status6) ?? "200"} ${latency}ms`;
623
- import_grpc_sdk6.ConduitGrpcSdk.Logger.log(log);
624
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.set("grpc_request_latency_seconds", latency / 1e3);
625
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.set(
702
+ log += ` ${mapGrpcStatusToHttp(status10) ?? "200"} ${latency}ms`;
703
+ import_grpc_sdk7.ConduitGrpcSdk.Logger.log(log);
704
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.set("grpc_request_latency_seconds", latency / 1e3);
705
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.set(
626
706
  `${routerType}_grpc_request_latency_seconds`,
627
707
  latency / 1e3
628
708
  );
629
- const successStatus = !status6 || status6.toString().startsWith("2");
630
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_response_statuses_total`, 1, {
709
+ const successStatus = !status10 || status10.toString().startsWith("2");
710
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_response_statuses_total`, 1, {
631
711
  success: successStatus ? "true" : "false"
632
712
  });
633
713
  }
@@ -643,7 +723,7 @@ function parseResponseData(r, routerRequest, requestReceive, call, callback, res
643
723
  responded.did = true;
644
724
  if (!r) {
645
725
  callback({
646
- code: import_grpc_js3.status.INTERNAL,
726
+ code: import_grpc_js4.status.INTERNAL,
647
727
  message: "Handler did not return a response"
648
728
  });
649
729
  }
@@ -674,11 +754,11 @@ function parseResponseData(r, routerRequest, requestReceive, call, callback, res
674
754
  }
675
755
  generateLog(routerType, routerRequest, requestReceive, call, void 0);
676
756
  }
677
- function wrapRouterGrpcFunction(fun, routerType) {
757
+ function wrapRouterGrpcFunction(fun, routerType, hydration) {
678
758
  return (call, callback) => {
679
759
  const requestReceive = Date.now();
680
760
  let routerRequest = true;
681
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_requests_total`);
761
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_requests_total`);
682
762
  try {
683
763
  call.request.context = parseRequestData(call.request.context);
684
764
  call.request.params = parseRequestData(call.request.params);
@@ -697,16 +777,16 @@ function wrapRouterGrpcFunction(fun, routerType) {
697
777
  }
698
778
  }
699
779
  } catch (e) {
700
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
701
- generateLog(routerType, routerRequest, requestReceive, call, import_grpc_js3.status.INTERNAL);
702
- import_grpc_sdk6.ConduitGrpcSdk.Logger.error(e.message ?? "Something went wrong");
780
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
781
+ generateLog(routerType, routerRequest, requestReceive, call, import_grpc_js4.status.INTERNAL);
782
+ import_grpc_sdk7.ConduitGrpcSdk.Logger.error(e.message ?? "Something went wrong");
703
783
  return callback({
704
- code: import_grpc_js3.status.INTERNAL,
784
+ code: import_grpc_js4.status.INTERNAL,
705
785
  message: e.message ?? "Something went wrong"
706
786
  });
707
787
  }
708
788
  const responded = { did: false };
709
- fun(call, (r) => {
789
+ const invoke = () => fun(call, (r) => {
710
790
  parseResponseData(
711
791
  r,
712
792
  routerRequest,
@@ -716,7 +796,13 @@ function wrapRouterGrpcFunction(fun, routerType) {
716
796
  responded,
717
797
  routerType
718
798
  );
719
- }).then(
799
+ });
800
+ const needsHydration = hydration && shouldHydrateUser(hydration.options, call.request.context);
801
+ (needsHydration ? hydrateContextUser(
802
+ hydration.options,
803
+ call.request.context,
804
+ hydration.hydrator
805
+ ).then(invoke) : invoke()).then(
720
806
  (r) => parseResponseData(
721
807
  r,
722
808
  routerRequest,
@@ -727,19 +813,19 @@ function wrapRouterGrpcFunction(fun, routerType) {
727
813
  routerType
728
814
  )
729
815
  ).catch((error) => {
730
- import_grpc_sdk6.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
816
+ import_grpc_sdk7.ConduitGrpcSdk.Metrics?.increment(`${routerType}_grpc_errors_total`);
731
817
  generateLog(
732
818
  routerType,
733
819
  routerRequest,
734
820
  requestReceive,
735
821
  call,
736
- error.code ?? import_grpc_js3.status.INTERNAL
822
+ error.code ?? import_grpc_js4.status.INTERNAL
737
823
  );
738
- import_grpc_sdk6.ConduitGrpcSdk.Logger.error(
824
+ import_grpc_sdk7.ConduitGrpcSdk.Logger.error(
739
825
  error.debugLogInfo ?? error.message ?? "Something went wrong"
740
826
  );
741
827
  callback({
742
- code: error.code ?? import_grpc_js3.status.INTERNAL,
828
+ code: error.code ?? import_grpc_js4.status.INTERNAL,
743
829
  message: error.details ?? error.message ?? "Something went wrong"
744
830
  });
745
831
  });
@@ -747,21 +833,26 @@ function wrapRouterGrpcFunction(fun, routerType) {
747
833
  }
748
834
 
749
835
  // src/routing/RoutingUtilities.ts
750
- function wrapFunctionsAsync(functions, routerType) {
836
+ function wrapFunctionsAsync(functions, routerType, hydrations = {}) {
751
837
  const modifiedFunctions = {};
752
838
  Object.keys(functions).forEach((key) => {
753
- modifiedFunctions[key] = wrapRouterGrpcFunction(functions[key], routerType);
839
+ modifiedFunctions[key] = wrapRouterGrpcFunction(
840
+ functions[key],
841
+ routerType,
842
+ hydrations[key]
843
+ );
754
844
  });
755
845
  return modifiedFunctions;
756
846
  }
757
847
 
758
848
  // src/routing/RoutingManager.ts
759
- var import_grpc_sdk7 = require("@conduitplatform/grpc-sdk");
849
+ var import_grpc_sdk8 = require("@conduitplatform/grpc-sdk");
850
+ var import_grpc_js5 = require("@grpc/grpc-js");
760
851
  var RoutingManager = class _RoutingManager {
761
852
  constructor(_router, _server) {
762
853
  this._router = _router;
763
854
  this._server = _server;
764
- if (_router instanceof import_grpc_sdk7.Admin) {
855
+ if (_router instanceof import_grpc_sdk8.Admin) {
765
856
  this.isAdmin = true;
766
857
  }
767
858
  }
@@ -770,22 +861,46 @@ var RoutingManager = class _RoutingManager {
770
861
  _moduleRoutes = {};
771
862
  static ClientController;
772
863
  static AdminController;
864
+ /** Set by ManagedModule; used to hydrate context.user on client routes. */
865
+ static grpcSdk;
866
+ static userResolver;
867
+ static warnedNoResolver = false;
868
+ /**
869
+ * Overrides how users are fetched for hydration (default: grpcSdk.authentication.getUser).
870
+ * Intended for the module that owns the users: the authentication module has no gRPC
871
+ * client to itself and will register a local resolver here.
872
+ */
873
+ static setUserResolver(resolver) {
874
+ _RoutingManager.userResolver = resolver;
875
+ }
876
+ static resolveUser() {
877
+ if (_RoutingManager.userResolver) return _RoutingManager.userResolver;
878
+ const sdk = _RoutingManager.grpcSdk;
879
+ if (!sdk) return void 0;
880
+ return async (userId) => {
881
+ const auth = sdk.authentication;
882
+ if (!auth) {
883
+ throw new import_grpc_sdk8.GrpcError(import_grpc_js5.status.UNAVAILABLE, "authentication module unavailable");
884
+ }
885
+ return auth.getUser(userId);
886
+ };
887
+ }
773
888
  _routeHandlers = {};
774
889
  isAdmin = false;
775
890
  get(path4) {
776
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.GET).path(path4);
891
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.GET).path(path4);
777
892
  }
778
893
  post(path4) {
779
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.POST).path(path4);
894
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.POST).path(path4);
780
895
  }
781
896
  delete(path4) {
782
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.DELETE).path(path4);
897
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.DELETE).path(path4);
783
898
  }
784
899
  update(path4) {
785
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.UPDATE).path(path4);
900
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.UPDATE).path(path4);
786
901
  }
787
902
  patch(path4) {
788
- return new RouteBuilder(this).method(import_grpc_sdk7.ConduitRouteActions.PATCH).path(path4);
903
+ return new RouteBuilder(this).method(import_grpc_sdk8.ConduitRouteActions.PATCH).path(path4);
789
904
  }
790
905
  clear() {
791
906
  this._moduleRoutes = {};
@@ -836,7 +951,27 @@ var RoutingManager = class _RoutingManager {
836
951
  }
837
952
  async registerRoutes() {
838
953
  if (Object.keys(this._routeHandlers).length === 0) return;
839
- const modifiedFunctions = wrapFunctionsAsync(this._routeHandlers, this.isAdmin ? "admin" : "client");
954
+ const hydrations = {};
955
+ if (!this.isAdmin) {
956
+ for (const [name, route] of Object.entries(this._moduleRoutes)) {
957
+ const options = route.options;
958
+ const hydrator = createUserHydrator(options, _RoutingManager.resolveUser);
959
+ if (!hydrator) continue;
960
+ const names = "events" in route ? Object.keys(JSON.parse(route.events || "{}")) : [name];
961
+ for (const n of names) hydrations[n] = { options, hydrator };
962
+ if (!_RoutingManager.grpcSdk && !_RoutingManager.userResolver && !_RoutingManager.warnedNoResolver) {
963
+ _RoutingManager.warnedNoResolver = true;
964
+ import_grpc_sdk8.ConduitGrpcSdk.Logger.warn(
965
+ "User hydration is needed but RoutingManager.grpcSdk is not set; hydration will fail"
966
+ );
967
+ }
968
+ }
969
+ }
970
+ const modifiedFunctions = wrapFunctionsAsync(
971
+ this._routeHandlers,
972
+ this.isAdmin ? "admin" : "client",
973
+ hydrations
974
+ );
840
975
  if (this.isAdmin) {
841
976
  _RoutingManager.AdminController.setRoutes(modifiedFunctions);
842
977
  } else {
@@ -882,7 +1017,7 @@ var ConduitServiceModule = class {
882
1017
  // external address:port of service (LoadBalancer)
883
1018
  grpcServer;
884
1019
  events = new import_events.EventEmitter();
885
- _serviceHealthState = import_grpc_sdk8.HealthCheckStatus.SERVING;
1020
+ _serviceHealthState = import_grpc_sdk9.HealthCheckStatus.SERVING;
886
1021
  // default for health-agnostic modules
887
1022
  constructor(moduleName) {
888
1023
  this._moduleName = (0, import_lodash_es2.camelCase)(moduleName);
@@ -909,15 +1044,15 @@ var ConduitServiceModule = class {
909
1044
  return this._serviceHealthState;
910
1045
  }
911
1046
  updateHealth(state, init = false) {
912
- if (state === import_grpc_sdk8.HealthCheckStatus.UNKNOWN && !init || state === import_grpc_sdk8.HealthCheckStatus.SERVICE_UNKNOWN) {
1047
+ if (state === import_grpc_sdk9.HealthCheckStatus.UNKNOWN && !init || state === import_grpc_sdk9.HealthCheckStatus.SERVICE_UNKNOWN) {
913
1048
  throw new Error(
914
- `Cannot explicitly set gRPC health state to ${import_grpc_sdk8.HealthCheckStatus[state]}`
1049
+ `Cannot explicitly set gRPC health state to ${import_grpc_sdk9.HealthCheckStatus[state]}`
915
1050
  );
916
1051
  }
917
1052
  if (!init) {
918
- import_grpc_sdk8.ConduitGrpcSdk.Metrics?.set(
1053
+ import_grpc_sdk9.ConduitGrpcSdk.Metrics?.set(
919
1054
  "module_health_state",
920
- state === import_grpc_sdk8.HealthCheckStatus.SERVING ? 1 : 0
1055
+ state === import_grpc_sdk9.HealthCheckStatus.SERVING ? 1 : 0
921
1056
  );
922
1057
  }
923
1058
  if (init) {
@@ -936,12 +1071,12 @@ var ConduitServiceModule = class {
936
1071
  const service = call.request.service.substring(call.request.service.indexOf(".") + 1);
937
1072
  if (service && service !== this._serviceName) {
938
1073
  callback(null, {
939
- status: import_grpc_sdk8.HealthCheckStatus.SERVICE_UNKNOWN
1074
+ status: import_grpc_sdk9.HealthCheckStatus.SERVICE_UNKNOWN
940
1075
  });
941
1076
  } else {
942
1077
  if (!this._registered) {
943
1078
  return callback(null, {
944
- status: import_grpc_sdk8.HealthCheckResponse_ServingStatus.UNKNOWN
1079
+ status: import_grpc_sdk9.HealthCheckResponse_ServingStatus.UNKNOWN
945
1080
  });
946
1081
  }
947
1082
  callback(null, {
@@ -953,14 +1088,14 @@ var ConduitServiceModule = class {
953
1088
  const service = call.request.service.substring(call.request.service.indexOf(".") + 1);
954
1089
  if (service && service !== this._serviceName) {
955
1090
  call.write({
956
- status: import_grpc_sdk8.HealthCheckStatus.SERVICE_UNKNOWN
1091
+ status: import_grpc_sdk9.HealthCheckStatus.SERVICE_UNKNOWN
957
1092
  });
958
1093
  } else {
959
1094
  this.events.on(
960
1095
  `grpc-health-change:${this._serviceName}`,
961
- (status6) => {
1096
+ (status10) => {
962
1097
  call.write({
963
- status: status6
1098
+ status: status10
964
1099
  });
965
1100
  }
966
1101
  );
@@ -983,7 +1118,7 @@ var ConduitServiceModule = class {
983
1118
  const resources = this.getExportableResources();
984
1119
  callback(null, { resources: JSON.stringify(resources) });
985
1120
  } catch (e) {
986
- callback({ code: import_grpc_js4.status.INTERNAL, message: e.message });
1121
+ callback({ code: import_grpc_js6.status.INTERNAL, message: e.message });
987
1122
  }
988
1123
  }
989
1124
  async exportResourcesHandler(call, callback) {
@@ -992,7 +1127,7 @@ var ConduitServiceModule = class {
992
1127
  const data = await this.exportResources(resourceTypes);
993
1128
  callback(null, { data: JSON.stringify(data) });
994
1129
  } catch (e) {
995
- callback({ code: import_grpc_js4.status.INTERNAL, message: e.message });
1130
+ callback({ code: import_grpc_js6.status.INTERNAL, message: e.message });
996
1131
  }
997
1132
  }
998
1133
  async importResourcesHandler(call, callback) {
@@ -1004,11 +1139,11 @@ var ConduitServiceModule = class {
1004
1139
  const err = e;
1005
1140
  if (err instanceof SyntaxError) {
1006
1141
  callback({
1007
- code: import_grpc_js4.status.INVALID_ARGUMENT,
1142
+ code: import_grpc_js6.status.INVALID_ARGUMENT,
1008
1143
  message: "Invalid JSON in import data"
1009
1144
  });
1010
1145
  } else {
1011
- callback({ code: import_grpc_js4.status.INTERNAL, message: err.message });
1146
+ callback({ code: import_grpc_js6.status.INTERNAL, message: err.message });
1012
1147
  }
1013
1148
  }
1014
1149
  }
@@ -1081,7 +1216,7 @@ var ConfigController = class _ConfigController {
1081
1216
 
1082
1217
  // src/ManagedModule.ts
1083
1218
  var import_lodash_es6 = require("lodash-es");
1084
- var import_grpc_js6 = require("@grpc/grpc-js");
1219
+ var import_grpc_js10 = require("@grpc/grpc-js");
1085
1220
  var import_convict = __toESM(require("convict"), 1);
1086
1221
 
1087
1222
  // src/interfaces/ModuleLifecycleStage.ts
@@ -1095,13 +1230,13 @@ var ModuleLifecycleStage = /* @__PURE__ */ ((ModuleLifecycleStage2) => {
1095
1230
  })(ModuleLifecycleStage || {});
1096
1231
 
1097
1232
  // src/ManagedModule.ts
1098
- var import_grpc_sdk14 = require("@conduitplatform/grpc-sdk");
1233
+ var import_grpc_sdk17 = require("@conduitplatform/grpc-sdk");
1099
1234
 
1100
1235
  // src/utilities/initializeSdk.ts
1101
- var import_grpc_sdk13 = require("@conduitplatform/grpc-sdk");
1236
+ var import_grpc_sdk14 = require("@conduitplatform/grpc-sdk");
1102
1237
 
1103
1238
  // src/logging/index.ts
1104
- var import_grpc_sdk9 = require("@conduitplatform/grpc-sdk");
1239
+ var import_grpc_sdk10 = require("@conduitplatform/grpc-sdk");
1105
1240
  var import_winston = __toESM(require("winston"), 1);
1106
1241
  var import_lodash_es3 = require("lodash-es");
1107
1242
  var import_http = require("http");
@@ -1258,7 +1393,7 @@ async function setupLoki(module2, instance) {
1258
1393
  if (lokiUrl.endsWith("/")) lokiUrl = lokiUrl.slice(0, -1);
1259
1394
  const onTry = async () => {
1260
1395
  return await lokiReadyCheck(lokiUrl).then(() => {
1261
- import_grpc_sdk9.ConduitGrpcSdk.Logger.addTransport(
1396
+ import_grpc_sdk10.ConduitGrpcSdk.Logger.addTransport(
1262
1397
  new import_winston_loki.default({
1263
1398
  level: "debug",
1264
1399
  host: lokiUrl,
@@ -1277,15 +1412,90 @@ async function setupLoki(module2, instance) {
1277
1412
  }).catch(() => true);
1278
1413
  };
1279
1414
  const onFailure = () => {
1280
- import_grpc_sdk9.ConduitGrpcSdk.Logger.error(`Failed to connect to Loki on '${lokiUrl}'`);
1415
+ import_grpc_sdk10.ConduitGrpcSdk.Logger.error(`Failed to connect to Loki on '${lokiUrl}'`);
1281
1416
  };
1282
1417
  await linearBackoffTimeoutAsync(onTry, 250, 15, onFailure, true);
1283
1418
  }
1284
1419
  }
1285
1420
 
1286
- // src/utilities/initializeSdk.ts
1287
- var import_winston2 = __toESM(require("winston"), 1);
1421
+ // src/logging/fileTransports.ts
1422
+ var import_fs = __toESM(require("fs"), 1);
1423
+ var import_os = __toESM(require("os"), 1);
1288
1424
  var import_path2 = __toESM(require("path"), 1);
1425
+ var import_winston2 = __toESM(require("winston"), 1);
1426
+ var import_winston_daily_rotate_file = __toESM(require("winston-daily-rotate-file"), 1);
1427
+ var DEFAULT_RETENTION_DAYS = 3;
1428
+ var DEFAULT_MAX_SIZE = "20m";
1429
+ var SIZE_STRING = /^(?:0\.)?\d+[kmg]$/i;
1430
+ function positiveIntFromEnv(raw, fallback) {
1431
+ if (raw === void 0) return fallback;
1432
+ const trimmed = raw.trim();
1433
+ if (!/^[0-9]+$/.test(trimmed)) return fallback;
1434
+ const value = Number(trimmed);
1435
+ if (!Number.isSafeInteger(value) || value <= 0) return fallback;
1436
+ return value;
1437
+ }
1438
+ function logFileMaxSize() {
1439
+ const raw = process.env.LOG_FILE_MAX_SIZE?.trim();
1440
+ if (!raw) return DEFAULT_MAX_SIZE;
1441
+ if (SIZE_STRING.test(raw)) return raw.toLowerCase();
1442
+ if (/^[0-9]+$/.test(raw)) {
1443
+ const bytes = Number(raw);
1444
+ if (Number.isSafeInteger(bytes) && bytes > 0) return bytes;
1445
+ }
1446
+ return DEFAULT_MAX_SIZE;
1447
+ }
1448
+ function formatMeta(meta) {
1449
+ if (Array.isArray(meta)) {
1450
+ if (meta.length === 0) return "";
1451
+ return meta.map((item) => JSON.stringify(item)).join(" ");
1452
+ }
1453
+ if (meta !== null && typeof meta === "object") {
1454
+ if (Object.keys(meta).length === 0) return "";
1455
+ return JSON.stringify(meta);
1456
+ }
1457
+ return "";
1458
+ }
1459
+ function createFileFormat() {
1460
+ return import_winston2.default.format.combine(
1461
+ import_winston2.default.format.errors({ stack: true }),
1462
+ import_winston2.default.format.timestamp({
1463
+ format: "YYYY-MM-DD HH:mm:ss"
1464
+ }),
1465
+ import_winston2.default.format.printf((info) => {
1466
+ const metaText = formatMeta(info.meta);
1467
+ const line = `[${info.timestamp}] [${info.level.toUpperCase()}]: ${info.message}`;
1468
+ if (info.stack) {
1469
+ return `${line} ${info.stack}`;
1470
+ }
1471
+ return metaText ? `${line} ${metaText}` : line;
1472
+ })
1473
+ );
1474
+ }
1475
+ function rotateTransport(logDir, name, level) {
1476
+ return new import_winston_daily_rotate_file.default({
1477
+ dirname: logDir,
1478
+ filename: `${name}-%DATE%.log`,
1479
+ datePattern: "YYYY-MM-DD",
1480
+ level,
1481
+ maxFiles: `${positiveIntFromEnv(process.env.LOG_RETENTION_DAYS, DEFAULT_RETENTION_DAYS)}d`,
1482
+ maxSize: logFileMaxSize(),
1483
+ zippedArchive: false,
1484
+ auditFile: import_path2.default.join(logDir, `.${name}-audit.json`),
1485
+ format: createFileFormat()
1486
+ });
1487
+ }
1488
+ function createFileLogTransports() {
1489
+ if (process.env.LOG_TO_FILE?.trim() === "false") {
1490
+ return [];
1491
+ }
1492
+ const logDir = process.env.LOG_FILE_DIR?.trim() || import_path2.default.join(import_os.default.tmpdir(), "conduit-logs");
1493
+ import_fs.default.mkdirSync(logDir, { recursive: true });
1494
+ return [
1495
+ rotateTransport(logDir, "combined", "info"),
1496
+ rotateTransport(logDir, "errors", "error")
1497
+ ];
1498
+ }
1289
1499
 
1290
1500
  // src/metrics/index.ts
1291
1501
  var import_prom_client = require("prom-client");
@@ -1293,7 +1503,7 @@ var import_prom_client = require("prom-client");
1293
1503
  // src/metrics/MetricsServer.ts
1294
1504
  var import_http2 = require("http");
1295
1505
  var import_lodash_es4 = require("lodash-es");
1296
- var import_grpc_sdk10 = require("@conduitplatform/grpc-sdk");
1506
+ var import_grpc_sdk11 = require("@conduitplatform/grpc-sdk");
1297
1507
  var MetricsServer = class {
1298
1508
  moduleName;
1299
1509
  instance;
@@ -1308,7 +1518,7 @@ var MetricsServer = class {
1308
1518
  const url = "0.0.0.0:" + port.toString();
1309
1519
  const server = (0, import_http2.createServer)(this.requestHandler.bind(this));
1310
1520
  server.listen(port, () => {
1311
- import_grpc_sdk10.ConduitGrpcSdk.Logger.info(`Metrics server listening on: ${url}`);
1521
+ import_grpc_sdk11.ConduitGrpcSdk.Logger.info(`Metrics server listening on: ${url}`);
1312
1522
  });
1313
1523
  return server;
1314
1524
  }
@@ -1330,12 +1540,12 @@ var MetricsServer = class {
1330
1540
  getHttpPort() {
1331
1541
  const value = process.env["METRICS_PORT"];
1332
1542
  if (!value) {
1333
- import_grpc_sdk10.ConduitGrpcSdk.Logger.error(`Metrics HTTP port not set`);
1543
+ import_grpc_sdk11.ConduitGrpcSdk.Logger.error(`Metrics HTTP port not set`);
1334
1544
  process.exit(-1);
1335
1545
  }
1336
1546
  const port = parseInt(value, 10);
1337
1547
  if ((0, import_lodash_es4.isNaN)(port) || port < 1 || port > 65535) {
1338
- import_grpc_sdk10.ConduitGrpcSdk.Logger.error(`Invalid HTTP port value: ${port}`);
1548
+ import_grpc_sdk11.ConduitGrpcSdk.Logger.error(`Invalid HTTP port value: ${port}`);
1339
1549
  process.exit(-1);
1340
1550
  }
1341
1551
  return port;
@@ -1343,73 +1553,73 @@ var MetricsServer = class {
1343
1553
  };
1344
1554
 
1345
1555
  // src/metrics/config/defaults.ts
1346
- var import_grpc_sdk11 = require("@conduitplatform/grpc-sdk");
1556
+ var import_grpc_sdk12 = require("@conduitplatform/grpc-sdk");
1347
1557
  var defaults_default = {
1348
1558
  internalGrpcRequests: {
1349
- type: import_grpc_sdk11.MetricType.Counter,
1559
+ type: import_grpc_sdk12.MetricType.Counter,
1350
1560
  config: {
1351
1561
  name: "internal_grpc_requests_total",
1352
1562
  help: "Tracks the total number of Conduit's internal gRPC requests"
1353
1563
  }
1354
1564
  },
1355
1565
  clientGrpcRequests: {
1356
- type: import_grpc_sdk11.MetricType.Counter,
1566
+ type: import_grpc_sdk12.MetricType.Counter,
1357
1567
  config: {
1358
1568
  name: "client_grpc_requests_total",
1359
1569
  help: "Tracks the total number of the client gRPC requests"
1360
1570
  }
1361
1571
  },
1362
1572
  adminGrpcRequests: {
1363
- type: import_grpc_sdk11.MetricType.Counter,
1573
+ type: import_grpc_sdk12.MetricType.Counter,
1364
1574
  config: {
1365
1575
  name: "admin_grpc_requests_total",
1366
1576
  help: "Tracks the total number of the admin gRPC requests"
1367
1577
  }
1368
1578
  },
1369
1579
  clientGrpcErrors: {
1370
- type: import_grpc_sdk11.MetricType.Counter,
1580
+ type: import_grpc_sdk12.MetricType.Counter,
1371
1581
  config: {
1372
1582
  name: "client_grpc_errors_total",
1373
1583
  help: "Tracks the total number of client grpc errors"
1374
1584
  }
1375
1585
  },
1376
1586
  adminGrpcErrors: {
1377
- type: import_grpc_sdk11.MetricType.Counter,
1587
+ type: import_grpc_sdk12.MetricType.Counter,
1378
1588
  config: {
1379
1589
  name: "admin_grpc_errors_total",
1380
1590
  help: "Tracks the total number of admin grpc errors"
1381
1591
  }
1382
1592
  },
1383
1593
  moduleHealthState: {
1384
- type: import_grpc_sdk11.MetricType.Gauge,
1594
+ type: import_grpc_sdk12.MetricType.Gauge,
1385
1595
  config: {
1386
1596
  name: "module_health_state",
1387
1597
  help: "Tracks the health state of the module"
1388
1598
  }
1389
1599
  },
1390
1600
  grpcRequestLatency: {
1391
- type: import_grpc_sdk11.MetricType.Gauge,
1601
+ type: import_grpc_sdk12.MetricType.Gauge,
1392
1602
  config: {
1393
1603
  name: "grpc_request_latency_seconds",
1394
1604
  help: "Tracks the latency of grpc requests in seconds"
1395
1605
  }
1396
1606
  },
1397
1607
  adminGrpcRequestLatency: {
1398
- type: import_grpc_sdk11.MetricType.Gauge,
1608
+ type: import_grpc_sdk12.MetricType.Gauge,
1399
1609
  config: {
1400
1610
  name: "admin_grpc_request_latency_seconds",
1401
1611
  help: "Tracks the latency of admin grpc requests in seconds"
1402
1612
  }
1403
1613
  },
1404
1614
  clientGrpcRequestLatency: {
1405
- type: import_grpc_sdk11.MetricType.Gauge,
1615
+ type: import_grpc_sdk12.MetricType.Gauge,
1406
1616
  config: {
1407
1617
  name: "client_grpc_request_latency_seconds",
1408
1618
  help: "Tracks the latency of client grpc requests in seconds"
1409
1619
  }
1410
1620
  },
1411
1621
  grpcResponseStatuses: {
1412
- type: import_grpc_sdk11.MetricType.Counter,
1622
+ type: import_grpc_sdk12.MetricType.Counter,
1413
1623
  config: {
1414
1624
  name: "grpc_response_statuses_total",
1415
1625
  help: "Tracks the total number of grpc response codes",
@@ -1417,7 +1627,7 @@ var defaults_default = {
1417
1627
  }
1418
1628
  },
1419
1629
  adminGrpcResponseStatuses: {
1420
- type: import_grpc_sdk11.MetricType.Counter,
1630
+ type: import_grpc_sdk12.MetricType.Counter,
1421
1631
  config: {
1422
1632
  name: "admin_grpc_response_statuses_total",
1423
1633
  help: "Tracks the total number of admin grpc response codes",
@@ -1425,7 +1635,7 @@ var defaults_default = {
1425
1635
  }
1426
1636
  },
1427
1637
  clientGrpcResponseStatuses: {
1428
- type: import_grpc_sdk11.MetricType.Counter,
1638
+ type: import_grpc_sdk12.MetricType.Counter,
1429
1639
  config: {
1430
1640
  name: "client_grpc_response_statuses_total",
1431
1641
  help: "Tracks the total number of client grpc response codes",
@@ -1435,7 +1645,7 @@ var defaults_default = {
1435
1645
  };
1436
1646
 
1437
1647
  // src/metrics/index.ts
1438
- var import_grpc_sdk12 = require("@conduitplatform/grpc-sdk");
1648
+ var import_grpc_sdk13 = require("@conduitplatform/grpc-sdk");
1439
1649
  var METRICS_PREFIX = "cnd_";
1440
1650
  var ConduitMetrics = class {
1441
1651
  moduleName;
@@ -1467,16 +1677,16 @@ var ConduitMetrics = class {
1467
1677
  const metricConfig = JSON.parse(JSON.stringify(config));
1468
1678
  metricConfig.name = this.addPrefix(config.name);
1469
1679
  switch (type) {
1470
- case import_grpc_sdk12.MetricType.Counter:
1680
+ case import_grpc_sdk13.MetricType.Counter:
1471
1681
  this.createCounter(metricConfig);
1472
1682
  break;
1473
- case import_grpc_sdk12.MetricType.Gauge:
1683
+ case import_grpc_sdk13.MetricType.Gauge:
1474
1684
  this.createGauge(metricConfig);
1475
1685
  break;
1476
- case import_grpc_sdk12.MetricType.Histogram:
1686
+ case import_grpc_sdk13.MetricType.Histogram:
1477
1687
  this.createHistogram(metricConfig);
1478
1688
  break;
1479
- case import_grpc_sdk12.MetricType.Summary:
1689
+ case import_grpc_sdk13.MetricType.Summary:
1480
1690
  this.createSummary(metricConfig);
1481
1691
  break;
1482
1692
  }
@@ -1624,7 +1834,7 @@ function clientMiddleware() {
1624
1834
  clientStartedMetric.inc(labels);
1625
1835
  const stopTimer = clientHandlingSecondsMetric.startTimer(labels);
1626
1836
  let settled = false;
1627
- let status6 = import_nice_grpc_common.Status.OK;
1837
+ let status10 = import_nice_grpc_common.Status.OK;
1628
1838
  try {
1629
1839
  let request;
1630
1840
  if (!call.requestStream) {
@@ -1650,44 +1860,36 @@ function clientMiddleware() {
1650
1860
  } catch (err) {
1651
1861
  settled = true;
1652
1862
  if (err instanceof import_nice_grpc_common.ClientError) {
1653
- status6 = err.code;
1863
+ status10 = err.code;
1654
1864
  } else if ((0, import_abort_controller_x.isAbortError)(err)) {
1655
- status6 = import_nice_grpc_common.Status.ABORTED;
1865
+ status10 = import_nice_grpc_common.Status.ABORTED;
1656
1866
  } else {
1657
- status6 = import_nice_grpc_common.Status.UNKNOWN;
1867
+ status10 = import_nice_grpc_common.Status.UNKNOWN;
1658
1868
  }
1659
1869
  throw err;
1660
1870
  } finally {
1661
1871
  if (!settled) {
1662
- status6 = import_nice_grpc_common.Status.CANCELLED;
1872
+ status10 = import_nice_grpc_common.Status.CANCELLED;
1663
1873
  }
1664
- stopTimer({ [codeLabel]: import_nice_grpc_common.Status[status6] });
1874
+ stopTimer({ [codeLabel]: import_nice_grpc_common.Status[status10] });
1665
1875
  clientHandledMetric.inc({
1666
1876
  ...labels,
1667
- [codeLabel]: import_nice_grpc_common.Status[status6]
1877
+ [codeLabel]: import_nice_grpc_common.Status[status10]
1668
1878
  });
1669
1879
  }
1670
1880
  };
1671
1881
  }
1672
1882
 
1673
1883
  // src/utilities/initializeSdk.ts
1674
- var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStatusGetter = () => import_grpc_sdk13.HealthCheckStatus.SERVING) => {
1884
+ var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStatusGetter = () => import_grpc_sdk14.HealthCheckStatus.SERVING) => {
1675
1885
  let logger;
1676
1886
  try {
1677
- logger = new ConduitLogger([
1678
- new import_winston2.default.transports.File({
1679
- filename: import_path2.default.join(__dirname, ".logs/combined.log"),
1680
- level: "info"
1681
- }),
1682
- new import_winston2.default.transports.File({
1683
- filename: import_path2.default.join(__dirname, ".logs/errors.log"),
1684
- level: "error"
1685
- })
1686
- ]);
1687
- } catch (e) {
1887
+ const fileTransports = createFileLogTransports();
1888
+ logger = fileTransports.length > 0 ? new ConduitLogger(fileTransports) : new ConduitLogger();
1889
+ } catch {
1688
1890
  logger = new ConduitLogger();
1689
1891
  }
1690
- const grpcSdk = new import_grpc_sdk13.ConduitGrpcSdk(
1892
+ const grpcSdk = new import_grpc_sdk14.ConduitGrpcSdk(
1691
1893
  coreUrl,
1692
1894
  moduleName,
1693
1895
  watchModules,
@@ -1696,7 +1898,7 @@ var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStat
1696
1898
  );
1697
1899
  setupLoki(grpcSdk.name, grpcSdk.instance).then();
1698
1900
  if (process.env.METRICS_PORT) {
1699
- import_grpc_sdk13.ConduitGrpcSdk.Metrics = new ConduitMetrics(grpcSdk.name, grpcSdk.instance);
1901
+ import_grpc_sdk14.ConduitGrpcSdk.Metrics = new ConduitMetrics(grpcSdk.name, grpcSdk.instance);
1700
1902
  grpcSdk.addMiddleware(clientMiddleware());
1701
1903
  }
1702
1904
  return grpcSdk;
@@ -1894,8 +2096,346 @@ async function reconcileStoredModuleConfig(args) {
1894
2096
  return { config: persisted, persisted: true };
1895
2097
  }
1896
2098
 
2099
+ // src/utilities/accessTokenVerifier.ts
2100
+ var import_grpc_sdk16 = require("@conduitplatform/grpc-sdk");
2101
+ var import_grpc_js8 = require("@grpc/grpc-js");
2102
+ var import_node_crypto = require("crypto");
2103
+
2104
+ // src/utilities/accessTokenVerifier.logic.ts
2105
+ var import_fast_jwt2 = require("fast-jwt");
2106
+ var import_grpc_js7 = require("@grpc/grpc-js");
2107
+ var import_grpc_sdk15 = require("@conduitplatform/grpc-sdk");
2108
+ var DEFAULT_LEEWAY_SECONDS = 5;
2109
+ var verifiers = /* @__PURE__ */ new Map();
2110
+ var decode = (0, import_fast_jwt2.createDecoder)({ complete: true });
2111
+ function fail(reason) {
2112
+ throw new import_grpc_sdk15.GrpcError(import_grpc_js7.status.UNAUTHENTICATED, reason);
2113
+ }
2114
+ function getVerifier(publicKey) {
2115
+ let verifier = verifiers.get(publicKey);
2116
+ if (!verifier) {
2117
+ verifier = (0, import_fast_jwt2.createVerifier)({
2118
+ key: publicKey,
2119
+ algorithms: ["EdDSA"],
2120
+ ignoreExpiration: true,
2121
+ ignoreNotBefore: true,
2122
+ cache: false
2123
+ });
2124
+ verifiers.set(publicKey, verifier);
2125
+ }
2126
+ return verifier;
2127
+ }
2128
+ function selectVerificationKey(kid, signing, now) {
2129
+ if (!kid) return void 0;
2130
+ if (signing.publicKey && kid === signing.kid) return signing.publicKey;
2131
+ if (signing.previousKid && signing.previousPublicKey && kid === signing.previousKid && (signing.previousRetiredAt ?? 0) > now) {
2132
+ return signing.previousPublicKey;
2133
+ }
2134
+ return void 0;
2135
+ }
2136
+ function verifyAccessToken(token, state, options = {}) {
2137
+ const now = options.now ?? Date.now();
2138
+ const leeway = options.leewaySeconds ?? DEFAULT_LEEWAY_SECONDS;
2139
+ if (typeof token !== "string" || token.length === 0) fail("malformed token");
2140
+ let kid;
2141
+ try {
2142
+ const decoded = decode(token);
2143
+ if (decoded.header.alg !== "EdDSA") fail("unsupported algorithm");
2144
+ kid = decoded.header.kid;
2145
+ } catch (e) {
2146
+ if (e instanceof import_grpc_sdk15.GrpcError) throw e;
2147
+ fail("malformed token");
2148
+ }
2149
+ const key = selectVerificationKey(kid, state.signing, now);
2150
+ if (!key) fail("unknown kid");
2151
+ let claims;
2152
+ try {
2153
+ claims = getVerifier(key)(token);
2154
+ } catch {
2155
+ fail("bad signature");
2156
+ }
2157
+ if (typeof claims.sub !== "string" || typeof claims.sid !== "string" || typeof claims.exp !== "number") {
2158
+ fail("malformed token");
2159
+ }
2160
+ if (now / 1e3 >= claims.exp + leeway) fail("expired");
2161
+ if (claims.tv !== state.tokenVersion) fail("version mismatch");
2162
+ if (options.clientId !== void 0 && claims.cid !== options.clientId) {
2163
+ fail("client mismatch");
2164
+ }
2165
+ return claims;
2166
+ }
2167
+
2168
+ // src/utilities/accessTokenVerifier.ts
2169
+ var AUTHENTICATION_CONFIG_UPDATE_CHANNEL = "authentication:config:update";
2170
+ var INITIAL_RETRY_MS = 1e3;
2171
+ var MAX_RETRY_MS = 3e4;
2172
+ var REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
2173
+ var UNKNOWN_KID_REFETCH_MIN_MS = 10 * 1e3;
2174
+ var SUBSCRIBER_ID = "access-token-verifier";
2175
+ function isConfigWithAccessTokens(config) {
2176
+ return typeof config === "object" && config !== null && typeof config.accessTokens === "object" && config.accessTokens !== null;
2177
+ }
2178
+ var AccessTokenVerifier = class _AccessTokenVerifier {
2179
+ constructor(grpcSdk) {
2180
+ this.grpcSdk = grpcSdk;
2181
+ }
2182
+ grpcSdk;
2183
+ static instance;
2184
+ _mode = "session";
2185
+ _strictRevocation = false;
2186
+ _tokenVersion = 0;
2187
+ _signing;
2188
+ _loaded = false;
2189
+ _subscribed = false;
2190
+ _generation = 0;
2191
+ _running = false;
2192
+ _retryTimer;
2193
+ _refreshTimer;
2194
+ _lastRefetch = 0;
2195
+ _refetching = false;
2196
+ _warned = /* @__PURE__ */ new Set();
2197
+ _listeners = /* @__PURE__ */ new Set();
2198
+ static getInstance(grpcSdk) {
2199
+ if (!_AccessTokenVerifier.instance) {
2200
+ _AccessTokenVerifier.instance = new _AccessTokenVerifier(grpcSdk);
2201
+ }
2202
+ return _AccessTokenVerifier.instance;
2203
+ }
2204
+ /**
2205
+ * Internal, for tests only: stops the verifier, clears all state and drops the singleton.
2206
+ */
2207
+ static resetForTests() {
2208
+ _AccessTokenVerifier.instance?.stop();
2209
+ _AccessTokenVerifier.instance?.resetState();
2210
+ _AccessTokenVerifier.instance = void 0;
2211
+ }
2212
+ resetState() {
2213
+ this._mode = "session";
2214
+ this._strictRevocation = false;
2215
+ this._tokenVersion = 0;
2216
+ this._signing = void 0;
2217
+ this._loaded = false;
2218
+ this._lastRefetch = 0;
2219
+ this._warned.clear();
2220
+ this._listeners.clear();
2221
+ }
2222
+ /** Snapshot of the verifier's observable state. */
2223
+ get state() {
2224
+ return {
2225
+ mode: this._mode,
2226
+ strictRevocation: this._strictRevocation,
2227
+ ready: this.ready,
2228
+ tokenVersion: this._tokenVersion,
2229
+ kid: this._signing?.kid
2230
+ };
2231
+ }
2232
+ /**
2233
+ * Registers a listener fired synchronously at the end of every applyConfig that changed
2234
+ * anything (including the first successful load). Returns an unsubscribe function.
2235
+ */
2236
+ onChange(listener) {
2237
+ this._listeners.add(listener);
2238
+ return () => {
2239
+ this._listeners.delete(listener);
2240
+ };
2241
+ }
2242
+ fingerprint() {
2243
+ return JSON.stringify([this._loaded, this._signing, this.state]);
2244
+ }
2245
+ get mode() {
2246
+ return this._mode;
2247
+ }
2248
+ get strictRevocation() {
2249
+ return this._strictRevocation;
2250
+ }
2251
+ /** True once signing keys have been loaded. */
2252
+ get ready() {
2253
+ return !!this._signing?.publicKey;
2254
+ }
2255
+ get tokenVersion() {
2256
+ return this._tokenVersion;
2257
+ }
2258
+ /**
2259
+ * Loads the authentication config, subscribes to its updates and starts a slow periodic
2260
+ * refresh. The first attempt is awaited; if it fails, retries continue in the background
2261
+ * with capped backoff until the config is loaded and the subscription is acknowledged.
2262
+ * Idempotent while running; never throws.
2263
+ */
2264
+ async start() {
2265
+ if (this._running) return;
2266
+ this._running = true;
2267
+ const generation = ++this._generation;
2268
+ await this.attempt(generation, INITIAL_RETRY_MS);
2269
+ if (generation !== this._generation) return;
2270
+ this._refreshTimer = setInterval(() => void this.refetch(true), REFRESH_INTERVAL_MS);
2271
+ this._refreshTimer.unref?.();
2272
+ }
2273
+ stop() {
2274
+ this._running = false;
2275
+ this._generation++;
2276
+ if (this._retryTimer) clearTimeout(this._retryTimer);
2277
+ if (this._refreshTimer) clearInterval(this._refreshTimer);
2278
+ this._retryTimer = void 0;
2279
+ this._refreshTimer = void 0;
2280
+ if (this._subscribed) {
2281
+ try {
2282
+ this.grpcSdk.bus?.unsubscribe(SUBSCRIBER_ID);
2283
+ } catch {
2284
+ }
2285
+ this._subscribed = false;
2286
+ }
2287
+ }
2288
+ async attempt(generation, delay) {
2289
+ if (generation !== this._generation) return;
2290
+ try {
2291
+ await this.subscribe(generation);
2292
+ if (!this._loaded) {
2293
+ this.applyConfig(await this.grpcSdk.config.get("authentication"));
2294
+ }
2295
+ } catch {
2296
+ }
2297
+ if (generation !== this._generation) return;
2298
+ if (this._subscribed && this._loaded) return;
2299
+ this._retryTimer = setTimeout(
2300
+ () => void this.attempt(generation, Math.min(delay * 2, MAX_RETRY_MS)),
2301
+ delay
2302
+ );
2303
+ this._retryTimer.unref?.();
2304
+ }
2305
+ async subscribe(generation) {
2306
+ if (this._subscribed) return;
2307
+ const bus = this.grpcSdk.bus;
2308
+ if (!bus) return;
2309
+ await bus.subscribeAck(
2310
+ AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
2311
+ (message) => {
2312
+ try {
2313
+ this.applyConfig(JSON.parse(message));
2314
+ } catch {
2315
+ }
2316
+ },
2317
+ SUBSCRIBER_ID
2318
+ );
2319
+ if (generation !== this._generation) {
2320
+ bus.unsubscribe(SUBSCRIBER_ID);
2321
+ return;
2322
+ }
2323
+ this._subscribed = true;
2324
+ }
2325
+ /** Refetches the config from the authentication module; failures keep the previous state. */
2326
+ async refetch(force = false) {
2327
+ const now = Date.now();
2328
+ if (this._refetching) return;
2329
+ if (!force && now - this._lastRefetch < UNKNOWN_KID_REFETCH_MIN_MS) return;
2330
+ this._lastRefetch = now;
2331
+ this._refetching = true;
2332
+ try {
2333
+ this.applyConfig(await this.grpcSdk.config.get("authentication"));
2334
+ } catch {
2335
+ } finally {
2336
+ this._refetching = false;
2337
+ }
2338
+ }
2339
+ warnOnce(key, message) {
2340
+ if (this._warned.has(key)) return;
2341
+ this._warned.add(key);
2342
+ import_grpc_sdk16.ConduitGrpcSdk.Logger.warn(`AccessTokenVerifier: ${message}`);
2343
+ }
2344
+ static validPem(pem) {
2345
+ if (typeof pem !== "string" || !pem) return false;
2346
+ try {
2347
+ (0, import_node_crypto.createPublicKey)(pem);
2348
+ return true;
2349
+ } catch {
2350
+ return false;
2351
+ }
2352
+ }
2353
+ /**
2354
+ * Validates and normalizes the signing keys. Returns undefined when no signing block is
2355
+ * configured, null when the public key is invalid (the caller keeps the previous state).
2356
+ */
2357
+ normalizeSigning(s) {
2358
+ if (!(s && typeof s.kid === "string" && s.kid && s.publicKey)) return void 0;
2359
+ if (!_AccessTokenVerifier.validPem(s.publicKey)) {
2360
+ this.warnOnce("publicKey", "invalid signing public key, keeping previous state");
2361
+ return null;
2362
+ }
2363
+ const previousPublicKey = this.validPreviousKey(s.previousPublicKey);
2364
+ return {
2365
+ kid: s.kid,
2366
+ publicKey: s.publicKey,
2367
+ previousKid: previousPublicKey ? s.previousKid || void 0 : void 0,
2368
+ previousPublicKey,
2369
+ previousRetiredAt: Number(s.previousRetiredAt ?? 0)
2370
+ };
2371
+ }
2372
+ validPreviousKey(pem) {
2373
+ if (!pem) return void 0;
2374
+ if (_AccessTokenVerifier.validPem(pem)) return pem;
2375
+ this.warnOnce("previousPublicKey", "invalid previous public key, ignoring it");
2376
+ return void 0;
2377
+ }
2378
+ parseTokenVersion(raw) {
2379
+ const version = Number(raw ?? 0);
2380
+ if (Number.isNaN(version)) {
2381
+ this.warnOnce("tokenVersion", "invalid tokenVersion, all tokens will be rejected");
2382
+ }
2383
+ return version;
2384
+ }
2385
+ notifyListeners() {
2386
+ const state = this.state;
2387
+ for (const listener of [...this._listeners]) {
2388
+ try {
2389
+ listener(state);
2390
+ } catch (e) {
2391
+ import_grpc_sdk16.ConduitGrpcSdk.Logger.warn(
2392
+ `AccessTokenVerifier: change listener failed: ${e?.message}`
2393
+ );
2394
+ }
2395
+ }
2396
+ }
2397
+ /**
2398
+ * Applies an authentication module config object (full config). Invalid input is ignored
2399
+ * and the previous state is kept; invalid keys are logged once.
2400
+ */
2401
+ applyConfig(config) {
2402
+ if (!isConfigWithAccessTokens(config)) return;
2403
+ const tokens = config.accessTokens;
2404
+ const before = this.fingerprint();
2405
+ const signing = this.normalizeSigning(tokens.signing);
2406
+ if (signing === null) return;
2407
+ const version = this.parseTokenVersion(tokens.tokenVersion);
2408
+ this._mode = tokens.mode === "jwt" ? "jwt" : "session";
2409
+ this._tokenVersion = version;
2410
+ this._strictRevocation = tokens.strictRevocation === true;
2411
+ this._signing = signing;
2412
+ this._loaded = true;
2413
+ if (this.fingerprint() !== before) this.notifyListeners();
2414
+ }
2415
+ /**
2416
+ * Verifies an access token, throwing GrpcError(UNAUTHENTICATED) with a short reason on failure.
2417
+ * `options.now` is epoch milliseconds. An `unknown kid` triggers a throttled (at most once
2418
+ * per 10 s) background refetch of the config, to pick up a rotation sooner.
2419
+ */
2420
+ verify(token, options) {
2421
+ if (!this._signing) {
2422
+ throw new import_grpc_sdk16.GrpcError(import_grpc_js8.status.UNAUTHENTICATED, "verifier not ready");
2423
+ }
2424
+ try {
2425
+ return verifyAccessToken(
2426
+ token,
2427
+ { tokenVersion: this._tokenVersion, signing: this._signing },
2428
+ options
2429
+ );
2430
+ } catch (e) {
2431
+ if (e.message === "unknown kid") void this.refetch();
2432
+ throw e;
2433
+ }
2434
+ }
2435
+ };
2436
+
1897
2437
  // src/routing/RoutingController.ts
1898
- var import_grpc_js5 = require("@grpc/grpc-js");
2438
+ var import_grpc_js9 = require("@grpc/grpc-js");
1899
2439
  var RoutingController = class {
1900
2440
  _routeHandlers = {};
1901
2441
  handleRequest(call, callback) {
@@ -1903,7 +2443,7 @@ var RoutingController = class {
1903
2443
  this._routeHandlers[call.request.functionName](call, callback);
1904
2444
  } else {
1905
2445
  callback({
1906
- code: import_grpc_js5.status.NOT_FOUND,
2446
+ code: import_grpc_js9.status.NOT_FOUND,
1907
2447
  message: "Not found"
1908
2448
  });
1909
2449
  }
@@ -1962,13 +2502,13 @@ var ManagedModule = class extends ConduitServiceModule {
1962
2502
  await this.preRegisterLifecycle();
1963
2503
  await this.grpcSdk.config.registerModule(this.name, this.address, this.healthState);
1964
2504
  } catch (err) {
1965
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error("Failed to initialize server");
1966
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error(err);
2505
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error("Failed to initialize server");
2506
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error(err);
1967
2507
  process.exit(-1);
1968
2508
  }
1969
2509
  await this.postRegisterLifecycle().catch((err) => {
1970
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error("Failed to activate module");
1971
- import_grpc_sdk14.ConduitGrpcSdk.Logger.error(err);
2510
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error("Failed to activate module");
2511
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.error(err);
1972
2512
  process.exit(-1);
1973
2513
  });
1974
2514
  }
@@ -2035,11 +2575,11 @@ var ManagedModule = class extends ConduitServiceModule {
2035
2575
  * Registers common and module-specific metric types.
2036
2576
  */
2037
2577
  async registerMetrics() {
2038
- if (import_grpc_sdk14.ConduitGrpcSdk.Metrics) {
2039
- import_grpc_sdk14.ConduitGrpcSdk.Metrics.initializeDefaultMetrics();
2578
+ if (import_grpc_sdk17.ConduitGrpcSdk.Metrics) {
2579
+ import_grpc_sdk17.ConduitGrpcSdk.Metrics.initializeDefaultMetrics();
2040
2580
  if (this.metricsSchema) {
2041
2581
  Object.values(this.metricsSchema).forEach((metric) => {
2042
- import_grpc_sdk14.ConduitGrpcSdk.Metrics.registerMetric(metric.type, metric.config);
2582
+ import_grpc_sdk17.ConduitGrpcSdk.Metrics.registerMetric(metric.type, metric.config);
2043
2583
  });
2044
2584
  }
2045
2585
  }
@@ -2063,7 +2603,7 @@ var ManagedModule = class extends ConduitServiceModule {
2063
2603
  const known = new Set(this.grpcSdk.getKnownModuleNames());
2064
2604
  for (const n of names) {
2065
2605
  if (!known.has(n)) {
2066
- import_grpc_sdk14.ConduitGrpcSdk.Logger.warn(
2606
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.warn(
2067
2607
  `Declared Conduit peer "${n}" is not a known built-in module (see ConduitGrpcSdk.getKnownModuleNames).`
2068
2608
  );
2069
2609
  }
@@ -2124,18 +2664,19 @@ var ManagedModule = class extends ConduitServiceModule {
2124
2664
  this.service.functions
2125
2665
  );
2126
2666
  }
2667
+ RoutingManager.grpcSdk = this.grpcSdk;
2127
2668
  RoutingManager.ClientController = new RoutingController();
2128
2669
  RoutingManager.AdminController = new RoutingController();
2129
2670
  await this.addHealthCheckService();
2130
2671
  await this.addModuleService();
2131
2672
  await this.grpcServer.start();
2132
- import_grpc_sdk14.ConduitGrpcSdk.Logger.log("gRPC server listening on " + this._port);
2673
+ import_grpc_sdk17.ConduitGrpcSdk.Logger.log("gRPC server listening on " + this._port);
2133
2674
  }
2134
2675
  async setConfig(call, callback) {
2135
2676
  try {
2136
2677
  if (!this.config) {
2137
2678
  return callback({
2138
- code: import_grpc_js6.status.INVALID_ARGUMENT,
2679
+ code: import_grpc_js10.status.INVALID_ARGUMENT,
2139
2680
  message: "Module is not configurable"
2140
2681
  });
2141
2682
  }
@@ -2154,7 +2695,7 @@ var ManagedModule = class extends ConduitServiceModule {
2154
2695
  this.config = (0, import_convict.default)(this.configSchema);
2155
2696
  this.config.load(previousConfig);
2156
2697
  return callback({
2157
- code: import_grpc_js6.status.INVALID_ARGUMENT,
2698
+ code: import_grpc_js10.status.INVALID_ARGUMENT,
2158
2699
  message: "Invalid configuration values: " + e.message
2159
2700
  });
2160
2701
  }
@@ -2163,7 +2704,7 @@ var ManagedModule = class extends ConduitServiceModule {
2163
2704
  await this._drainConfigQueue();
2164
2705
  this.grpcSdk.bus?.publish((0, import_lodash_es6.kebabCase)(this.name) + ":config:update", configJson);
2165
2706
  } catch (e) {
2166
- return callback({ code: import_grpc_js6.status.INTERNAL, message: e.message });
2707
+ return callback({ code: import_grpc_js10.status.INTERNAL, message: e.message });
2167
2708
  }
2168
2709
  }
2169
2710
  /** Used to update the module's configuration on initial Redis/DB reconciliation. */
@@ -2243,8 +2784,8 @@ var ManagedModule = class extends ConduitServiceModule {
2243
2784
  };
2244
2785
 
2245
2786
  // src/classes/ModuleError.ts
2246
- var import_grpc_sdk15 = require("@conduitplatform/grpc-sdk");
2247
- var ModuleError = class extends import_grpc_sdk15.GrpcError {
2787
+ var import_grpc_sdk18 = require("@conduitplatform/grpc-sdk");
2788
+ var ModuleError = class extends import_grpc_sdk18.GrpcError {
2248
2789
  debugLogInfo;
2249
2790
  constructor(errorDefinition, debugLogInfo) {
2250
2791
  const { grpcCode, conduitCode, message } = errorDefinition;
@@ -2260,6 +2801,8 @@ var ModuleError = class extends import_grpc_sdk15.GrpcError {
2260
2801
  };
2261
2802
  // Annotate the CommonJS export names for ESM import in node:
2262
2803
  0 && (module.exports = {
2804
+ AUTHENTICATION_CONFIG_UPDATE_CHANNEL,
2805
+ AccessTokenVerifier,
2263
2806
  ConduitActiveSchema,
2264
2807
  ConduitBoolean,
2265
2808
  ConduitDate,
@@ -2270,6 +2813,7 @@ var ModuleError = class extends import_grpc_sdk15.GrpcError {
2270
2813
  ConduitServiceModule,
2271
2814
  ConduitString,
2272
2815
  ConfigController,
2816
+ DEFAULT_LEEWAY_SECONDS,
2273
2817
  DEFAULT_STRIP_FIELDS,
2274
2818
  GrpcServer,
2275
2819
  ManagedModule,
@@ -2282,20 +2826,27 @@ var ModuleError = class extends import_grpc_sdk15.GrpcError {
2282
2826
  convictConfigParser,
2283
2827
  createParsedRouterRequest,
2284
2828
  createServer,
2829
+ createUserHydrator,
2285
2830
  exportModel,
2831
+ getAuthMiddlewareKind,
2832
+ hydrateContextUser,
2286
2833
  importModel,
2287
2834
  initializeSdk,
2288
2835
  linearBackoffTimeout,
2289
2836
  linearBackoffTimeoutAsync,
2837
+ mapHydrationErrorCode,
2290
2838
  merge,
2291
2839
  readConduitPeersManifest,
2292
2840
  reconcileStoredModuleConfig,
2293
2841
  redactSensitiveConfig,
2294
2842
  restoreRedactedSecrets,
2295
2843
  sanitizeDocumentsForExport,
2844
+ selectVerificationKey,
2845
+ shouldHydrateUser,
2296
2846
  stableConfigJson,
2297
2847
  storedConfigsEquivalent,
2298
2848
  stripInternalFields,
2849
+ verifyAccessToken,
2299
2850
  wrapFunctionsAsync,
2300
2851
  wrapGrpcFunctions,
2301
2852
  wrapRouterGrpcFunction