@backstage/backend-app-api 0.2.4 → 0.3.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @backstage/backend-app-api might be problematic. Click here for more details.

package/dist/index.cjs.js CHANGED
@@ -2,32 +2,575 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var backendPluginApi = require('@backstage/backend-plugin-api');
5
+ var http = require('http');
6
+ var https = require('https');
7
+ var stoppableServer = require('stoppable');
8
+ var fs = require('fs-extra');
9
+ var path = require('path');
10
+ var forge = require('node-forge');
11
+ var cors = require('cors');
12
+ var helmet = require('helmet');
13
+ var morgan = require('morgan');
14
+ var compression = require('compression');
15
+ var minimatch = require('minimatch');
6
16
  var errors = require('@backstage/errors');
17
+ var backendPluginApi = require('@backstage/backend-plugin-api');
18
+ var express = require('express');
7
19
  var backendCommon = require('@backstage/backend-common');
8
20
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
9
21
  var backendTasks = require('@backstage/backend-tasks');
10
- var Router = require('express-promise-router');
11
22
 
12
23
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
13
24
 
14
- var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
25
+ function _interopNamespace(e) {
26
+ if (e && e.__esModule) return e;
27
+ var n = Object.create(null);
28
+ if (e) {
29
+ Object.keys(e).forEach(function (k) {
30
+ if (k !== 'default') {
31
+ var d = Object.getOwnPropertyDescriptor(e, k);
32
+ Object.defineProperty(n, k, d.get ? d : {
33
+ enumerable: true,
34
+ get: function () { return e[k]; }
35
+ });
36
+ }
37
+ });
38
+ }
39
+ n["default"] = e;
40
+ return Object.freeze(n);
41
+ }
15
42
 
