@backstage/backend-app-api 1.7.3-next.0 → 1.7.3-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/backend-internal/src/wiring/helpers.cjs.js +13 -6
- package/dist/backend-internal/src/wiring/helpers.cjs.js.map +1 -1
- package/dist/connections-node/src/DefaultConnectionsService.cjs.js +78 -26
- package/dist/connections-node/src/DefaultConnectionsService.cjs.js.map +1 -1
- package/dist/connections-node/src/lookupStrategies.cjs.js +26 -0
- package/dist/connections-node/src/lookupStrategies.cjs.js.map +1 -0
- package/dist/wiring/BackendInitializer.cjs.js +91 -72
- package/dist/wiring/BackendInitializer.cjs.js.map +1 -1
- package/dist/wiring/BackstageBackend.cjs.js +1 -1
- package/dist/wiring/BackstageBackend.cjs.js.map +1 -1
- package/dist/wiring/ServiceRegistry.cjs.js +34 -18
- package/dist/wiring/ServiceRegistry.cjs.js.map +1 -1
- package/dist/wiring/validateBackendFeature.cjs.js +215 -0
- package/dist/wiring/validateBackendFeature.cjs.js.map +1 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @backstage/backend-app-api
|
|
2
2
|
|
|
3
|
+
## 1.7.3-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 03133fc: Hardened backend startup against malformed installed backend features, with contextual input errors and configured boot-failure handling when invalid registrations can be attributed to a plugin or module.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/connections@0.3.0-next.2
|
|
10
|
+
|
|
3
11
|
## 1.7.3-next.0
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -4,13 +4,20 @@ function isPromise(value) {
|
|
|
4
4
|
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
5
5
|
}
|
|
6
6
|
function unwrapFeature(feature) {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
let current = feature;
|
|
8
|
+
for (let i = 0; i < 2; i++) {
|
|
9
|
+
if (current !== null && (typeof current === "object" || typeof current === "function")) {
|
|
10
|
+
if ("$$type" in current) {
|
|
11
|
+
return current;
|
|
12
|
+
}
|
|
13
|
+
if ("default" in current) {
|
|
14
|
+
current = current.default;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
break;
|
|
9
19
|
}
|
|
10
|
-
|
|
11
|
-
return feature.default;
|
|
12
|
-
}
|
|
13
|
-
return feature;
|
|
20
|
+
return current;
|
|
14
21
|
}
|
|
15
22
|
|
|
16
23
|
exports.isPromise = isPromise;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.cjs.js","sources":["../../../../../backend-internal/src/wiring/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BackendFeature } from '@backstage/backend-plugin-api';\n\n/** @internal */\nexport function isPromise<T>(value: unknown | Promise<T>): value is Promise<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function'\n );\n}\n\n/**\n * Unwraps a backend feature from a possibly double-nested default export.\n * This is a workaround where default exports get transpiled to\n * `exports['default'] = ...` in CommonJS modules, which in turn results\n * in a double `{ default: { default: ... } }` nesting when importing\n * using a dynamic import.\n *\n * @internal\n */\nexport function unwrapFeature(\n feature: BackendFeature | { default: BackendFeature },\n): BackendFeature {\n if ('$$type' in
|
|
1
|
+
{"version":3,"file":"helpers.cjs.js","sources":["../../../../../backend-internal/src/wiring/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BackendFeature } from '@backstage/backend-plugin-api';\n\n/** @internal */\nexport function isPromise<T>(value: unknown | Promise<T>): value is Promise<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function'\n );\n}\n\n/**\n * Unwraps a backend feature from a possibly double-nested default export.\n * This is a workaround where default exports get transpiled to\n * `exports['default'] = ...` in CommonJS modules, which in turn results\n * in a double `{ default: { default: ... } }` nesting when importing\n * using a dynamic import.\n *\n * @internal\n */\nexport function unwrapFeature(\n feature: BackendFeature | { default: BackendFeature },\n): BackendFeature {\n let current: unknown = feature;\n for (let i = 0; i < 2; i++) {\n if (\n current !== null &&\n (typeof current === 'object' || typeof current === 'function')\n ) {\n if ('$$type' in current) {\n return current as BackendFeature;\n }\n if ('default' in current) {\n current = current.default;\n continue;\n }\n }\n break;\n }\n\n return current as BackendFeature;\n}\n"],"names":[],"mappings":";;AAmBO,SAAS,UAAa,KAAA,EAAkD;AAC7E,EAAA,OACE,OAAO,UAAU,QAAA,IACjB,KAAA,KAAU,QACV,MAAA,IAAU,KAAA,IACV,OAAO,KAAA,CAAM,IAAA,KAAS,UAAA;AAE1B;AAWO,SAAS,cACd,OAAA,EACgB;AAChB,EAAA,IAAI,OAAA,GAAmB,OAAA;AACvB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,IAAA,IACE,YAAY,IAAA,KACX,OAAO,YAAY,QAAA,IAAY,OAAO,YAAY,UAAA,CAAA,EACnD;AACA,MAAA,IAAI,YAAY,OAAA,EAAS;AACvB,QAAA,OAAO,OAAA;AAAA,MACT;AACA,MAAA,IAAI,aAAa,OAAA,EAAS;AACxB,QAAA,OAAA,GAAU,OAAA,CAAQ,OAAA;AAClB,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA;AAAA,EACF;AAEA,EAAA,OAAO,OAAA;AACT;;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var lookup = require('./lookup.cjs.js');
|
|
4
|
+
var lookupStrategies = require('./lookupStrategies.cjs.js');
|
|
4
5
|
var errors = require('@backstage/errors');
|
|
5
6
|
var v4 = require('zod/v4');
|
|
6
7
|
var getLegacyIntegrations = require('./getLegacyIntegrations.cjs.js');
|
|
@@ -19,6 +20,16 @@ function describeError(error) {
|
|
|
19
20
|
}
|
|
20
21
|
return e.message;
|
|
21
22
|
}
|
|
23
|
+
function getLookupStrategy(name) {
|
|
24
|
+
return lookupStrategies.lookupStrategies[name];
|
|
25
|
+
}
|
|
26
|
+
function connectionIdentityOf(strategy, connection) {
|
|
27
|
+
if (!strategy.identityField) {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
const value = connection[strategy.identityField];
|
|
31
|
+
return typeof value === "string" ? value : void 0;
|
|
32
|
+
}
|
|
22
33
|
class PluginConnectionsService {
|
|
23
34
|
logger;
|
|
24
35
|
connections;
|
|
@@ -30,40 +41,40 @@ class PluginConnectionsService {
|
|
|
30
41
|
const result = await this.findOptional(options);
|
|
31
42
|
if (!result) {
|
|
32
43
|
throw new errors.NotFoundError(
|
|
33
|
-
`Connection not found for type "${options.type}"
|
|
44
|
+
`Connection not found for type "${options.type}"`
|
|
34
45
|
);
|
|
35
46
|
}
|
|
36
47
|
return result;
|
|
37
48
|
}
|
|
38
49
|
async findOptional({
|
|
39
50
|
type,
|
|
40
|
-
|
|
51
|
+
query,
|
|
41
52
|
authMethods
|
|
42
53
|
}) {
|
|
54
|
+
const connectionType = lookup.getConnectionType(type);
|
|
55
|
+
const strategy = getLookupStrategy(connectionType.lookupStrategy);
|
|
56
|
+
const identity = strategy.identityFromQuery(query);
|
|
43
57
|
this.logger.debug(
|
|
44
|
-
`Finding connection of type "${type}" matching
|
|
58
|
+
`Finding connection of type "${type}"${identity ? ` matching ${strategy.identityField} "${identity}"` : ""}`
|
|
45
59
|
);
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
throw new errors.InputError(
|
|
51
|
-
`Invalid url "${url}" passed to ConnectionsService.find`
|
|
60
|
+
let connection;
|
|
61
|
+
if (identity !== void 0) {
|
|
62
|
+
connection = this.connections.find(
|
|
63
|
+
(c) => c.type === type && connectionIdentityOf(strategy, c) === identity
|
|
52
64
|
);
|
|
65
|
+
} else {
|
|
66
|
+
connection = this.connections.find((c) => c.type === type);
|
|
53
67
|
}
|
|
54
|
-
const connection = this.connections.find(
|
|
55
|
-
(c) => c.type === type && c.host === host
|
|
56
|
-
);
|
|
57
68
|
if (!connection) {
|
|
58
69
|
return void 0;
|
|
59
70
|
}
|
|
60
71
|
if (connection.auth.length === 0) {
|
|
61
72
|
throw new errors.NotAllowedError(
|
|
62
|
-
`Connection of type "${type}" for
|
|
73
|
+
`Connection of type "${type}"${identity ? ` for ${strategy.identityField} "${identity}"` : ""} has no auth method available to this plugin`
|
|
63
74
|
);
|
|
64
75
|
}
|
|
65
|
-
const matchAuth =
|
|
66
|
-
const selected = matchAuth ? matchAuth(connection.auth,
|
|
76
|
+
const matchAuth = connectionType.matchAuth;
|
|
77
|
+
const selected = matchAuth ? matchAuth(connection.auth, query) : connection.auth[0];
|
|
67
78
|
if (!selected) {
|
|
68
79
|
return void 0;
|
|
69
80
|
}
|
|
@@ -72,6 +83,9 @@ class PluginConnectionsService {
|
|
|
72
83
|
`Connection not found for type "${type}" with auth method "${selected.method}"`
|
|
73
84
|
);
|
|
74
85
|
}
|
|
86
|
+
this.logger.debug(
|
|
87
|
+
`Selected connection of type "${type}"${identity ? ` for ${strategy.identityField} "${identity}"` : ""} using auth method "${selected.method}"`
|
|
88
|
+
);
|
|
75
89
|
return {
|
|
76
90
|
...connection,
|
|
77
91
|
auth: selected
|
|
@@ -102,6 +116,9 @@ class DefaultConnectionsService {
|
|
|
102
116
|
const fromConfig = this.#validateConfig(
|
|
103
117
|
rawConnections ?? []
|
|
104
118
|
);
|
|
119
|
+
this.logger.debug(
|
|
120
|
+
`Connections configuration resolved ${legacy.length} connection${legacy.length === 1 ? "" : "s"} from legacy integrations and ${fromConfig.length} explicit connection${fromConfig.length === 1 ? "" : "s"}`
|
|
121
|
+
);
|
|
105
122
|
if (legacy.length === 0 && fromConfig.length === 0) {
|
|
106
123
|
return;
|
|
107
124
|
}
|
|
@@ -110,11 +127,13 @@ class DefaultConnectionsService {
|
|
|
110
127
|
);
|
|
111
128
|
const seen = /* @__PURE__ */ new Set();
|
|
112
129
|
for (const c of this.connections) {
|
|
113
|
-
const
|
|
114
|
-
const
|
|
130
|
+
const connectionType = lookup.getConnectionType(c.type);
|
|
131
|
+
const strategy = getLookupStrategy(connectionType.lookupStrategy);
|
|
132
|
+
const identity = connectionIdentityOf(strategy, c);
|
|
133
|
+
const key = `${c.type} ${identity ?? ""}`;
|
|
115
134
|
if (seen.has(key)) {
|
|
116
135
|
throw new errors.InputError(
|
|
117
|
-
`Duplicate connection of type "${c.type}" for
|
|
136
|
+
identity !== void 0 ? `Duplicate connection of type "${c.type}" for ${strategy.identityField} "${identity}"` : `Duplicate connection of type "${c.type}"`
|
|
118
137
|
);
|
|
119
138
|
}
|
|
120
139
|
seen.add(key);
|
|
@@ -164,15 +183,44 @@ ${describeError(
|
|
|
164
183
|
if (!lookup.isConnectionTypeKey(connection.type)) {
|
|
165
184
|
throw new errors.InputError(`Unrecognised connection type ${connection.type}`);
|
|
166
185
|
}
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
)
|
|
170
|
-
if (parsed.auth.length === 0) {
|
|
186
|
+
const connectionType = lookup.getConnectionType(connection.type);
|
|
187
|
+
const rawAuth = connection.auth;
|
|
188
|
+
if (!Array.isArray(rawAuth) || rawAuth.length === 0) {
|
|
171
189
|
throw new errors.InputError(
|
|
172
190
|
`Connection of type "${connection.type}" must configure at least one auth method`
|
|
173
191
|
);
|
|
174
192
|
}
|
|
175
|
-
|
|
193
|
+
const auth = rawAuth.map((entry) => {
|
|
194
|
+
if (typeof entry.method !== "string") {
|
|
195
|
+
throw new errors.InputError(
|
|
196
|
+
`Auth entry for connection type "${connection.type}" is missing a "method" field`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const authMethod = connectionType.authMethods.find(
|
|
200
|
+
(am) => am.method === entry.method
|
|
201
|
+
);
|
|
202
|
+
if (!authMethod) {
|
|
203
|
+
throw new errors.InputError(
|
|
204
|
+
`Unknown auth method "${entry.method}" for connection type "${connection.type}"`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const { method, title: title2, match: match2, ...rest } = entry;
|
|
208
|
+
return {
|
|
209
|
+
...authMethod.configSchema.parse(rest),
|
|
210
|
+
method,
|
|
211
|
+
title: title2,
|
|
212
|
+
match: match2
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
const { type, auth: _, title, match, ...configFields } = connection;
|
|
216
|
+
const parsed = connectionType.configSchema.parse(configFields);
|
|
217
|
+
return {
|
|
218
|
+
...parsed,
|
|
219
|
+
type: connection.type,
|
|
220
|
+
title,
|
|
221
|
+
match,
|
|
222
|
+
auth
|
|
223
|
+
};
|
|
176
224
|
}
|
|
177
225
|
#assignDefaultTitles() {
|
|
178
226
|
const typeCounts = /* @__PURE__ */ new Map();
|
|
@@ -183,9 +231,13 @@ ${describeError(
|
|
|
183
231
|
for (const c of this.connections) {
|
|
184
232
|
if (!c.title) {
|
|
185
233
|
const type = c.type;
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
234
|
+
const connectionType = lookup.getConnectionType(type);
|
|
235
|
+
const displayName = connectionType.title;
|
|
236
|
+
const identity = connectionIdentityOf(
|
|
237
|
+
getLookupStrategy(connectionType.lookupStrategy),
|
|
238
|
+
c
|
|
239
|
+
);
|
|
240
|
+
c.title = typeCounts.get(type) > 1 && identity ? `${displayName} (${identity})` : displayName;
|
|
189
241
|
}
|
|
190
242
|
}
|
|
191
243
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultConnectionsService.cjs.js","sources":["../../../../connections-node/src/DefaultConnectionsService.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport type {\n Connection,\n ConnectionAuthMethodKey,\n ConnectionsService,\n ConnectionTypeKey,\n} from '@backstage/connections';\nimport { getConnectionType, isConnectionTypeKey } from './lookup';\nimport type { RootConnection } from './types';\nimport { JsonObject } from '@backstage/types';\nimport {\n InputError,\n NotAllowedError,\n NotFoundError,\n toError,\n} from '@backstage/errors';\nimport { z } from 'zod/v4';\nimport { getLegacyIntegrations } from './getLegacyIntegrations';\nimport { combineConnectionSources } from './combineConnectionSources';\n\nfunction describeError(error: unknown): string {\n const e = toError(error);\n if (e.name === 'ZodError') {\n return z.prettifyError(e as unknown as z.ZodError);\n }\n if (e.cause !== undefined) {\n const cause = toError(e.cause);\n if (cause.name === 'ZodError') {\n return z.prettifyError(cause as unknown as z.ZodError);\n }\n }\n return e.message;\n}\n\nclass PluginConnectionsService implements ConnectionsService {\n private readonly logger: LoggerService;\n private readonly connections: Connection[];\n\n constructor(logger: LoggerService, connections: Connection[]) {\n this.logger = logger;\n this.connections = connections;\n }\n\n async find<\n TType extends ConnectionTypeKey,\n TAuthMethod extends ConnectionAuthMethodKey<TType>,\n >(options: {\n type: TType;\n url: string;\n authMethods: readonly [TAuthMethod, ...TAuthMethod[]];\n }): Promise<Connection<TType, TAuthMethod>> {\n const result = await this.findOptional(options);\n if (!result) {\n throw new NotFoundError(\n `Connection not found for type \"${options.type}\" matching url \"${options.url}\"`,\n );\n }\n return result;\n }\n\n async findOptional<\n TType extends ConnectionTypeKey,\n TAuthMethod extends ConnectionAuthMethodKey<TType>,\n >({\n type,\n url,\n authMethods,\n }: {\n type: TType;\n url: string;\n authMethods: readonly [TAuthMethod, ...TAuthMethod[]];\n }): Promise<Connection<TType, TAuthMethod> | undefined> {\n this.logger.debug(\n `Finding connection of type \"${type}\" matching url \"${url}\"`,\n );\n let host: string;\n try {\n host = new URL(url).host;\n } catch {\n throw new InputError(\n `Invalid url \"${url}\" passed to ConnectionsService.find`,\n );\n }\n\n const connection = this.connections.find(\n c => c.type === type && (c as { host?: unknown }).host === host,\n ) as Connection<TType> | undefined;\n\n if (!connection) {\n return undefined;\n }\n\n if (connection.auth.length === 0) {\n throw new NotAllowedError(\n `Connection of type \"${type}\" for host \"${host}\" has no auth method available to this plugin`,\n );\n }\n\n const matchAuth = getConnectionType(type).matchAuth as\n | ((authMethods: any[], query: string) => any | undefined)\n | undefined;\n\n // We take the host-matched connection and check to see if there's an auth method better suited to the current url\n // e.g. org selection\n const selected = matchAuth\n ? matchAuth(connection.auth, url)\n : connection.auth[0];\n\n if (!selected) {\n return undefined;\n }\n\n // Now we compare user requested auth methods with what the connection can provide\n if (!(authMethods as readonly string[]).includes(selected.method)) {\n throw new NotAllowedError(\n `Connection not found for type \"${type}\" with auth method \"${selected.method}\"`,\n );\n }\n\n return {\n ...connection,\n auth: selected,\n } as Connection<TType, TAuthMethod>;\n }\n}\n\n/** @public */\nexport class DefaultConnectionsService {\n private readonly logger: LoggerService;\n private readonly connections: RootConnection[];\n private readonly config: RootConfigService;\n\n private constructor(logger: LoggerService, config: RootConfigService) {\n this.logger = logger;\n this.config = config;\n this.connections = [];\n this.#registerConnectionsFromConfig();\n }\n\n static create(options: {\n logger: LoggerService;\n config: RootConfigService;\n }): DefaultConnectionsService {\n return new DefaultConnectionsService(options.logger, options.config);\n }\n\n #registerConnectionsFromConfig(): void {\n const legacy = this.#validateLegacy(getLegacyIntegrations(this.config));\n\n const rawConnections = this.config.getOptional('connections');\n if (rawConnections !== undefined && !Array.isArray(rawConnections)) {\n throw new InputError(\n 'Expected \"connections\" config to be an array of connection objects',\n );\n }\n\n const fromConfig = this.#validateConfig(\n (rawConnections as JsonObject[] | undefined) ?? [],\n );\n\n if (legacy.length === 0 && fromConfig.length === 0) {\n return;\n }\n\n this.connections.push(\n ...combineConnectionSources(legacy, fromConfig, this.logger),\n );\n\n const seen = new Set<string>();\n for (const c of this.connections) {\n const host = (c as unknown as { host: string }).host;\n const key = `${c.type} ${host}`;\n if (seen.has(key)) {\n throw new InputError(\n `Duplicate connection of type \"${c.type}\" for host \"${host}\"`,\n );\n }\n seen.add(key);\n }\n\n this.#assignDefaultTitles();\n this.#assignDefaultAuthTitles();\n\n this.logger.info(\n `Loaded ${this.connections.length} connection${\n this.connections.length === 1 ? '' : 's'\n } from configuration`,\n );\n }\n\n #validateConfig(raw: JsonObject[]): RootConnection[] {\n return raw.map(v => {\n try {\n return this.#validateConnection(v);\n } catch (e) {\n const type = typeof v.type === 'string' ? v.type : 'unknown';\n throw new InputError(\n `Invalid connection of type \"${type}\" in connections config:\\n${describeError(\n e,\n )}`,\n );\n }\n });\n }\n\n #validateLegacy(raw: JsonObject[]): RootConnection[] {\n const result: RootConnection[] = [];\n for (const v of raw) {\n try {\n result.push(this.#validateConnection(v));\n } catch (e) {\n const type = typeof v.type === 'string' ? v.type : 'unknown';\n this.logger.error(\n `Failed to validate connection of type \"${type}\":\\n${describeError(\n e,\n )}`,\n );\n }\n }\n return result;\n }\n\n #validateConnection(connection: JsonObject): RootConnection {\n if (typeof connection.type !== 'string') {\n throw new InputError(`Unrecognised connection type ${connection.type}`);\n }\n\n if (!isConnectionTypeKey(connection.type)) {\n throw new InputError(`Unrecognised connection type ${connection.type}`);\n }\n\n const parsed = getConnectionType(connection.type).configSchema.parse(\n connection,\n );\n if (parsed.auth.length === 0) {\n throw new InputError(\n `Connection of type \"${connection.type}\" must configure at least one auth method`,\n );\n }\n return parsed;\n }\n\n #assignDefaultTitles(): void {\n const typeCounts = new Map<string, number>();\n for (const c of this.connections) {\n const type = c.type as ConnectionTypeKey;\n typeCounts.set(type, (typeCounts.get(type) ?? 0) + 1);\n }\n for (const c of this.connections) {\n if (!c.title) {\n const type = c.type as ConnectionTypeKey;\n const displayName = getConnectionType(type).title;\n const host = (c as unknown as { host: string }).host;\n (c as { title?: string }).title =\n typeCounts.get(type)! > 1 ? `${displayName} (${host})` : displayName;\n }\n }\n }\n\n #assignDefaultAuthTitles(): void {\n for (const c of this.connections) {\n const type = c.type as ConnectionTypeKey;\n const connectionType = getConnectionType(type);\n for (const auth of c.auth) {\n const authMethod = connectionType.authMethods.find(\n am => am.method === auth.method,\n );\n // The config schema only allows methods declared by the connection\n // type, so failing to find one means that invariant has been broken.\n if (!authMethod) {\n throw new Error(\n `Unknown auth method \"${auth.method}\" for connection type \"${type}\"`,\n );\n }\n auth.title ??= authMethod.title;\n }\n }\n }\n\n #getConnectionsForPlugin(pluginId: string): Connection[] {\n // Filter connections and hide auth methods based on these conditions:\n // 1. Include Connections with no plugin matcher condition\n // 2. Include Connections with a plugin matcher condition for this plugin\n // 3. Include auth methods with no plugin matcher condition\n // 4. Remove auth methods with a plugin matcher condition for other plugins\n return this.connections.flatMap(({ match, auth, ...rest }) => {\n if (match && !match.plugins.includes(pluginId)) {\n return [];\n }\n\n const pluginMatched: Connection['auth'] = [];\n const unmatched: Connection['auth'] = [];\n for (const { match: authMatch, ...authRest } of auth) {\n if (authMatch) {\n if (!authMatch.plugins.includes(pluginId)) continue;\n pluginMatched.push(authRest as Connection['auth'][number]);\n } else {\n unmatched.push(authRest as Connection['auth'][number]);\n }\n }\n\n return [\n { ...rest, auth: [...pluginMatched, ...unmatched] } as Connection,\n ];\n });\n }\n\n forPlugin(\n pluginId: string,\n options?: {\n logger: LoggerService;\n },\n ): ConnectionsService {\n const logger = options?.logger ?? this.logger;\n return new PluginConnectionsService(\n logger,\n this.#getConnectionsForPlugin(pluginId),\n );\n }\n}\n"],"names":["toError","z","NotFoundError","InputError","NotAllowedError","getConnectionType","getLegacyIntegrations","combineConnectionSources","isConnectionTypeKey"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DefaultConnectionsService.cjs.js","sources":["../../../../connections-node/src/DefaultConnectionsService.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport type {\n Connection,\n ConnectionAuthMethodKey,\n ConnectionsService,\n ConnectionTypeKey,\n LookupConnectionType,\n LookupStrategy,\n} from '@backstage/connections';\nimport { getConnectionType, isConnectionTypeKey } from './lookup';\nimport { lookupStrategies } from './lookupStrategies';\nimport type { RootConnection } from './types';\nimport { JsonObject } from '@backstage/types';\nimport {\n InputError,\n NotAllowedError,\n NotFoundError,\n toError,\n} from '@backstage/errors';\nimport { z } from 'zod/v4';\nimport { getLegacyIntegrations } from './getLegacyIntegrations';\nimport { combineConnectionSources } from './combineConnectionSources';\n\nfunction describeError(error: unknown): string {\n const e = toError(error);\n if (e.name === 'ZodError') {\n return z.prettifyError(e as unknown as z.ZodError);\n }\n if (e.cause !== undefined) {\n const cause = toError(e.cause);\n if (cause.name === 'ZodError') {\n return z.prettifyError(cause as unknown as z.ZodError);\n }\n }\n return e.message;\n}\n\nfunction getLookupStrategy<K extends LookupStrategy>(\n name: K,\n): (typeof lookupStrategies)[K] {\n return lookupStrategies[name];\n}\n\n// The identity field name is only known at runtime, so the typed connection\n// object cannot be indexed directly; this helper contains the erased read.\nfunction connectionIdentityOf(\n strategy: { identityField?: string },\n connection: object,\n): string | undefined {\n if (!strategy.identityField) {\n return undefined;\n }\n const value = (connection as Record<string, unknown>)[strategy.identityField];\n return typeof value === 'string' ? value : undefined;\n}\n\nclass PluginConnectionsService implements ConnectionsService {\n private readonly logger: LoggerService;\n private readonly connections: Connection[];\n\n constructor(logger: LoggerService, connections: Connection[]) {\n this.logger = logger;\n this.connections = connections;\n }\n\n async find<\n TType extends ConnectionTypeKey,\n TAuthMethod extends ConnectionAuthMethodKey<TType>,\n >(options: {\n type: TType;\n query: LookupConnectionType<TType>['query'];\n authMethods: readonly [TAuthMethod, ...TAuthMethod[]];\n }): Promise<Connection<TType, TAuthMethod>> {\n const result = await this.findOptional(options);\n if (!result) {\n throw new NotFoundError(\n `Connection not found for type \"${options.type}\"`,\n );\n }\n return result;\n }\n\n async findOptional<\n TType extends ConnectionTypeKey,\n TAuthMethod extends ConnectionAuthMethodKey<TType>,\n >({\n type,\n query,\n authMethods,\n }: {\n type: TType;\n query: LookupConnectionType<TType>['query'];\n authMethods: readonly [TAuthMethod, ...TAuthMethod[]];\n }): Promise<Connection<TType, TAuthMethod> | undefined> {\n const connectionType = getConnectionType(type);\n const strategy = getLookupStrategy(connectionType.lookupStrategy);\n const identity = strategy.identityFromQuery(query);\n\n this.logger.debug(\n `Finding connection of type \"${type}\"${\n identity ? ` matching ${strategy.identityField} \"${identity}\"` : ''\n }`,\n );\n\n let connection: Connection<TType> | undefined;\n if (identity !== undefined) {\n connection = this.connections.find(\n c => c.type === type && connectionIdentityOf(strategy, c) === identity,\n ) as Connection<TType> | undefined;\n } else {\n connection = this.connections.find(c => c.type === type) as\n | Connection<TType>\n | undefined;\n }\n\n if (!connection) {\n return undefined;\n }\n\n if (connection.auth.length === 0) {\n throw new NotAllowedError(\n `Connection of type \"${type}\"${\n identity ? ` for ${strategy.identityField} \"${identity}\"` : ''\n } has no auth method available to this plugin`,\n );\n }\n\n const matchAuth = connectionType.matchAuth as\n | ((authMethods: any[], query: any) => any | undefined)\n | undefined;\n\n const selected = matchAuth\n ? matchAuth(connection.auth, query)\n : connection.auth[0];\n\n if (!selected) {\n return undefined;\n }\n\n if (!(authMethods as readonly string[]).includes(selected.method)) {\n throw new NotAllowedError(\n `Connection not found for type \"${type}\" with auth method \"${selected.method}\"`,\n );\n }\n\n this.logger.debug(\n `Selected connection of type \"${type}\"${\n identity ? ` for ${strategy.identityField} \"${identity}\"` : ''\n } using auth method \"${selected.method}\"`,\n );\n\n return {\n ...connection,\n auth: selected,\n } as Connection<TType, TAuthMethod>;\n }\n}\n\n/** @public */\nexport class DefaultConnectionsService {\n private readonly logger: LoggerService;\n private readonly connections: RootConnection[];\n private readonly config: RootConfigService;\n\n private constructor(logger: LoggerService, config: RootConfigService) {\n this.logger = logger;\n this.config = config;\n this.connections = [];\n this.#registerConnectionsFromConfig();\n }\n\n static create(options: {\n logger: LoggerService;\n config: RootConfigService;\n }): DefaultConnectionsService {\n return new DefaultConnectionsService(options.logger, options.config);\n }\n\n #registerConnectionsFromConfig(): void {\n const legacy = this.#validateLegacy(getLegacyIntegrations(this.config));\n\n const rawConnections = this.config.getOptional('connections');\n if (rawConnections !== undefined && !Array.isArray(rawConnections)) {\n throw new InputError(\n 'Expected \"connections\" config to be an array of connection objects',\n );\n }\n\n const fromConfig = this.#validateConfig(\n (rawConnections as JsonObject[] | undefined) ?? [],\n );\n\n this.logger.debug(\n `Connections configuration resolved ${legacy.length} connection${\n legacy.length === 1 ? '' : 's'\n } from legacy integrations and ${fromConfig.length} explicit connection${\n fromConfig.length === 1 ? '' : 's'\n }`,\n );\n\n if (legacy.length === 0 && fromConfig.length === 0) {\n return;\n }\n\n this.connections.push(\n ...combineConnectionSources(legacy, fromConfig, this.logger),\n );\n\n const seen = new Set<string>();\n for (const c of this.connections) {\n const connectionType = getConnectionType(c.type as ConnectionTypeKey);\n const strategy = getLookupStrategy(connectionType.lookupStrategy);\n const identity = connectionIdentityOf(strategy, c);\n const key = `${c.type} ${identity ?? ''}`;\n if (seen.has(key)) {\n throw new InputError(\n identity !== undefined\n ? `Duplicate connection of type \"${c.type}\" for ${strategy.identityField} \"${identity}\"`\n : `Duplicate connection of type \"${c.type}\"`,\n );\n }\n seen.add(key);\n }\n\n this.#assignDefaultTitles();\n this.#assignDefaultAuthTitles();\n\n this.logger.info(\n `Loaded ${this.connections.length} connection${\n this.connections.length === 1 ? '' : 's'\n } from configuration`,\n );\n }\n\n #validateConfig(raw: JsonObject[]): RootConnection[] {\n return raw.map(v => {\n try {\n return this.#validateConnection(v);\n } catch (e) {\n const type = typeof v.type === 'string' ? v.type : 'unknown';\n throw new InputError(\n `Invalid connection of type \"${type}\" in connections config:\\n${describeError(\n e,\n )}`,\n );\n }\n });\n }\n\n #validateLegacy(raw: JsonObject[]): RootConnection[] {\n const result: RootConnection[] = [];\n for (const v of raw) {\n try {\n result.push(this.#validateConnection(v));\n } catch (e) {\n const type = typeof v.type === 'string' ? v.type : 'unknown';\n this.logger.error(\n `Failed to validate connection of type \"${type}\":\\n${describeError(\n e,\n )}`,\n );\n }\n }\n return result;\n }\n\n #validateConnection(connection: JsonObject): RootConnection {\n if (typeof connection.type !== 'string') {\n throw new InputError(`Unrecognised connection type ${connection.type}`);\n }\n\n if (!isConnectionTypeKey(connection.type)) {\n throw new InputError(`Unrecognised connection type ${connection.type}`);\n }\n\n const connectionType = getConnectionType(connection.type);\n\n const rawAuth = connection.auth;\n if (!Array.isArray(rawAuth) || rawAuth.length === 0) {\n throw new InputError(\n `Connection of type \"${connection.type}\" must configure at least one auth method`,\n );\n }\n\n const auth = (rawAuth as JsonObject[]).map(entry => {\n if (typeof entry.method !== 'string') {\n throw new InputError(\n `Auth entry for connection type \"${connection.type}\" is missing a \"method\" field`,\n );\n }\n const authMethod = connectionType.authMethods.find(\n am => am.method === entry.method,\n );\n if (!authMethod) {\n throw new InputError(\n `Unknown auth method \"${entry.method}\" for connection type \"${connection.type}\"`,\n );\n }\n const { method, title, match, ...rest } = entry;\n return {\n ...authMethod.configSchema.parse(rest),\n method,\n title: title as string | undefined,\n match: match as { plugins: string[] } | undefined,\n } as RootConnection['auth'][number];\n });\n\n const { type, auth: _, title, match, ...configFields } = connection;\n const parsed = connectionType.configSchema.parse(configFields);\n\n return {\n ...parsed,\n type: connection.type,\n title: title as string | undefined,\n match: match as { plugins: string[] } | undefined,\n auth,\n } as RootConnection;\n }\n\n #assignDefaultTitles(): void {\n const typeCounts = new Map<string, number>();\n for (const c of this.connections) {\n const type = c.type as ConnectionTypeKey;\n typeCounts.set(type, (typeCounts.get(type) ?? 0) + 1);\n }\n for (const c of this.connections) {\n if (!c.title) {\n const type = c.type as ConnectionTypeKey;\n const connectionType = getConnectionType(type);\n const displayName = connectionType.title;\n const identity = connectionIdentityOf(\n getLookupStrategy(connectionType.lookupStrategy),\n c,\n );\n (c as { title?: string }).title =\n typeCounts.get(type)! > 1 && identity\n ? `${displayName} (${identity})`\n : displayName;\n }\n }\n }\n\n #assignDefaultAuthTitles(): void {\n for (const c of this.connections) {\n const type = c.type as ConnectionTypeKey;\n const connectionType = getConnectionType(type);\n for (const auth of c.auth) {\n const authMethod = connectionType.authMethods.find(\n am => am.method === auth.method,\n );\n // The config schema only allows methods declared by the connection\n // type, so failing to find one means that invariant has been broken.\n if (!authMethod) {\n throw new Error(\n `Unknown auth method \"${auth.method}\" for connection type \"${type}\"`,\n );\n }\n auth.title ??= authMethod.title;\n }\n }\n }\n\n #getConnectionsForPlugin(pluginId: string): Connection[] {\n // Filter connections and hide auth methods based on these conditions:\n // 1. Include Connections with no plugin matcher condition\n // 2. Include Connections with a plugin matcher condition for this plugin\n // 3. Include auth methods with no plugin matcher condition\n // 4. Remove auth methods with a plugin matcher condition for other plugins\n return this.connections.flatMap(({ match, auth, ...rest }) => {\n if (match && !match.plugins.includes(pluginId)) {\n return [];\n }\n\n const pluginMatched: Connection['auth'] = [];\n const unmatched: Connection['auth'] = [];\n for (const { match: authMatch, ...authRest } of auth) {\n if (authMatch) {\n if (!authMatch.plugins.includes(pluginId)) continue;\n pluginMatched.push(authRest as Connection['auth'][number]);\n } else {\n unmatched.push(authRest as Connection['auth'][number]);\n }\n }\n\n return [\n { ...rest, auth: [...pluginMatched, ...unmatched] } as Connection,\n ];\n });\n }\n\n forPlugin(\n pluginId: string,\n options?: {\n logger: LoggerService;\n },\n ): ConnectionsService {\n const logger = options?.logger ?? this.logger;\n return new PluginConnectionsService(\n logger,\n this.#getConnectionsForPlugin(pluginId),\n );\n }\n}\n"],"names":["toError","z","lookupStrategies","NotFoundError","getConnectionType","NotAllowedError","getLegacyIntegrations","InputError","combineConnectionSources","isConnectionTypeKey","title","match"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var errors = require('@backstage/errors');
|
|
4
|
+
|
|
5
|
+
const lookupStrategies = {
|
|
6
|
+
host: {
|
|
7
|
+
identityField: "host",
|
|
8
|
+
identityFromQuery(query) {
|
|
9
|
+
try {
|
|
10
|
+
return new URL(query.url).host;
|
|
11
|
+
} catch {
|
|
12
|
+
throw new errors.InputError(
|
|
13
|
+
`Invalid url "${query.url}" passed to ConnectionsService.find`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
aws: {
|
|
19
|
+
identityFromQuery(_query) {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
exports.lookupStrategies = lookupStrategies;
|
|
26
|
+
//# sourceMappingURL=lookupStrategies.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookupStrategies.cjs.js","sources":["../../../../connections-node/src/lookupStrategies.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { InputError } from '@backstage/errors';\n\n/**\n * The definitions of all lookup strategies, keyed by the `lookupStrategy` of\n * each connection type. Each definition knows how to derive the identity to\n * match connections against from the query passed to `ConnectionsService.find`.\n *\n * @internal\n */\nexport const lookupStrategies = {\n host: {\n identityField: 'host' as const,\n identityFromQuery(query: { url: string }): string | undefined {\n try {\n return new URL(query.url).host;\n } catch {\n throw new InputError(\n `Invalid url \"${query.url}\" passed to ConnectionsService.find`,\n );\n }\n },\n },\n aws: {\n identityFromQuery(_query: {\n accountId?: string;\n arn?: string;\n }): string | undefined {\n return undefined;\n },\n },\n};\n"],"names":["InputError"],"mappings":";;;;AAwBO,MAAM,gBAAA,GAAmB;AAAA,EAC9B,IAAA,EAAM;AAAA,IACJ,aAAA,EAAe,MAAA;AAAA,IACf,kBAAkB,KAAA,EAA4C;AAC5D,MAAA,IAAI;AACF,QAAA,OAAO,IAAI,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,CAAE,IAAA;AAAA,MAC5B,CAAA,CAAA,MAAQ;AACN,QAAA,MAAM,IAAIA,iBAAA;AAAA,UACR,CAAA,aAAA,EAAgB,MAAM,GAAG,CAAA,mCAAA;AAAA,SAC3B;AAAA,MACF;AAAA,IACF;AAAA,GACF;AAAA,EACA,GAAA,EAAK;AAAA,IACH,kBAAkB,MAAA,EAGK;AACrB,MAAA,OAAO,MAAA;AAAA,IACT;AAAA;AAEJ;;;;"}
|
|
@@ -12,6 +12,7 @@ var service = require('../connections-node/src/service.cjs.js');
|
|
|
12
12
|
require('../connections-node/src/lookup.cjs.js');
|
|
13
13
|
require('zod/v4');
|
|
14
14
|
var withDeclaredConnections = require('./withDeclaredConnections.cjs.js');
|
|
15
|
+
var validateBackendFeature = require('./validateBackendFeature.cjs.js');
|
|
15
16
|
var OpaqueExtensionPointFactoryMiddleware = require('../backend-internal/src/wiring/OpaqueExtensionPointFactoryMiddleware.cjs.js');
|
|
16
17
|
var helpers = require('../backend-internal/src/wiring/helpers.cjs.js');
|
|
17
18
|
|
|
@@ -74,9 +75,8 @@ function collectCallerConnectionRegistrations(registrations) {
|
|
|
74
75
|
}
|
|
75
76
|
return byCaller;
|
|
76
77
|
}
|
|
77
|
-
function createRootInstanceMetadataServiceFactory(
|
|
78
|
+
function createRootInstanceMetadataServiceFactory(registrations) {
|
|
78
79
|
const installedPlugins = /* @__PURE__ */ new Map();
|
|
79
|
-
const registrations = rawRegistrations.filter((registration) => registration.featureType === "registrations").flatMap((registration) => registration.getRegistrations());
|
|
80
80
|
const plugins = registrations.filter(
|
|
81
81
|
(registration) => registration.type === "plugin" || registration.type === "plugin-v1.1"
|
|
82
82
|
);
|
|
@@ -119,6 +119,7 @@ class BackendInitializer {
|
|
|
119
119
|
#startPromise;
|
|
120
120
|
#stopPromise;
|
|
121
121
|
#registrations = new Array();
|
|
122
|
+
#allRegistrations = new Array();
|
|
122
123
|
#extensionPoints = /* @__PURE__ */ new Map();
|
|
123
124
|
#serviceRegistry;
|
|
124
125
|
#registeredFeatures = new Array();
|
|
@@ -212,19 +213,24 @@ class BackendInitializer {
|
|
|
212
213
|
if (this.#startPromise) {
|
|
213
214
|
throw new Error("feature can not be added after the backend has started");
|
|
214
215
|
}
|
|
215
|
-
this.#registeredFeatures.push(
|
|
216
|
+
this.#registeredFeatures.push({
|
|
217
|
+
feature: Promise.resolve(feature),
|
|
218
|
+
source: `features[${this.#registeredFeatures.length}]`
|
|
219
|
+
});
|
|
216
220
|
}
|
|
217
|
-
#addFeature(feature) {
|
|
218
|
-
if (
|
|
219
|
-
this.#serviceRegistry.add(feature);
|
|
220
|
-
} else if (
|
|
221
|
-
this.#registeredFeatureLoaders.push(
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
#addFeature(feature, source) {
|
|
222
|
+
if (feature.type === "service") {
|
|
223
|
+
this.#serviceRegistry.add(feature.feature, source);
|
|
224
|
+
} else if (feature.type === "loader") {
|
|
225
|
+
this.#registeredFeatureLoaders.push({
|
|
226
|
+
feature: feature.feature,
|
|
227
|
+
source
|
|
228
|
+
});
|
|
224
229
|
} else {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
this.#registrations.push({
|
|
231
|
+
feature: feature.feature,
|
|
232
|
+
source
|
|
233
|
+
});
|
|
228
234
|
}
|
|
229
235
|
}
|
|
230
236
|
async start() {
|
|
@@ -240,12 +246,17 @@ class BackendInitializer {
|
|
|
240
246
|
}
|
|
241
247
|
async #doStart() {
|
|
242
248
|
this.#serviceRegistry.checkForCircularDeps();
|
|
243
|
-
for (const feature of this.#registeredFeatures) {
|
|
244
|
-
|
|
249
|
+
for (const { feature, source: fallbackSource } of this.#registeredFeatures) {
|
|
250
|
+
const resolvedFeature = await feature;
|
|
251
|
+
const source = validateBackendFeature.describeBackendFeature(resolvedFeature) ?? fallbackSource;
|
|
252
|
+
this.#addFeature(validateBackendFeature.validateBackendFeature(resolvedFeature, source), source);
|
|
245
253
|
}
|
|
246
254
|
await this.#applyBackendFeatureLoaders(this.#registeredFeatureLoaders);
|
|
255
|
+
this.#allRegistrations = this.#registrations.flatMap(
|
|
256
|
+
({ feature, source }) => validateBackendFeature.validateBackendRegistrations(feature, source)
|
|
257
|
+
);
|
|
247
258
|
this.#serviceRegistry.add(
|
|
248
|
-
createRootInstanceMetadataServiceFactory(this.#
|
|
259
|
+
createRootInstanceMetadataServiceFactory(this.#allRegistrations)
|
|
249
260
|
);
|
|
250
261
|
if (process.env.NODE_ENV !== "test") {
|
|
251
262
|
const rootLogger2 = await this.#serviceRegistry.get(
|
|
@@ -270,9 +281,7 @@ class BackendInitializer {
|
|
|
270
281
|
backendPluginApi.coreServices.rootLogger,
|
|
271
282
|
"root"
|
|
272
283
|
);
|
|
273
|
-
const allRegistrations = this.#
|
|
274
|
-
(f) => f.getRegistrations()
|
|
275
|
-
);
|
|
284
|
+
const allRegistrations = this.#allRegistrations;
|
|
276
285
|
this.#callerConnectionRegistrations = collectCallerConnectionRegistrations(allRegistrations);
|
|
277
286
|
const allPluginIds = [
|
|
278
287
|
...new Set(
|
|
@@ -368,8 +377,11 @@ class BackendInitializer {
|
|
|
368
377
|
const pluginInits = /* @__PURE__ */ new Map();
|
|
369
378
|
const moduleInits = /* @__PURE__ */ new Map();
|
|
370
379
|
for (const r of allRegistrations) {
|
|
380
|
+
const pluginId = "pluginId" in r && typeof r.pluginId === "string" ? r.pluginId : void 0;
|
|
381
|
+
const moduleId = "moduleId" in r && typeof r.moduleId === "string" ? r.moduleId : void 0;
|
|
371
382
|
const addedExtensionPointIds = [];
|
|
372
383
|
try {
|
|
384
|
+
validateBackendFeature.validateBackendRegistration(r);
|
|
373
385
|
const provides = /* @__PURE__ */ new Set();
|
|
374
386
|
if (r.type === "plugin" || r.type === "module") {
|
|
375
387
|
for (const [extRef, extImpl] of r.extensionPoints) {
|
|
@@ -433,12 +445,12 @@ class BackendInitializer {
|
|
|
433
445
|
for (const id of addedExtensionPointIds) {
|
|
434
446
|
this.#extensionPoints.delete(id);
|
|
435
447
|
}
|
|
436
|
-
if (
|
|
437
|
-
resultCollector.onPluginModuleResult(
|
|
438
|
-
} else if (
|
|
439
|
-
pluginInits.delete(
|
|
440
|
-
moduleInits.delete(
|
|
441
|
-
resultCollector.onPluginResult(
|
|
448
|
+
if (pluginId !== void 0 && moduleId !== void 0) {
|
|
449
|
+
resultCollector.onPluginModuleResult(pluginId, moduleId, err);
|
|
450
|
+
} else if (pluginId !== void 0) {
|
|
451
|
+
pluginInits.delete(pluginId);
|
|
452
|
+
moduleInits.delete(pluginId);
|
|
453
|
+
resultCollector.onPluginResult(pluginId, err);
|
|
442
454
|
} else {
|
|
443
455
|
throw err;
|
|
444
456
|
}
|
|
@@ -465,11 +477,9 @@ class BackendInitializer {
|
|
|
465
477
|
const rootLifecycleService = await this.#getRootLifecycleImpl();
|
|
466
478
|
await rootLifecycleService.beforeShutdown();
|
|
467
479
|
const allPlugins = /* @__PURE__ */ new Set();
|
|
468
|
-
for (const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
allPlugins.add(r.pluginId);
|
|
472
|
-
}
|
|
480
|
+
for (const registration of this.#allRegistrations) {
|
|
481
|
+
if (registration.type === "plugin" || registration.type === "plugin-v1.1") {
|
|
482
|
+
allPlugins.add(registration.pluginId);
|
|
473
483
|
}
|
|
474
484
|
}
|
|
475
485
|
await Promise.allSettled(
|
|
@@ -513,10 +523,19 @@ class BackendInitializer {
|
|
|
513
523
|
}
|
|
514
524
|
async #applyBackendFeatureLoaders(loaders) {
|
|
515
525
|
const servicesAddedByLoaders = /* @__PURE__ */ new Map();
|
|
516
|
-
for (const
|
|
526
|
+
for (const installedLoader of loaders) {
|
|
527
|
+
const { feature: loader, source: loaderSource } = installedLoader;
|
|
528
|
+
if (loader.deps !== void 0) {
|
|
529
|
+
validateBackendFeature.assertObject(
|
|
530
|
+
loader.deps,
|
|
531
|
+
`${loaderSource}.deps`,
|
|
532
|
+
"a dependency object"
|
|
533
|
+
);
|
|
534
|
+
}
|
|
517
535
|
const deps = /* @__PURE__ */ new Map();
|
|
518
536
|
const missingRefs = /* @__PURE__ */ new Set();
|
|
519
537
|
for (const [name, ref] of Object.entries(loader.deps ?? {})) {
|
|
538
|
+
validateBackendFeature.validateServiceRef(ref, `${loaderSource}.deps.${name}`);
|
|
520
539
|
if (ref.scope !== "root") {
|
|
521
540
|
throw new Error(
|
|
522
541
|
`Feature loaders can only depend on root scoped services, but '${name}' is scoped to '${ref.scope}'. Offending loader is ${loader.description}`
|
|
@@ -538,35 +557,64 @@ class BackendInitializer {
|
|
|
538
557
|
`No service available for the following ref(s): ${missing}, depended on by feature loader ${loader.description}`
|
|
539
558
|
);
|
|
540
559
|
}
|
|
541
|
-
|
|
560
|
+
let result;
|
|
561
|
+
try {
|
|
562
|
+
result = await loader.loader(Object.fromEntries(deps));
|
|
563
|
+
} catch (error) {
|
|
542
564
|
throw new errors.ForwardedError(
|
|
543
565
|
`Feature loader ${loader.description} failed`,
|
|
544
566
|
error
|
|
545
567
|
);
|
|
568
|
+
}
|
|
569
|
+
if (!Array.isArray(result)) {
|
|
570
|
+
validateBackendFeature.throwInvalidBackendFeature(
|
|
571
|
+
`${loaderSource} -> loader output`,
|
|
572
|
+
"an array of backend features",
|
|
573
|
+
result
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
const loadedFeatures = result.map((feature, index) => {
|
|
577
|
+
const fallbackSource = `${loaderSource} -> loader output[${index}]`;
|
|
578
|
+
const unwrappedFeature = helpers.unwrapFeature(
|
|
579
|
+
feature
|
|
580
|
+
);
|
|
581
|
+
const description = validateBackendFeature.describeBackendFeature(unwrappedFeature);
|
|
582
|
+
const source = description ? `${description} (returned by ${loaderSource}, output[${index}])` : fallbackSource;
|
|
583
|
+
return {
|
|
584
|
+
feature: validateBackendFeature.validateBackendFeature(unwrappedFeature, source),
|
|
585
|
+
source
|
|
586
|
+
};
|
|
546
587
|
});
|
|
547
588
|
let didAddServiceFactory = false;
|
|
548
589
|
const newLoaders = new Array();
|
|
549
|
-
for
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
590
|
+
for (const loadedFeature of loadedFeatures) {
|
|
591
|
+
const { feature, source } = loadedFeature;
|
|
592
|
+
if (feature.type === "loader") {
|
|
593
|
+
newLoaders.push({ feature: feature.feature, source });
|
|
594
|
+
} else if (feature.type === "service") {
|
|
595
|
+
validateBackendFeature.validateServiceRef(feature.feature.service, `${source}.service`);
|
|
596
|
+
if (!feature.feature.service.multiton) {
|
|
554
597
|
const conflictingLoader = servicesAddedByLoaders.get(
|
|
555
|
-
feature.service.id
|
|
598
|
+
feature.feature.service.id
|
|
556
599
|
);
|
|
557
600
|
if (conflictingLoader) {
|
|
558
601
|
throw new Error(
|
|
559
|
-
`Duplicate service implementations provided for ${feature.service.id} by both feature loader ${loader.description} and feature loader ${conflictingLoader.description}`
|
|
602
|
+
`Duplicate service implementations provided for ${feature.feature.service.id} by both feature loader ${loader.description} and feature loader ${conflictingLoader.feature.description}`
|
|
560
603
|
);
|
|
561
604
|
}
|
|
562
|
-
if (!this.#serviceRegistry.hasBeenAdded(feature.service)) {
|
|
605
|
+
if (!this.#serviceRegistry.hasBeenAdded(feature.feature.service)) {
|
|
563
606
|
didAddServiceFactory = true;
|
|
564
|
-
servicesAddedByLoaders.set(
|
|
565
|
-
|
|
607
|
+
servicesAddedByLoaders.set(
|
|
608
|
+
feature.feature.service.id,
|
|
609
|
+
installedLoader
|
|
610
|
+
);
|
|
611
|
+
this.#addFeature(feature, source);
|
|
566
612
|
}
|
|
567
613
|
} else {
|
|
568
|
-
this.#addFeature(feature);
|
|
614
|
+
this.#addFeature(feature, source);
|
|
569
615
|
}
|
|
616
|
+
} else {
|
|
617
|
+
this.#addFeature(feature, source);
|
|
570
618
|
}
|
|
571
619
|
}
|
|
572
620
|
if (didAddServiceFactory) {
|
|
@@ -578,35 +626,6 @@ class BackendInitializer {
|
|
|
578
626
|
}
|
|
579
627
|
}
|
|
580
628
|
}
|
|
581
|
-
function toInternalBackendFeature(feature) {
|
|
582
|
-
if (feature.$$type !== "@backstage/BackendFeature") {
|
|
583
|
-
throw new Error(`Invalid BackendFeature, bad type '${feature.$$type}'`);
|
|
584
|
-
}
|
|
585
|
-
const internal = feature;
|
|
586
|
-
if (internal.version !== "v1") {
|
|
587
|
-
throw new Error(
|
|
588
|
-
`Invalid BackendFeature, bad version '${internal.version}'`
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
return internal;
|
|
592
|
-
}
|
|
593
|
-
function isServiceFactory(feature) {
|
|
594
|
-
const internal = toInternalBackendFeature(feature);
|
|
595
|
-
if (internal.featureType === "service") {
|
|
596
|
-
return true;
|
|
597
|
-
}
|
|
598
|
-
return "service" in internal;
|
|
599
|
-
}
|
|
600
|
-
function isBackendRegistrations(feature) {
|
|
601
|
-
const internal = toInternalBackendFeature(feature);
|
|
602
|
-
if (internal.featureType === "registrations") {
|
|
603
|
-
return true;
|
|
604
|
-
}
|
|
605
|
-
return "getRegistrations" in internal;
|
|
606
|
-
}
|
|
607
|
-
function isBackendFeatureLoader(feature) {
|
|
608
|
-
return toInternalBackendFeature(feature).featureType === "loader";
|
|
609
|
-
}
|
|
610
629
|
|
|
611
630
|
exports.BackendInitializer = BackendInitializer;
|
|
612
631
|
//# sourceMappingURL=BackendInitializer.cjs.js.map
|