@backstage/backend-defaults 0.18.1-next.2 → 0.18.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.
Files changed (26) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/config.schema.json +8 -0
  3. package/dist/CreateBackend.cjs.js +1 -0
  4. package/dist/CreateBackend.cjs.js.map +1 -1
  5. package/dist/alpha/entrypoints/connections/connectionsServiceFactory.cjs.js +25 -0
  6. package/dist/alpha/entrypoints/connections/connectionsServiceFactory.cjs.js.map +1 -0
  7. package/dist/alpha.cjs.js +2 -0
  8. package/dist/alpha.cjs.js.map +1 -1
  9. package/dist/alpha.d.ts +9 -1
  10. package/dist/{package.json.cjs.js → backend-defaults/package.json.cjs.js} +1 -1
  11. package/dist/connections-node/src/DefaultConnectionsService.cjs.js +133 -0
  12. package/dist/connections-node/src/DefaultConnectionsService.cjs.js.map +1 -0
  13. package/dist/connections-node/src/lookup.cjs.js +11 -0
  14. package/dist/connections-node/src/lookup.cjs.js.map +1 -0
  15. package/dist/connections-node/src/lookupStrategies.cjs.js +30 -0
  16. package/dist/connections-node/src/lookupStrategies.cjs.js.map +1 -0
  17. package/dist/entrypoints/database/connectors/postgres.cjs.js +18 -2
  18. package/dist/entrypoints/database/connectors/postgres.cjs.js.map +1 -1
  19. package/dist/entrypoints/rootLogger/config.cjs.js +5 -1
  20. package/dist/entrypoints/rootLogger/config.cjs.js.map +1 -1
  21. package/dist/entrypoints/rootLogger/rootLoggerServiceFactory.cjs.js +14 -2
  22. package/dist/entrypoints/rootLogger/rootLoggerServiceFactory.cjs.js.map +1 -1
  23. package/dist/entrypoints/rootLogger/types.cjs.js.map +1 -1
  24. package/dist/entrypoints/urlReader/lib/GoogleGcsUrlReader.cjs.js +1 -1
  25. package/package.json +18 -17
  26. /package/dist/{package.json.cjs.js.map → backend-defaults/package.json.cjs.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @backstage/backend-defaults
2
2
 
3
+ ## 0.18.1
4
+
5
+ ### Patch Changes
6
+
7
+ - c478470: Added a service-authenticated action registry status endpoint that reports whether a plugin provides actions without exposing action metadata.
8
+ - f6054ac: Backends created with `createBackend` now provide the experimental connections service by default. Custom backends can install `connectionsServiceFactory` from `@backstage/backend-defaults/alpha`.
9
+ - e956084: Added an `instanceId` option to `createBackend`, allowing deployments to use an externally provided backend instance identifier. Each backend instance uses a random UUID by default.
10
+ - 1939ded: Added bounded retries for transient failures while initializing Cloud SQL database connections.
11
+ - a6bc5e4: Stopped issuing periodic per-plugin database keepalive queries by default and changed the default minimum PostgreSQL and MySQL connection pool size to zero. Idle connections can now be retired after the configured timeout, while explicitly configured pool minimums are preserved. Set `backend.database.keepalive: true` to continue issuing the periodic queries.
12
+ - bd1ff39: Fixed AWS RDS IAM authentication tokens being reused after their signing credentials expire, which could cause database connection failures when using temporary AWS credentials.
13
+ - 6887ecc: Updated dependency `infinispan` to `^0.16.0`.
14
+ - 495ef24: Added support for `backend.logger.redactAllowlist` configuration option, which allows specific secret values to pass through log redaction when they are known to be safe to log.
15
+ - Updated dependencies
16
+ - @backstage/cli-node@0.3.5
17
+ - @backstage/integration@2.1.3
18
+ - @backstage/backend-plugin-api@1.11.0
19
+ - @backstage/backend-app-api@1.8.0
20
+ - @backstage/config-loader@1.11.4
21
+ - @backstage/config@1.3.10
22
+ - @backstage/errors@1.3.2
23
+ - @backstage/connections@0.4.1
24
+ - @backstage/plugin-auth-node@0.7.7
25
+ - @backstage/integration-aws-node@0.2.3
26
+ - @backstage/plugin-events-node@0.4.27
27
+ - @backstage/plugin-permission-common@0.9.12
28
+ - @backstage/plugin-permission-node@0.11.5
29
+
30
+ ## 0.18.1-next.3
31
+
32
+ ### Patch Changes
33
+
34
+ - f6054ac: Backends created with `createBackend` now provide the experimental connections service by default. Custom backends can install `connectionsServiceFactory` from `@backstage/backend-defaults/alpha`.
35
+ - bd1ff39: Fixed AWS RDS IAM authentication tokens being reused after their signing credentials expire, which could cause database connection failures when using temporary AWS credentials.
36
+ - Updated dependencies
37
+ - @backstage/backend-plugin-api@1.11.0-next.2
38
+ - @backstage/backend-app-api@1.8.0-next.2
39
+
3
40
  ## 0.18.1-next.2
4
41
 
5
42
  ### Patch Changes
@@ -2114,6 +2114,14 @@
2114
2114
  ]
2115
2115
  },
2116
2116
  "description": "List of logger overrides.\n\nCan be used to configure a different level for logs matching certain criteria. For example, it can be used to ignore 'info' logs of given plugins."
2117
+ },
2118
+ "redactAllowlist": {
2119
+ "type": "array",
2120
+ "items": {
2121
+ "type": "string"
2122
+ },
2123
+ "description": "List of values to allow through redaction.\n\nThis makes it possible to avoid redacting certain values that are known to be safe to log.",
2124
+ "visibility": "backend"
2117
2125
  }
2118
2126
  },
2119
2127
  "description": "Options to configure the default RootLoggerService."
@@ -52,6 +52,7 @@ const defaultServiceFactories = [
52
52
  alpha.actionsServiceFactory,
53
53
  alpha.metricsServiceFactory,
54
54
  alpha.tracingServiceFactory,
55
+ alpha.connectionsServiceFactory,
55
56
  // Unexported alpha services kept around for compatibility reasons
56
57
  instanceMetadataServiceFactory.instanceMetadataServiceFactory
57
58
  ];
