@backstage/backend-app-api 0.6.3-next.0 → 0.7.0-next.1

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.js CHANGED
@@ -22,13 +22,15 @@ var winston = require('winston');
22
22
  var backendPluginApi = require('@backstage/backend-plugin-api');
23
23
  var alpha = require('@backstage/backend-plugin-api/alpha');
24
24
  var jose = require('jose');
25
+ var pluginAuthNode = require('@backstage/plugin-auth-node');
26
+ var uuid = require('uuid');
27
+ var luxon = require('luxon');
25
28
  var backendCommon = require('@backstage/backend-common');
26
29
  var backendAppApi = require('@backstage/backend-app-api');
27
30
  var cookie = require('cookie');
28
- var PromiseRouter = require('express-promise-router');
31
+ var Router = require('express-promise-router');
29
32
  var types = require('@backstage/types');
30
33
  var pathToRegexp = require('path-to-regexp');
31
- var pluginAuthNode = require('@backstage/plugin-auth-node');
32
34
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
33
35
  var express = require('express');
34
36
  var trimEnd = require('lodash/trimEnd');
@@ -64,22 +66,22 @@ var cors__default = /*#__PURE__*/_interopDefaultCompat(cors);
64
66
  var helmet__default = /*#__PURE__*/_interopDefaultCompat(helmet);
65
67
  var morgan__default = /*#__PURE__*/_interopDefaultCompat(morgan);
66
68
  var compression__default = /*#__PURE__*/_interopDefaultCompat(compression);
67
- var PromiseRouter__default = /*#__PURE__*/_interopDefaultCompat(PromiseRouter);
69
+ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
68
70
  var express__default = /*#__PURE__*/_interopDefaultCompat(express);
69
71
  var trimEnd__default = /*#__PURE__*/_interopDefaultCompat(trimEnd);
70
72
 
