@backstage/backend-app-api 0.7.5 → 0.7.6-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
@@ -73,22 +73,16 @@ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
73
73
  var express__default = /*#__PURE__*/_interopDefaultCompat(express);
74
74
  var trimEnd__default = /*#__PURE__*/_interopDefaultCompat(trimEnd);
75
75
 
76
- var __defProp$1 = Object.defineProperty;
77
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
78
- var __publicField$1 = (obj, key, value) => {
79
- __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
80
- return value;
81
- };
82
76
  class ObservableConfigProxy {
83
77
  constructor(parent, parentKey) {
84
78
  this.parent = parent;
85
79
  this.parentKey = parentKey;
86
- __publicField$1(this, "config", new config.ConfigReader({}));
87
- __publicField$1(this, "subscribers", []);
88
80
  if (parent && !parentKey) {
89
81
  throw new Error("parentKey is required if parent is set");
90
82
  }
91
83
  }
84
+ config = new config.ConfigReader({});
85
+ subscribers = [];
92
86
  setConfig(config) {
93
87
  if (this.parent) {
94
88
  throw new Error("immutable");
@@ -117,36 +111,31 @@ class ObservableConfigProxy {
117
111
  };
118
112
  }
119
113
  select(required) {
120
- var _a;
121
114
  if (this.parent && this.parentKey) {
122
115
  if (required) {
123
116
  return this.parent.select(true).getConfig(this.parentKey);
124
117
  }
125
- return (_a = this.parent.select(false)) == null ? void 0 : _a.getOptionalConfig(this.parentKey);
118
+ return this.parent.select(false)?.getOptionalConfig(this.parentKey);
126
119
  }
127
120
  return this.config;
128
121
  }
129
122
  has(key) {
130
- var _a, _b;
131
- return (_b = (_a = this.select(false)) == null ? void 0 : _a.has(key)) != null ? _b : false;
123
+ return this.select(false)?.has(key) ?? false;
132
124
  }
133
125
  keys() {
134
- var _a, _b;
135
- return (_b = (_a = this.select(false)) == null ? void 0 : _a.keys()) != null ? _b : [];
126
+ return this.select(false)?.keys() ?? [];
136
127
  }
137
128
  get(key) {
138
129
  return this.select(true).get(key);
139
130
  }
140
131
  getOptional(key) {
141
- var _a;
142
- return (_a = this.select(false)) == null ? void 0 : _a.getOptional(key);
132
+ return this.select(false)?.getOptional(key);
143
133
  }
144
134
  getConfig(key) {
145
135
  return new ObservableConfigProxy(this, key);
146
136
  }
147
137
  getOptionalConfig(key) {
148
- var _a;
149
- if ((_a = this.select(false)) == null ? void 0 : _a.has(key)) {
138
+ if (this.select(false)?.has(key)) {
150
139
  return new ObservableConfigProxy(this, key);
151
140
  }
152
141
  return void 0;
@@ -155,36 +144,31 @@ class ObservableConfigProxy {
155
144
  return this.select(true).getConfigArray(key);
156
145
  }
157
146
  getOptionalConfigArray(key) {
158
- var _a;
159
- return (_a = this.select(false)) == null ? void 0 : _a.getOptionalConfigArray(key);
147
+ return this.select(false)?.getOptionalConfigArray(key);
160
148
  }
161
149
  getNumber(key) {
162
150
  return this.select(true).getNumber(key);
163
151
  }
164
152
  getOptionalNumber(key) {
165
- var _a;
166
- return (_a = this.select(false)) == null ? void 0 : _a.getOptionalNumber(key);
153
+ return this.select(false)?.getOptionalNumber(key);
167
154
  }
168
155
  getBoolean(key) {
169
156
  return this.select(true).getBoolean(key);
170
157
  }
171
158
  getOptionalBoolean(key) {
172
- var _a;
173
- return (_a = this.select(false)) == null ? void 0 : _a.getOptionalBoolean(key);
159
+ return this.select(false)?.getOptionalBoolean(key);
174
160
  }
175
161
  getString(key) {
176
162
  return this.select(true).getString(key);
177
163
  }
178
164
  getOptionalString(key) {
179
- var _a;
180
- return (_a = this.select(false)) == null ? void 0 : _a.getOptionalString(key);
165
+ return this.select(false)?.getOptionalString(key);
181
166
  }
182
167
  getStringArray(key) {
183
168
  return this.select(true).getStringArray(key);
184
169
  }
185
170
  getOptionalStringArray(key) {
186
- var _a;
187
- return (_a = this.select(false)) == null ? void 0 : _a.getOptionalStringArray(key);
171
+ return this.select(false)?.getOptionalStringArray(key);
188
172
  }
189
173
  }
190
174
 
@@ -198,16 +182,14 @@ function isValidUrl(url) {
198
182
  }
199
183
 
200
184
  async function createConfigSecretEnumerator(options) {
201
- var _a;
202
185
  const { logger, dir = process.cwd() } = options;
203
186
  const { packages } = await getPackages.getPackages(dir);
204
- const schema = (_a = options.schema) != null ? _a : await configLoader.loadConfigSchema({
187
+ const schema = options.schema ?? await configLoader.loadConfigSchema({
205
188
  dependencies: packages.map((p) => p.packageJson.name)
206
189
  });
207
190
  return (config) => {
208
- var _a2;
209
191
  const [secretsData] = schema.process(
210
- [{ data: (_a2 = config.getOptional()) != null ? _a2 : {}, context: "schema-enumerator" }],
192
+ [{ data: config.getOptional() ?? {}, context: "schema-enumerator" }],
211
193
  {
212
194
  visibility: ["secret"],
213
195
  ignoreSchemaErrors: true
@@ -225,9 +207,8 @@ async function createConfigSecretEnumerator(options) {
225
207
  };
226
208
  }
227
209
  async function loadBackendConfig(options) {
228
- var _a, _b;
229
210
  const args = parseArgs__default.default(options.argv);
230
- const configTargets = [(_a = args.config) != null ? _a : []].flat().map((arg) => isValidUrl(arg) ? { url: arg } : { path: path.resolve(arg) });
211
+ const configTargets = [args.config ?? []].flat().map((arg) => isValidUrl(arg) ? { url: arg } : { path: path.resolve(arg) });
231
212
  const paths = cliCommon.findPaths(__dirname);
232
213
  let currentCancelFunc = void 0;
233
214
  const config$1 = new ObservableConfigProxy();
@@ -235,7 +216,7 @@ async function loadBackendConfig(options) {
235
216
  configRoot: paths.targetRoot,
236
217
  configTargets,
237
218
  remote: options.remote,
238
- watch: ((_b = options.watch) != null ? _b : true) ? {
219
+ watch: options.watch ?? true ? {
239
220
  onChange(newConfigs) {
240
221
  console.info(
241
222
  `Reloaded config from ${newConfigs.map((c) => c.context).join(", ")}`
@@ -277,8 +258,7 @@ function readHttpServerOptions(config) {
277
258
  };
278
259
  }
279
260
  function readHttpListenOptions(config) {
280
- var _a, _b;
281
- const listen = config == null ? void 0 : config.getOptional("listen");
261
+ const listen = config?.getOptional("listen");
282
262
  if (typeof listen === "string") {
283
263
  const parts = String(listen).split(":");
284
264
  const port = parseInt(parts[parts.length - 1], 10);
@@ -294,18 +274,18 @@ function readHttpListenOptions(config) {
294
274
  `Unable to parse listen address ${listen}, expected <port> or <host>:<port>`
295
275
  );
296
276
  }
297
- const host = (_a = config == null ? void 0 : config.getOptional("listen.host")) != null ? _a : DEFAULT_HOST;
277
+ const host = config?.getOptional("listen.host") ?? DEFAULT_HOST;
298
278
  if (typeof host !== "string") {
299
- config == null ? void 0 : config.getOptionalString("listen.host");
279
+ config?.getOptionalString("listen.host");
300
280
  throw new Error("unreachable");
301
281
  }
302
282
  return {
303
- port: (_b = config == null ? void 0 : config.getOptionalNumber("listen.port")) != null ? _b : DEFAULT_PORT,
283
+ port: config?.getOptionalNumber("listen.port") ?? DEFAULT_PORT,
304
284
  host
305
285
  };
306
286
  }
307
287
  function readHttpsOptions(config) {
308
- const https = config == null ? void 0 : config.getOptional("https");
288
+ const https = config?.getOptional("https");
309
289
  if (https === true) {
310
290
  const baseUrl = config.getString("baseUrl");
311
291
  let hostname;
@@ -316,7 +296,7 @@ function readHttpsOptions(config) {
316
296
  }
317
297
  return { certificate: { type: "generated", hostname } };
318
298
  }
319
- const cc = config == null ? void 0 : config.getOptionalConfig("https");
299
+ const cc = config?.getOptionalConfig("https");
320
300
  if (!cc) {
321
301
  return void 0;
322
302
  }
@@ -519,7 +499,7 @@ function readHelmetOptions(config) {
519
499
  };
520
500
  }
521
501
  function readCspDirectives(config) {
522
- const cc = config == null ? void 0 : config.getOptionalConfig("csp");
502
+ const cc = config?.getOptionalConfig("csp");
523
503
  if (!cc) {
524
504
  return void 0;
525
505
  }
@@ -551,7 +531,7 @@ function applyCspDirectives(directives) {
551
531
  }
552
532
 
553
533
  function readCorsOptions(config) {
554
- const cc = config == null ? void 0 : config.getOptionalConfig("cors");
534
+ const cc = config?.getOptionalConfig("cors");
555
535
  if (!cc) {
556
536
  return { origin: false };
557
537
  }
@@ -590,7 +570,7 @@ function createCorsOriginMatcher(allowedOriginPatterns) {
590
570
  return (origin, callback) => {
591
571
  return callback(
592
572
  null,
593
- allowedOriginMatchers.some((pattern) => pattern.match(origin != null ? origin : ""))
573
+ allowedOriginMatchers.some((pattern) => pattern.match(origin ?? ""))
594
574
  );
595
575
  };
596
576
  }
@@ -616,37 +596,18 @@ function applyInternalErrorFilter(error, logger) {
616
596
  return error;
617
597
  }
618
598
 
619
- var __accessCheck$e = (obj, member, msg) => {
620
- if (!member.has(obj))
621
- throw TypeError("Cannot " + msg);
622
- };
623
- var __privateGet$c = (obj, member, getter) => {
624
- __accessCheck$e(obj, member, "read from private field");
625
- return member.get(obj);
626
- };
627
- var __privateAdd$e = (obj, member, value) => {
628
- if (member.has(obj))
629
- throw TypeError("Cannot add the same private member more than once");
630
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
631
- };
632
- var __privateSet$a = (obj, member, value, setter) => {
633
- __accessCheck$e(obj, member, "write to private field");
634
- member.set(obj, value);
635
- return value;
636
- };
637
- var _config, _logger;
638
- const _MiddlewareFactory = class _MiddlewareFactory {
639
- constructor(options) {
640
- __privateAdd$e(this, _config, void 0);
641
- __privateAdd$e(this, _logger, void 0);
642
- __privateSet$a(this, _config, options.config);
643
- __privateSet$a(this, _logger, options.logger);
644
- }
599
+ class MiddlewareFactory {
600
+ #config;
601
+ #logger;
645
602
  /**
646
603
  * Creates a new {@link MiddlewareFactory}.
647
604
  */
648
605
  static create(options) {
649
- return new _MiddlewareFactory(options);
606
+ return new MiddlewareFactory(options);
607
+ }
608
+ constructor(options) {
609
+ this.#config = options.config;
610
+ this.#logger = options.logger;
650
611
  }
651
612
  /**
652
613
  * Returns a middleware that unconditionally produces a 404 error response.
@@ -686,7 +647,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
686
647
  * @returns An Express request handler
687
648
  */
688
649
  logging() {
689
- const logger = __privateGet$c(this, _logger).child({
650
+ const logger = this.#logger.child({
690
651
  type: "incomingRequest"
691
652
  });
692
653
  return morgan__default.default("combined", {
@@ -710,7 +671,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
710
671
  * @returns An Express request handler
711
672
  */
712
673
  helmet() {
713
- return helmet__default.default(readHelmetOptions(__privateGet$c(this, _config).getOptionalConfig("backend")));
674
+ return helmet__default.default(readHelmetOptions(this.#config.getOptionalConfig("backend")));
714
675
  }
715
676
  /**
716
677
  * Returns a middleware that implements the cors library.
@@ -725,7 +686,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
725
686
  * @returns An Express request handler
726
687
  */
727
688
  cors() {
728
- return cors__default.default(readCorsOptions(__privateGet$c(this, _config).getOptionalConfig("backend")));
689
+ return cors__default.default(readCorsOptions(this.#config.getOptionalConfig("backend")));
729
690
  }
730
691
  /**
731
692
  * Express middleware to handle errors during request processing.
@@ -748,9 +709,8 @@ const _MiddlewareFactory = class _MiddlewareFactory {
748
709
  * @returns An Express error request handler
749
710
  */
750
711
  error(options = {}) {
751
- var _a;
752
- const showStackTraces = (_a = options.showStackTraces) != null ? _a : process.env.NODE_ENV === "development";
753
- const logger = __privateGet$c(this, _logger).child({
712
+ const showStackTraces = options.showStackTraces ?? process.env.NODE_ENV === "development";
713
+ const logger = this.#logger.child({
754
714
  type: "errorHandler"
755
715
  });
756
716
  return (rawError, req, res, next) => {
@@ -771,10 +731,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
771
731
  res.status(statusCode).json(body);
772
732
  };
773
733
  }
774
- };
775
- _config = new WeakMap();
776
- _logger = new WeakMap();
777
- let MiddlewareFactory = _MiddlewareFactory;
734
+ }
778
735
  function getStatusCode(error) {
779
736
  const knownStatusCodeFields = ["statusCode", "status"];
780
737
  for (const field of knownStatusCodeFields) {
@@ -809,51 +766,27 @@ const escapeRegExp = (text) => {
809
766
  return text.replace(/[.*+?^${}(\)|[\]\\]/g, "\\$&");
810
767
  };
811
768
 
812
- var __accessCheck$d = (obj, member, msg) => {
813
- if (!member.has(obj))
814
- throw TypeError("Cannot " + msg);
815
- };
816
- var __privateGet$b = (obj, member, getter) => {
817
- __accessCheck$d(obj, member, "read from private field");
818
- return member.get(obj);
819
- };
820
- var __privateAdd$d = (obj, member, value) => {
821
- if (member.has(obj))
822
- throw TypeError("Cannot add the same private member more than once");
823
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
824
- };
825
- var __privateSet$9 = (obj, member, value, setter) => {
826
- __accessCheck$d(obj, member, "write to private field");
827
- member.set(obj, value);
828
- return value;
829
- };
830
- var _winston, _addRedactions;
831
- const _WinstonLogger = class _WinstonLogger {
832
- constructor(winston, addRedactions) {
833
- __privateAdd$d(this, _winston, void 0);
834
- __privateAdd$d(this, _addRedactions, void 0);
835
- __privateSet$9(this, _winston, winston);
836
- __privateSet$9(this, _addRedactions, addRedactions);
837
- }
769
+ class WinstonLogger {
770
+ #winston;
771
+ #addRedactions;
838
772
  /**
839
773
  * Creates a {@link WinstonLogger} instance.
840
774
  */
841
775
  static create(options) {
842
- var _a, _b;
843
- const redacter = _WinstonLogger.redacter();
844
- const defaultFormatter = process.env.NODE_ENV === "production" ? winston.format.json() : _WinstonLogger.colorFormat();
776
+ const redacter = WinstonLogger.redacter();
777
+ const defaultFormatter = process.env.NODE_ENV === "production" ? winston.format.json() : WinstonLogger.colorFormat();
845
778
  let logger = winston.createLogger({
846
779
  level: process.env.LOG_LEVEL || options.level || "info",
847
780
  format: winston.format.combine(
848
- (_a = options.format) != null ? _a : defaultFormatter,
781
+ options.format ?? defaultFormatter,
849
782
  redacter.format
850
783
  ),
851
- transports: (_b = options.transports) != null ? _b : new winston.transports.Console()
784
+ transports: options.transports ?? new winston.transports.Console()
852
785
  });
853
786
  if (options.meta) {
854
787
  logger = logger.child(options.meta);
855
788
  }
856
- return new _WinstonLogger(logger, redacter.add);
789
+ return new WinstonLogger(logger, redacter.add);
857
790
  }
858
791
  /**
859
792
  * Creates a winston log formatter for redacting secrets.
@@ -863,11 +796,10 @@ const _WinstonLogger = class _WinstonLogger {
863
796
  let redactionPattern = void 0;
864
797
  return {
865
798
  format: winston.format((obj) => {
866
- var _a, _b;
867
799
  if (!redactionPattern || !obj) {
868
800
  return obj;
869
801
  }
870
- obj[tripleBeam.MESSAGE] = (_b = (_a = obj[tripleBeam.MESSAGE]) == null ? void 0 : _a.replace) == null ? void 0 : _b.call(_a, redactionPattern, "[REDACTED]");
802
+ obj[tripleBeam.MESSAGE] = obj[tripleBeam.MESSAGE]?.replace?.(redactionPattern, "***");
871
803
  return obj;
872
804
  })(),
873
805
  add(newRedactions) {
@@ -916,227 +848,30 @@ const _WinstonLogger = class _WinstonLogger {
916
848
  })
917
849
  );
918
850
  }
851
+ constructor(winston, addRedactions) {
852
+ this.#winston = winston;
853
+ this.#addRedactions = addRedactions;
854
+ }
919
855
  error(message, meta) {
920
- __privateGet$b(this, _winston).error(message, meta);
856
+ this.#winston.error(message, meta);
921
857
  }
922
858
  warn(message, meta) {
923
- __privateGet$b(this, _winston).warn(message, meta);
859
+ this.#winston.warn(message, meta);
924
860
  }
925
861
  info(message, meta) {
926
- __privateGet$b(this, _winston).info(message, meta);
862
+ this.#winston.info(message, meta);
927
863
  }
928
864
  debug(message, meta) {
929
- __privateGet$b(this, _winston).debug(message, meta);
865
+ this.#winston.debug(message, meta);
930
866
  }
931
867
  child(meta) {
932
- return new _WinstonLogger(__privateGet$b(this, _winston).child(meta));
868
+ return new WinstonLogger(this.#winston.child(meta));
933
869
  }
934
870
  addRedactions(redactions) {
935
- var _a;
936
- (_a = __privateGet$b(this, _addRedactions)) == null ? void 0 : _a.call(this, redactions);
937
- }
938
- };
939
- _winston = new WeakMap();
940
- _addRedactions = new WeakMap();
941
- let WinstonLogger = _WinstonLogger;
942
-
943
- var __accessCheck$c = (obj, member, msg) => {
944
- if (!member.has(obj))
945
- throw TypeError("Cannot " + msg);
946
- };
947
- var __privateGet$a = (obj, member, getter) => {
948
- __accessCheck$c(obj, member, "read from private field");
949
- return member.get(obj);
950
- };
951
- var __privateAdd$c = (obj, member, value) => {
952
- if (member.has(obj))
953
- throw TypeError("Cannot add the same private member more than once");
954
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
955
- };
956
- var __privateSet$8 = (obj, member, value, setter) => {
957
- __accessCheck$c(obj, member, "write to private field");
958
- member.set(obj, value);
959
- return value;
960
- };
961
- var _hasStarted$1, _startupTasks$1, _hasShutdown, _shutdownTasks;
962
- class BackendLifecycleImpl {
963
- constructor(logger) {
964
- this.logger = logger;
965
- __privateAdd$c(this, _hasStarted$1, false);
966
- __privateAdd$c(this, _startupTasks$1, []);
967
- __privateAdd$c(this, _hasShutdown, false);
968
- __privateAdd$c(this, _shutdownTasks, []);
969
- }
970
- addStartupHook(hook, options) {
971
- if (__privateGet$a(this, _hasStarted$1)) {
972
- throw new Error("Attempted to add startup hook after startup");
973
- }
974
- __privateGet$a(this, _startupTasks$1).push({ hook, options });
975
- }
976
- async startup() {
977
- if (__privateGet$a(this, _hasStarted$1)) {
978
- return;
979
- }
980
- __privateSet$8(this, _hasStarted$1, true);
981
- this.logger.debug(`Running ${__privateGet$a(this, _startupTasks$1).length} startup tasks...`);
982
- await Promise.all(
983
- __privateGet$a(this, _startupTasks$1).map(async ({ hook, options }) => {
984
- var _a;
985
- const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
986
- try {
987
- await hook();
988
- logger.debug(`Startup hook succeeded`);
989
- } catch (error) {
990
- logger.error(`Startup hook failed, ${error}`);
991
- }
992
- })
993
- );
994
- }
995
- addShutdownHook(hook, options) {
996
- if (__privateGet$a(this, _hasShutdown)) {
997
- throw new Error("Attempted to add shutdown hook after shutdown");
998
- }
999
- __privateGet$a(this, _shutdownTasks).push({ hook, options });
1000
- }
1001
- async shutdown() {
1002
- if (__privateGet$a(this, _hasShutdown)) {
1003
- return;
1004
- }
1005
- __privateSet$8(this, _hasShutdown, true);
1006
- this.logger.debug(
1007
- `Running ${__privateGet$a(this, _shutdownTasks).length} shutdown tasks...`
1008
- );
1009
- await Promise.all(
1010
- __privateGet$a(this, _shutdownTasks).map(async ({ hook, options }) => {
1011
- var _a;
1012
- const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
1013
- try {
1014
- await hook();
1015
- logger.debug(`Shutdown hook succeeded`);
1016
- } catch (error) {
1017
- logger.error(`Shutdown hook failed, ${error}`);
1018
- }
1019
- })
1020
- );
1021
- }
1022
- }
1023
- _hasStarted$1 = new WeakMap();
1024
- _startupTasks$1 = new WeakMap();
1025
- _hasShutdown = new WeakMap();
1026
- _shutdownTasks = new WeakMap();
1027
- const rootLifecycleServiceFactory = backendPluginApi.createServiceFactory({
1028
- service: backendPluginApi.coreServices.rootLifecycle,
1029
- deps: {
1030
- logger: backendPluginApi.coreServices.rootLogger
1031
- },
1032
- async factory({ logger }) {
1033
- return new BackendLifecycleImpl(logger);
1034
- }
1035
- });
1036
-
1037
- var __accessCheck$b = (obj, member, msg) => {
1038
- if (!member.has(obj))
1039
- throw TypeError("Cannot " + msg);
1040
- };
1041
- var __privateGet$9 = (obj, member, getter) => {
1042
- __accessCheck$b(obj, member, "read from private field");
1043
- return member.get(obj);
1044
- };
1045
- var __privateAdd$b = (obj, member, value) => {
1046
- if (member.has(obj))
1047
- throw TypeError("Cannot add the same private member more than once");
1048
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1049
- };
1050
- var __privateSet$7 = (obj, member, value, setter) => {
1051
- __accessCheck$b(obj, member, "write to private field");
1052
- member.set(obj, value);
1053
- return value;
1054
- };
1055
- var _hasStarted, _startupTasks;
1056
- class BackendPluginLifecycleImpl {
1057
- constructor(logger, rootLifecycle, pluginMetadata) {
1058
- this.logger = logger;
1059
- this.rootLifecycle = rootLifecycle;
1060
- this.pluginMetadata = pluginMetadata;
1061
- __privateAdd$b(this, _hasStarted, false);
1062
- __privateAdd$b(this, _startupTasks, []);
1063
- }
1064
- addStartupHook(hook, options) {
1065
- if (__privateGet$9(this, _hasStarted)) {
1066
- throw new Error("Attempted to add startup hook after startup");
1067
- }
1068
- __privateGet$9(this, _startupTasks).push({ hook, options });
1069
- }
1070
- async startup() {
1071
- if (__privateGet$9(this, _hasStarted)) {
1072
- return;
1073
- }
1074
- __privateSet$7(this, _hasStarted, true);
1075
- this.logger.debug(
1076
- `Running ${__privateGet$9(this, _startupTasks).length} plugin startup tasks...`
1077
- );
1078
- await Promise.all(
1079
- __privateGet$9(this, _startupTasks).map(async ({ hook, options }) => {
1080
- var _a;
1081
- const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
1082
- try {
1083
- await hook();
1084
- logger.debug(`Plugin startup hook succeeded`);
1085
- } catch (error) {
1086
- logger.error(`Plugin startup hook failed, ${error}`);
1087
- }
1088
- })
1089
- );
1090
- }
1091
- addShutdownHook(hook, options) {
1092
- var _a, _b;
1093
- const plugin = this.pluginMetadata.getId();
1094
- this.rootLifecycle.addShutdownHook(hook, {
1095
- logger: (_b = (_a = options == null ? void 0 : options.logger) == null ? void 0 : _a.child({ plugin })) != null ? _b : this.logger
1096
- });
871
+ this.#addRedactions?.(redactions);
1097
872
  }
1098
873
  }
1099
- _hasStarted = new WeakMap();
1100
- _startupTasks = new WeakMap();
1101
- const lifecycleServiceFactory = backendPluginApi.createServiceFactory({
1102
- service: backendPluginApi.coreServices.lifecycle,
1103
- deps: {
1104
- logger: backendPluginApi.coreServices.logger,
1105
- rootLifecycle: backendPluginApi.coreServices.rootLifecycle,
1106
- pluginMetadata: backendPluginApi.coreServices.pluginMetadata
1107
- },
1108
- async factory({ rootLifecycle, logger, pluginMetadata }) {
1109
- return new BackendPluginLifecycleImpl(
1110
- logger,
1111
- rootLifecycle,
1112
- pluginMetadata
1113
- );
1114
- }
1115
- });
1116
874
 
1117
- var __accessCheck$a = (obj, member, msg) => {
1118
- if (!member.has(obj))
1119
- throw TypeError("Cannot " + msg);
1120
- };
1121
- var __privateGet$8 = (obj, member, getter) => {
1122
- __accessCheck$a(obj, member, "read from private field");
1123
- return member.get(obj);
1124
- };
1125
- var __privateAdd$a = (obj, member, value) => {
1126
- if (member.has(obj))
1127
- throw TypeError("Cannot add the same private member more than once");
1128
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1129
- };
1130
- var __privateSet$6 = (obj, member, value, setter) => {
1131
- __accessCheck$a(obj, member, "write to private field");
1132
- member.set(obj, value);
1133
- return value;
1134
- };
1135
- var __privateMethod$7 = (obj, member, method) => {
1136
- __accessCheck$a(obj, member, "access private method");
1137
- return method;
1138
- };
1139
- var _nodeIds, _cycleKeys, _getCycleKey, getCycleKey_fn, _nodes, _allProvided;
1140
875
  class Node {
1141
876
  constructor(value, consumes, provides) {
1142
877
  this.value = value;
@@ -1151,45 +886,29 @@ class Node {
1151
886
  );
1152
887
  }
1153
888
  }
1154
- const _CycleKeySet = class _CycleKeySet {
1155
- constructor(nodes) {
1156
- __privateAdd$a(this, _getCycleKey);
1157
- __privateAdd$a(this, _nodeIds, void 0);
1158
- __privateAdd$a(this, _cycleKeys, void 0);
1159
- __privateSet$6(this, _nodeIds, new Map(nodes.map((n, i) => [n.value, i])));
1160
- __privateSet$6(this, _cycleKeys, /* @__PURE__ */ new Set());
1161
- }
889
+ class CycleKeySet {
1162
890
  static from(nodes) {
1163
- return new _CycleKeySet(nodes);
891
+ return new CycleKeySet(nodes);
892
+ }
893
+ #nodeIds;
894
+ #cycleKeys;
895
+ constructor(nodes) {
896
+ this.#nodeIds = new Map(nodes.map((n, i) => [n.value, i]));
897
+ this.#cycleKeys = /* @__PURE__ */ new Set();
1164
898
  }
1165
899
  tryAdd(path) {
1166
- const cycleKey = __privateMethod$7(this, _getCycleKey, getCycleKey_fn).call(this, path);
1167
- if (__privateGet$8(this, _cycleKeys).has(cycleKey)) {
900
+ const cycleKey = this.#getCycleKey(path);
901
+ if (this.#cycleKeys.has(cycleKey)) {
1168
902
  return false;
1169
903
  }
1170
- __privateGet$8(this, _cycleKeys).add(cycleKey);
904
+ this.#cycleKeys.add(cycleKey);
1171
905
  return true;
1172
906
  }
1173
- };
1174
- _nodeIds = new WeakMap();
1175
- _cycleKeys = new WeakMap();
1176
- _getCycleKey = new WeakSet();
1177
- getCycleKey_fn = function(path) {
1178
- return path.map((n) => __privateGet$8(this, _nodeIds).get(n)).sort().join(",");
1179
- };
1180
- let CycleKeySet = _CycleKeySet;
1181
- const _DependencyGraph = class _DependencyGraph {
1182
- constructor(nodes) {
1183
- __privateAdd$a(this, _nodes, void 0);
1184
- __privateAdd$a(this, _allProvided, void 0);
1185
- __privateSet$6(this, _nodes, nodes);
1186
- __privateSet$6(this, _allProvided, /* @__PURE__ */ new Set());
1187
- for (const node of __privateGet$8(this, _nodes).values()) {
1188
- for (const produced of node.provides) {
1189
- __privateGet$8(this, _allProvided).add(produced);
1190
- }
1191
- }
907
+ #getCycleKey(path) {
908
+ return path.map((n) => this.#nodeIds.get(n)).sort().join(",");
1192
909
  }
910
+ }
911
+ class DependencyGraph {
1193
912
  static fromMap(nodes) {
1194
913
  return this.fromIterable(
1195
914
  Object.entries(nodes).map(([key, node]) => ({
@@ -1203,16 +922,27 @@ const _DependencyGraph = class _DependencyGraph {
1203
922
  for (const nodeInput of nodeInputs) {
1204
923
  nodes.push(Node.from(nodeInput));
1205
924
  }
1206
- return new _DependencyGraph(nodes);
925
+ return new DependencyGraph(nodes);
926
+ }
927
+ #nodes;
928
+ #allProvided;
929
+ constructor(nodes) {
930
+ this.#nodes = nodes;
931
+ this.#allProvided = /* @__PURE__ */ new Set();
932
+ for (const node of this.#nodes.values()) {
933
+ for (const produced of node.provides) {
934
+ this.#allProvided.add(produced);
935
+ }
936
+ }
1207
937
  }
1208
938
  /**
1209
939
  * Find all nodes that consume dependencies that are not provided by any other node.
1210
940
  */
1211
941
  findUnsatisfiedDeps() {
1212
942
  const unsatisfiedDependencies = [];
1213
- for (const node of __privateGet$8(this, _nodes).values()) {
943
+ for (const node of this.#nodes.values()) {
1214
944
  const unsatisfied = Array.from(node.consumes).filter(
1215
- (id) => !__privateGet$8(this, _allProvided).has(id)
945
+ (id) => !this.#allProvided.has(id)
1216
946
  );
1217
947
  if (unsatisfied.length > 0) {
1218
948
  unsatisfiedDependencies.push({ value: node.value, unsatisfied });
@@ -1232,8 +962,8 @@ const _DependencyGraph = class _DependencyGraph {
1232
962
  * form a cycle, with the same node as the first and last element of the array.
1233
963
  */
1234
964
  *detectCircularDependencies() {
1235
- const cycleKeys = CycleKeySet.from(__privateGet$8(this, _nodes));
1236
- for (const startNode of __privateGet$8(this, _nodes)) {
965
+ const cycleKeys = CycleKeySet.from(this.#nodes);
966
+ for (const startNode of this.#nodes) {
1237
967
  const visited = /* @__PURE__ */ new Set();
1238
968
  const stack = new Array([
1239
969
  startNode,
@@ -1246,7 +976,7 @@ const _DependencyGraph = class _DependencyGraph {
1246
976
  }
1247
977
  visited.add(node);
1248
978
  for (const consumed of node.consumes) {
1249
- const providerNodes = __privateGet$8(this, _nodes).filter(
979
+ const providerNodes = this.#nodes.filter(
1250
980
  (other) => other.provides.has(consumed)
1251
981
  );
1252
982
  for (const provider of providerNodes) {
@@ -1275,9 +1005,9 @@ const _DependencyGraph = class _DependencyGraph {
1275
1005
  * Dependencies of nodes that are not produced by any other nodes will be ignored.
1276
1006
  */
1277
1007
  async parallelTopologicalTraversal(fn) {
1278
- const allProvided = __privateGet$8(this, _allProvided);
1008
+ const allProvided = this.#allProvided;
1279
1009
  const producedSoFar = /* @__PURE__ */ new Set();
1280
- const waiting = new Set(__privateGet$8(this, _nodes).values());
1010
+ const waiting = new Set(this.#nodes.values());
1281
1011
  const visited = /* @__PURE__ */ new Set();
1282
1012
  const results = new Array();
1283
1013
  let inFlight = 0;
@@ -1318,34 +1048,8 @@ const _DependencyGraph = class _DependencyGraph {
1318
1048
  await processMoreNodes();
1319
1049
  return results;
1320
1050
  }
1321
- };
1322
- _nodes = new WeakMap();
1323
- _allProvided = new WeakMap();
1324
- let DependencyGraph = _DependencyGraph;
1051
+ }
1325
1052
 
1326
- var __accessCheck$9 = (obj, member, msg) => {
1327
- if (!member.has(obj))
1328
- throw TypeError("Cannot " + msg);
1329
- };
1330
- var __privateGet$7 = (obj, member, getter) => {
1331
- __accessCheck$9(obj, member, "read from private field");
1332
- return member.get(obj);
1333
- };
1334
- var __privateAdd$9 = (obj, member, value) => {
1335
- if (member.has(obj))
1336
- throw TypeError("Cannot add the same private member more than once");
1337
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1338
- };
1339
- var __privateSet$5 = (obj, member, value, setter) => {
1340
- __accessCheck$9(obj, member, "write to private field");
1341
- member.set(obj, value);
1342
- return value;
1343
- };
1344
- var __privateMethod$6 = (obj, member, method) => {
1345
- __accessCheck$9(obj, member, "access private method");
1346
- return method;
1347
- };
1348
- var _providedFactories, _loadedDefaultFactories, _implementations, _rootServiceImplementations, _addedFactoryIds, _instantiatedFactories, _resolveFactory, resolveFactory_fn, _checkForMissingDeps, checkForMissingDeps_fn;
1349
1053
  function toInternalServiceFactory(factory) {
1350
1054
  const f = factory;
1351
1055
  if (f.$$type !== "@backstage/BackendFeature") {
@@ -1360,33 +1064,77 @@ const pluginMetadataServiceFactory = backendPluginApi.createServiceFactory(
1360
1064
  (options) => ({
1361
1065
  service: backendPluginApi.coreServices.pluginMetadata,
1362
1066
  deps: {},
1363
- factory: async () => ({ getId: () => options == null ? void 0 : options.pluginId })
1067
+ factory: async () => ({ getId: () => options?.pluginId })
1364
1068
  })
1365
1069
  );
1366
- const _ServiceRegistry = class _ServiceRegistry {
1367
- constructor(factories) {
1368
- __privateAdd$9(this, _resolveFactory);
1369
- __privateAdd$9(this, _checkForMissingDeps);
1370
- __privateAdd$9(this, _providedFactories, void 0);
1371
- __privateAdd$9(this, _loadedDefaultFactories, void 0);
1372
- __privateAdd$9(this, _implementations, void 0);
1373
- __privateAdd$9(this, _rootServiceImplementations, /* @__PURE__ */ new Map());
1374
- __privateAdd$9(this, _addedFactoryIds, /* @__PURE__ */ new Set());
1375
- __privateAdd$9(this, _instantiatedFactories, /* @__PURE__ */ new Set());
1376
- __privateSet$5(this, _providedFactories, new Map(
1377
- factories.map((sf) => [sf.service.id, toInternalServiceFactory(sf)])
1378
- ));
1379
- __privateSet$5(this, _loadedDefaultFactories, /* @__PURE__ */ new Map());
1380
- __privateSet$5(this, _implementations, /* @__PURE__ */ new Map());
1381
- }
1070
+ class ServiceRegistry {
1382
1071
  static create(factories) {
1383
- const registry = new _ServiceRegistry(factories);
1072
+ const registry = new ServiceRegistry(factories);
1384
1073
  registry.checkForCircularDeps();
1385
1074
  return registry;
1386
1075
  }
1076
+ #providedFactories;
1077
+ #loadedDefaultFactories;
1078
+ #implementations;
1079
+ #rootServiceImplementations = /* @__PURE__ */ new Map();
1080
+ #addedFactoryIds = /* @__PURE__ */ new Set();
1081
+ #instantiatedFactories = /* @__PURE__ */ new Set();
1082
+ constructor(factories) {
1083
+ this.#providedFactories = new Map(
1084
+ factories.map((sf) => [sf.service.id, toInternalServiceFactory(sf)])
1085
+ );
1086
+ this.#loadedDefaultFactories = /* @__PURE__ */ new Map();
1087
+ this.#implementations = /* @__PURE__ */ new Map();
1088
+ }
1089
+ #resolveFactory(ref, pluginId) {
1090
+ if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
1091
+ return Promise.resolve(
1092
+ toInternalServiceFactory(pluginMetadataServiceFactory({ pluginId }))
1093
+ );
1094
+ }
1095
+ let resolvedFactory = this.#providedFactories.get(ref.id);
1096
+ const { __defaultFactory: defaultFactory } = ref;
1097
+ if (!resolvedFactory && !defaultFactory) {
1098
+ return void 0;
1099
+ }
1100
+ if (!resolvedFactory) {
1101
+ let loadedFactory = this.#loadedDefaultFactories.get(defaultFactory);
1102
+ if (!loadedFactory) {
1103
+ loadedFactory = Promise.resolve().then(() => defaultFactory(ref)).then(
1104
+ (f) => toInternalServiceFactory(typeof f === "function" ? f() : f)
1105
+ );
1106
+ this.#loadedDefaultFactories.set(defaultFactory, loadedFactory);
1107
+ }
1108
+ resolvedFactory = loadedFactory.catch((error) => {
1109
+ throw new Error(
1110
+ `Failed to instantiate service '${ref.id}' because the default factory loader threw an error, ${errors.stringifyError(
1111
+ error
1112
+ )}`
1113
+ );
1114
+ });
1115
+ }
1116
+ return Promise.resolve(resolvedFactory);
1117
+ }
1118
+ #checkForMissingDeps(factory, pluginId) {
1119
+ const missingDeps = Object.values(factory.deps).filter((ref) => {
1120
+ if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
1121
+ return false;
1122
+ }
1123
+ if (this.#providedFactories.get(ref.id)) {
1124
+ return false;
1125
+ }
1126
+ return !ref.__defaultFactory;
1127
+ });
1128
+ if (missingDeps.length) {
1129
+ const missing = missingDeps.map((r) => `'${r.id}'`).join(", ");
1130
+ throw new Error(
1131
+ `Failed to instantiate service '${factory.service.id}' for '${pluginId}' because the following dependent services are missing: ${missing}`
1132
+ );
1133
+ }
1134
+ }
1387
1135
  checkForCircularDeps() {
1388
1136
  const graph = DependencyGraph.fromIterable(
1389
- Array.from(__privateGet$7(this, _providedFactories)).map(
1137
+ Array.from(this.#providedFactories).map(
1390
1138
  ([serviceId, serviceFactory]) => ({
1391
1139
  value: serviceId,
1392
1140
  provides: [serviceId],
@@ -1408,21 +1156,21 @@ const _ServiceRegistry = class _ServiceRegistry {
1408
1156
  `The ${backendPluginApi.coreServices.pluginMetadata.id} service cannot be overridden`
1409
1157
  );
1410
1158
  }
1411
- if (__privateGet$7(this, _addedFactoryIds).has(factoryId)) {
1159
+ if (this.#addedFactoryIds.has(factoryId)) {
1412
1160
  throw new Error(
1413
1161
  `Duplicate service implementations provided for ${factoryId}`
1414
1162
  );
1415
1163
  }
1416
- if (__privateGet$7(this, _instantiatedFactories).has(factoryId)) {
1164
+ if (this.#instantiatedFactories.has(factoryId)) {
1417
1165
  throw new Error(
1418
1166
  `Unable to set service factory with id ${factoryId}, service has already been instantiated`
1419
1167
  );
1420
1168
  }
1421
- __privateGet$7(this, _addedFactoryIds).add(factoryId);
1422
- __privateGet$7(this, _providedFactories).set(factoryId, toInternalServiceFactory(factory));
1169
+ this.#addedFactoryIds.add(factoryId);
1170
+ this.#providedFactories.set(factoryId, toInternalServiceFactory(factory));
1423
1171
  }
1424
1172
  async initializeEagerServicesWithScope(scope, pluginId = "root") {
1425
- for (const factory of __privateGet$7(this, _providedFactories).values()) {
1173
+ for (const factory of this.#providedFactories.values()) {
1426
1174
  if (factory.service.scope === scope) {
1427
1175
  if (scope === "root" && factory.initialization !== "lazy") {
1428
1176
  await this.get(factory.service, pluginId);
@@ -1433,13 +1181,12 @@ const _ServiceRegistry = class _ServiceRegistry {
1433
1181
  }
1434
1182
  }
1435
1183
  get(ref, pluginId) {
1436
- var _a;
1437
- __privateGet$7(this, _instantiatedFactories).add(ref.id);
1438
- return (_a = __privateMethod$6(this, _resolveFactory, resolveFactory_fn).call(this, ref, pluginId)) == null ? void 0 : _a.then((factory) => {
1184
+ this.#instantiatedFactories.add(ref.id);
1185
+ return this.#resolveFactory(ref, pluginId)?.then((factory) => {
1439
1186
  if (factory.service.scope === "root") {
1440
- let existing = __privateGet$7(this, _rootServiceImplementations).get(factory);
1187
+ let existing = this.#rootServiceImplementations.get(factory);
1441
1188
  if (!existing) {
1442
- __privateMethod$6(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
1189
+ this.#checkForMissingDeps(factory, pluginId);
1443
1190
  const rootDeps = new Array();
1444
1191
  for (const [name, serviceRef] of Object.entries(factory.deps)) {
1445
1192
  if (serviceRef.scope !== "root") {
@@ -1453,13 +1200,13 @@ const _ServiceRegistry = class _ServiceRegistry {
1453
1200
  existing = Promise.all(rootDeps).then(
1454
1201
  (entries) => factory.factory(Object.fromEntries(entries), void 0)
1455
1202
  );
1456
- __privateGet$7(this, _rootServiceImplementations).set(factory, existing);
1203
+ this.#rootServiceImplementations.set(factory, existing);
1457
1204
  }
1458
1205
  return existing;
1459
1206
  }
1460
- let implementation = __privateGet$7(this, _implementations).get(factory);
1207
+ let implementation = this.#implementations.get(factory);
1461
1208
  if (!implementation) {
1462
- __privateMethod$6(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
1209
+ this.#checkForMissingDeps(factory, pluginId);
1463
1210
  const rootDeps = new Array();
1464
1211
  for (const [name, serviceRef] of Object.entries(factory.deps)) {
1465
1212
  if (serviceRef.scope === "root") {
@@ -1469,10 +1216,7 @@ const _ServiceRegistry = class _ServiceRegistry {
1469
1216
  }
1470
1217
  implementation = {
1471
1218
  context: Promise.all(rootDeps).then(
1472
- (entries) => {
1473
- var _a2;
1474
- return (_a2 = factory.createRootContext) == null ? void 0 : _a2.call(factory, Object.fromEntries(entries));
1475
- }
1219
+ (entries) => factory.createRootContext?.(Object.fromEntries(entries))
1476
1220
  ).catch((error) => {
1477
1221
  const cause = errors.stringifyError(error);
1478
1222
  throw new Error(
@@ -1481,7 +1225,7 @@ const _ServiceRegistry = class _ServiceRegistry {
1481
1225
  }),
1482
1226
  byPlugin: /* @__PURE__ */ new Map()
1483
1227
  };
1484
- __privateGet$7(this, _implementations).set(factory, implementation);
1228
+ this.#implementations.set(factory, implementation);
1485
1229
  }
1486
1230
  let result = implementation.byPlugin.get(pluginId);
1487
1231
  if (!result) {
@@ -1505,72 +1249,17 @@ const _ServiceRegistry = class _ServiceRegistry {
1505
1249
  return result;
1506
1250
  });
1507
1251
  }
1508
- };
1509
- _providedFactories = new WeakMap();
1510
- _loadedDefaultFactories = new WeakMap();
1511
- _implementations = new WeakMap();
1512
- _rootServiceImplementations = new WeakMap();
1513
- _addedFactoryIds = new WeakMap();
1514
- _instantiatedFactories = new WeakMap();
1515
- _resolveFactory = new WeakSet();
1516
- resolveFactory_fn = function(ref, pluginId) {
1517
- if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
1518
- return Promise.resolve(
1519
- toInternalServiceFactory(pluginMetadataServiceFactory({ pluginId }))
1520
- );
1521
- }
1522
- let resolvedFactory = __privateGet$7(this, _providedFactories).get(ref.id);
1523
- const { __defaultFactory: defaultFactory } = ref;
1524
- if (!resolvedFactory && !defaultFactory) {
1525
- return void 0;
1526
- }
1527
- if (!resolvedFactory) {
1528
- let loadedFactory = __privateGet$7(this, _loadedDefaultFactories).get(defaultFactory);
1529
- if (!loadedFactory) {
1530
- loadedFactory = Promise.resolve().then(() => defaultFactory(ref)).then(
1531
- (f) => toInternalServiceFactory(typeof f === "function" ? f() : f)
1532
- );
1533
- __privateGet$7(this, _loadedDefaultFactories).set(defaultFactory, loadedFactory);
1534
- }
1535
- resolvedFactory = loadedFactory.catch((error) => {
1536
- throw new Error(
1537
- `Failed to instantiate service '${ref.id}' because the default factory loader threw an error, ${errors.stringifyError(
1538
- error
1539
- )}`
1540
- );
1541
- });
1542
- }
1543
- return Promise.resolve(resolvedFactory);
1544
- };
1545
- _checkForMissingDeps = new WeakSet();
1546
- checkForMissingDeps_fn = function(factory, pluginId) {
1547
- const missingDeps = Object.values(factory.deps).filter((ref) => {
1548
- if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
1549
- return false;
1550
- }
1551
- if (__privateGet$7(this, _providedFactories).get(ref.id)) {
1552
- return false;
1553
- }
1554
- return !ref.__defaultFactory;
1555
- });
1556
- if (missingDeps.length) {
1557
- const missing = missingDeps.map((r) => `'${r.id}'`).join(", ");
1558
- throw new Error(
1559
- `Failed to instantiate service '${factory.service.id}' for '${pluginId}' because the following dependent services are missing: ${missing}`
1560
- );
1561
- }
1562
- };
1563
- let ServiceRegistry = _ServiceRegistry;
1252
+ }
1564
1253
 
1565
1254
  const LOGGER_INTERVAL_MAX = 6e4;
1566
1255
  function joinIds(ids) {
1567
1256
  return [...ids].map((id) => `'${id}'`).join(", ");
1568
1257
  }
1569
1258
  function createInitializationLogger(pluginIds, rootLogger) {
1570
- const logger = rootLogger == null ? void 0 : rootLogger.child({ type: "initialization" });
1259
+ const logger = rootLogger?.child({ type: "initialization" });
1571
1260
  const starting = new Set(pluginIds);
1572
1261
  const started = /* @__PURE__ */ new Set();
1573
- logger == null ? void 0 : logger.info(`Plugin initialization started: ${joinIds(pluginIds)}`);
1262
+ logger?.info(`Plugin initialization started: ${joinIds(pluginIds)}`);
1574
1263
  const getInitStatus = () => {
1575
1264
  let status = "";
1576
1265
  if (started.size > 0) {
@@ -1586,7 +1275,7 @@ function createInitializationLogger(pluginIds, rootLogger) {
1586
1275
  let prevInterval = 0;
1587
1276
  let timeout;
1588
1277
  const onTimeout = () => {
1589
- logger == null ? void 0 : logger.info(`Plugin initialization in progress${getInitStatus()}`);
1278
+ logger?.info(`Plugin initialization in progress${getInitStatus()}`);
1590
1279
  const nextInterval = Math.min(interval + prevInterval, LOGGER_INTERVAL_MAX);
1591
1280
  prevInterval = interval;
1592
1281
  interval = nextInterval;
@@ -1599,7 +1288,7 @@ function createInitializationLogger(pluginIds, rootLogger) {
1599
1288
  started.add(pluginId);
1600
1289
  },
1601
1290
  onAllStarted() {
1602
- logger == null ? void 0 : logger.info(`Plugin initialization complete${getInitStatus()}`);
1291
+ logger?.info(`Plugin initialization complete${getInitStatus()}`);
1603
1292
  if (timeout) {
1604
1293
  clearTimeout(timeout);
1605
1294
  timeout = void 0;
@@ -1608,52 +1297,76 @@ function createInitializationLogger(pluginIds, rootLogger) {
1608
1297
  };
1609
1298
  }
1610
1299
 
1611
- var __accessCheck$8 = (obj, member, msg) => {
1612
- if (!member.has(obj))
1613
- throw TypeError("Cannot " + msg);
1614
- };
1615
- var __privateGet$6 = (obj, member, getter) => {
1616
- __accessCheck$8(obj, member, "read from private field");
1617
- return member.get(obj);
1618
- };
1619
- var __privateAdd$8 = (obj, member, value) => {
1620
- if (member.has(obj))
1621
- throw TypeError("Cannot add the same private member more than once");
1622
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1623
- };
1624
- var __privateSet$4 = (obj, member, value, setter) => {
1625
- __accessCheck$8(obj, member, "write to private field");
1626
- member.set(obj, value);
1627
- return value;
1628
- };
1629
- var __privateMethod$5 = (obj, member, method) => {
1630
- __accessCheck$8(obj, member, "access private method");
1631
- return method;
1632
- };
1633
- var _startPromise, _features, _extensionPoints, _serviceRegistry, _registeredFeatures, _getInitDeps, getInitDeps_fn, _addFeature, addFeature_fn, _doStart, doStart_fn, _getRootLifecycleImpl, getRootLifecycleImpl_fn, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn;
1634
1300
  class BackendInitializer {
1301
+ #startPromise;
1302
+ #features = new Array();
1303
+ #extensionPoints = /* @__PURE__ */ new Map();
1304
+ #serviceRegistry;
1305
+ #registeredFeatures = new Array();
1635
1306
  constructor(defaultApiFactories) {
1636
- __privateAdd$8(this, _getInitDeps);
1637
- __privateAdd$8(this, _addFeature);
1638
- __privateAdd$8(this, _doStart);
1639
- // Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this
1640
- __privateAdd$8(this, _getRootLifecycleImpl);
1641
- __privateAdd$8(this, _getPluginLifecycleImpl);
1642
- __privateAdd$8(this, _startPromise, void 0);
1643
- __privateAdd$8(this, _features, new Array());
1644
- __privateAdd$8(this, _extensionPoints, /* @__PURE__ */ new Map());
1645
- __privateAdd$8(this, _serviceRegistry, void 0);
1646
- __privateAdd$8(this, _registeredFeatures, new Array());
1647
- __privateSet$4(this, _serviceRegistry, ServiceRegistry.create([...defaultApiFactories]));
1307
+ this.#serviceRegistry = ServiceRegistry.create([...defaultApiFactories]);
1308
+ }
1309
+ async #getInitDeps(deps, pluginId, moduleId) {
1310
+ const result = /* @__PURE__ */ new Map();
1311
+ const missingRefs = /* @__PURE__ */ new Set();
1312
+ for (const [name, ref] of Object.entries(deps)) {
1313
+ const ep = this.#extensionPoints.get(ref.id);
1314
+ if (ep) {
1315
+ if (ep.pluginId !== pluginId) {
1316
+ throw new Error(
1317
+ `Illegal dependency: Module '${moduleId}' for plugin '${pluginId}' attempted to depend on extension point '${ref.id}' for plugin '${ep.pluginId}'. Extension points can only be used within their plugin's scope.`
1318
+ );
1319
+ }
1320
+ result.set(name, ep.impl);
1321
+ } else {
1322
+ const impl = await this.#serviceRegistry.get(
1323
+ ref,
1324
+ pluginId
1325
+ );
1326
+ if (impl) {
1327
+ result.set(name, impl);
1328
+ } else {
1329
+ missingRefs.add(ref);
1330
+ }
1331
+ }
1332
+ }
1333
+ if (missingRefs.size > 0) {
1334
+ const missing = Array.from(missingRefs).join(", ");
1335
+ throw new Error(
1336
+ `No extension point or service available for the following ref(s): ${missing}`
1337
+ );
1338
+ }
1339
+ return Object.fromEntries(result);
1648
1340
  }
1649
1341
  add(feature) {
1650
- if (__privateGet$6(this, _startPromise)) {
1342
+ if (this.#startPromise) {
1651
1343
  throw new Error("feature can not be added after the backend has started");
1652
1344
  }
1653
- __privateGet$6(this, _registeredFeatures).push(Promise.resolve(feature));
1345
+ this.#registeredFeatures.push(Promise.resolve(feature));
1346
+ }
1347
+ #addFeature(feature) {
1348
+ if (feature.$$type !== "@backstage/BackendFeature") {
1349
+ throw new Error(
1350
+ `Failed to add feature, invalid type '${feature.$$type}'`
1351
+ );
1352
+ }
1353
+ if (isServiceFactory(feature)) {
1354
+ this.#serviceRegistry.add(feature);
1355
+ } else if (isInternalBackendFeature(feature)) {
1356
+ if (feature.version !== "v1") {
1357
+ throw new Error(
1358
+ `Failed to add feature, invalid version '${feature.version}'`
1359
+ );
1360
+ }
1361
+ this.#features.push(feature);
1362
+ } else {
1363
+ throw new Error(
1364
+ `Failed to add feature, invalid feature ${JSON.stringify(feature)}`
1365
+ );
1366
+ }
1654
1367
  }
1655
1368
  async start() {
1656
- if (__privateGet$6(this, _startPromise)) {
1369
+ if (this.#startPromise) {
1657
1370
  throw new Error("Backend has already started");
1658
1371
  }
1659
1372
  const exitHandler = async () => {
@@ -1671,242 +1384,187 @@ class BackendInitializer {
1671
1384
  process.addListener("SIGTERM", exitHandler);
1672
1385
  process.addListener("SIGINT", exitHandler);
1673
1386
  process.addListener("beforeExit", exitHandler);
1674
- __privateSet$4(this, _startPromise, __privateMethod$5(this, _doStart, doStart_fn).call(this));
1675
- await __privateGet$6(this, _startPromise);
1676
- }
1677
- async stop() {
1678
- if (!__privateGet$6(this, _startPromise)) {
1679
- return;
1680
- }
1681
- try {
1682
- await __privateGet$6(this, _startPromise);
1683
- } catch (error) {
1684
- }
1685
- const lifecycleService = await __privateMethod$5(this, _getRootLifecycleImpl, getRootLifecycleImpl_fn).call(this);
1686
- await lifecycleService.shutdown();
1387
+ this.#startPromise = this.#doStart();
1388
+ await this.#startPromise;
1687
1389
  }
1688
- }
1689
- _startPromise = new WeakMap();
1690
- _features = new WeakMap();
1691
- _extensionPoints = new WeakMap();
1692
- _serviceRegistry = new WeakMap();
1693
- _registeredFeatures = new WeakMap();
1694
- _getInitDeps = new WeakSet();
1695
- getInitDeps_fn = async function(deps, pluginId, moduleId) {
1696
- const result = /* @__PURE__ */ new Map();
1697
- const missingRefs = /* @__PURE__ */ new Set();
1698
- for (const [name, ref] of Object.entries(deps)) {
1699
- const ep = __privateGet$6(this, _extensionPoints).get(ref.id);
1700
- if (ep) {
1701
- if (ep.pluginId !== pluginId) {
1702
- throw new Error(
1703
- `Illegal dependency: Module '${moduleId}' for plugin '${pluginId}' attempted to depend on extension point '${ref.id}' for plugin '${ep.pluginId}'. Extension points can only be used within their plugin's scope.`
1704
- );
1705
- }
1706
- result.set(name, ep.impl);
1707
- } else {
1708
- const impl = await __privateGet$6(this, _serviceRegistry).get(
1709
- ref,
1710
- pluginId
1711
- );
1712
- if (impl) {
1713
- result.set(name, impl);
1714
- } else {
1715
- missingRefs.add(ref);
1716
- }
1390
+ async #doStart() {
1391
+ this.#serviceRegistry.checkForCircularDeps();
1392
+ for (const feature of this.#registeredFeatures) {
1393
+ this.#addFeature(await feature);
1717
1394
  }
1718
- }
1719
- if (missingRefs.size > 0) {
1720
- const missing = Array.from(missingRefs).join(", ");
1721
- throw new Error(
1722
- `No extension point or service available for the following ref(s): ${missing}`
1395
+ const featureDiscovery = await this.#serviceRegistry.get(
1396
+ alpha.featureDiscoveryServiceRef,
1397
+ "root"
1723
1398
  );
1724
- }
1725
- return Object.fromEntries(result);
1726
- };
1727
- _addFeature = new WeakSet();
1728
- addFeature_fn = function(feature) {
1729
- if (feature.$$type !== "@backstage/BackendFeature") {
1730
- throw new Error(
1731
- `Failed to add feature, invalid type '${feature.$$type}'`
1732
- );
1733
- }
1734
- if (isServiceFactory(feature)) {
1735
- __privateGet$6(this, _serviceRegistry).add(feature);
1736
- } else if (isInternalBackendFeature(feature)) {
1737
- if (feature.version !== "v1") {
1738
- throw new Error(
1739
- `Failed to add feature, invalid version '${feature.version}'`
1740
- );
1741
- }
1742
- __privateGet$6(this, _features).push(feature);
1743
- } else {
1744
- throw new Error(
1745
- `Failed to add feature, invalid feature ${JSON.stringify(feature)}`
1746
- );
1747
- }
1748
- };
1749
- _doStart = new WeakSet();
1750
- doStart_fn = async function() {
1751
- __privateGet$6(this, _serviceRegistry).checkForCircularDeps();
1752
- for (const feature of __privateGet$6(this, _registeredFeatures)) {
1753
- __privateMethod$5(this, _addFeature, addFeature_fn).call(this, await feature);
1754
- }
1755
- const featureDiscovery = await __privateGet$6(this, _serviceRegistry).get(
1756
- alpha.featureDiscoveryServiceRef,
1757
- "root"
1758
- );
1759
- if (featureDiscovery) {
1760
- const { features } = await featureDiscovery.getBackendFeatures();
1761
- for (const feature of features) {
1762
- __privateMethod$5(this, _addFeature, addFeature_fn).call(this, feature);
1763
- }
1764
- __privateGet$6(this, _serviceRegistry).checkForCircularDeps();
1765
- }
1766
- await __privateGet$6(this, _serviceRegistry).initializeEagerServicesWithScope("root");
1767
- const pluginInits = /* @__PURE__ */ new Map();
1768
- const moduleInits = /* @__PURE__ */ new Map();
1769
- for (const feature of __privateGet$6(this, _features)) {
1770
- for (const r of feature.getRegistrations()) {
1771
- const provides = /* @__PURE__ */ new Set();
1772
- if (r.type === "plugin" || r.type === "module") {
1773
- for (const [extRef, extImpl] of r.extensionPoints) {
1774
- if (__privateGet$6(this, _extensionPoints).has(extRef.id)) {
1399
+ if (featureDiscovery) {
1400
+ const { features } = await featureDiscovery.getBackendFeatures();
1401
+ for (const feature of features) {
1402
+ this.#addFeature(feature);
1403
+ }
1404
+ this.#serviceRegistry.checkForCircularDeps();
1405
+ }
1406
+ await this.#serviceRegistry.initializeEagerServicesWithScope("root");
1407
+ const pluginInits = /* @__PURE__ */ new Map();
1408
+ const moduleInits = /* @__PURE__ */ new Map();
1409
+ for (const feature of this.#features) {
1410
+ for (const r of feature.getRegistrations()) {
1411
+ const provides = /* @__PURE__ */ new Set();
1412
+ if (r.type === "plugin" || r.type === "module") {
1413
+ for (const [extRef, extImpl] of r.extensionPoints) {
1414
+ if (this.#extensionPoints.has(extRef.id)) {
1415
+ throw new Error(
1416
+ `ExtensionPoint with ID '${extRef.id}' is already registered`
1417
+ );
1418
+ }
1419
+ this.#extensionPoints.set(extRef.id, {
1420
+ impl: extImpl,
1421
+ pluginId: r.pluginId
1422
+ });
1423
+ provides.add(extRef);
1424
+ }
1425
+ }
1426
+ if (r.type === "plugin") {
1427
+ if (pluginInits.has(r.pluginId)) {
1428
+ throw new Error(`Plugin '${r.pluginId}' is already registered`);
1429
+ }
1430
+ pluginInits.set(r.pluginId, {
1431
+ provides,
1432
+ consumes: new Set(Object.values(r.init.deps)),
1433
+ init: r.init
1434
+ });
1435
+ } else {
1436
+ let modules = moduleInits.get(r.pluginId);
1437
+ if (!modules) {
1438
+ modules = /* @__PURE__ */ new Map();
1439
+ moduleInits.set(r.pluginId, modules);
1440
+ }
1441
+ if (modules.has(r.moduleId)) {
1775
1442
  throw new Error(
1776
- `ExtensionPoint with ID '${extRef.id}' is already registered`
1443
+ `Module '${r.moduleId}' for plugin '${r.pluginId}' is already registered`
1777
1444
  );
1778
1445
  }
1779
- __privateGet$6(this, _extensionPoints).set(extRef.id, {
1780
- impl: extImpl,
1781
- pluginId: r.pluginId
1446
+ modules.set(r.moduleId, {
1447
+ provides,
1448
+ consumes: new Set(Object.values(r.init.deps)),
1449
+ init: r.init
1782
1450
  });
1783
- provides.add(extRef);
1784
1451
  }
1785
1452
  }
1786
- if (r.type === "plugin") {
1787
- if (pluginInits.has(r.pluginId)) {
1788
- throw new Error(`Plugin '${r.pluginId}' is already registered`);
1789
- }
1790
- pluginInits.set(r.pluginId, {
1791
- provides,
1792
- consumes: new Set(Object.values(r.init.deps)),
1793
- init: r.init
1794
- });
1795
- } else {
1796
- let modules = moduleInits.get(r.pluginId);
1797
- if (!modules) {
1798
- modules = /* @__PURE__ */ new Map();
1799
- moduleInits.set(r.pluginId, modules);
1800
- }
1801
- if (modules.has(r.moduleId)) {
1802
- throw new Error(
1803
- `Module '${r.moduleId}' for plugin '${r.pluginId}' is already registered`
1804
- );
1805
- }
1806
- modules.set(r.moduleId, {
1807
- provides,
1808
- consumes: new Set(Object.values(r.init.deps)),
1809
- init: r.init
1810
- });
1811
- }
1812
1453
  }
1813
- }
1814
- const allPluginIds = [...pluginInits.keys()];
1815
- const initLogger = createInitializationLogger(
1816
- allPluginIds,
1817
- await __privateGet$6(this, _serviceRegistry).get(backendPluginApi.coreServices.rootLogger, "root")
1818
- );
1819
- await Promise.all(
1820
- allPluginIds.map(async (pluginId) => {
1821
- await __privateGet$6(this, _serviceRegistry).initializeEagerServicesWithScope(
1822
- "plugin",
1823
- pluginId
1824
- );
1825
- const modules = moduleInits.get(pluginId);
1826
- if (modules) {
1827
- const tree = DependencyGraph.fromIterable(
1828
- Array.from(modules).map(([moduleId, moduleInit]) => ({
1829
- value: { moduleId, moduleInit },
1830
- // Relationships are reversed at this point since we're only interested in the extension points.
1831
- // If a modules provides extension point A we want it to be initialized AFTER all modules
1832
- // that depend on extension point A, so that they can provide their extensions.
1833
- consumes: Array.from(moduleInit.provides).map((p) => p.id),
1834
- provides: Array.from(moduleInit.consumes).map((c) => c.id)
1835
- }))
1454
+ const allPluginIds = [...pluginInits.keys()];
1455
+ const initLogger = createInitializationLogger(
1456
+ allPluginIds,
1457
+ await this.#serviceRegistry.get(backendPluginApi.coreServices.rootLogger, "root")
1458
+ );
1459
+ await Promise.all(
1460
+ allPluginIds.map(async (pluginId) => {
1461
+ await this.#serviceRegistry.initializeEagerServicesWithScope(
1462
+ "plugin",
1463
+ pluginId
1836
1464
  );
1837
- const circular = tree.detectCircularDependency();
1838
- if (circular) {
1839
- throw new errors.ConflictError(
1840
- `Circular dependency detected for modules of plugin '${pluginId}', ${circular.map(({ moduleId }) => `'${moduleId}'`).join(" -> ")}`
1465
+ const modules = moduleInits.get(pluginId);
1466
+ if (modules) {
1467
+ const tree = DependencyGraph.fromIterable(
1468
+ Array.from(modules).map(([moduleId, moduleInit]) => ({
1469
+ value: { moduleId, moduleInit },
1470
+ // Relationships are reversed at this point since we're only interested in the extension points.
1471
+ // If a modules provides extension point A we want it to be initialized AFTER all modules
1472
+ // that depend on extension point A, so that they can provide their extensions.
1473
+ consumes: Array.from(moduleInit.provides).map((p) => p.id),
1474
+ provides: Array.from(moduleInit.consumes).map((c) => c.id)
1475
+ }))
1841
1476
  );
1842
- }
1843
- await tree.parallelTopologicalTraversal(
1844
- async ({ moduleId, moduleInit }) => {
1845
- const moduleDeps = await __privateMethod$5(this, _getInitDeps, getInitDeps_fn).call(this, moduleInit.init.deps, pluginId, moduleId);
1846
- await moduleInit.init.func(moduleDeps).catch((error) => {
1847
- throw new errors.ForwardedError(
1848
- `Module '${moduleId}' for plugin '${pluginId}' startup failed`,
1849
- error
1850
- );
1851
- });
1477
+ const circular = tree.detectCircularDependency();
1478
+ if (circular) {
1479
+ throw new errors.ConflictError(
1480
+ `Circular dependency detected for modules of plugin '${pluginId}', ${circular.map(({ moduleId }) => `'${moduleId}'`).join(" -> ")}`
1481
+ );
1852
1482
  }
1853
- );
1854
- }
1855
- const pluginInit = pluginInits.get(pluginId);
1856
- if (pluginInit) {
1857
- const pluginDeps = await __privateMethod$5(this, _getInitDeps, getInitDeps_fn).call(this, pluginInit.init.deps, pluginId);
1858
- await pluginInit.init.func(pluginDeps).catch((error) => {
1859
- throw new errors.ForwardedError(
1860
- `Plugin '${pluginId}' startup failed`,
1861
- error
1483
+ await tree.parallelTopologicalTraversal(
1484
+ async ({ moduleId, moduleInit }) => {
1485
+ const moduleDeps = await this.#getInitDeps(
1486
+ moduleInit.init.deps,
1487
+ pluginId,
1488
+ moduleId
1489
+ );
1490
+ await moduleInit.init.func(moduleDeps).catch((error) => {
1491
+ throw new errors.ForwardedError(
1492
+ `Module '${moduleId}' for plugin '${pluginId}' startup failed`,
1493
+ error
1494
+ );
1495
+ });
1496
+ }
1862
1497
  );
1863
- });
1864
- }
1865
- initLogger.onPluginStarted(pluginId);
1866
- const lifecycleService2 = await __privateMethod$5(this, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn).call(this, pluginId);
1867
- await lifecycleService2.startup();
1868
- })
1869
- );
1870
- const lifecycleService = await __privateMethod$5(this, _getRootLifecycleImpl, getRootLifecycleImpl_fn).call(this);
1871
- await lifecycleService.startup();
1872
- initLogger.onAllStarted();
1873
- if (process.env.NODE_ENV !== "test") {
1874
- const rootLogger = await __privateGet$6(this, _serviceRegistry).get(
1875
- backendPluginApi.coreServices.rootLogger,
1876
- "root"
1498
+ }
1499
+ const pluginInit = pluginInits.get(pluginId);
1500
+ if (pluginInit) {
1501
+ const pluginDeps = await this.#getInitDeps(
1502
+ pluginInit.init.deps,
1503
+ pluginId
1504
+ );
1505
+ await pluginInit.init.func(pluginDeps).catch((error) => {
1506
+ throw new errors.ForwardedError(
1507
+ `Plugin '${pluginId}' startup failed`,
1508
+ error
1509
+ );
1510
+ });
1511
+ }
1512
+ initLogger.onPluginStarted(pluginId);
1513
+ const lifecycleService2 = await this.#getPluginLifecycleImpl(pluginId);
1514
+ await lifecycleService2.startup();
1515
+ })
1877
1516
  );
1878
- process.on("unhandledRejection", (reason) => {
1879
- var _a;
1880
- (_a = rootLogger == null ? void 0 : rootLogger.child({ type: "unhandledRejection" })) == null ? void 0 : _a.error("Unhandled rejection", reason);
1881
- });
1882
- process.on("uncaughtException", (error) => {
1883
- var _a;
1884
- (_a = rootLogger == null ? void 0 : rootLogger.child({ type: "uncaughtException" })) == null ? void 0 : _a.error("Uncaught exception", error);
1885
- });
1517
+ const lifecycleService = await this.#getRootLifecycleImpl();
1518
+ await lifecycleService.startup();
1519
+ initLogger.onAllStarted();
1520
+ if (process.env.NODE_ENV !== "test") {
1521
+ const rootLogger = await this.#serviceRegistry.get(
1522
+ backendPluginApi.coreServices.rootLogger,
1523
+ "root"
1524
+ );
1525
+ process.on("unhandledRejection", (reason) => {
1526
+ rootLogger?.child({ type: "unhandledRejection" })?.error("Unhandled rejection", reason);
1527
+ });
1528
+ process.on("uncaughtException", (error) => {
1529
+ rootLogger?.child({ type: "uncaughtException" })?.error("Uncaught exception", error);
1530
+ });
1531
+ }
1886
1532
  }
1887
- };
1888
- _getRootLifecycleImpl = new WeakSet();
1889
- getRootLifecycleImpl_fn = async function() {
1890
- const lifecycleService = await __privateGet$6(this, _serviceRegistry).get(
1891
- backendPluginApi.coreServices.rootLifecycle,
1892
- "root"
1893
- );
1894
- if (lifecycleService instanceof BackendLifecycleImpl) {
1895
- return lifecycleService;
1533
+ async stop() {
1534
+ if (!this.#startPromise) {
1535
+ return;
1536
+ }
1537
+ try {
1538
+ await this.#startPromise;
1539
+ } catch (error) {
1540
+ }
1541
+ const lifecycleService = await this.#getRootLifecycleImpl();
1542
+ await lifecycleService.shutdown();
1896
1543
  }
1897
- throw new Error("Unexpected root lifecycle service implementation");
1898
- };
1899
- _getPluginLifecycleImpl = new WeakSet();
1900
- getPluginLifecycleImpl_fn = async function(pluginId) {
1901
- const lifecycleService = await __privateGet$6(this, _serviceRegistry).get(
1902
- backendPluginApi.coreServices.lifecycle,
1903
- pluginId
1904
- );
1905
- if (lifecycleService instanceof BackendPluginLifecycleImpl) {
1906
- return lifecycleService;
1544
+ // Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this
1545
+ async #getRootLifecycleImpl() {
1546
+ const lifecycleService = await this.#serviceRegistry.get(
1547
+ backendPluginApi.coreServices.rootLifecycle,
1548
+ "root"
1549
+ );
1550
+ const service = lifecycleService;
1551
+ if (service && typeof service.startup === "function" && typeof service.shutdown === "function") {
1552
+ return service;
1553
+ }
1554
+ throw new Error("Unexpected root lifecycle service implementation");
1907
1555
  }
1908
- throw new Error("Unexpected plugin lifecycle service implementation");
1909
- };
1556
+ async #getPluginLifecycleImpl(pluginId) {
1557
+ const lifecycleService = await this.#serviceRegistry.get(
1558
+ backendPluginApi.coreServices.lifecycle,
1559
+ pluginId
1560
+ );
1561
+ const service = lifecycleService;
1562
+ if (service && typeof service.startup === "function") {
1563
+ return service;
1564
+ }
1565
+ throw new Error("Unexpected plugin lifecycle service implementation");
1566
+ }
1567
+ }
1910
1568
  function isServiceFactory(feature) {
1911
1569
  return !!feature.service;
1912
1570
  }
@@ -1914,45 +1572,25 @@ function isInternalBackendFeature(feature) {
1914
1572
  return typeof feature.getRegistrations === "function";
1915
1573
  }
1916
1574
 
1917
- var __accessCheck$7 = (obj, member, msg) => {
1918
- if (!member.has(obj))
1919
- throw TypeError("Cannot " + msg);
1920
- };
1921
- var __privateGet$5 = (obj, member, getter) => {
1922
- __accessCheck$7(obj, member, "read from private field");
1923
- return member.get(obj);
1924
- };
1925
- var __privateAdd$7 = (obj, member, value) => {
1926
- if (member.has(obj))
1927
- throw TypeError("Cannot add the same private member more than once");
1928
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1929
- };
1930
- var __privateSet$3 = (obj, member, value, setter) => {
1931
- __accessCheck$7(obj, member, "write to private field");
1932
- member.set(obj, value);
1933
- return value;
1934
- };
1935
- var _initializer;
1936
1575
  class BackstageBackend {
1576
+ #initializer;
1937
1577
  constructor(defaultServiceFactories) {
1938
- __privateAdd$7(this, _initializer, void 0);
1939
- __privateSet$3(this, _initializer, new BackendInitializer(defaultServiceFactories));
1578
+ this.#initializer = new BackendInitializer(defaultServiceFactories);
1940
1579
  }
1941
1580
  add(feature) {
1942
1581
  if (isPromise(feature)) {
1943
- __privateGet$5(this, _initializer).add(feature.then((f) => unwrapFeature(f.default)));
1582
+ this.#initializer.add(feature.then((f) => unwrapFeature(f.default)));
1944
1583
  } else {
1945
- __privateGet$5(this, _initializer).add(unwrapFeature(feature));
1584
+ this.#initializer.add(unwrapFeature(feature));
1946
1585
  }
1947
1586
  }
1948
1587
  async start() {
1949
- await __privateGet$5(this, _initializer).start();
1588
+ await this.#initializer.start();
1950
1589
  }
1951
1590
  async stop() {
1952
- await __privateGet$5(this, _initializer).stop();
1591
+ await this.#initializer.stop();
1953
1592
  }
1954
1593
  }
1955
- _initializer = new WeakMap();
1956
1594
  function isPromise(value) {
1957
1595
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
1958
1596
  }
@@ -2013,10 +1651,9 @@ class DatabaseKeyStore {
2013
1651
  this.logger = logger;
2014
1652
  }
2015
1653
  static async create(options) {
2016
- var _a;
2017
1654
  const { database, logger } = options;
2018
1655
  const client = await database.getClient();
2019
- if (!((_a = database.migrations) == null ? void 0 : _a.skip)) {
1656
+ if (!database.migrations?.skip) {
2020
1657
  await applyDatabaseMigrations(client);
2021
1658
  }
2022
1659
  return new DatabaseKeyStore(client, logger);
@@ -2060,14 +1697,15 @@ class DatabaseKeyStore {
2060
1697
  }
2061
1698
  }
2062
1699
 
2063
- function createCredentialsWithServicePrincipal(sub, token) {
1700
+ function createCredentialsWithServicePrincipal(sub, token, accessRestrictions) {
2064
1701
  return {
2065
1702
  $$type: "@backstage/BackstageCredentials",
2066
1703
  version: "v1",
2067
1704
  token,
2068
1705
  principal: {
2069
1706
  type: "service",
2070
- subject: sub
1707
+ subject: sub,
1708
+ accessRestrictions
2071
1709
  }
2072
1710
  };
2073
1711
  }
@@ -2105,20 +1743,6 @@ function toInternalBackstageCredentials(credentials) {
2105
1743
  return internalCredentials;
2106
1744
  }
2107
1745
 
2108
- var __accessCheck$6 = (obj, member, msg) => {
2109
- if (!member.has(obj))
2110
- throw TypeError("Cannot " + msg);
2111
- };
2112
- var __privateAdd$6 = (obj, member, value) => {
2113
- if (member.has(obj))
2114
- throw TypeError("Cannot add the same private member more than once");
2115
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
2116
- };
2117
- var __privateMethod$4 = (obj, member, method) => {
2118
- __accessCheck$6(obj, member, "access private method");
2119
- return method;
2120
- };
2121
- var _getJwtExpiration, getJwtExpiration_fn;
2122
1746
  class DefaultAuthService {
2123
1747
  constructor(userTokenHandler, pluginTokenHandler, externalTokenHandler, tokenManager, pluginId, disableDefaultAuthPolicy, publicKeyStore) {
2124
1748
  this.userTokenHandler = userTokenHandler;
@@ -2128,7 +1752,6 @@ class DefaultAuthService {
2128
1752
  this.pluginId = pluginId;
2129
1753
  this.disableDefaultAuthPolicy = disableDefaultAuthPolicy;
2130
1754
  this.publicKeyStore = publicKeyStore;
2131
- __privateAdd$6(this, _getJwtExpiration);
2132
1755
  }
2133
1756
  // allowLimitedAccess is currently ignored, since we currently always use the full user tokens
2134
1757
  async authenticate(token) {
@@ -2146,7 +1769,7 @@ class DefaultAuthService {
2146
1769
  return createCredentialsWithUserPrincipal(
2147
1770
  userResult2.userEntityRef,
2148
1771
  pluginResult.limitedUserToken,
2149
- __privateMethod$4(this, _getJwtExpiration, getJwtExpiration_fn).call(this, pluginResult.limitedUserToken)
1772
+ this.#getJwtExpiration(pluginResult.limitedUserToken)
2150
1773
  );
2151
1774
  }
2152
1775
  return createCredentialsWithServicePrincipal(pluginResult.subject);
@@ -2156,12 +1779,16 @@ class DefaultAuthService {
2156
1779
  return createCredentialsWithUserPrincipal(
2157
1780
  userResult.userEntityRef,
2158
1781
  token,
2159
- __privateMethod$4(this, _getJwtExpiration, getJwtExpiration_fn).call(this, token)
1782
+ this.#getJwtExpiration(token)
2160
1783
  );
2161
1784
  }
2162
1785
  const externalResult = await this.externalTokenHandler.verifyToken(token);
2163
1786
  if (externalResult) {
2164
- return createCredentialsWithServicePrincipal(externalResult.subject);
1787
+ return createCredentialsWithServicePrincipal(
1788
+ externalResult.subject,
1789
+ void 0,
1790
+ externalResult.accessRestrictions
1791
+ );
2165
1792
  }
2166
1793
  throw new errors.AuthenticationError("Illegal token");
2167
1794
  }
@@ -2244,49 +1871,29 @@ class DefaultAuthService {
2244
1871
  const { keys } = await this.publicKeyStore.listKeys();
2245
1872
  return { keys: keys.map(({ key }) => key) };
2246
1873
  }
2247
- }
2248
- _getJwtExpiration = new WeakSet();
2249
- getJwtExpiration_fn = function(token) {
2250
- const { exp } = jose.decodeJwt(token);
2251
- if (!exp) {
2252
- throw new errors.AuthenticationError("User token is missing expiration");
1874
+ #getJwtExpiration(token) {
1875
+ const { exp } = jose.decodeJwt(token);
1876
+ if (!exp) {
1877
+ throw new errors.AuthenticationError("User token is missing expiration");
1878
+ }
1879
+ return new Date(exp * 1e3);
2253
1880
  }
2254
- return new Date(exp * 1e3);
2255
- };
1881
+ }
2256
1882
 
2257
- var __accessCheck$5 = (obj, member, msg) => {
2258
- if (!member.has(obj))
2259
- throw TypeError("Cannot " + msg);
2260
- };
2261
- var __privateGet$4 = (obj, member, getter) => {
2262
- __accessCheck$5(obj, member, "read from private field");
2263
- return member.get(obj);
2264
- };
2265
- var __privateAdd$5 = (obj, member, value) => {
2266
- if (member.has(obj))
2267
- throw TypeError("Cannot add the same private member more than once");
2268
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
2269
- };
2270
- var __privateSet$2 = (obj, member, value, setter) => {
2271
- __accessCheck$5(obj, member, "write to private field");
2272
- member.set(obj, value);
2273
- return value;
2274
- };
2275
- var _keyStore, _keyStoreUpdated;
2276
1883
  const CLOCK_MARGIN_S = 10;
2277
1884
  class JwksClient {
2278
1885
  constructor(getEndpoint) {
2279
1886
  this.getEndpoint = getEndpoint;
2280
- __privateAdd$5(this, _keyStore, void 0);
2281
- __privateAdd$5(this, _keyStoreUpdated, 0);
2282
1887
  }
1888
+ #keyStore;
1889
+ #keyStoreUpdated = 0;
2283
1890
  get getKey() {
2284
- if (!__privateGet$4(this, _keyStore)) {
1891
+ if (!this.#keyStore) {
2285
1892
  throw new errors.AuthenticationError(
2286
1893
  "refreshKeyStore must be called before jwksClient.getKey"
2287
1894
  );
2288
1895
  }
2289
- return __privateGet$4(this, _keyStore);
1896
+ return this.#keyStore;
2290
1897
  }
2291
1898
  /**
2292
1899
  * If the last keystore refresh is stale, update the keystore URL to the latest
@@ -2296,9 +1903,9 @@ class JwksClient {
2296
1903
  const header = await jose.decodeProtectedHeader(rawJwtToken);
2297
1904
  let keyStoreHasKey;
2298
1905
  try {
2299
- if (__privateGet$4(this, _keyStore)) {
1906
+ if (this.#keyStore) {
2300
1907
  const [_, rawPayload, rawSignature] = rawJwtToken.split(".");
2301
- keyStoreHasKey = await __privateGet$4(this, _keyStore).call(this, header, {
1908
+ keyStoreHasKey = await this.#keyStore(header, {
2302
1909
  payload: rawPayload,
2303
1910
  signature: rawSignature
2304
1911
  });
@@ -2306,23 +1913,15 @@ class JwksClient {
2306
1913
  } catch (error) {
2307
1914
  keyStoreHasKey = false;
2308
1915
  }
2309
- const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > __privateGet$4(this, _keyStoreUpdated) - CLOCK_MARGIN_S;
2310
- if (!__privateGet$4(this, _keyStore) || !keyStoreHasKey && issuedAfterLastRefresh) {
1916
+ const issuedAfterLastRefresh = payload?.iat && payload.iat > this.#keyStoreUpdated - CLOCK_MARGIN_S;
1917
+ if (!this.#keyStore || !keyStoreHasKey && issuedAfterLastRefresh) {
2311
1918
  const endpoint = await this.getEndpoint();
2312
- __privateSet$2(this, _keyStore, jose.createRemoteJWKSet(endpoint));
2313
- __privateSet$2(this, _keyStoreUpdated, Date.now() / 1e3);
1919
+ this.#keyStore = jose.createRemoteJWKSet(endpoint);
1920
+ this.#keyStoreUpdated = Date.now() / 1e3;
2314
1921
  }
2315
1922
  }
2316
1923
  }
2317
- _keyStore = new WeakMap();
2318
- _keyStoreUpdated = new WeakMap();
2319
1924
 
2320
- var __defProp = Object.defineProperty;
2321
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2322
- var __publicField = (obj, key, value) => {
2323
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
2324
- return value;
2325
- };
2326
1925
  const KEY_EXPIRATION_MARGIN_FACTOR = 3;
2327
1926
  const SECONDS_IN_MS = 1e3;
2328
1927
  const ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;
@@ -2334,21 +1933,20 @@ class PluginTokenHandler {
2334
1933
  this.keyDurationSeconds = keyDurationSeconds;
2335
1934
  this.algorithm = algorithm;
2336
1935
  this.discovery = discovery;
2337
- __publicField(this, "privateKeyPromise");
2338
- __publicField(this, "keyExpiry");
2339
- __publicField(this, "jwksMap", /* @__PURE__ */ new Map());
2340
- // Tracking state for isTargetPluginSupported
2341
- __publicField(this, "supportedTargetPlugins", /* @__PURE__ */ new Set());
2342
- __publicField(this, "targetPluginInflightChecks", /* @__PURE__ */ new Map());
2343
1936
  }
1937
+ privateKeyPromise;
1938
+ keyExpiry;
1939
+ jwksMap = /* @__PURE__ */ new Map();
1940
+ // Tracking state for isTargetPluginSupported
1941
+ supportedTargetPlugins = /* @__PURE__ */ new Set();
1942
+ targetPluginInflightChecks = /* @__PURE__ */ new Map();
2344
1943
  static create(options) {
2345
- var _a;
2346
1944
  return new PluginTokenHandler(
2347
1945
  options.logger,
2348
1946
  options.ownPluginId,
2349
1947
  options.publicKeyStore,
2350
1948
  Math.round(types.durationToMilliseconds(options.keyDuration) / 1e3),
2351
- (_a = options.algorithm) != null ? _a : "ES256",
1949
+ options.algorithm ?? "ES256",
2352
1950
  options.discovery
2353
1951
  );
2354
1952
  }
@@ -2396,7 +1994,7 @@ class PluginTokenHandler {
2396
1994
  ourExp,
2397
1995
  Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS)
2398
1996
  ) : ourExp;
2399
- const claims = { sub, aud, iat, exp, obo: onBehalfOf == null ? void 0 : onBehalfOf.token };
1997
+ const claims = { sub, aud, iat, exp, obo: onBehalfOf?.token };
2400
1998
  const token = await new jose.SignJWT(claims).setProtectedHeader({
2401
1999
  typ: pluginAuthNode.tokenTypes.plugin.typParam,
2402
2000
  alg: this.algorithm,
@@ -2502,34 +2100,19 @@ class PluginTokenHandler {
2502
2100
  }
2503
2101
  }
2504
2102
 
2505
- var __accessCheck$4 = (obj, member, msg) => {
2506
- if (!member.has(obj))
2507
- throw TypeError("Cannot " + msg);
2508
- };
2509
- var __privateAdd$4 = (obj, member, value) => {
2510
- if (member.has(obj))
2511
- throw TypeError("Cannot add the same private member more than once");
2512
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
2513
- };
2514
- var __privateMethod$3 = (obj, member, method) => {
2515
- __accessCheck$4(obj, member, "access private method");
2516
- return method;
2517
- };
2518
- var _getTokenVerificationOptions, getTokenVerificationOptions_fn;
2519
- const _UserTokenHandler = class _UserTokenHandler {
2103
+ class UserTokenHandler {
2520
2104
  constructor(jwksClient) {
2521
2105
  this.jwksClient = jwksClient;
2522
- __privateAdd$4(this, _getTokenVerificationOptions);
2523
2106
  }
2524
2107
  static create(options) {
2525
2108
  const jwksClient = new JwksClient(async () => {
2526
2109
  const url = await options.discovery.getBaseUrl("auth");
2527
2110
  return new URL(`${url}/.well-known/jwks.json`);
2528
2111
  });
2529
- return new _UserTokenHandler(jwksClient);
2112
+ return new UserTokenHandler(jwksClient);
2530
2113
  }
2531
2114
  async verifyToken(token) {
2532
- const verifyOpts = __privateMethod$3(this, _getTokenVerificationOptions, getTokenVerificationOptions_fn).call(this, token);
2115
+ const verifyOpts = this.#getTokenVerificationOptions(token);
2533
2116
  if (!verifyOpts) {
2534
2117
  return void 0;
2535
2118
  }
@@ -2547,6 +2130,31 @@ const _UserTokenHandler = class _UserTokenHandler {
2547
2130
  }
2548
2131
  return { userEntityRef };
2549
2132
  }
2133
+ #getTokenVerificationOptions(token) {
2134
+ try {
2135
+ const { typ } = jose.decodeProtectedHeader(token);
2136
+ if (typ === pluginAuthNode.tokenTypes.user.typParam) {
2137
+ return {
2138
+ requiredClaims: ["iat", "exp", "sub"],
2139
+ typ: pluginAuthNode.tokenTypes.user.typParam
2140
+ };
2141
+ }
2142
+ if (typ === pluginAuthNode.tokenTypes.limitedUser.typParam) {
2143
+ return {
2144
+ requiredClaims: ["iat", "exp", "sub"],
2145
+ typ: pluginAuthNode.tokenTypes.limitedUser.typParam
2146
+ };
2147
+ }
2148
+ const { aud } = jose.decodeJwt(token);
2149
+ if (aud === pluginAuthNode.tokenTypes.user.audClaim) {
2150
+ return {
2151
+ audience: pluginAuthNode.tokenTypes.user.audClaim
2152
+ };
2153
+ }
2154
+ } catch {
2155
+ }
2156
+ return void 0;
2157
+ }
2550
2158
  createLimitedUserToken(backstageToken) {
2551
2159
  const [headerRaw, payloadRaw] = backstageToken.split(".");
2552
2160
  const header = JSON.parse(
@@ -2592,64 +2200,133 @@ const _UserTokenHandler = class _UserTokenHandler {
2592
2200
  return false;
2593
2201
  }
2594
2202
  }
2595
- };
2596
- _getTokenVerificationOptions = new WeakSet();
2597
- getTokenVerificationOptions_fn = function(token) {
2598
- try {
2599
- const { typ } = jose.decodeProtectedHeader(token);
2600
- if (typ === pluginAuthNode.tokenTypes.user.typParam) {
2601
- return {
2602
- requiredClaims: ["iat", "exp", "sub"],
2603
- typ: pluginAuthNode.tokenTypes.user.typParam
2604
- };
2203
+ }
2204
+
2205
+ function readAccessRestrictionsFromConfig(externalAccessEntryConfig) {
2206
+ const configs = externalAccessEntryConfig.getOptionalConfigArray("accessRestrictions") ?? [];
2207
+ const result = /* @__PURE__ */ new Map();
2208
+ for (const config of configs) {
2209
+ const validKeys = ["plugin", "permission", "permissionAttribute"];
2210
+ for (const key of config.keys()) {
2211
+ if (!validKeys.includes(key)) {
2212
+ const valid = validKeys.map((k) => `'${k}'`).join(", ");
2213
+ throw new Error(
2214
+ `Invalid key '${key}' in 'accessRestrictions' config, expected one of ${valid}`
2215
+ );
2216
+ }
2605
2217
  }
2606
- if (typ === pluginAuthNode.tokenTypes.limitedUser.typParam) {
2607
- return {
2608
- requiredClaims: ["iat", "exp", "sub"],
2609
- typ: pluginAuthNode.tokenTypes.limitedUser.typParam
2610
- };
2218
+ const pluginId = config.getString("plugin");
2219
+ const permissionNames = readPermissionNames(config);
2220
+ const permissionAttributes = readPermissionAttributes(config);
2221
+ if (result.has(pluginId)) {
2222
+ throw new Error(
2223
+ `Attempted to declare 'accessRestrictions' twice for plugin '${pluginId}', which is not permitted`
2224
+ );
2611
2225
  }
2612
- const { aud } = jose.decodeJwt(token);
2613
- if (aud === pluginAuthNode.tokenTypes.user.audClaim) {
2614
- return {
2615
- audience: pluginAuthNode.tokenTypes.user.audClaim
2616
- };
2226
+ result.set(pluginId, {
2227
+ ...permissionNames ? { permissionNames } : {},
2228
+ ...permissionAttributes ? { permissionAttributes } : {}
2229
+ });
2230
+ }
2231
+ return result.size ? result : void 0;
2232
+ }
2233
+ function readStringOrStringArrayFromConfig(root, key, validValues) {
2234
+ if (!root.has(key)) {
2235
+ return void 0;
2236
+ }
2237
+ const rawValues = Array.isArray(root.get(key)) ? root.getStringArray(key) : [root.getString(key)];
2238
+ const values = [
2239
+ ...new Set(
2240
+ rawValues.map((v) => v.split(/[ ,]/)).flat().filter(Boolean)
2241
+ )
2242
+ ];
2243
+ if (!values.length) {
2244
+ return void 0;
2245
+ }
2246
+ if (validValues?.length) {
2247
+ for (const value of values) {
2248
+ if (!validValues.includes(value)) {
2249
+ const valid = validValues.map((k) => `'${k}'`).join(", ");
2250
+ throw new Error(
2251
+ `Invalid value '${value}' at '${key}' in 'permissionAttributes' config, valid values are ${valid}`
2252
+ );
2253
+ }
2617
2254
  }
2618
- } catch {
2619
2255
  }
2620
- return void 0;
2621
- };
2622
- let UserTokenHandler = _UserTokenHandler;
2256
+ return values;
2257
+ }
2258
+ function readPermissionNames(externalAccessEntryConfig) {
2259
+ return readStringOrStringArrayFromConfig(
2260
+ externalAccessEntryConfig,
2261
+ "permission"
2262
+ );
2263
+ }
2264
+ function readPermissionAttributes(externalAccessEntryConfig) {
2265
+ const config = externalAccessEntryConfig.getOptionalConfig(
2266
+ "permissionAttribute"
2267
+ );
2268
+ if (!config) {
2269
+ return void 0;
2270
+ }
2271
+ const validKeys = ["action"];
2272
+ for (const key of config.keys()) {
2273
+ if (!validKeys.includes(key)) {
2274
+ const valid = validKeys.map((k) => `'${k}'`).join(", ");
2275
+ throw new Error(
2276
+ `Invalid key '${key}' in 'permissionAttribute' config, expected ${valid}`
2277
+ );
2278
+ }
2279
+ }
2280
+ const action = readStringOrStringArrayFromConfig(config, "action", [
2281
+ "create",
2282
+ "read",
2283
+ "update",
2284
+ "delete"
2285
+ ]);
2286
+ const result = {
2287
+ ...action ? { action } : {}
2288
+ };
2289
+ return Object.keys(result).length ? result : void 0;
2290
+ }
2623
2291
 
2624
- var __accessCheck$3 = (obj, member, msg) => {
2625
- if (!member.has(obj))
2626
- throw TypeError("Cannot " + msg);
2627
- };
2628
- var __privateGet$3 = (obj, member, getter) => {
2629
- __accessCheck$3(obj, member, "read from private field");
2630
- return member.get(obj);
2631
- };
2632
- var __privateAdd$3 = (obj, member, value) => {
2633
- if (member.has(obj))
2634
- throw TypeError("Cannot add the same private member more than once");
2635
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
2636
- };
2637
- var __privateMethod$2 = (obj, member, method) => {
2638
- __accessCheck$3(obj, member, "access private method");
2639
- return method;
2640
- };
2641
- var _entries$1, _doAdd, doAdd_fn;
2642
2292
  class LegacyTokenHandler {
2643
- constructor() {
2644
- __privateAdd$3(this, _doAdd);
2645
- __privateAdd$3(this, _entries$1, []);
2646
- }
2647
- add(options) {
2648
- __privateMethod$2(this, _doAdd, doAdd_fn).call(this, options.getString("secret"), options.getString("subject"));
2293
+ #entries = new Array();
2294
+ add(config) {
2295
+ const allAccessRestrictions = readAccessRestrictionsFromConfig(config);
2296
+ this.#doAdd(
2297
+ config.getString("options.secret"),
2298
+ config.getString("options.subject"),
2299
+ allAccessRestrictions
2300
+ );
2649
2301
  }
2650
2302
  // used only for the old backend.auth.keys array
2651
- addOld(options) {
2652
- __privateMethod$2(this, _doAdd, doAdd_fn).call(this, options.getString("secret"), "external:backstage-plugin");
2303
+ addOld(config) {
2304
+ this.#doAdd(config.getString("secret"), "external:backstage-plugin");
2305
+ }
2306
+ #doAdd(secret, subject, allAccessRestrictions) {
2307
+ if (!secret.match(/^\S+$/)) {
2308
+ throw new Error("Illegal secret, must be a valid base64 string");
2309
+ } else if (!subject.match(/^\S+$/)) {
2310
+ throw new Error("Illegal subject, must be a set of non-space characters");
2311
+ }
2312
+ let key;
2313
+ try {
2314
+ key = jose.base64url.decode(secret);
2315
+ } catch {
2316
+ throw new Error("Illegal secret, must be a valid base64 string");
2317
+ }
2318
+ if (this.#entries.some((e) => e.key === key)) {
2319
+ throw new Error(
2320
+ "Legacy externalAccess token was declared more than once"
2321
+ );
2322
+ }
2323
+ this.#entries.push({
2324
+ key,
2325
+ result: {
2326
+ subject,
2327
+ allAccessRestrictions
2328
+ }
2329
+ });
2653
2330
  }
2654
2331
  async verifyToken(token) {
2655
2332
  try {
@@ -2664,10 +2341,10 @@ class LegacyTokenHandler {
2664
2341
  } catch (e) {
2665
2342
  return void 0;
2666
2343
  }
2667
- for (const entry of __privateGet$3(this, _entries$1)) {
2344
+ for (const { key, result } of this.#entries) {
2668
2345
  try {
2669
- await jose.jwtVerify(token, entry.key);
2670
- return { subject: entry.subject };
2346
+ await jose.jwtVerify(token, key);
2347
+ return result;
2671
2348
  } catch (e) {
2672
2349
  if (e.code !== "ERR_JWS_SIGNATURE_VERIFICATION_FAILED") {
2673
2350
  throw e;
@@ -2677,85 +2354,109 @@ class LegacyTokenHandler {
2677
2354
  return void 0;
2678
2355
  }
2679
2356
  }
2680
- _entries$1 = new WeakMap();
2681
- _doAdd = new WeakSet();
2682
- doAdd_fn = function(secret, subject) {
2683
- if (!secret.match(/^\S+$/)) {
2684
- throw new Error("Illegal secret, must be a valid base64 string");
2685
- }
2686
- let key;
2687
- try {
2688
- key = jose.base64url.decode(secret);
2689
- } catch {
2690
- throw new Error("Illegal secret, must be a valid base64 string");
2691
- }
2692
- if (!subject.match(/^\S+$/)) {
2693
- throw new Error("Illegal subject, must be a set of non-space characters");
2694
- }
2695
- __privateGet$3(this, _entries$1).push({ key, subject });
2696
- };
2697
2357
 
2698
- var __accessCheck$2 = (obj, member, msg) => {
2699
- if (!member.has(obj))
2700
- throw TypeError("Cannot " + msg);
2701
- };
2702
- var __privateGet$2 = (obj, member, getter) => {
2703
- __accessCheck$2(obj, member, "read from private field");
2704
- return member.get(obj);
2705
- };
2706
- var __privateAdd$2 = (obj, member, value) => {
2707
- if (member.has(obj))
2708
- throw TypeError("Cannot add the same private member more than once");
2709
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
2710
- };
2711
- var _entries;
2712
2358
  const MIN_TOKEN_LENGTH = 8;
2713
2359
  class StaticTokenHandler {
2714
- constructor() {
2715
- __privateAdd$2(this, _entries, []);
2716
- }
2717
- add(options) {
2718
- const token = options.getString("token");
2360
+ #entries = /* @__PURE__ */ new Map();
2361
+ add(config) {
2362
+ const token = config.getString("options.token");
2363
+ const subject = config.getString("options.subject");
2364
+ const allAccessRestrictions = readAccessRestrictionsFromConfig(config);
2719
2365
  if (!token.match(/^\S+$/)) {
2720
2366
  throw new Error("Illegal token, must be a set of non-space characters");
2721
- }
2722
- if (token.length < MIN_TOKEN_LENGTH) {
2367
+ } else if (token.length < MIN_TOKEN_LENGTH) {
2723
2368
  throw new Error(
2724
2369
  `Illegal token, must be at least ${MIN_TOKEN_LENGTH} characters length`
2725
2370
  );
2726
- }
2727
- const subject = options.getString("subject");
2728
- if (!subject.match(/^\S+$/)) {
2371
+ } else if (!subject.match(/^\S+$/)) {
2729
2372
  throw new Error("Illegal subject, must be a set of non-space characters");
2373
+ } else if (this.#entries.has(token)) {
2374
+ throw new Error(
2375
+ "Static externalAccess token was declared more than once"
2376
+ );
2730
2377
  }
2731
- __privateGet$2(this, _entries).push({ token, subject });
2378
+ this.#entries.set(token, { subject, allAccessRestrictions });
2732
2379
  }
2733
2380
  async verifyToken(token) {
2734
- const entry = __privateGet$2(this, _entries).find((e) => e.token === token);
2735
- if (!entry) {
2736
- return void 0;
2381
+ return this.#entries.get(token);
2382
+ }
2383
+ }
2384
+
2385
+ class JWKSHandler {
2386
+ #entries = [];
2387
+ add(config) {
2388
+ if (!config.getString("options.url").match(/^\S+$/)) {
2389
+ throw new Error(
2390
+ "Illegal JWKS URL, must be a set of non-space characters"
2391
+ );
2737
2392
  }
2738
- return { subject: entry.subject };
2393
+ const algorithms = readStringOrStringArrayFromConfig(
2394
+ config,
2395
+ "options.algorithm"
2396
+ );
2397
+ const issuers = readStringOrStringArrayFromConfig(config, "options.issuer");
2398
+ const audiences = readStringOrStringArrayFromConfig(
2399
+ config,
2400
+ "options.audience"
2401
+ );
2402
+ const subjectPrefix = config.getOptionalString("options.subjectPrefix");
2403
+ const url = new URL(config.getString("options.url"));
2404
+ const jwks = jose.createRemoteJWKSet(url);
2405
+ const allAccessRestrictions = readAccessRestrictionsFromConfig(config);
2406
+ this.#entries.push({
2407
+ algorithms,
2408
+ audiences,
2409
+ issuers,
2410
+ jwks,
2411
+ subjectPrefix,
2412
+ url,
2413
+ allAccessRestrictions
2414
+ });
2415
+ }
2416
+ async verifyToken(token) {
2417
+ for (const entry of this.#entries) {
2418
+ try {
2419
+ const {
2420
+ payload: { sub }
2421
+ } = await jose.jwtVerify(token, entry.jwks, {
2422
+ algorithms: entry.algorithms,
2423
+ issuer: entry.issuers,
2424
+ audience: entry.audiences
2425
+ });
2426
+ if (sub) {
2427
+ const prefix = entry.subjectPrefix ? `external:${entry.subjectPrefix}:` : "external:";
2428
+ return {
2429
+ subject: `${prefix}${sub}`,
2430
+ allAccessRestrictions: entry.allAccessRestrictions
2431
+ };
2432
+ }
2433
+ } catch {
2434
+ continue;
2435
+ }
2436
+ }
2437
+ return void 0;
2739
2438
  }
2740
2439
  }
2741
- _entries = new WeakMap();
2742
2440
 
2743
2441
  const NEW_CONFIG_KEY = "backend.auth.externalAccess";
2744
2442
  const OLD_CONFIG_KEY = "backend.auth.keys";
2443
+ let loggedDeprecationWarning = false;
2745
2444
  class ExternalTokenHandler {
2746
- constructor(handlers) {
2445
+ constructor(ownPluginId, handlers) {
2446
+ this.ownPluginId = ownPluginId;
2747
2447
  this.handlers = handlers;
2748
2448
  }
2749
2449
  static create(options) {
2750
- var _a, _b;
2751
- const { config, logger } = options;
2450
+ const { ownPluginId, config, logger } = options;
2752
2451
  const staticHandler = new StaticTokenHandler();
2753
2452
  const legacyHandler = new LegacyTokenHandler();
2453
+ const jwksHandler = new JWKSHandler();
2754
2454
  const handlers = {
2755
2455
  static: staticHandler,
2756
- legacy: legacyHandler
2456
+ legacy: legacyHandler,
2457
+ jwks: jwksHandler
2757
2458
  };
2758
- const handlerConfigs = (_a = config.getOptionalConfigArray(NEW_CONFIG_KEY)) != null ? _a : [];
2459
+ const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? [];
2759
2460
  for (const handlerConfig of handlerConfigs) {
2760
2461
  const type = handlerConfig.getString("type");
2761
2462
  const handler = handlers[type];
@@ -2765,10 +2466,11 @@ class ExternalTokenHandler {
2765
2466
  `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`
2766
2467
  );
2767
2468
  }
2768
- handler.add(handlerConfig.getConfig("options"));
2469
+ handler.add(handlerConfig);
2769
2470
  }
2770
- const legacyConfigs = (_b = config.getOptionalConfigArray(OLD_CONFIG_KEY)) != null ? _b : [];
2771
- if (legacyConfigs.length) {
2471
+ const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? [];
2472
+ if (legacyConfigs.length && !loggedDeprecationWarning) {
2473
+ loggedDeprecationWarning = true;
2772
2474
  logger.warn(
2773
2475
  `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`
2774
2476
  );
@@ -2776,13 +2478,29 @@ class ExternalTokenHandler {
2776
2478
  for (const handlerConfig of legacyConfigs) {
2777
2479
  legacyHandler.addOld(handlerConfig);
2778
2480
  }
2779
- return new ExternalTokenHandler(Object.values(handlers));
2481
+ return new ExternalTokenHandler(ownPluginId, Object.values(handlers));
2780
2482
  }
2781
2483
  async verifyToken(token) {
2782
2484
  for (const handler of this.handlers) {
2783
2485
  const result = await handler.verifyToken(token);
2784
2486
  if (result) {
2785
- return result;
2487
+ const { allAccessRestrictions, ...rest } = result;
2488
+ if (allAccessRestrictions) {
2489
+ const accessRestrictions = allAccessRestrictions.get(
2490
+ this.ownPluginId
2491
+ );
2492
+ if (!accessRestrictions) {
2493
+ const valid = [...allAccessRestrictions.keys()].map((k) => `'${k}'`).join(", ");
2494
+ throw new errors.NotAllowedError(
2495
+ `This token's access is restricted to plugin(s) ${valid}`
2496
+ );
2497
+ }
2498
+ return {
2499
+ ...rest,
2500
+ accessRestrictions
2501
+ };
2502
+ }
2503
+ return rest;
2786
2504
  }
2787
2505
  }
2788
2506
  return void 0;
@@ -2803,16 +2521,7 @@ const authServiceFactory = backendPluginApi.createServiceFactory({
2803
2521
  // new auth services in the new backend system.
2804
2522
  tokenManager: backendPluginApi.coreServices.tokenManager
2805
2523
  },
2806
- async createRootContext({ config, logger }) {
2807
- const externalTokens = ExternalTokenHandler.create({
2808
- config,
2809
- logger
2810
- });
2811
- return {
2812
- externalTokens
2813
- };
2814
- },
2815
- async factory({ config, discovery, plugin, tokenManager, logger, database }, { externalTokens }) {
2524
+ async factory({ config, discovery, plugin, tokenManager, logger, database }) {
2816
2525
  const disableDefaultAuthPolicy = Boolean(
2817
2526
  config.getOptionalBoolean(
2818
2527
  "backend.auth.dangerouslyDisableDefaultAuthPolicy"
@@ -2832,6 +2541,11 @@ const authServiceFactory = backendPluginApi.createServiceFactory({
2832
2541
  publicKeyStore,
2833
2542
  discovery
2834
2543
  });
2544
+ const externalTokens = ExternalTokenHandler.create({
2545
+ ownPluginId: plugin.getId(),
2546
+ config,
2547
+ logger
2548
+ });
2835
2549
  return new DefaultAuthService(
2836
2550
  userTokens,
2837
2551
  pluginTokens,
@@ -2848,10 +2562,11 @@ const cacheServiceFactory = backendPluginApi.createServiceFactory({
2848
2562
  service: backendPluginApi.coreServices.cache,
2849
2563
  deps: {
2850
2564
  config: backendPluginApi.coreServices.rootConfig,
2565
+ logger: backendPluginApi.coreServices.rootLogger,
2851
2566
  plugin: backendPluginApi.coreServices.pluginMetadata
2852
2567
  },
2853
- async createRootContext({ config }) {
2854
- return backendCommon.CacheManager.fromConfig(config);
2568
+ async createRootContext({ config, logger }) {
2569
+ return backendCommon.CacheManager.fromConfig(config, { logger });
2855
2570
  },
2856
2571
  async factory({ plugin }, manager) {
2857
2572
  return manager.forPlugin(plugin.getId()).getClient();
@@ -2864,9 +2579,9 @@ const rootConfigServiceFactory = backendPluginApi.createServiceFactory(
2864
2579
  deps: {},
2865
2580
  async factory() {
2866
2581
  const source = configLoader.ConfigSources.default({
2867
- argv: options == null ? void 0 : options.argv,
2868
- remote: options == null ? void 0 : options.remote,
2869
- watch: options == null ? void 0 : options.watch
2582
+ argv: options?.argv,
2583
+ remote: options?.remote,
2584
+ watch: options?.watch
2870
2585
  });
2871
2586
  console.log(`Loading config from ${source}`);
2872
2587
  return await configLoader.ConfigSources.toConfig(source);
@@ -2929,8 +2644,7 @@ class HostDiscovery {
2929
2644
  * path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
2930
2645
  */
2931
2646
  static fromConfig(config, options) {
2932
- var _a;
2933
- const basePath = (_a = options == null ? void 0 : options.basePath) != null ? _a : "/api";
2647
+ const basePath = options?.basePath ?? "/api";
2934
2648
  const externalBaseUrl = config.getString("backend.baseUrl").replace(/\/+$/, "");
2935
2649
  const {
2936
2650
  listen: { host: listenHost = "::", port: listenPort }
@@ -2953,9 +2667,8 @@ class HostDiscovery {
2953
2667
  );
2954
2668
  }
2955
2669
  getTargetFromConfig(pluginId, type) {
2956
- var _a, _b;
2957
- const endpoints = (_a = this.discoveryConfig) == null ? void 0 : _a.getOptionalConfigArray("endpoints");
2958
- const target = (_b = endpoints == null ? void 0 : endpoints.find((endpoint) => endpoint.getStringArray("plugins").includes(pluginId))) == null ? void 0 : _b.get("target");
2670
+ const endpoints = this.discoveryConfig?.getOptionalConfigArray("endpoints");
2671
+ const target = endpoints?.find((endpoint) => endpoint.getStringArray("plugins").includes(pluginId))?.get("target");
2959
2672
  if (!target) {
2960
2673
  const baseUrl = type === "external" ? this.externalBaseUrl : this.internalBaseUrl;
2961
2674
  return `${baseUrl}/${encodeURIComponent(pluginId)}`;
@@ -2989,36 +2702,13 @@ const discoveryServiceFactory = backendPluginApi.createServiceFactory({
2989
2702
  }
2990
2703
  });
2991
2704
 
2992
- var __accessCheck$1 = (obj, member, msg) => {
2993
- if (!member.has(obj))
2994
- throw TypeError("Cannot " + msg);
2995
- };
2996
- var __privateGet$1 = (obj, member, getter) => {
2997
- __accessCheck$1(obj, member, "read from private field");
2998
- return member.get(obj);
2999
- };
3000
- var __privateAdd$1 = (obj, member, value) => {
3001
- if (member.has(obj))
3002
- throw TypeError("Cannot add the same private member more than once");
3003
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
3004
- };
3005
- var __privateSet$1 = (obj, member, value, setter) => {
3006
- __accessCheck$1(obj, member, "write to private field");
3007
- member.set(obj, value);
3008
- return value;
3009
- };
3010
- var __privateMethod$1 = (obj, member, method) => {
3011
- __accessCheck$1(obj, member, "access private method");
3012
- return method;
3013
- };
3014
- var _auth, _discovery, _pluginId, _extractCredentialsFromRequest, extractCredentialsFromRequest_fn, _extractLimitedCredentialsFromRequest, extractLimitedCredentialsFromRequest_fn, _getCredentials, getCredentials_fn, _getLimitedCredentials, getLimitedCredentials_fn, _getCookieOptions, getCookieOptions_fn, _existingCookieExpiration, existingCookieExpiration_fn;
3015
2705
  const FIVE_MINUTES_MS = 5 * 60 * 1e3;
3016
2706
  const BACKSTAGE_AUTH_COOKIE = "backstage-auth";
3017
2707
  function getTokenFromRequest(req) {
3018
2708
  const authHeader = req.headers.authorization;
3019
2709
  if (typeof authHeader === "string") {
3020
2710
  const matches = authHeader.match(/^Bearer[ ]+(\S+)$/i);
3021
- const token = matches == null ? void 0 : matches[1];
2711
+ const token = matches?.[1];
3022
2712
  if (token) {
3023
2713
  return token;
3024
2714
  }
@@ -3042,39 +2732,61 @@ function willExpireSoon(expiresAt) {
3042
2732
  const credentialsSymbol = Symbol("backstage-credentials");
3043
2733
  const limitedCredentialsSymbol = Symbol("backstage-limited-credentials");
3044
2734
  class DefaultHttpAuthService {
2735
+ #auth;
2736
+ #discovery;
2737
+ #pluginId;
3045
2738
  constructor(auth, discovery, pluginId) {
3046
- __privateAdd$1(this, _extractCredentialsFromRequest);
3047
- __privateAdd$1(this, _extractLimitedCredentialsFromRequest);
3048
- __privateAdd$1(this, _getCredentials);
3049
- __privateAdd$1(this, _getLimitedCredentials);
3050
- __privateAdd$1(this, _getCookieOptions);
3051
- __privateAdd$1(this, _existingCookieExpiration);
3052
- __privateAdd$1(this, _auth, void 0);
3053
- __privateAdd$1(this, _discovery, void 0);
3054
- __privateAdd$1(this, _pluginId, void 0);
3055
- __privateSet$1(this, _auth, auth);
3056
- __privateSet$1(this, _discovery, discovery);
3057
- __privateSet$1(this, _pluginId, pluginId);
2739
+ this.#auth = auth;
2740
+ this.#discovery = discovery;
2741
+ this.#pluginId = pluginId;
2742
+ }
2743
+ async #extractCredentialsFromRequest(req) {
2744
+ const token = getTokenFromRequest(req);
2745
+ if (!token) {
2746
+ return await this.#auth.getNoneCredentials();
2747
+ }
2748
+ return await this.#auth.authenticate(token);
2749
+ }
2750
+ async #extractLimitedCredentialsFromRequest(req) {
2751
+ const token = getTokenFromRequest(req);
2752
+ if (token) {
2753
+ return await this.#auth.authenticate(token, {
2754
+ allowLimitedAccess: true
2755
+ });
2756
+ }
2757
+ const cookie = getCookieFromRequest(req);
2758
+ if (cookie) {
2759
+ return await this.#auth.authenticate(cookie, {
2760
+ allowLimitedAccess: true
2761
+ });
2762
+ }
2763
+ return await this.#auth.getNoneCredentials();
2764
+ }
2765
+ async #getCredentials(req) {
2766
+ return req[credentialsSymbol] ??= this.#extractCredentialsFromRequest(req);
2767
+ }
2768
+ async #getLimitedCredentials(req) {
2769
+ return req[limitedCredentialsSymbol] ??= this.#extractLimitedCredentialsFromRequest(req);
3058
2770
  }
3059
2771
  async credentials(req, options) {
3060
- const credentials = (options == null ? void 0 : options.allowLimitedAccess) ? await __privateMethod$1(this, _getLimitedCredentials, getLimitedCredentials_fn).call(this, req) : await __privateMethod$1(this, _getCredentials, getCredentials_fn).call(this, req);
3061
- const allowed = options == null ? void 0 : options.allow;
2772
+ const credentials = options?.allowLimitedAccess ? await this.#getLimitedCredentials(req) : await this.#getCredentials(req);
2773
+ const allowed = options?.allow;
3062
2774
  if (!allowed) {
3063
2775
  return credentials;
3064
2776
  }
3065
- if (__privateGet$1(this, _auth).isPrincipal(credentials, "none")) {
2777
+ if (this.#auth.isPrincipal(credentials, "none")) {
3066
2778
  if (allowed.includes("none")) {
3067
2779
  return credentials;
3068
2780
  }
3069
2781
  throw new errors.AuthenticationError("Missing credentials");
3070
- } else if (__privateGet$1(this, _auth).isPrincipal(credentials, "user")) {
2782
+ } else if (this.#auth.isPrincipal(credentials, "user")) {
3071
2783
  if (allowed.includes("user")) {
3072
2784
  return credentials;
3073
2785
  }
3074
2786
  throw new errors.NotAllowedError(
3075
2787
  `This endpoint does not allow 'user' credentials`
3076
2788
  );
3077
- } else if (__privateGet$1(this, _auth).isPrincipal(credentials, "service")) {
2789
+ } else if (this.#auth.isPrincipal(credentials, "service")) {
3078
2790
  if (allowed.includes("service")) {
3079
2791
  return credentials;
3080
2792
  }
@@ -3091,15 +2803,15 @@ class DefaultHttpAuthService {
3091
2803
  throw new Error("Failed to issue user cookie, headers were already sent");
3092
2804
  }
3093
2805
  let credentials;
3094
- if (options == null ? void 0 : options.credentials) {
3095
- if (__privateGet$1(this, _auth).isPrincipal(options.credentials, "none")) {
2806
+ if (options?.credentials) {
2807
+ if (this.#auth.isPrincipal(options.credentials, "none")) {
3096
2808
  res.clearCookie(
3097
2809
  BACKSTAGE_AUTH_COOKIE,
3098
- await __privateMethod$1(this, _getCookieOptions, getCookieOptions_fn).call(this, res.req)
2810
+ await this.#getCookieOptions(res.req)
3099
2811
  );
3100
2812
  return { expiresAt: /* @__PURE__ */ new Date() };
3101
2813
  }
3102
- if (!__privateGet$1(this, _auth).isPrincipal(options.credentials, "user")) {
2814
+ if (!this.#auth.isPrincipal(options.credentials, "user")) {
3103
2815
  throw new errors.AuthenticationError(
3104
2816
  "Refused to issue cookie for non-user principal"
3105
2817
  );
@@ -3108,99 +2820,60 @@ class DefaultHttpAuthService {
3108
2820
  } else {
3109
2821
  credentials = await this.credentials(res.req, { allow: ["user"] });
3110
2822
  }
3111
- const existingExpiresAt = await __privateMethod$1(this, _existingCookieExpiration, existingCookieExpiration_fn).call(this, res.req);
2823
+ const existingExpiresAt = await this.#existingCookieExpiration(res.req);
3112
2824
  if (existingExpiresAt && !willExpireSoon(existingExpiresAt)) {
3113
2825
  return { expiresAt: existingExpiresAt };
3114
2826
  }
3115
- const { token, expiresAt } = await __privateGet$1(this, _auth).getLimitedUserToken(
2827
+ const { token, expiresAt } = await this.#auth.getLimitedUserToken(
3116
2828
  credentials
3117
2829
  );
3118
2830
  if (!token) {
3119
2831
  throw new Error("User credentials is unexpectedly missing token");
3120
2832
  }
3121
2833
  res.cookie(BACKSTAGE_AUTH_COOKIE, token, {
3122
- ...await __privateMethod$1(this, _getCookieOptions, getCookieOptions_fn).call(this, res.req),
2834
+ ...await this.#getCookieOptions(res.req),
3123
2835
  expires: expiresAt
3124
2836
  });
3125
2837
  return { expiresAt };
3126
2838
  }
3127
- }
3128
- _auth = new WeakMap();
3129
- _discovery = new WeakMap();
3130
- _pluginId = new WeakMap();
3131
- _extractCredentialsFromRequest = new WeakSet();
3132
- extractCredentialsFromRequest_fn = async function(req) {
3133
- const token = getTokenFromRequest(req);
3134
- if (!token) {
3135
- return await __privateGet$1(this, _auth).getNoneCredentials();
3136
- }
3137
- return await __privateGet$1(this, _auth).authenticate(token);
3138
- };
3139
- _extractLimitedCredentialsFromRequest = new WeakSet();
3140
- extractLimitedCredentialsFromRequest_fn = async function(req) {
3141
- const token = getTokenFromRequest(req);
3142
- if (token) {
3143
- return await __privateGet$1(this, _auth).authenticate(token, {
3144
- allowLimitedAccess: true
3145
- });
3146
- }
3147
- const cookie = getCookieFromRequest(req);
3148
- if (cookie) {
3149
- return await __privateGet$1(this, _auth).authenticate(cookie, {
3150
- allowLimitedAccess: true
3151
- });
3152
- }
3153
- return await __privateGet$1(this, _auth).getNoneCredentials();
3154
- };
3155
- _getCredentials = new WeakSet();
3156
- getCredentials_fn = async function(req) {
3157
- var _a;
3158
- return (_a = req[credentialsSymbol]) != null ? _a : req[credentialsSymbol] = __privateMethod$1(this, _extractCredentialsFromRequest, extractCredentialsFromRequest_fn).call(this, req);
3159
- };
3160
- _getLimitedCredentials = new WeakSet();
3161
- getLimitedCredentials_fn = async function(req) {
3162
- var _a;
3163
- return (_a = req[limitedCredentialsSymbol]) != null ? _a : req[limitedCredentialsSymbol] = __privateMethod$1(this, _extractLimitedCredentialsFromRequest, extractLimitedCredentialsFromRequest_fn).call(this, req);
3164
- };
3165
- _getCookieOptions = new WeakSet();
3166
- getCookieOptions_fn = async function(_req) {
3167
- const externalBaseUrlStr = await __privateGet$1(this, _discovery).getExternalBaseUrl(
3168
- __privateGet$1(this, _pluginId)
3169
- );
3170
- const externalBaseUrl = new URL(externalBaseUrlStr);
3171
- const secure = externalBaseUrl.protocol === "https:" || externalBaseUrl.hostname === "localhost";
3172
- return {
3173
- domain: externalBaseUrl.hostname,
3174
- httpOnly: true,
3175
- secure,
3176
- priority: "high",
3177
- sameSite: secure ? "none" : "lax"
3178
- };
3179
- };
3180
- _existingCookieExpiration = new WeakSet();
3181
- existingCookieExpiration_fn = async function(req) {
3182
- const existingCookie = getCookieFromRequest(req);
3183
- if (!existingCookie) {
3184
- return void 0;
3185
- }
3186
- try {
3187
- const existingCredentials = await __privateGet$1(this, _auth).authenticate(
3188
- existingCookie,
3189
- {
3190
- allowLimitedAccess: true
3191
- }
2839
+ async #getCookieOptions(_req) {
2840
+ const externalBaseUrlStr = await this.#discovery.getExternalBaseUrl(
2841
+ this.#pluginId
3192
2842
  );
3193
- if (!__privateGet$1(this, _auth).isPrincipal(existingCredentials, "user")) {
2843
+ const externalBaseUrl = new URL(externalBaseUrlStr);
2844
+ const secure = externalBaseUrl.protocol === "https:" || externalBaseUrl.hostname === "localhost";
2845
+ return {
2846
+ domain: externalBaseUrl.hostname,
2847
+ httpOnly: true,
2848
+ secure,
2849
+ priority: "high",
2850
+ sameSite: secure ? "none" : "lax"
2851
+ };
2852
+ }
2853
+ async #existingCookieExpiration(req) {
2854
+ const existingCookie = getCookieFromRequest(req);
2855
+ if (!existingCookie) {
3194
2856
  return void 0;
3195
2857
  }
3196
- return existingCredentials.expiresAt;
3197
- } catch (error) {
3198
- if (error.name === "AuthenticationError") {
3199
- return void 0;
2858
+ try {
2859
+ const existingCredentials = await this.#auth.authenticate(
2860
+ existingCookie,
2861
+ {
2862
+ allowLimitedAccess: true
2863
+ }
2864
+ );
2865
+ if (!this.#auth.isPrincipal(existingCredentials, "user")) {
2866
+ return void 0;
2867
+ }
2868
+ return existingCredentials.expiresAt;
2869
+ } catch (error) {
2870
+ if (error.name === "AuthenticationError") {
2871
+ return void 0;
2872
+ }
2873
+ throw error;
3200
2874
  }
3201
- throw error;
3202
2875
  }
3203
- };
2876
+ }
3204
2877
  const httpAuthServiceFactory = backendPluginApi.createServiceFactory({
3205
2878
  service: backendPluginApi.coreServices.httpAuth,
3206
2879
  deps: {
@@ -3360,13 +3033,12 @@ const httpRouterServiceFactory = backendPluginApi.createServiceFactory(
3360
3033
  rootHttpRouter,
3361
3034
  lifecycle
3362
3035
  }) {
3363
- var _a;
3364
- if (options == null ? void 0 : options.getPath) {
3036
+ if (options?.getPath) {
3365
3037
  logger.warn(
3366
3038
  `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.`
3367
3039
  );
3368
3040
  }
3369
- const getPath = (_a = options == null ? void 0 : options.getPath) != null ? _a : (id) => `/api/${id}`;
3041
+ const getPath = options?.getPath ?? ((id) => `/api/${id}`);
3370
3042
  const path = getPath(plugin.getId());
3371
3043
  const router = Router__default.default();
3372
3044
  rootHttpRouter.use(path, router);
@@ -3402,6 +3074,63 @@ const identityServiceFactory = backendPluginApi.createServiceFactory(
3402
3074
  })
3403
3075
  );
3404
3076
 
3077
+ class BackendPluginLifecycleImpl {
3078
+ constructor(logger, rootLifecycle, pluginMetadata) {
3079
+ this.logger = logger;
3080
+ this.rootLifecycle = rootLifecycle;
3081
+ this.pluginMetadata = pluginMetadata;
3082
+ }
3083
+ #hasStarted = false;
3084
+ #startupTasks = [];
3085
+ addStartupHook(hook, options) {
3086
+ if (this.#hasStarted) {
3087
+ throw new Error("Attempted to add startup hook after startup");
3088
+ }
3089
+ this.#startupTasks.push({ hook, options });
3090
+ }
3091
+ async startup() {
3092
+ if (this.#hasStarted) {
3093
+ return;
3094
+ }
3095
+ this.#hasStarted = true;
3096
+ this.logger.debug(
3097
+ `Running ${this.#startupTasks.length} plugin startup tasks...`
3098
+ );
3099
+ await Promise.all(
3100
+ this.#startupTasks.map(async ({ hook, options }) => {
3101
+ const logger = options?.logger ?? this.logger;
3102
+ try {
3103
+ await hook();
3104
+ logger.debug(`Plugin startup hook succeeded`);
3105
+ } catch (error) {
3106
+ logger.error(`Plugin startup hook failed, ${error}`);
3107
+ }
3108
+ })
3109
+ );
3110
+ }
3111
+ addShutdownHook(hook, options) {
3112
+ const plugin = this.pluginMetadata.getId();
3113
+ this.rootLifecycle.addShutdownHook(hook, {
3114
+ logger: options?.logger?.child({ plugin }) ?? this.logger
3115
+ });
3116
+ }
3117
+ }
3118
+ const lifecycleServiceFactory = backendPluginApi.createServiceFactory({
3119
+ service: backendPluginApi.coreServices.lifecycle,
3120
+ deps: {
3121
+ logger: backendPluginApi.coreServices.logger,
3122
+ rootLifecycle: backendPluginApi.coreServices.rootLifecycle,
3123
+ pluginMetadata: backendPluginApi.coreServices.pluginMetadata
3124
+ },
3125
+ async factory({ rootLifecycle, logger, pluginMetadata }) {
3126
+ return new BackendPluginLifecycleImpl(
3127
+ logger,
3128
+ rootLifecycle,
3129
+ pluginMetadata
3130
+ );
3131
+ }
3132
+ });
3133
+
3405
3134
  const loggerServiceFactory = backendPluginApi.createServiceFactory({
3406
3135
  service: backendPluginApi.coreServices.logger,
3407
3136
  deps: {
@@ -3430,102 +3159,71 @@ const permissionsServiceFactory = backendPluginApi.createServiceFactory({
3430
3159
  }
3431
3160
  });
3432
3161
 
3433
- var __accessCheck = (obj, member, msg) => {
3434
- if (!member.has(obj))
3435
- throw TypeError("Cannot " + msg);
3436
- };
3437
- var __privateGet = (obj, member, getter) => {
3438
- __accessCheck(obj, member, "read from private field");
3439
- return member.get(obj);
3440
- };
3441
- var __privateAdd = (obj, member, value) => {
3442
- if (member.has(obj))
3443
- throw TypeError("Cannot add the same private member more than once");
3444
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
3445
- };
3446
- var __privateSet = (obj, member, value, setter) => {
3447
- __accessCheck(obj, member, "write to private field");
3448
- member.set(obj, value);
3449
- return value;
3450
- };
3451
- var __privateMethod = (obj, member, method) => {
3452
- __accessCheck(obj, member, "access private method");
3453
- return method;
3454
- };
3455
- var _indexPath, _router, _namedRoutes, _indexRouter, _existingPaths, _findConflictingPath, findConflictingPath_fn;
3456
3162
  function normalizePath(path) {
3457
3163
  return `${trimEnd__default.default(path, "/")}/`;
3458
3164
  }
3459
- const _DefaultRootHttpRouter = class _DefaultRootHttpRouter {
3460
- constructor(indexPath) {
3461
- __privateAdd(this, _findConflictingPath);
3462
- __privateAdd(this, _indexPath, void 0);
3463
- __privateAdd(this, _router, express.Router());
3464
- __privateAdd(this, _namedRoutes, express.Router());
3465
- __privateAdd(this, _indexRouter, express.Router());
3466
- __privateAdd(this, _existingPaths, new Array());
3467
- __privateSet(this, _indexPath, indexPath);
3468
- __privateGet(this, _router).use(__privateGet(this, _namedRoutes));
3469
- __privateGet(this, _router).use("/api/", (_req, _res, next) => {
3470
- next("router");
3471
- });
3472
- if (__privateGet(this, _indexPath)) {
3473
- __privateGet(this, _router).use(__privateGet(this, _indexRouter));
3474
- }
3475
- }
3165
+ class DefaultRootHttpRouter {
3166
+ #indexPath;
3167
+ #router = express.Router();
3168
+ #namedRoutes = express.Router();
3169
+ #indexRouter = express.Router();
3170
+ #existingPaths = new Array();
3476
3171
  static create(options) {
3477
3172
  let indexPath;
3478
- if ((options == null ? void 0 : options.indexPath) === false) {
3173
+ if (options?.indexPath === false) {
3479
3174
  indexPath = void 0;
3480
- } else if ((options == null ? void 0 : options.indexPath) === void 0) {
3175
+ } else if (options?.indexPath === void 0) {
3481
3176
  indexPath = "/api/app";
3482
- } else if ((options == null ? void 0 : options.indexPath) === "") {
3177
+ } else if (options?.indexPath === "") {
3483
3178
  throw new Error("indexPath option may not be an empty string");
3484
3179
  } else {
3485
3180
  indexPath = options.indexPath;
3486
3181
  }
3487
- return new _DefaultRootHttpRouter(indexPath);
3182
+ return new DefaultRootHttpRouter(indexPath);
3183
+ }
3184
+ constructor(indexPath) {
3185
+ this.#indexPath = indexPath;
3186
+ this.#router.use(this.#namedRoutes);
3187
+ this.#router.use("/api/", (_req, _res, next) => {
3188
+ next("router");
3189
+ });
3190
+ if (this.#indexPath) {
3191
+ this.#router.use(this.#indexRouter);
3192
+ }
3488
3193
  }
3489
3194
  use(path, handler) {
3490
3195
  if (path.match(/^[/\s]*$/)) {
3491
3196
  throw new Error(`Root router path may not be empty`);
3492
3197
  }
3493
- const conflictingPath = __privateMethod(this, _findConflictingPath, findConflictingPath_fn).call(this, path);
3198
+ const conflictingPath = this.#findConflictingPath(path);
3494
3199
  if (conflictingPath) {
3495
3200
  throw new Error(
3496
3201
  `Path ${path} conflicts with the existing path ${conflictingPath}`
3497
3202
  );
3498
3203
  }
3499
- __privateGet(this, _existingPaths).push(path);
3500
- __privateGet(this, _namedRoutes).use(path, handler);
3501
- if (__privateGet(this, _indexPath) === path) {
3502
- __privateGet(this, _indexRouter).use(handler);
3204
+ this.#existingPaths.push(path);
3205
+ this.#namedRoutes.use(path, handler);
3206
+ if (this.#indexPath === path) {
3207
+ this.#indexRouter.use(handler);
3503
3208
  }
3504
3209
  }
3505
3210
  handler() {
3506
- return __privateGet(this, _router);
3507
- }
3508
- };
3509
- _indexPath = new WeakMap();
3510
- _router = new WeakMap();
3511
- _namedRoutes = new WeakMap();
3512
- _indexRouter = new WeakMap();
3513
- _existingPaths = new WeakMap();
3514
- _findConflictingPath = new WeakSet();
3515
- findConflictingPath_fn = function(newPath) {
3516
- const normalizedNewPath = normalizePath(newPath);
3517
- for (const path of __privateGet(this, _existingPaths)) {
3518
- const normalizedPath = normalizePath(path);
3519
- if (normalizedPath.startsWith(normalizedNewPath)) {
3520
- return path;
3521
- }
3522
- if (normalizedNewPath.startsWith(normalizedPath)) {
3523
- return path;
3211
+ return this.#router;
3212
+ }
3213
+ #findConflictingPath(newPath) {
3214
+ const normalizedNewPath = normalizePath(newPath);
3215
+ for (const path of this.#existingPaths) {
3216
+ const normalizedPath = normalizePath(path);
3217
+ if (normalizedPath.startsWith(normalizedNewPath)) {
3218
+ return path;
3219
+ }
3220
+ if (normalizedNewPath.startsWith(normalizedPath)) {
3221
+ return path;
3222
+ }
3524
3223
  }
3224
+ return void 0;
3525
3225
  }
3526
- return void 0;
3527
- };
3528
- let DefaultRootHttpRouter = _DefaultRootHttpRouter;
3226
+ }
3529
3227
 
3530
3228
  function defaultConfigure({ applyDefaults }) {
3531
3229
  applyDefaults();
@@ -3539,7 +3237,7 @@ const rootHttpRouterServiceFactory = backendPluginApi.createServiceFactory(
3539
3237
  lifecycle: backendPluginApi.coreServices.rootLifecycle
3540
3238
  },
3541
3239
  async factory({ config, rootLogger, lifecycle }) {
3542
- const { indexPath, configure = defaultConfigure } = options != null ? options : {};
3240
+ const { indexPath, configure = defaultConfigure } = options ?? {};
3543
3241
  const logger = rootLogger.child({ service: "rootHttpRouter" });
3544
3242
  const app = express__default.default();
3545
3243
  const router = DefaultRootHttpRouter.create({ indexPath });
@@ -3575,13 +3273,81 @@ const rootHttpRouterServiceFactory = backendPluginApi.createServiceFactory(
3575
3273
  })
3576
3274
  );
3577
3275
 
3276
+ class BackendLifecycleImpl {
3277
+ constructor(logger) {
3278
+ this.logger = logger;
3279
+ }
3280
+ #hasStarted = false;
3281
+ #startupTasks = [];
3282
+ addStartupHook(hook, options) {
3283
+ if (this.#hasStarted) {
3284
+ throw new Error("Attempted to add startup hook after startup");
3285
+ }
3286
+ this.#startupTasks.push({ hook, options });
3287
+ }
3288
+ async startup() {
3289
+ if (this.#hasStarted) {
3290
+ return;
3291
+ }
3292
+ this.#hasStarted = true;
3293
+ this.logger.debug(`Running ${this.#startupTasks.length} startup tasks...`);
3294
+ await Promise.all(
3295
+ this.#startupTasks.map(async ({ hook, options }) => {
3296
+ const logger = options?.logger ?? this.logger;
3297
+ try {
3298
+ await hook();
3299
+ logger.debug(`Startup hook succeeded`);
3300
+ } catch (error) {
3301
+ logger.error(`Startup hook failed, ${error}`);
3302
+ }
3303
+ })
3304
+ );
3305
+ }
3306
+ #hasShutdown = false;
3307
+ #shutdownTasks = [];
3308
+ addShutdownHook(hook, options) {
3309
+ if (this.#hasShutdown) {
3310
+ throw new Error("Attempted to add shutdown hook after shutdown");
3311
+ }
3312
+ this.#shutdownTasks.push({ hook, options });
3313
+ }
3314
+ async shutdown() {
3315
+ if (this.#hasShutdown) {
3316
+ return;
3317
+ }
3318
+ this.#hasShutdown = true;
3319
+ this.logger.debug(
3320
+ `Running ${this.#shutdownTasks.length} shutdown tasks...`
3321
+ );
3322
+ await Promise.all(
3323
+ this.#shutdownTasks.map(async ({ hook, options }) => {
3324
+ const logger = options?.logger ?? this.logger;
3325
+ try {
3326
+ await hook();
3327
+ logger.debug(`Shutdown hook succeeded`);
3328
+ } catch (error) {
3329
+ logger.error(`Shutdown hook failed, ${error}`);
3330
+ }
3331
+ })
3332
+ );
3333
+ }
3334
+ }
3335
+ const rootLifecycleServiceFactory = backendPluginApi.createServiceFactory({
3336
+ service: backendPluginApi.coreServices.rootLifecycle,
3337
+ deps: {
3338
+ logger: backendPluginApi.coreServices.rootLogger
3339
+ },
3340
+ async factory({ logger }) {
3341
+ return new BackendLifecycleImpl(logger);
3342
+ }
3343
+ });
3344
+
3578
3345
  const rootLoggerServiceFactory = backendPluginApi.createServiceFactory({
3579
3346
  service: backendPluginApi.coreServices.rootLogger,
3580
3347
  deps: {
3581
3348
  config: backendPluginApi.coreServices.rootConfig
3582
3349
  },
3583
3350
  async factory({ config }) {
3584
- var _a;
3585
3351
  const logger = WinstonLogger.create({
3586
3352
  meta: {
3587
3353
  service: "backstage"
@@ -3592,27 +3358,11 @@ const rootLoggerServiceFactory = backendPluginApi.createServiceFactory({
3592
3358
  });
3593
3359
  const secretEnumerator = await createConfigSecretEnumerator({ logger });
3594
3360
  logger.addRedactions(secretEnumerator(config));
3595
- (_a = config.subscribe) == null ? void 0 : _a.call(config, () => logger.addRedactions(secretEnumerator(config)));
3361
+ config.subscribe?.(() => logger.addRedactions(secretEnumerator(config)));
3596
3362
  return logger;
3597
3363
  }
3598
3364
  });
3599
3365
 
3600
- const schedulerServiceFactory = backendPluginApi.createServiceFactory({
3601
- service: backendPluginApi.coreServices.scheduler,
3602
- deps: {
3603
- plugin: backendPluginApi.coreServices.pluginMetadata,
3604
- databaseManager: backendPluginApi.coreServices.database,
3605
- logger: backendPluginApi.coreServices.logger
3606
- },
3607
- async factory({ plugin, databaseManager, logger }) {
3608
- return backendTasks.TaskScheduler.forPlugin({
3609
- pluginId: plugin.getId(),
3610
- databaseManager,
3611
- logger
3612
- });
3613
- }
3614
- });
3615
-
3616
3366
  const tokenManagerServiceFactory = backendPluginApi.createServiceFactory({
3617
3367
  service: backendPluginApi.coreServices.tokenManager,
3618
3368
  deps: {
@@ -3673,6 +3423,22 @@ const userInfoServiceFactory = backendPluginApi.createServiceFactory({
3673
3423
  }
3674
3424
  });
3675
3425
 
3426
+ const schedulerServiceFactory = backendPluginApi.createServiceFactory({
3427
+ service: backendPluginApi.coreServices.scheduler,
3428
+ deps: {
3429
+ plugin: backendPluginApi.coreServices.pluginMetadata,
3430
+ databaseManager: backendPluginApi.coreServices.database,
3431
+ logger: backendPluginApi.coreServices.logger
3432
+ },
3433
+ async factory({ plugin, databaseManager, logger }) {
3434
+ return backendTasks.TaskScheduler.forPlugin({
3435
+ pluginId: plugin.getId(),
3436
+ databaseManager,
3437
+ logger
3438
+ });
3439
+ }
3440
+ });
3441
+
3676
3442
  exports.DefaultRootHttpRouter = DefaultRootHttpRouter;
3677
3443
  exports.HostDiscovery = HostDiscovery;
3678
3444
  exports.MiddlewareFactory = MiddlewareFactory;