@backstage/backend-app-api 0.7.4 → 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 -2
- 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 +769 -1190
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +51 -14
- package/package.json +10 -9
package/dist/index.cjs.js
CHANGED
|
@@ -20,6 +20,7 @@ var minimatch = require('minimatch');
|
|
|
20
20
|
var errors = require('@backstage/errors');
|
|
21
21
|
var crypto = require('crypto');
|
|
22
22
|
var winston = require('winston');
|
|
23
|
+
var tripleBeam = require('triple-beam');
|
|
23
24
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
24
25
|
var alpha = require('@backstage/backend-plugin-api/alpha');
|
|
25
26
|
var luxon = require('luxon');
|
|
@@ -72,22 +73,16 @@ var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
|
72
73
|
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
73
74
|
var trimEnd__default = /*#__PURE__*/_interopDefaultCompat(trimEnd);
|
|
74
75
|
|
|
75
|
-
var __defProp$1 = Object.defineProperty;
|
|
76
|
-
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
77
|
-
var __publicField$1 = (obj, key, value) => {
|
|
78
|
-
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
79
|
-
return value;
|
|
80
|
-
};
|
|
81
76
|
class ObservableConfigProxy {
|
|
82
77
|
constructor(parent, parentKey) {
|
|
83
78
|
this.parent = parent;
|
|
84
79
|
this.parentKey = parentKey;
|
|
85
|
-
__publicField$1(this, "config", new config.ConfigReader({}));
|
|
86
|
-
__publicField$1(this, "subscribers", []);
|
|
87
80
|
if (parent && !parentKey) {
|
|
88
81
|
throw new Error("parentKey is required if parent is set");
|
|
89
82
|
}
|
|
90
83
|
}
|
|
84
|
+
config = new config.ConfigReader({});
|
|
85
|
+
subscribers = [];
|
|
91
86
|
setConfig(config) {
|
|
92
87
|
if (this.parent) {
|
|
93
88
|
throw new Error("immutable");
|
|
@@ -116,36 +111,31 @@ class ObservableConfigProxy {
|
|
|
116
111
|
};
|
|
117
112
|
}
|
|
118
113
|
select(required) {
|
|
119
|
-
var _a;
|
|
120
114
|
if (this.parent && this.parentKey) {
|
|
121
115
|
if (required) {
|
|
122
116
|
return this.parent.select(true).getConfig(this.parentKey);
|
|
123
117
|
}
|
|
124
|
-
return
|
|
118
|
+
return this.parent.select(false)?.getOptionalConfig(this.parentKey);
|
|
125
119
|
}
|
|
126
120
|
return this.config;
|
|
127
121
|
}
|
|
128
122
|
has(key) {
|
|
129
|
-
|
|
130
|
-
return (_b = (_a = this.select(false)) == null ? void 0 : _a.has(key)) != null ? _b : false;
|
|
123
|
+
return this.select(false)?.has(key) ?? false;
|
|
131
124
|
}
|
|
132
125
|
keys() {
|
|
133
|
-
|
|
134
|
-
return (_b = (_a = this.select(false)) == null ? void 0 : _a.keys()) != null ? _b : [];
|
|
126
|
+
return this.select(false)?.keys() ?? [];
|
|
135
127
|
}
|
|
136
128
|
get(key) {
|
|
137
129
|
return this.select(true).get(key);
|
|
138
130
|
}
|
|
139
131
|
getOptional(key) {
|
|
140
|
-
|
|
141
|
-
return (_a = this.select(false)) == null ? void 0 : _a.getOptional(key);
|
|
132
|
+
return this.select(false)?.getOptional(key);
|
|
142
133
|
}
|
|
143
134
|
getConfig(key) {
|
|
144
135
|
return new ObservableConfigProxy(this, key);
|
|
145
136
|
}
|
|
146
137
|
getOptionalConfig(key) {
|
|
147
|
-
|
|
148
|
-
if ((_a = this.select(false)) == null ? void 0 : _a.has(key)) {
|
|
138
|
+
if (this.select(false)?.has(key)) {
|
|
149
139
|
return new ObservableConfigProxy(this, key);
|
|
150
140
|
}
|
|
151
141
|
return void 0;
|
|
@@ -154,36 +144,31 @@ class ObservableConfigProxy {
|
|
|
154
144
|
return this.select(true).getConfigArray(key);
|
|
155
145
|
}
|
|
156
146
|
getOptionalConfigArray(key) {
|
|
157
|
-
|
|
158
|
-
return (_a = this.select(false)) == null ? void 0 : _a.getOptionalConfigArray(key);
|
|
147
|
+
return this.select(false)?.getOptionalConfigArray(key);
|
|
159
148
|
}
|
|
160
149
|
getNumber(key) {
|
|
161
150
|
return this.select(true).getNumber(key);
|
|
162
151
|
}
|
|
163
152
|
getOptionalNumber(key) {
|
|
164
|
-
|
|
165
|
-
return (_a = this.select(false)) == null ? void 0 : _a.getOptionalNumber(key);
|
|
153
|
+
return this.select(false)?.getOptionalNumber(key);
|
|
166
154
|
}
|
|
167
155
|
getBoolean(key) {
|
|
168
156
|
return this.select(true).getBoolean(key);
|
|
169
157
|
}
|
|
170
158
|
getOptionalBoolean(key) {
|
|
171
|
-
|
|
172
|
-
return (_a = this.select(false)) == null ? void 0 : _a.getOptionalBoolean(key);
|
|
159
|
+
return this.select(false)?.getOptionalBoolean(key);
|
|
173
160
|
}
|
|
174
161
|
getString(key) {
|
|
175
162
|
return this.select(true).getString(key);
|
|
176
163
|
}
|
|
177
164
|
getOptionalString(key) {
|
|
178
|
-
|
|
179
|
-
return (_a = this.select(false)) == null ? void 0 : _a.getOptionalString(key);
|
|
165
|
+
return this.select(false)?.getOptionalString(key);
|
|
180
166
|
}
|
|
181
167
|
getStringArray(key) {
|
|
182
168
|
return this.select(true).getStringArray(key);
|
|
183
169
|
}
|
|
184
170
|
getOptionalStringArray(key) {
|
|
185
|
-
|
|
186
|
-
return (_a = this.select(false)) == null ? void 0 : _a.getOptionalStringArray(key);
|
|
171
|
+
return this.select(false)?.getOptionalStringArray(key);
|
|
187
172
|
}
|
|
188
173
|
}
|
|
189
174
|
|
|
@@ -197,16 +182,14 @@ function isValidUrl(url) {
|
|
|
197
182
|
}
|
|
198
183
|
|
|
199
184
|
async function createConfigSecretEnumerator(options) {
|
|
200
|
-
var _a;
|
|
201
185
|
const { logger, dir = process.cwd() } = options;
|
|
202
186
|
const { packages } = await getPackages.getPackages(dir);
|
|
203
|
-
const schema =
|
|
187
|
+
const schema = options.schema ?? await configLoader.loadConfigSchema({
|
|
204
188
|
dependencies: packages.map((p) => p.packageJson.name)
|
|
205
189
|
});
|
|
206
190
|
return (config) => {
|
|
207
|
-
var _a2;
|
|
208
191
|
const [secretsData] = schema.process(
|
|
209
|
-
[{ data:
|
|
192
|
+
[{ data: config.getOptional() ?? {}, context: "schema-enumerator" }],
|
|
210
193
|
{
|
|
211
194
|
visibility: ["secret"],
|
|
212
195
|
ignoreSchemaErrors: true
|
|
@@ -224,9 +207,8 @@ async function createConfigSecretEnumerator(options) {
|
|
|
224
207
|
};
|
|
225
208
|
}
|
|
226
209
|
async function loadBackendConfig(options) {
|
|
227
|
-
var _a, _b;
|
|
228
210
|
const args = parseArgs__default.default(options.argv);
|
|
229
|
-
const configTargets = [
|
|
211
|
+
const configTargets = [args.config ?? []].flat().map((arg) => isValidUrl(arg) ? { url: arg } : { path: path.resolve(arg) });
|
|
230
212
|
const paths = cliCommon.findPaths(__dirname);
|
|
231
213
|
let currentCancelFunc = void 0;
|
|
232
214
|
const config$1 = new ObservableConfigProxy();
|
|
@@ -234,7 +216,7 @@ async function loadBackendConfig(options) {
|
|
|
234
216
|
configRoot: paths.targetRoot,
|
|
235
217
|
configTargets,
|
|
236
218
|
remote: options.remote,
|
|
237
|
-
watch:
|
|
219
|
+
watch: options.watch ?? true ? {
|
|
238
220
|
onChange(newConfigs) {
|
|
239
221
|
console.info(
|
|
240
222
|
`Reloaded config from ${newConfigs.map((c) => c.context).join(", ")}`
|
|
@@ -276,8 +258,7 @@ function readHttpServerOptions(config) {
|
|
|
276
258
|
};
|
|
277
259
|
}
|
|
278
260
|
function readHttpListenOptions(config) {
|
|
279
|
-
|
|
280
|
-
const listen = config == null ? void 0 : config.getOptional("listen");
|
|
261
|
+
const listen = config?.getOptional("listen");
|
|
281
262
|
if (typeof listen === "string") {
|
|
282
263
|
const parts = String(listen).split(":");
|
|
283
264
|
const port = parseInt(parts[parts.length - 1], 10);
|
|
@@ -293,18 +274,18 @@ function readHttpListenOptions(config) {
|
|
|
293
274
|
`Unable to parse listen address ${listen}, expected <port> or <host>:<port>`
|
|
294
275
|
);
|
|
295
276
|
}
|
|
296
|
-
const host =
|
|
277
|
+
const host = config?.getOptional("listen.host") ?? DEFAULT_HOST;
|
|
297
278
|
if (typeof host !== "string") {
|
|
298
|
-
config
|
|
279
|
+
config?.getOptionalString("listen.host");
|
|
299
280
|
throw new Error("unreachable");
|
|
300
281
|
}
|
|
301
282
|
return {
|
|
302
|
-
port:
|
|
283
|
+
port: config?.getOptionalNumber("listen.port") ?? DEFAULT_PORT,
|
|
303
284
|
host
|
|
304
285
|
};
|
|
305
286
|
}
|
|
306
287
|
function readHttpsOptions(config) {
|
|
307
|
-
const https = config
|
|
288
|
+
const https = config?.getOptional("https");
|
|
308
289
|
if (https === true) {
|
|
309
290
|
const baseUrl = config.getString("baseUrl");
|
|
310
291
|
let hostname;
|
|
@@ -315,7 +296,7 @@ function readHttpsOptions(config) {
|
|
|
315
296
|
}
|
|
316
297
|
return { certificate: { type: "generated", hostname } };
|
|
317
298
|
}
|
|
318
|
-
const cc = config
|
|
299
|
+
const cc = config?.getOptionalConfig("https");
|
|
319
300
|
if (!cc) {
|
|
320
301
|
return void 0;
|
|
321
302
|
}
|
|
@@ -518,7 +499,7 @@ function readHelmetOptions(config) {
|
|
|
518
499
|
};
|
|
519
500
|
}
|
|
520
501
|
function readCspDirectives(config) {
|
|
521
|
-
const cc = config
|
|
502
|
+
const cc = config?.getOptionalConfig("csp");
|
|
522
503
|
if (!cc) {
|
|
523
504
|
return void 0;
|
|
524
505
|
}
|
|
@@ -550,7 +531,7 @@ function applyCspDirectives(directives) {
|
|
|
550
531
|
}
|
|
551
532
|
|
|
552
533
|
function readCorsOptions(config) {
|
|
553
|
-
const cc = config
|
|
534
|
+
const cc = config?.getOptionalConfig("cors");
|
|
554
535
|
if (!cc) {
|
|
555
536
|
return { origin: false };
|
|
556
537
|
}
|
|
@@ -589,7 +570,7 @@ function createCorsOriginMatcher(allowedOriginPatterns) {
|
|
|
589
570
|
return (origin, callback) => {
|
|
590
571
|
return callback(
|
|
591
572
|
null,
|
|
592
|
-
allowedOriginMatchers.some((pattern) => pattern.match(origin
|
|
573
|
+
allowedOriginMatchers.some((pattern) => pattern.match(origin ?? ""))
|
|
593
574
|
);
|
|
594
575
|
};
|
|
595
576
|
}
|
|
@@ -615,37 +596,18 @@ function applyInternalErrorFilter(error, logger) {
|
|
|
615
596
|
return error;
|
|
616
597
|
}
|
|
617
598
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
};
|
|
622
|
-
var __privateGet$c = (obj, member, getter) => {
|
|
623
|
-
__accessCheck$e(obj, member, "read from private field");
|
|
624
|
-
return member.get(obj);
|
|
625
|
-
};
|
|
626
|
-
var __privateAdd$e = (obj, member, value) => {
|
|
627
|
-
if (member.has(obj))
|
|
628
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
629
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
630
|
-
};
|
|
631
|
-
var __privateSet$a = (obj, member, value, setter) => {
|
|
632
|
-
__accessCheck$e(obj, member, "write to private field");
|
|
633
|
-
member.set(obj, value);
|
|
634
|
-
return value;
|
|
635
|
-
};
|
|
636
|
-
var _config, _logger;
|
|
637
|
-
const _MiddlewareFactory = class _MiddlewareFactory {
|
|
638
|
-
constructor(options) {
|
|
639
|
-
__privateAdd$e(this, _config, void 0);
|
|
640
|
-
__privateAdd$e(this, _logger, void 0);
|
|
641
|
-
__privateSet$a(this, _config, options.config);
|
|
642
|
-
__privateSet$a(this, _logger, options.logger);
|
|
643
|
-
}
|
|
599
|
+
class MiddlewareFactory {
|
|
600
|
+
#config;
|
|
601
|
+
#logger;
|
|
644
602
|
/**
|
|
645
603
|
* Creates a new {@link MiddlewareFactory}.
|
|
646
604
|
*/
|
|
647
605
|
static create(options) {
|
|
648
|
-
return new
|
|
606
|
+
return new MiddlewareFactory(options);
|
|
607
|
+
}
|
|
608
|
+
constructor(options) {
|
|
609
|
+
this.#config = options.config;
|
|
610
|
+
this.#logger = options.logger;
|
|
649
611
|
}
|
|
650
612
|
/**
|
|
651
613
|
* Returns a middleware that unconditionally produces a 404 error response.
|
|
@@ -685,7 +647,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
|
|
|
685
647
|
* @returns An Express request handler
|
|
686
648
|
*/
|
|
687
649
|
logging() {
|
|
688
|
-
const logger =
|
|
650
|
+
const logger = this.#logger.child({
|
|
689
651
|
type: "incomingRequest"
|
|
690
652
|
});
|
|
691
653
|
return morgan__default.default("combined", {
|
|
@@ -709,7 +671,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
|
|
|
709
671
|
* @returns An Express request handler
|
|
710
672
|
*/
|
|
711
673
|
helmet() {
|
|
712
|
-
return helmet__default.default(readHelmetOptions(
|
|
674
|
+
return helmet__default.default(readHelmetOptions(this.#config.getOptionalConfig("backend")));
|
|
713
675
|
}
|
|
714
676
|
/**
|
|
715
677
|
* Returns a middleware that implements the cors library.
|
|
@@ -724,7 +686,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
|
|
|
724
686
|
* @returns An Express request handler
|
|
725
687
|
*/
|
|
726
688
|
cors() {
|
|
727
|
-
return cors__default.default(readCorsOptions(
|
|
689
|
+
return cors__default.default(readCorsOptions(this.#config.getOptionalConfig("backend")));
|
|
728
690
|
}
|
|
729
691
|
/**
|
|
730
692
|
* Express middleware to handle errors during request processing.
|
|
@@ -747,9 +709,8 @@ const _MiddlewareFactory = class _MiddlewareFactory {
|
|
|
747
709
|
* @returns An Express error request handler
|
|
748
710
|
*/
|
|
749
711
|
error(options = {}) {
|
|
750
|
-
|
|
751
|
-
const
|
|
752
|
-
const logger = __privateGet$c(this, _logger).child({
|
|
712
|
+
const showStackTraces = options.showStackTraces ?? process.env.NODE_ENV === "development";
|
|
713
|
+
const logger = this.#logger.child({
|
|
753
714
|
type: "errorHandler"
|
|
754
715
|
});
|
|
755
716
|
return (rawError, req, res, next) => {
|
|
@@ -770,10 +731,7 @@ const _MiddlewareFactory = class _MiddlewareFactory {
|
|
|
770
731
|
res.status(statusCode).json(body);
|
|
771
732
|
};
|
|
772
733
|
}
|
|
773
|
-
}
|
|
774
|
-
_config = new WeakMap();
|
|
775
|
-
_logger = new WeakMap();
|
|
776
|
-
let MiddlewareFactory = _MiddlewareFactory;
|
|
734
|
+
}
|
|
777
735
|
function getStatusCode(error) {
|
|
778
736
|
const knownStatusCodeFields = ["statusCode", "status"];
|
|
779
737
|
for (const field of knownStatusCodeFields) {
|
|
@@ -808,51 +766,27 @@ const escapeRegExp = (text) => {
|
|
|
808
766
|
return text.replace(/[.*+?^${}(\)|[\]\\]/g, "\\$&");
|
|
809
767
|
};
|
|
810
768
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
};
|
|
815
|
-
var __privateGet$b = (obj, member, getter) => {
|
|
816
|
-
__accessCheck$d(obj, member, "read from private field");
|
|
817
|
-
return member.get(obj);
|
|
818
|
-
};
|
|
819
|
-
var __privateAdd$d = (obj, member, value) => {
|
|
820
|
-
if (member.has(obj))
|
|
821
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
822
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
823
|
-
};
|
|
824
|
-
var __privateSet$9 = (obj, member, value, setter) => {
|
|
825
|
-
__accessCheck$d(obj, member, "write to private field");
|
|
826
|
-
member.set(obj, value);
|
|
827
|
-
return value;
|
|
828
|
-
};
|
|
829
|
-
var _winston, _addRedactions;
|
|
830
|
-
const _WinstonLogger = class _WinstonLogger {
|
|
831
|
-
constructor(winston, addRedactions) {
|
|
832
|
-
__privateAdd$d(this, _winston, void 0);
|
|
833
|
-
__privateAdd$d(this, _addRedactions, void 0);
|
|
834
|
-
__privateSet$9(this, _winston, winston);
|
|
835
|
-
__privateSet$9(this, _addRedactions, addRedactions);
|
|
836
|
-
}
|
|
769
|
+
class WinstonLogger {
|
|
770
|
+
#winston;
|
|
771
|
+
#addRedactions;
|
|
837
772
|
/**
|
|
838
773
|
* Creates a {@link WinstonLogger} instance.
|
|
839
774
|
*/
|
|
840
775
|
static create(options) {
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
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();
|
|
844
778
|
let logger = winston.createLogger({
|
|
845
779
|
level: process.env.LOG_LEVEL || options.level || "info",
|
|
846
780
|
format: winston.format.combine(
|
|
847
|
-
|
|
848
|
-
|
|
781
|
+
options.format ?? defaultFormatter,
|
|
782
|
+
redacter.format
|
|
849
783
|
),
|
|
850
|
-
transports:
|
|
784
|
+
transports: options.transports ?? new winston.transports.Console()
|
|
851
785
|
});
|
|
852
786
|
if (options.meta) {
|
|
853
787
|
logger = logger.child(options.meta);
|
|
854
788
|
}
|
|
855
|
-
return new
|
|
789
|
+
return new WinstonLogger(logger, redacter.add);
|
|
856
790
|
}
|
|
857
791
|
/**
|
|
858
792
|
* Creates a winston log formatter for redacting secrets.
|
|
@@ -860,24 +794,14 @@ const _WinstonLogger = class _WinstonLogger {
|
|
|
860
794
|
static redacter() {
|
|
861
795
|
const redactionSet = /* @__PURE__ */ new Set();
|
|
862
796
|
let redactionPattern = void 0;
|
|
863
|
-
const replace = (obj) => {
|
|
864
|
-
var _a;
|
|
865
|
-
for (const key in obj) {
|
|
866
|
-
if (Object.hasOwn(obj, key)) {
|
|
867
|
-
if (typeof obj[key] === "object") {
|
|
868
|
-
obj[key] = replace(obj[key]);
|
|
869
|
-
} else if (typeof obj[key] === "string") {
|
|
870
|
-
try {
|
|
871
|
-
obj[key] = (_a = obj[key]) == null ? void 0 : _a.replace(redactionPattern, "[REDACTED]");
|
|
872
|
-
} catch {
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
return obj;
|
|
878
|
-
};
|
|
879
797
|
return {
|
|
880
|
-
format: winston.format(
|
|
798
|
+
format: winston.format((obj) => {
|
|
799
|
+
if (!redactionPattern || !obj) {
|
|
800
|
+
return obj;
|
|
801
|
+
}
|
|
802
|
+
obj[tripleBeam.MESSAGE] = obj[tripleBeam.MESSAGE]?.replace?.(redactionPattern, "***");
|
|
803
|
+
return obj;
|
|
804
|
+
})(),
|
|
881
805
|
add(newRedactions) {
|
|
882
806
|
let added = 0;
|
|
883
807
|
for (const redactionToTrim of newRedactions) {
|
|
@@ -924,227 +848,30 @@ const _WinstonLogger = class _WinstonLogger {
|
|
|
924
848
|
})
|
|
925
849
|
);
|
|
926
850
|
}
|
|
851
|
+
constructor(winston, addRedactions) {
|
|
852
|
+
this.#winston = winston;
|
|
853
|
+
this.#addRedactions = addRedactions;
|
|
854
|
+
}
|
|
927
855
|
error(message, meta) {
|
|
928
|
-
|
|
856
|
+
this.#winston.error(message, meta);
|
|
929
857
|
}
|
|
930
858
|
warn(message, meta) {
|
|
931
|
-
|
|
859
|
+
this.#winston.warn(message, meta);
|
|
932
860
|
}
|
|
933
861
|
info(message, meta) {
|
|
934
|
-
|
|
862
|
+
this.#winston.info(message, meta);
|
|
935
863
|
}
|
|
936
864
|
debug(message, meta) {
|
|
937
|
-
|
|
865
|
+
this.#winston.debug(message, meta);
|
|
938
866
|
}
|
|
939
867
|
child(meta) {
|
|
940
|
-
return new
|
|
868
|
+
return new WinstonLogger(this.#winston.child(meta));
|
|
941
869
|
}
|
|
942
870
|
addRedactions(redactions) {
|
|
943
|
-
|
|
944
|
-
(_a = __privateGet$b(this, _addRedactions)) == null ? void 0 : _a.call(this, redactions);
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
_winston = new WeakMap();
|
|
948
|
-
_addRedactions = new WeakMap();
|
|
949
|
-
let WinstonLogger = _WinstonLogger;
|
|
950
|
-
|
|
951
|
-
var __accessCheck$c = (obj, member, msg) => {
|
|
952
|
-
if (!member.has(obj))
|
|
953
|
-
throw TypeError("Cannot " + msg);
|
|
954
|
-
};
|
|
955
|
-
var __privateGet$a = (obj, member, getter) => {
|
|
956
|
-
__accessCheck$c(obj, member, "read from private field");
|
|
957
|
-
return member.get(obj);
|
|
958
|
-
};
|
|
959
|
-
var __privateAdd$c = (obj, member, value) => {
|
|
960
|
-
if (member.has(obj))
|
|
961
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
962
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
963
|
-
};
|
|
964
|
-
var __privateSet$8 = (obj, member, value, setter) => {
|
|
965
|
-
__accessCheck$c(obj, member, "write to private field");
|
|
966
|
-
member.set(obj, value);
|
|
967
|
-
return value;
|
|
968
|
-
};
|
|
969
|
-
var _hasStarted$1, _startupTasks$1, _hasShutdown, _shutdownTasks;
|
|
970
|
-
class BackendLifecycleImpl {
|
|
971
|
-
constructor(logger) {
|
|
972
|
-
this.logger = logger;
|
|
973
|
-
__privateAdd$c(this, _hasStarted$1, false);
|
|
974
|
-
__privateAdd$c(this, _startupTasks$1, []);
|
|
975
|
-
__privateAdd$c(this, _hasShutdown, false);
|
|
976
|
-
__privateAdd$c(this, _shutdownTasks, []);
|
|
977
|
-
}
|
|
978
|
-
addStartupHook(hook, options) {
|
|
979
|
-
if (__privateGet$a(this, _hasStarted$1)) {
|
|
980
|
-
throw new Error("Attempted to add startup hook after startup");
|
|
981
|
-
}
|
|
982
|
-
__privateGet$a(this, _startupTasks$1).push({ hook, options });
|
|
983
|
-
}
|
|
984
|
-
async startup() {
|
|
985
|
-
if (__privateGet$a(this, _hasStarted$1)) {
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
__privateSet$8(this, _hasStarted$1, true);
|
|
989
|
-
this.logger.debug(`Running ${__privateGet$a(this, _startupTasks$1).length} startup tasks...`);
|
|
990
|
-
await Promise.all(
|
|
991
|
-
__privateGet$a(this, _startupTasks$1).map(async ({ hook, options }) => {
|
|
992
|
-
var _a;
|
|
993
|
-
const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
|
|
994
|
-
try {
|
|
995
|
-
await hook();
|
|
996
|
-
logger.debug(`Startup hook succeeded`);
|
|
997
|
-
} catch (error) {
|
|
998
|
-
logger.error(`Startup hook failed, ${error}`);
|
|
999
|
-
}
|
|
1000
|
-
})
|
|
1001
|
-
);
|
|
1002
|
-
}
|
|
1003
|
-
addShutdownHook(hook, options) {
|
|
1004
|
-
if (__privateGet$a(this, _hasShutdown)) {
|
|
1005
|
-
throw new Error("Attempted to add shutdown hook after shutdown");
|
|
1006
|
-
}
|
|
1007
|
-
__privateGet$a(this, _shutdownTasks).push({ hook, options });
|
|
1008
|
-
}
|
|
1009
|
-
async shutdown() {
|
|
1010
|
-
if (__privateGet$a(this, _hasShutdown)) {
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
__privateSet$8(this, _hasShutdown, true);
|
|
1014
|
-
this.logger.debug(
|
|
1015
|
-
`Running ${__privateGet$a(this, _shutdownTasks).length} shutdown tasks...`
|
|
1016
|
-
);
|
|
1017
|
-
await Promise.all(
|
|
1018
|
-
__privateGet$a(this, _shutdownTasks).map(async ({ hook, options }) => {
|
|
1019
|
-
var _a;
|
|
1020
|
-
const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
|
|
1021
|
-
try {
|
|
1022
|
-
await hook();
|
|
1023
|
-
logger.debug(`Shutdown hook succeeded`);
|
|
1024
|
-
} catch (error) {
|
|
1025
|
-
logger.error(`Shutdown hook failed, ${error}`);
|
|
1026
|
-
}
|
|
1027
|
-
})
|
|
1028
|
-
);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
_hasStarted$1 = new WeakMap();
|
|
1032
|
-
_startupTasks$1 = new WeakMap();
|
|
1033
|
-
_hasShutdown = new WeakMap();
|
|
1034
|
-
_shutdownTasks = new WeakMap();
|
|
1035
|
-
const rootLifecycleServiceFactory = backendPluginApi.createServiceFactory({
|
|
1036
|
-
service: backendPluginApi.coreServices.rootLifecycle,
|
|
1037
|
-
deps: {
|
|
1038
|
-
logger: backendPluginApi.coreServices.rootLogger
|
|
1039
|
-
},
|
|
1040
|
-
async factory({ logger }) {
|
|
1041
|
-
return new BackendLifecycleImpl(logger);
|
|
1042
|
-
}
|
|
1043
|
-
});
|
|
1044
|
-
|
|
1045
|
-
var __accessCheck$b = (obj, member, msg) => {
|
|
1046
|
-
if (!member.has(obj))
|
|
1047
|
-
throw TypeError("Cannot " + msg);
|
|
1048
|
-
};
|
|
1049
|
-
var __privateGet$9 = (obj, member, getter) => {
|
|
1050
|
-
__accessCheck$b(obj, member, "read from private field");
|
|
1051
|
-
return member.get(obj);
|
|
1052
|
-
};
|
|
1053
|
-
var __privateAdd$b = (obj, member, value) => {
|
|
1054
|
-
if (member.has(obj))
|
|
1055
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
1056
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
1057
|
-
};
|
|
1058
|
-
var __privateSet$7 = (obj, member, value, setter) => {
|
|
1059
|
-
__accessCheck$b(obj, member, "write to private field");
|
|
1060
|
-
member.set(obj, value);
|
|
1061
|
-
return value;
|
|
1062
|
-
};
|
|
1063
|
-
var _hasStarted, _startupTasks;
|
|
1064
|
-
class BackendPluginLifecycleImpl {
|
|
1065
|
-
constructor(logger, rootLifecycle, pluginMetadata) {
|
|
1066
|
-
this.logger = logger;
|
|
1067
|
-
this.rootLifecycle = rootLifecycle;
|
|
1068
|
-
this.pluginMetadata = pluginMetadata;
|
|
1069
|
-
__privateAdd$b(this, _hasStarted, false);
|
|
1070
|
-
__privateAdd$b(this, _startupTasks, []);
|
|
1071
|
-
}
|
|
1072
|
-
addStartupHook(hook, options) {
|
|
1073
|
-
if (__privateGet$9(this, _hasStarted)) {
|
|
1074
|
-
throw new Error("Attempted to add startup hook after startup");
|
|
1075
|
-
}
|
|
1076
|
-
__privateGet$9(this, _startupTasks).push({ hook, options });
|
|
1077
|
-
}
|
|
1078
|
-
async startup() {
|
|
1079
|
-
if (__privateGet$9(this, _hasStarted)) {
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
__privateSet$7(this, _hasStarted, true);
|
|
1083
|
-
this.logger.debug(
|
|
1084
|
-
`Running ${__privateGet$9(this, _startupTasks).length} plugin startup tasks...`
|
|
1085
|
-
);
|
|
1086
|
-
await Promise.all(
|
|
1087
|
-
__privateGet$9(this, _startupTasks).map(async ({ hook, options }) => {
|
|
1088
|
-
var _a;
|
|
1089
|
-
const logger = (_a = options == null ? void 0 : options.logger) != null ? _a : this.logger;
|
|
1090
|
-
try {
|
|
1091
|
-
await hook();
|
|
1092
|
-
logger.debug(`Plugin startup hook succeeded`);
|
|
1093
|
-
} catch (error) {
|
|
1094
|
-
logger.error(`Plugin startup hook failed, ${error}`);
|
|
1095
|
-
}
|
|
1096
|
-
})
|
|
1097
|
-
);
|
|
1098
|
-
}
|
|
1099
|
-
addShutdownHook(hook, options) {
|
|
1100
|
-
var _a, _b;
|
|
1101
|
-
const plugin = this.pluginMetadata.getId();
|
|
1102
|
-
this.rootLifecycle.addShutdownHook(hook, {
|
|
1103
|
-
logger: (_b = (_a = options == null ? void 0 : options.logger) == null ? void 0 : _a.child({ plugin })) != null ? _b : this.logger
|
|
1104
|
-
});
|
|
871
|
+
this.#addRedactions?.(redactions);
|
|
1105
872
|
}
|
|
1106
873
|
}
|
|
1107
|
-
_hasStarted = new WeakMap();
|
|
1108
|
-
_startupTasks = new WeakMap();
|
|
1109
|
-
const lifecycleServiceFactory = backendPluginApi.createServiceFactory({
|
|
1110
|
-
service: backendPluginApi.coreServices.lifecycle,
|
|
1111
|
-
deps: {
|
|
1112
|
-
logger: backendPluginApi.coreServices.logger,
|
|
1113
|
-
rootLifecycle: backendPluginApi.coreServices.rootLifecycle,
|
|
1114
|
-
pluginMetadata: backendPluginApi.coreServices.pluginMetadata
|
|
1115
|
-
},
|
|
1116
|
-
async factory({ rootLifecycle, logger, pluginMetadata }) {
|
|
1117
|
-
return new BackendPluginLifecycleImpl(
|
|
1118
|
-
logger,
|
|
1119
|
-
rootLifecycle,
|
|
1120
|
-
pluginMetadata
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
});
|
|
1124
874
|
|
|
1125
|
-
var __accessCheck$a = (obj, member, msg) => {
|
|
1126
|
-
if (!member.has(obj))
|
|
1127
|
-
throw TypeError("Cannot " + msg);
|
|
1128
|
-
};
|
|
1129
|
-
var __privateGet$8 = (obj, member, getter) => {
|
|
1130
|
-
__accessCheck$a(obj, member, "read from private field");
|
|
1131
|
-
return member.get(obj);
|
|
1132
|
-
};
|
|
1133
|
-
var __privateAdd$a = (obj, member, value) => {
|
|
1134
|
-
if (member.has(obj))
|
|
1135
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
1136
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
1137
|
-
};
|
|
1138
|
-
var __privateSet$6 = (obj, member, value, setter) => {
|
|
1139
|
-
__accessCheck$a(obj, member, "write to private field");
|
|
1140
|
-
member.set(obj, value);
|
|
1141
|
-
return value;
|
|
1142
|
-
};
|
|
1143
|
-
var __privateMethod$7 = (obj, member, method) => {
|
|
1144
|
-
__accessCheck$a(obj, member, "access private method");
|
|
1145
|
-
return method;
|
|
1146
|
-
};
|
|
1147
|
-
var _nodeIds, _cycleKeys, _getCycleKey, getCycleKey_fn, _nodes, _allProvided;
|
|
1148
875
|
class Node {
|
|
1149
876
|
constructor(value, consumes, provides) {
|
|
1150
877
|
this.value = value;
|
|
@@ -1159,45 +886,29 @@ class Node {
|
|
|
1159
886
|
);
|
|
1160
887
|
}
|
|
1161
888
|
}
|
|
1162
|
-
|
|
1163
|
-
constructor(nodes) {
|
|
1164
|
-
__privateAdd$a(this, _getCycleKey);
|
|
1165
|
-
__privateAdd$a(this, _nodeIds, void 0);
|
|
1166
|
-
__privateAdd$a(this, _cycleKeys, void 0);
|
|
1167
|
-
__privateSet$6(this, _nodeIds, new Map(nodes.map((n, i) => [n.value, i])));
|
|
1168
|
-
__privateSet$6(this, _cycleKeys, /* @__PURE__ */ new Set());
|
|
1169
|
-
}
|
|
889
|
+
class CycleKeySet {
|
|
1170
890
|
static from(nodes) {
|
|
1171
|
-
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();
|
|
1172
898
|
}
|
|
1173
899
|
tryAdd(path) {
|
|
1174
|
-
const cycleKey =
|
|
1175
|
-
if (
|
|
900
|
+
const cycleKey = this.#getCycleKey(path);
|
|
901
|
+
if (this.#cycleKeys.has(cycleKey)) {
|
|
1176
902
|
return false;
|
|
1177
903
|
}
|
|
1178
|
-
|
|
904
|
+
this.#cycleKeys.add(cycleKey);
|
|
1179
905
|
return true;
|
|
1180
906
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
_cycleKeys = new WeakMap();
|
|
1184
|
-
_getCycleKey = new WeakSet();
|
|
1185
|
-
getCycleKey_fn = function(path) {
|
|
1186
|
-
return path.map((n) => __privateGet$8(this, _nodeIds).get(n)).sort().join(",");
|
|
1187
|
-
};
|
|
1188
|
-
let CycleKeySet = _CycleKeySet;
|
|
1189
|
-
const _DependencyGraph = class _DependencyGraph {
|
|
1190
|
-
constructor(nodes) {
|
|
1191
|
-
__privateAdd$a(this, _nodes, void 0);
|
|
1192
|
-
__privateAdd$a(this, _allProvided, void 0);
|
|
1193
|
-
__privateSet$6(this, _nodes, nodes);
|
|
1194
|
-
__privateSet$6(this, _allProvided, /* @__PURE__ */ new Set());
|
|
1195
|
-
for (const node of __privateGet$8(this, _nodes).values()) {
|
|
1196
|
-
for (const produced of node.provides) {
|
|
1197
|
-
__privateGet$8(this, _allProvided).add(produced);
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
907
|
+
#getCycleKey(path) {
|
|
908
|
+
return path.map((n) => this.#nodeIds.get(n)).sort().join(",");
|
|
1200
909
|
}
|
|
910
|
+
}
|
|
911
|
+
class DependencyGraph {
|
|
1201
912
|
static fromMap(nodes) {
|
|
1202
913
|
return this.fromIterable(
|
|
1203
914
|
Object.entries(nodes).map(([key, node]) => ({
|
|
@@ -1211,16 +922,27 @@ const _DependencyGraph = class _DependencyGraph {
|
|
|
1211
922
|
for (const nodeInput of nodeInputs) {
|
|
1212
923
|
nodes.push(Node.from(nodeInput));
|
|
1213
924
|
}
|
|
1214
|
-
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
|
+
}
|
|
1215
937
|
}
|
|
1216
938
|
/**
|
|
1217
939
|
* Find all nodes that consume dependencies that are not provided by any other node.
|
|
1218
940
|
*/
|
|
1219
941
|
findUnsatisfiedDeps() {
|
|
1220
942
|
const unsatisfiedDependencies = [];
|
|
1221
|
-
for (const node of
|
|
943
|
+
for (const node of this.#nodes.values()) {
|
|
1222
944
|
const unsatisfied = Array.from(node.consumes).filter(
|
|
1223
|
-
(id) => !
|
|
945
|
+
(id) => !this.#allProvided.has(id)
|
|
1224
946
|
);
|
|
1225
947
|
if (unsatisfied.length > 0) {
|
|
1226
948
|
unsatisfiedDependencies.push({ value: node.value, unsatisfied });
|
|
@@ -1240,8 +962,8 @@ const _DependencyGraph = class _DependencyGraph {
|
|
|
1240
962
|
* form a cycle, with the same node as the first and last element of the array.
|
|
1241
963
|
*/
|
|
1242
964
|
*detectCircularDependencies() {
|
|
1243
|
-
const cycleKeys = CycleKeySet.from(
|
|
1244
|
-
for (const startNode of
|
|
965
|
+
const cycleKeys = CycleKeySet.from(this.#nodes);
|
|
966
|
+
for (const startNode of this.#nodes) {
|
|
1245
967
|
const visited = /* @__PURE__ */ new Set();
|
|
1246
968
|
const stack = new Array([
|
|
1247
969
|
startNode,
|
|
@@ -1254,7 +976,7 @@ const _DependencyGraph = class _DependencyGraph {
|
|
|
1254
976
|
}
|
|
1255
977
|
visited.add(node);
|
|
1256
978
|
for (const consumed of node.consumes) {
|
|
1257
|
-
const providerNodes =
|
|
979
|
+
const providerNodes = this.#nodes.filter(
|
|
1258
980
|
(other) => other.provides.has(consumed)
|
|
1259
981
|
);
|
|
1260
982
|
for (const provider of providerNodes) {
|
|
@@ -1283,9 +1005,9 @@ const _DependencyGraph = class _DependencyGraph {
|
|
|
1283
1005
|
* Dependencies of nodes that are not produced by any other nodes will be ignored.
|
|
1284
1006
|
*/
|
|
1285
1007
|
async parallelTopologicalTraversal(fn) {
|
|
1286
|
-
const allProvided =
|
|
1008
|
+
const allProvided = this.#allProvided;
|
|
1287
1009
|
const producedSoFar = /* @__PURE__ */ new Set();
|
|
1288
|
-
const waiting = new Set(
|
|
1010
|
+
const waiting = new Set(this.#nodes.values());
|
|
1289
1011
|
const visited = /* @__PURE__ */ new Set();
|
|
1290
1012
|
const results = new Array();
|
|
1291
1013
|
let inFlight = 0;
|
|
@@ -1326,34 +1048,8 @@ const _DependencyGraph = class _DependencyGraph {
|
|
|
1326
1048
|
await processMoreNodes();
|
|
1327
1049
|
return results;
|
|
1328
1050
|
}
|
|
1329
|
-
}
|
|
1330
|
-
_nodes = new WeakMap();
|
|
1331
|
-
_allProvided = new WeakMap();
|
|
1332
|
-
let DependencyGraph = _DependencyGraph;
|
|
1051
|
+
}
|
|
1333
1052
|
|
|
1334
|
-
var __accessCheck$9 = (obj, member, msg) => {
|
|
1335
|
-
if (!member.has(obj))
|
|
1336
|
-
throw TypeError("Cannot " + msg);
|
|
1337
|
-
};
|
|
1338
|
-
var __privateGet$7 = (obj, member, getter) => {
|
|
1339
|
-
__accessCheck$9(obj, member, "read from private field");
|
|
1340
|
-
return member.get(obj);
|
|
1341
|
-
};
|
|
1342
|
-
var __privateAdd$9 = (obj, member, value) => {
|
|
1343
|
-
if (member.has(obj))
|
|
1344
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
1345
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
1346
|
-
};
|
|
1347
|
-
var __privateSet$5 = (obj, member, value, setter) => {
|
|
1348
|
-
__accessCheck$9(obj, member, "write to private field");
|
|
1349
|
-
member.set(obj, value);
|
|
1350
|
-
return value;
|
|
1351
|
-
};
|
|
1352
|
-
var __privateMethod$6 = (obj, member, method) => {
|
|
1353
|
-
__accessCheck$9(obj, member, "access private method");
|
|
1354
|
-
return method;
|
|
1355
|
-
};
|
|
1356
|
-
var _providedFactories, _loadedDefaultFactories, _implementations, _rootServiceImplementations, _addedFactoryIds, _instantiatedFactories, _resolveFactory, resolveFactory_fn, _checkForMissingDeps, checkForMissingDeps_fn;
|
|
1357
1053
|
function toInternalServiceFactory(factory) {
|
|
1358
1054
|
const f = factory;
|
|
1359
1055
|
if (f.$$type !== "@backstage/BackendFeature") {
|
|
@@ -1368,33 +1064,77 @@ const pluginMetadataServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
1368
1064
|
(options) => ({
|
|
1369
1065
|
service: backendPluginApi.coreServices.pluginMetadata,
|
|
1370
1066
|
deps: {},
|
|
1371
|
-
factory: async () => ({ getId: () => options
|
|
1067
|
+
factory: async () => ({ getId: () => options?.pluginId })
|
|
1372
1068
|
})
|
|
1373
1069
|
);
|
|
1374
|
-
|
|
1375
|
-
constructor(factories) {
|
|
1376
|
-
__privateAdd$9(this, _resolveFactory);
|
|
1377
|
-
__privateAdd$9(this, _checkForMissingDeps);
|
|
1378
|
-
__privateAdd$9(this, _providedFactories, void 0);
|
|
1379
|
-
__privateAdd$9(this, _loadedDefaultFactories, void 0);
|
|
1380
|
-
__privateAdd$9(this, _implementations, void 0);
|
|
1381
|
-
__privateAdd$9(this, _rootServiceImplementations, /* @__PURE__ */ new Map());
|
|
1382
|
-
__privateAdd$9(this, _addedFactoryIds, /* @__PURE__ */ new Set());
|
|
1383
|
-
__privateAdd$9(this, _instantiatedFactories, /* @__PURE__ */ new Set());
|
|
1384
|
-
__privateSet$5(this, _providedFactories, new Map(
|
|
1385
|
-
factories.map((sf) => [sf.service.id, toInternalServiceFactory(sf)])
|
|
1386
|
-
));
|
|
1387
|
-
__privateSet$5(this, _loadedDefaultFactories, /* @__PURE__ */ new Map());
|
|
1388
|
-
__privateSet$5(this, _implementations, /* @__PURE__ */ new Map());
|
|
1389
|
-
}
|
|
1070
|
+
class ServiceRegistry {
|
|
1390
1071
|
static create(factories) {
|
|
1391
|
-
const registry = new
|
|
1072
|
+
const registry = new ServiceRegistry(factories);
|
|
1392
1073
|
registry.checkForCircularDeps();
|
|
1393
1074
|
return registry;
|
|
1394
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
|
+
}
|
|
1395
1135
|
checkForCircularDeps() {
|
|
1396
1136
|
const graph = DependencyGraph.fromIterable(
|
|
1397
|
-
Array.from(
|
|
1137
|
+
Array.from(this.#providedFactories).map(
|
|
1398
1138
|
([serviceId, serviceFactory]) => ({
|
|
1399
1139
|
value: serviceId,
|
|
1400
1140
|
provides: [serviceId],
|
|
@@ -1416,21 +1156,21 @@ const _ServiceRegistry = class _ServiceRegistry {
|
|
|
1416
1156
|
`The ${backendPluginApi.coreServices.pluginMetadata.id} service cannot be overridden`
|
|
1417
1157
|
);
|
|
1418
1158
|
}
|
|
1419
|
-
if (
|
|
1159
|
+
if (this.#addedFactoryIds.has(factoryId)) {
|
|
1420
1160
|
throw new Error(
|
|
1421
1161
|
`Duplicate service implementations provided for ${factoryId}`
|
|
1422
1162
|
);
|
|
1423
1163
|
}
|
|
1424
|
-
if (
|
|
1164
|
+
if (this.#instantiatedFactories.has(factoryId)) {
|
|
1425
1165
|
throw new Error(
|
|
1426
1166
|
`Unable to set service factory with id ${factoryId}, service has already been instantiated`
|
|
1427
1167
|
);
|
|
1428
1168
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1169
|
+
this.#addedFactoryIds.add(factoryId);
|
|
1170
|
+
this.#providedFactories.set(factoryId, toInternalServiceFactory(factory));
|
|
1431
1171
|
}
|
|
1432
1172
|
async initializeEagerServicesWithScope(scope, pluginId = "root") {
|
|
1433
|
-
for (const factory of
|
|
1173
|
+
for (const factory of this.#providedFactories.values()) {
|
|
1434
1174
|
if (factory.service.scope === scope) {
|
|
1435
1175
|
if (scope === "root" && factory.initialization !== "lazy") {
|
|
1436
1176
|
await this.get(factory.service, pluginId);
|
|
@@ -1441,13 +1181,12 @@ const _ServiceRegistry = class _ServiceRegistry {
|
|
|
1441
1181
|
}
|
|
1442
1182
|
}
|
|
1443
1183
|
get(ref, pluginId) {
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
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) => {
|
|
1447
1186
|
if (factory.service.scope === "root") {
|
|
1448
|
-
let existing =
|
|
1187
|
+
let existing = this.#rootServiceImplementations.get(factory);
|
|
1449
1188
|
if (!existing) {
|
|
1450
|
-
|
|
1189
|
+
this.#checkForMissingDeps(factory, pluginId);
|
|
1451
1190
|
const rootDeps = new Array();
|
|
1452
1191
|
for (const [name, serviceRef] of Object.entries(factory.deps)) {
|
|
1453
1192
|
if (serviceRef.scope !== "root") {
|
|
@@ -1461,13 +1200,13 @@ const _ServiceRegistry = class _ServiceRegistry {
|
|
|
1461
1200
|
existing = Promise.all(rootDeps).then(
|
|
1462
1201
|
(entries) => factory.factory(Object.fromEntries(entries), void 0)
|
|
1463
1202
|
);
|
|
1464
|
-
|
|
1203
|
+
this.#rootServiceImplementations.set(factory, existing);
|
|
1465
1204
|
}
|
|
1466
1205
|
return existing;
|
|
1467
1206
|
}
|
|
1468
|
-
let implementation =
|
|
1207
|
+
let implementation = this.#implementations.get(factory);
|
|
1469
1208
|
if (!implementation) {
|
|
1470
|
-
|
|
1209
|
+
this.#checkForMissingDeps(factory, pluginId);
|
|
1471
1210
|
const rootDeps = new Array();
|
|
1472
1211
|
for (const [name, serviceRef] of Object.entries(factory.deps)) {
|
|
1473
1212
|
if (serviceRef.scope === "root") {
|
|
@@ -1477,10 +1216,7 @@ const _ServiceRegistry = class _ServiceRegistry {
|
|
|
1477
1216
|
}
|
|
1478
1217
|
implementation = {
|
|
1479
1218
|
context: Promise.all(rootDeps).then(
|
|
1480
|
-
(entries) =>
|
|
1481
|
-
var _a2;
|
|
1482
|
-
return (_a2 = factory.createRootContext) == null ? void 0 : _a2.call(factory, Object.fromEntries(entries));
|
|
1483
|
-
}
|
|
1219
|
+
(entries) => factory.createRootContext?.(Object.fromEntries(entries))
|
|
1484
1220
|
).catch((error) => {
|
|
1485
1221
|
const cause = errors.stringifyError(error);
|
|
1486
1222
|
throw new Error(
|
|
@@ -1489,7 +1225,7 @@ const _ServiceRegistry = class _ServiceRegistry {
|
|
|
1489
1225
|
}),
|
|
1490
1226
|
byPlugin: /* @__PURE__ */ new Map()
|
|
1491
1227
|
};
|
|
1492
|
-
|
|
1228
|
+
this.#implementations.set(factory, implementation);
|
|
1493
1229
|
}
|
|
1494
1230
|
let result = implementation.byPlugin.get(pluginId);
|
|
1495
1231
|
if (!result) {
|
|
@@ -1513,72 +1249,17 @@ const _ServiceRegistry = class _ServiceRegistry {
|
|
|
1513
1249
|
return result;
|
|
1514
1250
|
});
|
|
1515
1251
|
}
|
|
1516
|
-
}
|
|
1517
|
-
_providedFactories = new WeakMap();
|
|
1518
|
-
_loadedDefaultFactories = new WeakMap();
|
|
1519
|
-
_implementations = new WeakMap();
|
|
1520
|
-
_rootServiceImplementations = new WeakMap();
|
|
1521
|
-
_addedFactoryIds = new WeakMap();
|
|
1522
|
-
_instantiatedFactories = new WeakMap();
|
|
1523
|
-
_resolveFactory = new WeakSet();
|
|
1524
|
-
resolveFactory_fn = function(ref, pluginId) {
|
|
1525
|
-
if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
|
|
1526
|
-
return Promise.resolve(
|
|
1527
|
-
toInternalServiceFactory(pluginMetadataServiceFactory({ pluginId }))
|
|
1528
|
-
);
|
|
1529
|
-
}
|
|
1530
|
-
let resolvedFactory = __privateGet$7(this, _providedFactories).get(ref.id);
|
|
1531
|
-
const { __defaultFactory: defaultFactory } = ref;
|
|
1532
|
-
if (!resolvedFactory && !defaultFactory) {
|
|
1533
|
-
return void 0;
|
|
1534
|
-
}
|
|
1535
|
-
if (!resolvedFactory) {
|
|
1536
|
-
let loadedFactory = __privateGet$7(this, _loadedDefaultFactories).get(defaultFactory);
|
|
1537
|
-
if (!loadedFactory) {
|
|
1538
|
-
loadedFactory = Promise.resolve().then(() => defaultFactory(ref)).then(
|
|
1539
|
-
(f) => toInternalServiceFactory(typeof f === "function" ? f() : f)
|
|
1540
|
-
);
|
|
1541
|
-
__privateGet$7(this, _loadedDefaultFactories).set(defaultFactory, loadedFactory);
|
|
1542
|
-
}
|
|
1543
|
-
resolvedFactory = loadedFactory.catch((error) => {
|
|
1544
|
-
throw new Error(
|
|
1545
|
-
`Failed to instantiate service '${ref.id}' because the default factory loader threw an error, ${errors.stringifyError(
|
|
1546
|
-
error
|
|
1547
|
-
)}`
|
|
1548
|
-
);
|
|
1549
|
-
});
|
|
1550
|
-
}
|
|
1551
|
-
return Promise.resolve(resolvedFactory);
|
|
1552
|
-
};
|
|
1553
|
-
_checkForMissingDeps = new WeakSet();
|
|
1554
|
-
checkForMissingDeps_fn = function(factory, pluginId) {
|
|
1555
|
-
const missingDeps = Object.values(factory.deps).filter((ref) => {
|
|
1556
|
-
if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
|
|
1557
|
-
return false;
|
|
1558
|
-
}
|
|
1559
|
-
if (__privateGet$7(this, _providedFactories).get(ref.id)) {
|
|
1560
|
-
return false;
|
|
1561
|
-
}
|
|
1562
|
-
return !ref.__defaultFactory;
|
|
1563
|
-
});
|
|
1564
|
-
if (missingDeps.length) {
|
|
1565
|
-
const missing = missingDeps.map((r) => `'${r.id}'`).join(", ");
|
|
1566
|
-
throw new Error(
|
|
1567
|
-
`Failed to instantiate service '${factory.service.id}' for '${pluginId}' because the following dependent services are missing: ${missing}`
|
|
1568
|
-
);
|
|
1569
|
-
}
|
|
1570
|
-
};
|
|
1571
|
-
let ServiceRegistry = _ServiceRegistry;
|
|
1252
|
+
}
|
|
1572
1253
|
|
|
1573
1254
|
const LOGGER_INTERVAL_MAX = 6e4;
|
|
1574
1255
|
function joinIds(ids) {
|
|
1575
1256
|
return [...ids].map((id) => `'${id}'`).join(", ");
|
|
1576
1257
|
}
|
|
1577
1258
|
function createInitializationLogger(pluginIds, rootLogger) {
|
|
1578
|
-
const logger = rootLogger
|
|
1259
|
+
const logger = rootLogger?.child({ type: "initialization" });
|
|
1579
1260
|
const starting = new Set(pluginIds);
|
|
1580
1261
|
const started = /* @__PURE__ */ new Set();
|
|
1581
|
-
logger
|
|
1262
|
+
logger?.info(`Plugin initialization started: ${joinIds(pluginIds)}`);
|
|
1582
1263
|
const getInitStatus = () => {
|
|
1583
1264
|
let status = "";
|
|
1584
1265
|
if (started.size > 0) {
|
|
@@ -1594,7 +1275,7 @@ function createInitializationLogger(pluginIds, rootLogger) {
|
|
|
1594
1275
|
let prevInterval = 0;
|
|
1595
1276
|
let timeout;
|
|
1596
1277
|
const onTimeout = () => {
|
|
1597
|
-
logger
|
|
1278
|
+
logger?.info(`Plugin initialization in progress${getInitStatus()}`);
|
|
1598
1279
|
const nextInterval = Math.min(interval + prevInterval, LOGGER_INTERVAL_MAX);
|
|
1599
1280
|
prevInterval = interval;
|
|
1600
1281
|
interval = nextInterval;
|
|
@@ -1607,7 +1288,7 @@ function createInitializationLogger(pluginIds, rootLogger) {
|
|
|
1607
1288
|
started.add(pluginId);
|
|
1608
1289
|
},
|
|
1609
1290
|
onAllStarted() {
|
|
1610
|
-
logger
|
|
1291
|
+
logger?.info(`Plugin initialization complete${getInitStatus()}`);
|
|
1611
1292
|
if (timeout) {
|
|
1612
1293
|
clearTimeout(timeout);
|
|
1613
1294
|
timeout = void 0;
|
|
@@ -1616,52 +1297,76 @@ function createInitializationLogger(pluginIds, rootLogger) {
|
|
|
1616
1297
|
};
|
|
1617
1298
|
}
|
|
1618
1299
|
|
|
1619
|
-
var __accessCheck$8 = (obj, member, msg) => {
|
|
1620
|
-
if (!member.has(obj))
|
|
1621
|
-
throw TypeError("Cannot " + msg);
|
|
1622
|
-
};
|
|
1623
|
-
var __privateGet$6 = (obj, member, getter) => {
|
|
1624
|
-
__accessCheck$8(obj, member, "read from private field");
|
|
1625
|
-
return member.get(obj);
|
|
1626
|
-
};
|
|
1627
|
-
var __privateAdd$8 = (obj, member, value) => {
|
|
1628
|
-
if (member.has(obj))
|
|
1629
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
1630
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
1631
|
-
};
|
|
1632
|
-
var __privateSet$4 = (obj, member, value, setter) => {
|
|
1633
|
-
__accessCheck$8(obj, member, "write to private field");
|
|
1634
|
-
member.set(obj, value);
|
|
1635
|
-
return value;
|
|
1636
|
-
};
|
|
1637
|
-
var __privateMethod$5 = (obj, member, method) => {
|
|
1638
|
-
__accessCheck$8(obj, member, "access private method");
|
|
1639
|
-
return method;
|
|
1640
|
-
};
|
|
1641
|
-
var _startPromise, _features, _extensionPoints, _serviceRegistry, _registeredFeatures, _getInitDeps, getInitDeps_fn, _addFeature, addFeature_fn, _doStart, doStart_fn, _getRootLifecycleImpl, getRootLifecycleImpl_fn, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn;
|
|
1642
1300
|
class BackendInitializer {
|
|
1301
|
+
#startPromise;
|
|
1302
|
+
#features = new Array();
|
|
1303
|
+
#extensionPoints = /* @__PURE__ */ new Map();
|
|
1304
|
+
#serviceRegistry;
|
|
1305
|
+
#registeredFeatures = new Array();
|
|
1643
1306
|
constructor(defaultApiFactories) {
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
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);
|
|
1656
1340
|
}
|
|
1657
1341
|
add(feature) {
|
|
1658
|
-
if (
|
|
1342
|
+
if (this.#startPromise) {
|
|
1659
1343
|
throw new Error("feature can not be added after the backend has started");
|
|
1660
1344
|
}
|
|
1661
|
-
|
|
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
|
+
}
|
|
1662
1367
|
}
|
|
1663
1368
|
async start() {
|
|
1664
|
-
if (
|
|
1369
|
+
if (this.#startPromise) {
|
|
1665
1370
|
throw new Error("Backend has already started");
|
|
1666
1371
|
}
|
|
1667
1372
|
const exitHandler = async () => {
|
|
@@ -1679,242 +1384,187 @@ class BackendInitializer {
|
|
|
1679
1384
|
process.addListener("SIGTERM", exitHandler);
|
|
1680
1385
|
process.addListener("SIGINT", exitHandler);
|
|
1681
1386
|
process.addListener("beforeExit", exitHandler);
|
|
1682
|
-
|
|
1683
|
-
await
|
|
1387
|
+
this.#startPromise = this.#doStart();
|
|
1388
|
+
await this.#startPromise;
|
|
1684
1389
|
}
|
|
1685
|
-
async
|
|
1686
|
-
|
|
1687
|
-
|
|
1390
|
+
async #doStart() {
|
|
1391
|
+
this.#serviceRegistry.checkForCircularDeps();
|
|
1392
|
+
for (const feature of this.#registeredFeatures) {
|
|
1393
|
+
this.#addFeature(await feature);
|
|
1688
1394
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
_startPromise = new WeakMap();
|
|
1698
|
-
_features = new WeakMap();
|
|
1699
|
-
_extensionPoints = new WeakMap();
|
|
1700
|
-
_serviceRegistry = new WeakMap();
|
|
1701
|
-
_registeredFeatures = new WeakMap();
|
|
1702
|
-
_getInitDeps = new WeakSet();
|
|
1703
|
-
getInitDeps_fn = async function(deps, pluginId, moduleId) {
|
|
1704
|
-
const result = /* @__PURE__ */ new Map();
|
|
1705
|
-
const missingRefs = /* @__PURE__ */ new Set();
|
|
1706
|
-
for (const [name, ref] of Object.entries(deps)) {
|
|
1707
|
-
const ep = __privateGet$6(this, _extensionPoints).get(ref.id);
|
|
1708
|
-
if (ep) {
|
|
1709
|
-
if (ep.pluginId !== pluginId) {
|
|
1710
|
-
throw new Error(
|
|
1711
|
-
`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.`
|
|
1712
|
-
);
|
|
1395
|
+
const featureDiscovery = await this.#serviceRegistry.get(
|
|
1396
|
+
alpha.featureDiscoveryServiceRef,
|
|
1397
|
+
"root"
|
|
1398
|
+
);
|
|
1399
|
+
if (featureDiscovery) {
|
|
1400
|
+
const { features } = await featureDiscovery.getBackendFeatures();
|
|
1401
|
+
for (const feature of features) {
|
|
1402
|
+
this.#addFeature(feature);
|
|
1713
1403
|
}
|
|
1714
|
-
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
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
|
-
throw new Error(
|
|
1753
|
-
`Failed to add feature, invalid feature ${JSON.stringify(feature)}`
|
|
1754
|
-
);
|
|
1755
|
-
}
|
|
1756
|
-
};
|
|
1757
|
-
_doStart = new WeakSet();
|
|
1758
|
-
doStart_fn = async function() {
|
|
1759
|
-
__privateGet$6(this, _serviceRegistry).checkForCircularDeps();
|
|
1760
|
-
for (const feature of __privateGet$6(this, _registeredFeatures)) {
|
|
1761
|
-
__privateMethod$5(this, _addFeature, addFeature_fn).call(this, await feature);
|
|
1762
|
-
}
|
|
1763
|
-
const featureDiscovery = await __privateGet$6(this, _serviceRegistry).get(
|
|
1764
|
-
alpha.featureDiscoveryServiceRef,
|
|
1765
|
-
"root"
|
|
1766
|
-
);
|
|
1767
|
-
if (featureDiscovery) {
|
|
1768
|
-
const { features } = await featureDiscovery.getBackendFeatures();
|
|
1769
|
-
for (const feature of features) {
|
|
1770
|
-
__privateMethod$5(this, _addFeature, addFeature_fn).call(this, feature);
|
|
1771
|
-
}
|
|
1772
|
-
__privateGet$6(this, _serviceRegistry).checkForCircularDeps();
|
|
1773
|
-
}
|
|
1774
|
-
await __privateGet$6(this, _serviceRegistry).initializeEagerServicesWithScope("root");
|
|
1775
|
-
const pluginInits = /* @__PURE__ */ new Map();
|
|
1776
|
-
const moduleInits = /* @__PURE__ */ new Map();
|
|
1777
|
-
for (const feature of __privateGet$6(this, _features)) {
|
|
1778
|
-
for (const r of feature.getRegistrations()) {
|
|
1779
|
-
const provides = /* @__PURE__ */ new Set();
|
|
1780
|
-
if (r.type === "plugin" || r.type === "module") {
|
|
1781
|
-
for (const [extRef, extImpl] of r.extensionPoints) {
|
|
1782
|
-
if (__privateGet$6(this, _extensionPoints).has(extRef.id)) {
|
|
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)) {
|
|
1783
1442
|
throw new Error(
|
|
1784
|
-
`
|
|
1443
|
+
`Module '${r.moduleId}' for plugin '${r.pluginId}' is already registered`
|
|
1785
1444
|
);
|
|
1786
1445
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1446
|
+
modules.set(r.moduleId, {
|
|
1447
|
+
provides,
|
|
1448
|
+
consumes: new Set(Object.values(r.init.deps)),
|
|
1449
|
+
init: r.init
|
|
1790
1450
|
});
|
|
1791
|
-
provides.add(extRef);
|
|
1792
1451
|
}
|
|
1793
1452
|
}
|
|
1794
|
-
if (r.type === "plugin") {
|
|
1795
|
-
if (pluginInits.has(r.pluginId)) {
|
|
1796
|
-
throw new Error(`Plugin '${r.pluginId}' is already registered`);
|
|
1797
|
-
}
|
|
1798
|
-
pluginInits.set(r.pluginId, {
|
|
1799
|
-
provides,
|
|
1800
|
-
consumes: new Set(Object.values(r.init.deps)),
|
|
1801
|
-
init: r.init
|
|
1802
|
-
});
|
|
1803
|
-
} else {
|
|
1804
|
-
let modules = moduleInits.get(r.pluginId);
|
|
1805
|
-
if (!modules) {
|
|
1806
|
-
modules = /* @__PURE__ */ new Map();
|
|
1807
|
-
moduleInits.set(r.pluginId, modules);
|
|
1808
|
-
}
|
|
1809
|
-
if (modules.has(r.moduleId)) {
|
|
1810
|
-
throw new Error(
|
|
1811
|
-
`Module '${r.moduleId}' for plugin '${r.pluginId}' is already registered`
|
|
1812
|
-
);
|
|
1813
|
-
}
|
|
1814
|
-
modules.set(r.moduleId, {
|
|
1815
|
-
provides,
|
|
1816
|
-
consumes: new Set(Object.values(r.init.deps)),
|
|
1817
|
-
init: r.init
|
|
1818
|
-
});
|
|
1819
|
-
}
|
|
1820
1453
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
pluginId
|
|
1832
|
-
);
|
|
1833
|
-
const modules = moduleInits.get(pluginId);
|
|
1834
|
-
if (modules) {
|
|
1835
|
-
const tree = DependencyGraph.fromIterable(
|
|
1836
|
-
Array.from(modules).map(([moduleId, moduleInit]) => ({
|
|
1837
|
-
value: { moduleId, moduleInit },
|
|
1838
|
-
// Relationships are reversed at this point since we're only interested in the extension points.
|
|
1839
|
-
// If a modules provides extension point A we want it to be initialized AFTER all modules
|
|
1840
|
-
// that depend on extension point A, so that they can provide their extensions.
|
|
1841
|
-
consumes: Array.from(moduleInit.provides).map((p) => p.id),
|
|
1842
|
-
provides: Array.from(moduleInit.consumes).map((c) => c.id)
|
|
1843
|
-
}))
|
|
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
|
|
1844
1464
|
);
|
|
1845
|
-
const
|
|
1846
|
-
if (
|
|
1847
|
-
|
|
1848
|
-
|
|
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
|
+
}))
|
|
1849
1476
|
);
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
throw new errors.ForwardedError(
|
|
1856
|
-
`Module '${moduleId}' for plugin '${pluginId}' startup failed`,
|
|
1857
|
-
error
|
|
1858
|
-
);
|
|
1859
|
-
});
|
|
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
|
+
);
|
|
1860
1482
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
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
|
+
}
|
|
1870
1497
|
);
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
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
|
+
})
|
|
1885
1516
|
);
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
+
}
|
|
1894
1532
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
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();
|
|
1904
1543
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
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");
|
|
1915
1555
|
}
|
|
1916
|
-
|
|
1917
|
-
|
|
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
|
+
}
|
|
1918
1568
|
function isServiceFactory(feature) {
|
|
1919
1569
|
return !!feature.service;
|
|
1920
1570
|
}
|
|
@@ -1922,45 +1572,25 @@ function isInternalBackendFeature(feature) {
|
|
|
1922
1572
|
return typeof feature.getRegistrations === "function";
|
|
1923
1573
|
}
|
|
1924
1574
|
|
|
1925
|
-
var __accessCheck$7 = (obj, member, msg) => {
|
|
1926
|
-
if (!member.has(obj))
|
|
1927
|
-
throw TypeError("Cannot " + msg);
|
|
1928
|
-
};
|
|
1929
|
-
var __privateGet$5 = (obj, member, getter) => {
|
|
1930
|
-
__accessCheck$7(obj, member, "read from private field");
|
|
1931
|
-
return member.get(obj);
|
|
1932
|
-
};
|
|
1933
|
-
var __privateAdd$7 = (obj, member, value) => {
|
|
1934
|
-
if (member.has(obj))
|
|
1935
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
1936
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
1937
|
-
};
|
|
1938
|
-
var __privateSet$3 = (obj, member, value, setter) => {
|
|
1939
|
-
__accessCheck$7(obj, member, "write to private field");
|
|
1940
|
-
member.set(obj, value);
|
|
1941
|
-
return value;
|
|
1942
|
-
};
|
|
1943
|
-
var _initializer;
|
|
1944
1575
|
class BackstageBackend {
|
|
1576
|
+
#initializer;
|
|
1945
1577
|
constructor(defaultServiceFactories) {
|
|
1946
|
-
|
|
1947
|
-
__privateSet$3(this, _initializer, new BackendInitializer(defaultServiceFactories));
|
|
1578
|
+
this.#initializer = new BackendInitializer(defaultServiceFactories);
|
|
1948
1579
|
}
|
|
1949
1580
|
add(feature) {
|
|
1950
1581
|
if (isPromise(feature)) {
|
|
1951
|
-
|
|
1582
|
+
this.#initializer.add(feature.then((f) => unwrapFeature(f.default)));
|
|
1952
1583
|
} else {
|
|
1953
|
-
|
|
1584
|
+
this.#initializer.add(unwrapFeature(feature));
|
|
1954
1585
|
}
|
|
1955
1586
|
}
|
|
1956
1587
|
async start() {
|
|
1957
|
-
await
|
|
1588
|
+
await this.#initializer.start();
|
|
1958
1589
|
}
|
|
1959
1590
|
async stop() {
|
|
1960
|
-
await
|
|
1591
|
+
await this.#initializer.stop();
|
|
1961
1592
|
}
|
|
1962
1593
|
}
|
|
1963
|
-
_initializer = new WeakMap();
|
|
1964
1594
|
function isPromise(value) {
|
|
1965
1595
|
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
1966
1596
|
}
|
|
@@ -2021,10 +1651,9 @@ class DatabaseKeyStore {
|
|
|
2021
1651
|
this.logger = logger;
|
|
2022
1652
|
}
|
|
2023
1653
|
static async create(options) {
|
|
2024
|
-
var _a;
|
|
2025
1654
|
const { database, logger } = options;
|
|
2026
1655
|
const client = await database.getClient();
|
|
2027
|
-
if (!
|
|
1656
|
+
if (!database.migrations?.skip) {
|
|
2028
1657
|
await applyDatabaseMigrations(client);
|
|
2029
1658
|
}
|
|
2030
1659
|
return new DatabaseKeyStore(client, logger);
|
|
@@ -2113,20 +1742,6 @@ function toInternalBackstageCredentials(credentials) {
|
|
|
2113
1742
|
return internalCredentials;
|
|
2114
1743
|
}
|
|
2115
1744
|
|
|
2116
|
-
var __accessCheck$6 = (obj, member, msg) => {
|
|
2117
|
-
if (!member.has(obj))
|
|
2118
|
-
throw TypeError("Cannot " + msg);
|
|
2119
|
-
};
|
|
2120
|
-
var __privateAdd$6 = (obj, member, value) => {
|
|
2121
|
-
if (member.has(obj))
|
|
2122
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
2123
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
2124
|
-
};
|
|
2125
|
-
var __privateMethod$4 = (obj, member, method) => {
|
|
2126
|
-
__accessCheck$6(obj, member, "access private method");
|
|
2127
|
-
return method;
|
|
2128
|
-
};
|
|
2129
|
-
var _getJwtExpiration, getJwtExpiration_fn;
|
|
2130
1745
|
class DefaultAuthService {
|
|
2131
1746
|
constructor(userTokenHandler, pluginTokenHandler, externalTokenHandler, tokenManager, pluginId, disableDefaultAuthPolicy, publicKeyStore) {
|
|
2132
1747
|
this.userTokenHandler = userTokenHandler;
|
|
@@ -2136,7 +1751,6 @@ class DefaultAuthService {
|
|
|
2136
1751
|
this.pluginId = pluginId;
|
|
2137
1752
|
this.disableDefaultAuthPolicy = disableDefaultAuthPolicy;
|
|
2138
1753
|
this.publicKeyStore = publicKeyStore;
|
|
2139
|
-
__privateAdd$6(this, _getJwtExpiration);
|
|
2140
1754
|
}
|
|
2141
1755
|
// allowLimitedAccess is currently ignored, since we currently always use the full user tokens
|
|
2142
1756
|
async authenticate(token) {
|
|
@@ -2154,7 +1768,7 @@ class DefaultAuthService {
|
|
|
2154
1768
|
return createCredentialsWithUserPrincipal(
|
|
2155
1769
|
userResult2.userEntityRef,
|
|
2156
1770
|
pluginResult.limitedUserToken,
|
|
2157
|
-
|
|
1771
|
+
this.#getJwtExpiration(pluginResult.limitedUserToken)
|
|
2158
1772
|
);
|
|
2159
1773
|
}
|
|
2160
1774
|
return createCredentialsWithServicePrincipal(pluginResult.subject);
|
|
@@ -2164,7 +1778,7 @@ class DefaultAuthService {
|
|
|
2164
1778
|
return createCredentialsWithUserPrincipal(
|
|
2165
1779
|
userResult.userEntityRef,
|
|
2166
1780
|
token,
|
|
2167
|
-
|
|
1781
|
+
this.#getJwtExpiration(token)
|
|
2168
1782
|
);
|
|
2169
1783
|
}
|
|
2170
1784
|
const externalResult = await this.externalTokenHandler.verifyToken(token);
|
|
@@ -2252,49 +1866,29 @@ class DefaultAuthService {
|
|
|
2252
1866
|
const { keys } = await this.publicKeyStore.listKeys();
|
|
2253
1867
|
return { keys: keys.map(({ key }) => key) };
|
|
2254
1868
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
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);
|
|
2261
1875
|
}
|
|
2262
|
-
|
|
2263
|
-
};
|
|
1876
|
+
}
|
|
2264
1877
|
|
|
2265
|
-
var __accessCheck$5 = (obj, member, msg) => {
|
|
2266
|
-
if (!member.has(obj))
|
|
2267
|
-
throw TypeError("Cannot " + msg);
|
|
2268
|
-
};
|
|
2269
|
-
var __privateGet$4 = (obj, member, getter) => {
|
|
2270
|
-
__accessCheck$5(obj, member, "read from private field");
|
|
2271
|
-
return member.get(obj);
|
|
2272
|
-
};
|
|
2273
|
-
var __privateAdd$5 = (obj, member, value) => {
|
|
2274
|
-
if (member.has(obj))
|
|
2275
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
2276
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
2277
|
-
};
|
|
2278
|
-
var __privateSet$2 = (obj, member, value, setter) => {
|
|
2279
|
-
__accessCheck$5(obj, member, "write to private field");
|
|
2280
|
-
member.set(obj, value);
|
|
2281
|
-
return value;
|
|
2282
|
-
};
|
|
2283
|
-
var _keyStore, _keyStoreUpdated;
|
|
2284
1878
|
const CLOCK_MARGIN_S = 10;
|
|
2285
1879
|
class JwksClient {
|
|
2286
1880
|
constructor(getEndpoint) {
|
|
2287
1881
|
this.getEndpoint = getEndpoint;
|
|
2288
|
-
__privateAdd$5(this, _keyStore, void 0);
|
|
2289
|
-
__privateAdd$5(this, _keyStoreUpdated, 0);
|
|
2290
1882
|
}
|
|
1883
|
+
#keyStore;
|
|
1884
|
+
#keyStoreUpdated = 0;
|
|
2291
1885
|
get getKey() {
|
|
2292
|
-
if (!
|
|
1886
|
+
if (!this.#keyStore) {
|
|
2293
1887
|
throw new errors.AuthenticationError(
|
|
2294
1888
|
"refreshKeyStore must be called before jwksClient.getKey"
|
|
2295
1889
|
);
|
|
2296
1890
|
}
|
|
2297
|
-
return
|
|
1891
|
+
return this.#keyStore;
|
|
2298
1892
|
}
|
|
2299
1893
|
/**
|
|
2300
1894
|
* If the last keystore refresh is stale, update the keystore URL to the latest
|
|
@@ -2304,9 +1898,9 @@ class JwksClient {
|
|
|
2304
1898
|
const header = await jose.decodeProtectedHeader(rawJwtToken);
|
|
2305
1899
|
let keyStoreHasKey;
|
|
2306
1900
|
try {
|
|
2307
|
-
if (
|
|
1901
|
+
if (this.#keyStore) {
|
|
2308
1902
|
const [_, rawPayload, rawSignature] = rawJwtToken.split(".");
|
|
2309
|
-
keyStoreHasKey = await
|
|
1903
|
+
keyStoreHasKey = await this.#keyStore(header, {
|
|
2310
1904
|
payload: rawPayload,
|
|
2311
1905
|
signature: rawSignature
|
|
2312
1906
|
});
|
|
@@ -2314,23 +1908,15 @@ class JwksClient {
|
|
|
2314
1908
|
} catch (error) {
|
|
2315
1909
|
keyStoreHasKey = false;
|
|
2316
1910
|
}
|
|
2317
|
-
const issuedAfterLastRefresh =
|
|
2318
|
-
if (!
|
|
1911
|
+
const issuedAfterLastRefresh = payload?.iat && payload.iat > this.#keyStoreUpdated - CLOCK_MARGIN_S;
|
|
1912
|
+
if (!this.#keyStore || !keyStoreHasKey && issuedAfterLastRefresh) {
|
|
2319
1913
|
const endpoint = await this.getEndpoint();
|
|
2320
|
-
|
|
2321
|
-
|
|
1914
|
+
this.#keyStore = jose.createRemoteJWKSet(endpoint);
|
|
1915
|
+
this.#keyStoreUpdated = Date.now() / 1e3;
|
|
2322
1916
|
}
|
|
2323
1917
|
}
|
|
2324
1918
|
}
|
|
2325
|
-
_keyStore = new WeakMap();
|
|
2326
|
-
_keyStoreUpdated = new WeakMap();
|
|
2327
1919
|
|
|
2328
|
-
var __defProp = Object.defineProperty;
|
|
2329
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
2330
|
-
var __publicField = (obj, key, value) => {
|
|
2331
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
2332
|
-
return value;
|
|
2333
|
-
};
|
|
2334
1920
|
const KEY_EXPIRATION_MARGIN_FACTOR = 3;
|
|
2335
1921
|
const SECONDS_IN_MS = 1e3;
|
|
2336
1922
|
const ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;
|
|
@@ -2342,21 +1928,20 @@ class PluginTokenHandler {
|
|
|
2342
1928
|
this.keyDurationSeconds = keyDurationSeconds;
|
|
2343
1929
|
this.algorithm = algorithm;
|
|
2344
1930
|
this.discovery = discovery;
|
|
2345
|
-
__publicField(this, "privateKeyPromise");
|
|
2346
|
-
__publicField(this, "keyExpiry");
|
|
2347
|
-
__publicField(this, "jwksMap", /* @__PURE__ */ new Map());
|
|
2348
|
-
// Tracking state for isTargetPluginSupported
|
|
2349
|
-
__publicField(this, "supportedTargetPlugins", /* @__PURE__ */ new Set());
|
|
2350
|
-
__publicField(this, "targetPluginInflightChecks", /* @__PURE__ */ new Map());
|
|
2351
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();
|
|
2352
1938
|
static create(options) {
|
|
2353
|
-
var _a;
|
|
2354
1939
|
return new PluginTokenHandler(
|
|
2355
1940
|
options.logger,
|
|
2356
1941
|
options.ownPluginId,
|
|
2357
1942
|
options.publicKeyStore,
|
|
2358
1943
|
Math.round(types.durationToMilliseconds(options.keyDuration) / 1e3),
|
|
2359
|
-
|
|
1944
|
+
options.algorithm ?? "ES256",
|
|
2360
1945
|
options.discovery
|
|
2361
1946
|
);
|
|
2362
1947
|
}
|
|
@@ -2404,7 +1989,7 @@ class PluginTokenHandler {
|
|
|
2404
1989
|
ourExp,
|
|
2405
1990
|
Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS)
|
|
2406
1991
|
) : ourExp;
|
|
2407
|
-
const claims = { sub, aud, iat, exp, obo: onBehalfOf
|
|
1992
|
+
const claims = { sub, aud, iat, exp, obo: onBehalfOf?.token };
|
|
2408
1993
|
const token = await new jose.SignJWT(claims).setProtectedHeader({
|
|
2409
1994
|
typ: pluginAuthNode.tokenTypes.plugin.typParam,
|
|
2410
1995
|
alg: this.algorithm,
|
|
@@ -2510,34 +2095,19 @@ class PluginTokenHandler {
|
|
|
2510
2095
|
}
|
|
2511
2096
|
}
|
|
2512
2097
|
|
|
2513
|
-
|
|
2514
|
-
if (!member.has(obj))
|
|
2515
|
-
throw TypeError("Cannot " + msg);
|
|
2516
|
-
};
|
|
2517
|
-
var __privateAdd$4 = (obj, member, value) => {
|
|
2518
|
-
if (member.has(obj))
|
|
2519
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
2520
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
2521
|
-
};
|
|
2522
|
-
var __privateMethod$3 = (obj, member, method) => {
|
|
2523
|
-
__accessCheck$4(obj, member, "access private method");
|
|
2524
|
-
return method;
|
|
2525
|
-
};
|
|
2526
|
-
var _getTokenVerificationOptions, getTokenVerificationOptions_fn;
|
|
2527
|
-
const _UserTokenHandler = class _UserTokenHandler {
|
|
2098
|
+
class UserTokenHandler {
|
|
2528
2099
|
constructor(jwksClient) {
|
|
2529
2100
|
this.jwksClient = jwksClient;
|
|
2530
|
-
__privateAdd$4(this, _getTokenVerificationOptions);
|
|
2531
2101
|
}
|
|
2532
2102
|
static create(options) {
|
|
2533
2103
|
const jwksClient = new JwksClient(async () => {
|
|
2534
2104
|
const url = await options.discovery.getBaseUrl("auth");
|
|
2535
2105
|
return new URL(`${url}/.well-known/jwks.json`);
|
|
2536
2106
|
});
|
|
2537
|
-
return new
|
|
2107
|
+
return new UserTokenHandler(jwksClient);
|
|
2538
2108
|
}
|
|
2539
2109
|
async verifyToken(token) {
|
|
2540
|
-
const verifyOpts =
|
|
2110
|
+
const verifyOpts = this.#getTokenVerificationOptions(token);
|
|
2541
2111
|
if (!verifyOpts) {
|
|
2542
2112
|
return void 0;
|
|
2543
2113
|
}
|
|
@@ -2555,6 +2125,31 @@ const _UserTokenHandler = class _UserTokenHandler {
|
|
|
2555
2125
|
}
|
|
2556
2126
|
return { userEntityRef };
|
|
2557
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
|
+
}
|
|
2558
2153
|
createLimitedUserToken(backstageToken) {
|
|
2559
2154
|
const [headerRaw, payloadRaw] = backstageToken.split(".");
|
|
2560
2155
|
const header = JSON.parse(
|
|
@@ -2600,64 +2195,31 @@ const _UserTokenHandler = class _UserTokenHandler {
|
|
|
2600
2195
|
return false;
|
|
2601
2196
|
}
|
|
2602
2197
|
}
|
|
2603
|
-
}
|
|
2604
|
-
_getTokenVerificationOptions = new WeakSet();
|
|
2605
|
-
getTokenVerificationOptions_fn = function(token) {
|
|
2606
|
-
try {
|
|
2607
|
-
const { typ } = jose.decodeProtectedHeader(token);
|
|
2608
|
-
if (typ === pluginAuthNode.tokenTypes.user.typParam) {
|
|
2609
|
-
return {
|
|
2610
|
-
requiredClaims: ["iat", "exp", "sub"],
|
|
2611
|
-
typ: pluginAuthNode.tokenTypes.user.typParam
|
|
2612
|
-
};
|
|
2613
|
-
}
|
|
2614
|
-
if (typ === pluginAuthNode.tokenTypes.limitedUser.typParam) {
|
|
2615
|
-
return {
|
|
2616
|
-
requiredClaims: ["iat", "exp", "sub"],
|
|
2617
|
-
typ: pluginAuthNode.tokenTypes.limitedUser.typParam
|
|
2618
|
-
};
|
|
2619
|
-
}
|
|
2620
|
-
const { aud } = jose.decodeJwt(token);
|
|
2621
|
-
if (aud === pluginAuthNode.tokenTypes.user.audClaim) {
|
|
2622
|
-
return {
|
|
2623
|
-
audience: pluginAuthNode.tokenTypes.user.audClaim
|
|
2624
|
-
};
|
|
2625
|
-
}
|
|
2626
|
-
} catch {
|
|
2627
|
-
}
|
|
2628
|
-
return void 0;
|
|
2629
|
-
};
|
|
2630
|
-
let UserTokenHandler = _UserTokenHandler;
|
|
2198
|
+
}
|
|
2631
2199
|
|
|
2632
|
-
var __accessCheck$3 = (obj, member, msg) => {
|
|
2633
|
-
if (!member.has(obj))
|
|
2634
|
-
throw TypeError("Cannot " + msg);
|
|
2635
|
-
};
|
|
2636
|
-
var __privateGet$3 = (obj, member, getter) => {
|
|
2637
|
-
__accessCheck$3(obj, member, "read from private field");
|
|
2638
|
-
return member.get(obj);
|
|
2639
|
-
};
|
|
2640
|
-
var __privateAdd$3 = (obj, member, value) => {
|
|
2641
|
-
if (member.has(obj))
|
|
2642
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
2643
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
2644
|
-
};
|
|
2645
|
-
var __privateMethod$2 = (obj, member, method) => {
|
|
2646
|
-
__accessCheck$3(obj, member, "access private method");
|
|
2647
|
-
return method;
|
|
2648
|
-
};
|
|
2649
|
-
var _entries$1, _doAdd, doAdd_fn;
|
|
2650
2200
|
class LegacyTokenHandler {
|
|
2651
|
-
|
|
2652
|
-
__privateAdd$3(this, _doAdd);
|
|
2653
|
-
__privateAdd$3(this, _entries$1, []);
|
|
2654
|
-
}
|
|
2201
|
+
#entries = [];
|
|
2655
2202
|
add(options) {
|
|
2656
|
-
|
|
2203
|
+
this.#doAdd(options.getString("secret"), options.getString("subject"));
|
|
2657
2204
|
}
|
|
2658
2205
|
// used only for the old backend.auth.keys array
|
|
2659
2206
|
addOld(options) {
|
|
2660
|
-
|
|
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 });
|
|
2661
2223
|
}
|
|
2662
2224
|
async verifyToken(token) {
|
|
2663
2225
|
try {
|
|
@@ -2672,7 +2234,7 @@ class LegacyTokenHandler {
|
|
|
2672
2234
|
} catch (e) {
|
|
2673
2235
|
return void 0;
|
|
2674
2236
|
}
|
|
2675
|
-
for (const entry of
|
|
2237
|
+
for (const entry of this.#entries) {
|
|
2676
2238
|
try {
|
|
2677
2239
|
await jose.jwtVerify(token, entry.key);
|
|
2678
2240
|
return { subject: entry.subject };
|
|
@@ -2685,43 +2247,10 @@ class LegacyTokenHandler {
|
|
|
2685
2247
|
return void 0;
|
|
2686
2248
|
}
|
|
2687
2249
|
}
|
|
2688
|
-
_entries$1 = new WeakMap();
|
|
2689
|
-
_doAdd = new WeakSet();
|
|
2690
|
-
doAdd_fn = function(secret, subject) {
|
|
2691
|
-
if (!secret.match(/^\S+$/)) {
|
|
2692
|
-
throw new Error("Illegal secret, must be a valid base64 string");
|
|
2693
|
-
}
|
|
2694
|
-
let key;
|
|
2695
|
-
try {
|
|
2696
|
-
key = jose.base64url.decode(secret);
|
|
2697
|
-
} catch {
|
|
2698
|
-
throw new Error("Illegal secret, must be a valid base64 string");
|
|
2699
|
-
}
|
|
2700
|
-
if (!subject.match(/^\S+$/)) {
|
|
2701
|
-
throw new Error("Illegal subject, must be a set of non-space characters");
|
|
2702
|
-
}
|
|
2703
|
-
__privateGet$3(this, _entries$1).push({ key, subject });
|
|
2704
|
-
};
|
|
2705
2250
|
|
|
2706
|
-
var __accessCheck$2 = (obj, member, msg) => {
|
|
2707
|
-
if (!member.has(obj))
|
|
2708
|
-
throw TypeError("Cannot " + msg);
|
|
2709
|
-
};
|
|
2710
|
-
var __privateGet$2 = (obj, member, getter) => {
|
|
2711
|
-
__accessCheck$2(obj, member, "read from private field");
|
|
2712
|
-
return member.get(obj);
|
|
2713
|
-
};
|
|
2714
|
-
var __privateAdd$2 = (obj, member, value) => {
|
|
2715
|
-
if (member.has(obj))
|
|
2716
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
2717
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
2718
|
-
};
|
|
2719
|
-
var _entries;
|
|
2720
2251
|
const MIN_TOKEN_LENGTH = 8;
|
|
2721
2252
|
class StaticTokenHandler {
|
|
2722
|
-
|
|
2723
|
-
__privateAdd$2(this, _entries, []);
|
|
2724
|
-
}
|
|
2253
|
+
#entries = [];
|
|
2725
2254
|
add(options) {
|
|
2726
2255
|
const token = options.getString("token");
|
|
2727
2256
|
if (!token.match(/^\S+$/)) {
|
|
@@ -2736,17 +2265,16 @@ class StaticTokenHandler {
|
|
|
2736
2265
|
if (!subject.match(/^\S+$/)) {
|
|
2737
2266
|
throw new Error("Illegal subject, must be a set of non-space characters");
|
|
2738
2267
|
}
|
|
2739
|
-
|
|
2268
|
+
this.#entries.push({ token, subject });
|
|
2740
2269
|
}
|
|
2741
2270
|
async verifyToken(token) {
|
|
2742
|
-
const entry =
|
|
2271
|
+
const entry = this.#entries.find((e) => e.token === token);
|
|
2743
2272
|
if (!entry) {
|
|
2744
2273
|
return void 0;
|
|
2745
2274
|
}
|
|
2746
2275
|
return { subject: entry.subject };
|
|
2747
2276
|
}
|
|
2748
2277
|
}
|
|
2749
|
-
_entries = new WeakMap();
|
|
2750
2278
|
|
|
2751
2279
|
const NEW_CONFIG_KEY = "backend.auth.externalAccess";
|
|
2752
2280
|
const OLD_CONFIG_KEY = "backend.auth.keys";
|
|
@@ -2755,7 +2283,6 @@ class ExternalTokenHandler {
|
|
|
2755
2283
|
this.handlers = handlers;
|
|
2756
2284
|
}
|
|
2757
2285
|
static create(options) {
|
|
2758
|
-
var _a, _b;
|
|
2759
2286
|
const { config, logger } = options;
|
|
2760
2287
|
const staticHandler = new StaticTokenHandler();
|
|
2761
2288
|
const legacyHandler = new LegacyTokenHandler();
|
|
@@ -2763,7 +2290,7 @@ class ExternalTokenHandler {
|
|
|
2763
2290
|
static: staticHandler,
|
|
2764
2291
|
legacy: legacyHandler
|
|
2765
2292
|
};
|
|
2766
|
-
const handlerConfigs =
|
|
2293
|
+
const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? [];
|
|
2767
2294
|
for (const handlerConfig of handlerConfigs) {
|
|
2768
2295
|
const type = handlerConfig.getString("type");
|
|
2769
2296
|
const handler = handlers[type];
|
|
@@ -2775,7 +2302,7 @@ class ExternalTokenHandler {
|
|
|
2775
2302
|
}
|
|
2776
2303
|
handler.add(handlerConfig.getConfig("options"));
|
|
2777
2304
|
}
|
|
2778
|
-
const legacyConfigs =
|
|
2305
|
+
const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? [];
|
|
2779
2306
|
if (legacyConfigs.length) {
|
|
2780
2307
|
logger.warn(
|
|
2781
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`
|
|
@@ -2856,10 +2383,11 @@ const cacheServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
2856
2383
|
service: backendPluginApi.coreServices.cache,
|
|
2857
2384
|
deps: {
|
|
2858
2385
|
config: backendPluginApi.coreServices.rootConfig,
|
|
2386
|
+
logger: backendPluginApi.coreServices.rootLogger,
|
|
2859
2387
|
plugin: backendPluginApi.coreServices.pluginMetadata
|
|
2860
2388
|
},
|
|
2861
|
-
async createRootContext({ config }) {
|
|
2862
|
-
return backendCommon.CacheManager.fromConfig(config);
|
|
2389
|
+
async createRootContext({ config, logger }) {
|
|
2390
|
+
return backendCommon.CacheManager.fromConfig(config, { logger });
|
|
2863
2391
|
},
|
|
2864
2392
|
async factory({ plugin }, manager) {
|
|
2865
2393
|
return manager.forPlugin(plugin.getId()).getClient();
|
|
@@ -2872,9 +2400,9 @@ const rootConfigServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
2872
2400
|
deps: {},
|
|
2873
2401
|
async factory() {
|
|
2874
2402
|
const source = configLoader.ConfigSources.default({
|
|
2875
|
-
argv: options
|
|
2876
|
-
remote: options
|
|
2877
|
-
watch: options
|
|
2403
|
+
argv: options?.argv,
|
|
2404
|
+
remote: options?.remote,
|
|
2405
|
+
watch: options?.watch
|
|
2878
2406
|
});
|
|
2879
2407
|
console.log(`Loading config from ${source}`);
|
|
2880
2408
|
return await configLoader.ConfigSources.toConfig(source);
|
|
@@ -2937,8 +2465,7 @@ class HostDiscovery {
|
|
|
2937
2465
|
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
|
|
2938
2466
|
*/
|
|
2939
2467
|
static fromConfig(config, options) {
|
|
2940
|
-
|
|
2941
|
-
const basePath = (_a = options == null ? void 0 : options.basePath) != null ? _a : "/api";
|
|
2468
|
+
const basePath = options?.basePath ?? "/api";
|
|
2942
2469
|
const externalBaseUrl = config.getString("backend.baseUrl").replace(/\/+$/, "");
|
|
2943
2470
|
const {
|
|
2944
2471
|
listen: { host: listenHost = "::", port: listenPort }
|
|
@@ -2961,9 +2488,8 @@ class HostDiscovery {
|
|
|
2961
2488
|
);
|
|
2962
2489
|
}
|
|
2963
2490
|
getTargetFromConfig(pluginId, type) {
|
|
2964
|
-
|
|
2965
|
-
const
|
|
2966
|
-
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");
|
|
2967
2493
|
if (!target) {
|
|
2968
2494
|
const baseUrl = type === "external" ? this.externalBaseUrl : this.internalBaseUrl;
|
|
2969
2495
|
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
|
|
@@ -2997,36 +2523,13 @@ const discoveryServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
2997
2523
|
}
|
|
2998
2524
|
});
|
|
2999
2525
|
|
|
3000
|
-
var __accessCheck$1 = (obj, member, msg) => {
|
|
3001
|
-
if (!member.has(obj))
|
|
3002
|
-
throw TypeError("Cannot " + msg);
|
|
3003
|
-
};
|
|
3004
|
-
var __privateGet$1 = (obj, member, getter) => {
|
|
3005
|
-
__accessCheck$1(obj, member, "read from private field");
|
|
3006
|
-
return member.get(obj);
|
|
3007
|
-
};
|
|
3008
|
-
var __privateAdd$1 = (obj, member, value) => {
|
|
3009
|
-
if (member.has(obj))
|
|
3010
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
3011
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
3012
|
-
};
|
|
3013
|
-
var __privateSet$1 = (obj, member, value, setter) => {
|
|
3014
|
-
__accessCheck$1(obj, member, "write to private field");
|
|
3015
|
-
member.set(obj, value);
|
|
3016
|
-
return value;
|
|
3017
|
-
};
|
|
3018
|
-
var __privateMethod$1 = (obj, member, method) => {
|
|
3019
|
-
__accessCheck$1(obj, member, "access private method");
|
|
3020
|
-
return method;
|
|
3021
|
-
};
|
|
3022
|
-
var _auth, _discovery, _pluginId, _extractCredentialsFromRequest, extractCredentialsFromRequest_fn, _extractLimitedCredentialsFromRequest, extractLimitedCredentialsFromRequest_fn, _getCredentials, getCredentials_fn, _getLimitedCredentials, getLimitedCredentials_fn, _getCookieOptions, getCookieOptions_fn, _existingCookieExpiration, existingCookieExpiration_fn;
|
|
3023
2526
|
const FIVE_MINUTES_MS = 5 * 60 * 1e3;
|
|
3024
2527
|
const BACKSTAGE_AUTH_COOKIE = "backstage-auth";
|
|
3025
2528
|
function getTokenFromRequest(req) {
|
|
3026
2529
|
const authHeader = req.headers.authorization;
|
|
3027
2530
|
if (typeof authHeader === "string") {
|
|
3028
2531
|
const matches = authHeader.match(/^Bearer[ ]+(\S+)$/i);
|
|
3029
|
-
const token = matches
|
|
2532
|
+
const token = matches?.[1];
|
|
3030
2533
|
if (token) {
|
|
3031
2534
|
return token;
|
|
3032
2535
|
}
|
|
@@ -3050,39 +2553,61 @@ function willExpireSoon(expiresAt) {
|
|
|
3050
2553
|
const credentialsSymbol = Symbol("backstage-credentials");
|
|
3051
2554
|
const limitedCredentialsSymbol = Symbol("backstage-limited-credentials");
|
|
3052
2555
|
class DefaultHttpAuthService {
|
|
2556
|
+
#auth;
|
|
2557
|
+
#discovery;
|
|
2558
|
+
#pluginId;
|
|
3053
2559
|
constructor(auth, discovery, pluginId) {
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
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);
|
|
3066
2591
|
}
|
|
3067
2592
|
async credentials(req, options) {
|
|
3068
|
-
const credentials =
|
|
3069
|
-
const allowed = options
|
|
2593
|
+
const credentials = options?.allowLimitedAccess ? await this.#getLimitedCredentials(req) : await this.#getCredentials(req);
|
|
2594
|
+
const allowed = options?.allow;
|
|
3070
2595
|
if (!allowed) {
|
|
3071
2596
|
return credentials;
|
|
3072
2597
|
}
|
|
3073
|
-
if (
|
|
2598
|
+
if (this.#auth.isPrincipal(credentials, "none")) {
|
|
3074
2599
|
if (allowed.includes("none")) {
|
|
3075
2600
|
return credentials;
|
|
3076
2601
|
}
|
|
3077
2602
|
throw new errors.AuthenticationError("Missing credentials");
|
|
3078
|
-
} else if (
|
|
2603
|
+
} else if (this.#auth.isPrincipal(credentials, "user")) {
|
|
3079
2604
|
if (allowed.includes("user")) {
|
|
3080
2605
|
return credentials;
|
|
3081
2606
|
}
|
|
3082
2607
|
throw new errors.NotAllowedError(
|
|
3083
2608
|
`This endpoint does not allow 'user' credentials`
|
|
3084
2609
|
);
|
|
3085
|
-
} else if (
|
|
2610
|
+
} else if (this.#auth.isPrincipal(credentials, "service")) {
|
|
3086
2611
|
if (allowed.includes("service")) {
|
|
3087
2612
|
return credentials;
|
|
3088
2613
|
}
|
|
@@ -3099,15 +2624,15 @@ class DefaultHttpAuthService {
|
|
|
3099
2624
|
throw new Error("Failed to issue user cookie, headers were already sent");
|
|
3100
2625
|
}
|
|
3101
2626
|
let credentials;
|
|
3102
|
-
if (options
|
|
3103
|
-
if (
|
|
2627
|
+
if (options?.credentials) {
|
|
2628
|
+
if (this.#auth.isPrincipal(options.credentials, "none")) {
|
|
3104
2629
|
res.clearCookie(
|
|
3105
2630
|
BACKSTAGE_AUTH_COOKIE,
|
|
3106
|
-
await
|
|
2631
|
+
await this.#getCookieOptions(res.req)
|
|
3107
2632
|
);
|
|
3108
2633
|
return { expiresAt: /* @__PURE__ */ new Date() };
|
|
3109
2634
|
}
|
|
3110
|
-
if (!
|
|
2635
|
+
if (!this.#auth.isPrincipal(options.credentials, "user")) {
|
|
3111
2636
|
throw new errors.AuthenticationError(
|
|
3112
2637
|
"Refused to issue cookie for non-user principal"
|
|
3113
2638
|
);
|
|
@@ -3116,99 +2641,60 @@ class DefaultHttpAuthService {
|
|
|
3116
2641
|
} else {
|
|
3117
2642
|
credentials = await this.credentials(res.req, { allow: ["user"] });
|
|
3118
2643
|
}
|
|
3119
|
-
const existingExpiresAt = await
|
|
2644
|
+
const existingExpiresAt = await this.#existingCookieExpiration(res.req);
|
|
3120
2645
|
if (existingExpiresAt && !willExpireSoon(existingExpiresAt)) {
|
|
3121
2646
|
return { expiresAt: existingExpiresAt };
|
|
3122
2647
|
}
|
|
3123
|
-
const { token, expiresAt } = await
|
|
2648
|
+
const { token, expiresAt } = await this.#auth.getLimitedUserToken(
|
|
3124
2649
|
credentials
|
|
3125
2650
|
);
|
|
3126
2651
|
if (!token) {
|
|
3127
2652
|
throw new Error("User credentials is unexpectedly missing token");
|
|
3128
2653
|
}
|
|
3129
2654
|
res.cookie(BACKSTAGE_AUTH_COOKIE, token, {
|
|
3130
|
-
...await
|
|
2655
|
+
...await this.#getCookieOptions(res.req),
|
|
3131
2656
|
expires: expiresAt
|
|
3132
2657
|
});
|
|
3133
2658
|
return { expiresAt };
|
|
3134
2659
|
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
_pluginId = new WeakMap();
|
|
3139
|
-
_extractCredentialsFromRequest = new WeakSet();
|
|
3140
|
-
extractCredentialsFromRequest_fn = async function(req) {
|
|
3141
|
-
const token = getTokenFromRequest(req);
|
|
3142
|
-
if (!token) {
|
|
3143
|
-
return await __privateGet$1(this, _auth).getNoneCredentials();
|
|
3144
|
-
}
|
|
3145
|
-
return await __privateGet$1(this, _auth).authenticate(token);
|
|
3146
|
-
};
|
|
3147
|
-
_extractLimitedCredentialsFromRequest = new WeakSet();
|
|
3148
|
-
extractLimitedCredentialsFromRequest_fn = async function(req) {
|
|
3149
|
-
const token = getTokenFromRequest(req);
|
|
3150
|
-
if (token) {
|
|
3151
|
-
return await __privateGet$1(this, _auth).authenticate(token, {
|
|
3152
|
-
allowLimitedAccess: true
|
|
3153
|
-
});
|
|
3154
|
-
}
|
|
3155
|
-
const cookie = getCookieFromRequest(req);
|
|
3156
|
-
if (cookie) {
|
|
3157
|
-
return await __privateGet$1(this, _auth).authenticate(cookie, {
|
|
3158
|
-
allowLimitedAccess: true
|
|
3159
|
-
});
|
|
3160
|
-
}
|
|
3161
|
-
return await __privateGet$1(this, _auth).getNoneCredentials();
|
|
3162
|
-
};
|
|
3163
|
-
_getCredentials = new WeakSet();
|
|
3164
|
-
getCredentials_fn = async function(req) {
|
|
3165
|
-
var _a;
|
|
3166
|
-
return (_a = req[credentialsSymbol]) != null ? _a : req[credentialsSymbol] = __privateMethod$1(this, _extractCredentialsFromRequest, extractCredentialsFromRequest_fn).call(this, req);
|
|
3167
|
-
};
|
|
3168
|
-
_getLimitedCredentials = new WeakSet();
|
|
3169
|
-
getLimitedCredentials_fn = async function(req) {
|
|
3170
|
-
var _a;
|
|
3171
|
-
return (_a = req[limitedCredentialsSymbol]) != null ? _a : req[limitedCredentialsSymbol] = __privateMethod$1(this, _extractLimitedCredentialsFromRequest, extractLimitedCredentialsFromRequest_fn).call(this, req);
|
|
3172
|
-
};
|
|
3173
|
-
_getCookieOptions = new WeakSet();
|
|
3174
|
-
getCookieOptions_fn = async function(_req) {
|
|
3175
|
-
const externalBaseUrlStr = await __privateGet$1(this, _discovery).getExternalBaseUrl(
|
|
3176
|
-
__privateGet$1(this, _pluginId)
|
|
3177
|
-
);
|
|
3178
|
-
const externalBaseUrl = new URL(externalBaseUrlStr);
|
|
3179
|
-
const secure = externalBaseUrl.protocol === "https:" || externalBaseUrl.hostname === "localhost";
|
|
3180
|
-
return {
|
|
3181
|
-
domain: externalBaseUrl.hostname,
|
|
3182
|
-
httpOnly: true,
|
|
3183
|
-
secure,
|
|
3184
|
-
priority: "high",
|
|
3185
|
-
sameSite: secure ? "none" : "lax"
|
|
3186
|
-
};
|
|
3187
|
-
};
|
|
3188
|
-
_existingCookieExpiration = new WeakSet();
|
|
3189
|
-
existingCookieExpiration_fn = async function(req) {
|
|
3190
|
-
const existingCookie = getCookieFromRequest(req);
|
|
3191
|
-
if (!existingCookie) {
|
|
3192
|
-
return void 0;
|
|
3193
|
-
}
|
|
3194
|
-
try {
|
|
3195
|
-
const existingCredentials = await __privateGet$1(this, _auth).authenticate(
|
|
3196
|
-
existingCookie,
|
|
3197
|
-
{
|
|
3198
|
-
allowLimitedAccess: true
|
|
3199
|
-
}
|
|
2660
|
+
async #getCookieOptions(_req) {
|
|
2661
|
+
const externalBaseUrlStr = await this.#discovery.getExternalBaseUrl(
|
|
2662
|
+
this.#pluginId
|
|
3200
2663
|
);
|
|
3201
|
-
|
|
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) {
|
|
3202
2677
|
return void 0;
|
|
3203
2678
|
}
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
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;
|
|
3208
2695
|
}
|
|
3209
|
-
throw error;
|
|
3210
2696
|
}
|
|
3211
|
-
}
|
|
2697
|
+
}
|
|
3212
2698
|
const httpAuthServiceFactory = backendPluginApi.createServiceFactory({
|
|
3213
2699
|
service: backendPluginApi.coreServices.httpAuth,
|
|
3214
2700
|
deps: {
|
|
@@ -3368,13 +2854,12 @@ const httpRouterServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3368
2854
|
rootHttpRouter,
|
|
3369
2855
|
lifecycle
|
|
3370
2856
|
}) {
|
|
3371
|
-
|
|
3372
|
-
if (options == null ? void 0 : options.getPath) {
|
|
2857
|
+
if (options?.getPath) {
|
|
3373
2858
|
logger.warn(
|
|
3374
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.`
|
|
3375
2860
|
);
|
|
3376
2861
|
}
|
|
3377
|
-
const getPath =
|
|
2862
|
+
const getPath = options?.getPath ?? ((id) => `/api/${id}`);
|
|
3378
2863
|
const path = getPath(plugin.getId());
|
|
3379
2864
|
const router = Router__default.default();
|
|
3380
2865
|
rootHttpRouter.use(path, router);
|
|
@@ -3410,6 +2895,63 @@ const identityServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3410
2895
|
})
|
|
3411
2896
|
);
|
|
3412
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
|
+
|
|
3413
2955
|
const loggerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3414
2956
|
service: backendPluginApi.coreServices.logger,
|
|
3415
2957
|
deps: {
|
|
@@ -3438,102 +2980,71 @@ const permissionsServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
3438
2980
|
}
|
|
3439
2981
|
});
|
|
3440
2982
|
|
|
3441
|
-
var __accessCheck = (obj, member, msg) => {
|
|
3442
|
-
if (!member.has(obj))
|
|
3443
|
-
throw TypeError("Cannot " + msg);
|
|
3444
|
-
};
|
|
3445
|
-
var __privateGet = (obj, member, getter) => {
|
|
3446
|
-
__accessCheck(obj, member, "read from private field");
|
|
3447
|
-
return member.get(obj);
|
|
3448
|
-
};
|
|
3449
|
-
var __privateAdd = (obj, member, value) => {
|
|
3450
|
-
if (member.has(obj))
|
|
3451
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
3452
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
3453
|
-
};
|
|
3454
|
-
var __privateSet = (obj, member, value, setter) => {
|
|
3455
|
-
__accessCheck(obj, member, "write to private field");
|
|
3456
|
-
member.set(obj, value);
|
|
3457
|
-
return value;
|
|
3458
|
-
};
|
|
3459
|
-
var __privateMethod = (obj, member, method) => {
|
|
3460
|
-
__accessCheck(obj, member, "access private method");
|
|
3461
|
-
return method;
|
|
3462
|
-
};
|
|
3463
|
-
var _indexPath, _router, _namedRoutes, _indexRouter, _existingPaths, _findConflictingPath, findConflictingPath_fn;
|
|
3464
2983
|
function normalizePath(path) {
|
|
3465
2984
|
return `${trimEnd__default.default(path, "/")}/`;
|
|
3466
2985
|
}
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
__privateAdd(this, _indexRouter, express.Router());
|
|
3474
|
-
__privateAdd(this, _existingPaths, new Array());
|
|
3475
|
-
__privateSet(this, _indexPath, indexPath);
|
|
3476
|
-
__privateGet(this, _router).use(__privateGet(this, _namedRoutes));
|
|
3477
|
-
__privateGet(this, _router).use("/api/", (_req, _res, next) => {
|
|
3478
|
-
next("router");
|
|
3479
|
-
});
|
|
3480
|
-
if (__privateGet(this, _indexPath)) {
|
|
3481
|
-
__privateGet(this, _router).use(__privateGet(this, _indexRouter));
|
|
3482
|
-
}
|
|
3483
|
-
}
|
|
2986
|
+
class DefaultRootHttpRouter {
|
|
2987
|
+
#indexPath;
|
|
2988
|
+
#router = express.Router();
|
|
2989
|
+
#namedRoutes = express.Router();
|
|
2990
|
+
#indexRouter = express.Router();
|
|
2991
|
+
#existingPaths = new Array();
|
|
3484
2992
|
static create(options) {
|
|
3485
2993
|
let indexPath;
|
|
3486
|
-
if (
|
|
2994
|
+
if (options?.indexPath === false) {
|
|
3487
2995
|
indexPath = void 0;
|
|
3488
|
-
} else if (
|
|
2996
|
+
} else if (options?.indexPath === void 0) {
|
|
3489
2997
|
indexPath = "/api/app";
|
|
3490
|
-
} else if (
|
|
2998
|
+
} else if (options?.indexPath === "") {
|
|
3491
2999
|
throw new Error("indexPath option may not be an empty string");
|
|
3492
3000
|
} else {
|
|
3493
3001
|
indexPath = options.indexPath;
|
|
3494
3002
|
}
|
|
3495
|
-
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
|
+
}
|
|
3496
3014
|
}
|
|
3497
3015
|
use(path, handler) {
|
|
3498
3016
|
if (path.match(/^[/\s]*$/)) {
|
|
3499
3017
|
throw new Error(`Root router path may not be empty`);
|
|
3500
3018
|
}
|
|
3501
|
-
const conflictingPath =
|
|
3019
|
+
const conflictingPath = this.#findConflictingPath(path);
|
|
3502
3020
|
if (conflictingPath) {
|
|
3503
3021
|
throw new Error(
|
|
3504
3022
|
`Path ${path} conflicts with the existing path ${conflictingPath}`
|
|
3505
3023
|
);
|
|
3506
3024
|
}
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
if (
|
|
3510
|
-
|
|
3025
|
+
this.#existingPaths.push(path);
|
|
3026
|
+
this.#namedRoutes.use(path, handler);
|
|
3027
|
+
if (this.#indexPath === path) {
|
|
3028
|
+
this.#indexRouter.use(handler);
|
|
3511
3029
|
}
|
|
3512
3030
|
}
|
|
3513
3031
|
handler() {
|
|
3514
|
-
return
|
|
3515
|
-
}
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
const normalizedPath = normalizePath(path);
|
|
3527
|
-
if (normalizedPath.startsWith(normalizedNewPath)) {
|
|
3528
|
-
return path;
|
|
3529
|
-
}
|
|
3530
|
-
if (normalizedNewPath.startsWith(normalizedPath)) {
|
|
3531
|
-
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
|
+
}
|
|
3532
3044
|
}
|
|
3045
|
+
return void 0;
|
|
3533
3046
|
}
|
|
3534
|
-
|
|
3535
|
-
};
|
|
3536
|
-
let DefaultRootHttpRouter = _DefaultRootHttpRouter;
|
|
3047
|
+
}
|
|
3537
3048
|
|
|
3538
3049
|
function defaultConfigure({ applyDefaults }) {
|
|
3539
3050
|
applyDefaults();
|
|
@@ -3547,7 +3058,7 @@ const rootHttpRouterServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3547
3058
|
lifecycle: backendPluginApi.coreServices.rootLifecycle
|
|
3548
3059
|
},
|
|
3549
3060
|
async factory({ config, rootLogger, lifecycle }) {
|
|
3550
|
-
const { indexPath, configure = defaultConfigure } = options
|
|
3061
|
+
const { indexPath, configure = defaultConfigure } = options ?? {};
|
|
3551
3062
|
const logger = rootLogger.child({ service: "rootHttpRouter" });
|
|
3552
3063
|
const app = express__default.default();
|
|
3553
3064
|
const router = DefaultRootHttpRouter.create({ indexPath });
|
|
@@ -3583,13 +3094,81 @@ const rootHttpRouterServiceFactory = backendPluginApi.createServiceFactory(
|
|
|
3583
3094
|
})
|
|
3584
3095
|
);
|
|
3585
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
|
+
|
|
3586
3166
|
const rootLoggerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3587
3167
|
service: backendPluginApi.coreServices.rootLogger,
|
|
3588
3168
|
deps: {
|
|
3589
3169
|
config: backendPluginApi.coreServices.rootConfig
|
|
3590
3170
|
},
|
|
3591
3171
|
async factory({ config }) {
|
|
3592
|
-
var _a;
|
|
3593
3172
|
const logger = WinstonLogger.create({
|
|
3594
3173
|
meta: {
|
|
3595
3174
|
service: "backstage"
|
|
@@ -3600,27 +3179,11 @@ const rootLoggerServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
3600
3179
|
});
|
|
3601
3180
|
const secretEnumerator = await createConfigSecretEnumerator({ logger });
|
|
3602
3181
|
logger.addRedactions(secretEnumerator(config));
|
|
3603
|
-
|
|
3182
|
+
config.subscribe?.(() => logger.addRedactions(secretEnumerator(config)));
|
|
3604
3183
|
return logger;
|
|
3605
3184
|
}
|
|
3606
3185
|
});
|
|
3607
3186
|
|
|
3608
|
-
const schedulerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3609
|
-
service: backendPluginApi.coreServices.scheduler,
|
|
3610
|
-
deps: {
|
|
3611
|
-
plugin: backendPluginApi.coreServices.pluginMetadata,
|
|
3612
|
-
databaseManager: backendPluginApi.coreServices.database,
|
|
3613
|
-
logger: backendPluginApi.coreServices.logger
|
|
3614
|
-
},
|
|
3615
|
-
async factory({ plugin, databaseManager, logger }) {
|
|
3616
|
-
return backendTasks.TaskScheduler.forPlugin({
|
|
3617
|
-
pluginId: plugin.getId(),
|
|
3618
|
-
databaseManager,
|
|
3619
|
-
logger
|
|
3620
|
-
});
|
|
3621
|
-
}
|
|
3622
|
-
});
|
|
3623
|
-
|
|
3624
3187
|
const tokenManagerServiceFactory = backendPluginApi.createServiceFactory({
|
|
3625
3188
|
service: backendPluginApi.coreServices.tokenManager,
|
|
3626
3189
|
deps: {
|
|
@@ -3681,6 +3244,22 @@ const userInfoServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
3681
3244
|
}
|
|
3682
3245
|
});
|
|
3683
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
|
+
|
|
3684
3263
|
exports.DefaultRootHttpRouter = DefaultRootHttpRouter;
|
|
3685
3264
|
exports.HostDiscovery = HostDiscovery;
|
|
3686
3265
|
exports.MiddlewareFactory = MiddlewareFactory;
|