71
- var __defProp = Object.defineProperty;
72
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
73
- var __publicField = (obj, key, value) => {
74
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
73
+ var __defProp$1 = Object.defineProperty;
74
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
75
+ var __publicField$1 = (obj, key, value) => {
76
+ __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
75
77
  return value;
76
78
  };
77
79
  class ObservableConfigProxy {
78
80
  constructor(parent, parentKey) {
79
81
  this.parent = parent;
80
82
  this.parentKey = parentKey;
81
- __publicField(this, "config", new config.ConfigReader({}));
82
- __publicField(this, "subscribers", []);
83
+ __publicField$1(this, "config", new config.ConfigReader({}));
84
+ __publicField$1(this, "subscribers", []);
83
85
  if (parent && !parentKey) {
84
86
  throw new Error("parentKey is required if parent is set");
85
87
  }
@@ -610,31 +612,31 @@ function applyInternalErrorFilter(error, logger) {
610
612
  return error;
611
613
  }
612
614
 
613
- var __accessCheck$a = (obj, member, msg) => {
615
+ var __accessCheck$c = (obj, member, msg) => {
614
616
  if (!member.has(obj))
615
617
  throw TypeError("Cannot " + msg);
616
618
  };
617
- var __privateGet$9 = (obj, member, getter) => {
618
- __accessCheck$a(obj, member, "read from private field");
619
+ var __privateGet$b = (obj, member, getter) => {
620
+ __accessCheck$c(obj, member, "read from private field");
619
621
  return getter ? getter.call(obj) : member.get(obj);
620
622
  };
621
- var __privateAdd$a = (obj, member, value) => {
623
+ var __privateAdd$c = (obj, member, value) => {
622
624
  if (member.has(obj))
623
625
  throw TypeError("Cannot add the same private member more than once");
624
626
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
625
627
  };
626
- var __privateSet$9 = (obj, member, value, setter) => {
627
- __accessCheck$a(obj, member, "write to private field");
628
+ var __privateSet$b = (obj, member, value, setter) => {
629
+ __accessCheck$c(obj, member, "write to private field");
628
630
  setter ? setter.call(obj, value) : member.set(obj, value);
629
631
  return value;
630
632
  };
631
633
  var _config, _logger;
632
634
  const _MiddlewareFactory = class _MiddlewareFactory {
633
635
  constructor(options) {
634
- __privateAdd$a(this, _config, void 0);
635
- __privateAdd$a(this, _logger, void 0);
636
- __privateSet$9(this, _config, options.config);
637
- __privateSet$9(this, _logger, options.logger);
636
+ __privateAdd$c(this, _config, void 0);
637
+ __privateAdd$c(this, _logger, void 0);
638
+ __privateSet$b(this, _config, options.config);
639
+ __privateSet$b(this, _logger, options.logger);
638
640
  }
639
641
  /**
640
642
  * Creates a new {@link MiddlewareFactory}.
@@ -680,7 +682,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
680
682
  * @returns An Express request handler
681
683
  */
682
684
  logging() {
683
- const logger = __privateGet$9(this, _logger).child({
685
+ const logger = __privateGet$b(this, _logger).child({
684
686
  type: "incomingRequest"
685
687
  });
686
688
  return morgan__default.default("combined", {
@@ -704,7 +706,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
704
706
  * @returns An Express request handler
705
707
  */
706
708
  helmet() {
707
- return helmet__default.default(readHelmetOptions(__privateGet$9(this, _config).getOptionalConfig("backend")));
709
+ return helmet__default.default(readHelmetOptions(__privateGet$b(this, _config).getOptionalConfig("backend")));
708
710
  }
709
711
  /**
710
712
  * Returns a middleware that implements the cors library.
@@ -719,7 +721,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
719
721
  * @returns An Express request handler
720
722
  */
721
723
  cors() {
722
- return cors__default.default(readCorsOptions(__privateGet$9(this, _config).getOptionalConfig("backend")));
724
+ return cors__default.default(readCorsOptions(__privateGet$b(this, _config).getOptionalConfig("backend")));
723
725
  }
724
726
  /**
725
727
  * Express middleware to handle errors during request processing.
@@ -744,7 +746,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
744
746
  error(options = {}) {
745
747
  var _a;
746
748
  const showStackTraces = (_a = options.showStackTraces) != null ? _a : process.env.NODE_ENV === "development";
747
- const logger = __privateGet$9(this, _logger).child({
749
+ const logger = __privateGet$b(this, _logger).child({
748
750
  type: "errorHandler"
749
751
  });
750
752
  return (rawError, req, res, next) => {
@@ -803,31 +805,31 @@ const escapeRegExp = (text) => {
803
805
  return text.replace(/[.*+?^${}(\)|[\]\\]/g, "\\$&");
804
806
  };
805
807
 
806
- var __accessCheck$9 = (obj, member, msg) => {
808
+ var __accessCheck$b = (obj, member, msg) => {
807
809
  if (!member.has(obj))
808
810
  throw TypeError("Cannot " + msg);
809
811
  };
810
- var __privateGet$8 = (obj, member, getter) => {
811
- __accessCheck$9(obj, member, "read from private field");
812
+ var __privateGet$a = (obj, member, getter) => {
813
+ __accessCheck$b(obj, member, "read from private field");
812
814
  return getter ? getter.call(obj) : member.get(obj);
813
815
  };
814
- var __privateAdd$9 = (obj, member, value) => {
816
+ var __privateAdd$b = (obj, member, value) => {
815
817
  if (member.has(obj))
816
818
  throw TypeError("Cannot add the same private member more than once");
817
819
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
818
820
  };
819
- var __privateSet$8 = (obj, member, value, setter) => {
820
- __accessCheck$9(obj, member, "write to private field");
821
+ var __privateSet$a = (obj, member, value, setter) => {
822
+ __accessCheck$b(obj, member, "write to private field");
821
823
  setter ? setter.call(obj, value) : member.set(obj, value);
822
824
  return value;
823
825
  };
824
826
  var _winston, _addRedactions;
825
827
  const _WinstonLogger = class _WinstonLogger {
826
828
  constructor(winston, addRedactions) {
827
- __privateAdd$9(this, _winston, void 0);
828
- __privateAdd$9(this, _addRedactions, void 0);
829
- __privateSet$8(this, _winston, winston);
830
- __privateSet$8(this, _addRedactions, addRedactions);
829
+ __privateAdd$b(this, _winston, void 0);
830
+ __privateAdd$b(this, _addRedactions, void 0);
831
+ __privateSet$a(this, _winston, winston);
832
+ __privateSet$a(this, _addRedactions, addRedactions);
831
833
  }
832
834
  /**
833
835
  * Creates a {@link WinstonLogger} instance.
@@ -912,44 +914,44 @@ const _WinstonLogger = class _WinstonLogger {
912
914
  );
913
915
  }
914
916
  error(message, meta) {
915
- __privateGet$8(this, _winston).error(message, meta);
917
+ __privateGet$a(this, _winston).error(message, meta);
916
918
  }
917
919
  warn(message, meta) {
918
- __privateGet$8(this, _winston).warn(message, meta);
920
+ __privateGet$a(this, _winston).warn(message, meta);
919
921
  }
920
922
  info(message, meta) {
921
- __privateGet$8(this, _winston).info(message, meta);
923
+ __privateGet$a(this, _winston).info(message, meta);
922
924
  }
923
925
  debug(message, meta) {
924
- __privateGet$8(this, _winston).debug(message, meta);
926
+ __privateGet$a(this, _winston).debug(message, meta);
925
927
  }
926
928
  child(meta) {
927
- return new _WinstonLogger(__privateGet$8(this, _winston).child(meta));
929
+ return new _WinstonLogger(__privateGet$a(this, _winston).child(meta));
928
930
  }
929
931
  addRedactions(redactions) {
930
932
  var _a;
931
- (_a = __privateGet$8(this, _addRedactions)) == null ? void 0 : _a.call(this, redactions);
933
+ (_a = __privateGet$a(this, _addRedactions)) == null ? void 0 : _a.call(this, redactions);
932
934
  }
933
935
  };
934
936
  _winston = new WeakMap();
935
937
  _addRedactions = new WeakMap();
936
938
  let WinstonLogger = _WinstonLogger;
937
939
 
938
- var __accessCheck$8 = (obj, member, msg) => {
940
+ var __accessCheck$a = (obj, member, msg) => {
939
941
  if (!member.has(obj))
940
942
  throw TypeError("Cannot " + msg);
941
943
  };
942
- var __privateGet$7 = (obj, member, getter) => {
943
- __accessCheck$8(obj, member, "read from private field");
944
+ var __privateGet$9 = (obj, member, getter) => {
945
+ __accessCheck$a(obj, member, "read from private field");
944
946
  return getter ? getter.call(obj) : member.get(obj);
945
947
  };
946
- var __privateAdd$8 = (obj, member, value) => {
948
+ var __privateAdd$a = (obj, member, value) => {
947
949
  if (member.has(obj))
948
950
  throw TypeError("Cannot add the same private member more than once");
949
951
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
950
952
  };
951
- var __privateSet$7 = (obj, member, value, setter) => {
952
- __accessCheck$8(obj, member, "write to private field");
953
+ var __privateSet$9 = (obj, member, value, setter) => {
954
+ __accessCheck$a(obj, member, "write to private field");
953
955
  setter ? setter.call(obj, value) : member.set(obj, value);
954
956
  return value;
955
957
  };
@@ -957,25 +959,25 @@ var _hasStarted$1, _startupTasks$1, _hasShutdown, _shutdownTasks;
957
959
  class BackendLifecycleImpl {
958
960
  constructor(logger) {
959
961
  this.logger = logger;
960
- __privateAdd$8(this, _hasStarted$1, false);
961
- __privateAdd$8(this, _startupTasks$1, []);
962
- __privateAdd$8(this, _hasShutdown, false);
963
- __privateAdd$8(this, _shutdownTasks, []);
962
+ __privateAdd$a(this, _hasStarted$1, false);
963
+ __privateAdd$a(this, _startupTasks$1, []);
964
+ __privateAdd$a(this, _hasShutdown, false);
965
+ __privateAdd$a(this, _shutdownTasks, []);
964
966
  }
965
967
  addStartupHook(hook, options) {
966
- if (__privateGet$7(this, _hasStarted$1)) {
968
+ if (__privateGet$9(this, _hasStarted$1)) {
967
969
  throw new Error("Attempted to add startup hook after startup");
968
970
  }
969
- __privateGet$7(this, _startupTasks$1).push({ hook, options });
971
+ __privateGet$9(this, _startupTasks$1).push({ hook, options });
970
972
  }
971
973
  async startup() {
972
- if (__privateGet$7(this, _hasStarted$1)) {
974
+ if (__privateGet$9(this, _hasStarted$1)) {
973
975
  return;
974
976
  }
975
- __privateSet$7(this, _hasStarted$1, true);
976
- this.logger.debug(`Running ${__privateGet$7(this, _startupTasks$1).length} startup tasks...`);
977
+ __privateSet$9(this, _hasStarted$1, true);
978
+ this.logger.debug(`Running ${__privateGet$9(this, _startupTasks$1).length} startup tasks...`);
977
979
  await Promise.all(
978
- __privateGet$7(this, _startupTasks$1).map(async ({ hook, options }) => {
980
+ __privateGet$9(this, _startupTasks$1).map(async ({ hook, options }) => {
979
981
  var _a;
980
982
  const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
981
983
  try {
@@ -988,21 +990,21 @@ class BackendLifecycleImpl {
988
990
  );
989
991
  }
990
992
  addShutdownHook(hook, options) {
991
- if (__privateGet$7(this, _hasShutdown)) {
993
+ if (__privateGet$9(this, _hasShutdown)) {
992
994
  throw new Error("Attempted to add shutdown hook after shutdown");
993
995
  }
994
- __privateGet$7(this, _shutdownTasks).push({ hook, options });
996
+ __privateGet$9(this, _shutdownTasks).push({ hook, options });
995
997
  }
996
998
  async shutdown() {
997
- if (__privateGet$7(this, _hasShutdown)) {
999
+ if (__privateGet$9(this, _hasShutdown)) {
998
1000
  return;
999
1001
  }
1000
- __privateSet$7(this, _hasShutdown, true);
1002
+ __privateSet$9(this, _hasShutdown, true);
1001
1003
  this.logger.debug(
1002
- `Running ${__privateGet$7(this, _shutdownTasks).length} shutdown tasks...`
1004
+ `Running ${__privateGet$9(this, _shutdownTasks).length} shutdown tasks...`
1003
1005
  );
1004
1006
  await Promise.all(
1005
- __privateGet$7(this, _shutdownTasks).map(async ({ hook, options }) => {
1007
+ __privateGet$9(this, _shutdownTasks).map(async ({ hook, options }) => {
1006
1008
  var _a;
1007
1009
  const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
1008
1010
  try {
@@ -1029,21 +1031,21 @@ const rootLifecycleServiceFactory = backendPluginApi.createServiceFactory({
1029
1031
  }
1030
1032
  });
1031
1033
 
1032
- var __accessCheck$7 = (obj, member, msg) => {
1034
+ var __accessCheck$9 = (obj, member, msg) => {
1033
1035
  if (!member.has(obj))
1034
1036
  throw TypeError("Cannot " + msg);
1035
1037
  };
1036
- var __privateGet$6 = (obj, member, getter) => {
1037
- __accessCheck$7(obj, member, "read from private field");
1038
+ var __privateGet$8 = (obj, member, getter) => {
1039
+ __accessCheck$9(obj, member, "read from private field");
1038
1040
  return getter ? getter.call(obj) : member.get(obj);
1039
1041
  };
1040
- var __privateAdd$7 = (obj, member, value) => {
1042
+ var __privateAdd$9 = (obj, member, value) => {
1041
1043
  if (member.has(obj))
1042
1044
  throw TypeError("Cannot add the same private member more than once");
1043
1045
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1044
1046
  };
1045
- var __privateSet$6 = (obj, member, value, setter) => {
1046
- __accessCheck$7(obj, member, "write to private field");
1047
+ var __privateSet$8 = (obj, member, value, setter) => {
1048
+ __accessCheck$9(obj, member, "write to private field");
1047
1049
  setter ? setter.call(obj, value) : member.set(obj, value);
1048
1050
  return value;
1049
1051
  };
@@ -1053,25 +1055,25 @@ class BackendPluginLifecycleImpl {
1053
1055
  this.logger = logger;
1054
1056
  this.rootLifecycle = rootLifecycle;
1055
1057
  this.pluginMetadata = pluginMetadata;
1056
- __privateAdd$7(this, _hasStarted, false);
1057
- __privateAdd$7(this, _startupTasks, []);
1058
+ __privateAdd$9(this, _hasStarted, false);
1059
+ __privateAdd$9(this, _startupTasks, []);
1058
1060
  }
1059
1061
  addStartupHook(hook, options) {
1060
- if (__privateGet$6(this, _hasStarted)) {
1062
+ if (__privateGet$8(this, _hasStarted)) {
1061
1063
  throw new Error("Attempted to add startup hook after startup");
1062
1064
  }
1063
- __privateGet$6(this, _startupTasks).push({ hook, options });
1065
+ __privateGet$8(this, _startupTasks).push({ hook, options });
1064
1066
  }
1065
1067
  async startup() {
1066
- if (__privateGet$6(this, _hasStarted)) {
1068
+ if (__privateGet$8(this, _hasStarted)) {
1067
1069
  return;
1068
1070
  }
1069
- __privateSet$6(this, _hasStarted, true);
1071
+ __privateSet$8(this, _hasStarted, true);
1070
1072
  this.logger.debug(
1071
- `Running ${__privateGet$6(this, _startupTasks).length} plugin startup tasks...`
1073
+ `Running ${__privateGet$8(this, _startupTasks).length} plugin startup tasks...`
1072
1074
  );
1073
1075
  await Promise.all(
1074
- __privateGet$6(this, _startupTasks).map(async ({ hook, options }) => {
1076
+ __privateGet$8(this, _startupTasks).map(async ({ hook, options }) => {
1075
1077
  var _a;
1076
1078
  const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
1077
1079
  try {
@@ -1109,26 +1111,26 @@ const lifecycleServiceFactory = backendPluginApi.createServiceFactory({
1109
1111
  }
1110
1112
  });
1111
1113
 
1112
- var __accessCheck$6 = (obj, member, msg) => {
1114
+ var __accessCheck$8 = (obj, member, msg) => {
1113
1115
  if (!member.has(obj))
1114
1116
  throw TypeError("Cannot " + msg);
1115
1117
  };
1116
- var __privateGet$5 = (obj, member, getter) => {
1117
- __accessCheck$6(obj, member, "read from private field");
1118
+ var __privateGet$7 = (obj, member, getter) => {
1119
+ __accessCheck$8(obj, member, "read from private field");
1118
1120
  return getter ? getter.call(obj) : member.get(obj);
1119
1121
  };
1120
- var __privateAdd$6 = (obj, member, value) => {
1122
+ var __privateAdd$8 = (obj, member, value) => {
1121
1123
  if (member.has(obj))
1122
1124
  throw TypeError("Cannot add the same private member more than once");
1123
1125
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1124
1126
  };
1125
- var __privateSet$5 = (obj, member, value, setter) => {
1126
- __accessCheck$6(obj, member, "write to private field");
1127
+ var __privateSet$7 = (obj, member, value, setter) => {
1128
+ __accessCheck$8(obj, member, "write to private field");
1127
1129
  setter ? setter.call(obj, value) : member.set(obj, value);
1128
1130
  return value;
1129
1131
  };
1130
- var __privateMethod$5 = (obj, member, method) => {
1131
- __accessCheck$6(obj, member, "access private method");
1132
+ var __privateMethod$6 = (obj, member, method) => {
1133
+ __accessCheck$8(obj, member, "access private method");
1132
1134
  return method;
1133
1135
  };
1134
1136
  var _nodeIds, _cycleKeys, _getCycleKey, getCycleKey_fn, _nodes, _allProvided;
@@ -1148,21 +1150,21 @@ class Node {
1148
1150
  }
1149
1151
  const _CycleKeySet = class _CycleKeySet {
1150
1152
  constructor(nodes) {
1151
- __privateAdd$6(this, _getCycleKey);
1152
- __privateAdd$6(this, _nodeIds, void 0);
1153
- __privateAdd$6(this, _cycleKeys, void 0);
1154
- __privateSet$5(this, _nodeIds, new Map(nodes.map((n, i) => [n.value, i])));
1155
- __privateSet$5(this, _cycleKeys, /* @__PURE__ */ new Set());
1153
+ __privateAdd$8(this, _getCycleKey);
1154
+ __privateAdd$8(this, _nodeIds, void 0);
1155
+ __privateAdd$8(this, _cycleKeys, void 0);
1156
+ __privateSet$7(this, _nodeIds, new Map(nodes.map((n, i) => [n.value, i])));
1157
+ __privateSet$7(this, _cycleKeys, /* @__PURE__ */ new Set());
1156
1158
  }
1157
1159
  static from(nodes) {
1158
1160
  return new _CycleKeySet(nodes);
1159
1161
  }
1160
1162
  tryAdd(path) {
1161
- const cycleKey = __privateMethod$5(this, _getCycleKey, getCycleKey_fn).call(this, path);
1162
- if (__privateGet$5(this, _cycleKeys).has(cycleKey)) {
1163
+ const cycleKey = __privateMethod$6(this, _getCycleKey, getCycleKey_fn).call(this, path);
1164
+ if (__privateGet$7(this, _cycleKeys).has(cycleKey)) {
1163
1165
  return false;
1164
1166
  }
1165
- __privateGet$5(this, _cycleKeys).add(cycleKey);
1167
+ __privateGet$7(this, _cycleKeys).add(cycleKey);
1166
1168
  return true;
1167
1169
  }
1168
1170
  };
@@ -1170,18 +1172,18 @@ _nodeIds = new WeakMap();
1170
1172
  _cycleKeys = new WeakMap();
1171
1173
  _getCycleKey = new WeakSet();
1172
1174
  getCycleKey_fn = function(path) {
1173
- return path.map((n) => __privateGet$5(this, _nodeIds).get(n)).sort().join(",");
1175
+ return path.map((n) => __privateGet$7(this, _nodeIds).get(n)).sort().join(",");
1174
1176
  };
1175
1177
  let CycleKeySet = _CycleKeySet;
1176
1178
  const _DependencyGraph = class _DependencyGraph {
1177
1179
  constructor(nodes) {
1178
- __privateAdd$6(this, _nodes, void 0);
1179
- __privateAdd$6(this, _allProvided, void 0);
1180
- __privateSet$5(this, _nodes, nodes);
1181
- __privateSet$5(this, _allProvided, /* @__PURE__ */ new Set());
1182
- for (const node of __privateGet$5(this, _nodes).values()) {
1180
+ __privateAdd$8(this, _nodes, void 0);
1181
+ __privateAdd$8(this, _allProvided, void 0);
1182
+ __privateSet$7(this, _nodes, nodes);
1183
+ __privateSet$7(this, _allProvided, /* @__PURE__ */ new Set());
1184
+ for (const node of __privateGet$7(this, _nodes).values()) {
1183
1185
  for (const produced of node.provides) {
1184
- __privateGet$5(this, _allProvided).add(produced);
1186
+ __privateGet$7(this, _allProvided).add(produced);
1185
1187
  }
1186
1188
  }
1187
1189
  }
@@ -1205,9 +1207,9 @@ const _DependencyGraph = class _DependencyGraph {
1205
1207
  */
1206
1208
  findUnsatisfiedDeps() {
1207
1209
  const unsatisfiedDependencies = [];
1208
- for (const node of __privateGet$5(this, _nodes).values()) {
1210
+ for (const node of __privateGet$7(this, _nodes).values()) {
1209
1211
  const unsatisfied = Array.from(node.consumes).filter(
1210
- (id) => !__privateGet$5(this, _allProvided).has(id)
1212
+ (id) => !__privateGet$7(this, _allProvided).has(id)
1211
1213
  );
1212
1214
  if (unsatisfied.length > 0) {
1213
1215
  unsatisfiedDependencies.push({ value: node.value, unsatisfied });
@@ -1227,8 +1229,8 @@ const _DependencyGraph = class _DependencyGraph {
1227
1229
  * form a cycle, with the same node as the first and last element of the array.
1228
1230
  */
1229
1231
  *detectCircularDependencies() {
1230
- const cycleKeys = CycleKeySet.from(__privateGet$5(this, _nodes));
1231
- for (const startNode of __privateGet$5(this, _nodes)) {
1232
+ const cycleKeys = CycleKeySet.from(__privateGet$7(this, _nodes));
1233
+ for (const startNode of __privateGet$7(this, _nodes)) {
1232
1234
  const visited = /* @__PURE__ */ new Set();
1233
1235
  const stack = new Array([
1234
1236
  startNode,
@@ -1241,7 +1243,7 @@ const _DependencyGraph = class _DependencyGraph {
1241
1243
  }
1242
1244
  visited.add(node);
1243
1245
  for (const consumed of node.consumes) {
1244
- const providerNodes = __privateGet$5(this, _nodes).filter(
1246
+ const providerNodes = __privateGet$7(this, _nodes).filter(
1245
1247
  (other) => other.provides.has(consumed)
1246
1248
  );
1247
1249
  for (const provider of providerNodes) {
@@ -1270,9 +1272,9 @@ const _DependencyGraph = class _DependencyGraph {
1270
1272
  * Dependencies of nodes that are not produced by any other nodes will be ignored.
1271
1273
  */
1272
1274
  async parallelTopologicalTraversal(fn) {
1273
- const allProvided = __privateGet$5(this, _allProvided);
1275
+ const allProvided = __privateGet$7(this, _allProvided);
1274
1276
  const producedSoFar = /* @__PURE__ */ new Set();
1275
- const waiting = new Set(__privateGet$5(this, _nodes).values());
1277
+ const waiting = new Set(__privateGet$7(this, _nodes).values());
1276
1278
  const visited = /* @__PURE__ */ new Set();
1277
1279
  const results = new Array();
1278
1280
  let inFlight = 0;
@@ -1318,26 +1320,26 @@ _nodes = new WeakMap();
1318
1320
  _allProvided = new WeakMap();
1319
1321
  let DependencyGraph = _DependencyGraph;
1320
1322
 
1321
- var __accessCheck$5 = (obj, member, msg) => {
1323
+ var __accessCheck$7 = (obj, member, msg) => {
1322
1324
  if (!member.has(obj))
1323
1325
  throw TypeError("Cannot " + msg);
1324
1326
  };
1325
- var __privateGet$4 = (obj, member, getter) => {
1326
- __accessCheck$5(obj, member, "read from private field");
1327
+ var __privateGet$6 = (obj, member, getter) => {
1328
+ __accessCheck$7(obj, member, "read from private field");
1327
1329
  return getter ? getter.call(obj) : member.get(obj);
1328
1330
  };
1329
- var __privateAdd$5 = (obj, member, value) => {
1331
+ var __privateAdd$7 = (obj, member, value) => {
1330
1332
  if (member.has(obj))
1331
1333
  throw TypeError("Cannot add the same private member more than once");
1332
1334
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1333
1335
  };
1334
- var __privateSet$4 = (obj, member, value, setter) => {
1335
- __accessCheck$5(obj, member, "write to private field");
1336
+ var __privateSet$6 = (obj, member, value, setter) => {
1337
+ __accessCheck$7(obj, member, "write to private field");
1336
1338
  setter ? setter.call(obj, value) : member.set(obj, value);
1337
1339
  return value;
1338
1340
  };
1339
- var __privateMethod$4 = (obj, member, method) => {
1340
- __accessCheck$5(obj, member, "access private method");
1341
+ var __privateMethod$5 = (obj, member, method) => {
1342
+ __accessCheck$7(obj, member, "access private method");
1341
1343
  return method;
1342
1344
  };
1343
1345
  var _providedFactories, _loadedDefaultFactories, _implementations, _rootServiceImplementations, _addedFactoryIds, _instantiatedFactories, _resolveFactory, resolveFactory_fn, _checkForMissingDeps, checkForMissingDeps_fn;
@@ -1360,19 +1362,19 @@ const pluginMetadataServiceFactory = backendPluginApi.createServiceFactory(
1360
1362
  );
1361
1363
  const _ServiceRegistry = class _ServiceRegistry {
1362
1364
  constructor(factories) {
1363
- __privateAdd$5(this, _resolveFactory);
1364
- __privateAdd$5(this, _checkForMissingDeps);
1365
- __privateAdd$5(this, _providedFactories, void 0);
1366
- __privateAdd$5(this, _loadedDefaultFactories, void 0);
1367
- __privateAdd$5(this, _implementations, void 0);
1368
- __privateAdd$5(this, _rootServiceImplementations, /* @__PURE__ */ new Map());
1369
- __privateAdd$5(this, _addedFactoryIds, /* @__PURE__ */ new Set());
1370
- __privateAdd$5(this, _instantiatedFactories, /* @__PURE__ */ new Set());
1371
- __privateSet$4(this, _providedFactories, new Map(
1365
+ __privateAdd$7(this, _resolveFactory);
1366
+ __privateAdd$7(this, _checkForMissingDeps);
1367
+ __privateAdd$7(this, _providedFactories, void 0);
1368
+ __privateAdd$7(this, _loadedDefaultFactories, void 0);
1369
+ __privateAdd$7(this, _implementations, void 0);
1370
+ __privateAdd$7(this, _rootServiceImplementations, /* @__PURE__ */ new Map());
1371
+ __privateAdd$7(this, _addedFactoryIds, /* @__PURE__ */ new Set());
1372
+ __privateAdd$7(this, _instantiatedFactories, /* @__PURE__ */ new Set());
1373
+ __privateSet$6(this, _providedFactories, new Map(
1372
1374
  factories.map((sf) => [sf.service.id, toInternalServiceFactory(sf)])
1373
1375
  ));
1374
- __privateSet$4(this, _loadedDefaultFactories, /* @__PURE__ */ new Map());
1375
- __privateSet$4(this, _implementations, /* @__PURE__ */ new Map());
1376
+ __privateSet$6(this, _loadedDefaultFactories, /* @__PURE__ */ new Map());
1377
+ __privateSet$6(this, _implementations, /* @__PURE__ */ new Map());
1376
1378
  }
1377
1379
  static create(factories) {
1378
1380
  const registry = new _ServiceRegistry(factories);
@@ -1381,7 +1383,7 @@ const _ServiceRegistry = class _ServiceRegistry {
1381
1383
  }
1382
1384
  checkForCircularDeps() {
1383
1385
  const graph = DependencyGraph.fromIterable(
1384
- Array.from(__privateGet$4(this, _providedFactories)).map(
1386
+ Array.from(__privateGet$6(this, _providedFactories)).map(
1385
1387
  ([serviceId, serviceFactory]) => ({
1386
1388
  value: serviceId,
1387
1389
  provides: [serviceId],
@@ -1403,30 +1405,38 @@ const _ServiceRegistry = class _ServiceRegistry {
1403
1405
  `The ${backendPluginApi.coreServices.pluginMetadata.id} service cannot be overridden`
1404
1406
  );
1405
1407
  }
1406
- if (__privateGet$4(this, _addedFactoryIds).has(factoryId)) {
1408
+ if (__privateGet$6(this, _addedFactoryIds).has(factoryId)) {
1407
1409
  throw new Error(
1408
1410
  `Duplicate service implementations provided for ${factoryId}`
1409
1411
  );
1410
1412
  }
1411
- if (__privateGet$4(this, _instantiatedFactories).has(factoryId)) {
1413
+ if (__privateGet$6(this, _instantiatedFactories).has(factoryId)) {
1412
1414
  throw new Error(
1413
1415
  `Unable to set service factory with id ${factoryId}, service has already been instantiated`
1414
1416
  );
1415
1417
  }
1416
- __privateGet$4(this, _addedFactoryIds).add(factoryId);
1417
- __privateGet$4(this, _providedFactories).set(factoryId, toInternalServiceFactory(factory));
1418
+ __privateGet$6(this, _addedFactoryIds).add(factoryId);
1419
+ __privateGet$6(this, _providedFactories).set(factoryId, toInternalServiceFactory(factory));
1418
1420
  }
1419
- getServiceRefs() {
1420
- return Array.from(__privateGet$4(this, _providedFactories).values()).map((f) => f.service);
1421
+ async initializeEagerServicesWithScope(scope, pluginId = "root") {
1422
+ for (const factory of __privateGet$6(this, _providedFactories).values()) {
1423
+ if (factory.service.scope === scope) {
1424
+ if (scope === "root" && factory.initialization !== "lazy") {
1425
+ await this.get(factory.service, pluginId);
1426
+ } else if (scope === "plugin" && factory.initialization === "always") {
1427
+ await this.get(factory.service, pluginId);
1428
+ }
1429
+ }
1430
+ }
1421
1431
  }
1422
1432
  get(ref, pluginId) {
1423
1433
  var _a;
1424
- __privateGet$4(this, _instantiatedFactories).add(ref.id);
1425
- return (_a = __privateMethod$4(this, _resolveFactory, resolveFactory_fn).call(this, ref, pluginId)) == null ? void 0 : _a.then((factory) => {
1434
+ __privateGet$6(this, _instantiatedFactories).add(ref.id);
1435
+ return (_a = __privateMethod$5(this, _resolveFactory, resolveFactory_fn).call(this, ref, pluginId)) == null ? void 0 : _a.then((factory) => {
1426
1436
  if (factory.service.scope === "root") {
1427
- let existing = __privateGet$4(this, _rootServiceImplementations).get(factory);
1437
+ let existing = __privateGet$6(this, _rootServiceImplementations).get(factory);
1428
1438
  if (!existing) {
1429
- __privateMethod$4(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
1439
+ __privateMethod$5(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
1430
1440
  const rootDeps = new Array();
1431
1441
  for (const [name, serviceRef] of Object.entries(factory.deps)) {
1432
1442
  if (serviceRef.scope !== "root") {
@@ -1440,13 +1450,13 @@ const _ServiceRegistry = class _ServiceRegistry {
1440
1450
  existing = Promise.all(rootDeps).then(
1441
1451
  (entries) => factory.factory(Object.fromEntries(entries), void 0)
1442
1452
  );
1443
- __privateGet$4(this, _rootServiceImplementations).set(factory, existing);
1453
+ __privateGet$6(this, _rootServiceImplementations).set(factory, existing);
1444
1454
  }
1445
1455
  return existing;
1446
1456
  }
1447
- let implementation = __privateGet$4(this, _implementations).get(factory);
1457
+ let implementation = __privateGet$6(this, _implementations).get(factory);
1448
1458
  if (!implementation) {
1449
- __privateMethod$4(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
1459
+ __privateMethod$5(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
1450
1460
  const rootDeps = new Array();
1451
1461
  for (const [name, serviceRef] of Object.entries(factory.deps)) {
1452
1462
  if (serviceRef.scope === "root") {
@@ -1468,7 +1478,7 @@ const _ServiceRegistry = class _ServiceRegistry {
1468
1478
  }),
1469
1479
  byPlugin: /* @__PURE__ */ new Map()
1470
1480
  };
1471
- __privateGet$4(this, _implementations).set(factory, implementation);
1481
+ __privateGet$6(this, _implementations).set(factory, implementation);
1472
1482
  }
1473
1483
  let result = implementation.byPlugin.get(pluginId);
1474
1484
  if (!result) {
@@ -1506,18 +1516,18 @@ resolveFactory_fn = function(ref, pluginId) {
1506
1516
  toInternalServiceFactory(pluginMetadataServiceFactory({ pluginId }))
1507
1517
  );
1508
1518
  }
1509
- let resolvedFactory = __privateGet$4(this, _providedFactories).get(ref.id);
1519
+ let resolvedFactory = __privateGet$6(this, _providedFactories).get(ref.id);
1510
1520
  const { __defaultFactory: defaultFactory } = ref;
1511
1521
  if (!resolvedFactory && !defaultFactory) {
1512
1522
  return void 0;
1513
1523
  }
1514
1524
  if (!resolvedFactory) {
1515
- let loadedFactory = __privateGet$4(this, _loadedDefaultFactories).get(defaultFactory);
1525
+ let loadedFactory = __privateGet$6(this, _loadedDefaultFactories).get(defaultFactory);
1516
1526
  if (!loadedFactory) {
1517
1527
  loadedFactory = Promise.resolve().then(() => defaultFactory(ref)).then(
1518
1528
  (f) => toInternalServiceFactory(typeof f === "function" ? f() : f)
1519
1529
  );
1520
- __privateGet$4(this, _loadedDefaultFactories).set(defaultFactory, loadedFactory);
1530
+ __privateGet$6(this, _loadedDefaultFactories).set(defaultFactory, loadedFactory);
1521
1531
  }
1522
1532
  resolvedFactory = loadedFactory.catch((error) => {
1523
1533
  throw new Error(
@@ -1535,7 +1545,7 @@ checkForMissingDeps_fn = function(factory, pluginId) {
1535
1545
  if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
1536
1546
  return false;
1537
1547
  }
1538
- if (__privateGet$4(this, _providedFactories).get(ref.id)) {
1548
+ if (__privateGet$6(this, _providedFactories).get(ref.id)) {
1539
1549
  return false;
1540
1550
  }
1541
1551
  return !ref.__defaultFactory;
@@ -1549,52 +1559,52 @@ checkForMissingDeps_fn = function(factory, pluginId) {
1549
1559
  };
1550
1560
  let ServiceRegistry = _ServiceRegistry;
1551
1561
 
1552
- var __accessCheck$4 = (obj, member, msg) => {
1562
+ var __accessCheck$6 = (obj, member, msg) => {
1553
1563
  if (!member.has(obj))
1554
1564
  throw TypeError("Cannot " + msg);
1555
1565
  };
1556
- var __privateGet$3 = (obj, member, getter) => {
1557
- __accessCheck$4(obj, member, "read from private field");
1566
+ var __privateGet$5 = (obj, member, getter) => {
1567
+ __accessCheck$6(obj, member, "read from private field");
1558
1568
  return getter ? getter.call(obj) : member.get(obj);
1559
1569
  };
1560
- var __privateAdd$4 = (obj, member, value) => {
1570
+ var __privateAdd$6 = (obj, member, value) => {
1561
1571
  if (member.has(obj))
1562
1572
  throw TypeError("Cannot add the same private member more than once");
1563
1573
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1564
1574
  };
1565
- var __privateSet$3 = (obj, member, value, setter) => {
1566
- __accessCheck$4(obj, member, "write to private field");
1575
+ var __privateSet$5 = (obj, member, value, setter) => {
1576
+ __accessCheck$6(obj, member, "write to private field");
1567
1577
  setter ? setter.call(obj, value) : member.set(obj, value);
1568
1578
  return value;
1569
1579
  };
1570
- var __privateMethod$3 = (obj, member, method) => {
1571
- __accessCheck$4(obj, member, "access private method");
1580
+ var __privateMethod$4 = (obj, member, method) => {
1581
+ __accessCheck$6(obj, member, "access private method");
1572
1582
  return method;
1573
1583
  };
1574
1584
  var _startPromise, _features, _extensionPoints, _serviceRegistry, _registeredFeatures, _getInitDeps, getInitDeps_fn, _addFeature, addFeature_fn, _doStart, doStart_fn, _getRootLifecycleImpl, getRootLifecycleImpl_fn, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn;
1575
1585
  class BackendInitializer {
1576
1586
  constructor(defaultApiFactories) {
1577
- __privateAdd$4(this, _getInitDeps);
1578
- __privateAdd$4(this, _addFeature);
1579
- __privateAdd$4(this, _doStart);
1587
+ __privateAdd$6(this, _getInitDeps);
1588
+ __privateAdd$6(this, _addFeature);
1589
+ __privateAdd$6(this, _doStart);
1580
1590
  // Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this
1581
- __privateAdd$4(this, _getRootLifecycleImpl);
1582
- __privateAdd$4(this, _getPluginLifecycleImpl);
1583
- __privateAdd$4(this, _startPromise, void 0);
1584
- __privateAdd$4(this, _features, new Array());
1585
- __privateAdd$4(this, _extensionPoints, /* @__PURE__ */ new Map());
1586
- __privateAdd$4(this, _serviceRegistry, void 0);
1587
- __privateAdd$4(this, _registeredFeatures, new Array());
1588
- __privateSet$3(this, _serviceRegistry, ServiceRegistry.create([...defaultApiFactories]));
1591
+ __privateAdd$6(this, _getRootLifecycleImpl);
1592
+ __privateAdd$6(this, _getPluginLifecycleImpl);
1593
+ __privateAdd$6(this, _startPromise, void 0);
1594
+ __privateAdd$6(this, _features, new Array());
1595
+ __privateAdd$6(this, _extensionPoints, /* @__PURE__ */ new Map());
1596
+ __privateAdd$6(this, _serviceRegistry, void 0);
1597
+ __privateAdd$6(this, _registeredFeatures, new Array());
1598
+ __privateSet$5(this, _serviceRegistry, ServiceRegistry.create([...defaultApiFactories]));
1589
1599
  }
1590
1600
  add(feature) {
1591
- if (__privateGet$3(this, _startPromise)) {
1601
+ if (__privateGet$5(this, _startPromise)) {
1592
1602
  throw new Error("feature can not be added after the backend has started");
1593
1603
  }
1594
- __privateGet$3(this, _registeredFeatures).push(Promise.resolve(feature));
1604
+ __privateGet$5(this, _registeredFeatures).push(Promise.resolve(feature));
1595
1605
  }
1596
1606
  async start() {
1597
- if (__privateGet$3(this, _startPromise)) {
1607
+ if (__privateGet$5(this, _startPromise)) {
1598
1608
  throw new Error("Backend has already started");
1599
1609
  }
1600
1610
  const exitHandler = async () => {
@@ -1612,18 +1622,18 @@ class BackendInitializer {
1612
1622
  process.addListener("SIGTERM", exitHandler);
1613
1623
  process.addListener("SIGINT", exitHandler);
1614
1624
  process.addListener("beforeExit", exitHandler);
1615
- __privateSet$3(this, _startPromise, __privateMethod$3(this, _doStart, doStart_fn).call(this));
1616
- await __privateGet$3(this, _startPromise);
1625
+ __privateSet$5(this, _startPromise, __privateMethod$4(this, _doStart, doStart_fn).call(this));
1626
+ await __privateGet$5(this, _startPromise);
1617
1627
  }
1618
1628
  async stop() {
1619
- if (!__privateGet$3(this, _startPromise)) {
1629
+ if (!__privateGet$5(this, _startPromise)) {
1620
1630
  return;
1621
1631
  }
1622
1632
  try {
1623
- await __privateGet$3(this, _startPromise);
1633
+ await __privateGet$5(this, _startPromise);
1624
1634
  } catch (error) {
1625
1635
  }
1626
- const lifecycleService = await __privateMethod$3(this, _getRootLifecycleImpl, getRootLifecycleImpl_fn).call(this);
1636
+ const lifecycleService = await __privateMethod$4(this, _getRootLifecycleImpl, getRootLifecycleImpl_fn).call(this);
1627
1637
  await lifecycleService.shutdown();
1628
1638
  }
1629
1639
  }
@@ -1637,7 +1647,7 @@ getInitDeps_fn = async function(deps, pluginId, moduleId) {
1637
1647
  const result = /* @__PURE__ */ new Map();
1638
1648
  const missingRefs = /* @__PURE__ */ new Set();
1639
1649
  for (const [name, ref] of Object.entries(deps)) {
1640
- const ep = __privateGet$3(this, _extensionPoints).get(ref.id);
1650
+ const ep = __privateGet$5(this, _extensionPoints).get(ref.id);
1641
1651
  if (ep) {
1642
1652
  if (ep.pluginId !== pluginId) {
1643
1653
  throw new Error(
@@ -1646,7 +1656,7 @@ getInitDeps_fn = async function(deps, pluginId, moduleId) {
1646
1656
  }
1647
1657
  result.set(name, ep.impl);
1648
1658
  } else {
1649
- const impl = await __privateGet$3(this, _serviceRegistry).get(
1659
+ const impl = await __privateGet$5(this, _serviceRegistry).get(
1650
1660
  ref,
1651
1661
  pluginId
1652
1662
  );
@@ -1673,14 +1683,14 @@ addFeature_fn = function(feature) {
1673
1683
  );
1674
1684
  }
1675
1685
  if (isServiceFactory(feature)) {
1676
- __privateGet$3(this, _serviceRegistry).add(feature);
1686
+ __privateGet$5(this, _serviceRegistry).add(feature);
1677
1687
  } else if (isInternalBackendFeature(feature)) {
1678
1688
  if (feature.version !== "v1") {
1679
1689
  throw new Error(
1680
1690
  `Failed to add feature, invalid version '${feature.version}'`
1681
1691
  );
1682
1692
  }
1683
- __privateGet$3(this, _features).push(feature);
1693
+ __privateGet$5(this, _features).push(feature);
1684
1694
  } else {
1685
1695
  throw new Error(
1686
1696
  `Failed to add feature, invalid feature ${JSON.stringify(feature)}`
@@ -1689,39 +1699,35 @@ addFeature_fn = function(feature) {
1689
1699
  };
1690
1700
  _doStart = new WeakSet();
1691
1701
  doStart_fn = async function() {
1692
- __privateGet$3(this, _serviceRegistry).checkForCircularDeps();
1693
- for (const feature of __privateGet$3(this, _registeredFeatures)) {
1694
- __privateMethod$3(this, _addFeature, addFeature_fn).call(this, await feature);
1702
+ __privateGet$5(this, _serviceRegistry).checkForCircularDeps();
1703
+ for (const feature of __privateGet$5(this, _registeredFeatures)) {
1704
+ __privateMethod$4(this, _addFeature, addFeature_fn).call(this, await feature);
1695
1705
  }
1696
- const featureDiscovery = await __privateGet$3(this, _serviceRegistry).get(
1706
+ const featureDiscovery = await __privateGet$5(this, _serviceRegistry).get(
1697
1707
  alpha.featureDiscoveryServiceRef,
1698
1708
  "root"
1699
1709
  );
1700
1710
  if (featureDiscovery) {
1701
1711
  const { features } = await featureDiscovery.getBackendFeatures();
1702
1712
  for (const feature of features) {
1703
- __privateMethod$3(this, _addFeature, addFeature_fn).call(this, feature);
1704
- }
1705
- __privateGet$3(this, _serviceRegistry).checkForCircularDeps();
1706
- }
1707
- for (const ref of __privateGet$3(this, _serviceRegistry).getServiceRefs()) {
1708
- if (ref.scope === "root") {
1709
- await __privateGet$3(this, _serviceRegistry).get(ref, "root");
1713
+ __privateMethod$4(this, _addFeature, addFeature_fn).call(this, feature);
1710
1714
  }
1715
+ __privateGet$5(this, _serviceRegistry).checkForCircularDeps();
1711
1716
  }
1717
+ await __privateGet$5(this, _serviceRegistry).initializeEagerServicesWithScope("root");
1712
1718
  const pluginInits = /* @__PURE__ */ new Map();
1713
1719
  const moduleInits = /* @__PURE__ */ new Map();
1714
- for (const feature of __privateGet$3(this, _features)) {
1720
+ for (const feature of __privateGet$5(this, _features)) {
1715
1721
  for (const r of feature.getRegistrations()) {
1716
1722
  const provides = /* @__PURE__ */ new Set();
1717
1723
  if (r.type === "plugin" || r.type === "module") {
1718
1724
  for (const [extRef, extImpl] of r.extensionPoints) {
1719
- if (__privateGet$3(this, _extensionPoints).has(extRef.id)) {
1725
+ if (__privateGet$5(this, _extensionPoints).has(extRef.id)) {
1720
1726
  throw new Error(
1721
1727
  `ExtensionPoint with ID '${extRef.id}' is already registered`
1722
1728
  );
1723
1729
  }
1724
- __privateGet$3(this, _extensionPoints).set(extRef.id, {
1730
+ __privateGet$5(this, _extensionPoints).set(extRef.id, {
1725
1731
  impl: extImpl,
1726
1732
  pluginId: r.pluginId
1727
1733
  });
@@ -1756,11 +1762,13 @@ doStart_fn = async function() {
1756
1762
  }
1757
1763
  }
1758
1764
  }
1759
- const allPluginIds = [
1760
- .../* @__PURE__ */ new Set([...pluginInits.keys(), ...moduleInits.keys()])
1761
- ];
1765
+ const allPluginIds = [...pluginInits.keys()];
1762
1766
  await Promise.all(
1763
1767
  allPluginIds.map(async (pluginId) => {
1768
+ await __privateGet$5(this, _serviceRegistry).initializeEagerServicesWithScope(
1769
+ "plugin",
1770
+ pluginId
1771
+ );
1764
1772
  const modules = moduleInits.get(pluginId);
1765
1773
  if (modules) {
1766
1774
  const tree = DependencyGraph.fromIterable(
@@ -1781,7 +1789,7 @@ doStart_fn = async function() {
1781
1789
  }
1782
1790
  await tree.parallelTopologicalTraversal(
1783
1791
  async ({ moduleId, moduleInit }) => {
1784
- const moduleDeps = await __privateMethod$3(this, _getInitDeps, getInitDeps_fn).call(this, moduleInit.init.deps, pluginId, moduleId);
1792
+ const moduleDeps = await __privateMethod$4(this, _getInitDeps, getInitDeps_fn).call(this, moduleInit.init.deps, pluginId, moduleId);
1785
1793
  await moduleInit.init.func(moduleDeps).catch((error) => {
1786
1794
  throw new errors.ForwardedError(
1787
1795
  `Module '${moduleId}' for plugin '${pluginId}' startup failed`,
@@ -1793,7 +1801,7 @@ doStart_fn = async function() {
1793
1801
  }
1794
1802
  const pluginInit = pluginInits.get(pluginId);
1795
1803
  if (pluginInit) {
1796
- const pluginDeps = await __privateMethod$3(this, _getInitDeps, getInitDeps_fn).call(this, pluginInit.init.deps, pluginId);
1804
+ const pluginDeps = await __privateMethod$4(this, _getInitDeps, getInitDeps_fn).call(this, pluginInit.init.deps, pluginId);
1797
1805
  await pluginInit.init.func(pluginDeps).catch((error) => {
1798
1806
  throw new errors.ForwardedError(
1799
1807
  `Plugin '${pluginId}' startup failed`,
@@ -1801,14 +1809,14 @@ doStart_fn = async function() {
1801
1809
  );
1802
1810
  });
1803
1811
  }
1804
- const lifecycleService2 = await __privateMethod$3(this, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn).call(this, pluginId);
1812
+ const lifecycleService2 = await __privateMethod$4(this, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn).call(this, pluginId);
1805
1813
  await lifecycleService2.startup();
1806
1814
  })
1807
1815
  );
1808
- const lifecycleService = await __privateMethod$3(this, _getRootLifecycleImpl, getRootLifecycleImpl_fn).call(this);
1816
+ const lifecycleService = await __privateMethod$4(this, _getRootLifecycleImpl, getRootLifecycleImpl_fn).call(this);
1809
1817
  await lifecycleService.startup();
1810
1818
  if (process.env.NODE_ENV !== "test") {
1811
- const rootLogger = await __privateGet$3(this, _serviceRegistry).get(
1819
+ const rootLogger = await __privateGet$5(this, _serviceRegistry).get(
1812
1820
  backendPluginApi.coreServices.rootLogger,
1813
1821
  "root"
1814
1822
  );
@@ -1824,7 +1832,7 @@ doStart_fn = async function() {
1824
1832
  };
1825
1833
  _getRootLifecycleImpl = new WeakSet();
1826
1834
  getRootLifecycleImpl_fn = async function() {
1827
- const lifecycleService = await __privateGet$3(this, _serviceRegistry).get(
1835
+ const lifecycleService = await __privateGet$5(this, _serviceRegistry).get(
1828
1836
  backendPluginApi.coreServices.rootLifecycle,
1829
1837
  "root"
1830
1838
  );
@@ -1835,7 +1843,7 @@ getRootLifecycleImpl_fn = async function() {
1835
1843
  };
1836
1844
  _getPluginLifecycleImpl = new WeakSet();
1837
1845
  getPluginLifecycleImpl_fn = async function(pluginId) {
1838
- const lifecycleService = await __privateGet$3(this, _serviceRegistry).get(
1846
+ const lifecycleService = await __privateGet$5(this, _serviceRegistry).get(
1839
1847
  backendPluginApi.coreServices.lifecycle,
1840
1848
  pluginId
1841
1849
  );
@@ -1851,42 +1859,42 @@ function isInternalBackendFeature(feature) {
1851
1859
  return typeof feature.getRegistrations === "function";
1852
1860
  }
1853
1861
 
1854
- var __accessCheck$3 = (obj, member, msg) => {
1862
+ var __accessCheck$5 = (obj, member, msg) => {
1855
1863
  if (!member.has(obj))
1856
1864
  throw TypeError("Cannot " + msg);
1857
1865
  };
1858
- var __privateGet$2 = (obj, member, getter) => {
1859
- __accessCheck$3(obj, member, "read from private field");
1866
+ var __privateGet$4 = (obj, member, getter) => {
1867
+ __accessCheck$5(obj, member, "read from private field");
1860
1868
  return getter ? getter.call(obj) : member.get(obj);
1861
1869
  };
1862
- var __privateAdd$3 = (obj, member, value) => {
1870
+ var __privateAdd$5 = (obj, member, value) => {
1863
1871
  if (member.has(obj))
1864
1872
  throw TypeError("Cannot add the same private member more than once");
1865
1873
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1866
1874
  };
1867
- var __privateSet$2 = (obj, member, value, setter) => {
1868
- __accessCheck$3(obj, member, "write to private field");
1875
+ var __privateSet$4 = (obj, member, value, setter) => {
1876
+ __accessCheck$5(obj, member, "write to private field");
1869
1877
  setter ? setter.call(obj, value) : member.set(obj, value);
1870
1878
  return value;
1871
1879
  };
1872
1880
  var _initializer;
1873
1881
  class BackstageBackend {
1874
1882
  constructor(defaultServiceFactories) {
1875
- __privateAdd$3(this, _initializer, void 0);
1876
- __privateSet$2(this, _initializer, new BackendInitializer(defaultServiceFactories));
1883
+ __privateAdd$5(this, _initializer, void 0);
1884
+ __privateSet$4(this, _initializer, new BackendInitializer(defaultServiceFactories));
1877
1885
  }
1878
1886
  add(feature) {
1879
1887
  if (isPromise(feature)) {
1880
- __privateGet$2(this, _initializer).add(feature.then((f) => unwrapFeature(f.default)));
1888
+ __privateGet$4(this, _initializer).add(feature.then((f) => unwrapFeature(f.default)));
1881
1889
  } else {
1882
- __privateGet$2(this, _initializer).add(unwrapFeature(feature));
1890
+ __privateGet$4(this, _initializer).add(unwrapFeature(feature));
1883
1891
  }
1884
1892
  }
1885
1893
  async start() {
1886
- await __privateGet$2(this, _initializer).start();
1894
+ await __privateGet$4(this, _initializer).start();
1887
1895
  }
1888
1896
  async stop() {
1889
- await __privateGet$2(this, _initializer).stop();
1897
+ await __privateGet$4(this, _initializer).stop();
1890
1898
  }
1891
1899
  }
1892
1900
  _initializer = new WeakMap();
@@ -1932,6 +1940,450 @@ function createSpecializedBackend(options) {
1932
1940
  return new BackstageBackend(services);
1933
1941
  }
1934
1942
 
1943
+ var __accessCheck$4 = (obj, member, msg) => {
1944
+ if (!member.has(obj))
1945
+ throw TypeError("Cannot " + msg);
1946
+ };
1947
+ var __privateGet$3 = (obj, member, getter) => {
1948
+ __accessCheck$4(obj, member, "read from private field");
1949
+ return getter ? getter.call(obj) : member.get(obj);
1950
+ };
1951
+ var __privateAdd$4 = (obj, member, value) => {
1952
+ if (member.has(obj))
1953
+ throw TypeError("Cannot add the same private member more than once");
1954
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1955
+ };
1956
+ var __privateSet$3 = (obj, member, value, setter) => {
1957
+ __accessCheck$4(obj, member, "write to private field");
1958
+ setter ? setter.call(obj, value) : member.set(obj, value);
1959
+ return value;
1960
+ };
1961
+ var _keyStore, _keyStoreUpdated;
1962
+ const CLOCK_MARGIN_S = 10;
1963
+ class JwksClient {
1964
+ constructor(getEndpoint) {
1965
+ this.getEndpoint = getEndpoint;
1966
+ __privateAdd$4(this, _keyStore, void 0);
1967
+ __privateAdd$4(this, _keyStoreUpdated, 0);
1968
+ }
1969
+ get getKey() {
1970
+ if (!__privateGet$3(this, _keyStore)) {
1971
+ throw new errors.AuthenticationError(
1972
+ "refreshKeyStore must be called before jwksClient.getKey"
1973
+ );
1974
+ }
1975
+ return __privateGet$3(this, _keyStore);
1976
+ }
1977
+ /**
1978
+ * If the last keystore refresh is stale, update the keystore URL to the latest
1979
+ */
1980
+ async refreshKeyStore(rawJwtToken) {
1981
+ const payload = await jose.decodeJwt(rawJwtToken);
1982
+ const header = await jose.decodeProtectedHeader(rawJwtToken);
1983
+ let keyStoreHasKey;
1984
+ try {
1985
+ if (__privateGet$3(this, _keyStore)) {
1986
+ const [_, rawPayload, rawSignature] = rawJwtToken.split(".");
1987
+ keyStoreHasKey = await __privateGet$3(this, _keyStore).call(this, header, {
1988
+ payload: rawPayload,
1989
+ signature: rawSignature
1990
+ });
1991
+ }
1992
+ } catch (error) {
1993
+ keyStoreHasKey = false;
1994
+ }
1995
+ const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > __privateGet$3(this, _keyStoreUpdated) - CLOCK_MARGIN_S;
1996
+ if (!__privateGet$3(this, _keyStore) || !keyStoreHasKey && issuedAfterLastRefresh) {
1997
+ const endpoint = await this.getEndpoint();
1998
+ __privateSet$3(this, _keyStore, jose.createRemoteJWKSet(endpoint));
1999
+ __privateSet$3(this, _keyStoreUpdated, Date.now() / 1e3);
2000
+ }
2001
+ }
2002
+ }
2003
+ _keyStore = new WeakMap();
2004
+ _keyStoreUpdated = new WeakMap();
2005
+
2006
+ var __accessCheck$3 = (obj, member, msg) => {
2007
+ if (!member.has(obj))
2008
+ throw TypeError("Cannot " + msg);
2009
+ };
2010
+ var __privateGet$2 = (obj, member, getter) => {
2011
+ __accessCheck$3(obj, member, "read from private field");
2012
+ return getter ? getter.call(obj) : member.get(obj);
2013
+ };
2014
+ var __privateAdd$3 = (obj, member, value) => {
2015
+ if (member.has(obj))
2016
+ throw TypeError("Cannot add the same private member more than once");
2017
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
2018
+ };
2019
+ var __privateSet$2 = (obj, member, value, setter) => {
2020
+ __accessCheck$3(obj, member, "write to private field");
2021
+ setter ? setter.call(obj, value) : member.set(obj, value);
2022
+ return value;
2023
+ };
2024
+ var __privateMethod$3 = (obj, member, method) => {
2025
+ __accessCheck$3(obj, member, "access private method");
2026
+ return method;
2027
+ };
2028
+ var _jwksClient, _algorithms, _getTokenVerificationOptions, getTokenVerificationOptions_fn;
2029
+ class UserTokenHandler {
2030
+ constructor(options) {
2031
+ __privateAdd$3(this, _getTokenVerificationOptions);
2032
+ __privateAdd$3(this, _jwksClient, void 0);
2033
+ __privateAdd$3(this, _algorithms, void 0);
2034
+ __privateSet$2(this, _algorithms, ["ES256"]);
2035
+ __privateSet$2(this, _jwksClient, new JwksClient(async () => {
2036
+ const url = await options.discovery.getBaseUrl("auth");
2037
+ return new URL(`${url}/.well-known/jwks.json`);
2038
+ }));
2039
+ }
2040
+ async verifyToken(token) {
2041
+ const verifyOpts = __privateMethod$3(this, _getTokenVerificationOptions, getTokenVerificationOptions_fn).call(this, token);
2042
+ if (!verifyOpts) {
2043
+ return void 0;
2044
+ }
2045
+ await __privateGet$2(this, _jwksClient).refreshKeyStore(token);
2046
+ const { payload } = await jose.jwtVerify(
2047
+ token,
2048
+ __privateGet$2(this, _jwksClient).getKey,
2049
+ verifyOpts
2050
+ ).catch((e) => {
2051
+ throw new errors.AuthenticationError("Invalid token", e);
2052
+ });
2053
+ const userEntityRef = payload.sub;
2054
+ if (!userEntityRef) {
2055
+ throw new errors.AuthenticationError("No user sub found in token");
2056
+ }
2057
+ return { userEntityRef };
2058
+ }
2059
+ createLimitedUserToken(backstageToken) {
2060
+ const [headerRaw, payloadRaw] = backstageToken.split(".");
2061
+ const header = JSON.parse(
2062
+ new TextDecoder().decode(jose.base64url.decode(headerRaw))
2063
+ );
2064
+ const payload = JSON.parse(
2065
+ new TextDecoder().decode(jose.base64url.decode(payloadRaw))
2066
+ );
2067
+ const tokenType = header.typ;
2068
+ if (!tokenType || tokenType === pluginAuthNode.tokenTypes.limitedUser.typParam) {
2069
+ return { token: backstageToken, expiresAt: new Date(payload.exp * 1e3) };
2070
+ }
2071
+ if (tokenType !== pluginAuthNode.tokenTypes.user.typParam) {
2072
+ throw new errors.AuthenticationError(
2073
+ "Failed to create limited user token, invalid token type"
2074
+ );
2075
+ }
2076
+ const limitedUserToken = [
2077
+ jose.base64url.encode(
2078
+ JSON.stringify({
2079
+ typ: pluginAuthNode.tokenTypes.limitedUser.typParam,
2080
+ alg: header.alg,
2081
+ kid: header.kid
2082
+ })
2083
+ ),
2084
+ jose.base64url.encode(
2085
+ JSON.stringify({
2086
+ sub: payload.sub,
2087
+ ent: payload.ent,
2088
+ iat: payload.iat,
2089
+ exp: payload.exp
2090
+ })
2091
+ ),
2092
+ payload.uip
2093
+ ].join(".");
2094
+ return { token: limitedUserToken, expiresAt: new Date(payload.exp * 1e3) };
2095
+ }
2096
+ isLimitedUserToken(token) {
2097
+ try {
2098
+ const { typ } = jose.decodeProtectedHeader(token);
2099
+ return typ === pluginAuthNode.tokenTypes.limitedUser.typParam;
2100
+ } catch {
2101
+ return false;
2102
+ }
2103
+ }
2104
+ }
2105
+ _jwksClient = new WeakMap();
2106
+ _algorithms = new WeakMap();
2107
+ _getTokenVerificationOptions = new WeakSet();
2108
+ getTokenVerificationOptions_fn = function(token) {
2109
+ try {
2110
+ const { typ } = jose.decodeProtectedHeader(token);
2111
+ if (typ === pluginAuthNode.tokenTypes.user.typParam) {
2112
+ return {
2113
+ algorithms: __privateGet$2(this, _algorithms),
2114
+ requiredClaims: ["iat", "exp", "sub"],
2115
+ typ: pluginAuthNode.tokenTypes.user.typParam
2116
+ };
2117
+ }
2118
+ if (typ === pluginAuthNode.tokenTypes.limitedUser.typParam) {
2119
+ return {
2120
+ algorithms: __privateGet$2(this, _algorithms),
2121
+ requiredClaims: ["iat", "exp", "sub"],
2122
+ typ: pluginAuthNode.tokenTypes.limitedUser.typParam
2123
+ };
2124
+ }
2125
+ const { aud } = jose.decodeJwt(token);
2126
+ if (aud === pluginAuthNode.tokenTypes.user.audClaim) {
2127
+ return {
2128
+ algorithms: __privateGet$2(this, _algorithms),
2129
+ audience: pluginAuthNode.tokenTypes.user.audClaim
2130
+ };
2131
+ }
2132
+ } catch {
2133
+ }
2134
+ return void 0;
2135
+ };
2136
+
2137
+ var __defProp = Object.defineProperty;
2138
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2139
+ var __publicField = (obj, key, value) => {
2140
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
2141
+ return value;
2142
+ };
2143
+ const KEY_EXPIRATION_MARGIN_FACTOR = 3;
2144
+ const SECONDS_IN_MS = 1e3;
2145
+ const ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;
2146
+ class PluginTokenHandler {
2147
+ constructor(logger, ownPluginId, publicKeyStore, keyDurationSeconds, algorithm, discovery) {
2148
+ this.logger = logger;
2149
+ this.ownPluginId = ownPluginId;
2150
+ this.publicKeyStore = publicKeyStore;
2151
+ this.keyDurationSeconds = keyDurationSeconds;
2152
+ this.algorithm = algorithm;
2153
+ this.discovery = discovery;
2154
+ __publicField(this, "privateKeyPromise");
2155
+ __publicField(this, "keyExpiry");
2156
+ __publicField(this, "jwksMap", /* @__PURE__ */ new Map());
2157
+ // Tracking state for isTargetPluginSupported
2158
+ __publicField(this, "supportedTargetPlugins", /* @__PURE__ */ new Set());
2159
+ __publicField(this, "targetPluginInflightChecks", /* @__PURE__ */ new Map());
2160
+ }
2161
+ static create(options) {
2162
+ var _a;
2163
+ return new PluginTokenHandler(
2164
+ options.logger,
2165
+ options.ownPluginId,
2166
+ options.publicKeyStore,
2167
+ options.keyDurationSeconds,
2168
+ (_a = options.algorithm) != null ? _a : "ES256",
2169
+ options.discovery
2170
+ );
2171
+ }
2172
+ async verifyToken(token) {
2173
+ try {
2174
+ const { typ } = jose.decodeProtectedHeader(token);
2175
+ if (typ !== pluginAuthNode.tokenTypes.plugin.typParam) {
2176
+ return void 0;
2177
+ }
2178
+ } catch {
2179
+ return void 0;
2180
+ }
2181
+ const pluginId = String(jose.decodeJwt(token).sub);
2182
+ if (!pluginId) {
2183
+ throw new errors.AuthenticationError("Invalid plugin token: missing subject");
2184
+ }
2185
+ if (!ALLOWED_PLUGIN_ID_PATTERN.test(pluginId)) {
2186
+ throw new errors.AuthenticationError(
2187
+ "Invalid plugin token: forbidden subject format"
2188
+ );
2189
+ }
2190
+ const jwksClient = await this.getJwksClient(pluginId);
2191
+ await jwksClient.refreshKeyStore(token);
2192
+ const { payload } = await jose.jwtVerify(
2193
+ token,
2194
+ jwksClient.getKey,
2195
+ {
2196
+ typ: pluginAuthNode.tokenTypes.plugin.typParam,
2197
+ audience: this.ownPluginId,
2198
+ requiredClaims: ["iat", "exp", "sub", "aud"]
2199
+ }
2200
+ ).catch((e) => {
2201
+ throw new errors.AuthenticationError("Invalid plugin token", e);
2202
+ });
2203
+ return { subject: `plugin:${payload.sub}`, limitedUserToken: payload.obo };
2204
+ }
2205
+ async issueToken(options) {
2206
+ const { pluginId, targetPluginId, onBehalfOf } = options;
2207
+ const key = await this.getKey();
2208
+ const sub = pluginId;
2209
+ const aud = targetPluginId;
2210
+ const iat = Math.floor(Date.now() / SECONDS_IN_MS);
2211
+ const ourExp = iat + this.keyDurationSeconds;
2212
+ const exp = onBehalfOf ? Math.min(
2213
+ ourExp,
2214
+ Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS)
2215
+ ) : ourExp;
2216
+ const claims = { sub, aud, iat, exp, obo: onBehalfOf == null ? void 0 : onBehalfOf.token };
2217
+ const token = await new jose.SignJWT(claims).setProtectedHeader({
2218
+ typ: pluginAuthNode.tokenTypes.plugin.typParam,
2219
+ alg: this.algorithm,
2220
+ kid: key.kid
2221
+ }).setAudience(aud).setSubject(sub).setIssuedAt(iat).setExpirationTime(exp).sign(await jose.importJWK(key));
2222
+ return { token };
2223
+ }
2224
+ async isTargetPluginSupported(targetPluginId) {
2225
+ if (this.supportedTargetPlugins.has(targetPluginId)) {
2226
+ return true;
2227
+ }
2228
+ const inFlight = this.targetPluginInflightChecks.get(targetPluginId);
2229
+ if (inFlight) {
2230
+ return inFlight;
2231
+ }
2232
+ const doCheck = async () => {
2233
+ try {
2234
+ const res = await fetch(
2235
+ `${await this.discovery.getBaseUrl(
2236
+ targetPluginId
2237
+ )}/.backstage/auth/v1/jwks.json`
2238
+ );
2239
+ if (res.status === 404) {
2240
+ return false;
2241
+ }
2242
+ if (!res.ok) {
2243
+ throw new Error(`Failed to fetch jwks.json, ${res.status}`);
2244
+ }
2245
+ const data = await res.json();
2246
+ if (!data.keys) {
2247
+ throw new Error(`Invalid jwks.json response, missing keys`);
2248
+ }
2249
+ this.supportedTargetPlugins.add(targetPluginId);
2250
+ return true;
2251
+ } catch (error) {
2252
+ this.logger.error("Unexpected failure for target JWKS check", error);
2253
+ return false;
2254
+ } finally {
2255
+ this.targetPluginInflightChecks.delete(targetPluginId);
2256
+ }
2257
+ };
2258
+ const check = doCheck();
2259
+ this.targetPluginInflightChecks.set(targetPluginId, check);
2260
+ return check;
2261
+ }
2262
+ async getJwksClient(pluginId) {
2263
+ const client = this.jwksMap.get(pluginId);
2264
+ if (client) {
2265
+ return client;
2266
+ }
2267
+ if (!await this.isTargetPluginSupported(pluginId)) {
2268
+ throw new errors.AuthenticationError(
2269
+ `Received a plugin token where the source '${pluginId}' plugin unexpectedly does not have a JWKS endpoint`
2270
+ );
2271
+ }
2272
+ const newClient = new JwksClient(async () => {
2273
+ return new URL(
2274
+ `${await this.discovery.getBaseUrl(
2275
+ pluginId
2276
+ )}/.backstage/auth/v1/jwks.json`
2277
+ );
2278
+ });
2279
+ this.jwksMap.set(pluginId, newClient);
2280
+ return newClient;
2281
+ }
2282
+ async getKey() {
2283
+ if (this.privateKeyPromise) {
2284
+ if (this.keyExpiry && this.keyExpiry.getTime() > Date.now()) {
2285
+ return this.privateKeyPromise;
2286
+ }
2287
+ this.logger.info(`Signing key has expired, generating new key`);
2288
+ delete this.privateKeyPromise;
2289
+ }
2290
+ this.keyExpiry = new Date(
2291
+ Date.now() + this.keyDurationSeconds * SECONDS_IN_MS
2292
+ );
2293
+ const promise = (async () => {
2294
+ const kid = uuid.v4();
2295
+ const key = await jose.generateKeyPair(this.algorithm);
2296
+ const publicKey = await jose.exportJWK(key.publicKey);
2297
+ const privateKey = await jose.exportJWK(key.privateKey);
2298
+ publicKey.kid = privateKey.kid = kid;
2299
+ publicKey.alg = privateKey.alg = this.algorithm;
2300
+ this.logger.info(`Created new signing key ${kid}`);
2301
+ await this.publicKeyStore.addKey({
2302
+ id: kid,
2303
+ key: publicKey,
2304
+ expiresAt: new Date(
2305
+ Date.now() + this.keyDurationSeconds * SECONDS_IN_MS * KEY_EXPIRATION_MARGIN_FACTOR
2306
+ )
2307
+ });
2308
+ return privateKey;
2309
+ })();
2310
+ this.privateKeyPromise = promise;
2311
+ try {
2312
+ await promise;
2313
+ } catch (error) {
2314
+ this.logger.error(`Failed to generate new signing key, ${error}`);
2315
+ delete this.keyExpiry;
2316
+ delete this.privateKeyPromise;
2317
+ }
2318
+ return promise;
2319
+ }
2320
+ }
2321
+
2322
+ const MIGRATIONS_TABLE = "backstage_backend_public_keys__knex_migrations";
2323
+ const TABLE = "backstage_backend_public_keys__keys";
2324
+ function applyDatabaseMigrations(knex) {
2325
+ const migrationsDir = backendCommon.resolvePackagePath(
2326
+ "@backstage/backend-app-api",
2327
+ "migrations"
2328
+ );
2329
+ return knex.migrate.latest({
2330
+ directory: migrationsDir,
2331
+ tableName: MIGRATIONS_TABLE
2332
+ });
2333
+ }
2334
+ class DatabaseKeyStore {
2335
+ constructor(client, logger) {
2336
+ this.client = client;
2337
+ this.logger = logger;
2338
+ }
2339
+ static async create(options) {
2340
+ var _a;
2341
+ const { database, logger } = options;
2342
+ const client = await database.getClient();
2343
+ if (!((_a = database.migrations) == null ? void 0 : _a.skip)) {
2344
+ await applyDatabaseMigrations(client);
2345
+ }
2346
+ return new DatabaseKeyStore(client, logger);
2347
+ }
2348
+ async addKey(options) {
2349
+ await this.client(TABLE).insert({
2350
+ id: options.key.kid,
2351
+ key: JSON.stringify(options.key),
2352
+ expires_at: options.expiresAt.toISOString()
2353
+ });
2354
+ }
2355
+ async listKeys() {
2356
+ const rows = await this.client(TABLE).select();
2357
+ const keys = rows.map((row) => ({
2358
+ id: row.id,
2359
+ key: JSON.parse(row.key),
2360
+ expiresAt: new Date(row.expires_at)
2361
+ }));
2362
+ const validKeys = [];
2363
+ const expiredKeys = [];
2364
+ for (const key of keys) {
2365
+ if (luxon.DateTime.fromJSDate(key.expiresAt) < luxon.DateTime.local()) {
2366
+ expiredKeys.push(key);
2367
+ } else {
2368
+ validKeys.push(key);
2369
+ }
2370
+ }
2371
+ if (expiredKeys.length > 0) {
2372
+ const kids = expiredKeys.map(({ key }) => key.kid);
2373
+ this.logger.info(
2374
+ `Removing expired plugin service keys, '${kids.join("', '")}'`
2375
+ );
2376
+ this.client(TABLE).delete().whereIn("id", kids).catch((error) => {
2377
+ this.logger.error(
2378
+ "Failed to remove expired plugin service keys",
2379
+ error
2380
+ );
2381
+ });
2382
+ }
2383
+ return { keys: validKeys };
2384
+ }
2385
+ }
2386
+
1935
2387
  var __accessCheck$2 = (obj, member, msg) => {
1936
2388
  if (!member.has(obj))
1937
2389
  throw TypeError("Cannot " + msg);
@@ -1991,33 +2443,50 @@ function toInternalBackstageCredentials(credentials) {
1991
2443
  return internalCredentials;
1992
2444
  }
1993
2445
  class DefaultAuthService {
1994
- constructor(tokenManager, identity, pluginId, disableDefaultAuthPolicy) {
2446
+ constructor(tokenManager, userTokenHandler, pluginId, disableDefaultAuthPolicy, publicKeyStore, pluginTokenHandler) {
1995
2447
  this.tokenManager = tokenManager;
1996
- this.identity = identity;
2448
+ this.userTokenHandler = userTokenHandler;
1997
2449
  this.pluginId = pluginId;
1998
2450
  this.disableDefaultAuthPolicy = disableDefaultAuthPolicy;
2451
+ this.publicKeyStore = publicKeyStore;
2452
+ this.pluginTokenHandler = pluginTokenHandler;
1999
2453
  __privateAdd$2(this, _getJwtExpiration);
2000
2454
  }
2001
2455
  // allowLimitedAccess is currently ignored, since we currently always use the full user tokens
2002
2456
  async authenticate(token) {
2457
+ const pluginResult = await this.pluginTokenHandler.verifyToken(token);
2458
+ if (pluginResult) {
2459
+ if (pluginResult.limitedUserToken) {
2460
+ const userResult2 = await this.userTokenHandler.verifyToken(
2461
+ pluginResult.limitedUserToken
2462
+ );
2463
+ if (!userResult2) {
2464
+ throw new errors.AuthenticationError(
2465
+ "Invalid user token in plugin token obo claim"
2466
+ );
2467
+ }
2468
+ return createCredentialsWithUserPrincipal(
2469
+ userResult2.userEntityRef,
2470
+ pluginResult.limitedUserToken,
2471
+ __privateMethod$2(this, _getJwtExpiration, getJwtExpiration_fn).call(this, pluginResult.limitedUserToken)
2472
+ );
2473
+ }
2474
+ return createCredentialsWithServicePrincipal(pluginResult.subject);
2475
+ }
2476
+ const userResult = await this.userTokenHandler.verifyToken(token);
2477
+ if (userResult) {
2478
+ return createCredentialsWithUserPrincipal(
2479
+ userResult.userEntityRef,
2480
+ token,
2481
+ __privateMethod$2(this, _getJwtExpiration, getJwtExpiration_fn).call(this, token)
2482
+ );
2483
+ }
2003
2484
  const { sub, aud } = jose.decodeJwt(token);
2004
2485
  if (sub === "backstage-server" && !aud) {
2005
2486
  await this.tokenManager.authenticate(token);
2006
2487
  return createCredentialsWithServicePrincipal("external:backstage-plugin");
2007
2488
  }
2008
- const identity = await this.identity.getIdentity({
2009
- request: {
2010
- headers: { authorization: `Bearer ${token}` }
2011
- }
2012
- });
2013
- if (!identity) {
2014
- throw new errors.AuthenticationError("Invalid user token");
2015
- }
2016
- return createCredentialsWithUserPrincipal(
2017
- identity.identity.userEntityRef,
2018
- token,
2019
- __privateMethod$2(this, _getJwtExpiration, getJwtExpiration_fn).call(this, token)
2020
- );
2489
+ throw new errors.AuthenticationError("Unknown token");
2021
2490
  }
2022
2491
  isPrincipal(credentials, type) {
2023
2492
  const principal = credentials.principal;
@@ -2036,19 +2505,44 @@ class DefaultAuthService {
2036
2505
  return createCredentialsWithServicePrincipal(`plugin:${this.pluginId}`);
2037
2506
  }
2038
2507
  async getPluginRequestToken(options) {
2508
+ const { targetPluginId } = options;
2039
2509
  const internalForward = toInternalBackstageCredentials(options.onBehalfOf);
2040
2510
  const { type } = internalForward.principal;
2041
2511
  if (type === "none" && this.disableDefaultAuthPolicy) {
2042
2512
  return { token: "" };
2043
2513
  }
2514
+ const targetSupportsNewAuth = await this.pluginTokenHandler.isTargetPluginSupported(targetPluginId);
2044
2515
  switch (type) {
2045
2516
  case "service":
2517
+ if (targetSupportsNewAuth) {
2518
+ return this.pluginTokenHandler.issueToken({
2519
+ pluginId: this.pluginId,
2520
+ targetPluginId
2521
+ });
2522
+ }
2046
2523
  return this.tokenManager.getToken();
2047
- case "user":
2048
- if (!internalForward.token) {
2524
+ case "user": {
2525
+ const { token } = internalForward;
2526
+ if (!token) {
2049
2527
  throw new Error("User credentials is unexpectedly missing token");
2050
2528
  }
2051
- return { token: internalForward.token };
2529
+ if (targetSupportsNewAuth) {
2530
+ const onBehalfOf = await this.userTokenHandler.createLimitedUserToken(
2531
+ token
2532
+ );
2533
+ return this.pluginTokenHandler.issueToken({
2534
+ pluginId: this.pluginId,
2535
+ targetPluginId,
2536
+ onBehalfOf
2537
+ });
2538
+ }
2539
+ if (this.userTokenHandler.isLimitedUserToken(token)) {
2540
+ throw new errors.AuthenticationError(
2541
+ `Unable to call '${targetPluginId}' plugin on behalf of user, because the target plugin does not support on-behalf-of tokens`
2542
+ );
2543
+ }
2544
+ return { token };
2545
+ }
2052
2546
  default:
2053
2547
  throw new errors.AuthenticationError(
2054
2548
  `Refused to issue service token for credential type '${type}'`
@@ -2056,14 +2550,17 @@ class DefaultAuthService {
2056
2550
  }
2057
2551
  }
2058
2552
  async getLimitedUserToken(credentials) {
2059
- const internalCredentials = toInternalBackstageCredentials(credentials);
2060
- const { token } = internalCredentials;
2061
- if (!token) {
2553
+ const { token: backstageToken } = toInternalBackstageCredentials(credentials);
2554
+ if (!backstageToken) {
2062
2555
  throw new errors.AuthenticationError(
2063
2556
  "User credentials is unexpectedly missing token"
2064
2557
  );
2065
2558
  }
2066
- return { token, expiresAt: __privateMethod$2(this, _getJwtExpiration, getJwtExpiration_fn).call(this, token) };
2559
+ return this.userTokenHandler.createLimitedUserToken(backstageToken);
2560
+ }
2561
+ async listPublicServiceKeys() {
2562
+ const { keys } = await this.publicKeyStore.listKeys();
2563
+ return { keys: keys.map(({ key }) => key) };
2067
2564
  }
2068
2565
  }
2069
2566
  _getJwtExpiration = new WeakSet();
@@ -2079,25 +2576,35 @@ const authServiceFactory = backendPluginApi.createServiceFactory({
2079
2576
  deps: {
2080
2577
  config: backendPluginApi.coreServices.rootConfig,
2081
2578
  logger: backendPluginApi.coreServices.rootLogger,
2579
+ discovery: backendPluginApi.coreServices.discovery,
2082
2580
  plugin: backendPluginApi.coreServices.pluginMetadata,
2083
- identity: backendPluginApi.coreServices.identity,
2581
+ database: backendPluginApi.coreServices.database,
2084
2582
  // Re-using the token manager makes sure that we use the same generated keys for
2085
2583
  // development as plugins that have not yet been migrated. It's important that this
2086
2584
  // keeps working as long as there are plugins that have not been migrated to the
2087
2585
  // new auth services in the new backend system.
2088
2586
  tokenManager: backendPluginApi.coreServices.tokenManager
2089
2587
  },
2090
- async factory({ config, plugin, identity, tokenManager }) {
2588
+ async factory({ config, discovery, plugin, tokenManager, logger, database }) {
2091
2589
  const disableDefaultAuthPolicy = Boolean(
2092
2590
  config.getOptionalBoolean(
2093
2591
  "backend.auth.dangerouslyDisableDefaultAuthPolicy"
2094
2592
  )
2095
2593
  );
2594
+ const publicKeyStore = await DatabaseKeyStore.create({ database, logger });
2096
2595
  return new DefaultAuthService(
2097
2596
  tokenManager,
2098
- identity,
2597
+ new UserTokenHandler({ discovery }),
2099
2598
  plugin.getId(),
2100
- disableDefaultAuthPolicy
2599
+ disableDefaultAuthPolicy,
2600
+ publicKeyStore,
2601
+ PluginTokenHandler.create({
2602
+ ownPluginId: plugin.getId(),
2603
+ keyDurationSeconds: 60 * 60,
2604
+ logger,
2605
+ publicKeyStore,
2606
+ discovery
2607
+ })
2101
2608
  );
2102
2609
  }
2103
2610
  });
@@ -2269,7 +2776,7 @@ var __privateMethod$1 = (obj, member, method) => {
2269
2776
  __accessCheck$1(obj, member, "access private method");
2270
2777
  return method;
2271
2778
  };
2272
- var _auth, _discovery, _pluginId, _extractCredentialsFromRequest, extractCredentialsFromRequest_fn, _extractLimitedCredentialsFromRequest, extractLimitedCredentialsFromRequest_fn, _getCredentials, getCredentials_fn, _getLimitedCredentials, getLimitedCredentials_fn, _existingCookieExpiration, existingCookieExpiration_fn;
2779
+ var _auth, _discovery, _pluginId, _extractCredentialsFromRequest, extractCredentialsFromRequest_fn, _extractLimitedCredentialsFromRequest, extractLimitedCredentialsFromRequest_fn, _getCredentials, getCredentials_fn, _getLimitedCredentials, getLimitedCredentials_fn, _getCookieOptions, getCookieOptions_fn, _existingCookieExpiration, existingCookieExpiration_fn;
2273
2780
  const FIVE_MINUTES_MS = 5 * 60 * 1e3;
2274
2781
  const BACKSTAGE_AUTH_COOKIE = "backstage-auth";
2275
2782
  function getTokenFromRequest(req) {
@@ -2305,6 +2812,7 @@ class DefaultHttpAuthService {
2305
2812
  __privateAdd$1(this, _extractLimitedCredentialsFromRequest);
2306
2813
  __privateAdd$1(this, _getCredentials);
2307
2814
  __privateAdd$1(this, _getLimitedCredentials);
2815
+ __privateAdd$1(this, _getCookieOptions);
2308
2816
  __privateAdd$1(this, _existingCookieExpiration);
2309
2817
  __privateAdd$1(this, _auth, void 0);
2310
2818
  __privateAdd$1(this, _discovery, void 0);
@@ -2349,6 +2857,13 @@ class DefaultHttpAuthService {
2349
2857
  }
2350
2858
  let credentials;
2351
2859
  if (options == null ? void 0 : options.credentials) {
2860
+ if (__privateGet$1(this, _auth).isPrincipal(options.credentials, "none")) {
2861
+ res.clearCookie(
2862
+ BACKSTAGE_AUTH_COOKIE,
2863
+ await __privateMethod$1(this, _getCookieOptions, getCookieOptions_fn).call(this, res.req)
2864
+ );
2865
+ return { expiresAt: /* @__PURE__ */ new Date() };
2866
+ }
2352
2867
  if (!__privateGet$1(this, _auth).isPrincipal(options.credentials, "user")) {
2353
2868
  throw new errors.AuthenticationError(
2354
2869
  "Refused to issue cookie for non-user principal"
@@ -2362,26 +2877,15 @@ class DefaultHttpAuthService {
2362
2877
  if (existingExpiresAt && !willExpireSoon(existingExpiresAt)) {
2363
2878
  return { expiresAt: existingExpiresAt };
2364
2879
  }
2365
- const originHeader = res.req.headers.origin;
2366
- const origin = !originHeader || originHeader === "null" ? void 0 : originHeader;
2367
- const externalBaseUrlStr = await __privateGet$1(this, _discovery).getExternalBaseUrl(
2368
- __privateGet$1(this, _pluginId)
2369
- );
2370
- const externalBaseUrl = new URL(origin != null ? origin : externalBaseUrlStr);
2371
2880
  const { token, expiresAt } = await __privateGet$1(this, _auth).getLimitedUserToken(
2372
2881
  credentials
2373
2882
  );
2374
2883
  if (!token) {
2375
2884
  throw new Error("User credentials is unexpectedly missing token");
2376
2885
  }
2377
- const secure = externalBaseUrl.protocol === "https:" || externalBaseUrl.hostname === "localhost";
2378
2886
  res.cookie(BACKSTAGE_AUTH_COOKIE, token, {
2379
- domain: externalBaseUrl.hostname,
2380
- httpOnly: true,
2381
- expires: expiresAt,
2382
- secure,
2383
- priority: "high",
2384
- sameSite: secure ? "none" : "lax"
2887
+ ...await __privateMethod$1(this, _getCookieOptions, getCookieOptions_fn).call(this, res.req),
2888
+ expires: expiresAt
2385
2889
  });
2386
2890
  return { expiresAt };
2387
2891
  }
@@ -2423,6 +2927,23 @@ getLimitedCredentials_fn = async function(req) {
2423
2927
  var _a;
2424
2928
  return (_a = req[limitedCredentialsSymbol]) != null ? _a : req[limitedCredentialsSymbol] = __privateMethod$1(this, _extractLimitedCredentialsFromRequest, extractLimitedCredentialsFromRequest_fn).call(this, req);
2425
2929
  };
2930
+ _getCookieOptions = new WeakSet();
2931
+ getCookieOptions_fn = async function(req) {
2932
+ const originHeader = req.headers.origin;
2933
+ const origin = !originHeader || originHeader === "null" ? void 0 : originHeader;
2934
+ const externalBaseUrlStr = await __privateGet$1(this, _discovery).getExternalBaseUrl(
2935
+ __privateGet$1(this, _pluginId)
2936
+ );
2937
+ const externalBaseUrl = new URL(origin != null ? origin : externalBaseUrlStr);
2938
+ const secure = externalBaseUrl.protocol === "https:" || externalBaseUrl.hostname === "localhost";
2939
+ return {
2940
+ domain: externalBaseUrl.hostname,
2941
+ httpOnly: true,
2942
+ secure,
2943
+ priority: "high",
2944
+ sameSite: secure ? "none" : "lax"
2945
+ };
2946
+ };
2426
2947
  _existingCookieExpiration = new WeakSet();
2427
2948
  existingCookieExpiration_fn = async function(req) {
2428
2949
  const existingCookie = getCookieFromRequest(req);
@@ -2559,25 +3080,71 @@ function createCredentialsBarrier(options) {
2559
3080
  return { middleware, addAuthPolicy };
2560
3081
  }
2561
3082
 
3083
+ function createAuthIntegrationRouter(options) {
3084
+ const router = Router__default.default();
3085
+ router.get("/.backstage/auth/v1/jwks.json", async (_req, res) => {
3086
+ const { keys } = await options.auth.listPublicServiceKeys();
3087
+ res.json({ keys });
3088
+ });
3089
+ return router;
3090
+ }
3091
+
3092
+ const WELL_KNOWN_COOKIE_PATH_V1 = "/.backstage/auth/v1/cookie";
3093
+ function createCookieAuthRefreshMiddleware(options) {
3094
+ const { auth, httpAuth } = options;
3095
+ const router = Router__default.default();
3096
+ router.get(WELL_KNOWN_COOKIE_PATH_V1, async (_, res) => {
3097
+ const { expiresAt } = await httpAuth.issueUserCookie(res);
3098
+ res.json({ expiresAt: expiresAt.toISOString() });
3099
+ });
3100
+ router.delete(WELL_KNOWN_COOKIE_PATH_V1, async (_, res) => {
3101
+ const credentials = await auth.getNoneCredentials();
3102
+ await httpAuth.issueUserCookie(res, { credentials });
3103
+ res.status(204).end();
3104
+ });
3105
+ return router;
3106
+ }
3107
+
2562
3108
  const httpRouterServiceFactory = backendPluginApi.createServiceFactory(
2563
3109
  (options) => ({
2564
3110
  service: backendPluginApi.coreServices.httpRouter,
3111
+ initialization: "always",
2565
3112
  deps: {
2566
3113
  plugin: backendPluginApi.coreServices.pluginMetadata,
2567
3114
  config: backendPluginApi.coreServices.rootConfig,
3115
+ logger: backendPluginApi.coreServices.logger,
2568
3116
  lifecycle: backendPluginApi.coreServices.lifecycle,
2569
3117
  rootHttpRouter: backendPluginApi.coreServices.rootHttpRouter,
3118
+ auth: backendPluginApi.coreServices.auth,
2570
3119
  httpAuth: backendPluginApi.coreServices.httpAuth
2571
3120
  },
2572
- async factory({ httpAuth, config, plugin, rootHttpRouter, lifecycle }) {
3121
+ async factory({
3122
+ auth,
3123
+ httpAuth,
3124
+ config,
3125
+ logger,
3126
+ plugin,
3127
+ rootHttpRouter,
3128
+ lifecycle
3129
+ }) {
2573
3130
  var _a;
3131
+ if (options == null ? void 0 : options.getPath) {
3132
+ logger.warn(
3133
+ `DEPRECATION WARNING: The 'getPath' option for HttpRouterService is deprecated. The ability to reconfigure the '/api/' path prefix for plugins will be removed in the future.`
3134
+ );
3135
+ }
2574
3136
  const getPath = (_a = options == null ? void 0 : options.getPath) != null ? _a : (id) => `/api/${id}`;
2575
3137
  const path = getPath(plugin.getId());
2576
- const router = PromiseRouter__default.default();
3138
+ const router = Router__default.default();
2577
3139
  rootHttpRouter.use(path, router);
2578
- const credentialsBarrier = createCredentialsBarrier({ httpAuth, config });
3140
+ const credentialsBarrier = createCredentialsBarrier({
3141
+ httpAuth,
3142
+ config
3143
+ });
2579
3144
  router.use(createLifecycleMiddleware({ lifecycle }));
3145
+ router.use(createAuthIntegrationRouter({ auth }));
2580
3146
  router.use(credentialsBarrier.middleware);
3147
+ router.use(createCookieAuthRefreshMiddleware({ auth, httpAuth }));
2581
3148
  return {
2582
3149
  use(handler) {
2583
3150
  router.use(handler);
@@ -2666,6 +3233,9 @@ const _DefaultRootHttpRouter = class _DefaultRootHttpRouter {
2666
3233
  __privateAdd(this, _existingPaths, new Array());
2667
3234
  __privateSet(this, _indexPath, indexPath);
2668
3235
  __privateGet(this, _router).use(__privateGet(this, _namedRoutes));
3236
+ __privateGet(this, _router).use("/api/", (_req, _res, next) => {
3237
+ next("router");
3238
+ });
2669
3239
  if (__privateGet(this, _indexPath)) {
2670
3240
  __privateGet(this, _router).use(__privateGet(this, _indexRouter));
2671
3241
  }