@@ -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 { rootSystemMetadataServiceFactory } from '@backstage/backend-defaults/rootSystemMetadata';\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 rootSystemMetadataServiceFactory,\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/** @public */\nexport interface CreateBackendOptions {\n /**\n * The globally unique identifier of this specific Backstage backend\n * instance.\n *\n * If omitted, a UUID is generated. Any provided value must not be used by\n * any other backend instance, including after this instance has stopped.\n */\n instanceId?: string;\n}\n\n/**\n * @public\n */\nexport function createBackend(options: CreateBackendOptions = {}): Backend {\n return createSpecializedBackend({\n defaultServiceFactories,\n instanceId: options.instanceId,\n });\n}\n"],"names":["auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","rootSystemMetadataServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","actionsRegistryServiceFactory","actionsServiceFactory","metricsServiceFactory","tracingServiceFactory","instanceMetadataServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDO,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,mDAAA;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;AAiBO,SAAS,aAAA,CAAc,OAAA,GAAgC,EAAC,EAAY;AACzE,EAAA,OAAOC,sCAAA,CAAyB;AAAA,IAC9B,uBAAA;AAAA,IACA,YAAY,OAAA,CAAQ;AAAA,GACrB,CAAA;AACH;;;"}
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 { rootSystemMetadataServiceFactory } from '@backstage/backend-defaults/rootSystemMetadata';\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 connectionsServiceFactory,\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 rootSystemMetadataServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n\n // alpha services\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n metricsServiceFactory,\n tracingServiceFactory,\n connectionsServiceFactory,\n\n // Unexported alpha services kept around for compatibility reasons\n instanceMetadataServiceFactory,\n];\n\n/** @public */\nexport interface CreateBackendOptions {\n /**\n * The globally unique identifier of this specific Backstage backend\n * instance.\n *\n * If omitted, a UUID is generated. Any provided value must not be used by\n * any other backend instance, including after this instance has stopped.\n */\n instanceId?: string;\n}\n\n/**\n * @public\n */\nexport function createBackend(options: CreateBackendOptions = {}): Backend {\n return createSpecializedBackend({\n defaultServiceFactories,\n instanceId: options.instanceId,\n });\n}\n"],"names":["auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","rootSystemMetadataServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","actionsRegistryServiceFactory","actionsServiceFactory","metricsServiceFactory","tracingServiceFactory","connectionsServiceFactory","instanceMetadataServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDO,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,mDAAA;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,EACAC,+BAAA;AAAA;AAAA,EAGAC;AACF;AAiBO,SAAS,aAAA,CAAc,OAAA,GAAgC,EAAC,EAAY;AACzE,EAAA,OAAOC,sCAAA,CAAyB;AAAA,IAC9B,uBAAA;AAAA,IACA,YAAY,OAAA,CAAQ;AAAA,GACrB,CAAA;AACH;;;"}
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var alpha = require('@backstage/backend-plugin-api/alpha');
5
+ var DefaultConnectionsService = require('../../../connections-node/src/DefaultConnectionsService.cjs.js');
6
+
7
+ const connectionsServiceFactory = backendPluginApi.createServiceFactory({
8
+ service: alpha.connectionsServiceRef,
9
+ deps: {
10
+ pluginMetadata: backendPluginApi.coreServices.pluginMetadata,
11
+ rootLogger: backendPluginApi.coreServices.rootLogger,
12
+ logger: backendPluginApi.coreServices.logger,
13
+ config: backendPluginApi.coreServices.rootConfig
14
+ },
15
+ async createRootContext({ rootLogger, config }) {
16
+ return DefaultConnectionsService.DefaultConnectionsService.create({ logger: rootLogger, config });
17
+ },
18
+ async factory({ logger, pluginMetadata }, connectionsService) {
19
+ const pluginId = pluginMetadata.getId();
20
+ return connectionsService.forPlugin(pluginId, { logger });
21
+ }
22
+ });
23
+
24
+ exports.connectionsServiceFactory = connectionsServiceFactory;
25
+ //# sourceMappingURL=connectionsServiceFactory.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectionsServiceFactory.cjs.js","sources":["../../../../src/alpha/entrypoints/connections/connectionsServiceFactory.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 {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { connectionsServiceRef } from '@backstage/backend-plugin-api/alpha';\nimport { DefaultConnectionsService } from '@backstage/connections-node';\n\n/**\n * Creates the default plugin-scoped connections service.\n *\n * @alpha\n */\nexport const connectionsServiceFactory = createServiceFactory({\n service: connectionsServiceRef,\n deps: {\n pluginMetadata: coreServices.pluginMetadata,\n rootLogger: coreServices.rootLogger,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n },\n async createRootContext({ rootLogger, config }) {\n return DefaultConnectionsService.create({ logger: rootLogger, config });\n },\n\n async factory({ logger, pluginMetadata }, connectionsService) {\n const pluginId = pluginMetadata.getId();\n return connectionsService.forPlugin(pluginId, { logger });\n },\n});\n"],"names":["createServiceFactory","connectionsServiceRef","coreServices","DefaultConnectionsService"],"mappings":";;;;;;AA4BO,MAAM,4BAA4BA,qCAAA,CAAqB;AAAA,EAC5D,OAAA,EAASC,2BAAA;AAAA,EACT,IAAA,EAAM;AAAA,IACJ,gBAAgBC,6BAAA,CAAa,cAAA;AAAA,IAC7B,YAAYA,6BAAA,CAAa,UAAA;AAAA,IACzB,QAAQA,6BAAA,CAAa,MAAA;AAAA,IACrB,QAAQA,6BAAA,CAAa;AAAA,GACvB;AAAA,EACA,MAAM,iBAAA,CAAkB,EAAE,UAAA,EAAY,QAAO,EAAG;AAC9C,IAAA,OAAOC,oDAA0B,MAAA,CAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,QAAQ,CAAA;AAAA,EACxE,CAAA;AAAA,EAEA,MAAM,OAAA,CAAQ,EAAE,MAAA,EAAQ,cAAA,IAAkB,kBAAA,EAAoB;AAC5D,IAAA,MAAM,QAAA,GAAW,eAAe,KAAA,EAAM;AACtC,IAAA,OAAO,kBAAA,CAAmB,SAAA,CAAU,QAAA,EAAU,EAAE,QAAQ,CAAA;AAAA,EAC1D;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 tracingServiceFactory = require('./alpha/entrypoints/tracing/tracingServiceFactory.cjs.js');
7
+ var connectionsServiceFactory = require('./alpha/entrypoints/connections/connectionsServiceFactory.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.tracingServiceFactory = tracingServiceFactory.tracingServiceFactory;
15
+ exports.connectionsServiceFactory = connectionsServiceFactory.connectionsServiceFactory;
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
@@ -1,5 +1,6 @@
1
1
  import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
2
  import * as _backstage_backend_plugin_api_alpha from '@backstage/backend-plugin-api/alpha';
3
+ import * as _backstage_connections from '@backstage/connections';
3
4
 
4
5
  /**
5
6
  * @public
@@ -25,4 +26,11 @@ declare const metricsServiceFactory: _backstage_backend_plugin_api.ServiceFactor
25
26
  */
26
27
  declare const tracingServiceFactory: _backstage_backend_plugin_api.ServiceFactory<_backstage_backend_plugin_api_alpha.TracingService, "plugin", "singleton">;
27
28
 
28
- export { actionsRegistryServiceFactory, actionsServiceFactory, metricsServiceFactory, tracingServiceFactory };
29
+ /**
30
+ * Creates the default plugin-scoped connections service.
31
+ *
32
+ * @alpha
33
+ */
34
+ declare const connectionsServiceFactory: _backstage_backend_plugin_api.ServiceFactory<_backstage_connections.ConnectionsService, "plugin", "singleton">;
35
+
36
+ export { actionsRegistryServiceFactory, actionsServiceFactory, connectionsServiceFactory, metricsServiceFactory, tracingServiceFactory };
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version = "0.18.1-next.2";
5
+ var version = "0.18.1";
6
6
  var packageinfo = {
7
7
  version: version};
8
8
 
@@ -0,0 +1,133 @@
1
+ 'use strict';
2
+
3
+ var config = require('@backstage/connections/config');
4
+ var lookup = require('./lookup.cjs.js');
5
+ var lookupStrategies = require('./lookupStrategies.cjs.js');
6
+ var errors = require('@backstage/errors');
7
+
8
+ function getLookupStrategy(name) {
9
+ return lookupStrategies.lookupStrategies[name];
10
+ }
11
+ function connectionIdentityOf(strategy, connection) {
12
+ if (!strategy.identityField) {
13
+ return void 0;
14
+ }
15
+ const value = connection[strategy.identityField];
16
+ return typeof value === "string" ? value : void 0;
17
+ }
18
+ class PluginConnectionsService {
19
+ logger;
20
+ connections;
21
+ constructor(logger, connections) {
22
+ this.logger = logger;
23
+ this.connections = connections;
24
+ }
25
+ async find(options) {
26
+ const result = await this.findOptional(options);
27
+ if (!result) {
28
+ throw new errors.NotFoundError(
29
+ `Connection not found for type "${options.type}"`
30
+ );
31
+ }
32
+ return result;
33
+ }
34
+ async findOptional({
35
+ type,
36
+ query,
37
+ authMethods
38
+ }) {
39
+ const connectionType = lookup.getConnectionType(type);
40
+ const strategy = getLookupStrategy(connectionType.lookupStrategy);
41
+ const identity = strategy.identityFromQuery(query);
42
+ this.logger.debug(
43
+ `Finding connection of type "${type}"${identity ? ` matching ${strategy.identityField} "${identity}"` : ""}`
44
+ );
45
+ let connection;
46
+ if (identity !== void 0) {
47
+ connection = this.connections.find(
48
+ (c) => c.type === type && connectionIdentityOf(strategy, c) === identity
49
+ );
50
+ } else {
51
+ connection = this.connections.find((c) => c.type === type);
52
+ }
53
+ if (!connection) {
54
+ return void 0;
55
+ }
56
+ if (!authMethods) {
57
+ const { auth: _, ...info } = connection;
58
+ return info;
59
+ }
60
+ if (connection.auth.length === 0) {
61
+ throw new errors.NotAllowedError(
62
+ `Connection of type "${type}"${identity ? ` for ${strategy.identityField} "${identity}"` : ""} has no auth method available to this plugin`
63
+ );
64
+ }
65
+ const matchAuth = connectionType.matchAuth;
66
+ const selected = matchAuth ? matchAuth(connection.auth, query) : connection.auth[0];
67
+ if (!selected) {
68
+ return void 0;
69
+ }
70
+ if (!authMethods.includes(selected.method)) {
71
+ throw new errors.NotAllowedError(
72
+ `Connection not found for type "${type}" with auth method "${selected.method}"`
73
+ );
74
+ }
75
+ this.logger.debug(
76
+ `Selected connection of type "${type}"${identity ? ` for ${strategy.identityField} "${identity}"` : ""} using auth method "${selected.method}"`
77
+ );
78
+ return {
79
+ ...connection,
80
+ auth: selected
81
+ };
82
+ }
83
+ }
84
+ class DefaultConnectionsService {
85
+ logger;
86
+ connections;
87
+ config;
88
+ constructor(logger, config) {
89
+ this.logger = logger;
90
+ this.config = config;
91
+ this.connections = [];
92
+ this.#registerConnectionsFromConfig();
93
+ }
94
+ static create(options) {
95
+ return new DefaultConnectionsService(options.logger, options.config);
96
+ }
97
+ #registerConnectionsFromConfig() {
98
+ this.connections.push(
99
+ ...config.buildConnectionsFromConfig({
100
+ config: this.config,
101
+ logger: this.logger
102
+ })
103
+ );
104
+ if (this.connections.length === 0) {
105
+ return;
106
+ }
107
+ this.logger.info(
108
+ `Loaded ${this.connections.length} connection${this.connections.length === 1 ? "" : "s"} from configuration`
109
+ );
110
+ }
111
+ #getConnectionsForPlugin(pluginId) {
112
+ return this.connections.flatMap((connection) => {
113
+ if (connection.match && !connection.match.plugins.includes(pluginId)) {
114
+ return [];
115
+ }
116
+ const pluginMatched = connection.auth.filter(
117
+ (a) => a.match?.plugins.includes(pluginId)
118
+ );
119
+ const unmatched = connection.auth.filter((a) => !a.match);
120
+ return [{ ...connection, auth: [...pluginMatched, ...unmatched] }];
121
+ });
122
+ }
123
+ forPlugin(pluginId, options) {
124
+ const logger = options?.logger ?? this.logger;
125
+ return new PluginConnectionsService(
126
+ logger,
127
+ this.#getConnectionsForPlugin(pluginId)
128
+ );
129
+ }
130
+ }
131
+
132
+ exports.DefaultConnectionsService = DefaultConnectionsService;
133
+ //# 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 ConnectionLookupStrategy,\n ConnectionsService,\n ConnectionTypeDefinition,\n ConnectionType,\n LookupConnectionType,\n} from '@backstage/connections';\nimport type { ConfiguredConnection } from '@backstage/connections/config';\nimport { buildConnectionsFromConfig } from '@backstage/connections/config';\nimport { getConnectionType } from './lookup';\nimport { lookupStrategies } from './lookupStrategies';\nimport { NotAllowedError, NotFoundError } from '@backstage/errors';\n\ntype ConnectionQuery<TType extends ConnectionType> =\n LookupConnectionType<TType> extends ConnectionTypeDefinition<\n infer TDefinition\n >\n ? TDefinition['query']\n : never;\n\nfunction getLookupStrategy<K extends ConnectionLookupStrategy>(\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: ConfiguredConnection[];\n\n constructor(logger: LoggerService, connections: ConfiguredConnection[]) {\n this.logger = logger;\n this.connections = connections;\n }\n\n async find<\n TType extends ConnectionType,\n TAuthMethod extends LookupConnectionType<TType>['authMethods'][number]['method'],\n >(options: {\n type: TType;\n query: ConnectionQuery<TType>;\n authMethods?: readonly [TAuthMethod, ...TAuthMethod[]];\n }): Promise<\n Connection<TType, TAuthMethod> | Omit<Connection<TType>, 'auth'>\n > {\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 private async findOptional<\n TType extends ConnectionType,\n TAuthMethod extends LookupConnectionType<TType>['authMethods'][number]['method'],\n >({\n type,\n query,\n authMethods,\n }: {\n type: TType;\n query: ConnectionQuery<TType>;\n authMethods?: readonly [TAuthMethod, ...TAuthMethod[]];\n }): Promise<\n Connection<TType, TAuthMethod> | Omit<Connection<TType>, 'auth'> | undefined\n > {\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: ConfiguredConnection | undefined;\n if (identity !== undefined) {\n connection = this.connections.find(\n c => c.type === type && connectionIdentityOf(strategy, c) === identity,\n );\n } else {\n connection = this.connections.find(c => c.type === type);\n }\n\n if (!connection) {\n return undefined;\n }\n\n if (!authMethods) {\n const { auth: _, ...info } = connection;\n return info as Omit<Connection<TType>, 'auth'>;\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 as any).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: ConfiguredConnection[];\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 this.connections.push(\n ...buildConnectionsFromConfig({\n config: this.config,\n logger: this.logger,\n }),\n );\n\n if (this.connections.length === 0) {\n return;\n }\n\n this.logger.info(\n `Loaded ${this.connections.length} connection${\n this.connections.length === 1 ? '' : 's'\n } from configuration`,\n );\n }\n\n #getConnectionsForPlugin(pluginId: string): ConfiguredConnection[] {\n // Filter connections and auth methods by plugin scope. Auth entries\n // explicitly matched to this plugin are ordered before unscoped entries\n // so that plugin-specific credentials take precedence.\n return this.connections.flatMap(connection => {\n if (connection.match && !connection.match.plugins.includes(pluginId)) {\n return [];\n }\n\n const pluginMatched = connection.auth.filter(a =>\n a.match?.plugins.includes(pluginId),\n );\n const unmatched = connection.auth.filter(a => !a.match);\n\n return [{ ...connection, auth: [...pluginMatched, ...unmatched] }];\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":["lookupStrategies","NotFoundError","getConnectionType","NotAllowedError","buildConnectionsFromConfig"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var connections = require('@backstage/connections');
4
+
5
+ const connectionTypesMap = new Map(Object.entries(connections.connectionTypes));
6
+ function getConnectionType(key) {
7
+ return connectionTypesMap.get(key);
8
+ }
9
+
10
+ exports.getConnectionType = getConnectionType;
11
+ //# sourceMappingURL=lookup.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.cjs.js","sources":["../../../../connections-node/src/lookup.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 LookupConnectionType,\n ConnectionType,\n connectionTypes,\n} from '@backstage/connections';\n\nconst connectionTypesMap = new Map(Object.entries(connectionTypes));\n\nexport function getConnectionType<T extends ConnectionType>(\n key: T,\n): LookupConnectionType<T> {\n return connectionTypesMap.get(key) as LookupConnectionType<T>;\n}\n\nexport function isConnectionType(\n value: string | undefined,\n): value is ConnectionType {\n if (!value) return false;\n\n return connectionTypesMap.has(value);\n}\n"],"names":["connectionTypes"],"mappings":";;;;AAqBA,MAAM,qBAAqB,IAAI,GAAA,CAAI,MAAA,CAAO,OAAA,CAAQA,2BAAe,CAAC,CAAA;AAE3D,SAAS,kBACd,GAAA,EACyB;AACzB,EAAA,OAAO,kBAAA,CAAmB,IAAI,GAAG,CAAA;AACnC;;"}
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@backstage/errors');
4
+
5
+ const lookupStrategies = {
6
+ host: {
7
+ identityField: "host",
8
+ identityFromQuery(query) {
9
+ const { url } = query;
10
+ try {
11
+ return new URL(url).host;
12
+ } catch {
13
+ throw new errors.InputError(
14
+ `Invalid url "${url}" passed to ConnectionsService.find`
15
+ );
16
+ }
17
+ }
18
+ },
19
+ // AWS has no identity field to match against — all accounts live under a
20
+ // single connection. Account selection is handled entirely by the
21
+ // connection type's matchAuth implementation.
22
+ aws: {
23
+ identityFromQuery() {
24
+ return void 0;
25
+ }
26
+ }
27
+ };
28
+
29
+ exports.lookupStrategies = lookupStrategies;
30
+ //# 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';\nimport type { ConnectionLookupStrategy } from '@backstage/connections';\n\n// The concrete strategy is only known at runtime, so definitions receive the\n// query with its type erased and narrow it to their own query shape.\ntype ConnectionLookupStrategyDefinition = {\n identityField?: string;\n identityFromQuery(query: unknown): string | undefined;\n};\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 * The `identityField` of each strategy is mirrored by the `identityFields` map\n * in the config pipeline of the `@backstage/connections` package; keep the two\n * in sync when adding or changing strategies.\n *\n * @internal\n */\nexport const lookupStrategies: Record<\n ConnectionLookupStrategy,\n ConnectionLookupStrategyDefinition\n> = {\n host: {\n identityField: 'host',\n identityFromQuery(query) {\n const { url } = query as { url: string };\n try {\n return new URL(url).host;\n } catch {\n throw new InputError(\n `Invalid url \"${url}\" passed to ConnectionsService.find`,\n );\n }\n },\n },\n // AWS has no identity field to match against — all accounts live under a\n // single connection. Account selection is handled entirely by the\n // connection type's matchAuth implementation.\n aws: {\n identityFromQuery() {\n return undefined;\n },\n },\n};\n"],"names":["InputError"],"mappings":";;;;AAoCO,MAAM,gBAAA,GAGT;AAAA,EACF,IAAA,EAAM;AAAA,IACJ,aAAA,EAAe,MAAA;AAAA,IACf,kBAAkB,KAAA,EAAO;AACvB,MAAA,MAAM,EAAE,KAAI,GAAI,KAAA;AAChB,MAAA,IAAI;AACF,QAAA,OAAO,IAAI,GAAA,CAAI,GAAG,CAAA,CAAE,IAAA;AAAA,MACtB,CAAA,CAAA,MAAQ;AACN,QAAA,MAAM,IAAIA,iBAAA;AAAA,UACR,gBAAgB,GAAG,CAAA,mCAAA;AAAA,SACrB;AAAA,MACF;AAAA,IACF;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAIA,GAAA,EAAK;AAAA,IACH,iBAAA,GAAoB;AAClB,MAAA,OAAO,MAAA;AAAA,IACT;AAAA;AAEJ;;"}
@@ -342,6 +342,7 @@ async function buildCloudSqlConfig(config) {
342
342
  }
