@backstage/backend-app-api 1.7.2 → 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 CHANGED
@@ -1,5 +1,22 @@
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
+
11
+ ## 1.7.3-next.0
12
+
13
+ ### Patch Changes
14
+
15
+ - 64cea29: Updated the backend runtime to use the internal connection service implementation after the shared connection contract moved into `@backstage/connections`.
16
+ - Updated dependencies
17
+ - @backstage/connections@0.3.0-next.0
18
+ - @backstage/backend-plugin-api@1.10.0-next.0
19
+
3
20
  ## 1.7.2
4
21
 
5
22
  ### 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
- if ("$$type" in feature) {
8
- return feature;
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
- if ("default" in feature) {
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 feature) {\n return feature;\n }\n\n if ('default' in feature) {\n return feature.default;\n }\n\n return feature;\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,YAAY,OAAA,EAAS;AACvB,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,IAAI,aAAa,OAAA,EAAS;AACxB,IAAA,OAAO,OAAA,CAAQ,OAAA;AAAA,EACjB;AAEA,EAAA,OAAO,OAAA;AACT;;;;;"}
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;;;;;"}
@@ -0,0 +1,291 @@
1
+ 'use strict';
2
+
3
+ var lookup = require('./lookup.cjs.js');
4
+ var lookupStrategies = require('./lookupStrategies.cjs.js');
5
+ var errors = require('@backstage/errors');
6
+ var v4 = require('zod/v4');
7
+ var getLegacyIntegrations = require('./getLegacyIntegrations.cjs.js');
8
+ var combineConnectionSources = require('./combineConnectionSources.cjs.js');
9
+
10
+ function describeError(error) {
11
+ const e = errors.toError(error);
12
+ if (e.name === "ZodError") {
13
+ return v4.z.prettifyError(e);
14
+ }
15
+ if (e.cause !== void 0) {
16
+ const cause = errors.toError(e.cause);
17
+ if (cause.name === "ZodError") {
18
+ return v4.z.prettifyError(cause);
19
+ }
20
+ }
21
+ return e.message;
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
+ }
33
+ class PluginConnectionsService {
34
+ logger;
35
+ connections;
36
+ constructor(logger, connections) {
37
+ this.logger = logger;
38
+ this.connections = connections;
39
+ }
40
+ async find(options) {
41
+ const result = await this.findOptional(options);
42
+ if (!result) {
43
+ throw new errors.NotFoundError(
44
+ `Connection not found for type "${options.type}"`
45
+ );
46
+ }
47
+ return result;
48
+ }
49
+ async findOptional({
50
+ type,
51
+ query,
52
+ authMethods
53
+ }) {
54
+ const connectionType = lookup.getConnectionType(type);
55
+ const strategy = getLookupStrategy(connectionType.lookupStrategy);
56
+ const identity = strategy.identityFromQuery(query);
57
+ this.logger.debug(
58
+ `Finding connection of type "${type}"${identity ? ` matching ${strategy.identityField} "${identity}"` : ""}`
59
+ );
60
+ let connection;
61
+ if (identity !== void 0) {
62
+ connection = this.connections.find(
63
+ (c) => c.type === type && connectionIdentityOf(strategy, c) === identity
64
+ );
65
+ } else {
66
+ connection = this.connections.find((c) => c.type === type);
67
+ }
68
+ if (!connection) {
69
+ return void 0;
70
+ }
71
+ if (connection.auth.length === 0) {
72
+ throw new errors.NotAllowedError(
73
+ `Connection of type "${type}"${identity ? ` for ${strategy.identityField} "${identity}"` : ""} has no auth method available to this plugin`
74
+ );
75
+ }
76
+ const matchAuth = connectionType.matchAuth;
77
+ const selected = matchAuth ? matchAuth(connection.auth, query) : connection.auth[0];
78
+ if (!selected) {
79
+ return void 0;
80
+ }
81
+ if (!authMethods.includes(selected.method)) {
82
+ throw new errors.NotAllowedError(
83
+ `Connection not found for type "${type}" with auth method "${selected.method}"`
84
+ );
85
+ }
86
+ this.logger.debug(
87
+ `Selected connection of type "${type}"${identity ? ` for ${strategy.identityField} "${identity}"` : ""} using auth method "${selected.method}"`
88
+ );
89
+ return {
90
+ ...connection,
91
+ auth: selected
92
+ };
93
+ }
94
+ }
95
+ class DefaultConnectionsService {
96
+ logger;
97
+ connections;
98
+ config;
99
+ constructor(logger, config) {
100
+ this.logger = logger;
101
+ this.config = config;
102
+ this.connections = [];
103
+ this.#registerConnectionsFromConfig();
104
+ }
105
+ static create(options) {
106
+ return new DefaultConnectionsService(options.logger, options.config);
107
+ }
108
+ #registerConnectionsFromConfig() {
109
+ const legacy = this.#validateLegacy(getLegacyIntegrations.getLegacyIntegrations(this.config));
110
+ const rawConnections = this.config.getOptional("connections");
111
+ if (rawConnections !== void 0 && !Array.isArray(rawConnections)) {
112
+ throw new errors.InputError(
113
+ 'Expected "connections" config to be an array of connection objects'
114
+ );
115
+ }
116
+ const fromConfig = this.#validateConfig(
117
+ rawConnections ?? []
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
+ );
122
+ if (legacy.length === 0 && fromConfig.length === 0) {
123
+ return;
124
+ }
125
+ this.connections.push(
126
+ ...combineConnectionSources.combineConnectionSources(legacy, fromConfig, this.logger)
127
+ );
128
+ const seen = /* @__PURE__ */ new Set();
129
+ for (const c of this.connections) {
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 ?? ""}`;
134
+ if (seen.has(key)) {
135
+ throw new errors.InputError(
136
+ identity !== void 0 ? `Duplicate connection of type "${c.type}" for ${strategy.identityField} "${identity}"` : `Duplicate connection of type "${c.type}"`
137
+ );
138
+ }
139
+ seen.add(key);
140
+ }
141
+ this.#assignDefaultTitles();
142
+ this.#assignDefaultAuthTitles();
143
+ this.logger.info(
144
+ `Loaded ${this.connections.length} connection${this.connections.length === 1 ? "" : "s"} from configuration`
145
+ );
146
+ }
147
+ #validateConfig(raw) {
148
+ return raw.map((v) => {
149
+ try {
150
+ return this.#validateConnection(v);
151
+ } catch (e) {
152
+ const type = typeof v.type === "string" ? v.type : "unknown";
153
+ throw new errors.InputError(
154
+ `Invalid connection of type "${type}" in connections config:
155
+ ${describeError(
156
+ e
157
+ )}`
158
+ );
159
+ }
160
+ });
161
+ }
162
+ #validateLegacy(raw) {
163
+ const result = [];
164
+ for (const v of raw) {
165
+ try {
166
+ result.push(this.#validateConnection(v));
167
+ } catch (e) {
168
+ const type = typeof v.type === "string" ? v.type : "unknown";
169
+ this.logger.error(
170
+ `Failed to validate connection of type "${type}":
171
+ ${describeError(
172
+ e
173
+ )}`
174
+ );
175
+ }
176
+ }
177
+ return result;
178
+ }
179
+ #validateConnection(connection) {
180
+ if (typeof connection.type !== "string") {
181
+ throw new errors.InputError(`Unrecognised connection type ${connection.type}`);
182
+ }
183
+ if (!lookup.isConnectionTypeKey(connection.type)) {
184
+ throw new errors.InputError(`Unrecognised connection type ${connection.type}`);
185
+ }
186
+ const connectionType = lookup.getConnectionType(connection.type);
187
+ const rawAuth = connection.auth;
188
+ if (!Array.isArray(rawAuth) || rawAuth.length === 0) {
189
+ throw new errors.InputError(
190
+ `Connection of type "${connection.type}" must configure at least one auth method`
191
+ );
192
+ }
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
+ };
224
+ }
225
+ #assignDefaultTitles() {
226
+ const typeCounts = /* @__PURE__ */ new Map();
227
+ for (const c of this.connections) {
228
+ const type = c.type;
229
+ typeCounts.set(type, (typeCounts.get(type) ?? 0) + 1);
230
+ }
231
+ for (const c of this.connections) {
232
+ if (!c.title) {
233
+ const type = c.type;
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;
241
+ }
242
+ }
243
+ }
244
+ #assignDefaultAuthTitles() {
245
+ for (const c of this.connections) {
246
+ const type = c.type;
247
+ const connectionType = lookup.getConnectionType(type);
248
+ for (const auth of c.auth) {
249
+ const authMethod = connectionType.authMethods.find(
250
+ (am) => am.method === auth.method
251
+ );
252
+ if (!authMethod) {
253
+ throw new Error(
254
+ `Unknown auth method "${auth.method}" for connection type "${type}"`
255
+ );
256
+ }
257
+ auth.title ??= authMethod.title;
258
+ }
259
+ }
260
+ }
261
+ #getConnectionsForPlugin(pluginId) {
262
+ return this.connections.flatMap(({ match, auth, ...rest }) => {
263
+ if (match && !match.plugins.includes(pluginId)) {
264
+ return [];
265
+ }
266
+ const pluginMatched = [];
267
+ const unmatched = [];
268
+ for (const { match: authMatch, ...authRest } of auth) {
269
+ if (authMatch) {
270
+ if (!authMatch.plugins.includes(pluginId)) continue;
271
+ pluginMatched.push(authRest);
272
+ } else {
273
+ unmatched.push(authRest);
274
+ }
275
+ }
276
+ return [
277
+ { ...rest, auth: [...pluginMatched, ...unmatched] }
278
+ ];
279
+ });
280
+ }
281
+ forPlugin(pluginId, options) {
282
+ const logger = options?.logger ?? this.logger;
283
+ return new PluginConnectionsService(
284
+ logger,
285
+ this.#getConnectionsForPlugin(pluginId)
286
+ );
287
+ }
288
+ }
289
+
290
+ exports.DefaultConnectionsService = DefaultConnectionsService;
291
+ //# sourceMappingURL=DefaultConnectionsService.cjs.js.map
@@ -0,0 +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 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
+ function combineConnectionSources(legacy, fromConfig, logger) {
4
+ const typeOf = (c) => c.type;
5
+ const typesInConfig = new Set(fromConfig.map(typeOf));
6
+ const warned = /* @__PURE__ */ new Set();
7
+ const result = [];
8
+ for (const legacyConn of legacy) {
9
+ const type = typeOf(legacyConn);
10
+ if (typesInConfig.has(type)) {
11
+ if (!warned.has(type)) {
12
+ warned.add(type);
13
+ logger.warn(
14
+ `Connection type "${type}" is defined in both legacy integrations and connections config; legacy integrations of this type are ignored.`
15
+ );
16
+ }
17
+ continue;
18
+ }
19
+ result.push(legacyConn);
20
+ }
21
+ result.push(...fromConfig);
22
+ return result;
23
+ }
24
+
25
+ exports.combineConnectionSources = combineConnectionSources;
26
+ //# sourceMappingURL=combineConnectionSources.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combineConnectionSources.cjs.js","sources":["../../../../connections-node/src/combineConnectionSources.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 { LoggerService } from '@backstage/backend-plugin-api';\nimport type { RootConnection } from './types';\n\n/**\n * Merges connections derived from legacy `integrations.*` config with\n * connections defined explicitly in `connections:` config.\n *\n * If any entry in `fromConfig` declares a given connection type, all legacy\n * entries of that type are discarded — the connections config fully takes\n * over for that type. A single warning is logged per discarded type.\n */\nexport function combineConnectionSources(\n legacy: RootConnection[],\n fromConfig: RootConnection[],\n logger: LoggerService,\n): RootConnection[] {\n const typeOf = (c: RootConnection) => c.type as string;\n const typesInConfig = new Set(fromConfig.map(typeOf));\n\n const warned = new Set<string>();\n const result: RootConnection[] = [];\n\n for (const legacyConn of legacy) {\n const type = typeOf(legacyConn);\n if (typesInConfig.has(type)) {\n if (!warned.has(type)) {\n warned.add(type);\n logger.warn(\n `Connection type \"${type}\" is defined in both legacy integrations and connections config; legacy integrations of this type are ignored.`,\n );\n }\n continue;\n }\n result.push(legacyConn);\n }\n\n result.push(...fromConfig);\n\n return result;\n}\n"],"names":[],"mappings":";;AA0BO,SAAS,wBAAA,CACd,MAAA,EACA,UAAA,EACA,MAAA,EACkB;AAClB,EAAA,MAAM,MAAA,GAAS,CAAC,CAAA,KAAsB,CAAA,CAAE,IAAA;AACxC,EAAA,MAAM,gBAAgB,IAAI,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,MAAM,CAAC,CAAA;AAEpD,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAC/B,EAAA,MAAM,SAA2B,EAAC;AAElC,EAAA,KAAA,MAAW,cAAc,MAAA,EAAQ;AAC/B,IAAA,MAAM,IAAA,GAAO,OAAO,UAAU,CAAA;AAC9B,IAAA,IAAI,aAAA,CAAc,GAAA,CAAI,IAAI,CAAA,EAAG;AAC3B,MAAA,IAAI,CAAC,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA,EAAG;AACrB,QAAA,MAAA,CAAO,IAAI,IAAI,CAAA;AACf,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,oBAAoB,IAAI,CAAA,8GAAA;AAAA,SAC1B;AAAA,MACF;AACA,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,KAAK,UAAU,CAAA;AAAA,EACxB;AAEA,EAAA,MAAA,CAAO,IAAA,CAAK,GAAG,UAAU,CAAA;AAEzB,EAAA,OAAO,MAAA;AACT;;;;"}