@backstage/backend-defaults 0.17.1-next.1 → 0.17.1-next.2

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,14 @@
1
1
  # @backstage/backend-defaults
2
2
 
3
+ ## 0.17.1-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 90b572e: Adds an alpha `TracingService` to provide a unified interface for emitting trace spans across Backstage plugins.
8
+ - Updated dependencies
9
+ - @backstage/integration-aws-node@0.2.0-next.1
10
+ - @backstage/backend-plugin-api@1.9.1-next.1
11
+
3
12
  ## 0.17.1-next.1
4
13
 
5
14
  ### Patch Changes
package/config.d.ts CHANGED
@@ -1186,6 +1186,52 @@ export interface Config {
1186
1186
  };
1187
1187
  };
1188
1188
 
1189
+ /**
1190
+ * Tracing-related backend configuration. Honored by Backstage backend
1191
+ * plugins that emit OpenTelemetry trace spans.
1192
+ */
1193
+ tracing?: {
1194
+ /**
1195
+ * Opt-in capture of attributes that may identify users or contain
1196
+ * sensitive data on backend trace spans.
1197
+ */
1198
+ capture?: {
1199
+ /**
1200
+ * When true, backend plugins emitting trace spans for authenticated
1201
+ * requests SHOULD include the authenticated principal's identity as
1202
+ * `enduser.id` (the user entity ref for a user principal, or the
1203
+ * service subject for a service principal). Defaults to false.
1204
+ */
1205
+ endUser?: boolean;
1206
+ };
1207
+ /**
1208
+ * Plugin-specific tracing configuration. Each plugin can override
1209
+ * tracer instrumentation scope metadata.
1210
+ */
1211
+ plugin?: {
1212
+ [pluginId: string]: {
1213
+ /**
1214
+ * Tracer configuration for this plugin.
1215
+ */
1216
+ tracer?: {
1217
+ /**
1218
+ * Custom tracer name. If not set, defaults to
1219
+ * backstage-plugin-{pluginId}.
1220
+ */
1221
+ name?: string;
1222
+ /**
1223
+ * Version for the tracer.
1224
+ */
1225
+ version?: string;
1226
+ /**
1227
+ * Schema URL for the tracer.
1228
+ */
1229
+ schemaUrl?: string;
1230
+ };
1231
+ };
1232
+ };
1233
+ };
1234
+
1189
1235
  /**
1190
1236
  * Options to configure the default RootLoggerService.
1191
1237
  */