343
343
  async function buildRdsPgConfig(config) {
344
344
  const { Signer } = require("@aws-sdk/rds-signer");
345
+ const { fromNodeProviderChain } = require("@aws-sdk/credential-providers");
345
346
  let hostname;
346
347
  let port;
347
348
  let username;
@@ -366,13 +367,28 @@ async function buildRdsPgConfig(config) {
366
367
  config.get("connection"),
367
368
  ["type", "region"]
368
369
  );
369
- const signer = new Signer({ hostname, port, username, region });
370
+ const credentialsProvider = fromNodeProviderChain();
370
371
  const tokenTtlMs = 15 * 60 * 1e3;
371
372
  const renewalOffsetMs = 60 * 1e3;
372
373
  async function getConnectionConfig() {
373
374
  try {
375
+ let credentials = await credentialsProvider();
376
+ if (credentials.expiration && credentials.expiration.getTime() <= Date.now() + renewalOffsetMs) {
377
+ credentials = await credentialsProvider({ forceRefresh: true });
378
+ }
379
+ const signer = new Signer({
380
+ hostname,
381
+ port,
382
+ username,
383
+ region,
384
+ credentials
385
+ });
386
+ const tokenIssuedAt = Date.now();
374
387
  const password = await signer.getAuthToken();
375
- const tokenExpiration = Date.now() + tokenTtlMs - renewalOffsetMs;
388
+ const tokenExpiration = Math.min(
389
+ tokenIssuedAt + tokenTtlMs,
390
+ credentials.expiration?.getTime() ?? Infinity
391
+ ) - renewalOffsetMs;
376
392
  return {
377
393
  ...sanitizedConnection,
378
394
  password,
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.cjs.js","sources":["../../../../src/entrypoints/database/connectors/postgres.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 { LifecycleService, LoggerService } from '@backstage/backend-plugin-api';\nimport {\n Config,\n ConfigReader,\n readDurationFromConfig,\n} from '@backstage/config';\nimport { ForwardedError } from '@backstage/errors';\nimport {\n durationToMilliseconds,\n HumanDuration,\n JsonObject,\n} from '@backstage/types';\nimport knexFactory, { Knex } from 'knex';\nimport { merge, omit } from 'lodash';\nimport limiterFactory from 'p-limit';\nimport { Client } from 'pg';\nimport { Connector } from '../types';\nimport { mergeDatabaseConfig } from './mergeDatabaseConfig';\nimport format from 'pg-format';\nimport { TokenCredential } from '@azure/identity';\n\n// Limits the number of concurrent DDL operations to 1\nconst ddlLimiter = limiterFactory(1);\n\nconst cloudSqlConnectorMaxAttempts = 3;\nconst cloudSqlConnectorInitialRetryDelayMs = 200;\nconst cloudSqlConnectorRetryableErrorCodes = new Set([\n 'EAI_AGAIN',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'EPIPE',\n 'ETIMEDOUT',\n 'UND_ERR_CONNECT_TIMEOUT',\n]);\n\nasync function retryWithExponentialBackoff<T>(\n operation: () => Promise<T>,\n options: {\n maxAttempts: number;\n initialDelayMs: number;\n shouldRetry: (error: unknown) => boolean;\n },\n): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n try {\n return await operation();\n } catch (error) {\n if (attempt >= options.maxAttempts || !options.shouldRetry(error)) {\n throw error;\n }\n const maxDelay = options.initialDelayMs * 2 ** (attempt - 1);\n const delay = Math.random() * maxDelay;\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n}\n\nfunction isCloudSqlConnectorRetryableError(error: unknown): boolean {\n let current = error;\n const seen = new Set<unknown>();\n\n while (current && typeof current === 'object' && !seen.has(current)) {\n seen.add(current);\n const errorRecord = current as Record<string, unknown>;\n const code = errorRecord.code;\n if (\n typeof code === 'string' &&\n cloudSqlConnectorRetryableErrorCodes.has(code)\n ) {\n return true;\n }\n\n let status = errorRecord.status;\n if (typeof status !== 'number') {\n status = errorRecord.statusCode;\n }\n if (typeof status !== 'number') {\n const response = errorRecord.response;\n status =\n response && typeof response === 'object'\n ? (response as Record<string, unknown>).status\n : undefined;\n }\n if (\n typeof status === 'number' &&\n (status === 408 || status === 429 || (status >= 500 && status <= 599))\n ) {\n return true;\n }\n\n current = errorRecord.cause;\n }\n\n return false;\n}\n\nexport class PgAdminPool {\n private client?: Promise<Knex>;\n private destruction?: Promise<void>;\n private readonly destructionErrors: unknown[] = [];\n private idleTimer?: NodeJS.Timeout;\n private readonly operations = new Set<Promise<unknown>>();\n private isShutdown = false;\n\n constructor(\n private readonly createClient: () => Promise<Knex>,\n private readonly idleTimeoutMillis: number,\n ) {}\n\n async run<T>(operation: (client: Knex) => Promise<T> | T): Promise<T> {\n if (this.isShutdown) {\n throw new Error('PostgreSQL admin pool is shut down');\n }\n\n clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n\n const operationPromise = this.runOperation(operation);\n this.operations.add(operationPromise);\n try {\n return await operationPromise;\n } finally {\n this.operations.delete(operationPromise);\n this.scheduleIdleDestroy();\n }\n }\n\n async shutdown(): Promise<void> {\n this.isShutdown = true;\n clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n\n await Promise.allSettled(this.operations);\n await this.destroyClient().catch(() => {});\n if (this.destructionErrors.length > 0) {\n throw new AggregateError(\n this.destructionErrors,\n 'Failed to destroy PostgreSQL admin pool clients',\n );\n }\n }\n\n async invalidate(): Promise<void> {\n clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n\n await Promise.allSettled(this.operations);\n await this.destroyClient();\n }\n\n private async runOperation<T>(\n operation: (client: Knex) => Promise<T> | T,\n ): Promise<T> {\n if (this.destruction) {\n await this.destruction;\n }\n if (this.isShutdown) {\n throw new Error('PostgreSQL admin pool is shut down');\n }\n\n let clientPromise = this.client;\n if (!clientPromise) {\n clientPromise = this.createClient();\n this.client = clientPromise;\n void clientPromise.catch(() => {\n if (this.client === clientPromise) {\n this.client = undefined;\n }\n });\n }\n return operation(await clientPromise);\n }\n\n private scheduleIdleDestroy(): void {\n if (this.isShutdown || this.operations.size > 0) {\n return;\n }\n\n const timer = setTimeout(() => {\n if (this.idleTimer === timer) {\n this.idleTimer = undefined;\n void this.destroyClient().catch(() => {});\n }\n }, this.idleTimeoutMillis);\n timer.unref();\n this.idleTimer = timer;\n }\n\n private destroyClient(): Promise<void> {\n if (this.destruction) {\n return this.destruction;\n }\n\n const clientPromise = this.client;\n this.client = undefined;\n if (!clientPromise) {\n return Promise.resolve();\n }\n\n const destruction = clientPromise.then(\n client => client.destroy(),\n () => undefined,\n );\n this.destruction = destruction;\n void destruction.then(\n () => {\n if (this.destruction === destruction) {\n this.destruction = undefined;\n }\n },\n error => {\n if (this.destruction === destruction) {\n this.destruction = undefined;\n this.destructionErrors.push(error);\n }\n },\n );\n return destruction;\n }\n}\n\n/**\n * Creates a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function createPgDatabaseClient(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const knexConfig = await buildPgDatabaseConfig(dbConfig, overrides);\n const database = knexFactory(knexConfig);\n\n const role = dbConfig.getOptionalString('role');\n\n if (role) {\n database.client.pool.on(\n 'createSuccess',\n async (_event: number, pgClient: Client) => {\n const query = format('SET ROLE %I', role);\n await pgClient.query(query);\n },\n );\n }\n return database;\n}\n\n/**\n * Builds a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function buildPgDatabaseConfig(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const config = mergeDatabaseConfig(\n { pool: { min: 0 } },\n dbConfig.get(),\n {\n connection: getPgConnectionConfig(dbConfig, !!overrides),\n useNullAsDefault: true,\n },\n overrides,\n );\n const mergedConfigReader = new ConfigReader(config);\n\n if (config.connection.type === 'default' || !config.connection.type) {\n const connectionValue = config.connection;\n const sanitizedConnection =\n typeof connectionValue === 'string' || connectionValue instanceof String\n ? connectionValue\n : // connection is an object, omit config-only props\n omit(connectionValue as Record<string, unknown>, [\n 'type',\n 'instance',\n 'tokenCredential',\n ]);\n\n return {\n ...config,\n connection: sanitizedConnection,\n };\n }\n\n switch (config.connection.type) {\n case 'azure':\n return buildAzurePgConfig(mergedConfigReader);\n case 'cloudsql':\n return buildCloudSqlConfig(mergedConfigReader);\n case 'rds':\n return buildRdsPgConfig(mergedConfigReader);\n default:\n throw new Error(`Unknown connection type: ${config.connection.type}`);\n }\n}\n\n/* Note: the following type definition is intentionally duplicated in\n * /packages/backend-defaults/config.d.ts so the clientSecret property\n * can be annotated with \"@visibility secret\" there.\n */\nexport type AzureTokenCredentialConfig = {\n /**\n * How early before an access token expires to refresh it with a new one.\n * Defaults to 5 minutes\n * Supported formats:\n * - A string in the format of '1d', '2 seconds' etc. as supported by the `ms`\n * library.\n * - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M'.\n * - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }`.\n */\n tokenRenewableOffsetTime?: string | HumanDuration;\n /**\n * The client ID of a user-assigned managed identity.\n * If not provided, the system-assigned managed identity is used.\n */\n clientId?: string;\n clientSecret?: string;\n tenantId?: string;\n};\n\nexport async function buildAzurePgConfig(config: Config): Promise<Knex.Config> {\n const {\n DefaultAzureCredential,\n ManagedIdentityCredential,\n ClientSecretCredential,\n } = require('@azure/identity');\n\n const tokenConfig = config.getOptionalConfig('connection.tokenCredential');\n\n const tokenRenewableOffsetTime = durationToMilliseconds(\n tokenConfig?.has('tokenRenewableOffsetTime')\n ? readDurationFromConfig(tokenConfig, { key: 'tokenRenewableOffsetTime' })\n : { minutes: 5 },\n );\n\n const clientId = tokenConfig?.getOptionalString('clientId');\n const tenantId = tokenConfig?.getOptionalString('tenantId');\n const clientSecret = tokenConfig?.getOptionalString('clientSecret');\n let credential: TokenCredential;\n\n /**\n * Determine which TokenCredential to use based on provided config\n * 1. If clientId, tenantId and clientSecret are provided, use ClientSecretCredential\n * 2. If only clientId is provided, use ManagedIdentityCredential with user-assigned identity\n * 3. Otherwise, use DefaultAzureCredential (which may use system-assigned identity among other methods)\n */\n if (clientId && tenantId && clientSecret) {\n credential = new ClientSecretCredential(tenantId, clientId, clientSecret);\n } else if (clientId) {\n credential = new ManagedIdentityCredential(clientId);\n } else {\n credential = new DefaultAzureCredential();\n }\n\n const rawConfig = config.get() as Record<string, unknown>;\n\n const normalized = normalizeConnection(rawConfig.connection as any);\n const sanitizedConnection = omit(normalized, [\n 'type',\n 'instance',\n 'tokenCredential',\n ]) as Partial<Knex.StaticConnectionConfig>;\n\n async function getConnectionConfig() {\n const token = await credential.getToken(\n 'https://ossrdbms-aad.database.windows.net/.default',\n );\n\n if (!token) {\n throw new Error(\n 'Failed to acquire Azure access token for database authentication',\n );\n }\n\n const connectionConfig = {\n ...sanitizedConnection,\n password: token.token,\n expirationChecker: () =>\n /* return true if the token is within the renewable offset time */\n token.expiresOnTimestamp - tokenRenewableOffsetTime <= Date.now(),\n };\n\n return connectionConfig;\n }\n\n return {\n ...(rawConfig as Record<string, unknown>),\n connection: getConnectionConfig,\n };\n}\n\n/**\n * Builds a PostgreSQL configuration for Cloud SQL.\n *\n * Transient failures during connector initialization are retried up to three\n * times with jittered exponential backoff. Configuration and authorization\n * failures are returned immediately.\n */\nexport async function buildCloudSqlConfig(\n config: Config,\n): Promise<Knex.Config> {\n const client = config.getOptionalString('client');\n\n if (client && client !== 'pg') {\n throw new Error('Cloud SQL only supports the pg client');\n }\n\n const instance = config.getOptionalString('connection.instance');\n if (!instance) {\n throw new Error('Missing instance connection name for Cloud SQL');\n }\n\n const {\n Connector: CloudSqlConnector,\n IpAddressTypes,\n AuthTypes,\n } = require('@google-cloud/cloud-sql-connector') as typeof import('@google-cloud/cloud-sql-connector');\n\n type IpType = (typeof IpAddressTypes)[keyof typeof IpAddressTypes];\n const ipTypeRaw = config.getOptionalString('connection.ipAddressType');\n\n let ipType: IpType | undefined;\n if (ipTypeRaw !== undefined) {\n if (\n !(Object.values(IpAddressTypes) as Array<string | number>).includes(\n ipTypeRaw as any,\n )\n ) {\n throw new Error(\n `Invalid connection.ipAddressType: ${ipTypeRaw}; valid values: ${Object.values(\n IpAddressTypes,\n ).join(', ')}`,\n );\n }\n ipType = ipTypeRaw as unknown as IpType;\n }\n\n /**\n * Retry only transient connector initialization failures. A fresh connector\n * is required because a failed initialization may be cached by the client.\n */\n const clientOpts = await retryWithExponentialBackoff(\n async () => {\n const connector = new CloudSqlConnector();\n return connector.getOptions({\n instanceConnectionName: instance,\n ipType: ipType ?? IpAddressTypes.PUBLIC,\n authType: AuthTypes.IAM,\n });\n },\n {\n maxAttempts: cloudSqlConnectorMaxAttempts,\n initialDelayMs: cloudSqlConnectorInitialRetryDelayMs,\n shouldRetry: isCloudSqlConnectorRetryableError,\n },\n );\n\n const rawConfig = config.get() as Record<string, unknown>;\n const normalized = normalizeConnection(rawConfig.connection as any);\n const sanitizedConnection = omit(normalized, [\n 'type',\n 'instance',\n ]) as Partial<Knex.StaticConnectionConfig>;\n\n return {\n ...(rawConfig as Record<string, unknown>),\n client: 'pg',\n connection: {\n ...sanitizedConnection,\n ...clientOpts,\n },\n };\n}\n\nexport async function buildRdsPgConfig(config: Config): Promise<Knex.Config> {\n const { Signer } =\n require('@aws-sdk/rds-signer') as typeof import('@aws-sdk/rds-signer');\n\n let hostname: string;\n let port: number;\n let username: string;\n try {\n hostname = config.getString('connection.host');\n port = config.getNumber('connection.port');\n username = config.getString('connection.user');\n } catch (err) {\n throw new ForwardedError(\n 'AWS RDS IAM auth: missing required database connection config — make sure connection.host, connection.port, and connection.user are set and any environment variables they reference are set',\n err,\n );\n }\n const region =\n config.getOptionalString('connection.region') ??\n process.env.AWS_REGION ??\n process.env.AWS_DEFAULT_REGION;\n if (!region) {\n throw new Error(\n 'Missing region for AWS RDS IAM auth: set connection.region or the AWS_REGION environment variable',\n );\n }\n\n const rawConfig = config.get() as Record<string, unknown>;\n const sanitizedConnection = omit(\n config.get('connection') as Record<string, unknown>,\n ['type', 'region'],\n ) as Partial<Knex.StaticConnectionConfig>;\n\n const signer = new Signer({ hostname, port, username, region });\n\n // RDS IAM auth tokens are valid for 15 minutes. Renew 1 minute early so\n // that pooled connections are refreshed before the token actually expires.\n const tokenTtlMs = 15 * 60 * 1000;\n const renewalOffsetMs = 60 * 1000;\n\n async function getConnectionConfig() {\n try {\n const password = await signer.getAuthToken();\n const tokenExpiration = Date.now() + tokenTtlMs - renewalOffsetMs;\n return {\n ...sanitizedConnection,\n password,\n expirationChecker: () => tokenExpiration <= Date.now(),\n };\n } catch (err) {\n throw new ForwardedError(\n `AWS RDS IAM auth token acquisition failed for ${username}@${hostname}:${port}`,\n err,\n );\n }\n }\n\n return {\n ...(rawConfig as Record<string, unknown>),\n connection: getConnectionConfig,\n };\n}\n\n/**\n * Gets the postgres connection config\n *\n * @param dbConfig - The database config\n * @param parseConnectionString - Flag to explicitly control connection string parsing\n */\nexport function getPgConnectionConfig(\n dbConfig: Config,\n parseConnectionString?: boolean,\n): Knex.PgConnectionConfig | string {\n const connection = dbConfig.get('connection') as any;\n const isConnectionString =\n typeof connection === 'string' || connection instanceof String;\n const autoParse = typeof parseConnectionString !== 'boolean';\n\n const shouldParseConnectionString = autoParse\n ? isConnectionString\n : parseConnectionString && isConnectionString;\n\n return shouldParseConnectionString\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\n/**\n * Parses a connection string using pg-connection-string\n *\n * @param connectionString - The postgres connection string\n */\nexport function parsePgConnectionString(connectionString: string) {\n const parse = requirePgConnectionString();\n return parse(connectionString);\n}\n\nfunction requirePgConnectionString() {\n try {\n return require('pg-connection-string').parse;\n } catch (e) {\n throw new ForwardedError(\"Postgres: Install 'pg-connection-string'\", e);\n }\n}\n\nconst PG_DATABASE_ADMIN_OVERRIDES: Knex.Config = {\n connection: { database: 'postgres' },\n pool: { min: 0, max: 1, acquireTimeoutMillis: 10000 },\n};\n\nconst PG_SCHEMA_ADMIN_OVERRIDES: Knex.Config = {\n pool: { min: 0, max: 1, acquireTimeoutMillis: 10000 },\n};\n\n/**\n * Creates the missing Postgres database if it does not exist\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to create\n */\nexport async function ensurePgDatabaseExists(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n await Promise.all(\n databases.map(async database => {\n return retryPgAdminOperation(() =>\n ddlLimiter(async () => {\n const admin = await createPgDatabaseClient(\n dbConfig,\n PG_DATABASE_ADMIN_OVERRIDES,\n );\n try {\n await ensurePgDatabase(admin, database);\n } finally {\n await admin.destroy();\n }\n }),\n );\n }),\n );\n}\n\nasync function ensurePgDatabase(admin: Knex, database: string): Promise<void> {\n const result = await admin\n .from('pg_database')\n .where('datname', database)\n .count<Record<string, { count: string }>>();\n\n if (parseInt(result[0].count, 10) === 0) {\n await admin.raw(`CREATE DATABASE ??`, [database]);\n }\n}\n\nasync function retryPgAdminOperation(\n operation: () => Promise<void>,\n): Promise<void> {\n // For initial setup we use a smaller timeout but several retries. Given that this\n // is a separate connection pool we should never really run into issues with connection\n // acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.\n const maxAttempts = 3;\n for (let attempt = 1; ; attempt++) {\n try {\n return await operation();\n } catch (error) {\n if (attempt >= maxAttempts) {\n throw error;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n }\n}\n\nasync function ensurePgSchema(\n admin: Knex,\n role: string | undefined,\n schema: string,\n): Promise<void> {\n if (role) {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ?? AUTHORIZATION ??`, [\n schema,\n role,\n ]);\n } else {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [schema]);\n }\n}\n\n/**\n * Creates the missing Postgres schema if it does not exist\n *\n * @param dbConfig - The database config\n * @param schemas - The name of the schemas to create\n */\nexport async function ensurePgSchemaExists(\n dbConfig: Config,\n ...schemas: Array<string>\n): Promise<void> {\n const admin = await createPgDatabaseClient(dbConfig);\n const role = dbConfig.getOptionalString('role');\n\n try {\n await Promise.all(\n schemas.map(schema =>\n ddlLimiter(() => ensurePgSchema(admin, role, schema)),\n ),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Drops the Postgres databases.\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to drop\n */\nexport async function dropPgDatabase(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = await createPgDatabaseClient(dbConfig);\n try {\n await Promise.all(\n databases.map(async database => {\n await ddlLimiter(() => admin.raw(`DROP DATABASE ??`, [database]));\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\nfunction normalizeConnection(\n connection: Knex.StaticConnectionConfig | JsonObject | string | undefined,\n): Partial<Knex.StaticConnectionConfig> {\n if (typeof connection === 'undefined' || connection === null) {\n return {};\n }\n\n return typeof connection === 'string' || connection instanceof String\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\n/**\n * The computed configuration for a plugin's postgres database connection.\n */\nexport interface PgPluginDatabaseConfig {\n /** The database client type (e.g. 'pg') */\n client: string;\n /** Whether the client type was overridden at the plugin level */\n clientOverridden: boolean;\n /** The optional role to set on connections */\n role: string | undefined;\n /** Additional knex configuration merged from base and plugin config */\n additionalKnexConfig: JsonObject | undefined;\n /** Whether to ensure the database exists */\n ensureExists: boolean;\n /** Whether to ensure the schema exists */\n ensureSchemaExists: boolean;\n /** The plugin division mode ('database' or 'schema') */\n pluginDivisionMode: string;\n /** The connection configuration */\n connection: Knex.PgConnectionConfig;\n /** The database name, if any */\n databaseName: string | undefined;\n /** The schema name, if using schema division mode */\n schemaName: string | undefined;\n /** Database client overrides including schema overrides if applicable */\n databaseClientOverrides: Knex.Config;\n /** The full knex config for the plugin */\n knexConfig: Knex.Config;\n}\n\n/**\n * Computes all postgres database configuration for a plugin from the provided config.\n *\n * @param config - The database config object\n * @param pluginId - The plugin ID to compute config for\n * @param prefix - The database name prefix (e.g. 'backstage_plugin_')\n * @returns All computed configuration values for the plugin\n */\nexport function computePgPluginConfig(\n config: Config,\n pluginId: string,\n prefix: string,\n schemaPrefix: string = '',\n): PgPluginDatabaseConfig {\n // Client type\n const pluginClient = config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n const baseClient = config.getString('client');\n const client = pluginClient ?? baseClient;\n const clientOverridden = client !== baseClient;\n\n // Role\n const role =\n config.getOptionalString(`${pluginPath(pluginId)}.role`) ??\n config.getOptionalString('role');\n\n // Additional knex config\n const pluginKnexConfig = config\n .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`)\n ?.get<JsonObject>();\n const baseKnexConfig = config\n .getOptionalConfig('knexConfig')\n ?.get<JsonObject>();\n const additionalKnexConfig = merge(baseKnexConfig, pluginKnexConfig);\n\n // Ensure exists flags\n const baseEnsureExists = config.getOptionalBoolean('ensureExists') ?? true;\n const ensureExists =\n config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ??\n baseEnsureExists;\n\n const baseEnsureSchemaExists =\n config.getOptionalBoolean('ensureSchemaExists') ?? false;\n const ensureSchemaExists =\n config.getOptionalBoolean(\n `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`,\n ) ?? baseEnsureSchemaExists;\n\n // Plugin division mode\n const pluginDivisionMode =\n config.getOptionalString('pluginDivisionMode') ?? 'database';\n\n // Connection config\n let baseConnection = normalizeConnection(config.get('connection'));\n\n // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`.\n // The `database` property from the base connection is omitted unless\n // `pluginDivisionMode` is set to `schema`.\n if (pluginDivisionMode !== 'schema') {\n baseConnection = omit(baseConnection, 'database');\n }\n\n // Get and normalize optional plugin specific database connection\n const pluginConnection = normalizeConnection(\n config.getOptional(`${pluginPath(pluginId)}.connection`),\n );\n\n (\n baseConnection as Knex.PgConnectionConfig\n ).application_name ||= `backstage_plugin_${pluginId}`;\n\n const connection = {\n // Include base connection if client type has not been overridden\n ...(clientOverridden ? {} : baseConnection),\n ...pluginConnection,\n } as Knex.PgConnectionConfig;\n\n // Database name\n const connectionDatabaseName = (connection as Knex.ConnectionConfig)\n ?.database;\n let databaseName: string | undefined;\n\n if (pluginDivisionMode === 'schema') {\n // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied\n // or fallback to default knex database\n databaseName = connectionDatabaseName;\n } else {\n // All other supported databases should fallback to an auto-prefixed name\n databaseName = connectionDatabaseName ?? `${prefix}${pluginId}`;\n }\n\n // Database client overrides\n let databaseClientOverrides: Knex.Config = {};\n let schemaName: string | undefined;\n\n if (databaseName) {\n databaseClientOverrides = { connection: { database: databaseName } };\n }\n if (pluginDivisionMode === 'schema') {\n schemaName = `${schemaPrefix}${pluginId}`;\n\n if (Buffer.byteLength(schemaName, 'utf8') > 63) {\n throw new Error(\n `PostgreSQL schema name \"${schemaName}\" exceeds the 63-byte limit. ` +\n `Consider using a shorter schemaPrefix (current: \"${schemaPrefix}\") or ` +\n `plugin ID (current: \"${pluginId}\").`,\n );\n }\n\n databaseClientOverrides = mergeDatabaseConfig({}, databaseClientOverrides, {\n searchPath: [schemaName],\n });\n }\n\n // Full knex config for plugin\n const knexConfig: Knex.Config = {\n ...additionalKnexConfig,\n client,\n connection,\n ...(role && { role }),\n };\n\n return {\n client,\n clientOverridden,\n role,\n additionalKnexConfig,\n ensureExists,\n ensureSchemaExists,\n pluginDivisionMode,\n connection,\n databaseName,\n schemaName,\n databaseClientOverrides,\n knexConfig,\n };\n}\n\nexport class PgConnector implements Connector {\n private readonly config: Config;\n private readonly prefix: string;\n private readonly schemaPrefix: string;\n private readonly databaseEnsureCache = new Map<string, Promise<void>>();\n private readonly customEnsureDatabaseExists?: typeof ensurePgDatabaseExists;\n private readonly databaseAdminPool: PgAdminPool;\n private readonly schemaAdminPool: PgAdminPool;\n\n constructor(\n config: Config,\n prefix: string,\n schemaPrefix: string,\n options: {\n ensureDatabaseExists?: typeof ensurePgDatabaseExists;\n createAdminClient?: (overrides: Knex.Config) => Promise<Knex>;\n adminPoolIdleTimeoutMillis?: number;\n } = {},\n ) {\n this.config = config;\n this.prefix = prefix;\n this.schemaPrefix = schemaPrefix;\n this.customEnsureDatabaseExists = options.ensureDatabaseExists;\n\n const createAdminClient =\n options.createAdminClient ??\n ((overrides: Knex.Config) =>\n createPgDatabaseClient(this.config, overrides));\n const idleTimeoutMillis = options.adminPoolIdleTimeoutMillis ?? 10_000;\n this.databaseAdminPool = new PgAdminPool(\n () => createAdminClient(PG_DATABASE_ADMIN_OVERRIDES),\n idleTimeoutMillis,\n );\n this.schemaAdminPool = new PgAdminPool(\n () => createAdminClient(PG_SCHEMA_ADMIN_OVERRIDES),\n idleTimeoutMillis,\n );\n }\n\n private async ensureDatabase(databaseName: string): Promise<void> {\n let promise = this.databaseEnsureCache.get(databaseName);\n if (!promise) {\n promise = this.customEnsureDatabaseExists\n ? this.customEnsureDatabaseExists(this.config, databaseName)\n : retryPgAdminOperation(() =>\n ddlLimiter(() =>\n this.runAdminOperation(this.databaseAdminPool, admin =>\n ensurePgDatabase(admin, databaseName),\n ),\n ),\n );\n this.databaseEnsureCache.set(databaseName, promise);\n }\n\n try {\n await promise;\n } catch (error) {\n if (this.databaseEnsureCache.get(databaseName) === promise) {\n this.databaseEnsureCache.delete(databaseName);\n }\n throw error;\n }\n }\n\n private async runAdminOperation<T>(\n pool: PgAdminPool,\n operation: (client: Knex) => Promise<T>,\n ): Promise<T> {\n try {\n return await pool.run(operation);\n } catch (error) {\n await pool.invalidate().catch(() => {});\n throw error;\n }\n }\n\n async shutdown(): Promise<void> {\n const results = await Promise.allSettled([\n this.databaseAdminPool.shutdown(),\n this.schemaAdminPool.shutdown(),\n ]);\n const errors = results\n .filter(\n (result): result is PromiseRejectedResult =>\n result.status === 'rejected',\n )\n .map(result => result.reason);\n if (errors.length === 1) {\n throw errors[0];\n }\n if (errors.length > 1) {\n throw new AggregateError(\n errors,\n 'Failed to shut down PostgreSQL admin pools',\n );\n }\n }\n\n async getClient(\n pluginId: string,\n _deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n const pluginDbConfig = computePgPluginConfig(\n this.config,\n pluginId,\n this.prefix,\n this.schemaPrefix,\n );\n\n if (pluginDbConfig.databaseName && pluginDbConfig.ensureExists) {\n try {\n await this.ensureDatabase(pluginDbConfig.databaseName);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that '${pluginDbConfig.databaseName}' exists, ${error}`,\n );\n }\n }\n\n if (pluginDbConfig.pluginDivisionMode === 'schema') {\n if (pluginDbConfig.ensureSchemaExists || pluginDbConfig.ensureExists) {\n try {\n await ddlLimiter(() =>\n this.runAdminOperation(this.schemaAdminPool, admin =>\n ensurePgSchema(\n admin,\n this.config.getOptionalString('role'),\n pluginDbConfig.schemaName!,\n ),\n ),\n );\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`,\n );\n }\n }\n }\n\n const client = createPgDatabaseClient(\n this.config,\n mergeDatabaseConfig(\n pluginDbConfig.knexConfig,\n pluginDbConfig.databaseClientOverrides,\n ),\n );\n\n return client;\n }\n}\n"],"names":["limiterFactory","knexFactory","format","config","mergeDatabaseConfig","ConfigReader","omit","durationToMilliseconds","readDurationFromConfig","ForwardedError","merge"],"mappings":"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"}
1
+ {"version":3,"file":"postgres.cjs.js","sources":["../../../../src/entrypoints/database/connectors/postgres.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 { LifecycleService, LoggerService } from '@backstage/backend-plugin-api';\nimport {\n Config,\n ConfigReader,\n readDurationFromConfig,\n} from '@backstage/config';\nimport { ForwardedError } from '@backstage/errors';\nimport {\n durationToMilliseconds,\n HumanDuration,\n JsonObject,\n} from '@backstage/types';\nimport knexFactory, { Knex } from 'knex';\nimport { merge, omit } from 'lodash';\nimport limiterFactory from 'p-limit';\nimport { Client } from 'pg';\nimport { Connector } from '../types';\nimport { mergeDatabaseConfig } from './mergeDatabaseConfig';\nimport format from 'pg-format';\nimport { TokenCredential } from '@azure/identity';\n\n// Limits the number of concurrent DDL operations to 1\nconst ddlLimiter = limiterFactory(1);\n\nconst cloudSqlConnectorMaxAttempts = 3;\nconst cloudSqlConnectorInitialRetryDelayMs = 200;\nconst cloudSqlConnectorRetryableErrorCodes = new Set([\n 'EAI_AGAIN',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'EPIPE',\n 'ETIMEDOUT',\n 'UND_ERR_CONNECT_TIMEOUT',\n]);\n\nasync function retryWithExponentialBackoff<T>(\n operation: () => Promise<T>,\n options: {\n maxAttempts: number;\n initialDelayMs: number;\n shouldRetry: (error: unknown) => boolean;\n },\n): Promise<T> {\n for (let attempt = 1; ; attempt++) {\n try {\n return await operation();\n } catch (error) {\n if (attempt >= options.maxAttempts || !options.shouldRetry(error)) {\n throw error;\n }\n const maxDelay = options.initialDelayMs * 2 ** (attempt - 1);\n const delay = Math.random() * maxDelay;\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n}\n\nfunction isCloudSqlConnectorRetryableError(error: unknown): boolean {\n let current = error;\n const seen = new Set<unknown>();\n\n while (current && typeof current === 'object' && !seen.has(current)) {\n seen.add(current);\n const errorRecord = current as Record<string, unknown>;\n const code = errorRecord.code;\n if (\n typeof code === 'string' &&\n cloudSqlConnectorRetryableErrorCodes.has(code)\n ) {\n return true;\n }\n\n let status = errorRecord.status;\n if (typeof status !== 'number') {\n status = errorRecord.statusCode;\n }\n if (typeof status !== 'number') {\n const response = errorRecord.response;\n status =\n response && typeof response === 'object'\n ? (response as Record<string, unknown>).status\n : undefined;\n }\n if (\n typeof status === 'number' &&\n (status === 408 || status === 429 || (status >= 500 && status <= 599))\n ) {\n return true;\n }\n\n current = errorRecord.cause;\n }\n\n return false;\n}\n\nexport class PgAdminPool {\n private client?: Promise<Knex>;\n private destruction?: Promise<void>;\n private readonly destructionErrors: unknown[] = [];\n private idleTimer?: NodeJS.Timeout;\n private readonly operations = new Set<Promise<unknown>>();\n private isShutdown = false;\n\n constructor(\n private readonly createClient: () => Promise<Knex>,\n private readonly idleTimeoutMillis: number,\n ) {}\n\n async run<T>(operation: (client: Knex) => Promise<T> | T): Promise<T> {\n if (this.isShutdown) {\n throw new Error('PostgreSQL admin pool is shut down');\n }\n\n clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n\n const operationPromise = this.runOperation(operation);\n this.operations.add(operationPromise);\n try {\n return await operationPromise;\n } finally {\n this.operations.delete(operationPromise);\n this.scheduleIdleDestroy();\n }\n }\n\n async shutdown(): Promise<void> {\n this.isShutdown = true;\n clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n\n await Promise.allSettled(this.operations);\n await this.destroyClient().catch(() => {});\n if (this.destructionErrors.length > 0) {\n throw new AggregateError(\n this.destructionErrors,\n 'Failed to destroy PostgreSQL admin pool clients',\n );\n }\n }\n\n async invalidate(): Promise<void> {\n clearTimeout(this.idleTimer);\n this.idleTimer = undefined;\n\n await Promise.allSettled(this.operations);\n await this.destroyClient();\n }\n\n private async runOperation<T>(\n operation: (client: Knex) => Promise<T> | T,\n ): Promise<T> {\n if (this.destruction) {\n await this.destruction;\n }\n if (this.isShutdown) {\n throw new Error('PostgreSQL admin pool is shut down');\n }\n\n let clientPromise = this.client;\n if (!clientPromise) {\n clientPromise = this.createClient();\n this.client = clientPromise;\n void clientPromise.catch(() => {\n if (this.client === clientPromise) {\n this.client = undefined;\n }\n });\n }\n return operation(await clientPromise);\n }\n\n private scheduleIdleDestroy(): void {\n if (this.isShutdown || this.operations.size > 0) {\n return;\n }\n\n const timer = setTimeout(() => {\n if (this.idleTimer === timer) {\n this.idleTimer = undefined;\n void this.destroyClient().catch(() => {});\n }\n }, this.idleTimeoutMillis);\n timer.unref();\n this.idleTimer = timer;\n }\n\n private destroyClient(): Promise<void> {\n if (this.destruction) {\n return this.destruction;\n }\n\n const clientPromise = this.client;\n this.client = undefined;\n if (!clientPromise) {\n return Promise.resolve();\n }\n\n const destruction = clientPromise.then(\n client => client.destroy(),\n () => undefined,\n );\n this.destruction = destruction;\n void destruction.then(\n () => {\n if (this.destruction === destruction) {\n this.destruction = undefined;\n }\n },\n error => {\n if (this.destruction === destruction) {\n this.destruction = undefined;\n this.destructionErrors.push(error);\n }\n },\n );\n return destruction;\n }\n}\n\n/**\n * Creates a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function createPgDatabaseClient(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const knexConfig = await buildPgDatabaseConfig(dbConfig, overrides);\n const database = knexFactory(knexConfig);\n\n const role = dbConfig.getOptionalString('role');\n\n if (role) {\n database.client.pool.on(\n 'createSuccess',\n async (_event: number, pgClient: Client) => {\n const query = format('SET ROLE %I', role);\n await pgClient.query(query);\n },\n );\n }\n return database;\n}\n\n/**\n * Builds a knex postgres database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport async function buildPgDatabaseConfig(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const config = mergeDatabaseConfig(\n { pool: { min: 0 } },\n dbConfig.get(),\n {\n connection: getPgConnectionConfig(dbConfig, !!overrides),\n useNullAsDefault: true,\n },\n overrides,\n );\n const mergedConfigReader = new ConfigReader(config);\n\n if (config.connection.type === 'default' || !config.connection.type) {\n const connectionValue = config.connection;\n const sanitizedConnection =\n typeof connectionValue === 'string' || connectionValue instanceof String\n ? connectionValue\n : // connection is an object, omit config-only props\n omit(connectionValue as Record<string, unknown>, [\n 'type',\n 'instance',\n 'tokenCredential',\n ]);\n\n return {\n ...config,\n connection: sanitizedConnection,\n };\n }\n\n switch (config.connection.type) {\n case 'azure':\n return buildAzurePgConfig(mergedConfigReader);\n case 'cloudsql':\n return buildCloudSqlConfig(mergedConfigReader);\n case 'rds':\n return buildRdsPgConfig(mergedConfigReader);\n default:\n throw new Error(`Unknown connection type: ${config.connection.type}`);\n }\n}\n\n/* Note: the following type definition is intentionally duplicated in\n * /packages/backend-defaults/config.d.ts so the clientSecret property\n * can be annotated with \"@visibility secret\" there.\n */\nexport type AzureTokenCredentialConfig = {\n /**\n * How early before an access token expires to refresh it with a new one.\n * Defaults to 5 minutes\n * Supported formats:\n * - A string in the format of '1d', '2 seconds' etc. as supported by the `ms`\n * library.\n * - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M'.\n * - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }`.\n */\n tokenRenewableOffsetTime?: string | HumanDuration;\n /**\n * The client ID of a user-assigned managed identity.\n * If not provided, the system-assigned managed identity is used.\n */\n clientId?: string;\n clientSecret?: string;\n tenantId?: string;\n};\n\nexport async function buildAzurePgConfig(config: Config): Promise<Knex.Config> {\n const {\n DefaultAzureCredential,\n ManagedIdentityCredential,\n ClientSecretCredential,\n } = require('@azure/identity');\n\n const tokenConfig = config.getOptionalConfig('connection.tokenCredential');\n\n const tokenRenewableOffsetTime = durationToMilliseconds(\n tokenConfig?.has('tokenRenewableOffsetTime')\n ? readDurationFromConfig(tokenConfig, { key: 'tokenRenewableOffsetTime' })\n : { minutes: 5 },\n );\n\n const clientId = tokenConfig?.getOptionalString('clientId');\n const tenantId = tokenConfig?.getOptionalString('tenantId');\n const clientSecret = tokenConfig?.getOptionalString('clientSecret');\n let credential: TokenCredential;\n\n /**\n * Determine which TokenCredential to use based on provided config\n * 1. If clientId, tenantId and clientSecret are provided, use ClientSecretCredential\n * 2. If only clientId is provided, use ManagedIdentityCredential with user-assigned identity\n * 3. Otherwise, use DefaultAzureCredential (which may use system-assigned identity among other methods)\n */\n if (clientId && tenantId && clientSecret) {\n credential = new ClientSecretCredential(tenantId, clientId, clientSecret);\n } else if (clientId) {\n credential = new ManagedIdentityCredential(clientId);\n } else {\n credential = new DefaultAzureCredential();\n }\n\n const rawConfig = config.get() as Record<string, unknown>;\n\n const normalized = normalizeConnection(rawConfig.connection as any);\n const sanitizedConnection = omit(normalized, [\n 'type',\n 'instance',\n 'tokenCredential',\n ]) as Partial<Knex.StaticConnectionConfig>;\n\n async function getConnectionConfig() {\n const token = await credential.getToken(\n 'https://ossrdbms-aad.database.windows.net/.default',\n );\n\n if (!token) {\n throw new Error(\n 'Failed to acquire Azure access token for database authentication',\n );\n }\n\n const connectionConfig = {\n ...sanitizedConnection,\n password: token.token,\n expirationChecker: () =>\n /* return true if the token is within the renewable offset time */\n token.expiresOnTimestamp - tokenRenewableOffsetTime <= Date.now(),\n };\n\n return connectionConfig;\n }\n\n return {\n ...(rawConfig as Record<string, unknown>),\n connection: getConnectionConfig,\n };\n}\n\n/**\n * Builds a PostgreSQL configuration for Cloud SQL.\n *\n * Transient failures during connector initialization are retried up to three\n * times with jittered exponential backoff. Configuration and authorization\n * failures are returned immediately.\n */\nexport async function buildCloudSqlConfig(\n config: Config,\n): Promise<Knex.Config> {\n const client = config.getOptionalString('client');\n\n if (client && client !== 'pg') {\n throw new Error('Cloud SQL only supports the pg client');\n }\n\n const instance = config.getOptionalString('connection.instance');\n if (!instance) {\n throw new Error('Missing instance connection name for Cloud SQL');\n }\n\n const {\n Connector: CloudSqlConnector,\n IpAddressTypes,\n AuthTypes,\n } = require('@google-cloud/cloud-sql-connector') as typeof import('@google-cloud/cloud-sql-connector');\n\n type IpType = (typeof IpAddressTypes)[keyof typeof IpAddressTypes];\n const ipTypeRaw = config.getOptionalString('connection.ipAddressType');\n\n let ipType: IpType | undefined;\n if (ipTypeRaw !== undefined) {\n if (\n !(Object.values(IpAddressTypes) as Array<string | number>).includes(\n ipTypeRaw as any,\n )\n ) {\n throw new Error(\n `Invalid connection.ipAddressType: ${ipTypeRaw}; valid values: ${Object.values(\n IpAddressTypes,\n ).join(', ')}`,\n );\n }\n ipType = ipTypeRaw as unknown as IpType;\n }\n\n /**\n * Retry only transient connector initialization failures. A fresh connector\n * is required because a failed initialization may be cached by the client.\n */\n const clientOpts = await retryWithExponentialBackoff(\n async () => {\n const connector = new CloudSqlConnector();\n return connector.getOptions({\n instanceConnectionName: instance,\n ipType: ipType ?? IpAddressTypes.PUBLIC,\n authType: AuthTypes.IAM,\n });\n },\n {\n maxAttempts: cloudSqlConnectorMaxAttempts,\n initialDelayMs: cloudSqlConnectorInitialRetryDelayMs,\n shouldRetry: isCloudSqlConnectorRetryableError,\n },\n );\n\n const rawConfig = config.get() as Record<string, unknown>;\n const normalized = normalizeConnection(rawConfig.connection as any);\n const sanitizedConnection = omit(normalized, [\n 'type',\n 'instance',\n ]) as Partial<Knex.StaticConnectionConfig>;\n\n return {\n ...(rawConfig as Record<string, unknown>),\n client: 'pg',\n connection: {\n ...sanitizedConnection,\n ...clientOpts,\n },\n };\n}\n\nexport async function buildRdsPgConfig(config: Config): Promise<Knex.Config> {\n const { Signer } =\n require('@aws-sdk/rds-signer') as typeof import('@aws-sdk/rds-signer');\n const { fromNodeProviderChain } =\n require('@aws-sdk/credential-providers') as typeof import('@aws-sdk/credential-providers');\n\n let hostname: string;\n let port: number;\n let username: string;\n try {\n hostname = config.getString('connection.host');\n port = config.getNumber('connection.port');\n username = config.getString('connection.user');\n } catch (err) {\n throw new ForwardedError(\n 'AWS RDS IAM auth: missing required database connection config — make sure connection.host, connection.port, and connection.user are set and any environment variables they reference are set',\n err,\n );\n }\n const region =\n config.getOptionalString('connection.region') ??\n process.env.AWS_REGION ??\n process.env.AWS_DEFAULT_REGION;\n if (!region) {\n throw new Error(\n 'Missing region for AWS RDS IAM auth: set connection.region or the AWS_REGION environment variable',\n );\n }\n\n const rawConfig = config.get() as Record<string, unknown>;\n const sanitizedConnection = omit(\n config.get('connection') as Record<string, unknown>,\n ['type', 'region'],\n ) as Partial<Knex.StaticConnectionConfig>;\n\n const credentialsProvider = fromNodeProviderChain();\n\n // A token cannot outlive the credentials used to sign it. Renew the cached\n // connection settings one minute before either expiration.\n const tokenTtlMs = 15 * 60 * 1000;\n const renewalOffsetMs = 60 * 1000;\n\n async function getConnectionConfig() {\n try {\n let credentials = await credentialsProvider();\n // The SDK may return its cached credentials while refreshing them in the\n // background. Wait for a refresh if they are already inside our margin.\n if (\n credentials.expiration &&\n credentials.expiration.getTime() <= Date.now() + renewalOffsetMs\n ) {\n credentials = await credentialsProvider({ forceRefresh: true });\n }\n const signer = new Signer({\n hostname,\n port,\n username,\n region,\n credentials,\n });\n const tokenIssuedAt = Date.now();\n const password = await signer.getAuthToken();\n const tokenExpiration =\n Math.min(\n tokenIssuedAt + tokenTtlMs,\n credentials.expiration?.getTime() ?? Infinity,\n ) - renewalOffsetMs;\n return {\n ...sanitizedConnection,\n password,\n expirationChecker: () => tokenExpiration <= Date.now(),\n };\n } catch (err) {\n throw new ForwardedError(\n `AWS RDS IAM auth token acquisition failed for ${username}@${hostname}:${port}`,\n err,\n );\n }\n }\n\n return {\n ...(rawConfig as Record<string, unknown>),\n connection: getConnectionConfig,\n };\n}\n\n/**\n * Gets the postgres connection config\n *\n * @param dbConfig - The database config\n * @param parseConnectionString - Flag to explicitly control connection string parsing\n */\nexport function getPgConnectionConfig(\n dbConfig: Config,\n parseConnectionString?: boolean,\n): Knex.PgConnectionConfig | string {\n const connection = dbConfig.get('connection') as any;\n const isConnectionString =\n typeof connection === 'string' || connection instanceof String;\n const autoParse = typeof parseConnectionString !== 'boolean';\n\n const shouldParseConnectionString = autoParse\n ? isConnectionString\n : parseConnectionString && isConnectionString;\n\n return shouldParseConnectionString\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\n/**\n * Parses a connection string using pg-connection-string\n *\n * @param connectionString - The postgres connection string\n */\nexport function parsePgConnectionString(connectionString: string) {\n const parse = requirePgConnectionString();\n return parse(connectionString);\n}\n\nfunction requirePgConnectionString() {\n try {\n return require('pg-connection-string').parse;\n } catch (e) {\n throw new ForwardedError(\"Postgres: Install 'pg-connection-string'\", e);\n }\n}\n\nconst PG_DATABASE_ADMIN_OVERRIDES: Knex.Config = {\n connection: { database: 'postgres' },\n pool: { min: 0, max: 1, acquireTimeoutMillis: 10000 },\n};\n\nconst PG_SCHEMA_ADMIN_OVERRIDES: Knex.Config = {\n pool: { min: 0, max: 1, acquireTimeoutMillis: 10000 },\n};\n\n/**\n * Creates the missing Postgres database if it does not exist\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to create\n */\nexport async function ensurePgDatabaseExists(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n await Promise.all(\n databases.map(async database => {\n return retryPgAdminOperation(() =>\n ddlLimiter(async () => {\n const admin = await createPgDatabaseClient(\n dbConfig,\n PG_DATABASE_ADMIN_OVERRIDES,\n );\n try {\n await ensurePgDatabase(admin, database);\n } finally {\n await admin.destroy();\n }\n }),\n );\n }),\n );\n}\n\nasync function ensurePgDatabase(admin: Knex, database: string): Promise<void> {\n const result = await admin\n .from('pg_database')\n .where('datname', database)\n .count<Record<string, { count: string }>>();\n\n if (parseInt(result[0].count, 10) === 0) {\n await admin.raw(`CREATE DATABASE ??`, [database]);\n }\n}\n\nasync function retryPgAdminOperation(\n operation: () => Promise<void>,\n): Promise<void> {\n // For initial setup we use a smaller timeout but several retries. Given that this\n // is a separate connection pool we should never really run into issues with connection\n // acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.\n const maxAttempts = 3;\n for (let attempt = 1; ; attempt++) {\n try {\n return await operation();\n } catch (error) {\n if (attempt >= maxAttempts) {\n throw error;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n }\n}\n\nasync function ensurePgSchema(\n admin: Knex,\n role: string | undefined,\n schema: string,\n): Promise<void> {\n if (role) {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ?? AUTHORIZATION ??`, [\n schema,\n role,\n ]);\n } else {\n await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [schema]);\n }\n}\n\n/**\n * Creates the missing Postgres schema if it does not exist\n *\n * @param dbConfig - The database config\n * @param schemas - The name of the schemas to create\n */\nexport async function ensurePgSchemaExists(\n dbConfig: Config,\n ...schemas: Array<string>\n): Promise<void> {\n const admin = await createPgDatabaseClient(dbConfig);\n const role = dbConfig.getOptionalString('role');\n\n try {\n await Promise.all(\n schemas.map(schema =>\n ddlLimiter(() => ensurePgSchema(admin, role, schema)),\n ),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Drops the Postgres databases.\n *\n * @param dbConfig - The database config\n * @param databases - The name of the databases to drop\n */\nexport async function dropPgDatabase(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = await createPgDatabaseClient(dbConfig);\n try {\n await Promise.all(\n databases.map(async database => {\n await ddlLimiter(() => admin.raw(`DROP DATABASE ??`, [database]));\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\nfunction normalizeConnection(\n connection: Knex.StaticConnectionConfig | JsonObject | string | undefined,\n): Partial<Knex.StaticConnectionConfig> {\n if (typeof connection === 'undefined' || connection === null) {\n return {};\n }\n\n return typeof connection === 'string' || connection instanceof String\n ? parsePgConnectionString(connection as string)\n : connection;\n}\n\n/**\n * The computed configuration for a plugin's postgres database connection.\n */\nexport interface PgPluginDatabaseConfig {\n /** The database client type (e.g. 'pg') */\n client: string;\n /** Whether the client type was overridden at the plugin level */\n clientOverridden: boolean;\n /** The optional role to set on connections */\n role: string | undefined;\n /** Additional knex configuration merged from base and plugin config */\n additionalKnexConfig: JsonObject | undefined;\n /** Whether to ensure the database exists */\n ensureExists: boolean;\n /** Whether to ensure the schema exists */\n ensureSchemaExists: boolean;\n /** The plugin division mode ('database' or 'schema') */\n pluginDivisionMode: string;\n /** The connection configuration */\n connection: Knex.PgConnectionConfig;\n /** The database name, if any */\n databaseName: string | undefined;\n /** The schema name, if using schema division mode */\n schemaName: string | undefined;\n /** Database client overrides including schema overrides if applicable */\n databaseClientOverrides: Knex.Config;\n /** The full knex config for the plugin */\n knexConfig: Knex.Config;\n}\n\n/**\n * Computes all postgres database configuration for a plugin from the provided config.\n *\n * @param config - The database config object\n * @param pluginId - The plugin ID to compute config for\n * @param prefix - The database name prefix (e.g. 'backstage_plugin_')\n * @returns All computed configuration values for the plugin\n */\nexport function computePgPluginConfig(\n config: Config,\n pluginId: string,\n prefix: string,\n schemaPrefix: string = '',\n): PgPluginDatabaseConfig {\n // Client type\n const pluginClient = config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n const baseClient = config.getString('client');\n const client = pluginClient ?? baseClient;\n const clientOverridden = client !== baseClient;\n\n // Role\n const role =\n config.getOptionalString(`${pluginPath(pluginId)}.role`) ??\n config.getOptionalString('role');\n\n // Additional knex config\n const pluginKnexConfig = config\n .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`)\n ?.get<JsonObject>();\n const baseKnexConfig = config\n .getOptionalConfig('knexConfig')\n ?.get<JsonObject>();\n const additionalKnexConfig = merge(baseKnexConfig, pluginKnexConfig);\n\n // Ensure exists flags\n const baseEnsureExists = config.getOptionalBoolean('ensureExists') ?? true;\n const ensureExists =\n config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ??\n baseEnsureExists;\n\n const baseEnsureSchemaExists =\n config.getOptionalBoolean('ensureSchemaExists') ?? false;\n const ensureSchemaExists =\n config.getOptionalBoolean(\n `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`,\n ) ?? baseEnsureSchemaExists;\n\n // Plugin division mode\n const pluginDivisionMode =\n config.getOptionalString('pluginDivisionMode') ?? 'database';\n\n // Connection config\n let baseConnection = normalizeConnection(config.get('connection'));\n\n // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`.\n // The `database` property from the base connection is omitted unless\n // `pluginDivisionMode` is set to `schema`.\n if (pluginDivisionMode !== 'schema') {\n baseConnection = omit(baseConnection, 'database');\n }\n\n // Get and normalize optional plugin specific database connection\n const pluginConnection = normalizeConnection(\n config.getOptional(`${pluginPath(pluginId)}.connection`),\n );\n\n (\n baseConnection as Knex.PgConnectionConfig\n ).application_name ||= `backstage_plugin_${pluginId}`;\n\n const connection = {\n // Include base connection if client type has not been overridden\n ...(clientOverridden ? {} : baseConnection),\n ...pluginConnection,\n } as Knex.PgConnectionConfig;\n\n // Database name\n const connectionDatabaseName = (connection as Knex.ConnectionConfig)\n ?.database;\n let databaseName: string | undefined;\n\n if (pluginDivisionMode === 'schema') {\n // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied\n // or fallback to default knex database\n databaseName = connectionDatabaseName;\n } else {\n // All other supported databases should fallback to an auto-prefixed name\n databaseName = connectionDatabaseName ?? `${prefix}${pluginId}`;\n }\n\n // Database client overrides\n let databaseClientOverrides: Knex.Config = {};\n let schemaName: string | undefined;\n\n if (databaseName) {\n databaseClientOverrides = { connection: { database: databaseName } };\n }\n if (pluginDivisionMode === 'schema') {\n schemaName = `${schemaPrefix}${pluginId}`;\n\n if (Buffer.byteLength(schemaName, 'utf8') > 63) {\n throw new Error(\n `PostgreSQL schema name \"${schemaName}\" exceeds the 63-byte limit. ` +\n `Consider using a shorter schemaPrefix (current: \"${schemaPrefix}\") or ` +\n `plugin ID (current: \"${pluginId}\").`,\n );\n }\n\n databaseClientOverrides = mergeDatabaseConfig({}, databaseClientOverrides, {\n searchPath: [schemaName],\n });\n }\n\n // Full knex config for plugin\n const knexConfig: Knex.Config = {\n ...additionalKnexConfig,\n client,\n connection,\n ...(role && { role }),\n };\n\n return {\n client,\n clientOverridden,\n role,\n additionalKnexConfig,\n ensureExists,\n ensureSchemaExists,\n pluginDivisionMode,\n connection,\n databaseName,\n schemaName,\n databaseClientOverrides,\n knexConfig,\n };\n}\n\nexport class PgConnector implements Connector {\n private readonly config: Config;\n private readonly prefix: string;\n private readonly schemaPrefix: string;\n private readonly databaseEnsureCache = new Map<string, Promise<void>>();\n private readonly customEnsureDatabaseExists?: typeof ensurePgDatabaseExists;\n private readonly databaseAdminPool: PgAdminPool;\n private readonly schemaAdminPool: PgAdminPool;\n\n constructor(\n config: Config,\n prefix: string,\n schemaPrefix: string,\n options: {\n ensureDatabaseExists?: typeof ensurePgDatabaseExists;\n createAdminClient?: (overrides: Knex.Config) => Promise<Knex>;\n adminPoolIdleTimeoutMillis?: number;\n } = {},\n ) {\n this.config = config;\n this.prefix = prefix;\n this.schemaPrefix = schemaPrefix;\n this.customEnsureDatabaseExists = options.ensureDatabaseExists;\n\n const createAdminClient =\n options.createAdminClient ??\n ((overrides: Knex.Config) =>\n createPgDatabaseClient(this.config, overrides));\n const idleTimeoutMillis = options.adminPoolIdleTimeoutMillis ?? 10_000;\n this.databaseAdminPool = new PgAdminPool(\n () => createAdminClient(PG_DATABASE_ADMIN_OVERRIDES),\n idleTimeoutMillis,\n );\n this.schemaAdminPool = new PgAdminPool(\n () => createAdminClient(PG_SCHEMA_ADMIN_OVERRIDES),\n idleTimeoutMillis,\n );\n }\n\n private async ensureDatabase(databaseName: string): Promise<void> {\n let promise = this.databaseEnsureCache.get(databaseName);\n if (!promise) {\n promise = this.customEnsureDatabaseExists\n ? this.customEnsureDatabaseExists(this.config, databaseName)\n : retryPgAdminOperation(() =>\n ddlLimiter(() =>\n this.runAdminOperation(this.databaseAdminPool, admin =>\n ensurePgDatabase(admin, databaseName),\n ),\n ),\n );\n this.databaseEnsureCache.set(databaseName, promise);\n }\n\n try {\n await promise;\n } catch (error) {\n if (this.databaseEnsureCache.get(databaseName) === promise) {\n this.databaseEnsureCache.delete(databaseName);\n }\n throw error;\n }\n }\n\n private async runAdminOperation<T>(\n pool: PgAdminPool,\n operation: (client: Knex) => Promise<T>,\n ): Promise<T> {\n try {\n return await pool.run(operation);\n } catch (error) {\n await pool.invalidate().catch(() => {});\n throw error;\n }\n }\n\n async shutdown(): Promise<void> {\n const results = await Promise.allSettled([\n this.databaseAdminPool.shutdown(),\n this.schemaAdminPool.shutdown(),\n ]);\n const errors = results\n .filter(\n (result): result is PromiseRejectedResult =>\n result.status === 'rejected',\n )\n .map(result => result.reason);\n if (errors.length === 1) {\n throw errors[0];\n }\n if (errors.length > 1) {\n throw new AggregateError(\n errors,\n 'Failed to shut down PostgreSQL admin pools',\n );\n }\n }\n\n async getClient(\n pluginId: string,\n _deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n const pluginDbConfig = computePgPluginConfig(\n this.config,\n pluginId,\n this.prefix,\n this.schemaPrefix,\n );\n\n if (pluginDbConfig.databaseName && pluginDbConfig.ensureExists) {\n try {\n await this.ensureDatabase(pluginDbConfig.databaseName);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that '${pluginDbConfig.databaseName}' exists, ${error}`,\n );\n }\n }\n\n if (pluginDbConfig.pluginDivisionMode === 'schema') {\n if (pluginDbConfig.ensureSchemaExists || pluginDbConfig.ensureExists) {\n try {\n await ddlLimiter(() =>\n this.runAdminOperation(this.schemaAdminPool, admin =>\n ensurePgSchema(\n admin,\n this.config.getOptionalString('role'),\n pluginDbConfig.schemaName!,\n ),\n ),\n );\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`,\n );\n }\n }\n }\n\n const client = createPgDatabaseClient(\n this.config,\n mergeDatabaseConfig(\n pluginDbConfig.knexConfig,\n pluginDbConfig.databaseClientOverrides,\n ),\n );\n\n return client;\n }\n}\n"],"names":["limiterFactory","knexFactory","format","config","mergeDatabaseConfig","ConfigReader","omit","durationToMilliseconds","readDurationFromConfig","ForwardedError","merge"],"mappings":"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"}
@@ -21,10 +21,14 @@ const getRootLoggerConfig = (config) => {
21
21
  level: overrideLevel
22
22
  };
23
23
  });
24
+ const redactAllowlist = config.getOptionalStringArray(
25
+ "backend.logger.redactAllowlist"
26
+ );
24
27
  return {
25
28
  meta,
26
29
  level,
27
- overrides
30
+ overrides,
31
+ redactAllowlist
28
32
  };
29
33
  };
30
34
 
@@ -1 +1 @@
1
- {"version":3,"file":"config.cjs.js","sources":["../../../src/entrypoints/rootLogger/config.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RootConfigService } from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport {\n RootLoggerConfig,\n winstonLevels,\n WinstonLoggerLevelOverrideMatchers,\n} from './types';\n\nexport const getRootLoggerConfig = (\n config: RootConfigService,\n): RootLoggerConfig => {\n const level = config.getOptionalString('backend.logger.level');\n const meta = config\n .getOptionalConfig('backend.logger.meta')\n ?.get<JsonObject>();\n\n const overridesConfig = config.getOptionalConfigArray(\n 'backend.logger.overrides',\n );\n const overrides = overridesConfig?.map((override, i) => {\n const overrideLevel = override.getString('level');\n if (winstonLevels[overrideLevel] === undefined) {\n throw new Error(\n `Invalid config at backend.logger.overrides[${i}].level, '${overrideLevel}' is not a valid logging level, must be one of 'error', 'warn', 'info' or 'debug'.`,\n );\n }\n\n const matchers = override\n .getConfig('matchers')\n .get<WinstonLoggerLevelOverrideMatchers>();\n\n return {\n matchers,\n level: overrideLevel,\n };\n });\n\n return {\n meta,\n level,\n overrides,\n };\n};\n"],"names":["winstonLevels"],"mappings":";;;;AAuBO,MAAM,mBAAA,GAAsB,CACjC,MAAA,KACqB;AACrB,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,iBAAA,CAAkB,sBAAsB,CAAA;AAC7D,EAAA,MAAM,IAAA,GAAO,MAAA,CACV,iBAAA,CAAkB,qBAAqB,GACtC,GAAA,EAAgB;AAEpB,EAAA,MAAM,kBAAkB,MAAA,CAAO,sBAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAA,MAAM,SAAA,GAAY,eAAA,EAAiB,GAAA,CAAI,CAAC,UAAU,CAAA,KAAM;AACtD,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,SAAA,CAAU,OAAO,CAAA;AAChD,IAAA,IAAIA,mBAAA,CAAc,aAAa,CAAA,KAAM,MAAA,EAAW;AAC9C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,2CAAA,EAA8C,CAAC,CAAA,UAAA,EAAa,aAAa,CAAA,kFAAA;AAAA,OAC3E;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,QAAA,CACd,SAAA,CAAU,UAAU,EACpB,GAAA,EAAwC;AAE3C,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,KAAA,EAAO;AAAA,KACT;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GACF;AACF;;"}
1
+ {"version":3,"file":"config.cjs.js","sources":["../../../src/entrypoints/rootLogger/config.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RootConfigService } from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport {\n RootLoggerConfig,\n winstonLevels,\n WinstonLoggerLevelOverrideMatchers,\n} from './types';\n\nexport const getRootLoggerConfig = (\n config: RootConfigService,\n): RootLoggerConfig => {\n const level = config.getOptionalString('backend.logger.level');\n const meta = config\n .getOptionalConfig('backend.logger.meta')\n ?.get<JsonObject>();\n\n const overridesConfig = config.getOptionalConfigArray(\n 'backend.logger.overrides',\n );\n const overrides = overridesConfig?.map((override, i) => {\n const overrideLevel = override.getString('level');\n if (winstonLevels[overrideLevel] === undefined) {\n throw new Error(\n `Invalid config at backend.logger.overrides[${i}].level, '${overrideLevel}' is not a valid logging level, must be one of 'error', 'warn', 'info' or 'debug'.`,\n );\n }\n\n const matchers = override\n .getConfig('matchers')\n .get<WinstonLoggerLevelOverrideMatchers>();\n\n return {\n matchers,\n level: overrideLevel,\n };\n });\n\n const redactAllowlist = config.getOptionalStringArray(\n 'backend.logger.redactAllowlist',\n );\n\n return {\n meta,\n level,\n overrides,\n redactAllowlist,\n };\n};\n"],"names":["winstonLevels"],"mappings":";;;;AAuBO,MAAM,mBAAA,GAAsB,CACjC,MAAA,KACqB;AACrB,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,iBAAA,CAAkB,sBAAsB,CAAA;AAC7D,EAAA,MAAM,IAAA,GAAO,MAAA,CACV,iBAAA,CAAkB,qBAAqB,GACtC,GAAA,EAAgB;AAEpB,EAAA,MAAM,kBAAkB,MAAA,CAAO,sBAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAA,MAAM,SAAA,GAAY,eAAA,EAAiB,GAAA,CAAI,CAAC,UAAU,CAAA,KAAM;AACtD,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,SAAA,CAAU,OAAO,CAAA;AAChD,IAAA,IAAIA,mBAAA,CAAc,aAAa,CAAA,KAAM,MAAA,EAAW;AAC9C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,2CAAA,EAA8C,CAAC,CAAA,UAAA,EAAa,aAAa,CAAA,kFAAA;AAAA,OAC3E;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAW,QAAA,CACd,SAAA,CAAU,UAAU,EACpB,GAAA,EAAwC;AAE3C,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,KAAA,EAAO;AAAA,KACT;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,kBAAkB,MAAA,CAAO,sBAAA;AAAA,IAC7B;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;;"}
@@ -23,8 +23,20 @@ const rootLoggerServiceFactory = backendPluginApi.createServiceFactory({
23
23
  transports: [new winston.transports.Console()]
24
24
  });
25
25
  const secretEnumerator = await createConfigSecretEnumerator.createConfigSecretEnumerator({ logger });
26
- logger.addRedactions(secretEnumerator(config$1));
27
- config$1.subscribe?.(() => logger.addRedactions(secretEnumerator(config$1)));
26
+ const allowlist = new Set(
27
+ rootLoggerConfig.redactAllowlist?.map((a) => a.trim())
28
+ );
29
+ logger.addRedactions(
30
+ [...secretEnumerator(config$1)].filter((s) => !allowlist.has(s.trim()))
31
+ );
32
+ config$1.subscribe?.(() => {
33
+ const newAllowlist = new Set(
34
+ config.getRootLoggerConfig(config$1).redactAllowlist?.map((a) => a.trim())
35
+ );
36
+ logger.addRedactions(
37
+ [...secretEnumerator(config$1)].filter((s) => !newAllowlist.has(s.trim()))
38
+ );
39
+ });
28
40
  logger.setLevelOverrides(rootLoggerConfig.overrides ?? []);
29
41
  config$1.subscribe?.(
30
42
  () => logger.setLevelOverrides(config.getRootLoggerConfig(config$1).overrides ?? [])
@@ -1 +1 @@
1
- {"version":3,"file":"rootLoggerServiceFactory.cjs.js","sources":["../../../src/entrypoints/rootLogger/rootLoggerServiceFactory.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 {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { format, transports } from 'winston';\nimport { createConfigSecretEnumerator } from '../rootConfig/createConfigSecretEnumerator';\nimport { WinstonLogger } from '../rootLogger/WinstonLogger';\nimport { getRootLoggerConfig } from './config';\n\n/**\n * Root-level logging.\n *\n * See {@link @backstage/backend-plugin-api#RootLoggerService}\n * and {@link https://backstage.io/docs/backend-system/core-services/root-logger | the service docs}\n * for more information.\n *\n * @public\n */\nexport const rootLoggerServiceFactory = createServiceFactory({\n service: coreServices.rootLogger,\n deps: {\n config: coreServices.rootConfig,\n },\n async factory({ config }) {\n const rootLoggerConfig = getRootLoggerConfig(config);\n\n const logger = WinstonLogger.create({\n meta: {\n service: 'backstage',\n ...rootLoggerConfig.meta,\n },\n level: process.env.LOG_LEVEL || rootLoggerConfig.level || 'info',\n format:\n process.env.NODE_ENV === 'production'\n ? format.json()\n : WinstonLogger.colorFormat(),\n transports: [new transports.Console()],\n });\n\n const secretEnumerator = await createConfigSecretEnumerator({ logger });\n logger.addRedactions(secretEnumerator(config));\n config.subscribe?.(() => logger.addRedactions(secretEnumerator(config)));\n\n logger.setLevelOverrides(rootLoggerConfig.overrides ?? []);\n config.subscribe?.(() =>\n logger.setLevelOverrides(getRootLoggerConfig(config).overrides ?? []),\n );\n\n return logger;\n },\n});\n"],"names":["createServiceFactory","coreServices","config","getRootLoggerConfig","WinstonLogger","format","transports","createConfigSecretEnumerator"],"mappings":";;;;;;;;AAkCO,MAAM,2BAA2BA,qCAAA,CAAqB;AAAA,EAC3D,SAASC,6BAAA,CAAa,UAAA;AAAA,EACtB,IAAA,EAAM;AAAA,IACJ,QAAQA,6BAAA,CAAa;AAAA,GACvB;AAAA,EACA,MAAM,OAAA,CAAQ,UAAEC,QAAA,EAAO,EAAG;AACxB,IAAA,MAAM,gBAAA,GAAmBC,2BAAoBD,QAAM,CAAA;AAEnD,IAAA,MAAM,MAAA,GAASE,4BAAc,MAAA,CAAO;AAAA,MAClC,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,WAAA;AAAA,QACT,GAAG,gBAAA,CAAiB;AAAA,OACtB;AAAA,MACA,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,SAAA,IAAa,iBAAiB,KAAA,IAAS,MAAA;AAAA,MAC1D,MAAA,EACE,QAAQ,GAAA,CAAI,QAAA,KAAa,eACrBC,cAAA,CAAO,IAAA,EAAK,GACZD,2BAAA,CAAc,WAAA,EAAY;AAAA,MAChC,UAAA,EAAY,CAAC,IAAIE,kBAAA,CAAW,SAAS;AAAA,KACtC,CAAA;AAED,IAAA,MAAM,gBAAA,GAAmB,MAAMC,yDAAA,CAA6B,EAAE,QAAQ,CAAA;AACtE,IAAA,MAAA,CAAO,aAAA,CAAc,gBAAA,CAAiBL,QAAM,CAAC,CAAA;AAC7C,IAAAA,QAAA,CAAO,YAAY,MAAM,MAAA,CAAO,cAAc,gBAAA,CAAiBA,QAAM,CAAC,CAAC,CAAA;AAEvE,IAAA,MAAA,CAAO,iBAAA,CAAkB,gBAAA,CAAiB,SAAA,IAAa,EAAE,CAAA;AACzD,IAAAA,QAAA,CAAO,SAAA;AAAA,MAAY,MACjB,OAAO,iBAAA,CAAkBC,0BAAA,CAAoBD,QAAM,CAAA,CAAE,SAAA,IAAa,EAAE;AAAA,KACtE;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAC;;"}
1
+ {"version":3,"file":"rootLoggerServiceFactory.cjs.js","sources":["../../../src/entrypoints/rootLogger/rootLoggerServiceFactory.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 {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { format, transports } from 'winston';\nimport { createConfigSecretEnumerator } from '../rootConfig/createConfigSecretEnumerator';\nimport { WinstonLogger } from '../rootLogger/WinstonLogger';\nimport { getRootLoggerConfig } from './config';\n\n/**\n * Root-level logging.\n *\n * See {@link @backstage/backend-plugin-api#RootLoggerService}\n * and {@link https://backstage.io/docs/backend-system/core-services/root-logger | the service docs}\n * for more information.\n *\n * @public\n */\nexport const rootLoggerServiceFactory = createServiceFactory({\n service: coreServices.rootLogger,\n deps: {\n config: coreServices.rootConfig,\n },\n async factory({ config }) {\n const rootLoggerConfig = getRootLoggerConfig(config);\n\n const logger = WinstonLogger.create({\n meta: {\n service: 'backstage',\n ...rootLoggerConfig.meta,\n },\n level: process.env.LOG_LEVEL || rootLoggerConfig.level || 'info',\n format:\n process.env.NODE_ENV === 'production'\n ? format.json()\n : WinstonLogger.colorFormat(),\n transports: [new transports.Console()],\n });\n\n const secretEnumerator = await createConfigSecretEnumerator({ logger });\n const allowlist = new Set(\n rootLoggerConfig.redactAllowlist?.map(a => a.trim()),\n );\n logger.addRedactions(\n [...secretEnumerator(config)].filter(s => !allowlist.has(s.trim())),\n );\n config.subscribe?.(() => {\n const newAllowlist = new Set(\n getRootLoggerConfig(config).redactAllowlist?.map(a => a.trim()),\n );\n logger.addRedactions(\n [...secretEnumerator(config)].filter(s => !newAllowlist.has(s.trim())),\n );\n });\n\n logger.setLevelOverrides(rootLoggerConfig.overrides ?? []);\n config.subscribe?.(() =>\n logger.setLevelOverrides(getRootLoggerConfig(config).overrides ?? []),\n );\n\n return logger;\n },\n});\n"],"names":["createServiceFactory","coreServices","config","getRootLoggerConfig","WinstonLogger","format","transports","createConfigSecretEnumerator"],"mappings":";;;;;;;;AAkCO,MAAM,2BAA2BA,qCAAA,CAAqB;AAAA,EAC3D,SAASC,6BAAA,CAAa,UAAA;AAAA,EACtB,IAAA,EAAM;AAAA,IACJ,QAAQA,6BAAA,CAAa;AAAA,GACvB;AAAA,EACA,MAAM,OAAA,CAAQ,UAAEC,QAAA,EAAO,EAAG;AACxB,IAAA,MAAM,gBAAA,GAAmBC,2BAAoBD,QAAM,CAAA;AAEnD,IAAA,MAAM,MAAA,GAASE,4BAAc,MAAA,CAAO;AAAA,MAClC,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,WAAA;AAAA,QACT,GAAG,gBAAA,CAAiB;AAAA,OACtB;AAAA,MACA,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,SAAA,IAAa,iBAAiB,KAAA,IAAS,MAAA;AAAA,MAC1D,MAAA,EACE,QAAQ,GAAA,CAAI,QAAA,KAAa,eACrBC,cAAA,CAAO,IAAA,EAAK,GACZD,2BAAA,CAAc,WAAA,EAAY;AAAA,MAChC,UAAA,EAAY,CAAC,IAAIE,kBAAA,CAAW,SAAS;AAAA,KACtC,CAAA;AAED,IAAA,MAAM,gBAAA,GAAmB,MAAMC,yDAAA,CAA6B,EAAE,QAAQ,CAAA;AACtE,IAAA,MAAM,YAAY,IAAI,GAAA;AAAA,MACpB,iBAAiB,eAAA,EAAiB,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM;AAAA,KACrD;AACA,IAAA,MAAA,CAAO,aAAA;AAAA,MACL,CAAC,GAAG,gBAAA,CAAiBL,QAAM,CAAC,CAAA,CAAE,MAAA,CAAO,CAAA,CAAA,KAAK,CAAC,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,IAAA,EAAM,CAAC;AAAA,KACpE;AACA,IAAAA,QAAA,CAAO,YAAY,MAAM;AACvB,MAAA,MAAM,eAAe,IAAI,GAAA;AAAA,QACvBC,0BAAA,CAAoBD,QAAM,CAAA,CAAE,eAAA,EAAiB,IAAI,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM;AAAA,OAChE;AACA,MAAA,MAAA,CAAO,aAAA;AAAA,QACL,CAAC,GAAG,gBAAA,CAAiBA,QAAM,CAAC,CAAA,CAAE,MAAA,CAAO,CAAA,CAAA,KAAK,CAAC,YAAA,CAAa,GAAA,CAAI,CAAA,CAAE,IAAA,EAAM,CAAC;AAAA,OACvE;AAAA,IACF,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,iBAAA,CAAkB,gBAAA,CAAiB,SAAA,IAAa,EAAE,CAAA;AACzD,IAAAA,QAAA,CAAO,SAAA;AAAA,MAAY,MACjB,OAAO,iBAAA,CAAkBC,0BAAA,CAAoBD,QAAM,CAAA,CAAE,SAAA,IAAa,EAAE;AAAA,KACtE;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAC;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.cjs.js","sources":["../../../src/entrypoints/rootLogger/types.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { JsonObject, JsonPrimitive } from '@backstage/types';\nimport { config as winstonConfig } from 'winston';\n\n/**\n * @public\n */\nexport type WinstonLoggerLevelOverrideMatchers = {\n [key: string]: JsonPrimitive | JsonPrimitive[] | undefined;\n};\n\n/**\n * @public\n */\nexport type WinstonLoggerLevelOverride = {\n matchers: WinstonLoggerLevelOverrideMatchers;\n level: string;\n};\n\nexport type RootLoggerConfig = {\n level?: string;\n meta?: JsonObject;\n overrides?: WinstonLoggerLevelOverride[];\n};\n\nexport const winstonLevels = winstonConfig.npm.levels;\n"],"names":["winstonConfig"],"mappings":";;;;AAuCO,MAAM,aAAA,GAAgBA,eAAc,GAAA,CAAI;;"}
1
+ {"version":3,"file":"types.cjs.js","sources":["../../../src/entrypoints/rootLogger/types.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { JsonObject, JsonPrimitive } from '@backstage/types';\nimport { config as winstonConfig } from 'winston';\n\n/**\n * @public\n */\nexport type WinstonLoggerLevelOverrideMatchers = {\n [key: string]: JsonPrimitive | JsonPrimitive[] | undefined;\n};\n\n/**\n * @public\n */\nexport type WinstonLoggerLevelOverride = {\n matchers: WinstonLoggerLevelOverrideMatchers;\n level: string;\n};\n\nexport type RootLoggerConfig = {\n level?: string;\n meta?: JsonObject;\n overrides?: WinstonLoggerLevelOverride[];\n redactAllowlist?: string[];\n};\n\nexport const winstonLevels = winstonConfig.npm.levels;\n"],"names":["winstonConfig"],"mappings":";;;;AAwCO,MAAM,aAAA,GAAgBA,eAAc,GAAA,CAAI;;"}
@@ -4,7 +4,7 @@ var GoogleCloud = require('@google-cloud/storage');
4
4
  var getRawBody = require('raw-body');
5
5
  var integration = require('@backstage/integration');
6
6
  var ReadUrlResponseFactory = require('./ReadUrlResponseFactory.cjs.js');
7
- var _package = require('../../../package.json.cjs.js');
7
+ var _package = require('../../../backend-defaults/package.json.cjs.js');
8
8
  var errors = require('@backstage/errors');
9
9
  var posix = require('node:path/posix');
10
10
  var util = require('./util.cjs.js');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-defaults",
3
- "version": "0.18.1-next.2",
3
+ "version": "0.18.1",
4
4
  "description": "Backend defaults used by Backstage backend apps",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -226,20 +226,21 @@
226
226
  "@aws-sdk/types": "^3.347.0",
227
227
  "@azure/identity": "^4.0.0",
228
228
  "@azure/storage-blob": "^12.5.0",
229
- "@backstage/backend-app-api": "1.8.0-next.1",
230
- "@backstage/backend-dev-utils": "0.1.7",
231
- "@backstage/backend-plugin-api": "1.11.0-next.1",
232
- "@backstage/cli-node": "0.3.5-next.1",
233
- "@backstage/config": "1.3.10-next.0",
234
- "@backstage/config-loader": "1.11.4-next.0",
235
- "@backstage/errors": "1.3.2-next.0",
236
- "@backstage/integration": "2.1.3-next.1",
237
- "@backstage/integration-aws-node": "0.2.3-next.0",
238
- "@backstage/plugin-auth-node": "0.7.7-next.1",
239
- "@backstage/plugin-events-node": "0.4.27-next.1",
240
- "@backstage/plugin-permission-common": "0.9.12-next.0",
241
- "@backstage/plugin-permission-node": "0.11.5-next.1",
242
- "@backstage/types": "1.2.2",
229
+ "@backstage/backend-app-api": "^1.8.0",
230
+ "@backstage/backend-dev-utils": "^0.1.7",
231
+ "@backstage/backend-plugin-api": "^1.11.0",
232
+ "@backstage/cli-node": "^0.3.5",
233
+ "@backstage/config": "^1.3.10",
234
+ "@backstage/config-loader": "^1.11.4",
235
+ "@backstage/connections": "^0.4.1",
236
+ "@backstage/errors": "^1.3.2",
237
+ "@backstage/integration": "^2.1.3",
238
+ "@backstage/integration-aws-node": "^0.2.3",
239
+ "@backstage/plugin-auth-node": "^0.7.7",
240
+ "@backstage/plugin-events-node": "^0.4.27",
241
+ "@backstage/plugin-permission-common": "^0.9.12",
242
+ "@backstage/plugin-permission-node": "^0.11.5",
243
+ "@backstage/types": "^1.2.2",
243
244
  "@google-cloud/storage": "^7.0.0",
244
245
  "@keyv/memcache": "^2.0.1",
245
246
  "@keyv/redis": "^4.0.1",
@@ -294,8 +295,8 @@
294
295
  },
295
296
  "devDependencies": {
296
297
  "@aws-sdk/util-stream-node": "^3.350.0",
297
- "@backstage/backend-test-utils": "1.11.8-next.1",
298
- "@backstage/cli": "0.36.7-next.0",
298
+ "@backstage/backend-test-utils": "^1.11.8",
299
+ "@backstage/cli": "^0.36.7",
299
300
  "@google-cloud/cloud-sql-connector": "^1.4.0",
300
301
  "@types/archiver": "^7.0.0",
301
302
  "@types/base64-stream": "^1.0.2",