@backstage/backend-app-api 0.7.5 → 0.7.6-next.0
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/CHANGELOG.md +21 -8
- package/alpha/package.json +1 -1
- package/dist/alpha.cjs.js +1 -2
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/index.cjs.js +761 -1174
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +51 -14
- package/package.json +9 -9
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
|
|
118
|
+
return this.parent.select(false)?.getOptionalConfig(this.parentKey);
|
|
126
119
|
}
|
|
127
120
|
return this.config;
|
|
128
121
|
}
|
|
129
122
|
has(key) {
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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 = [
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
277
|
+
const host = config?.getOptional("listen.host") ?? DEFAULT_HOST;
|
|
298
278
|
if (typeof host !== "string") {
|
|
299
|
-
config
|
|
279
|
+
config?.getOptionalString("listen.host");
|
|
300
280
|
throw new Error("unreachable");
|
|
301
281
|
}
|
|
302
282
|
return {
|
|
303
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
752
|
-
const
|
|
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
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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
|
-
|
|
843
|
-
const
|
|
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
|
-
|
|
781
|
+
options.format ?? defaultFormatter,
|
|
849
782
|
redacter.format
|
|
850
783
|
),
|
|
851
|
-
transports:
|
|
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
|
|
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] =
|
|
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
|
-
|
|
856
|
+
this.#winston.error(message, meta);
|
|
921
857
|
}
|
|
922
858
|
warn(message, meta) {
|
|
923
|
-
|
|
859
|
+
this.#winston.warn(message, meta);
|
|
924
860
|
}
|
|
925
861
|
info(message, meta) {
|
|
926
|
-
|
|
862
|
+
this.#winston.info(message, meta);
|
|
927
863
|
}
|
|
928
864
|
debug(message, meta) {
|
|
929
|
-
|
|
865
|
+
this.#winston.debug(message, meta);
|
|
930
866
|
}
|
|
931
867
|
child(meta) {
|
|
932
|
-
return new
|
|
868
|
+
return new WinstonLogger(this.#winston.child(meta));
|
|
933
869
|
}
|
|
934
870
|
addRedactions(redactions) {
|
|
935
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
1167
|
-
if (
|
|
900
|
+
const cycleKey = this.#getCycleKey(path);
|
|
901
|
+
if (this.#cycleKeys.has(cycleKey)) {
|
|
1168
902
|
return false;
|
|
1169
903
|
}
|
|
1170
|
-
|
|
904
|
+
this.#cycleKeys.add(cycleKey);
|
|
1171
905
|
return true;
|
|
1172
906
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
|
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
|
|
943
|
+
for (const node of this.#nodes.values()) {
|
|
1214
944
|
const unsatisfied = Array.from(node.consumes).filter(
|
|
1215
|
-
(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(
|
|
1236
|
-
for (const startNode of
|
|
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 =
|
|
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 =
|
|
1008
|
+
const allProvided = this.#allProvided;
|
|
1279
1009
|
const producedSoFar = /* @__PURE__ */ new Set();
|
|
1280
|
-
const waiting = new Set(
|
|
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
|
|
1067
|
+
factory: async () => ({ getId: () => options?.pluginId })
|
|
1364
1068
|
})
|
|
1365
1069
|
);
|
|
1366
|
-
|
|
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
|
|
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(
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
1422
|
-
|
|
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
|
|
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
|
-
|
|
1437
|
-
|
|
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 =
|
|
1187
|
+
let existing = this.#rootServiceImplementations.get(factory);
|
|
1441
1188
|
if (!existing) {
|
|
1442
|
-
|
|
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
|
-
|
|
1203
|
+
this.#rootServiceImplementations.set(factory, existing);
|
|
1457
1204
|
}
|
|
1458
1205
|
return existing;
|
|
1459
1206
|
}
|
|
1460
|
-
let implementation =
|
|
1207
|
+
let implementation = this.#implementations.get(factory);
|
|
1461
1208
|
if (!implementation) {
|
|
1462
|
-
|
|
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
|
-
|
|
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
|
|
1259
|
+
const logger = rootLogger?.child({ type: "initialization" });
|
|
1571
1260
|
const starting = new Set(pluginIds);
|
|
1572
1261
|
const started = /* @__PURE__ */ new Set();
|
|
1573
|
-
logger
|
|
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
|
|
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
|
|
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
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
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 (
|
|
1342
|
+
if (this.#startPromise) {
|
|
1651
1343
|
throw new Error("feature can not be added after the backend has started");
|
|
1652
1344
|
}
|
|
1653
|
-
|
|
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 (
|
|
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
|
-
|
|
1675
|
-
await
|
|
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
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
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
|
-
|
|
1720
|
-
|
|
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
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
);
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
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
|
-
`
|
|
1443
|
+
`Module '${r.moduleId}' for plugin '${r.pluginId}' is already registered`
|
|
1777
1444
|
);
|
|
1778
1445
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
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
|
-
}
|
|
1785
|
-
}
|
|
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
1451
|
}
|
|
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
1452
|
}
|
|
1812
1453
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
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
|
|
1838
|
-
if (
|
|
1839
|
-
|
|
1840
|
-
|
|
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
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
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
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
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
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
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
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
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
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
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
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1582
|
+
this.#initializer.add(feature.then((f) => unwrapFeature(f.default)));
|
|
1944
1583
|
} else {
|
|
1945
|
-
|
|
1584
|
+
this.#initializer.add(unwrapFeature(feature));
|
|
1946
1585
|
}
|
|
1947
1586
|
}
|
|
1948
1587
|
async start() {
|
|
1949
|
-
await
|
|
1588
|
+
await this.#initializer.start();
|
|
1950
1589
|
}
|
|
1951
1590
|
async stop() {
|
|
1952
|
-
await
|
|
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 (!
|
|
1656
|
+
if (!database.migrations?.skip) {
|
|
2020
1657
|
await applyDatabaseMigrations(client);
|
|
2021
1658
|
}
|
|
2022
1659
|
return new DatabaseKeyStore(client, logger);
|
|
@@ -2105,20 +1742,6 @@ function toInternalBackstageCredentials(credentials) {
|
|
|
2105
1742
|
return internalCredentials;
|
|
2106
1743
|
}
|
|
2107
1744
|
|
|
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
1745
|
class DefaultAuthService {
|
|
2123
1746
|
constructor(userTokenHandler, pluginTokenHandler, externalTokenHandler, tokenManager, pluginId, disableDefaultAuthPolicy, publicKeyStore) {
|
|
2124
1747
|
this.userTokenHandler = userTokenHandler;
|
|
@@ -2128,7 +1751,6 @@ class DefaultAuthService {
|
|
|
2128
1751
|
this.pluginId = pluginId;
|
|
2129
1752
|
this.disableDefaultAuthPolicy = disableDefaultAuthPolicy;
|
|
2130
1753
|
this.publicKeyStore = publicKeyStore;
|
|
2131
|
-
__privateAdd$6(this, _getJwtExpiration);
|
|
2132
1754
|
}
|
|
2133
1755
|
// allowLimitedAccess is currently ignored, since we currently always use the full user tokens
|
|
2134
1756
|
async authenticate(token) {
|
|
@@ -2146,7 +1768,7 @@ class DefaultAuthService {
|
|
|
2146
1768
|
return createCredentialsWithUserPrincipal(
|
|
2147
1769
|
userResult2.userEntityRef,
|
|
2148
1770
|
pluginResult.limitedUserToken,
|
|
2149
|
-
|
|
1771
|
+
this.#getJwtExpiration(pluginResult.limitedUserToken)
|
|
2150
1772
|
);
|
|
2151
1773
|
}
|
|
2152
1774
|
return createCredentialsWithServicePrincipal(pluginResult.subject);
|
|
@@ -2156,7 +1778,7 @@ class DefaultAuthService {
|
|
|
2156
1778
|
return createCredentialsWithUserPrincipal(
|
|
2157
1779
|
userResult.userEntityRef,
|
|
2158
1780
|
token,
|
|
2159
|
-
|
|
1781
|
+
this.#getJwtExpiration(token)
|
|
2160
1782
|
);
|
|
2161
1783
|
}
|
|
2162
1784
|
const externalResult = await this.externalTokenHandler.verifyToken(token);
|
|
@@ -2244,49 +1866,29 @@ class DefaultAuthService {
|
|
|
2244
1866
|
const { keys } = await this.publicKeyStore.listKeys();
|
|
2245
1867
|
return { keys: keys.map(({ key }) => key) };
|
|
2246
1868
|
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
1869
|
+
#getJwtExpiration(token) {
|
|
1870
|
+
const { exp } = jose.decodeJwt(token);
|
|
1871
|
+
if (!exp) {
|
|
1872
|
+
throw new errors.AuthenticationError("User token is missing expiration");
|
|
1873
|
+
}
|
|
1874
|
+
return new Date(exp * 1e3);
|
|
2253
1875
|
}
|
|
2254
|
-
|
|
2255
|
-
};
|
|
1876
|
+
}
|
|
2256
1877
|
|
|
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
1878
|
const CLOCK_MARGIN_S = 10;
|
|
2277
1879
|
class JwksClient {
|
|
2278
1880
|
constructor(getEndpoint) {
|
|
2279
1881
|
this.getEndpoint = getEndpoint;
|
|
2280
|
-
__privateAdd$5(this, _keyStore, void 0);
|
|
2281
|
-
__privateAdd$5(this, _keyStoreUpdated, 0);
|
|
2282
1882
|
}
|
|
1883
|
+
#keyStore;
|
|
1884
|
+
#keyStoreUpdated = 0;
|
|
2283
1885
|
get getKey() {
|
|
2284
|
-
if (!
|
|
1886
|
+
if (!this.#keyStore) {
|
|
2285
1887
|
throw new errors.AuthenticationError(
|
|
2286
1888
|
"refreshKeyStore must be called before jwksClient.getKey"
|
|
2287
1889
|
);
|
|
2288
1890
|
}
|
|
2289
|
-
return
|
|
1891
|
+
return this.#keyStore;
|
|
2290
1892
|
}
|
|
2291
1893
|
/**
|
|
2292
1894
|
* If the last keystore refresh is stale, update the keystore URL to the latest
|
|
@@ -2296,9 +1898,9 @@ class JwksClient {
|
|
|
2296
1898
|
const header = await jose.decodeProtectedHeader(rawJwtToken);
|
|
2297
1899
|
let keyStoreHasKey;
|
|
2298
1900
|
try {
|
|
2299
|
-
if (
|
|
1901
|
+
if (this.#keyStore) {
|
|
2300
1902
|
const [_, rawPayload, rawSignature] = rawJwtToken.split(".");
|
|
2301
|
-
keyStoreHasKey = await
|
|
1903
|
+
keyStoreHasKey = await this.#keyStore(header, {
|
|
2302
1904
|
payload: rawPayload,
|
|
2303
1905
|
signature: rawSignature
|
|
2304
1906
|
});
|
|
@@ -2306,23 +1908,15 @@ class JwksClient {
|
|
|
2306
1908
|
} catch (error) {
|
|
2307
1909
|
keyStoreHasKey = false;
|
|
2308
1910
|
}
|
|
2309
|
-
const issuedAfterLastRefresh =
|
|
2310
|
-
if (!
|
|
1911
|
+
const issuedAfterLastRefresh = payload?.iat && payload.iat > this.#keyStoreUpdated - CLOCK_MARGIN_S;
|
|
1912
|
+
if (!this.#keyStore || !keyStoreHasKey && issuedAfterLastRefresh) {
|
|
2311
1913
|
const endpoint = await this.getEndpoint();
|
|
2312
|
-
|
|
2313
|
-
|
|
1914
|
+
this.#keyStore = jose.createRemoteJWKSet(endpoint);
|
|
1915
|
+
this.#keyStoreUpdated = Date.now() / 1e3;
|
|
2314
1916
|
}
|
|
2315
1917
|
}
|
|
2316
1918
|
}
|
|
2317
|
-
_keyStore = new WeakMap();
|
|
2318
|
-
_keyStoreUpdated = new WeakMap();
|
|
2319
1919
|
|
|
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
1920
|
const KEY_EXPIRATION_MARGIN_FACTOR = 3;
|
|
2327
1921
|
const SECONDS_IN_MS = 1e3;
|
|
2328
1922
|
const ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;
|
|
@@ -2334,21 +1928,20 @@ class PluginTokenHandler {
|
|
|
2334
1928
|
this.keyDurationSeconds = keyDurationSeconds;
|
|
2335
1929
|
this.algorithm = algorithm;
|
|
2336
1930
|
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
1931
|
}
|
|
1932
|
+
privateKeyPromise;
|
|
1933
|
+
keyExpiry;
|
|
1934
|
+
jwksMap = /* @__PURE__ */ new Map();
|
|
1935
|
+
// Tracking state for isTargetPluginSupported
|
|
1936
|
+
supportedTargetPlugins = /* @__PURE__ */ new Set();
|
|
1937
|
+
targetPluginInflightChecks = /* @__PURE__ */ new Map();
|
|
2344
1938
|
static create(options) {
|
|
2345
|
-
var _a;
|
|
2346
1939
|
return new PluginTokenHandler(
|
|
2347
1940
|
options.logger,
|
|
2348
1941
|
options.ownPluginId,
|
|
2349
1942
|
options.publicKeyStore,
|
|
2350
1943
|
Math.round(types.durationToMilliseconds(options.keyDuration) / 1e3),
|
|
2351
|
-
|
|
1944
|
+
options.algorithm ?? "ES256",
|
|
2352
1945
|
options.discovery
|
|
2353
1946
|
);
|
|
2354
1947
|
}
|
|
@@ -2396,7 +1989,7 @@ class PluginTokenHandler {
|
|
|
2396
1989
|
ourExp,
|
|
2397
1990
|
Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS)
|
|
2398
1991
|
) : ourExp;
|
|
2399
|
-
const claims = { sub, aud, iat, exp, obo: onBehalfOf
|
|
1992
|
+
const claims = { sub, aud, iat, exp, obo: onBehalfOf?.token };
|
|
2400
1993
|
const token = await new jose.SignJWT(claims).setProtectedHeader({
|
|
2401
1994
|
typ: pluginAuthNode.tokenTypes.plugin.typParam,
|
|
2402
1995
|
alg: this.algorithm,
|
|
@@ -2502,34 +2095,19 @@ class PluginTokenHandler {
|
|
|
2502
2095
|
}
|
|
2503
2096
|
}
|
|
2504
2097
|
|
|
2505
|
-
|
|
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 {
|
|
2098
|
+
class UserTokenHandler {
|
|
2520
2099
|
constructor(jwksClient) {
|
|
2521
2100
|
this.jwksClient = jwksClient;
|
|
2522
|
-
__privateAdd$4(this, _getTokenVerificationOptions);
|
|
2523
2101
|
}
|
|
2524
2102
|
static create(options) {
|
|
2525
2103
|
const jwksClient = new JwksClient(async () => {
|
|
2526
2104
|
const url = await options.discovery.getBaseUrl("auth");
|
|
2527
2105
|
return new URL(`${url}/.well-known/jwks.json`);
|
|
2528
2106
|
});
|
|
2529
|
-
return new
|
|
2107
|
+
return new UserTokenHandler(jwksClient);
|
|
2530
2108
|
}
|
|
2531
2109
|
async verifyToken(token) {
|
|
2532
|
-
const verifyOpts =
|
|
2110
|
+
const verifyOpts = this.#getTokenVerificationOptions(token);
|
|
2533
2111
|
if (!verifyOpts) {
|
|
2534
2112
|
return void 0;
|
|
2535
2113
|
}
|
|
@@ -2547,6 +2125,31 @@ const _UserTokenHandler = class _UserTokenHandler {
|
|
|
2547
2125
|
}
|
|
2548
2126
|
return { userEntityRef };
|
|
2549
2127
|
}
|
|
2128
|
+
#getTokenVerificationOptions(token) {
|
|
2129
|
+
try {
|
|
2130
|
+
const { typ } = jose.decodeProtectedHeader(token);
|
|
2131
|
+
if (typ === pluginAuthNode.tokenTypes.user.typParam) {
|
|
2132
|
+
return {
|
|
2133
|
+
requiredClaims: ["iat", "exp", "sub"],
|
|
2134
|
+
typ: pluginAuthNode.tokenTypes.user.typParam
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
if (typ === pluginAuthNode.tokenTypes.limitedUser.typParam) {
|
|
2138
|
+
return {
|
|
2139
|
+
requiredClaims: ["iat", "exp", "sub"],
|
|
2140
|
+
typ: pluginAuthNode.tokenTypes.limitedUser.typParam
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
const { aud } = jose.decodeJwt(token);
|
|
2144
|
+
if (aud === pluginAuthNode.tokenTypes.user.audClaim) {
|
|
2145
|
+
return {
|
|
2146
|
+
audience: pluginAuthNode.tokenTypes.user.audClaim
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
} catch {
|
|
2150
|
+
}
|
|
2151
|
+
return void 0;
|
|
2152
|
+
}
|
|
2550
2153
|
createLimitedUserToken(backstageToken) {
|
|
2551
2154
|
const [headerRaw, payloadRaw] = backstageToken.split(".");
|
|
2552
2155
|
const header = JSON.parse(
|
|
@@ -2592,64 +2195,31 @@ const _UserTokenHandler = class _UserTokenHandler {
|
|
|
2592
2195
|
return false;
|
|
2593
2196
|
}
|
|
2594
2197
|
}
|
|
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
|
-
};
|
|
2605
|
-
}
|
|
2606
|
-
if (typ === pluginAuthNode.tokenTypes.limitedUser.typParam) {
|
|
2607
|
-
return {
|
|
2608
|
-
requiredClaims: ["iat", "exp", "sub"],
|
|
2609
|
-
typ: pluginAuthNode.tokenTypes.limitedUser.typParam
|
|
2610
|
-
};
|
|
2611
|
-
}
|
|
2612
|
-
const { aud } = jose.decodeJwt(token);
|
|
2613
|
-
if (aud === pluginAuthNode.tokenTypes.user.audClaim) {
|
|
2614
|
-
return {
|
|
2615
|
-
audience: pluginAuthNode.tokenTypes.user.audClaim
|
|
2616
|
-
};
|
|
2617
|
-
}
|
|
2618
|
-
} catch {
|
|
2619
|
-
}
|
|
2620
|
-
return void 0;
|
|
2621
|
-
};
|
|
2622
|
-
let UserTokenHandler = _UserTokenHandler;
|
|
2198
|
+
}
|
|
2623
2199
|
|
|
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
2200
|
class LegacyTokenHandler {
|
|
2643
|
-
|
|
2644
|
-
__privateAdd$3(this, _doAdd);
|
|
2645
|
-
__privateAdd$3(this, _entries$1, []);
|
|
2646
|
-
}
|
|
2201
|
+
#entries = [];
|
|
2647
2202
|
add(options) {
|
|
2648
|
-
|
|
2203
|
+
this.#doAdd(options.getString("secret"), options.getString("subject"));
|
|
2649
2204
|
}
|
|
2650
2205
|
// used only for the old backend.auth.keys array
|
|
2651
2206
|
addOld(options) {
|
|
2652
|
-
|
|
2207
|
+
this.#doAdd(options.getString("secret"), "external:backstage-plugin");
|
|
2208
|
+
}
|
|
2209
|
+
#doAdd(secret, subject) {
|
|
2210
|
+
if (!secret.match(/^\S+$/)) {
|
|
2211
|
+
throw new Error("Illegal secret, must be a valid base64 string");
|
|
2212
|
+
}
|
|
2213
|
+
let key;
|
|
2214
|
+
try {
|
|
2215
|
+
key = jose.base64url.decode(secret);
|
|
2216
|
+
} catch {
|
|
2217
|
+
throw new Error("Illegal secret, must be a valid base64 string");
|
|
2218
|
+
}
|
|
2219
|
+
if (!subject.match(/^\S+$/)) {
|
|
2220
|
+
throw new Error("Illegal subject, must be a set of non-space characters");
|
|
2221
|
+
}
|
|
2222
|
+
this.#entries.push({ key, subject });
|
|
2653
2223
|
}
|
|
2654
2224
|
async verifyToken(token) {
|
|
2655
2225
|
try {
|
|
@@ -2664,7 +2234,7 @@ class LegacyTokenHandler {
|
|
|
2664
2234
|
} catch (e) {
|
|
2665
2235
|
return void 0;
|
|
2666
2236
|
}
|
|
2667
|
-
for (const entry of
|
|
2237
|
+
for (const entry of this.#entries) {
|
|
2668
2238
|
try {
|
|
2669
2239
|
await jose.jwtVerify(token, entry.key);
|
|
2670
2240
|
return { subject: entry.subject };
|
|
@@ -2677,43 +2247,10 @@ class LegacyTokenHandler {
|
|
|
2677
2247
|
return void 0;
|
|
2678
2248
|
}
|
|
2679
2249
|
}
|
|
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
2250
|
|
|
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
2251
|
const MIN_TOKEN_LENGTH = 8;
|
|
2713
2252
|
class StaticTokenHandler {
|
|
2714
|
-
|
|
2715
|
-
__privateAdd$2(this, _entries, []);
|
|
2716
|
-
}
|
|
2253
|
+
#entries = [];
|
|
2717
2254
|
add(options) {
|
|
2718
2255
|
const token = options.getString("token");
|
|
2719
2256
|
if (!token.match(/^\S+$/)) {
|
|
@@ -2728,17 +2265,16 @@ class StaticTokenHandler {
|
|
|
2728
2265
|
if (!subject.match(/^\S+$/)) {
|
|
2729
2266
|
throw new Error("Illegal subject, must be a set of non-space characters");
|
|
2730
2267
|
}
|
|
2731
|
-
|
|
2268
|
+
this.#entries.push({ token, subject });
|
|
2732
2269
|
}
|
|
2733
2270
|
async verifyToken(token) {
|
|
2734
|
-
const entry =
|
|
2271
|
+
const entry = this.#entries.find((e) => e.token === token);
|
|
2735
2272
|
if (!entry) {
|
|
2736
2273
|
return void 0;
|
|
2737
2274
|
}
|
|
2738
2275
|
return { subject: entry.subject };
|
|
2739
2276
|
}
|
|
2740
2277
|
}
|
|
2741
|
-
_entries = new WeakMap();
|
|
2742
2278
|
|
|
2743
2279
|
const NEW_CONFIG_KEY = "backend.auth.externalAccess";
|
|
2744
2280
|
const OLD_CONFIG_KEY = "backend.auth.keys";
|
|
@@ -2747,7 +2283,6 @@ class ExternalTokenHandler {
|
|
|
2747
2283
|
this.handlers = handlers;
|
|
2748
2284
|
}
|
|
2749
2285
|
static create(options) {
|
|
2750
|
-
var _a, _b;
|
|
2751
2286
|
const { config, logger } = options;
|
|
2752
2287
|
const staticHandler = new StaticTokenHandler();
|
|
2753
2288
|
const legacyHandler = new LegacyTokenHandler();
|
|
@@ -2755,7 +2290,7 @@ class ExternalTokenHandler {
|
|
|
2755
2290
|
static: staticHandler,
|
|
2756
2291
|
legacy: legacyHandler
|
|
2757
2292
|
};
|
|
2758
|
-
const handlerConfigs =
|
|
2293
|
+
const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? [];
|
|
2759
2294
|
for (const handlerConfig of handlerConfigs) {
|
|
2760
2295
|
const type = handlerConfig.getString("type");
|
|
2761
2296
|
const handler = handlers[type];
|
|
@@ -2767,7 +2302,7 @@ class ExternalTokenHandler {
|
|
|
2767
2302
|
}
|
|
2768
2303
|
handler.add(handlerConfig.getConfig("options"));
|
|
2769
2304
|
}
|
|
2770
|
-
const legacyConfigs =
|
|
2305
|
+
const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? [];
|
|
2771
2306
|
if (legacyConfigs.length) {
|
|
2772
2307
|
logger.warn(
|
|
2773
2308
|
`DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`
|
|
@@ -2848,10 +2383,11 @@ const cacheServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
2848
2383
|
service: backendPluginApi.coreServices.cache,
|
|
2849
2384
|
deps: {
|
|
2850
2385
|
config: backendPluginApi.coreServices.rootConfig,
|
|
2386
|
+
logger: backendPluginApi.coreServices.rootLogger,
|
|
2851
2387
|
plugin: backendPluginApi.coreServices.pluginMetadata
|
|
2852
2388
|
},
|
|
2853
|
-
async createRootContext({ config }) {
|
|
2854
|
-
return backendCommon.CacheManager.fromConfig(config);
|
|
2389
|
+
async createRootContext({ config, logger }) {
|
|
2390
|
+
return backendCommon.CacheManager.fromConfig(config, { logger });
|
|
2855
2391
|
},
|
|
2856
2392
|
async factory({ plugin }, manager) {
|
|
2857
2393
|
return manager.forPlugin(plugin.getId()).getClient();
|
|
@@ -2864,9 +2400,9 @@ const rootConfigServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
2864
2400
|
deps: {},
|
|
2865
2401
|
async factory() {
|
|
2866
2402
|
const source = configLoader.ConfigSources.default({
|
|
2867
|
-
argv: options
|
|
2868
|
-
remote: options
|
|
2869
|
-
watch: options
|
|
2403
|
+
argv: options?.argv,
|
|
2404
|
+
remote: options?.remote,
|
|
2405
|
+
watch: options?.watch
|
|
2870
2406
|
});
|
|
2871
2407
|
console.log(`Loading config from ${source}`);
|
|
2872
2408
|
return await configLoader.ConfigSources.toConfig(source);
|
|
@@ -2929,8 +2465,7 @@ class HostDiscovery {
|
|
|
2929
2465
|
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
|
|
2930
2466
|
*/
|
|
2931
2467
|
static fromConfig(config, options) {
|
|
2932
|
-
|
|
2933
|
-
const basePath = (_a = options == null ? void 0 : options.basePath) != null ? _a : "/api";
|
|
2468
|
+
const basePath = options?.basePath ?? "/api";
|
|
2934
2469
|
const externalBaseUrl = config.getString("backend.baseUrl").replace(/\/+$/, "");
|
|
2935
2470
|
const {
|
|
2936
2471
|
listen: { host: listenHost = "::", port: listenPort }
|
|
@@ -2953,9 +2488,8 @@ class HostDiscovery {
|
|
|
2953
2488
|
);
|
|
2954
2489
|
}
|
|
2955
2490
|
getTargetFromConfig(pluginId, type) {
|
|
2956
|
-
|
|
2957
|
-
const
|
|
2958
|
-
const target = (_b = endpoints == null ? void 0 : endpoints.find((endpoint) => endpoint.getStringArray("plugins").includes(pluginId))) == null ? void 0 : _b.get("target");
|
|
2491
|
+
const endpoints = this.discoveryConfig?.getOptionalConfigArray("endpoints");
|
|
2492
|
+
const target = endpoints?.find((endpoint) => endpoint.getStringArray("plugins").includes(pluginId))?.get("target");
|
|
2959
2493
|
if (!target) {
|
|
2960
2494
|
const baseUrl = type === "external" ? this.externalBaseUrl : this.internalBaseUrl;
|
|
2961
2495
|
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
|
|
@@ -2989,36 +2523,13 @@ const discoveryServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
2989
2523
|
}
|
|
2990
2524
|
});
|
|
2991
2525
|
|
|
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
2526
|
const FIVE_MINUTES_MS = 5 * 60 * 1e3;
|
|
3016
2527
|
const BACKSTAGE_AUTH_COOKIE = "backstage-auth";
|
|
3017
2528
|
function getTokenFromRequest(req) {
|
|
3018
2529
|
const authHeader = req.headers.authorization;
|
|
3019
2530
|
if (typeof authHeader === "string") {
|
|
3020
2531
|
const matches = authHeader.match(/^Bearer[ ]+(\S+)$/i);
|
|
3021
|
-
const token = matches
|
|
2532
|
+
const token = matches?.[1];
|
|
3022
2533
|
if (token) {
|
|
3023
2534
|
return token;
|
|
3024
2535
|
}
|
|
@@ -3042,39 +2553,61 @@ function willExpireSoon(expiresAt) {
|
|
|
3042
2553
|
const credentialsSymbol = Symbol("backstage-credentials");
|
|
3043
2554
|
const limitedCredentialsSymbol = Symbol("backstage-limited-credentials");
|
|
3044
2555
|
class DefaultHttpAuthService {
|
|
2556
|
+
#auth;
|
|
2557
|
+
#discovery;
|
|
2558
|
+
#pluginId;
|
|
3045
2559
|
constructor(auth, discovery, pluginId) {
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
2560
|
+
this.#auth = auth;
|
|
2561
|
+
this.#discovery = discovery;
|
|
2562
|
+
this.#pluginId = pluginId;
|
|
2563
|
+
}
|
|
2564
|
+
async #extractCredentialsFromRequest(req) {
|
|
2565
|
+
const token = getTokenFromRequest(req);
|
|
2566
|
+
if (!token) {
|
|
2567
|
+
return await this.#auth.getNoneCredentials();
|
|
2568
|
+
}
|
|
2569
|
+
return await this.#auth.authenticate(token);
|
|
2570
|
+
}
|
|
2571
|
+
async #extractLimitedCredentialsFromRequest(req) {
|
|
2572
|
+
const token = getTokenFromRequest(req);
|
|
2573
|
+
if (token) {
|
|
2574
|
+
return await this.#auth.authenticate(token, {
|
|
2575
|
+
allowLimitedAccess: true
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
const cookie = getCookieFromRequest(req);
|
|
2579
|
+
if (cookie) {
|
|
2580
|
+
return await this.#auth.authenticate(cookie, {
|
|
2581
|
+
allowLimitedAccess: true
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
return await this.#auth.getNoneCredentials();
|
|
2585
|
+
}
|
|
2586
|
+
async #getCredentials(req) {
|
|
2587
|
+
return req[credentialsSymbol] ??= this.#extractCredentialsFromRequest(req);
|
|
2588
|
+
}
|
|
2589
|
+
async #getLimitedCredentials(req) {
|
|
2590
|
+
return req[limitedCredentialsSymbol] ??= this.#extractLimitedCredentialsFromRequest(req);
|
|
3058
2591
|
}
|
|
3059
2592
|
async credentials(req, options) {
|
|
3060
|
-
const credentials =
|
|
3061
|
-
const allowed = options
|
|
2593
|
+
const credentials = options?.allowLimitedAccess ? await this.#getLimitedCredentials(req) : await this.#getCredentials(req);
|
|
2594
|
+
const allowed = options?.allow;
|
|
3062
2595
|
if (!allowed) {
|
|
3063
2596
|
return credentials;
|
|
3064
2597
|
}
|
|
3065
|
-
if (
|
|
2598
|
+
if (this.#auth.isPrincipal(credentials, "none")) {
|
|
3066
2599
|
if (allowed.includes("none")) {
|
|
3067
2600
|
return credentials;
|
|
3068
2601
|
}
|
|
3069
2602
|
throw new errors.AuthenticationError("Missing credentials");
|
|
3070
|
-
} else if (
|
|
2603
|
+
} else if (this.#auth.isPrincipal(credentials, "user")) {
|
|
3071
2604
|
if (allowed.includes("user")) {
|
|
3072
2605
|
return credentials;
|
|
3073
2606
|
}
|
|
3074
2607
|
throw new errors.NotAllowedError(
|
|
3075
2608
|
`This endpoint does not allow 'user' credentials`
|
|
3076
2609
|
);
|
|
3077
|
-
} else if (
|
|
2610
|
+
} else if (this.#auth.isPrincipal(credentials, "service")) {
|
|
3078
2611
|
if (allowed.includes("service")) {
|
|
3079
2612
|
return credentials;
|
|
3080
2613
|
}
|
|
@@ -3091,15 +2624,15 @@ class DefaultHttpAuthService {
|
|
|
3091
2624
|
throw new Error("Failed to issue user cookie, headers were already sent");
|
|
3092
2625
|
}
|
|
3093
2626
|
let credentials;
|
|
3094
|
-
if (options
|
|
3095
|
-
if (
|
|
2627
|
+
if (options?.credentials) {
|
|
2628
|
+
if (this.#auth.isPrincipal(options.credentials, "none")) {
|
|
3096
2629
|
res.clearCookie(
|
|
3097
2630
|
BACKSTAGE_AUTH_COOKIE,
|
|
3098
|
-
await
|
|
2631
|
+
await this.#getCookieOptions(res.req)
|
|
3099
2632
|
);
|
|
3100
2633
|
return { expiresAt: /* @__PURE__ */ new Date() };
|
|
3101
2634
|
}
|
|
3102
|
-
if (!
|
|
2635
|
+
if (!this.#auth.isPrincipal(options.credentials, "user")) {
|
|
3103
2636
|
throw new errors.AuthenticationError(
|
|
3104
2637
|
"Refused to issue cookie for non-user principal"
|
|
3105
2638
|
);
|
|
@@ -3108,99 +2641,60 @@ class DefaultHttpAuthService {
|
|
|
3108
2641
|
} else {
|
|
3109
2642
|
credentials = await this.credentials(res.req, { allow: ["user"] });
|
|
3110
2643
|
}
|
|
3111
|
-
const existingExpiresAt = await
|
|
2644
|
+
const existingExpiresAt = await this.#existingCookieExpiration(res.req);
|
|
3112
2645
|
if (existingExpiresAt && !willExpireSoon(existingExpiresAt)) {
|
|
3113
2646
|
return { expiresAt: existingExpiresAt };
|
|
3114
2647
|
}
|
|
3115
|
-
const { token, expiresAt } = await
|
|
2648
|
+
const { token, expiresAt } = await this.#auth.getLimitedUserToken(
|
|
3116
2649
|
credentials
|
|
3117
2650
|
);
|
|
3118
2651
|
if (!token) {
|
|
3119
2652
|
throw new Error("User credentials is unexpectedly missing token");
|
|
3120
2653
|
}
|
|
3121
2654
|
res.cookie(BACKSTAGE_AUTH_COOKIE, token, {
|
|
3122
|
-
...await
|
|
2655
|
+
...await this.#getCookieOptions(res.req),
|
|
3123
2656
|
expires: expiresAt
|
|
3124
2657
|
});
|
|
3125
2658
|
return { expiresAt };
|
|
3126
2659
|
}
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
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
|
-
}
|
|
2660
|
+
async #getCookieOptions(_req) {
|
|
2661
|
+
const externalBaseUrlStr = await this.#discovery.getExternalBaseUrl(
|
|
2662
|
+
this.#pluginId
|
|
3192
2663
|
);
|
|
3193
|
-
|
|
2664
|
+
const externalBaseUrl = new URL(externalBaseUrlStr);
|
|
2665
|
+
const secure = externalBaseUrl.protocol === "https:" || externalBaseUrl.hostname === "localhost";
|
|
2666
|
+
return {
|
|
2667
|
+
domain: externalBaseUrl.hostname,
|
|
2668
|
+
httpOnly: true,
|
|
2669
|
+
secure,
|
|
2670
|
+
priority: "high",
|
|
2671
|
+
sameSite: secure ? "none" : "lax"
|
|
2672
|
+
};
|
|
2673
|
+
}
|
|
2674
|
+
async #existingCookieExpiration(req) {
|
|
2675
|
+
const existingCookie = getCookieFromRequest(req);
|
|
2676
|
+
if (!existingCookie) {
|
|
3194
2677
|
return void 0;
|
|
3195
2678
|
}
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
2679
|
+
try {
|
|
2680
|
+
const existingCredentials = await this.#auth.authenticate(
|
|
2681
|
+
existingCookie,
|
|
2682
|
+
{
|
|
2683
|
+
allowLimitedAccess: true
|
|
2684
|
+
}
|
|
2685
|
+
);
|
|
2686
|
+
if (!this.#auth.isPrincipal(existingCredentials, "user")) {
|
|
2687
|
+
return void 0;
|
|
2688
|
+
}
|
|
2689
|
+
return existingCredentials.expiresAt;
|
|
2690
|
+
} catch (error) {
|
|
2691
|
+
if (error.name === "AuthenticationError") {
|
|
2692
|
+
return void 0;
|
|
2693
|
+
}
|
|
2694
|
+
throw error;
|
|
3200
2695
|
}
|
|
3201
|
-
throw error;
|
|
3202
2696
|
}
|
|
3203
|
-
}
|
|
2697
|
+
}
|
|
3204
2698
|
const httpAuthServiceFactory = backendPluginApi.createServiceFactory({
|
|
3205
2699
|
service: backendPluginApi.coreServices.httpAuth,
|
|
3206
2700
|
deps: {
|
|
@@ -3360,13 +2854,12 @@ const httpRouterServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3360
2854
|
rootHttpRouter,
|
|
3361
2855
|
lifecycle
|
|
3362
2856
|
}) {
|
|
3363
|
-
|
|
3364
|
-
if (options == null ? void 0 : options.getPath) {
|
|
2857
|
+
if (options?.getPath) {
|
|
3365
2858
|
logger.warn(
|
|
3366
2859
|
`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
2860
|
);
|
|
3368
2861
|
}
|
|
3369
|
-
const getPath =
|
|
2862
|
+
const getPath = options?.getPath ?? ((id) => `/api/${id}`);
|
|
3370
2863
|
const path = getPath(plugin.getId());
|
|
3371
2864
|
const router = Router__default.default();
|
|
3372
2865
|
rootHttpRouter.use(path, router);
|
|
@@ -3402,6 +2895,63 @@ const identityServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3402
2895
|
})
|
|
3403
2896
|
);
|
|
3404
2897
|
|
|
2898
|
+
class BackendPluginLifecycleImpl {
|
|
2899
|
+
constructor(logger, rootLifecycle, pluginMetadata) {
|
|
2900
|
+
this.logger = logger;
|
|
2901
|
+
this.rootLifecycle = rootLifecycle;
|
|
2902
|
+
this.pluginMetadata = pluginMetadata;
|
|
2903
|
+
}
|
|
2904
|
+
#hasStarted = false;
|
|
2905
|
+
#startupTasks = [];
|
|
2906
|
+
addStartupHook(hook, options) {
|
|
2907
|
+
if (this.#hasStarted) {
|
|
2908
|
+
throw new Error("Attempted to add startup hook after startup");
|
|
2909
|
+
}
|
|
2910
|
+
this.#startupTasks.push({ hook, options });
|
|
2911
|
+
}
|
|
2912
|
+
async startup() {
|
|
2913
|
+
if (this.#hasStarted) {
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
this.#hasStarted = true;
|
|
2917
|
+
this.logger.debug(
|
|
2918
|
+
`Running ${this.#startupTasks.length} plugin startup tasks...`
|
|
2919
|
+
);
|
|
2920
|
+
await Promise.all(
|
|
2921
|
+
this.#startupTasks.map(async ({ hook, options }) => {
|
|
2922
|
+
const logger = options?.logger ?? this.logger;
|
|
2923
|
+
try {
|
|
2924
|
+
await hook();
|
|
2925
|
+
logger.debug(`Plugin startup hook succeeded`);
|
|
2926
|
+
} catch (error) {
|
|
2927
|
+
logger.error(`Plugin startup hook failed, ${error}`);
|
|
2928
|
+
}
|
|
2929
|
+
})
|
|
2930
|
+
);
|
|
2931
|
+
}
|
|
2932
|
+
addShutdownHook(hook, options) {
|
|
2933
|
+
const plugin = this.pluginMetadata.getId();
|
|
2934
|
+
this.rootLifecycle.addShutdownHook(hook, {
|
|
2935
|
+
logger: options?.logger?.child({ plugin }) ?? this.logger
|
|
2936
|
+
});
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
const lifecycleServiceFactory = backendPluginApi.createServiceFactory({
|
|
2940
|
+
service: backendPluginApi.coreServices.lifecycle,
|
|
2941
|
+
deps: {
|
|
2942
|
+
logger: backendPluginApi.coreServices.logger,
|
|
2943
|
+
rootLifecycle: backendPluginApi.coreServices.rootLifecycle,
|
|
2944
|
+
pluginMetadata: backendPluginApi.coreServices.pluginMetadata
|
|
2945
|
+
},
|
|
2946
|
+
async factory({ rootLifecycle, logger, pluginMetadata }) {
|
|
2947
|
+
return new BackendPluginLifecycleImpl(
|
|
2948
|
+
logger,
|
|
2949
|
+
rootLifecycle,
|
|
2950
|
+
pluginMetadata
|
|
2951
|
+
);
|
|
2952
|
+
}
|
|
2953
|
+
});
|
|
2954
|
+
|
|
3405
2955
|
const loggerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3406
2956
|
service: backendPluginApi.coreServices.logger,
|
|
3407
2957
|
deps: {
|
|
@@ -3430,102 +2980,71 @@ const permissionsServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
3430
2980
|
}
|
|
3431
2981
|
});
|
|
3432
2982
|
|
|
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
2983
|
function normalizePath(path) {
|
|
3457
2984
|
return `${trimEnd__default.default(path, "/")}/`;
|
|
3458
2985
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
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
|
-
}
|
|
2986
|
+
class DefaultRootHttpRouter {
|
|
2987
|
+
#indexPath;
|
|
2988
|
+
#router = express.Router();
|
|
2989
|
+
#namedRoutes = express.Router();
|
|
2990
|
+
#indexRouter = express.Router();
|
|
2991
|
+
#existingPaths = new Array();
|
|
3476
2992
|
static create(options) {
|
|
3477
2993
|
let indexPath;
|
|
3478
|
-
if (
|
|
2994
|
+
if (options?.indexPath === false) {
|
|
3479
2995
|
indexPath = void 0;
|
|
3480
|
-
} else if (
|
|
2996
|
+
} else if (options?.indexPath === void 0) {
|
|
3481
2997
|
indexPath = "/api/app";
|
|
3482
|
-
} else if (
|
|
2998
|
+
} else if (options?.indexPath === "") {
|
|
3483
2999
|
throw new Error("indexPath option may not be an empty string");
|
|
3484
3000
|
} else {
|
|
3485
3001
|
indexPath = options.indexPath;
|
|
3486
3002
|
}
|
|
3487
|
-
return new
|
|
3003
|
+
return new DefaultRootHttpRouter(indexPath);
|
|
3004
|
+
}
|
|
3005
|
+
constructor(indexPath) {
|
|
3006
|
+
this.#indexPath = indexPath;
|
|
3007
|
+
this.#router.use(this.#namedRoutes);
|
|
3008
|
+
this.#router.use("/api/", (_req, _res, next) => {
|
|
3009
|
+
next("router");
|
|
3010
|
+
});
|
|
3011
|
+
if (this.#indexPath) {
|
|
3012
|
+
this.#router.use(this.#indexRouter);
|
|
3013
|
+
}
|
|
3488
3014
|
}
|
|
3489
3015
|
use(path, handler) {
|
|
3490
3016
|
if (path.match(/^[/\s]*$/)) {
|
|
3491
3017
|
throw new Error(`Root router path may not be empty`);
|
|
3492
3018
|
}
|
|
3493
|
-
const conflictingPath =
|
|
3019
|
+
const conflictingPath = this.#findConflictingPath(path);
|
|
3494
3020
|
if (conflictingPath) {
|
|
3495
3021
|
throw new Error(
|
|
3496
3022
|
`Path ${path} conflicts with the existing path ${conflictingPath}`
|
|
3497
3023
|
);
|
|
3498
3024
|
}
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
if (
|
|
3502
|
-
|
|
3025
|
+
this.#existingPaths.push(path);
|
|
3026
|
+
this.#namedRoutes.use(path, handler);
|
|
3027
|
+
if (this.#indexPath === path) {
|
|
3028
|
+
this.#indexRouter.use(handler);
|
|
3503
3029
|
}
|
|
3504
3030
|
}
|
|
3505
3031
|
handler() {
|
|
3506
|
-
return
|
|
3507
|
-
}
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
const normalizedPath = normalizePath(path);
|
|
3519
|
-
if (normalizedPath.startsWith(normalizedNewPath)) {
|
|
3520
|
-
return path;
|
|
3521
|
-
}
|
|
3522
|
-
if (normalizedNewPath.startsWith(normalizedPath)) {
|
|
3523
|
-
return path;
|
|
3032
|
+
return this.#router;
|
|
3033
|
+
}
|
|
3034
|
+
#findConflictingPath(newPath) {
|
|
3035
|
+
const normalizedNewPath = normalizePath(newPath);
|
|
3036
|
+
for (const path of this.#existingPaths) {
|
|
3037
|
+
const normalizedPath = normalizePath(path);
|
|
3038
|
+
if (normalizedPath.startsWith(normalizedNewPath)) {
|
|
3039
|
+
return path;
|
|
3040
|
+
}
|
|
3041
|
+
if (normalizedNewPath.startsWith(normalizedPath)) {
|
|
3042
|
+
return path;
|
|
3043
|
+
}
|
|
3524
3044
|
}
|
|
3045
|
+
return void 0;
|
|
3525
3046
|
}
|
|
3526
|
-
|
|
3527
|
-
};
|
|
3528
|
-
let DefaultRootHttpRouter = _DefaultRootHttpRouter;
|
|
3047
|
+
}
|
|
3529
3048
|
|
|
3530
3049
|
function defaultConfigure({ applyDefaults }) {
|
|
3531
3050
|
applyDefaults();
|
|
@@ -3539,7 +3058,7 @@ const rootHttpRouterServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3539
3058
|
lifecycle: backendPluginApi.coreServices.rootLifecycle
|
|
3540
3059
|
},
|
|
3541
3060
|
async factory({ config, rootLogger, lifecycle }) {
|
|
3542
|
-
const { indexPath, configure = defaultConfigure } = options
|
|
3061
|
+
const { indexPath, configure = defaultConfigure } = options ?? {};
|
|
3543
3062
|
const logger = rootLogger.child({ service: "rootHttpRouter" });
|
|
3544
3063
|
const app = express__default.default();
|
|
3545
3064
|
const router = DefaultRootHttpRouter.create({ indexPath });
|
|
@@ -3575,13 +3094,81 @@ const rootHttpRouterServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3575
3094
|
})
|
|
3576
3095
|
);
|
|
3577
3096
|
|
|
3097
|
+
class BackendLifecycleImpl {
|
|
3098
|
+
constructor(logger) {
|
|
3099
|
+
this.logger = logger;
|
|
3100
|
+
}
|
|
3101
|
+
#hasStarted = false;
|
|
3102
|
+
#startupTasks = [];
|
|
3103
|
+
addStartupHook(hook, options) {
|
|
3104
|
+
if (this.#hasStarted) {
|
|
3105
|
+
throw new Error("Attempted to add startup hook after startup");
|
|
3106
|
+
}
|
|
3107
|
+
this.#startupTasks.push({ hook, options });
|
|
3108
|
+
}
|
|
3109
|
+
async startup() {
|
|
3110
|
+
if (this.#hasStarted) {
|
|
3111
|
+
return;
|
|
3112
|
+
}
|
|
3113
|
+
this.#hasStarted = true;
|
|
3114
|
+
this.logger.debug(`Running ${this.#startupTasks.length} startup tasks...`);
|
|
3115
|
+
await Promise.all(
|
|
3116
|
+
this.#startupTasks.map(async ({ hook, options }) => {
|
|
3117
|
+
const logger = options?.logger ?? this.logger;
|
|
3118
|
+
try {
|
|
3119
|
+
await hook();
|
|
3120
|
+
logger.debug(`Startup hook succeeded`);
|
|
3121
|
+
} catch (error) {
|
|
3122
|
+
logger.error(`Startup hook failed, ${error}`);
|
|
3123
|
+
}
|
|
3124
|
+
})
|
|
3125
|
+
);
|
|
3126
|
+
}
|
|
3127
|
+
#hasShutdown = false;
|
|
3128
|
+
#shutdownTasks = [];
|
|
3129
|
+
addShutdownHook(hook, options) {
|
|
3130
|
+
if (this.#hasShutdown) {
|
|
3131
|
+
throw new Error("Attempted to add shutdown hook after shutdown");
|
|
3132
|
+
}
|
|
3133
|
+
this.#shutdownTasks.push({ hook, options });
|
|
3134
|
+
}
|
|
3135
|
+
async shutdown() {
|
|
3136
|
+
if (this.#hasShutdown) {
|
|
3137
|
+
return;
|
|
3138
|
+
}
|
|
3139
|
+
this.#hasShutdown = true;
|
|
3140
|
+
this.logger.debug(
|
|
3141
|
+
`Running ${this.#shutdownTasks.length} shutdown tasks...`
|
|
3142
|
+
);
|
|
3143
|
+
await Promise.all(
|
|
3144
|
+
this.#shutdownTasks.map(async ({ hook, options }) => {
|
|
3145
|
+
const logger = options?.logger ?? this.logger;
|
|
3146
|
+
try {
|
|
3147
|
+
await hook();
|
|
3148
|
+
logger.debug(`Shutdown hook succeeded`);
|
|
3149
|
+
} catch (error) {
|
|
3150
|
+
logger.error(`Shutdown hook failed, ${error}`);
|
|
3151
|
+
}
|
|
3152
|
+
})
|
|
3153
|
+
);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
const rootLifecycleServiceFactory = backendPluginApi.createServiceFactory({
|
|
3157
|
+
service: backendPluginApi.coreServices.rootLifecycle,
|
|
3158
|
+
deps: {
|
|
3159
|
+
logger: backendPluginApi.coreServices.rootLogger
|
|
3160
|
+
},
|
|
3161
|
+
async factory({ logger }) {
|
|
3162
|
+
return new BackendLifecycleImpl(logger);
|
|
3163
|
+
}
|
|
3164
|
+
});
|
|
3165
|
+
|
|
3578
3166
|
const rootLoggerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3579
3167
|
service: backendPluginApi.coreServices.rootLogger,
|
|
3580
3168
|
deps: {
|
|
3581
3169
|
config: backendPluginApi.coreServices.rootConfig
|
|
3582
3170
|
},
|
|
3583
3171
|
async factory({ config }) {
|
|
3584
|
-
var _a;
|
|
3585
3172
|
const logger = WinstonLogger.create({
|
|
3586
3173
|
meta: {
|
|
3587
3174
|
service: "backstage"
|
|
@@ -3592,27 +3179,11 @@ const rootLoggerServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
3592
3179
|
});
|
|
3593
3180
|
const secretEnumerator = await createConfigSecretEnumerator({ logger });
|
|
3594
3181
|
logger.addRedactions(secretEnumerator(config));
|
|
3595
|
-
|
|
3182
|
+
config.subscribe?.(() => logger.addRedactions(secretEnumerator(config)));
|
|
3596
3183
|
return logger;
|
|
3597
3184
|
}
|
|
3598
3185
|
});
|
|
3599
3186
|
|
|
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
3187
|
const tokenManagerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3617
3188
|
service: backendPluginApi.coreServices.tokenManager,
|
|
3618
3189
|
deps: {
|
|
@@ -3673,6 +3244,22 @@ const userInfoServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
3673
3244
|
}
|
|
3674
3245
|
});
|
|
3675
3246
|
|
|
3247
|
+
const schedulerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3248
|
+
service: backendPluginApi.coreServices.scheduler,
|
|
3249
|
+
deps: {
|
|
3250
|
+
plugin: backendPluginApi.coreServices.pluginMetadata,
|
|
3251
|
+
databaseManager: backendPluginApi.coreServices.database,
|
|
3252
|
+
logger: backendPluginApi.coreServices.logger
|
|
3253
|
+
},
|
|
3254
|
+
async factory({ plugin, databaseManager, logger }) {
|
|
3255
|
+
return backendTasks.TaskScheduler.forPlugin({
|
|
3256
|
+
pluginId: plugin.getId(),
|
|
3257
|
+
databaseManager,
|
|
3258
|
+
logger
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
3262
|
+
|
|
3676
3263
|
exports.DefaultRootHttpRouter = DefaultRootHttpRouter;
|
|
3677
3264
|
exports.HostDiscovery = HostDiscovery;
|
|
3678
3265
|
exports.MiddlewareFactory = MiddlewareFactory;
|