@@ -49,6 +49,7 @@ const defaultServiceFactories = [
49
49
  alpha.actionsRegistryServiceFactory,
50
50
  alpha.actionsServiceFactory,
51
51
  alpha.metricsServiceFactory,
52
+ alpha.tracingServiceFactory,
52
53
  // Unexported alpha services kept around for compatibility reasons
53
54
  instanceMetadataServiceFactory.instanceMetadataServiceFactory
54
55
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { ServiceFactory } from '@backstage/backend-plugin-api';\nimport { auditorServiceFactory } from '@backstage/backend-defaults/auditor';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\nimport {\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n} from '@backstage/backend-defaults/alpha';\nimport { instanceMetadataServiceFactory } from './alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory';\n\n/** @public */\nexport const defaultServiceFactories: ServiceFactory[] = [\n auditorServiceFactory,\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n permissionsRegistryServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n\n // alpha services\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n\n // Unexported alpha services kept around for compatibility reasons\n instanceMetadataServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","actionsRegistryServiceFactory","actionsServiceFactory","metricsServiceFactory","instanceMetadataServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA8CO,MAAM,uBAAA,GAA4C;AAAA,EACvDA,6BAAA;AAAA,EACAC,uBAAA;AAAA,EACAC,yBAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,qCAAA;AAAA,EACAC,qDAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,yCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,qCAAA;AAAA;AAAA,EAGAC,mCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,2BAAA;AAAA;AAAA,EAGAC;AACF;AAKO,SAAS,aAAA,GAAyB;AACvC,EAAA,OAAOC,sCAAA,CAAyB,EAAE,uBAAA,EAAyB,CAAA;AAC7D;;;;;"}
1
+ {"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { ServiceFactory } from '@backstage/backend-plugin-api';\nimport { auditorServiceFactory } from '@backstage/backend-defaults/auditor';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\nimport {\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n tracingServiceFactory,\n} from '@backstage/backend-defaults/alpha';\nimport { instanceMetadataServiceFactory } from './alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory';\n\n/** @public */\nexport const defaultServiceFactories: ServiceFactory[] = [\n auditorServiceFactory,\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n permissionsRegistryServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n\n // alpha services\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n tracingServiceFactory,\n\n // Unexported alpha services kept around for compatibility reasons\n instanceMetadataServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","actionsRegistryServiceFactory","actionsServiceFactory","metricsServiceFactory","tracingServiceFactory","instanceMetadataServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA+CO,MAAM,uBAAA,GAA4C;AAAA,EACvDA,6BAAA;AAAA,EACAC,uBAAA;AAAA,EACAC,yBAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,qCAAA;AAAA,EACAC,qDAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,yCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,qCAAA;AAAA;AAAA,EAGAC,mCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,2BAAA;AAAA;AAAA,EAGAC;AACF;AAKO,SAAS,aAAA,GAAyB;AACvC,EAAA,OAAOC,sCAAA,CAAyB,EAAE,uBAAA,EAAyB,CAAA;AAC7D;;;;;"}
@@ -0,0 +1,107 @@
1
+ 'use strict';
2
+
3
+ var api = require('@opentelemetry/api');
4
+
5
+ class DefaultTracingService {
6
+ tracer;
7
+ pluginId;
8
+ captureEndUser;
9
+ httpAuth;
10
+ constructor(opts) {
11
+ this.tracer = api.trace.getTracerProvider().getTracer(opts.name, opts.version, { schemaUrl: opts.schemaUrl });
12
+ this.pluginId = opts.pluginId;
13
+ this.captureEndUser = opts.captureEndUser;
14
+ this.httpAuth = opts.httpAuth;
15
+ }
16
+ static create(opts) {
17
+ return new DefaultTracingService(opts);
18
+ }
19
+ async startActiveSpan(name, fn, options = {}) {
20
+ let credentials = options.credentials;
21
+ if (!credentials && options.request) {
22
+ credentials = await this.httpAuth.credentials(options.request);
23
+ }
24
+ const principalAttributes = this.getPrincipalAttributes(credentials);
25
+ const attributes = {
26
+ "backstage.plugin.id": this.pluginId,
27
+ ...options.attributes,
28
+ ...principalAttributes
29
+ };
30
+ return this.tracer.startActiveSpan(
31
+ name,
32
+ { kind: toSpanKind(options.kind), attributes },
33
+ async (span) => {
34
+ try {
35
+ const wrapped = {
36
+ setAttribute(key, value) {
37
+ span.setAttribute(key, value);
38
+ },
39
+ setStatus(status) {
40
+ span.setStatus({
41
+ code: toSpanStatusCode(status.code),
42
+ message: status.message
43
+ });
44
+ }
45
+ };
46
+ const result = await fn(wrapped);
47
+ span.end();
48
+ return result;
49
+ } catch (err) {
50
+ const error = err;
51
+ span.recordException(error);
52
+ span.setAttribute("error.type", error.name || "Error");
53
+ span.setStatus({
54
+ code: api.SpanStatusCode.ERROR,
55
+ message: error.message || String(error)
56
+ });
57
+ span.end();
58
+ throw err;
59
+ }
60
+ }
61
+ );
62
+ }
63
+ getPrincipalAttributes(credentials) {
64
+ if (!credentials) return {};
65
+ const principal = credentials.principal;
66
+ if (!principal?.type) return {};
67
+ const attrs = {
68
+ "backstage.principal.type": principal.type
69
+ };
70
+ if (!this.captureEndUser) return attrs;
71
+ if (principal.type === "user" && principal.userEntityRef) {
72
+ attrs["enduser.id"] = principal.userEntityRef;
73
+ } else if (principal.type === "service" && principal.subject) {
74
+ attrs["enduser.id"] = principal.subject;
75
+ }
76
+ return attrs;
77
+ }
78
+ }
79
+ function toSpanKind(kind) {
80
+ switch (kind) {
81
+ case "internal":
82
+ return api.SpanKind.INTERNAL;
83
+ case "server":
84
+ return api.SpanKind.SERVER;
85
+ case "client":
86
+ return api.SpanKind.CLIENT;
87
+ case "producer":
88
+ return api.SpanKind.PRODUCER;
89
+ case "consumer":
90
+ return api.SpanKind.CONSUMER;
91
+ default:
92
+ return void 0;
93
+ }
94
+ }
95
+ function toSpanStatusCode(code) {
96
+ switch (code) {
97
+ case "ok":
98
+ return api.SpanStatusCode.OK;
99
+ case "error":
100
+ return api.SpanStatusCode.ERROR;
101
+ default:
102
+ return api.SpanStatusCode.UNSET;
103
+ }
104
+ }
105
+
106
+ exports.DefaultTracingService = DefaultTracingService;
107
+ //# sourceMappingURL=DefaultTracingService.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultTracingService.cjs.js","sources":["../../../../src/alpha/entrypoints/tracing/DefaultTracingService.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 */\n\nimport { SpanKind, SpanStatusCode, Tracer, trace } from '@opentelemetry/api';\nimport {\n BackstageCredentials,\n HttpAuthService,\n} from '@backstage/backend-plugin-api';\nimport {\n TracingService,\n TracingServiceAttributes,\n TracingServiceSpan,\n TracingServiceSpanKind,\n TracingServiceSpanOptions,\n TracingServiceSpanStatus,\n} from '@backstage/backend-plugin-api/alpha';\n\n/**\n * Options for creating a {@link DefaultTracingService}.\n *\n * @alpha\n */\nexport interface DefaultTracingServiceOptions {\n name: string;\n version?: string;\n schemaUrl?: string;\n pluginId: string;\n captureEndUser: boolean;\n httpAuth: HttpAuthService;\n}\n\n/**\n * Default implementation of the {@link TracingService} interface.\n *\n * @alpha\n */\nexport class DefaultTracingService implements TracingService {\n private readonly tracer: Tracer;\n private readonly pluginId: string;\n private readonly captureEndUser: boolean;\n private readonly httpAuth: HttpAuthService;\n\n private constructor(opts: DefaultTracingServiceOptions) {\n this.tracer = trace\n .getTracerProvider()\n .getTracer(opts.name, opts.version, { schemaUrl: opts.schemaUrl });\n this.pluginId = opts.pluginId;\n this.captureEndUser = opts.captureEndUser;\n this.httpAuth = opts.httpAuth;\n }\n\n static create(opts: DefaultTracingServiceOptions): TracingService {\n return new DefaultTracingService(opts);\n }\n\n async startActiveSpan<T>(\n name: string,\n fn: (span: TracingServiceSpan) => T | Promise<T>,\n options: TracingServiceSpanOptions = {},\n ): Promise<T> {\n let credentials = options.credentials;\n if (!credentials && options.request) {\n credentials = await this.httpAuth.credentials(options.request);\n }\n\n const principalAttributes = this.getPrincipalAttributes(credentials);\n const attributes: TracingServiceAttributes = {\n 'backstage.plugin.id': this.pluginId,\n ...options.attributes,\n ...principalAttributes,\n };\n\n return this.tracer.startActiveSpan(\n name,\n { kind: toSpanKind(options.kind), attributes },\n async span => {\n try {\n const wrapped: TracingServiceSpan = {\n setAttribute(key, value) {\n span.setAttribute(key, value);\n },\n setStatus(status) {\n span.setStatus({\n code: toSpanStatusCode(status.code),\n message: status.message,\n });\n },\n };\n const result = await fn(wrapped);\n span.end();\n return result;\n } catch (err) {\n const error = err as Error;\n span.recordException(error);\n span.setAttribute('error.type', error.name || 'Error');\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message || String(error),\n });\n span.end();\n throw err;\n }\n },\n );\n }\n\n private getPrincipalAttributes(\n credentials: BackstageCredentials | undefined,\n ): TracingServiceAttributes {\n if (!credentials) return {};\n const principal = credentials.principal as\n | { type?: string; userEntityRef?: string; subject?: string }\n | undefined;\n if (!principal?.type) return {};\n const attrs: TracingServiceAttributes = {\n 'backstage.principal.type': principal.type,\n };\n if (!this.captureEndUser) return attrs;\n if (principal.type === 'user' && principal.userEntityRef) {\n attrs['enduser.id'] = principal.userEntityRef;\n } else if (principal.type === 'service' && principal.subject) {\n attrs['enduser.id'] = principal.subject;\n }\n return attrs;\n }\n}\n\nfunction toSpanKind(\n kind: TracingServiceSpanKind | undefined,\n): SpanKind | undefined {\n switch (kind) {\n case 'internal':\n return SpanKind.INTERNAL;\n case 'server':\n return SpanKind.SERVER;\n case 'client':\n return SpanKind.CLIENT;\n case 'producer':\n return SpanKind.PRODUCER;\n case 'consumer':\n return SpanKind.CONSUMER;\n default:\n return undefined;\n }\n}\n\nfunction toSpanStatusCode(\n code: TracingServiceSpanStatus['code'],\n): SpanStatusCode {\n switch (code) {\n case 'ok':\n return SpanStatusCode.OK;\n case 'error':\n return SpanStatusCode.ERROR;\n default:\n return SpanStatusCode.UNSET;\n }\n}\n"],"names":["trace","SpanStatusCode","SpanKind"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ var alpha = require('@backstage/backend-plugin-api/alpha');
4
+ var backendPluginApi = require('@backstage/backend-plugin-api');
5
+ var DefaultTracingService = require('./DefaultTracingService.cjs.js');
6
+
7
+ const tracingServiceFactory = backendPluginApi.createServiceFactory({
8
+ service: alpha.tracingServiceRef,
9
+ deps: {
10
+ config: backendPluginApi.coreServices.rootConfig,
11
+ pluginMetadata: backendPluginApi.coreServices.pluginMetadata,
12
+ httpAuth: backendPluginApi.coreServices.httpAuth
13
+ },
14
+ factory: ({ config, pluginMetadata, httpAuth }) => {
15
+ const pluginId = pluginMetadata.getId();
16
+ const tracerConfig = config.getOptionalConfig(
17
+ `backend.tracing.plugin.${pluginId}.tracer`
18
+ );
19
+ const scopeName = `backstage-plugin-${pluginId}`;
20
+ const name = tracerConfig?.getOptionalString("name") ?? scopeName;
21
+ const version = tracerConfig?.getOptionalString("version");
22
+ const schemaUrl = tracerConfig?.getOptionalString("schemaUrl");
23
+ const captureEndUser = config.getOptionalBoolean("backend.tracing.capture.endUser") ?? false;
24
+ return DefaultTracingService.DefaultTracingService.create({
25
+ name,
26
+ version,
27
+ schemaUrl,
28
+ pluginId,
29
+ captureEndUser,
30
+ httpAuth
31
+ });
32
+ }
33
+ });
34
+
35
+ exports.tracingServiceFactory = tracingServiceFactory;
36
+ //# sourceMappingURL=tracingServiceFactory.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracingServiceFactory.cjs.js","sources":["../../../../src/alpha/entrypoints/tracing/tracingServiceFactory.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 */\n\nimport { tracingServiceRef } from '@backstage/backend-plugin-api/alpha';\nimport {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { DefaultTracingService } from './DefaultTracingService';\n\n/**\n * Service factory for emitting plugin-scoped trace spans.\n *\n * @alpha\n */\nexport const tracingServiceFactory = createServiceFactory({\n service: tracingServiceRef,\n deps: {\n config: coreServices.rootConfig,\n pluginMetadata: coreServices.pluginMetadata,\n httpAuth: coreServices.httpAuth,\n },\n factory: ({ config, pluginMetadata, httpAuth }) => {\n const pluginId = pluginMetadata.getId();\n\n const tracerConfig = config.getOptionalConfig(\n `backend.tracing.plugin.${pluginId}.tracer`,\n );\n const scopeName = `backstage-plugin-${pluginId}`;\n const name = tracerConfig?.getOptionalString('name') ?? scopeName;\n const version = tracerConfig?.getOptionalString('version');\n const schemaUrl = tracerConfig?.getOptionalString('schemaUrl');\n\n const captureEndUser =\n config.getOptionalBoolean('backend.tracing.capture.endUser') ?? false;\n\n return DefaultTracingService.create({\n name,\n version,\n schemaUrl,\n pluginId,\n captureEndUser,\n httpAuth,\n });\n },\n});\n"],"names":["createServiceFactory","tracingServiceRef","coreServices","DefaultTracingService"],"mappings":";;;;;;AA4BO,MAAM,wBAAwBA,qCAAA,CAAqB;AAAA,EACxD,OAAA,EAASC,uBAAA;AAAA,EACT,IAAA,EAAM;AAAA,IACJ,QAAQC,6BAAA,CAAa,UAAA;AAAA,IACrB,gBAAgBA,6BAAA,CAAa,cAAA;AAAA,IAC7B,UAAUA,6BAAA,CAAa;AAAA,GACzB;AAAA,EACA,SAAS,CAAC,EAAE,MAAA,EAAQ,cAAA,EAAgB,UAAS,KAAM;AACjD,IAAA,MAAM,QAAA,GAAW,eAAe,KAAA,EAAM;AAEtC,IAAA,MAAM,eAAe,MAAA,CAAO,iBAAA;AAAA,MAC1B,0BAA0B,QAAQ,CAAA,OAAA;AAAA,KACpC;AACA,IAAA,MAAM,SAAA,GAAY,oBAAoB,QAAQ,CAAA,CAAA;AAC9C,IAAA,MAAM,IAAA,GAAO,YAAA,EAAc,iBAAA,CAAkB,MAAM,CAAA,IAAK,SAAA;AACxD,IAAA,MAAM,OAAA,GAAU,YAAA,EAAc,iBAAA,CAAkB,SAAS,CAAA;AACzD,IAAA,MAAM,SAAA,GAAY,YAAA,EAAc,iBAAA,CAAkB,WAAW,CAAA;AAE7D,IAAA,MAAM,cAAA,GACJ,MAAA,CAAO,kBAAA,CAAmB,iCAAiC,CAAA,IAAK,KAAA;AAElE,IAAA,OAAOC,4CAAsB,MAAA,CAAO;AAAA,MAClC,IAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
package/dist/alpha.cjs.js CHANGED
@@ -4,6 +4,7 @@ var actionsRegistryServiceFactory = require('./alpha/entrypoints/actionsRegistry
4
4
  var actionsServiceFactory = require('./alpha/entrypoints/actions/actionsServiceFactory.cjs.js');
5
5
  var metricsServiceFactory = require('./alpha/entrypoints/metrics/metricsServiceFactory.cjs.js');
6
6
  var rootSystemMetadataServiceFactory = require('./alpha/entrypoints/rootSystemMetadata/rootSystemMetadataServiceFactory.cjs.js');
7
+ var tracingServiceFactory = require('./alpha/entrypoints/tracing/tracingServiceFactory.cjs.js');
7
8
 
8
9
 
9
10
 
@@ -11,4 +12,5 @@ exports.actionsRegistryServiceFactory = actionsRegistryServiceFactory.actionsReg
11
12
  exports.actionsServiceFactory = actionsServiceFactory.actionsServiceFactory;
12
13
  exports.metricsServiceFactory = metricsServiceFactory.metricsServiceFactory;
13
14
  exports.rootSystemMetadataServiceFactory = rootSystemMetadataServiceFactory.rootSystemMetadataServiceFactory;
15
+ exports.tracingServiceFactory = tracingServiceFactory.tracingServiceFactory;
14
16
  //# sourceMappingURL=alpha.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
1
+ {"version":3,"file":"alpha.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
package/dist/alpha.d.ts CHANGED
@@ -25,4 +25,11 @@ declare const metricsServiceFactory: _backstage_backend_plugin_api.ServiceFactor
25
25
  */
26
26
  declare const rootSystemMetadataServiceFactory: _backstage_backend_plugin_api.ServiceFactory<_backstage_backend_plugin_api_alpha.RootSystemMetadataService, "root", "singleton">;
27
27
 
28
- export { actionsRegistryServiceFactory, actionsServiceFactory, metricsServiceFactory, rootSystemMetadataServiceFactory };
28
+ /**
29
+ * Service factory for emitting plugin-scoped trace spans.
30
+ *
31
+ * @alpha
32
+ */
33
+ declare const tracingServiceFactory: _backstage_backend_plugin_api.ServiceFactory<_backstage_backend_plugin_api_alpha.TracingService, "plugin", "singleton">;
34
+
35
+ export { actionsRegistryServiceFactory, actionsServiceFactory, metricsServiceFactory, rootSystemMetadataServiceFactory, tracingServiceFactory };
@@ -80,7 +80,7 @@ interface HostDiscoveryOptions {
80
80
  * These endpoints have lower priority than any that are defined in
81
81
  * app-config, but higher priority than the fallback ones.
82
82
  *
83
- * This parameter is usedful for example if you want to provide a shared
83
+ * This parameter is useful for example if you want to provide a shared
84
84
  * library of core services to your plugin developers, which is set up for the
85
85
  * default behaviors in your org. This alleviates the need for replicating any
86
86
  * given set of endpoint config in every backend that you deploy.
@@ -1 +1 @@
1
- {"version":3,"file":"HostDiscovery.cjs.js","sources":["../../../src/entrypoints/discovery/HostDiscovery.ts"],"sourcesContent":["/*\n * Copyright 2020 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 { Config } from '@backstage/config';\nimport {\n DiscoveryService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { readHttpServerOptions } from '../rootHttpRouter/http/config';\nimport { SrvResolvers } from './SrvResolvers';\nimport { trimEnd } from 'lodash';\nimport { getEndpoints } from './parsing';\n\ntype Resolver = (pluginId: string) => Promise<string>;\n\n/**\n * A list of target base URLs and their associated plugins.\n *\n * @public\n */\nexport interface HostDiscoveryEndpoint {\n /**\n * The target base URL to use for the given set of plugins. Note that this\n * needs to be a full URL _including_ the protocol and path parts that fully\n * address the root of a plugin's API endpoints.\n *\n * @remarks\n *\n * Can be either a single URL or an object where you can explicitly give a\n * dedicated URL for internal (as seen from the backend) and/or external (as\n * seen from the frontend) lookups.\n *\n * The default behavior is to use the backend base URL for external lookups,\n * and a URL formed from the `.listen` and `.https` configs for internal\n * lookups. Adding discovery endpoints as described here overrides one or both\n * of those behaviors for a given set of plugins.\n *\n * URLs can be in the form of a regular HTTP or HTTPS URL if you are using\n * A/AAAA/CNAME records or IP addresses. Specifically for internal URLs, if\n * you add `+src` to the protocol part then the hostname is treated as an SRV\n * record name and resolved. For example, if you pass in\n * `http+srv://<record>/path` then the record part is resolved into an\n * actual host and port (with random weighted choice as usual when there is\n * more than one match).\n *\n * Any strings with `{{pluginId}}` or `{{ pluginId }}` placeholders in them\n * will have them replaced with the plugin ID.\n *\n * Example URLs:\n *\n * - `https://internal.example.com/secure/api/{{ pluginId }}`\n * - `http+srv://backstage-plugin-{{pluginId}}.http.services.company.net/api/{{pluginId}}`\n * (can only be used in the `internal` key)\n */\n target:\n | string\n | {\n internal?: string;\n external?: string;\n };\n\n /**\n * Array of plugins which use that target base URL.\n *\n * The special value `*` can be used to match all plugins.\n */\n plugins: string[];\n}\n\n/**\n * Options for the {@link HostDiscovery} class.\n *\n * @public\n */\nexport interface HostDiscoveryOptions {\n /**\n * The logger to use.\n */\n logger: LoggerService;\n\n /**\n * A default set of endpoints to use.\n *\n * @remarks\n *\n * These endpoints have lower priority than any that are defined in\n * app-config, but higher priority than the fallback ones.\n *\n * This parameter is usedful for example if you want to provide a shared\n * library of core services to your plugin developers, which is set up for the\n * default behaviors in your org. This alleviates the need for replicating any\n * given set of endpoint config in every backend that you deploy.\n */\n defaultEndpoints?: HostDiscoveryEndpoint[];\n}\n\n/**\n * A basic {@link @backstage/backend-plugin-api#DiscoveryService} implementation\n * that can handle plugins that are hosted in a single or multiple deployments.\n *\n * @public\n * @remarks\n *\n * Configuration is read from the `backend` config section, specifically the\n * `.baseUrl` for discovering the external URL, and the `.listen` and `.https`\n * config for the internal one. The fixed base path for these is `/api`, meaning\n * for example the default full internal path for the `catalog` plugin typically\n * will be `http://localhost:7007/api/catalog`.\n *\n * Those defaults can be overridden by providing a target and corresponding\n * plugins in `discovery.endpoints`, e.g.:\n *\n * ```yaml\n * discovery:\n * endpoints:\n * # Set a static internal and external base URL for a plugin\n * - target: https://internal.example.com/internal-catalog\n * plugins: [catalog]\n * # Sets a dynamic internal and external base URL pattern for two plugins\n * - target: https://internal.example.com/secure/api/{{pluginId}}\n * plugins: [auth, permission]\n * # Sets a dynamic base URL pattern for only the internal resolution for all\n * # other plugins, while leaving the external resolution unaffected\n * - target:\n * internal: http+srv://backstage-plugin-{{pluginId}}.http.${SERVICE_DOMAIN}/api/{{pluginId}}\n * plugins: [*]\n * ```\n */\nexport class HostDiscovery implements DiscoveryService {\n #srvResolver: SrvResolvers;\n #internalResolvers: Map<string, Resolver> = new Map();\n #externalResolvers: Map<string, Resolver> = new Map();\n #internalFallbackResolver: Resolver = async () => {\n throw new Error('Not initialized');\n };\n #externalFallbackResolver: Resolver = async () => {\n throw new Error('Not initialized');\n };\n\n static fromConfig(config: RootConfigService, options?: HostDiscoveryOptions) {\n // The getExternalBaseUrl implementation relies on the backend base URL\n // being a valid, non-local URL that others will be able to route to.\n const baseUrl = config.getString('backend.baseUrl');\n try {\n const { hostname } = new URL(baseUrl);\n const isLocalhost =\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname === '::';\n if (isLocalhost && process.env.NODE_ENV === 'production') {\n options?.logger?.warn(\n `backend.baseUrl is set to a localhost URL and NODE_ENV is '${process.env.NODE_ENV}'. This is likely a misconfiguration — localhost URLs are not reachable by other services in a deployed environment. Prefer setting it to a routable URL that can be resolved and reached both by your app and by other plugin deployments / services.`,\n );\n }\n } catch {\n options?.logger?.warn(\n `backend.baseUrl config value '${baseUrl}' does not appear to be a valid URL.`,\n );\n }\n\n const discovery = new HostDiscovery(new SrvResolvers());\n\n discovery.#updateResolvers(config, options?.defaultEndpoints);\n config.subscribe?.(() => {\n try {\n discovery.#updateResolvers(config, options?.defaultEndpoints);\n } catch (e) {\n options?.logger.error(`Failed to update discovery service: ${e}`);\n }\n });\n\n return discovery;\n }\n\n private constructor(srvResolver: SrvResolvers) {\n this.#srvResolver = srvResolver;\n this.#internalResolvers = new Map();\n this.#externalResolvers = new Map();\n this.#internalFallbackResolver = () => {\n throw new Error('Not initialized');\n };\n this.#externalFallbackResolver = () => {\n throw new Error('Not initialized');\n };\n }\n\n async getBaseUrl(pluginId: string): Promise<string> {\n const resolver =\n this.#internalResolvers.get(pluginId) ??\n this.#internalResolvers.get('*') ??\n this.#internalFallbackResolver;\n return await resolver(pluginId);\n }\n\n async getExternalBaseUrl(pluginId: string): Promise<string> {\n const resolver =\n this.#externalResolvers.get(pluginId) ??\n this.#externalResolvers.get('*') ??\n this.#externalFallbackResolver;\n return await resolver(pluginId);\n }\n\n #updateResolvers(config: Config, defaultEndpoints?: HostDiscoveryEndpoint[]) {\n this.#updateFallbackResolvers(config);\n this.#updatePluginResolvers(config, defaultEndpoints);\n }\n\n #updateFallbackResolvers(config: Config) {\n const backendBaseUrl = trimEnd(config.getString('backend.baseUrl'), '/');\n\n const {\n listen: { host: listenHost = '::', port: listenPort },\n } = readHttpServerOptions(config.getConfig('backend'));\n const protocol = config.has('backend.https') ? 'https' : 'http';\n\n // Translate bind-all to localhost, and support IPv6\n let host = listenHost;\n if (host === '::' || host === '') {\n // We use localhost instead of ::1, since IPv6-compatible systems should default\n // to using IPv6 when they see localhost, but if the system doesn't support IPv6\n // things will still work.\n host = 'localhost';\n } else if (host === '0.0.0.0') {\n host = '127.0.0.1';\n }\n if (host.includes(':')) {\n host = `[${host}]`;\n }\n\n this.#internalFallbackResolver = this.#makeResolver(\n `${protocol}://${host}:${listenPort}/api/{{pluginId}}`,\n false,\n );\n this.#externalFallbackResolver = this.#makeResolver(\n `${backendBaseUrl}/api/{{pluginId}}`,\n false,\n );\n }\n\n #updatePluginResolvers(\n config: Config,\n defaultEndpoints?: HostDiscoveryEndpoint[],\n ) {\n // Start out with the default endpoints, if any\n const endpoints = defaultEndpoints?.slice() ?? [];\n\n // Allow config to override the default endpoints\n endpoints.push(...getEndpoints(config));\n\n // Build up a new set of resolvers\n const internalResolvers: Map<string, Resolver> = new Map();\n const externalResolvers: Map<string, Resolver> = new Map();\n for (const { target, plugins } of endpoints) {\n let internalResolver: Resolver | undefined;\n let externalResolver: Resolver | undefined;\n\n if (typeof target === 'string') {\n internalResolver = externalResolver = this.#makeResolver(target, false);\n } else {\n if (target.internal) {\n internalResolver = this.#makeResolver(target.internal, true);\n }\n if (target.external) {\n externalResolver = this.#makeResolver(target.external, false);\n }\n }\n\n if (internalResolver) {\n for (const pluginId of plugins) {\n internalResolvers.set(pluginId, internalResolver);\n }\n }\n if (externalResolver) {\n for (const pluginId of plugins) {\n externalResolvers.set(pluginId, externalResolver);\n }\n }\n }\n\n // Only persist if no errors were thrown above\n this.#internalResolvers = internalResolvers;\n this.#externalResolvers = externalResolvers;\n }\n\n #makeResolver(urlPattern: string, allowSrv: boolean): Resolver {\n const withPluginId = (pluginId: string, url: string) => {\n return url.replace(\n /\\{\\{\\s*pluginId\\s*\\}\\}/g,\n encodeURIComponent(pluginId),\n );\n };\n\n if (!this.#srvResolver.isSrvUrl(urlPattern)) {\n return async pluginId => withPluginId(pluginId, urlPattern);\n }\n\n if (!allowSrv) {\n throw new Error(\n `SRV resolver URLs cannot be used in the target for external endpoints`,\n );\n }\n\n const lazyResolvers = new Map<string, () => Promise<string>>();\n return async pluginId => {\n let lazyResolver = lazyResolvers.get(pluginId);\n if (!lazyResolver) {\n lazyResolver = this.#srvResolver.getResolver(\n withPluginId(pluginId, urlPattern),\n );\n lazyResolvers.set(pluginId, lazyResolver);\n }\n return await lazyResolver();\n };\n }\n}\n"],"names":["SrvResolvers","config","trimEnd","readHttpServerOptions","getEndpoints"],"mappings":"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"}
1
+ {"version":3,"file":"HostDiscovery.cjs.js","sources":["../../../src/entrypoints/discovery/HostDiscovery.ts"],"sourcesContent":["/*\n * Copyright 2020 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 { Config } from '@backstage/config';\nimport {\n DiscoveryService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { readHttpServerOptions } from '../rootHttpRouter/http/config';\nimport { SrvResolvers } from './SrvResolvers';\nimport { trimEnd } from 'lodash';\nimport { getEndpoints } from './parsing';\n\ntype Resolver = (pluginId: string) => Promise<string>;\n\n/**\n * A list of target base URLs and their associated plugins.\n *\n * @public\n */\nexport interface HostDiscoveryEndpoint {\n /**\n * The target base URL to use for the given set of plugins. Note that this\n * needs to be a full URL _including_ the protocol and path parts that fully\n * address the root of a plugin's API endpoints.\n *\n * @remarks\n *\n * Can be either a single URL or an object where you can explicitly give a\n * dedicated URL for internal (as seen from the backend) and/or external (as\n * seen from the frontend) lookups.\n *\n * The default behavior is to use the backend base URL for external lookups,\n * and a URL formed from the `.listen` and `.https` configs for internal\n * lookups. Adding discovery endpoints as described here overrides one or both\n * of those behaviors for a given set of plugins.\n *\n * URLs can be in the form of a regular HTTP or HTTPS URL if you are using\n * A/AAAA/CNAME records or IP addresses. Specifically for internal URLs, if\n * you add `+src` to the protocol part then the hostname is treated as an SRV\n * record name and resolved. For example, if you pass in\n * `http+srv://<record>/path` then the record part is resolved into an\n * actual host and port (with random weighted choice as usual when there is\n * more than one match).\n *\n * Any strings with `{{pluginId}}` or `{{ pluginId }}` placeholders in them\n * will have them replaced with the plugin ID.\n *\n * Example URLs:\n *\n * - `https://internal.example.com/secure/api/{{ pluginId }}`\n * - `http+srv://backstage-plugin-{{pluginId}}.http.services.company.net/api/{{pluginId}}`\n * (can only be used in the `internal` key)\n */\n target:\n | string\n | {\n internal?: string;\n external?: string;\n };\n\n /**\n * Array of plugins which use that target base URL.\n *\n * The special value `*` can be used to match all plugins.\n */\n plugins: string[];\n}\n\n/**\n * Options for the {@link HostDiscovery} class.\n *\n * @public\n */\nexport interface HostDiscoveryOptions {\n /**\n * The logger to use.\n */\n logger: LoggerService;\n\n /**\n * A default set of endpoints to use.\n *\n * @remarks\n *\n * These endpoints have lower priority than any that are defined in\n * app-config, but higher priority than the fallback ones.\n *\n * This parameter is useful for example if you want to provide a shared\n * library of core services to your plugin developers, which is set up for the\n * default behaviors in your org. This alleviates the need for replicating any\n * given set of endpoint config in every backend that you deploy.\n */\n defaultEndpoints?: HostDiscoveryEndpoint[];\n}\n\n/**\n * A basic {@link @backstage/backend-plugin-api#DiscoveryService} implementation\n * that can handle plugins that are hosted in a single or multiple deployments.\n *\n * @public\n * @remarks\n *\n * Configuration is read from the `backend` config section, specifically the\n * `.baseUrl` for discovering the external URL, and the `.listen` and `.https`\n * config for the internal one. The fixed base path for these is `/api`, meaning\n * for example the default full internal path for the `catalog` plugin typically\n * will be `http://localhost:7007/api/catalog`.\n *\n * Those defaults can be overridden by providing a target and corresponding\n * plugins in `discovery.endpoints`, e.g.:\n *\n * ```yaml\n * discovery:\n * endpoints:\n * # Set a static internal and external base URL for a plugin\n * - target: https://internal.example.com/internal-catalog\n * plugins: [catalog]\n * # Sets a dynamic internal and external base URL pattern for two plugins\n * - target: https://internal.example.com/secure/api/{{pluginId}}\n * plugins: [auth, permission]\n * # Sets a dynamic base URL pattern for only the internal resolution for all\n * # other plugins, while leaving the external resolution unaffected\n * - target:\n * internal: http+srv://backstage-plugin-{{pluginId}}.http.${SERVICE_DOMAIN}/api/{{pluginId}}\n * plugins: [*]\n * ```\n */\nexport class HostDiscovery implements DiscoveryService {\n #srvResolver: SrvResolvers;\n #internalResolvers: Map<string, Resolver> = new Map();\n #externalResolvers: Map<string, Resolver> = new Map();\n #internalFallbackResolver: Resolver = async () => {\n throw new Error('Not initialized');\n };\n #externalFallbackResolver: Resolver = async () => {\n throw new Error('Not initialized');\n };\n\n static fromConfig(config: RootConfigService, options?: HostDiscoveryOptions) {\n // The getExternalBaseUrl implementation relies on the backend base URL\n // being a valid, non-local URL that others will be able to route to.\n const baseUrl = config.getString('backend.baseUrl');\n try {\n const { hostname } = new URL(baseUrl);\n const isLocalhost =\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname === '::';\n if (isLocalhost && process.env.NODE_ENV === 'production') {\n options?.logger?.warn(\n `backend.baseUrl is set to a localhost URL and NODE_ENV is '${process.env.NODE_ENV}'. This is likely a misconfiguration — localhost URLs are not reachable by other services in a deployed environment. Prefer setting it to a routable URL that can be resolved and reached both by your app and by other plugin deployments / services.`,\n );\n }\n } catch {\n options?.logger?.warn(\n `backend.baseUrl config value '${baseUrl}' does not appear to be a valid URL.`,\n );\n }\n\n const discovery = new HostDiscovery(new SrvResolvers());\n\n discovery.#updateResolvers(config, options?.defaultEndpoints);\n config.subscribe?.(() => {\n try {\n discovery.#updateResolvers(config, options?.defaultEndpoints);\n } catch (e) {\n options?.logger.error(`Failed to update discovery service: ${e}`);\n }\n });\n\n return discovery;\n }\n\n private constructor(srvResolver: SrvResolvers) {\n this.#srvResolver = srvResolver;\n this.#internalResolvers = new Map();\n this.#externalResolvers = new Map();\n this.#internalFallbackResolver = () => {\n throw new Error('Not initialized');\n };\n this.#externalFallbackResolver = () => {\n throw new Error('Not initialized');\n };\n }\n\n async getBaseUrl(pluginId: string): Promise<string> {\n const resolver =\n this.#internalResolvers.get(pluginId) ??\n this.#internalResolvers.get('*') ??\n this.#internalFallbackResolver;\n return await resolver(pluginId);\n }\n\n async getExternalBaseUrl(pluginId: string): Promise<string> {\n const resolver =\n this.#externalResolvers.get(pluginId) ??\n this.#externalResolvers.get('*') ??\n this.#externalFallbackResolver;\n return await resolver(pluginId);\n }\n\n #updateResolvers(config: Config, defaultEndpoints?: HostDiscoveryEndpoint[]) {\n this.#updateFallbackResolvers(config);\n this.#updatePluginResolvers(config, defaultEndpoints);\n }\n\n #updateFallbackResolvers(config: Config) {\n const backendBaseUrl = trimEnd(config.getString('backend.baseUrl'), '/');\n\n const {\n listen: { host: listenHost = '::', port: listenPort },\n } = readHttpServerOptions(config.getConfig('backend'));\n const protocol = config.has('backend.https') ? 'https' : 'http';\n\n // Translate bind-all to localhost, and support IPv6\n let host = listenHost;\n if (host === '::' || host === '') {\n // We use localhost instead of ::1, since IPv6-compatible systems should default\n // to using IPv6 when they see localhost, but if the system doesn't support IPv6\n // things will still work.\n host = 'localhost';\n } else if (host === '0.0.0.0') {\n host = '127.0.0.1';\n }\n if (host.includes(':')) {\n host = `[${host}]`;\n }\n\n this.#internalFallbackResolver = this.#makeResolver(\n `${protocol}://${host}:${listenPort}/api/{{pluginId}}`,\n false,\n );\n this.#externalFallbackResolver = this.#makeResolver(\n `${backendBaseUrl}/api/{{pluginId}}`,\n false,\n );\n }\n\n #updatePluginResolvers(\n config: Config,\n defaultEndpoints?: HostDiscoveryEndpoint[],\n ) {\n // Start out with the default endpoints, if any\n const endpoints = defaultEndpoints?.slice() ?? [];\n\n // Allow config to override the default endpoints\n endpoints.push(...getEndpoints(config));\n\n // Build up a new set of resolvers\n const internalResolvers: Map<string, Resolver> = new Map();\n const externalResolvers: Map<string, Resolver> = new Map();\n for (const { target, plugins } of endpoints) {\n let internalResolver: Resolver | undefined;\n let externalResolver: Resolver | undefined;\n\n if (typeof target === 'string') {\n internalResolver = externalResolver = this.#makeResolver(target, false);\n } else {\n if (target.internal) {\n internalResolver = this.#makeResolver(target.internal, true);\n }\n if (target.external) {\n externalResolver = this.#makeResolver(target.external, false);\n }\n }\n\n if (internalResolver) {\n for (const pluginId of plugins) {\n internalResolvers.set(pluginId, internalResolver);\n }\n }\n if (externalResolver) {\n for (const pluginId of plugins) {\n externalResolvers.set(pluginId, externalResolver);\n }\n }\n }\n\n // Only persist if no errors were thrown above\n this.#internalResolvers = internalResolvers;\n this.#externalResolvers = externalResolvers;\n }\n\n #makeResolver(urlPattern: string, allowSrv: boolean): Resolver {\n const withPluginId = (pluginId: string, url: string) => {\n return url.replace(\n /\\{\\{\\s*pluginId\\s*\\}\\}/g,\n encodeURIComponent(pluginId),\n );\n };\n\n if (!this.#srvResolver.isSrvUrl(urlPattern)) {\n return async pluginId => withPluginId(pluginId, urlPattern);\n }\n\n if (!allowSrv) {\n throw new Error(\n `SRV resolver URLs cannot be used in the target for external endpoints`,\n );\n }\n\n const lazyResolvers = new Map<string, () => Promise<string>>();\n return async pluginId => {\n let lazyResolver = lazyResolvers.get(pluginId);\n if (!lazyResolver) {\n lazyResolver = this.#srvResolver.getResolver(\n withPluginId(pluginId, urlPattern),\n );\n lazyResolvers.set(pluginId, lazyResolver);\n }\n return await lazyResolver();\n };\n }\n}\n"],"names":["SrvResolvers","config","trimEnd","readHttpServerOptions","getEndpoints"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version = "0.17.1-next.1";
5
+ var version = "0.17.1-next.2";
6
6
  var packageinfo = {
7
7
  version: version};
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-defaults",
3
- "version": "0.17.1-next.1",
3
+ "version": "0.17.1-next.2",
4
4
  "description": "Backend defaults used by Backstage backend apps",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -220,13 +220,13 @@
220
220
  "@azure/storage-blob": "^12.5.0",
221
221
  "@backstage/backend-app-api": "1.7.0-next.0",
222
222
  "@backstage/backend-dev-utils": "0.1.7",
223
- "@backstage/backend-plugin-api": "1.9.1-next.0",
223
+ "@backstage/backend-plugin-api": "1.9.1-next.1",
224
224
  "@backstage/cli-node": "0.3.2-next.1",
225
225
  "@backstage/config": "1.3.8-next.0",
226
226
  "@backstage/config-loader": "1.10.11-next.0",
227
227
  "@backstage/errors": "1.3.1-next.0",
228
- "@backstage/integration": "2.0.2-next.0",
229
- "@backstage/integration-aws-node": "0.1.22-next.0",
228
+ "@backstage/integration": "2.0.2-next.1",
229
+ "@backstage/integration-aws-node": "0.2.0-next.1",
230
230
  "@backstage/plugin-auth-node": "0.7.1-next.1",
231
231
  "@backstage/plugin-events-node": "0.4.22-next.0",
232
232
  "@backstage/plugin-permission-common": "0.9.9-next.1",
@@ -285,7 +285,7 @@
285
285
  },
286
286
  "devDependencies": {
287
287
  "@aws-sdk/util-stream-node": "^3.350.0",
288
- "@backstage/backend-test-utils": "1.11.3-next.1",
288
+ "@backstage/backend-test-utils": "1.11.3-next.2",
289
289
  "@backstage/cli": "0.36.2-next.1",
290
290
  "@google-cloud/cloud-sql-connector": "^1.4.0",
291
291
  "@types/archiver": "^7.0.0",