16
- var __accessCheck$3 = (obj, member, msg) => {
43
+ var http__namespace = /*#__PURE__*/_interopNamespace(http);
44
+ var https__namespace = /*#__PURE__*/_interopNamespace(https);
45
+ var stoppableServer__default = /*#__PURE__*/_interopDefaultLegacy(stoppableServer);
46
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
47
+ var forge__default = /*#__PURE__*/_interopDefaultLegacy(forge);
48
+ var cors__default = /*#__PURE__*/_interopDefaultLegacy(cors);
49
+ var helmet__default = /*#__PURE__*/_interopDefaultLegacy(helmet);
50
+ var morgan__default = /*#__PURE__*/_interopDefaultLegacy(morgan);
51
+ var compression__default = /*#__PURE__*/_interopDefaultLegacy(compression);
52
+ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
53
+
54
+ const DEFAULT_PORT = 7007;
55
+ const DEFAULT_HOST = "";
56
+ function readHttpServerOptions(config) {
57
+ return {
58
+ listen: readHttpListenOptions(config),
59
+ https: readHttpsOptions(config)
60
+ };
61
+ }
62
+ function readHttpListenOptions(config) {
63
+ var _a, _b;
64
+ const listen = config == null ? void 0 : config.getOptional("listen");
65
+ if (typeof listen === "string") {
66
+ const parts = String(listen).split(":");
67
+ const port = parseInt(parts[parts.length - 1], 10);
68
+ if (!isNaN(port)) {
69
+ if (parts.length === 1) {
70
+ return { port, host: DEFAULT_HOST };
71
+ }
72
+ if (parts.length === 2) {
73
+ return { host: parts[0], port };
74
+ }
75
+ }
76
+ throw new Error(
77
+ `Unable to parse listen address ${listen}, expected <port> or <host>:<port>`
78
+ );
79
+ }
80
+ const host = (_a = config == null ? void 0 : config.getOptional("listen.host")) != null ? _a : DEFAULT_HOST;
81
+ if (typeof host !== "string") {
82
+ config == null ? void 0 : config.getOptionalString("listen.host");
83
+ throw new Error("unreachable");
84
+ }
85
+ return {
86
+ port: (_b = config == null ? void 0 : config.getOptionalNumber("listen.port")) != null ? _b : DEFAULT_PORT,
87
+ host
88
+ };
89
+ }
90
+ function readHttpsOptions(config) {
91
+ const https = config == null ? void 0 : config.getOptional("https");
92
+ if (https === true) {
93
+ const baseUrl = config.getString("baseUrl");
94
+ let hostname;
95
+ try {
96
+ hostname = new URL(baseUrl).hostname;
97
+ } catch (error) {
98
+ throw new Error(`Invalid baseUrl "${baseUrl}"`);
99
+ }
100
+ return { certificate: { type: "generated", hostname } };
101
+ }
102
+ const cc = config == null ? void 0 : config.getOptionalConfig("https");
103
+ if (!cc) {
104
+ return void 0;
105
+ }
106
+ return {
107
+ certificate: {
108
+ type: "plain",
109
+ cert: cc.getString("certificate.cert"),
110
+ key: cc.getString("certificate.key")
111
+ }
112
+ };
113
+ }
114
+
115
+ const FIVE_DAYS_IN_MS = 5 * 24 * 60 * 60 * 1e3;
116
+ const IP_HOSTNAME_REGEX = /:|^\d+\.\d+\.\d+\.\d+$/;
117
+ async function getGeneratedCertificate(hostname, logger) {
118
+ const hasModules = await fs__default["default"].pathExists("node_modules");
119
+ let certPath;
120
+ if (hasModules) {
121
+ certPath = path.resolve(
122
+ "node_modules/.cache/backstage-backend/dev-cert.pem"
123
+ );
124
+ await fs__default["default"].ensureDir(path.dirname(certPath));
125
+ } else {
126
+ certPath = path.resolve(".dev-cert.pem");
127
+ }
128
+ if (await fs__default["default"].pathExists(certPath)) {
129
+ try {
130
+ const cert = await fs__default["default"].readFile(certPath);
131
+ const crt = forge__default["default"].pki.certificateFromPem(cert.toString());
132
+ const remainingMs = crt.validity.notAfter.getTime() - Date.now();
133
+ if (remainingMs > FIVE_DAYS_IN_MS) {
134
+ logger.info("Using existing self-signed certificate");
135
+ return {
136
+ key: cert,
137
+ cert
138
+ };
139
+ }
140
+ } catch (error) {
141
+ logger.warn(`Unable to use existing self-signed certificate, ${error}`);
142
+ }
143
+ }
144
+ logger.info("Generating new self-signed certificate");
145
+ const newCert = await generateCertificate(hostname);
146
+ await fs__default["default"].writeFile(certPath, newCert.cert + newCert.key, "utf8");
147
+ return newCert;
148
+ }
149
+ async function generateCertificate(hostname) {
150
+ const attributes = [
151
+ {
152
+ name: "commonName",
153
+ value: "dev-cert"
154
+ }
155
+ ];
156
+ const sans = [
157
+ {
158
+ type: 2,
159
+ // DNS
160
+ value: "localhost"
161
+ },
162
+ {
163
+ type: 2,
164
+ value: "localhost.localdomain"
165
+ },
166
+ {
167
+ type: 2,
168
+ value: "[::1]"
169
+ },
170
+ {
171
+ type: 7,
172
+ // IP
173
+ ip: "127.0.0.1"
174
+ },
175
+ {
176
+ type: 7,
177
+ ip: "fe80::1"
178
+ }
179
+ ];
180
+ if (!sans.find(({ value, ip }) => value === hostname || ip === hostname)) {
181
+ sans.push(
182
+ IP_HOSTNAME_REGEX.test(hostname) ? {
183
+ type: 7,
184
+ ip: hostname
185
+ } : {
186
+ type: 2,
187
+ value: hostname
188
+ }
189
+ );
190
+ }
191
+ const params = {
192
+ algorithm: "sha256",
193
+ keySize: 2048,
194
+ days: 30,
195
+ extensions: [
196
+ {
197
+ name: "keyUsage",
198
+ keyCertSign: true,
199
+ digitalSignature: true,
200
+ nonRepudiation: true,
201
+ keyEncipherment: true,
202
+ dataEncipherment: true
203
+ },
204
+ {
205
+ name: "extKeyUsage",
206
+ serverAuth: true,
207
+ clientAuth: true,
208
+ codeSigning: true,
209
+ timeStamping: true
210
+ },
211
+ {
212
+ name: "subjectAltName",
213
+ altNames: sans
214
+ }
215
+ ]
216
+ };
217
+ return new Promise(
218
+ (resolve, reject) => require("selfsigned").generate(
219
+ attributes,
220
+ params,
221
+ (err, bundle) => {
222
+ if (err) {
223
+ reject(err);
224
+ } else {
225
+ resolve({ key: bundle.private, cert: bundle.cert });
226
+ }
227
+ }
228
+ )
229
+ );
230
+ }
231
+
232
+ async function createHttpServer(listener, options, deps) {
233
+ const server = await createServer(listener, options, deps);
234
+ const stopper = stoppableServer__default["default"](server, 0);
235
+ const stopServer = stopper.stop.bind(stopper);
236
+ return Object.assign(server, {
237
+ start() {
238
+ return new Promise((resolve, reject) => {
239
+ const handleStartupError = (error) => {
240
+ server.close();
241
+ reject(error);
242
+ };
243
+ server.on("error", handleStartupError);
244
+ const { host, port } = options.listen;
245
+ server.listen(port, host, () => {
246
+ server.off("error", handleStartupError);
247
+ deps.logger.info(`Listening on ${host}:${port}`);
248
+ resolve();
249
+ });
250
+ });
251
+ },
252
+ stop() {
253
+ return new Promise((resolve, reject) => {
254
+ stopServer((error) => {
255
+ if (error) {
256
+ reject(error);
257
+ } else {
258
+ resolve();
259
+ }
260
+ });
261
+ });
262
+ },
263
+ port() {
264
+ const address = server.address();
265
+ if (typeof address === "string" || address === null) {
266
+ throw new Error(`Unexpected server address '${address}'`);
267
+ }
268
+ return address.port;
269
+ }
270
+ });
271
+ }
272
+ async function createServer(listener, options, deps) {
273
+ if (options.https) {
274
+ const { certificate } = options.https;
275
+ if (certificate.type === "generated") {
276
+ const credentials = await getGeneratedCertificate(
277
+ certificate.hostname,
278
+ deps.logger
279
+ );
280
+ return https__namespace.createServer(credentials, listener);
281
+ }
282
+ return https__namespace.createServer(certificate, listener);
283
+ }
284
+ return http__namespace.createServer(listener);
285
+ }
286
+
287
+ function readHelmetOptions(config) {
288
+ const cspOptions = readCspDirectives(config);
289
+ return {
290
+ contentSecurityPolicy: {
291
+ useDefaults: false,
292
+ directives: applyCspDirectives(cspOptions)
293
+ },
294
+ // These are all disabled in order to maintain backwards compatibility
295
+ // when bumping helmet v5. We can't enable these by default because
296
+ // there is no way for users to configure them.
297
+ // TODO(Rugvip): We should give control of this setup to consumers
298
+ crossOriginEmbedderPolicy: false,
299
+ crossOriginOpenerPolicy: false,
300
+ crossOriginResourcePolicy: false,
301
+ originAgentCluster: false
302
+ };
303
+ }
304
+ function readCspDirectives(config) {
305
+ const cc = config == null ? void 0 : config.getOptionalConfig("csp");
306
+ if (!cc) {
307
+ return void 0;
308
+ }
309
+ const result = {};
310
+ for (const key of cc.keys()) {
311
+ if (cc.get(key) === false) {
312
+ result[key] = false;
313
+ } else {
314
+ result[key] = cc.getStringArray(key);
315
+ }
316
+ }
317
+ return result;
318
+ }
319
+ function applyCspDirectives(directives) {
320
+ const result = helmet__default["default"].contentSecurityPolicy.getDefaultDirectives();
321
+ result["script-src"] = ["'self'", "'unsafe-eval'"];
322
+ delete result["form-action"];
323
+ if (directives) {
324
+ for (const [key, value] of Object.entries(directives)) {
325
+ if (value === false) {
326
+ delete result[key];
327
+ } else {
328
+ result[key] = value;
329
+ }
330
+ }
331
+ }
332
+ return result;
333
+ }
334
+
335
+ function readCorsOptions(config) {
336
+ const cc = config == null ? void 0 : config.getOptionalConfig("cors");
337
+ if (!cc) {
338
+ return { origin: false };
339
+ }
340
+ return {
341
+ origin: createCorsOriginMatcher(readStringArray(cc, "origin")),
342
+ methods: readStringArray(cc, "methods"),
343
+ allowedHeaders: readStringArray(cc, "allowedHeaders"),
344
+ exposedHeaders: readStringArray(cc, "exposedHeaders"),
345
+ credentials: cc.getOptionalBoolean("credentials"),
346
+ maxAge: cc.getOptionalNumber("maxAge"),
347
+ preflightContinue: cc.getOptionalBoolean("preflightContinue"),
348
+ optionsSuccessStatus: cc.getOptionalNumber("optionsSuccessStatus")
349
+ };
350
+ }
351
+ function readStringArray(config, key) {
352
+ const value = config.getOptional(key);
353
+ if (typeof value === "string") {
354
+ return [value];
355
+ } else if (!value) {
356
+ return void 0;
357
+ }
358
+ return config.getStringArray(key);
359
+ }
360
+ function createCorsOriginMatcher(allowedOriginPatterns) {
361
+ if (!allowedOriginPatterns) {
362
+ return void 0;
363
+ }
364
+ const allowedOriginMatchers = allowedOriginPatterns.map(
365
+ (pattern) => new minimatch.Minimatch(pattern, { nocase: true, noglobstar: true })
366
+ );
367
+ return (origin, callback) => {
368
+ return callback(
369
+ null,
370
+ allowedOriginMatchers.some((pattern) => pattern.match(origin != null ? origin : ""))
371
+ );
372
+ };
373
+ }
374
+
375
+ var __accessCheck$5 = (obj, member, msg) => {
17
376
  if (!member.has(obj))
18
377
  throw TypeError("Cannot " + msg);
19
378
  };
20
- var __privateGet$3 = (obj, member, getter) => {
21
- __accessCheck$3(obj, member, "read from private field");
379
+ var __privateGet$5 = (obj, member, getter) => {
380
+ __accessCheck$5(obj, member, "read from private field");
22
381
  return getter ? getter.call(obj) : member.get(obj);
23
382
  };
24
- var __privateAdd$3 = (obj, member, value) => {
383
+ var __privateAdd$5 = (obj, member, value) => {
25
384
  if (member.has(obj))
26
385
  throw TypeError("Cannot add the same private member more than once");
27
386
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
28
387
  };
29
- var __privateSet$3 = (obj, member, value, setter) => {
30
- __accessCheck$3(obj, member, "write to private field");
388
+ var __privateSet$5 = (obj, member, value, setter) => {
389
+ __accessCheck$5(obj, member, "write to private field");
390
+ setter ? setter.call(obj, value) : member.set(obj, value);
391
+ return value;
392
+ };
393
+ var _config, _logger;
394
+ const _MiddlewareFactory = class {
395
+ constructor(options) {
396
+ __privateAdd$5(this, _config, void 0);
397
+ __privateAdd$5(this, _logger, void 0);
398
+ __privateSet$5(this, _config, options.config);
399
+ __privateSet$5(this, _logger, options.logger);
400
+ }
401
+ /**
402
+ * Creates a new {@link MiddlewareFactory}.
403
+ */
404
+ static create(options) {
405
+ return new _MiddlewareFactory(options);
406
+ }
407
+ /**
408
+ * Returns a middleware that unconditionally produces a 404 error response.
409
+ *
410
+ * @remarks
411
+ *
412
+ * Typically you want to place this middleware at the end of the chain, such
413
+ * that it's the last one attempted after no other routes matched.
414
+ *
415
+ * @returns An Express request handler
416
+ */
417
+ notFound() {
418
+ return (_req, res) => {
419
+ res.status(404).end();
420
+ };
421
+ }
422
+ /**
423
+ * Returns the compression middleware.
424
+ *
425
+ * @remarks
426
+ *
427
+ * The middleware will attempt to compress response bodies for all requests
428
+ * that traverse through the middleware.
429
+ */
430
+ compression() {
431
+ return compression__default["default"]();
432
+ }
433
+ /**
434
+ * Returns a request logging middleware.
435
+ *
436
+ * @remarks
437
+ *
438
+ * Typically you want to place this middleware at the start of the chain, such
439
+ * that it always logs requests whether they are "caught" by handlers farther
440
+ * down or not.
441
+ *
442
+ * @returns An Express request handler
443
+ */
444
+ logging() {
445
+ const logger = __privateGet$5(this, _logger).child({
446
+ type: "incomingRequest"
447
+ });
448
+ return morgan__default["default"]("combined", {
449
+ stream: {
450
+ write(message) {
451
+ logger.info(message.trimEnd());
452
+ }
453
+ }
454
+ });
455
+ }
456
+ /**
457
+ * Returns a middleware that implements the helmet library.
458
+ *
459
+ * @remarks
460
+ *
461
+ * This middleware applies security policies to incoming requests and outgoing
462
+ * responses. It is configured using config keys such as `backend.csp`.
463
+ *
464
+ * @see {@link https://helmetjs.github.io/}
465
+ *
466
+ * @returns An Express request handler
467
+ */
468
+ helmet() {
469
+ return helmet__default["default"](readHelmetOptions(__privateGet$5(this, _config).getOptionalConfig("backend")));
470
+ }
471
+ /**
472
+ * Returns a middleware that implements the cors library.
473
+ *
474
+ * @remarks
475
+ *
476
+ * This middleware handles CORS. It is configured using the config key
477
+ * `backend.cors`.
478
+ *
479
+ * @see {@link https://github.com/expressjs/cors}
480
+ *
481
+ * @returns An Express request handler
482
+ */
483
+ cors() {
484
+ return cors__default["default"](readCorsOptions(__privateGet$5(this, _config).getOptionalConfig("backend")));
485
+ }
486
+ /**
487
+ * Express middleware to handle errors during request processing.
488
+ *
489
+ * @remarks
490
+ *
491
+ * This is commonly the very last middleware in the chain.
492
+ *
493
+ * Its primary purpose is not to do translation of business logic exceptions,
494
+ * but rather to be a global catch-all for uncaught "fatal" errors that are
495
+ * expected to result in a 500 error. However, it also does handle some common
496
+ * error types (such as http-error exceptions, and the well-known error types
497
+ * in the `@backstage/errors` package) and returns the enclosed status code
498
+ * accordingly.
499
+ *
500
+ * It will also produce a response body with a serialized form of the error,
501
+ * unless a previous handler already did send a body. See
502
+ * {@link @backstage/errors#ErrorResponseBody} for the response shape used.
503
+ *
504
+ * @returns An Express error request handler
505
+ */
506
+ error(options = {}) {
507
+ var _a;
508
+ const showStackTraces = (_a = options.showStackTraces) != null ? _a : process.env.NODE_ENV === "development";
509
+ const logger = __privateGet$5(this, _logger).child({
510
+ type: "errorHandler"
511
+ });
512
+ return (error, req, res, next) => {
513
+ const statusCode = getStatusCode(error);
514
+ if (options.logAllErrors || statusCode >= 500) {
515
+ logger.error(`Request failed with status ${statusCode}`, error);
516
+ }
517
+ if (res.headersSent) {
518
+ next(error);
519
+ return;
520
+ }
521
+ const body = {
522
+ error: errors.serializeError(error, { includeStack: showStackTraces }),
523
+ request: { method: req.method, url: req.url },
524
+ response: { statusCode }
525
+ };
526
+ res.status(statusCode).json(body);
527
+ };
528
+ }
529
+ };
530
+ let MiddlewareFactory = _MiddlewareFactory;
531
+ _config = new WeakMap();
532
+ _logger = new WeakMap();
533
+ function getStatusCode(error) {
534
+ const knownStatusCodeFields = ["statusCode", "status"];
535
+ for (const field of knownStatusCodeFields) {
536
+ const statusCode = error[field];
537
+ if (typeof statusCode === "number" && (statusCode | 0) === statusCode && // is whole integer
538
+ statusCode >= 100 && statusCode <= 599) {
539
+ return statusCode;
540
+ }
541
+ }
542
+ switch (error.name) {
543
+ case errors.NotModifiedError.name:
544
+ return 304;
545
+ case errors.InputError.name:
546
+ return 400;
547
+ case errors.AuthenticationError.name:
548
+ return 401;
549
+ case errors.NotAllowedError.name:
550
+ return 403;
551
+ case errors.NotFoundError.name:
552
+ return 404;
553
+ case errors.ConflictError.name:
554
+ return 409;
555
+ }
556
+ return 500;
557
+ }
558
+
559
+ var __accessCheck$4 = (obj, member, msg) => {
560
+ if (!member.has(obj))
561
+ throw TypeError("Cannot " + msg);
562
+ };
563
+ var __privateGet$4 = (obj, member, getter) => {
564
+ __accessCheck$4(obj, member, "read from private field");
565
+ return getter ? getter.call(obj) : member.get(obj);
566
+ };
567
+ var __privateAdd$4 = (obj, member, value) => {
568
+ if (member.has(obj))
569
+ throw TypeError("Cannot add the same private member more than once");
570
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
571
+ };
572
+ var __privateSet$4 = (obj, member, value, setter) => {
573
+ __accessCheck$4(obj, member, "write to private field");
31
574
  setter ? setter.call(obj, value) : member.set(obj, value);
32
575
  return value;
33
576
  };
@@ -36,112 +579,95 @@ const CALLBACKS = ["SIGTERM", "SIGINT", "beforeExit"];
36
579
  class BackendLifecycleImpl {
37
580
  constructor(logger) {
38
581
  this.logger = logger;
39
- __privateAdd$3(this, _isCalled, false);
40
- __privateAdd$3(this, _shutdownTasks, []);
582
+ __privateAdd$4(this, _isCalled, false);
583
+ __privateAdd$4(this, _shutdownTasks, []);
41
584
  CALLBACKS.map((signal) => process.on(signal, () => this.shutdown()));
42
585
  }
43
586
  addShutdownHook(options) {
44
- __privateGet$3(this, _shutdownTasks).push(options);
587
+ __privateGet$4(this, _shutdownTasks).push(options);
45
588
  }
46
589
  async shutdown() {
47
- if (__privateGet$3(this, _isCalled)) {
590
+ if (__privateGet$4(this, _isCalled)) {
48
591
  return;
49
592
  }
50
- __privateSet$3(this, _isCalled, true);
51
- this.logger.info(`Running ${__privateGet$3(this, _shutdownTasks).length} shutdown tasks...`);
593
+ __privateSet$4(this, _isCalled, true);
594
+ this.logger.info(`Running ${__privateGet$4(this, _shutdownTasks).length} shutdown tasks...`);
52
595
  await Promise.all(
53
- __privateGet$3(this, _shutdownTasks).map(
54
- (hook) => Promise.resolve().then(() => hook.fn()).catch((e) => {
55
- this.logger.error(
56
- `Shutdown hook registered by plugin '${hook.pluginId}' failed with: ${e}`
57
- );
58
- }).then(
59
- () => this.logger.info(
60
- `Successfully ran shutdown hook registered by plugin ${hook.pluginId}`
61
- )
62
- )
63
- )
596
+ __privateGet$4(this, _shutdownTasks).map(async (hook) => {
597
+ const { logger = this.logger } = hook;
598
+ try {
599
+ await hook.fn();
600
+ logger.info(`Shutdown hook succeeded`);
601
+ } catch (error) {
602
+ logger.error(`Shutdown hook failed, ${error}`);
603
+ }
604
+ })
64
605
  );
65
606
  }
66
607
  }
67
608
  _isCalled = new WeakMap();
68
609
  _shutdownTasks = new WeakMap();
69
- class PluginScopedLifecycleImpl {
70
- constructor(lifecycle, pluginId) {
71
- this.lifecycle = lifecycle;
72
- this.pluginId = pluginId;
73
- }
74
- addShutdownHook(options) {
75
- this.lifecycle.addShutdownHook({ ...options, pluginId: this.pluginId });
76
- }
77
- }
78
- const lifecycleFactory = backendPluginApi.createServiceFactory({
79
- service: backendPluginApi.coreServices.lifecycle,
610
+ const rootLifecycleFactory = backendPluginApi.createServiceFactory({
611
+ service: backendPluginApi.coreServices.rootLifecycle,
80
612
  deps: {
81
- logger: backendPluginApi.coreServices.rootLogger,
82
- plugin: backendPluginApi.coreServices.pluginMetadata
613
+ logger: backendPluginApi.coreServices.rootLogger
83
614
  },
84
615
  async factory({ logger }) {
85
- const rootLifecycle = new BackendLifecycleImpl(
86
- backendPluginApi.loggerToWinstonLogger(logger)
87
- );
88
- return async ({ plugin }) => {
89
- return new PluginScopedLifecycleImpl(rootLifecycle, plugin.getId());
90
- };
616
+ return new BackendLifecycleImpl(logger);
91
617
  }
92
618
  });
93
619
 
94
- var __accessCheck$2 = (obj, member, msg) => {
620
+ var __accessCheck$3 = (obj, member, msg) => {
95
621
  if (!member.has(obj))
96
622
  throw TypeError("Cannot " + msg);
97
623
  };
98
- var __privateGet$2 = (obj, member, getter) => {
99
- __accessCheck$2(obj, member, "read from private field");
624
+ var __privateGet$3 = (obj, member, getter) => {
625
+ __accessCheck$3(obj, member, "read from private field");
100
626
  return getter ? getter.call(obj) : member.get(obj);
101
627
  };
102
- var __privateAdd$2 = (obj, member, value) => {
628
+ var __privateAdd$3 = (obj, member, value) => {
103
629
  if (member.has(obj))
104
630
  throw TypeError("Cannot add the same private member more than once");
105
631
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
106
632
  };
107
- var __privateSet$2 = (obj, member, value, setter) => {
108
- __accessCheck$2(obj, member, "write to private field");
633
+ var __privateSet$3 = (obj, member, value, setter) => {
634
+ __accessCheck$3(obj, member, "write to private field");
109
635
  setter ? setter.call(obj, value) : member.set(obj, value);
110
636
  return value;
111
637
  };
112
- var __privateMethod$1 = (obj, member, method) => {
113
- __accessCheck$2(obj, member, "access private method");
638
+ var __privateMethod$2 = (obj, member, method) => {
639
+ __accessCheck$3(obj, member, "access private method");
114
640
  return method;
115
641
  };
116
642
  var _started, _features, _registerInits, _extensionPoints, _serviceHolder, _getInitDeps, getInitDeps_fn, _resolveInitOrder, resolveInitOrder_fn;
117
643
  class BackendInitializer {
118
644
  constructor(serviceHolder) {
119
- __privateAdd$2(this, _getInitDeps);
120
- __privateAdd$2(this, _resolveInitOrder);
121
- __privateAdd$2(this, _started, false);
122
- __privateAdd$2(this, _features, /* @__PURE__ */ new Map());
123
- __privateAdd$2(this, _registerInits, new Array());
124
- __privateAdd$2(this, _extensionPoints, /* @__PURE__ */ new Map());
125
- __privateAdd$2(this, _serviceHolder, void 0);
126
- __privateSet$2(this, _serviceHolder, serviceHolder);
645
+ __privateAdd$3(this, _getInitDeps);
646
+ __privateAdd$3(this, _resolveInitOrder);
647
+ __privateAdd$3(this, _started, false);
648
+ __privateAdd$3(this, _features, /* @__PURE__ */ new Map());
649
+ __privateAdd$3(this, _registerInits, new Array());
650
+ __privateAdd$3(this, _extensionPoints, /* @__PURE__ */ new Map());
651
+ __privateAdd$3(this, _serviceHolder, void 0);
652
+ __privateSet$3(this, _serviceHolder, serviceHolder);
127
653
  }
128
654
  add(feature, options) {
129
- if (__privateGet$2(this, _started)) {
655
+ if (__privateGet$3(this, _started)) {
130
656
  throw new Error("feature can not be added after the backend has started");
131
657
  }
132
- __privateGet$2(this, _features).set(feature, options);
658
+ __privateGet$3(this, _features).set(feature, options);
133
659
  }
134
660
  async start() {
135
- if (__privateGet$2(this, _started)) {
661
+ if (__privateGet$3(this, _started)) {
136
662
  throw new Error("Backend has already started");
137
663
  }
138
- __privateSet$2(this, _started, true);
139
- for (const ref of __privateGet$2(this, _serviceHolder).getServiceRefs()) {
664
+ __privateSet$3(this, _started, true);
665
+ for (const ref of __privateGet$3(this, _serviceHolder).getServiceRefs()) {
140
666
  if (ref.scope === "root") {
141
- await __privateGet$2(this, _serviceHolder).get(ref, "root");
667
+ await __privateGet$3(this, _serviceHolder).get(ref, "root");
142
668
  }
143
669
  }
144
- for (const [feature] of __privateGet$2(this, _features)) {
670
+ for (const [feature] of __privateGet$3(this, _features)) {
145
671
  const provides = /* @__PURE__ */ new Set();
146
672
  let registerInit = void 0;
147
673
  feature.register({
@@ -149,10 +675,10 @@ class BackendInitializer {
149
675
  if (registerInit) {
150
676
  throw new Error("registerExtensionPoint called after registerInit");
151
677
  }
152
- if (__privateGet$2(this, _extensionPoints).has(extensionPointRef)) {
678
+ if (__privateGet$3(this, _extensionPoints).has(extensionPointRef)) {
153
679
  throw new Error(`API ${extensionPointRef.id} already registered`);
154
680
  }
155
- __privateGet$2(this, _extensionPoints).set(extensionPointRef, impl);
681
+ __privateGet$3(this, _extensionPoints).set(extensionPointRef, impl);
156
682
  provides.add(extensionPointRef);
157
683
  },
158
684
  registerInit: (registerOptions) => {
@@ -173,25 +699,24 @@ class BackendInitializer {
173
699
  `registerInit was not called by register in ${feature.id}`
174
700
  );
175
701
  }
176
- __privateGet$2(this, _registerInits).push(registerInit);
702
+ __privateGet$3(this, _registerInits).push(registerInit);
177
703
  }
178
- const orderedRegisterResults = __privateMethod$1(this, _resolveInitOrder, resolveInitOrder_fn).call(this, __privateGet$2(this, _registerInits));
704
+ const orderedRegisterResults = __privateMethod$2(this, _resolveInitOrder, resolveInitOrder_fn).call(this, __privateGet$3(this, _registerInits));
179
705
  for (const registerInit of orderedRegisterResults) {
180
- const deps = await __privateMethod$1(this, _getInitDeps, getInitDeps_fn).call(this, registerInit.deps, registerInit.id);
706
+ const deps = await __privateMethod$2(this, _getInitDeps, getInitDeps_fn).call(this, registerInit.deps, registerInit.id);
181
707
  await registerInit.init(deps);
182
708
  }
183
709
  }
184
710
  async stop() {
185
- if (!__privateGet$2(this, _started)) {
711
+ if (!__privateGet$3(this, _started)) {
186
712
  return;
187
713
  }
188
- const lifecycleService = await __privateGet$2(this, _serviceHolder).get(
189
- backendPluginApi.coreServices.lifecycle,
714
+ const lifecycleService = await __privateGet$3(this, _serviceHolder).get(
715
+ backendPluginApi.coreServices.rootLifecycle,
190
716
  "root"
191
717
  );
192
- const lifecycle = lifecycleService == null ? void 0 : lifecycleService.lifecycle;
193
- if (lifecycle instanceof BackendLifecycleImpl) {
194
- await lifecycle.shutdown();
718
+ if (lifecycleService instanceof BackendLifecycleImpl) {
719
+ await lifecycleService.shutdown();
195
720
  } else {
196
721
  throw new Error("Unexpected lifecycle service implementation");
197
722
  }
@@ -207,13 +732,13 @@ getInitDeps_fn = async function(deps, pluginId) {
207
732
  const result = /* @__PURE__ */ new Map();
208
733
  const missingRefs = /* @__PURE__ */ new Set();
209
734
  for (const [name, ref] of Object.entries(deps)) {
210
- const extensionPoint = __privateGet$2(this, _extensionPoints).get(
735
+ const extensionPoint = __privateGet$3(this, _extensionPoints).get(
211
736
  ref
212
737
  );
213
738
  if (extensionPoint) {
214
739
  result.set(name, extensionPoint);
215
740
  } else {
216
- const impl = await __privateGet$2(this, _serviceHolder).get(
741
+ const impl = await __privateGet$3(this, _serviceHolder).get(
217
742
  ref,
218
743
  pluginId
219
744
  );
@@ -257,59 +782,51 @@ resolveInitOrder_fn = function(registerInits) {
257
782
  return orderedRegisterInits;
258
783
  };
259
784
 
260
- var __accessCheck$1 = (obj, member, msg) => {
785
+ var __accessCheck$2 = (obj, member, msg) => {
261
786
  if (!member.has(obj))
262
787
  throw TypeError("Cannot " + msg);
263
788
  };
264
- var __privateGet$1 = (obj, member, getter) => {
265
- __accessCheck$1(obj, member, "read from private field");
789
+ var __privateGet$2 = (obj, member, getter) => {
790
+ __accessCheck$2(obj, member, "read from private field");
266
791
  return getter ? getter.call(obj) : member.get(obj);
267
792
  };
268
- var __privateAdd$1 = (obj, member, value) => {
793
+ var __privateAdd$2 = (obj, member, value) => {
269
794
  if (member.has(obj))
270
795
  throw TypeError("Cannot add the same private member more than once");
271
796
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
272
797
  };
273
- var __privateSet$1 = (obj, member, value, setter) => {
274
- __accessCheck$1(obj, member, "write to private field");
798
+ var __privateSet$2 = (obj, member, value, setter) => {
799
+ __accessCheck$2(obj, member, "write to private field");
275
800
  setter ? setter.call(obj, value) : member.set(obj, value);
276
801
  return value;
277
802
  };
278
- var __privateMethod = (obj, member, method) => {
279
- __accessCheck$1(obj, member, "access private method");
803
+ var __privateMethod$1 = (obj, member, method) => {
804
+ __accessCheck$2(obj, member, "access private method");
280
805
  return method;
281
806
  };
282
807
  var _providedFactories, _loadedDefaultFactories, _implementations, _resolveFactory, resolveFactory_fn, _separateMapForTheRootService, _checkForMissingDeps, checkForMissingDeps_fn;
283
808
  class ServiceRegistry {
284
809
  constructor(factories) {
285
- __privateAdd$1(this, _resolveFactory);
286
- __privateAdd$1(this, _checkForMissingDeps);
287
- __privateAdd$1(this, _providedFactories, void 0);
288
- __privateAdd$1(this, _loadedDefaultFactories, void 0);
289
- __privateAdd$1(this, _implementations, void 0);
290
- __privateAdd$1(this, _separateMapForTheRootService, /* @__PURE__ */ new Map());
291
- __privateSet$1(this, _providedFactories, new Map(
292
- factories.map((f) => {
293
- if (typeof f === "function") {
294
- const cf = f();
295
- return [cf.service.id, cf];
296
- }
297
- return [f.service.id, f];
298
- })
299
- ));
300
- __privateSet$1(this, _loadedDefaultFactories, /* @__PURE__ */ new Map());
301
- __privateSet$1(this, _implementations, /* @__PURE__ */ new Map());
810
+ __privateAdd$2(this, _resolveFactory);
811
+ __privateAdd$2(this, _checkForMissingDeps);
812
+ __privateAdd$2(this, _providedFactories, void 0);
813
+ __privateAdd$2(this, _loadedDefaultFactories, void 0);
814
+ __privateAdd$2(this, _implementations, void 0);
815
+ __privateAdd$2(this, _separateMapForTheRootService, /* @__PURE__ */ new Map());
816
+ __privateSet$2(this, _providedFactories, new Map(factories.map((f) => [f.service.id, f])));
817
+ __privateSet$2(this, _loadedDefaultFactories, /* @__PURE__ */ new Map());
818
+ __privateSet$2(this, _implementations, /* @__PURE__ */ new Map());
302
819
  }
303
820
  getServiceRefs() {
304
- return Array.from(__privateGet$1(this, _providedFactories).values()).map((f) => f.service);
821
+ return Array.from(__privateGet$2(this, _providedFactories).values()).map((f) => f.service);
305
822
  }
306
823
  get(ref, pluginId) {
307
824
  var _a;
308
- return (_a = __privateMethod(this, _resolveFactory, resolveFactory_fn).call(this, ref, pluginId)) == null ? void 0 : _a.then((factory) => {
825
+ return (_a = __privateMethod$1(this, _resolveFactory, resolveFactory_fn).call(this, ref, pluginId)) == null ? void 0 : _a.then((factory) => {
309
826
  if (factory.scope === "root") {
310
- let existing = __privateGet$1(this, _separateMapForTheRootService).get(factory);
827
+ let existing = __privateGet$2(this, _separateMapForTheRootService).get(factory);
311
828
  if (!existing) {
312
- __privateMethod(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
829
+ __privateMethod$1(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
313
830
  const rootDeps = new Array();
314
831
  for (const [name, serviceRef] of Object.entries(factory.deps)) {
315
832
  if (serviceRef.scope !== "root") {
@@ -323,13 +840,13 @@ class ServiceRegistry {
323
840
  existing = Promise.all(rootDeps).then(
324
841
  (entries) => factory.factory(Object.fromEntries(entries))
325
842
  );
326
- __privateGet$1(this, _separateMapForTheRootService).set(factory, existing);
843
+ __privateGet$2(this, _separateMapForTheRootService).set(factory, existing);
327
844
  }
328
845
  return existing;
329
846
  }
330
- let implementation = __privateGet$1(this, _implementations).get(factory);
847
+ let implementation = __privateGet$2(this, _implementations).get(factory);
331
848
  if (!implementation) {
332
- __privateMethod(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
849
+ __privateMethod$1(this, _checkForMissingDeps, checkForMissingDeps_fn).call(this, factory, pluginId);
333
850
  const rootDeps = new Array();
334
851
  for (const [name, serviceRef] of Object.entries(factory.deps)) {
335
852
  if (serviceRef.scope === "root") {
@@ -346,7 +863,7 @@ class ServiceRegistry {
346
863
  }),
347
864
  byPlugin: /* @__PURE__ */ new Map()
348
865
  };
349
- __privateGet$1(this, _implementations).set(factory, implementation);
866
+ __privateGet$2(this, _implementations).set(factory, implementation);
350
867
  }
351
868
  let result = implementation.byPlugin.get(pluginId);
352
869
  if (!result) {
@@ -388,18 +905,18 @@ resolveFactory_fn = function(ref, pluginId) {
388
905
  })
389
906
  });
390
907
  }
391
- let resolvedFactory = __privateGet$1(this, _providedFactories).get(ref.id);
908
+ let resolvedFactory = __privateGet$2(this, _providedFactories).get(ref.id);
392
909
  const { __defaultFactory: defaultFactory } = ref;
393
910
  if (!resolvedFactory && !defaultFactory) {
394
911
  return void 0;
395
912
  }
396
913
  if (!resolvedFactory) {
397
- let loadedFactory = __privateGet$1(this, _loadedDefaultFactories).get(defaultFactory);
914
+ let loadedFactory = __privateGet$2(this, _loadedDefaultFactories).get(defaultFactory);
398
915
  if (!loadedFactory) {
399
916
  loadedFactory = Promise.resolve().then(() => defaultFactory(ref)).then(
400
917
  (f) => typeof f === "function" ? f() : f
401
918
  );
402
- __privateGet$1(this, _loadedDefaultFactories).set(defaultFactory, loadedFactory);
919
+ __privateGet$2(this, _loadedDefaultFactories).set(defaultFactory, loadedFactory);
403
920
  }
404
921
  resolvedFactory = loadedFactory.catch((error) => {
405
922
  throw new Error(
@@ -418,7 +935,7 @@ checkForMissingDeps_fn = function(factory, pluginId) {
418
935
  if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
419
936
  return false;
420
937
  }
421
- if (__privateGet$1(this, _providedFactories).get(ref.id)) {
938
+ if (__privateGet$2(this, _providedFactories).get(ref.id)) {
422
939
  return false;
423
940
  }
424
941
  return !ref.__defaultFactory;
@@ -431,51 +948,220 @@ checkForMissingDeps_fn = function(factory, pluginId) {
431
948
  }
432
949
  };
433
950
 
434
- var __accessCheck = (obj, member, msg) => {
951
+ var __accessCheck$1 = (obj, member, msg) => {
435
952
  if (!member.has(obj))
436
953
  throw TypeError("Cannot " + msg);
437
954
  };
438
- var __privateGet = (obj, member, getter) => {
439
- __accessCheck(obj, member, "read from private field");
955
+ var __privateGet$1 = (obj, member, getter) => {
956
+ __accessCheck$1(obj, member, "read from private field");
440
957
  return getter ? getter.call(obj) : member.get(obj);
441
958
  };
442
- var __privateAdd = (obj, member, value) => {
959
+ var __privateAdd$1 = (obj, member, value) => {
443
960
  if (member.has(obj))
444
961
  throw TypeError("Cannot add the same private member more than once");
445
962
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
446
963
  };
447
- var __privateSet = (obj, member, value, setter) => {
448
- __accessCheck(obj, member, "write to private field");
964
+ var __privateSet$1 = (obj, member, value, setter) => {
965
+ __accessCheck$1(obj, member, "write to private field");
449
966
  setter ? setter.call(obj, value) : member.set(obj, value);
450
967
  return value;
451
968
  };
452
969
  var _services, _initializer;
453
970
  class BackstageBackend {
454
971
  constructor(apiFactories) {
455
- __privateAdd(this, _services, void 0);
456
- __privateAdd(this, _initializer, void 0);
457
- __privateSet(this, _services, new ServiceRegistry(apiFactories));
458
- __privateSet(this, _initializer, new BackendInitializer(__privateGet(this, _services)));
972
+ __privateAdd$1(this, _services, void 0);
973
+ __privateAdd$1(this, _initializer, void 0);
974
+ __privateSet$1(this, _services, new ServiceRegistry(apiFactories));
975
+ __privateSet$1(this, _initializer, new BackendInitializer(__privateGet$1(this, _services)));
459
976
  }
460
977
  add(feature) {
461
- __privateGet(this, _initializer).add(feature);
978
+ __privateGet$1(this, _initializer).add(feature);
462
979
  }
463
980
  async start() {
464
- await __privateGet(this, _initializer).start();
981
+ await __privateGet$1(this, _initializer).start();
465
982
  }
466
983
  async stop() {
467
- await __privateGet(this, _initializer).stop();
984
+ await __privateGet$1(this, _initializer).stop();
468
985
  }
469
986
  }
470
987
  _services = new WeakMap();
471
988
  _initializer = new WeakMap();
472
989
 
473
990
  function createSpecializedBackend(options) {
474
- return new BackstageBackend(
475
- options.services.map((s) => typeof s === "function" ? s() : s)
991
+ const services = options.services.map(
992
+ (sf) => typeof sf === "function" ? sf() : sf
476
993
  );
994
+ const exists = /* @__PURE__ */ new Set();
995
+ const duplicates = /* @__PURE__ */ new Set();
996
+ for (const { service } of services) {
997
+ if (exists.has(service.id)) {
998
+ duplicates.add(service.id);
999
+ } else {
1000
+ exists.add(service.id);
1001
+ }
1002
+ }
1003
+ if (duplicates.size > 0) {
1004
+ const ids = Array.from(duplicates).join(", ");
1005
+ throw new Error(`Duplicate service implementations provided for ${ids}`);
1006
+ }
1007
+ if (exists.has(backendPluginApi.coreServices.pluginMetadata.id)) {
1008
+ throw new Error(
1009
+ `The ${backendPluginApi.coreServices.pluginMetadata.id} service cannot be overridden`
1010
+ );
1011
+ }
1012
+ return new BackstageBackend(services);
477
1013
  }
478
1014
 
1015
+ const httpRouterFactory = backendPluginApi.createServiceFactory({
1016
+ service: backendPluginApi.coreServices.httpRouter,
1017
+ deps: {
1018
+ plugin: backendPluginApi.coreServices.pluginMetadata,
1019
+ rootHttpRouter: backendPluginApi.coreServices.rootHttpRouter
1020
+ },
1021
+ async factory({ rootHttpRouter }, options) {
1022
+ var _a;
1023
+ const getPath = (_a = options == null ? void 0 : options.getPath) != null ? _a : (id) => `/api/${id}`;
1024
+ return async ({ plugin }) => {
1025
+ const path = getPath(plugin.getId());
1026
+ return {
1027
+ use(handler) {
1028
+ rootHttpRouter.use(path, handler);
1029
+ }
1030
+ };
1031
+ };
1032
+ }
1033
+ });
1034
+
1035
+ var __accessCheck = (obj, member, msg) => {
1036
+ if (!member.has(obj))
1037
+ throw TypeError("Cannot " + msg);
1038
+ };
1039
+ var __privateGet = (obj, member, getter) => {
1040
+ __accessCheck(obj, member, "read from private field");
1041
+ return getter ? getter.call(obj) : member.get(obj);
1042
+ };
1043
+ var __privateAdd = (obj, member, value) => {
1044
+ if (member.has(obj))
1045
+ throw TypeError("Cannot add the same private member more than once");
1046
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1047
+ };
1048
+ var __privateSet = (obj, member, value, setter) => {
1049
+ __accessCheck(obj, member, "write to private field");
1050
+ setter ? setter.call(obj, value) : member.set(obj, value);
1051
+ return value;
1052
+ };
1053
+ var __privateMethod = (obj, member, method) => {
1054
+ __accessCheck(obj, member, "access private method");
1055
+ return method;
1056
+ };
1057
+ var _indexPath, _router, _namedRoutes, _indexRouter, _existingPaths, _findConflictingPath, findConflictingPath_fn;
1058
+ function normalizePath(path) {
1059
+ return path.replace(/\/*$/, "/");
1060
+ }
1061
+ class RestrictedIndexedRouter {
1062
+ constructor(indexPath) {
1063
+ __privateAdd(this, _findConflictingPath);
1064
+ __privateAdd(this, _indexPath, void 0);
1065
+ __privateAdd(this, _router, express.Router());
1066
+ __privateAdd(this, _namedRoutes, express.Router());
1067
+ __privateAdd(this, _indexRouter, express.Router());
1068
+ __privateAdd(this, _existingPaths, new Array());
1069
+ __privateSet(this, _indexPath, indexPath);
1070
+ __privateGet(this, _router).use(__privateGet(this, _namedRoutes));
1071
+ __privateGet(this, _router).use(__privateGet(this, _indexRouter));
1072
+ }
1073
+ use(path, handler) {
1074
+ if (path.match(/^[/\s]*$/)) {
1075
+ throw new Error(`Root router path may not be empty`);
1076
+ }
1077
+ const conflictingPath = __privateMethod(this, _findConflictingPath, findConflictingPath_fn).call(this, path);
1078
+ if (conflictingPath) {
1079
+ throw new Error(
1080
+ `Path ${path} conflicts with the existing path ${conflictingPath}`
1081
+ );
1082
+ }
1083
+ __privateGet(this, _existingPaths).push(path);
1084
+ __privateGet(this, _namedRoutes).use(path, handler);
1085
+ if (__privateGet(this, _indexPath) === path) {
1086
+ __privateGet(this, _indexRouter).use(handler);
1087
+ }
1088
+ }
1089
+ handler() {
1090
+ return __privateGet(this, _router);
1091
+ }
1092
+ }
1093
+ _indexPath = new WeakMap();
1094
+ _router = new WeakMap();
1095
+ _namedRoutes = new WeakMap();
1096
+ _indexRouter = new WeakMap();
1097
+ _existingPaths = new WeakMap();
1098
+ _findConflictingPath = new WeakSet();
1099
+ findConflictingPath_fn = function(newPath) {
1100
+ const normalizedNewPath = normalizePath(newPath);
1101
+ for (const path of __privateGet(this, _existingPaths)) {
1102
+ const normalizedPath = normalizePath(path);
1103
+ if (normalizedPath.startsWith(normalizedNewPath)) {
1104
+ return path;
1105
+ }
1106
+ if (normalizedNewPath.startsWith(normalizedPath)) {
1107
+ return path;
1108
+ }
1109
+ }
1110
+ return void 0;
1111
+ };
1112
+
1113
+ function defaultConfigure({
1114
+ app,
1115
+ routes,
1116
+ middleware
1117
+ }) {
1118
+ app.use(middleware.helmet());
1119
+ app.use(middleware.cors());
1120
+ app.use(middleware.compression());
1121
+ app.use(middleware.logging());
1122
+ app.use(routes);
1123
+ app.use(middleware.notFound());
1124
+ app.use(middleware.error());
1125
+ }
1126
+ const rootHttpRouterFactory = backendPluginApi.createServiceFactory({
1127
+ service: backendPluginApi.coreServices.rootHttpRouter,
1128
+ deps: {
1129
+ config: backendPluginApi.coreServices.config,
1130
+ rootLogger: backendPluginApi.coreServices.rootLogger,
1131
+ lifecycle: backendPluginApi.coreServices.rootLifecycle
1132
+ },
1133
+ async factory({ config, rootLogger, lifecycle }, {
1134
+ indexPath,
1135
+ configure = defaultConfigure
1136
+ } = {}) {
1137
+ const router = new RestrictedIndexedRouter(indexPath != null ? indexPath : "/api/app");
1138
+ const logger = rootLogger.child({ service: "rootHttpRouter" });
1139
+ const app = express__default["default"]();
1140
+ const middleware = MiddlewareFactory.create({ config, logger });
1141
+ configure({
1142
+ app,
1143
+ routes: router.handler(),
1144
+ middleware,
1145
+ config,
1146
+ logger,
1147
+ lifecycle
1148
+ });
1149
+ const server = await createHttpServer(
1150
+ app,
1151
+ readHttpServerOptions(config.getOptionalConfig("backend")),
1152
+ { logger }
1153
+ );
1154
+ lifecycle.addShutdownHook({
1155
+ async fn() {
1156
+ await server.stop();
1157
+ },
1158
+ logger
1159
+ });
1160
+ await server.start();
1161
+ return router;
1162
+ }
1163
+ });
1164
+
479
1165
  const cacheFactory = backendPluginApi.createServiceFactory({
480
1166
  service: backendPluginApi.coreServices.cache,
481
1167
  deps: {
@@ -498,7 +1184,7 @@ const configFactory = backendPluginApi.createServiceFactory({
498
1184
  async factory({ logger }) {
499
1185
  const config = await backendCommon.loadBackendConfig({
500
1186
  argv: process.argv,
501
- logger: backendPluginApi.loggerToWinstonLogger(logger)
1187
+ logger: backendCommon.loggerToWinstonLogger(logger)
502
1188
  });
503
1189
  return config;
504
1190
  }
@@ -539,7 +1225,7 @@ const loggerFactory = backendPluginApi.createServiceFactory({
539
1225
  },
540
1226
  async factory({ rootLogger }) {
541
1227
  return async ({ plugin }) => {
542
- return rootLogger.child({ pluginId: plugin.getId() });
1228
+ return rootLogger.child({ plugin: plugin.getId() });
543
1229
  };
544
1230
  }
545
1231
  });
@@ -615,7 +1301,7 @@ const tokenManagerFactory = backendPluginApi.createServiceFactory({
615
1301
  async factory() {
616
1302
  return async ({ config, logger }) => {
617
1303
  return backendCommon.ServerTokenManager.fromConfig(config, {
618
- logger: backendPluginApi.loggerToWinstonLogger(logger)
1304
+ logger: backendCommon.loggerToWinstonLogger(logger)
619
1305
  });
620
1306
  };
621
1307
  }
@@ -631,42 +1317,39 @@ const urlReaderFactory = backendPluginApi.createServiceFactory({
631
1317
  return async ({ config, logger }) => {
632
1318
  return backendCommon.UrlReaders.default({
633
1319
  config,
634
- logger: backendPluginApi.loggerToWinstonLogger(logger)
1320
+ logger: backendCommon.loggerToWinstonLogger(logger)
635
1321
  });
636
1322
  };
637
1323
  }
638
1324
  });
639
1325
 
640
- const httpRouterFactory = backendPluginApi.createServiceFactory({
641
- service: backendPluginApi.coreServices.httpRouter,
1326
+ const lifecycleFactory = backendPluginApi.createServiceFactory({
1327
+ service: backendPluginApi.coreServices.lifecycle,
642
1328
  deps: {
643
- config: backendPluginApi.coreServices.config,
644
- plugin: backendPluginApi.coreServices.pluginMetadata
1329
+ logger: backendPluginApi.coreServices.logger,
1330
+ rootLifecycle: backendPluginApi.coreServices.rootLifecycle,
1331
+ pluginMetadata: backendPluginApi.coreServices.pluginMetadata
645
1332
  },
646
- async factory({ config }, options) {
647
- var _a;
648
- const defaultPluginId = (_a = options == null ? void 0 : options.indexPlugin) != null ? _a : "app";
649
- const apiRouter = Router__default["default"]();
650
- const rootRouter = Router__default["default"]();
651
- const service = backendCommon.createServiceBuilder(module).loadConfig(config).addRouter("/api", apiRouter).addRouter("", rootRouter);
652
- await service.start();
653
- return async ({ plugin }) => {
654
- const pluginId = plugin.getId();
1333
+ async factory({ rootLifecycle }) {
1334
+ return async ({ logger, pluginMetadata }) => {
1335
+ const plugin = pluginMetadata.getId();
655
1336
  return {
656
- use(handler) {
657
- if (pluginId === defaultPluginId) {
658
- rootRouter.use(handler);
659
- } else {
660
- apiRouter.use(`/${pluginId}`, handler);
661
- }
1337
+ addShutdownHook(options) {
1338
+ var _a, _b;
1339
+ rootLifecycle.addShutdownHook({
1340
+ ...options,
1341
+ logger: (_b = (_a = options.logger) == null ? void 0 : _a.child({ plugin })) != null ? _b : logger
1342
+ });
662
1343
  }
663
1344
  };
664
1345
  };
665
1346
  }
666
1347
  });
667
1348
 
1349
+ exports.MiddlewareFactory = MiddlewareFactory;
668
1350
  exports.cacheFactory = cacheFactory;
669
1351
  exports.configFactory = configFactory;
1352
+ exports.createHttpServer = createHttpServer;
670
1353
  exports.createSpecializedBackend = createSpecializedBackend;
671
1354
  exports.databaseFactory = databaseFactory;
672
1355
  exports.discoveryFactory = discoveryFactory;
@@ -674,6 +1357,11 @@ exports.httpRouterFactory = httpRouterFactory;
674
1357
  exports.lifecycleFactory = lifecycleFactory;
675
1358
  exports.loggerFactory = loggerFactory;
676
1359
  exports.permissionsFactory = permissionsFactory;
1360
+ exports.readCorsOptions = readCorsOptions;
1361
+ exports.readHelmetOptions = readHelmetOptions;
1362
+ exports.readHttpServerOptions = readHttpServerOptions;
1363
+ exports.rootHttpRouterFactory = rootHttpRouterFactory;
1364
+ exports.rootLifecycleFactory = rootLifecycleFactory;
677
1365
  exports.rootLoggerFactory = rootLoggerFactory;
678
1366
  exports.schedulerFactory = schedulerFactory;
679
1367
  exports.tokenManagerFactory = tokenManagerFactory;