@backstage/plugin-catalog-backend 4.0.0-next.1 → 4.0.1-next.0

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 (148) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +13 -0
  3. package/config.schema.json +0 -4
  4. package/dist/actions/createGetCatalogEntityAction.cjs.js.map +1 -1
  5. package/dist/actions/createGetCatalogModelDescriptionAction.cjs.js.map +1 -1
  6. package/dist/actions/createQueryCatalogEntitiesAction.cjs.js +40 -31
  7. package/dist/actions/createQueryCatalogEntitiesAction.cjs.js.map +1 -1
  8. package/dist/actions/createRefreshCatalogEntityAction.cjs.js.map +1 -1
  9. package/dist/actions/createRegisterCatalogEntitiesAction.cjs.js.map +1 -1
  10. package/dist/actions/createUnregisterCatalogEntitiesAction.cjs.js.map +1 -1
  11. package/dist/actions/createValidateEntityAction.cjs.js.map +1 -1
  12. package/dist/actions/index.cjs.js.map +1 -1
  13. package/dist/constants.cjs.js.map +1 -1
  14. package/dist/database/DefaultCatalogDatabase.cjs.js +3 -2
  15. package/dist/database/DefaultCatalogDatabase.cjs.js.map +1 -1
  16. package/dist/database/DefaultProcessingDatabase.cjs.js.map +1 -1
  17. package/dist/database/DefaultProviderDatabase.cjs.js +80 -25
  18. package/dist/database/DefaultProviderDatabase.cjs.js.map +1 -1
  19. package/dist/database/conversion.cjs.js.map +1 -1
  20. package/dist/database/metrics.cjs.js.map +1 -1
  21. package/dist/database/migrations.cjs.js.map +1 -1
  22. package/dist/database/operations/provider/deleteWithEagerPruningOfChildren.cjs.js.map +1 -1
  23. package/dist/database/operations/provider/refreshByRefreshKeys.cjs.js.map +1 -1
  24. package/dist/database/operations/refreshState/checkLocationKeyConflict.cjs.js.map +1 -1
  25. package/dist/database/operations/refreshState/insertUnprocessedEntity.cjs.js +1 -1
  26. package/dist/database/operations/refreshState/insertUnprocessedEntity.cjs.js.map +1 -1
  27. package/dist/database/operations/refreshState/updateUnprocessedEntity.cjs.js +1 -1
  28. package/dist/database/operations/refreshState/updateUnprocessedEntity.cjs.js.map +1 -1
  29. package/dist/database/operations/relations/syncRelations.cjs.js.map +1 -1
  30. package/dist/database/operations/stitcher/buildEntitySearch.cjs.js +5 -5
  31. package/dist/database/operations/stitcher/buildEntitySearch.cjs.js.map +1 -1
  32. package/dist/database/operations/stitcher/getDeferredStitchableEntities.cjs.js.map +1 -1
  33. package/dist/database/operations/stitcher/markDeferredStitchCompleted.cjs.js.map +1 -1
  34. package/dist/database/operations/stitcher/markForStitching.cjs.js.map +1 -1
  35. package/dist/database/operations/stitcher/performStitching.cjs.js.map +1 -1
  36. package/dist/database/operations/stitcher/syncSearchRows.cjs.js +10 -1
  37. package/dist/database/operations/stitcher/syncSearchRows.cjs.js.map +1 -1
  38. package/dist/database/operations/stitcher/util.cjs.js.map +1 -1
  39. package/dist/database/operations/util/deleteOrphanedEntities.cjs.js.map +1 -1
  40. package/dist/database/util.cjs.js.map +1 -1
  41. package/dist/index.cjs.js +0 -4
  42. package/dist/index.cjs.js.map +1 -1
  43. package/dist/index.d.ts +2 -41
  44. package/dist/ingestion/CatalogRules.cjs.js +2 -2
  45. package/dist/ingestion/CatalogRules.cjs.js.map +1 -1
  46. package/dist/ingestion/LocationAnalyzer.cjs.js.map +1 -1
  47. package/dist/model/ModelHolder.cjs.js.map +1 -1
  48. package/dist/permissions/conditionExports.cjs.js.map +1 -1
  49. package/dist/permissions/rules/createPropertyRule.cjs.js +2 -2
  50. package/dist/permissions/rules/createPropertyRule.cjs.js.map +1 -1
  51. package/dist/permissions/rules/hasAnnotation.cjs.js +4 -4
  52. package/dist/permissions/rules/hasAnnotation.cjs.js.map +1 -1
  53. package/dist/permissions/rules/hasLabel.cjs.js +4 -4
  54. package/dist/permissions/rules/hasLabel.cjs.js.map +1 -1
  55. package/dist/permissions/rules/hasMetadata.cjs.js.map +1 -1
  56. package/dist/permissions/rules/hasSpec.cjs.js.map +1 -1
  57. package/dist/permissions/rules/index.cjs.js.map +1 -1
  58. package/dist/permissions/rules/isEntityKind.cjs.js +3 -3
  59. package/dist/permissions/rules/isEntityKind.cjs.js.map +1 -1
  60. package/dist/permissions/rules/isEntityOwner.cjs.js.map +1 -1
  61. package/dist/permissions/rules/util.cjs.js.map +1 -1
  62. package/dist/processing/DefaultCatalogProcessingEngine.cjs.js.map +1 -1
  63. package/dist/processing/DefaultCatalogProcessingOrchestrator.cjs.js +4 -4
  64. package/dist/processing/DefaultCatalogProcessingOrchestrator.cjs.js.map +1 -1
  65. package/dist/processing/ProcessorCacheManager.cjs.js.map +1 -1
  66. package/dist/processing/ProcessorOutputCollector.cjs.js.map +1 -1
  67. package/dist/processing/TaskPipeline.cjs.js.map +1 -1
  68. package/dist/processing/connectEntityProviders.cjs.js +13 -16
  69. package/dist/processing/connectEntityProviders.cjs.js.map +1 -1
  70. package/dist/processing/evictEntitiesFromOrphanedProviders.cjs.js.map +1 -1
  71. package/dist/processing/refresh.cjs.js.map +1 -1
  72. package/dist/processing/util.cjs.js.map +1 -1
  73. package/dist/processors/AnnotateLocationEntityProcessor.cjs.js.map +1 -1
  74. package/dist/processors/BuiltinKindsEntityProcessor.cjs.js.map +1 -1
  75. package/dist/processors/FileReaderProcessor.cjs.js.map +1 -1
  76. package/dist/processors/ModelProcessor.cjs.js +7 -7
  77. package/dist/processors/ModelProcessor.cjs.js.map +1 -1
  78. package/dist/processors/PlaceholderProcessor.cjs.js.map +1 -1
  79. package/dist/processors/SchemaValidator.cjs.js +3 -1
  80. package/dist/processors/SchemaValidator.cjs.js.map +1 -1
  81. package/dist/processors/UrlReaderProcessor.cjs.js.map +1 -1
  82. package/dist/processors/transformLegacyPolicyToProcessor.cjs.js.map +1 -1
  83. package/dist/providers/ConfigLocationEntityProvider.cjs.js.map +1 -1
  84. package/dist/providers/DefaultLocationStore.cjs.js +1 -1
  85. package/dist/providers/DefaultLocationStore.cjs.js.map +1 -1
  86. package/dist/providers/GenericScmEventRefreshProvider.cjs.js.map +1 -1
  87. package/dist/schema/openapi/generated/router.cjs.js.map +1 -1
  88. package/dist/service/AuthorizedEntitiesCatalog.cjs.js.map +1 -1
  89. package/dist/service/AuthorizedLocationAnalyzer.cjs.js.map +1 -1
  90. package/dist/service/AuthorizedLocationService.cjs.js.map +1 -1
  91. package/dist/service/AuthorizedRefreshService.cjs.js.map +1 -1
  92. package/dist/service/AuthorizedValidationService.cjs.js.map +1 -1
  93. package/dist/service/CatalogBuilder.cjs.js +3 -6
  94. package/dist/service/CatalogBuilder.cjs.js.map +1 -1
  95. package/dist/service/CatalogPlugin.cjs.js.map +1 -1
  96. package/dist/service/DefaultEntitiesCatalog.cjs.js +10 -10
  97. package/dist/service/DefaultEntitiesCatalog.cjs.js.map +1 -1
  98. package/dist/service/DefaultLocationService.cjs.js.map +1 -1
  99. package/dist/service/DefaultRefreshService.cjs.js.map +1 -1
  100. package/dist/service/createRouter.cjs.js +2 -0
  101. package/dist/service/createRouter.cjs.js.map +1 -1
  102. package/dist/service/request/applyEntityFilterToQuery.cjs.js.map +1 -1
  103. package/dist/service/request/applyPredicateEntityFilterToQuery.cjs.js +9 -9
  104. package/dist/service/request/applyPredicateEntityFilterToQuery.cjs.js.map +1 -1
  105. package/dist/service/request/basicEntityFilter.cjs.js.map +1 -1
  106. package/dist/service/request/common.cjs.js.map +1 -1
  107. package/dist/service/request/entitiesBatchRequest.cjs.js.map +1 -1
  108. package/dist/service/request/entityFilterToFilterPredicate.cjs.js.map +1 -1
  109. package/dist/service/request/parseEntityFacetParams.cjs.js.map +1 -1
  110. package/dist/service/request/parseEntityFacetsQuery.cjs.js.map +1 -1
  111. package/dist/service/request/parseEntityFilterParams.cjs.js.map +1 -1
  112. package/dist/service/request/parseEntityOrderFieldParams.cjs.js.map +1 -1
  113. package/dist/service/request/parseEntityOrderParams.cjs.js.map +1 -1
  114. package/dist/service/request/parseEntityPaginationParams.cjs.js.map +1 -1
  115. package/dist/service/request/parseEntityQuery.cjs.js.map +1 -1
  116. package/dist/service/request/parseEntityTransformParams.cjs.js.map +1 -1
  117. package/dist/service/request/parseLocationQuery.cjs.js.map +1 -1
  118. package/dist/service/request/parseQueryEntitiesParams.cjs.js.map +1 -1
  119. package/dist/service/request/searchSubquery.cjs.js.map +1 -1
  120. package/dist/service/response/createEntityArrayJsonStream.cjs.js.map +1 -1
  121. package/dist/service/response/process.cjs.js +18 -5
  122. package/dist/service/response/process.cjs.js.map +1 -1
  123. package/dist/service/response/write.cjs.js.map +1 -1
  124. package/dist/service/util.cjs.js.map +1 -1
  125. package/dist/stitching/DefaultStitcher.cjs.js +1 -3
  126. package/dist/stitching/DefaultStitcher.cjs.js.map +1 -1
  127. package/dist/stitching/progressTracker.cjs.js.map +1 -1
  128. package/dist/stitching/types.cjs.js +1 -17
  129. package/dist/stitching/types.cjs.js.map +1 -1
  130. package/dist/util/conversion.cjs.js.map +1 -1
  131. package/dist/util/metrics.cjs.js.map +1 -1
  132. package/dist/util/opentelemetry.cjs.js.map +1 -1
  133. package/dist/util/parse.cjs.js.map +1 -1
  134. package/dist/util/readScmEventHandlingConfig.cjs.js.map +1 -1
  135. package/migrations/20260403000000_add_location_entity_ref.js +1 -1
  136. package/migrations/20260510000000_search_indices_and_dedup.js +50 -48
  137. package/migrations/20260912000000_refresh_state_maintenance.js +70 -0
  138. package/package.json +20 -21
  139. package/dist/processors/AnnotateScmSlugEntityProcessor.cjs.js +0 -77
  140. package/dist/processors/AnnotateScmSlugEntityProcessor.cjs.js.map +0 -1
  141. package/dist/processors/CodeOwnersProcessor.cjs.js +0 -56
  142. package/dist/processors/CodeOwnersProcessor.cjs.js.map +0 -1
  143. package/dist/processors/codeowners/read.cjs.js +0 -45
  144. package/dist/processors/codeowners/read.cjs.js.map +0 -1
  145. package/dist/processors/codeowners/resolve.cjs.js +0 -51
  146. package/dist/processors/codeowners/resolve.cjs.js.map +0 -1
  147. package/dist/processors/codeowners/scm.cjs.js +0 -14
  148. package/dist/processors/codeowners/scm.cjs.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,73 @@
1
1
  # @backstage/plugin-catalog-backend
2
2
 
3
+ ## 4.0.1-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - a08e746: Improved large entity provider mutations by yielding to the event loop while preparing database changes.
8
+ - 727d53f: Improve responsiveness to other requests while reading large sets of entities with field selection, without reducing page sizes.
9
+
10
+ Like full-entity responses, projected list responses may now be streamed without a `Content-Length` or automatically generated `ETag` header.
11
+
12
+ - e783f4b: Fixed catalog processing error messages to report stable processor names in minified builds.
13
+ - Updated dependencies
14
+ - @backstage/integration@2.1.3-next.0
15
+ - @backstage/config@1.3.10-next.0
16
+ - @backstage/errors@1.3.2-next.0
17
+ - @backstage/backend-plugin-api@1.10.2-next.0
18
+ - @backstage/filter-predicates@0.1.6-next.0
19
+ - @backstage/plugin-permission-common@0.9.12-next.0
20
+ - @backstage/plugin-permission-node@0.11.5-next.0
21
+ - @backstage/backend-openapi-utils@0.7.3-next.0
22
+ - @backstage/catalog-client@1.16.3-next.0
23
+ - @backstage/catalog-model@1.10.2-next.0
24
+ - @backstage/plugin-catalog-node@2.2.6-next.0
25
+ - @backstage/plugin-events-node@0.4.27-next.0
26
+ - @backstage/plugin-catalog-common@1.2.1-next.0
27
+
28
+ ## 4.0.0
29
+
30
+ ### Major Changes
31
+
32
+ - b0d8a53: **BREAKING**: Removed the deprecated `CodeOwnersProcessor` and `AnnotateScmSlugEntityProcessor` exports. To retain their behavior, install and register the corresponding community backend module:
33
+
34
+ - `CodeOwnersProcessor`: [`@backstage-community/plugin-catalog-backend-module-codeowners`](https://github.com/backstage/community-plugins/tree/main/workspaces/catalog/plugins/catalog-backend-module-codeowners)
35
+ - `AnnotateScmSlugEntityProcessor`: [`@backstage-community/plugin-catalog-backend-module-annotate-scm-slug`](https://github.com/backstage/community-plugins/tree/main/workspaces/catalog/plugins/catalog-backend-module-annotate-scm-slug)
36
+
37
+ Add the module to your backend with `backend.add(import('<module-package>'))` and remove the custom registration of the old processor. For explicit processor configuration, follow the replacement module's documentation.
38
+
39
+ - b0d8a53: **BREAKING**: Removed the deprecated `catalog.stitchingStrategy.mode` configuration setting and its startup warnings. Remove this key from your configuration, whether it is set to `immediate` or `deferred`. Stitching continues to run asynchronously via the worker queue. The `catalog.stitchingStrategy.pollingInterval` and `catalog.stitchingStrategy.stitchTimeout` settings remain supported and should be kept if you use them.
40
+ - d2cf99f: **BREAKING**: Removed the deprecated `catalog.enableRelationsCompatibility` config option and its associated compatibility layer. Entity relations are now always returned in the standard format with only `targetRef`. If you were relying on the `target` field in relations, update your code to use `targetRef` instead.
41
+
42
+ ### Patch Changes
43
+
44
+ - 736d84e: Use locale-insensitive Unicode casing for consistent string handling across environments.
45
+ - f5119cc: Improved PostgreSQL catalog query planning by correcting entity cardinality statistics for the search table.
46
+ - 72c16f5: Use stable PostgreSQL array parameters for `$in` filter predicates.
47
+ - 78656f8: Fixed catalog model relations to inherit the source entity namespace when no default namespace is configured, as documented. Explicit namespaces in entity references and explicitly configured default namespaces continue to take precedence.
48
+ - 7761a50: Improved path validation for catalog entity placeholders.
49
+ - 49b41f6: Fixed a bug where catalog entities could fail to process when catalog model sources are enabled and the catalog mixes `backstage.io/v1alpha1` and `backstage.io/v1beta1` for the same kind.
50
+ - 4b922b3: The `catalog:query-catalog-entities` action now accepts `limit` and `offset` when they are passed as strings, coercing them to numbers before validation. Previously the action failed with a validation error when a client sent these pagination arguments as strings, which is common for MCP/LLM clients.
51
+ - fda0ef1: Correct catalog property permission matching.
52
+ - ff327e0: Apply catalog location analysis permissions consistently when using a custom location analyzer.
53
+ - 05e2005: Fixed PostgreSQL catalog migrations sometimes failing during search index deduplication when using a database connection pool.
54
+ - 7629e5e: Use stable PostgreSQL array parameters for batched refresh state lookups and updates.
55
+ - c26a19b: Improved catalog database performance stability for large PostgreSQL installations with frequent entity updates.
56
+ - e363ae2: Allowed location type restrictions are now applied consistently during catalog processing.
57
+ - Updated dependencies
58
+ - @backstage/integration@2.1.2
59
+ - @backstage/plugin-catalog-common@1.2.0
60
+ - @backstage/backend-openapi-utils@0.7.2
61
+ - @backstage/catalog-client@1.16.2
62
+ - @backstage/catalog-model@1.10.1
63
+ - @backstage/filter-predicates@0.1.5
64
+ - @backstage/config@1.3.9
65
+ - @backstage/plugin-permission-node@0.11.4
66
+ - @backstage/backend-plugin-api@1.10.1
67
+ - @backstage/plugin-catalog-node@2.2.5
68
+ - @backstage/plugin-events-node@0.4.26
69
+ - @backstage/plugin-permission-common@0.9.11
70
+
3
71
  ## 4.0.0-next.1
4
72
 
5
73
  ### Major Changes
package/README.md CHANGED
@@ -69,6 +69,19 @@ you will not have any catalog entities loaded. See [Catalog Configuration](https
69
69
  for how to add locations, or copy the catalog locations from the [create-app template](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/app-config.yaml.hbs)
70
70
  to get up and running quickly.
71
71
 
72
+ ## Migrating removed processors
73
+
74
+ `CodeOwnersProcessor` and `AnnotateScmSlugEntityProcessor` are no longer exported
75
+ by this package. Use the corresponding community modules to retain their behavior:
76
+
77
+ - `CodeOwnersProcessor`: [`@backstage-community/plugin-catalog-backend-module-codeowners`](https://github.com/backstage/community-plugins/tree/main/workspaces/catalog/plugins/catalog-backend-module-codeowners)
78
+ - `AnnotateScmSlugEntityProcessor`: [`@backstage-community/plugin-catalog-backend-module-annotate-scm-slug`](https://github.com/backstage/community-plugins/tree/main/workspaces/catalog/plugins/catalog-backend-module-annotate-scm-slug)
79
+
80
+ Install the module you need in your backend package and register it with
81
+ `backend.add(import('<module-package>'))`. Remove your custom registration of the
82
+ old processor to avoid registering it twice. If you need to configure a processor
83
+ explicitly, follow the replacement module's documentation.
84
+
72
85
  ## Development
73
86
 
74
87
  This backend plugin can be started in a standalone mode from directly in this
@@ -133,10 +133,6 @@
133
133
  "stitchingStrategy": {
134
134
  "type": "object",
135
135
  "properties": {
136
- "mode": {
137
- "type": "string",
138
- "deprecated": "Immediate mode has been removed. This field is ignored."
139
- },
140
136
  "pollingInterval": {
141
137
  "anyOf": [
142
138
  {
@@ -1 +1 @@
1
- {"version":3,"file":"createGetCatalogEntityAction.cjs.js","sources":["../../src/actions/createGetCatalogEntityAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createGetCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-entity',\n title: 'Get Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: `\nThis allows you to get a single entity from the software catalog.\nEach entity in the software catalog has a unique name, kind, and namespace. The default namespace is \"default\".\nEach entity is identified by a unique entity reference, which is a string of the form \"kind:namespace/name\".\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .describe(\n 'The kind of the entity to query. If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .describe(\n 'The namespace of the entity to query. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z.string().describe('The name of the entity to query'),\n }),\n // TODO: is there a better way to do this?\n output: z => z.object({}).passthrough(),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n {\n credentials,\n },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const [entity] = items;\n\n return {\n output: entity,\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,+BAA+B,CAAC;AAAA,EAC3C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,oBAAA;AAAA,IACN,KAAA,EAAO,oBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC;AAAA,OAC5D,CAAA;AAAA;AAAA,MAEH,QAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,EAAE,WAAA;AAAY,KACxC;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,CAAC,MAAM,CAAA,GAAI,KAAA;AAEjB,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,OACV;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"createGetCatalogEntityAction.cjs.js","sources":["../../src/actions/createGetCatalogEntityAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createGetCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-entity',\n title: 'Get Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: `\nThis allows you to get a single entity from the software catalog.\nEach entity in the software catalog has a unique name, kind, and namespace. The default namespace is \"default\".\nEach entity is identified by a unique entity reference, which is a string of the form \"kind:namespace/name\".\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .describe(\n 'The kind of the entity to query. If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .describe(\n 'The namespace of the entity to query. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z.string().describe('The name of the entity to query'),\n }),\n // TODO: is there a better way to do this?\n output: z => z.object({}).passthrough(),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n {\n credentials,\n },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const [entity] = items;\n\n return {\n output: entity,\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,+BAA+B,CAAC;AAAA,EAC3C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,oBAAA;AAAA,IACN,KAAA,EAAO,oBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC;AAAA,OAC5D,CAAA;AAAA;AAAA,MAEH,QAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,EAAE,WAAA;AAAY,KACxC;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,CAAC,MAAM,CAAA,GAAI,KAAA;AAEjB,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,OACV;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createGetCatalogModelDescriptionAction.cjs.js","sources":["../../src/actions/createGetCatalogModelDescriptionAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport {\n CatalogModel,\n CatalogModelAnnotationSummary,\n CatalogModelKindSummary,\n CatalogModelLabelSummary,\n CatalogModelRelationSummary,\n CatalogModelTagSummary,\n compileCatalogModel,\n defaultCatalogEntityModel,\n} from '@backstage/catalog-model/alpha';\nimport { ModelHolder } from '../model/ModelHolder';\n\n/**\n * Lets users fetch a markdown formatted description of the catalog model. This\n * is useful for informing LLMs how to properly work with it.\n */\nexport const createGetCatalogModelDescriptionAction = ({\n modelHolder,\n actionsRegistry,\n}: {\n modelHolder: ModelHolder | undefined;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-model-description',\n title: 'Get a Catalog Model Description',\n description:\n 'Returns a markdown formatted description of the current catalog model, including all registered entity kinds, annotations, labels, tags, and relations.',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n schema: {\n input: z => z.object({}),\n output: z =>\n z.object({\n description: z\n .string()\n .describe(\n 'Markdown description of the catalog model including entity kinds, spec fields, and relations.',\n ),\n }),\n },\n action: async () => {\n return {\n output: {\n description: describeCatalogModel(modelHolder?.model),\n },\n };\n },\n });\n};\n\n// Compute the default description once (if needed) and cache it\nlet defaultModelDescription: string | undefined;\n\nfunction describeCatalogModel(model: CatalogModel | undefined): string {\n if (!model) {\n if (!defaultModelDescription) {\n defaultModelDescription = describeModel(\n compileCatalogModel([defaultCatalogEntityModel]),\n );\n }\n return defaultModelDescription;\n }\n\n return describeModel(model);\n}\n\nfunction describeModel(model: CatalogModel): string {\n return `# Catalog Model\n\nThe software catalog contains a few key concepts:\n\n* Entities\n * rich objects with various kinds, with different schemas\n * can have relations between each other\n* Locations\n * registered as type/target pairs\n * typically in the form of URLs that the catalog has been tasked with keeping track of\n\n## Entities\n\nThe catalog contains entities of different kinds. Every entity is an object with\nfields \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\".\n\nWhen querying the catalog you use dot path notation to address fields, e.g. \"metadata.name\".\nWhen querying for entity relationships, prefer using relations over spec fields, e.g. the\nspecial syntax \"relations.ownedBy\" instead of \"spec.owner\".\n\nThe unique identifying key for an entity is its so called entity reference (or entity ref\nfor short), on the form of \"kind:namespace/name\", e.g. \"component:default/my-service\".\nThese are always uniformly lowercased in the \"en-US\" locale.\n\n## Entity Metadata field\n\nThe \"metadata\" object field on all entities has the same static schema. Some common fields\nthere are:\n\n* \"name\": The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair. This value is part of the technical identifier of the entity, and as such it will appear in URLs, database tables, entity references, and similar. It is subject to restrictions regarding what characters are allowed. If you want to use a different, more human readable string with fewer restrictions on it in user interfaces, see the \"title\" field below.\n* \"namespace\" (default value: \"default\"): The namespace that the entity belongs to.\n* \"uid\": A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics.\n* \"etag\": An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value.\n* \"title\": A display name of the entity, to be presented in user interfaces instead of the name property, when available. This field is sometimes useful when the name is cumbersome or ends up being perceived as overly technical. The title generally does not have as stringent format requirements on it, so it may contain special characters and be more explanatory. Do keep it very short though, and avoid situations where a title can be confused with the name of another entity, or where two entities share a title. Note that this is only for display purposes, and may be ignored by some parts of the code. Entity references still always make use of the name property, not the title.\n* \"description\": A short (typically relatively few words, on one line) description of the entity.\n* \"annotations\": A map of annotations on the entity.\n* \"labels\": A map of labels on the entity.\n* \"tags\": A list of tags on the entity.\n* \"links\": A list of links on the entity.\n\n${model.getMetadata().annotations.map(describeAnnotation).join('\\n')}\n${model.getMetadata().labels.map(describeLabel).join('\\n')}\n${model.getMetadata().tags.map(describeTag).join('\\n')}\n\n## Entity Kinds\n\nThe model contains the following entity kinds:\n\n${model.listKinds().map(describeKind).join('\\n')}\n\n## Entity Relations\n\nThe model contains the following entity relations:\n\n${model.listRelations().map(describeRelation).join('\\n')}\n`;\n}\n\nfunction describeKind(kind: CatalogModelKindSummary): string {\n return `### Entity Kind \"${kind.names.kind}\"\n\n* Singular in text: \"${kind.names.singular}\"\n* Plural in text: \"${kind.names.plural}\"\n* Versions:\n${kind.versions\n .map(\n version =>\n ` * apiVersion: \"${version.apiVersion}\"${\n version.specType ? ` (spec.type: \"${version.specType}\")` : ''\n }`,\n )\n .join('\\n')}\n\n${kind.description}\n`;\n}\n\nfunction describeAnnotation(annotation: CatalogModelAnnotationSummary): string {\n const title = annotation.title ? `: ${annotation.title}` : '';\n return `### Annotation \"${annotation.name}\"${title}\n\n${annotation.description}\n`;\n}\n\nfunction describeLabel(label: CatalogModelLabelSummary): string {\n const title = label.title ? `: ${label.title}` : '';\n return `### Label \"${label.name}\"${title}\n\n${label.description}\n`;\n}\n\nfunction describeTag(tag: CatalogModelTagSummary): string {\n const title = tag.title ? `: ${tag.title}` : '';\n return `### Tag \"${tag.name}\"${title}\n\n${tag.description}\n`;\n}\n\nfunction describeRelation(relation: CatalogModelRelationSummary): string {\n return `### Relation \"${relation.forward.type}\": ${relation.forward.title}\n\n* Reverse type: \"${relation.reverse.type}\": ${relation.reverse.title}\n\n${relation.description}\n`;\n}\n"],"names":["compileCatalogModel","defaultCatalogEntityModel"],"mappings":"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"}
1
+ {"version":3,"file":"createGetCatalogModelDescriptionAction.cjs.js","sources":["../../src/actions/createGetCatalogModelDescriptionAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport {\n CatalogModel,\n CatalogModelAnnotationSummary,\n CatalogModelKindSummary,\n CatalogModelLabelSummary,\n CatalogModelRelationSummary,\n CatalogModelTagSummary,\n compileCatalogModel,\n defaultCatalogEntityModel,\n} from '@backstage/catalog-model/alpha';\nimport { ModelHolder } from '../model/ModelHolder';\n\n/**\n * Lets users fetch a markdown formatted description of the catalog model. This\n * is useful for informing LLMs how to properly work with it.\n */\nexport const createGetCatalogModelDescriptionAction = ({\n modelHolder,\n actionsRegistry,\n}: {\n modelHolder: ModelHolder | undefined;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-model-description',\n title: 'Get a Catalog Model Description',\n description:\n 'Returns a markdown formatted description of the current catalog model, including all registered entity kinds, annotations, labels, tags, and relations.',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n schema: {\n input: z => z.object({}),\n output: z =>\n z.object({\n description: z\n .string()\n .describe(\n 'Markdown description of the catalog model including entity kinds, spec fields, and relations.',\n ),\n }),\n },\n action: async () => {\n return {\n output: {\n description: describeCatalogModel(modelHolder?.model),\n },\n };\n },\n });\n};\n\n// Compute the default description once (if needed) and cache it\nlet defaultModelDescription: string | undefined;\n\nfunction describeCatalogModel(model: CatalogModel | undefined): string {\n if (!model) {\n if (!defaultModelDescription) {\n defaultModelDescription = describeModel(\n compileCatalogModel([defaultCatalogEntityModel]),\n );\n }\n return defaultModelDescription;\n }\n\n return describeModel(model);\n}\n\nfunction describeModel(model: CatalogModel): string {\n return `# Catalog Model\n\nThe software catalog contains a few key concepts:\n\n* Entities\n * rich objects with various kinds, with different schemas\n * can have relations between each other\n* Locations\n * registered as type/target pairs\n * typically in the form of URLs that the catalog has been tasked with keeping track of\n\n## Entities\n\nThe catalog contains entities of different kinds. Every entity is an object with\nfields \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\".\n\nWhen querying the catalog you use dot path notation to address fields, e.g. \"metadata.name\".\nWhen querying for entity relationships, prefer using relations over spec fields, e.g. the\nspecial syntax \"relations.ownedBy\" instead of \"spec.owner\".\n\nThe unique identifying key for an entity is its so called entity reference (or entity ref\nfor short), on the form of \"kind:namespace/name\", e.g. \"component:default/my-service\".\nThese are always uniformly lowercased in the \"en-US\" locale.\n\n## Entity Metadata field\n\nThe \"metadata\" object field on all entities has the same static schema. Some common fields\nthere are:\n\n* \"name\": The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair. This value is part of the technical identifier of the entity, and as such it will appear in URLs, database tables, entity references, and similar. It is subject to restrictions regarding what characters are allowed. If you want to use a different, more human readable string with fewer restrictions on it in user interfaces, see the \"title\" field below.\n* \"namespace\" (default value: \"default\"): The namespace that the entity belongs to.\n* \"uid\": A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics.\n* \"etag\": An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value.\n* \"title\": A display name of the entity, to be presented in user interfaces instead of the name property, when available. This field is sometimes useful when the name is cumbersome or ends up being perceived as overly technical. The title generally does not have as stringent format requirements on it, so it may contain special characters and be more explanatory. Do keep it very short though, and avoid situations where a title can be confused with the name of another entity, or where two entities share a title. Note that this is only for display purposes, and may be ignored by some parts of the code. Entity references still always make use of the name property, not the title.\n* \"description\": A short (typically relatively few words, on one line) description of the entity.\n* \"annotations\": A map of annotations on the entity.\n* \"labels\": A map of labels on the entity.\n* \"tags\": A list of tags on the entity.\n* \"links\": A list of links on the entity.\n\n${model.getMetadata().annotations.map(describeAnnotation).join('\\n')}\n${model.getMetadata().labels.map(describeLabel).join('\\n')}\n${model.getMetadata().tags.map(describeTag).join('\\n')}\n\n## Entity Kinds\n\nThe model contains the following entity kinds:\n\n${model.listKinds().map(describeKind).join('\\n')}\n\n## Entity Relations\n\nThe model contains the following entity relations:\n\n${model.listRelations().map(describeRelation).join('\\n')}\n`;\n}\n\nfunction describeKind(kind: CatalogModelKindSummary): string {\n return `### Entity Kind \"${kind.names.kind}\"\n\n* Singular in text: \"${kind.names.singular}\"\n* Plural in text: \"${kind.names.plural}\"\n* Versions:\n${kind.versions\n .map(\n version =>\n ` * apiVersion: \"${version.apiVersion}\"${\n version.specType ? ` (spec.type: \"${version.specType}\")` : ''\n }`,\n )\n .join('\\n')}\n\n${kind.description}\n`;\n}\n\nfunction describeAnnotation(annotation: CatalogModelAnnotationSummary): string {\n const title = annotation.title ? `: ${annotation.title}` : '';\n return `### Annotation \"${annotation.name}\"${title}\n\n${annotation.description}\n`;\n}\n\nfunction describeLabel(label: CatalogModelLabelSummary): string {\n const title = label.title ? `: ${label.title}` : '';\n return `### Label \"${label.name}\"${title}\n\n${label.description}\n`;\n}\n\nfunction describeTag(tag: CatalogModelTagSummary): string {\n const title = tag.title ? `: ${tag.title}` : '';\n return `### Tag \"${tag.name}\"${title}\n\n${tag.description}\n`;\n}\n\nfunction describeRelation(relation: CatalogModelRelationSummary): string {\n return `### Relation \"${relation.forward.type}\": ${relation.forward.title}\n\n* Reverse type: \"${relation.reverse.type}\": ${relation.reverse.title}\n\n${relation.description}\n`;\n}\n"],"names":["compileCatalogModel","defaultCatalogEntityModel"],"mappings":";;;;AAiCO,MAAM,yCAAyC,CAAC;AAAA,EACrD,WAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,+BAAA;AAAA,IACN,KAAA,EAAO,iCAAA;AAAA,IACP,WAAA,EACE,yJAAA;AAAA,IACF,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,CAAO,EAAE,CAAA;AAAA,MACvB,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA;AACF,OACH;AAAA,KACL;AAAA,IACA,QAAQ,YAAY;AAClB,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,WAAA,EAAa,oBAAA,CAAqB,WAAA,EAAa,KAAK;AAAA;AACtD,OACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;AAGA,IAAI,uBAAA;AAEJ,SAAS,qBAAqB,KAAA,EAAyC;AACrE,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,IAAI,CAAC,uBAAA,EAAyB;AAC5B,MAAA,uBAAA,GAA0B,aAAA;AAAA,QACxBA,yBAAA,CAAoB,CAACC,+BAAyB,CAAC;AAAA,OACjD;AAAA,IACF;AACA,IAAA,OAAO,uBAAA;AAAA,EACT;AAEA,EAAA,OAAO,cAAc,KAAK,CAAA;AAC5B;AAEA,SAAS,cAAc,KAAA,EAA6B;AAClD,EAAA,OAAO,CAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA,EAwCP,KAAA,CAAM,aAAY,CAAE,WAAA,CAAY,IAAI,kBAAkB,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC;AAAA,EAClE,KAAA,CAAM,aAAY,CAAE,MAAA,CAAO,IAAI,aAAa,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC;AAAA,EACxD,KAAA,CAAM,aAAY,CAAE,IAAA,CAAK,IAAI,WAAW,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC;;AAAA;;AAAA;;AAAA,EAMpD,KAAA,CAAM,WAAU,CAAE,GAAA,CAAI,YAAY,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC;;AAAA;;AAAA;;AAAA,EAM9C,KAAA,CAAM,eAAc,CAAE,GAAA,CAAI,gBAAgB,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC;AAAA,CAAA;AAExD;AAEA,SAAS,aAAa,IAAA,EAAuC;AAC3D,EAAA,OAAO,CAAA,iBAAA,EAAoB,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;;AAAA,qBAAA,EAErB,IAAA,CAAK,MAAM,QAAQ,CAAA;AAAA,mBAAA,EACrB,IAAA,CAAK,MAAM,MAAM,CAAA;AAAA;AAAA,EAEpC,KAAK,QAAA,CACJ,GAAA;AAAA,IACC,CAAA,OAAA,KACE,CAAA,iBAAA,EAAoB,OAAA,CAAQ,UAAU,CAAA,CAAA,EACpC,OAAA,CAAQ,QAAA,GAAW,CAAA,cAAA,EAAiB,OAAA,CAAQ,QAAQ,CAAA,EAAA,CAAA,GAAO,EAC7D,CAAA;AAAA,GACJ,CACC,IAAA,CAAK,IAAI,CAAC;;AAAA,EAEX,KAAK,WAAW;AAAA,CAAA;AAElB;AAEA,SAAS,mBAAmB,UAAA,EAAmD;AAC7E,EAAA,MAAM,QAAQ,UAAA,CAAW,KAAA,GAAQ,CAAA,EAAA,EAAK,UAAA,CAAW,KAAK,CAAA,CAAA,GAAK,EAAA;AAC3D,EAAA,OAAO,CAAA,gBAAA,EAAmB,UAAA,CAAW,IAAI,CAAA,CAAA,EAAI,KAAK;;AAAA,EAElD,WAAW,WAAW;AAAA,CAAA;AAExB;AAEA,SAAS,cAAc,KAAA,EAAyC;AAC9D,EAAA,MAAM,QAAQ,KAAA,CAAM,KAAA,GAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,KAAK,CAAA,CAAA,GAAK,EAAA;AACjD,EAAA,OAAO,CAAA,WAAA,EAAc,KAAA,CAAM,IAAI,CAAA,CAAA,EAAI,KAAK;;AAAA,EAExC,MAAM,WAAW;AAAA,CAAA;AAEnB;AAEA,SAAS,YAAY,GAAA,EAAqC;AACxD,EAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,GAAQ,CAAA,EAAA,EAAK,GAAA,CAAI,KAAK,CAAA,CAAA,GAAK,EAAA;AAC7C,EAAA,OAAO,CAAA,SAAA,EAAY,GAAA,CAAI,IAAI,CAAA,CAAA,EAAI,KAAK;;AAAA,EAEpC,IAAI,WAAW;AAAA,CAAA;AAEjB;AAEA,SAAS,iBAAiB,QAAA,EAA+C;AACvE,EAAA,OAAO,iBAAiB,QAAA,CAAS,OAAA,CAAQ,IAAI,CAAA,GAAA,EAAM,QAAA,CAAS,QAAQ,KAAK;;AAAA,iBAAA,EAExD,SAAS,OAAA,CAAQ,IAAI,CAAA,GAAA,EAAM,QAAA,CAAS,QAAQ,KAAK;;AAAA,EAElE,SAAS,WAAW;AAAA,CAAA;AAEtB;;"}
@@ -104,43 +104,52 @@ const createQueryCatalogEntitiesAction = ({
104
104
  },
105
105
  description: useExperimentalCatalogLayersDescriptions ? MODEL_REFERENCE_DESCRIPTION : INLINE_MODEL_DESCRIPTION,
106
106
  schema: {
107
- input: (z) => z.object({
108
- query: filterPredicates.createZodV3FilterPredicateSchema(z).optional().describe(
109
- "Entity predicate query. Supports field matching, $all, $any, $not, $exists, $in, $contains, and $hasPrefix operators."
110
- ),
111
- fields: z.array(z.string()).optional().describe(
112
- "Specific fields to include in the response. If not provided, all fields are returned. Each entry is a dot separated path into an entity, e.g. `spec.type`."
113
- ),
114
- limit: z.number().int().positive().optional().describe("Maximum number of entities to return at a time."),
115
- offset: z.number().int().min(0).optional().describe("Number of entities to skip before returning results."),
116
- orderFields: z.union([
117
- z.object({
118
- field: z.string().describe(
119
- "Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`."
120
- ),
121
- order: z.enum(["asc", "desc"]).describe("Sort order")
122
- }),
123
- z.array(
107
+ input: (z) => {
108
+ const numericFromString = (value) => {
109
+ if (typeof value === "string" && value.trim() !== "") {
110
+ const parsed = Number(value);
111
+ return Number.isFinite(parsed) ? parsed : value;
112
+ }
113
+ return value;
114
+ };
115
+ return z.object({
116
+ query: filterPredicates.createZodV3FilterPredicateSchema(z).optional().describe(
117
+ "Entity predicate query. Supports field matching, $all, $any, $not, $exists, $in, $contains, and $hasPrefix operators."
118
+ ),
119
+ fields: z.array(z.string()).optional().describe(
120
+ "Specific fields to include in the response. If not provided, all fields are returned. Each entry is a dot separated path into an entity, e.g. `spec.type`."
121
+ ),
122
+ limit: z.preprocess(numericFromString, z.number().int().positive()).optional().describe("Maximum number of entities to return at a time."),
123
+ offset: z.preprocess(numericFromString, z.number().int().min(0)).optional().describe("Number of entities to skip before returning results."),
124
+ orderFields: z.union([
124
125
  z.object({
125
126
  field: z.string().describe(
126
127
  "Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`."
127
128
  ),
128
129
  order: z.enum(["asc", "desc"]).describe("Sort order")
129
- })
130
- )
131
- ]).optional().describe(
132
- "Ordering criteria for the results. Can be a single order directive or an array for multi-field sorting."
133
- ),
134
- fullTextFilter: z.object({
135
- term: z.string().describe("Full text search term"),
136
- fields: z.array(z.string()).optional().describe(
137
- "Fields to search within. Each entry is a dot separated path into an entity, e.g. `spec.type`."
130
+ }),
131
+ z.array(
132
+ z.object({
133
+ field: z.string().describe(
134
+ "Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`."
135
+ ),
136
+ order: z.enum(["asc", "desc"]).describe("Sort order")
137
+ })
138
+ )
139
+ ]).optional().describe(
140
+ "Ordering criteria for the results. Can be a single order directive or an array for multi-field sorting."
141
+ ),
142
+ fullTextFilter: z.object({
143
+ term: z.string().describe("Full text search term"),
144
+ fields: z.array(z.string()).optional().describe(
145
+ "Fields to search within. Each entry is a dot separated path into an entity, e.g. `spec.type`."
146
+ )
147
+ }).optional().describe("Full text search criteria"),
148
+ cursor: z.string().optional().describe(
149
+ "Cursor for pagination. This can be used only after the first request with a response containing a cursor. If a cursor is given it takes precedence over `offset`."
138
150
  )
139
- }).optional().describe("Full text search criteria"),
140
- cursor: z.string().optional().describe(
141
- "Cursor for pagination. This can be used only after the first request with a response containing a cursor. If a cursor is given it takes precedence over `offset`."
142
- )
143
- }),
151
+ });
152
+ },
144
153
  output: (z) => z.object({
145
154
  items: z.array(z.object({}).passthrough()).describe("List of entities"),
146
155
  totalItems: z.number().describe("Total number of entities"),
@@ -1 +1 @@
1
- {"version":3,"file":"createQueryCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createQueryCatalogEntitiesAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { createZodV3FilterPredicateSchema } from '@backstage/filter-predicates';\n\nconst QUERY_SYNTAX = `\n## Query Syntax\n\nThe query uses predicate expressions with dot-notation field paths.\n\nSimple matching:\n { query: { kind: \"Component\" } }\n { query: { kind: \"Component\", \"spec.type\": \"service\" } }\n\nValue operators:\n { query: { kind: { \"$in\": [\"API\", \"Component\"] } } }\n { query: { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } } }\n { query: { \"metadata.tags\": { \"$contains\": \"java\" } } }\n { query: { \"metadata.name\": { \"$hasPrefix\": \"team-\" } } }\n\nLogical operators:\n { query: { \"$all\": [{ kind: \"Component\" }, { \"spec.lifecycle\": \"production\" }] } }\n { query: { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] } }\n { query: { \"$not\": { kind: \"Group\" } } }\n\nQuerying relations - find all entities owned by a specific group:\n { query: { \"relations.ownedby\": \"group:default/team-alpha\" } }\n\nCombined example - find production services or websites with TechDocs:\n { query: { \"$all\": [\n { kind: \"Component\", \"spec.lifecycle\": \"production\" },\n { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] },\n { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } }\n ] } }\n\n## Other Options\n\nLimit returned fields: { fields: [\"kind\", \"metadata.name\", \"metadata.namespace\"] }\nSort results: { orderFields: { field: \"metadata.name\", order: \"asc\" } }\nFull text search: { fullTextFilter: { term: \"auth\", fields: [\"metadata.name\", \"metadata.title\"] } }\nPagination: Use limit (e.g. 20) and the returned nextPageCursor for subsequent requests via cursor.\n`;\n\nconst INLINE_MODEL_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\n## Catalog Model\n\nThe catalog contains entities of different kinds. Every entity has \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\". Fields use dot notation for querying.\n\nCommon metadata fields on all entities: name, namespace (default: \"default\"), title, description, labels, annotations, tags (string array), links.\n\nEntity references use the format \"kind:namespace/name\", e.g. \"component:default/my-service\" or \"user:default/jane.doe\".\n\n### Entity Kinds\n\n**Component** - A piece of software such as a service, website, or library.\n spec fields: type (e.g. \"service\", \"website\", \"library\"), lifecycle (e.g. \"production\", \"experimental\", \"deprecated\"), owner (entity ref), system, subcomponentOf, providesApis, consumesApis, dependsOn, dependencyOf.\n\n**API** - An interface that components expose, such as REST APIs or event streams.\n spec fields: type (e.g. \"openapi\", \"asyncapi\", \"graphql\", \"grpc\"), lifecycle, owner (entity ref), definition (the API spec content), system.\n\n**System** - A collection of components, APIs, and resources that together expose some functionality.\n spec fields: owner (entity ref), domain, type.\n\n**Domain** - A grouping of systems that share terminology, domain models, and business purpose.\n spec fields: owner (entity ref), subdomainOf, type.\n\n**Resource** - Infrastructure required to operate a component, such as databases or storage buckets.\n spec fields: type, owner (entity ref), system, dependsOn, dependencyOf.\n\n**Group** - An organizational entity such as a team or business unit.\n spec fields: type (e.g. \"team\", \"business-unit\"), children (entity refs), parent (entity ref), members (entity refs), profile (displayName, email, picture).\n\n**User** - A person, such as an employee or contractor.\n spec fields: memberOf (entity refs), profile (displayName, email, picture).\n\n**Location** - A marker that references other catalog descriptor files to be ingested.\n spec fields: type, target, targets, presence.\n\n### Relations\n\nEntities have bidirectional relations stored in the \"relations\" array. Common relation types: ownedBy/ownerOf, dependsOn/dependencyOf, providesApi/apiProvidedBy, consumesApi/apiConsumedBy, parentOf/childOf, memberOf/hasMember, partOf/hasPart.\n\nRelations can be queried via \"relations.<type>\" e.g. \"relations.ownedby: user:default/jane-doe\". The value there must always be a valid entity reference.\n\nWhen querying for entity relationships, prefer using relations over spec fields. For example, use \"relations.ownedby\" instead of \"spec.owner\" to find entities owned by a particular group or user.\n${QUERY_SYNTAX}`;\n\nconst MODEL_REFERENCE_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\nFor a complete list of entity kinds, fields, relations, and other queryable attributes available in the catalog, use \\`get-catalog-model-description\\`.\n${QUERY_SYNTAX}`;\n\nexport const createQueryCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n useExperimentalCatalogLayersDescriptions,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n actionsRegistry.register({\n name: 'query-catalog-entities',\n title: 'Query Catalog Entities',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: useExperimentalCatalogLayersDescriptions\n ? MODEL_REFERENCE_DESCRIPTION\n : INLINE_MODEL_DESCRIPTION,\n schema: {\n input: z =>\n z.object({\n query: createZodV3FilterPredicateSchema(z)\n .optional()\n .describe(\n 'Entity predicate query. Supports field matching, $all, $any, $not, $exists, $in, $contains, and $hasPrefix operators.',\n ),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Specific fields to include in the response. If not provided, all fields are returned. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n limit: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Maximum number of entities to return at a time.'),\n offset: z\n .number()\n .int()\n .min(0)\n .optional()\n .describe('Number of entities to skip before returning results.'),\n orderFields: z\n .union([\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n z.array(\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n ),\n ])\n .optional()\n .describe(\n 'Ordering criteria for the results. Can be a single order directive or an array for multi-field sorting.',\n ),\n fullTextFilter: z\n .object({\n term: z.string().describe('Full text search term'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Fields to search within. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n })\n .optional()\n .describe('Full text search criteria'),\n cursor: z\n .string()\n .optional()\n .describe(\n 'Cursor for pagination. This can be used only after the first request with a response containing a cursor. If a cursor is given it takes precedence over `offset`.',\n ),\n }),\n output: z =>\n z.object({\n items: z\n .array(z.object({}).passthrough())\n .describe('List of entities'),\n totalItems: z.number().describe('Total number of entities'),\n hasMoreEntities: z\n .boolean()\n .describe('Whether more entities are available'),\n nextPageCursor: z\n .string()\n .optional()\n .describe('Next page cursor used to fetch next page of entities'),\n }),\n },\n action: async ({ input, credentials }) => {\n const response = await catalog.queryEntities(\n {\n ...input,\n query: input.query,\n },\n { credentials },\n );\n\n return {\n output: {\n items: response.items,\n totalItems: response.totalItems,\n hasMoreEntities: !!response.pageInfo.nextCursor,\n nextPageCursor: response.pageInfo.nextCursor,\n },\n };\n },\n });\n};\n"],"names":["createZodV3FilterPredicateSchema"],"mappings":"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"}
1
+ {"version":3,"file":"createQueryCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createQueryCatalogEntitiesAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { createZodV3FilterPredicateSchema } from '@backstage/filter-predicates';\n\nconst QUERY_SYNTAX = `\n## Query Syntax\n\nThe query uses predicate expressions with dot-notation field paths.\n\nSimple matching:\n { query: { kind: \"Component\" } }\n { query: { kind: \"Component\", \"spec.type\": \"service\" } }\n\nValue operators:\n { query: { kind: { \"$in\": [\"API\", \"Component\"] } } }\n { query: { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } } }\n { query: { \"metadata.tags\": { \"$contains\": \"java\" } } }\n { query: { \"metadata.name\": { \"$hasPrefix\": \"team-\" } } }\n\nLogical operators:\n { query: { \"$all\": [{ kind: \"Component\" }, { \"spec.lifecycle\": \"production\" }] } }\n { query: { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] } }\n { query: { \"$not\": { kind: \"Group\" } } }\n\nQuerying relations - find all entities owned by a specific group:\n { query: { \"relations.ownedby\": \"group:default/team-alpha\" } }\n\nCombined example - find production services or websites with TechDocs:\n { query: { \"$all\": [\n { kind: \"Component\", \"spec.lifecycle\": \"production\" },\n { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] },\n { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } }\n ] } }\n\n## Other Options\n\nLimit returned fields: { fields: [\"kind\", \"metadata.name\", \"metadata.namespace\"] }\nSort results: { orderFields: { field: \"metadata.name\", order: \"asc\" } }\nFull text search: { fullTextFilter: { term: \"auth\", fields: [\"metadata.name\", \"metadata.title\"] } }\nPagination: Use limit (e.g. 20) and the returned nextPageCursor for subsequent requests via cursor.\n`;\n\nconst INLINE_MODEL_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\n## Catalog Model\n\nThe catalog contains entities of different kinds. Every entity has \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\". Fields use dot notation for querying.\n\nCommon metadata fields on all entities: name, namespace (default: \"default\"), title, description, labels, annotations, tags (string array), links.\n\nEntity references use the format \"kind:namespace/name\", e.g. \"component:default/my-service\" or \"user:default/jane.doe\".\n\n### Entity Kinds\n\n**Component** - A piece of software such as a service, website, or library.\n spec fields: type (e.g. \"service\", \"website\", \"library\"), lifecycle (e.g. \"production\", \"experimental\", \"deprecated\"), owner (entity ref), system, subcomponentOf, providesApis, consumesApis, dependsOn, dependencyOf.\n\n**API** - An interface that components expose, such as REST APIs or event streams.\n spec fields: type (e.g. \"openapi\", \"asyncapi\", \"graphql\", \"grpc\"), lifecycle, owner (entity ref), definition (the API spec content), system.\n\n**System** - A collection of components, APIs, and resources that together expose some functionality.\n spec fields: owner (entity ref), domain, type.\n\n**Domain** - A grouping of systems that share terminology, domain models, and business purpose.\n spec fields: owner (entity ref), subdomainOf, type.\n\n**Resource** - Infrastructure required to operate a component, such as databases or storage buckets.\n spec fields: type, owner (entity ref), system, dependsOn, dependencyOf.\n\n**Group** - An organizational entity such as a team or business unit.\n spec fields: type (e.g. \"team\", \"business-unit\"), children (entity refs), parent (entity ref), members (entity refs), profile (displayName, email, picture).\n\n**User** - A person, such as an employee or contractor.\n spec fields: memberOf (entity refs), profile (displayName, email, picture).\n\n**Location** - A marker that references other catalog descriptor files to be ingested.\n spec fields: type, target, targets, presence.\n\n### Relations\n\nEntities have bidirectional relations stored in the \"relations\" array. Common relation types: ownedBy/ownerOf, dependsOn/dependencyOf, providesApi/apiProvidedBy, consumesApi/apiConsumedBy, parentOf/childOf, memberOf/hasMember, partOf/hasPart.\n\nRelations can be queried via \"relations.<type>\" e.g. \"relations.ownedby: user:default/jane-doe\". The value there must always be a valid entity reference.\n\nWhen querying for entity relationships, prefer using relations over spec fields. For example, use \"relations.ownedby\" instead of \"spec.owner\" to find entities owned by a particular group or user.\n${QUERY_SYNTAX}`;\n\nconst MODEL_REFERENCE_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\nFor a complete list of entity kinds, fields, relations, and other queryable attributes available in the catalog, use \\`get-catalog-model-description\\`.\n${QUERY_SYNTAX}`;\n\nexport const createQueryCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n useExperimentalCatalogLayersDescriptions,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n actionsRegistry.register({\n name: 'query-catalog-entities',\n title: 'Query Catalog Entities',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: useExperimentalCatalogLayersDescriptions\n ? MODEL_REFERENCE_DESCRIPTION\n : INLINE_MODEL_DESCRIPTION,\n schema: {\n input: z => {\n // MCP/LLM clients often send numeric arguments as strings. Convert numeric\n // strings to numbers, but leave everything else untouched so Zod rejects the\n // original value (e.g. reports \"received string\" for 'abc' rather than\n // \"received NaN\", and doesn't silently turn true/null/'' into 1/0/0).\n const numericFromString = (value: unknown) => {\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : value;\n }\n return value;\n };\n\n return z.object({\n query: createZodV3FilterPredicateSchema(z)\n .optional()\n .describe(\n 'Entity predicate query. Supports field matching, $all, $any, $not, $exists, $in, $contains, and $hasPrefix operators.',\n ),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Specific fields to include in the response. If not provided, all fields are returned. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n limit: z\n .preprocess(numericFromString, z.number().int().positive())\n .optional()\n .describe('Maximum number of entities to return at a time.'),\n offset: z\n .preprocess(numericFromString, z.number().int().min(0))\n .optional()\n .describe('Number of entities to skip before returning results.'),\n orderFields: z\n .union([\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n z.array(\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n ),\n ])\n .optional()\n .describe(\n 'Ordering criteria for the results. Can be a single order directive or an array for multi-field sorting.',\n ),\n fullTextFilter: z\n .object({\n term: z.string().describe('Full text search term'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Fields to search within. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n })\n .optional()\n .describe('Full text search criteria'),\n cursor: z\n .string()\n .optional()\n .describe(\n 'Cursor for pagination. This can be used only after the first request with a response containing a cursor. If a cursor is given it takes precedence over `offset`.',\n ),\n });\n },\n output: z =>\n z.object({\n items: z\n .array(z.object({}).passthrough())\n .describe('List of entities'),\n totalItems: z.number().describe('Total number of entities'),\n hasMoreEntities: z\n .boolean()\n .describe('Whether more entities are available'),\n nextPageCursor: z\n .string()\n .optional()\n .describe('Next page cursor used to fetch next page of entities'),\n }),\n },\n action: async ({ input, credentials }) => {\n const response = await catalog.queryEntities(\n {\n ...input,\n query: input.query,\n },\n { credentials },\n );\n\n return {\n output: {\n items: response.items,\n totalItems: response.totalItems,\n hasMoreEntities: !!response.pageInfo.nextCursor,\n nextPageCursor: response.pageInfo.nextCursor,\n },\n };\n },\n });\n};\n"],"names":["createZodV3FilterPredicateSchema"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"createRefreshCatalogEntityAction.cjs.js","sources":["../../src/actions/createRefreshCatalogEntityAction.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 type { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport type { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createRefreshCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'refresh-catalog-entity',\n title: 'Refresh Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: true,\n },\n description: `\nThis allows you to trigger a refresh of a single entity in the software catalog, requeuing it for processing.\nThis is useful immediately after creating or updating an entity (for example via a scaffolder template invoked by an MCP client) so the new data becomes visible before subsequent actions read it.\nEach entity in the software catalog has a unique name, kind, and namespace. If the name alone matches multiple entities, provide the kind (and namespace) to disambiguate.\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .min(1)\n .describe(\n 'The kind of the entity to refresh, e.g. \"Component\", \"API\", \"System\". If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .min(1)\n .describe(\n 'The namespace of the entity to refresh. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z\n .string()\n .trim()\n .min(1)\n .describe('The name of the entity to refresh.'),\n }),\n output: z =>\n z.object({\n entityRef: z\n .string()\n .describe('The canonical entity reference that was refreshed.'),\n }),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n { credentials },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const entityRef = stringifyEntityRef(items[0]);\n\n await catalog.refreshEntity(entityRef, { credentials });\n\n return {\n output: { entityRef },\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,mCAAmC,CAAC;AAAA,EAC/C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,wBAAA;AAAA,IACN,KAAA,EAAO,wBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAM,CAAA,CACH,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,WAAW,CAAA,CACR,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,IAAA,GACA,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,CAAS,oCAAoC;AAAA,OACjD,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,SAAS,oDAAoD;AAAA,OACjE;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT,EAAE,WAAA;AAAY,OAChB;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAYA,+BAAA,CAAmB,KAAA,CAAM,CAAC,CAAC,CAAA;AAE7C,MAAA,MAAM,OAAA,CAAQ,aAAA,CAAc,SAAA,EAAW,EAAE,aAAa,CAAA;AAEtD,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,EAAE,SAAA;AAAU,OACtB;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"createRefreshCatalogEntityAction.cjs.js","sources":["../../src/actions/createRefreshCatalogEntityAction.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 type { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport type { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createRefreshCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'refresh-catalog-entity',\n title: 'Refresh Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: true,\n },\n description: `\nThis allows you to trigger a refresh of a single entity in the software catalog, requeuing it for processing.\nThis is useful immediately after creating or updating an entity (for example via a scaffolder template invoked by an MCP client) so the new data becomes visible before subsequent actions read it.\nEach entity in the software catalog has a unique name, kind, and namespace. If the name alone matches multiple entities, provide the kind (and namespace) to disambiguate.\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .min(1)\n .describe(\n 'The kind of the entity to refresh, e.g. \"Component\", \"API\", \"System\". If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .min(1)\n .describe(\n 'The namespace of the entity to refresh. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z\n .string()\n .trim()\n .min(1)\n .describe('The name of the entity to refresh.'),\n }),\n output: z =>\n z.object({\n entityRef: z\n .string()\n .describe('The canonical entity reference that was refreshed.'),\n }),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n { credentials },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const entityRef = stringifyEntityRef(items[0]);\n\n await catalog.refreshEntity(entityRef, { credentials });\n\n return {\n output: { entityRef },\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,mCAAmC,CAAC;AAAA,EAC/C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,wBAAA;AAAA,IACN,KAAA,EAAO,wBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAM,CAAA,CACH,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,WAAW,CAAA,CACR,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,IAAA,GACA,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,CAAS,oCAAoC;AAAA,OACjD,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,SAAS,oDAAoD;AAAA,OACjE;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT,EAAE,WAAA;AAAY,OAChB;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAYA,+BAAA,CAAmB,KAAA,CAAM,CAAC,CAAC,CAAA;AAE7C,MAAA,MAAM,OAAA,CAAQ,aAAA,CAAc,SAAA,EAAW,EAAE,aAAa,CAAA;AAEtD,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,EAAE,SAAA;AAAU,OACtB;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createRegisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createRegisterCatalogEntitiesAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nconst isValidUrl = (url: string) => {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch (error) {\n return false;\n }\n};\n\nexport const createRegisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'register-entity',\n title: 'Register entity in the Catalog',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: false,\n },\n description: `Registers one or more entities in the Backstage catalog by creating a Location entity that points to a remote catalog-info.yaml file.\n\nThis action is similar to the \"Register existing component\" flow in the Backstage UI, where you provide a URL to a catalog-info.yaml file describing catalog entities such as Components, Systems, Resources, APIs, Users, and Groups. The action will create a new Location entity that references the provided file; all entities defined within that file will be imported into the catalog.\n\nA unique identifier (locationId) will be returned for the newly created Location. You can use this identifier in the future to unregister or delete the Location and all entities it owns.\n`,\n schema: {\n input: z =>\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL reference to the catalog-info.yaml file that describes the entity to register. For example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n output: z =>\n z.object({\n locationId: z\n .string()\n .describe('The ID of the entity that was registered'),\n }),\n },\n action: async ({ input, credentials }) => {\n if (!isValidUrl(input.locationUrl)) {\n throw new InputError(`${input.locationUrl} is an invalid URL`);\n }\n\n const result = await catalog.addLocation(\n {\n type: 'url',\n target: input.locationUrl,\n },\n {\n credentials,\n },\n );\n\n return {\n output: {\n locationId: result.location.id,\n },\n };\n },\n });\n};\n"],"names":["InputError"],"mappings":";;;;AAmBA,MAAM,UAAA,GAAa,CAAC,GAAA,KAAgB;AAClC,EAAA,IAAI;AAEF,IAAA,IAAI,IAAI,GAAG,CAAA;AACX,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACF,CAAA;AAEO,MAAM,sCAAsC,CAAC;AAAA,EAClD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,gCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,UACC,CAAA,+JAAA;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,0CAA0C;AAAA,OACvD;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,IAAI,CAAC,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,EAAG;AAClC,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,EAAG,KAAA,CAAM,WAAW,CAAA,kBAAA,CAAoB,CAAA;AAAA,MAC/D;AAEA,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,WAAA;AAAA,QAC3B;AAAA,UACE,IAAA,EAAM,KAAA;AAAA,UACN,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,UAAA,EAAY,OAAO,QAAA,CAAS;AAAA;AAC9B,OACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"createRegisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createRegisterCatalogEntitiesAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nconst isValidUrl = (url: string) => {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch (error) {\n return false;\n }\n};\n\nexport const createRegisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'register-entity',\n title: 'Register entity in the Catalog',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: false,\n },\n description: `Registers one or more entities in the Backstage catalog by creating a Location entity that points to a remote catalog-info.yaml file.\n\nThis action is similar to the \"Register existing component\" flow in the Backstage UI, where you provide a URL to a catalog-info.yaml file describing catalog entities such as Components, Systems, Resources, APIs, Users, and Groups. The action will create a new Location entity that references the provided file; all entities defined within that file will be imported into the catalog.\n\nA unique identifier (locationId) will be returned for the newly created Location. You can use this identifier in the future to unregister or delete the Location and all entities it owns.\n`,\n schema: {\n input: z =>\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL reference to the catalog-info.yaml file that describes the entity to register. For example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n output: z =>\n z.object({\n locationId: z\n .string()\n .describe('The ID of the entity that was registered'),\n }),\n },\n action: async ({ input, credentials }) => {\n if (!isValidUrl(input.locationUrl)) {\n throw new InputError(`${input.locationUrl} is an invalid URL`);\n }\n\n const result = await catalog.addLocation(\n {\n type: 'url',\n target: input.locationUrl,\n },\n {\n credentials,\n },\n );\n\n return {\n output: {\n locationId: result.location.id,\n },\n };\n },\n });\n};\n"],"names":["InputError"],"mappings":";;;;AAmBA,MAAM,UAAA,GAAa,CAAC,GAAA,KAAgB;AAClC,EAAA,IAAI;AAEF,IAAA,IAAI,IAAI,GAAG,CAAA;AACX,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACF,CAAA;AAEO,MAAM,sCAAsC,CAAC;AAAA,EAClD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,gCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,UACC,CAAA,+JAAA;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,0CAA0C;AAAA,OACvD;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,IAAI,CAAC,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,EAAG;AAClC,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,EAAG,KAAA,CAAM,WAAW,CAAA,kBAAA,CAAoB,CAAA;AAAA,MAC/D;AAEA,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,WAAA;AAAA,QAC3B;AAAA,UACE,IAAA,EAAM,KAAA;AAAA,UACN,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,UAAA,EAAY,OAAO,QAAA,CAAS;AAAA;AAC9B,OACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createUnregisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createUnregisterCatalogEntitiesAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { NotFoundError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createUnregisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'unregister-entity',\n title: 'Unregister entity from the Catalog',\n attributes: {\n destructive: true,\n readOnly: false,\n idempotent: true,\n },\n description: `Unregisters a Location entity and all entities it owns from the Backstage catalog.\n\nThis action is similar to the \"Unregister location\" function in the Backstage UI, where you provide the unique identifier (locationId) of a Location entity. Alternatively, you can provide the URL used to register the location. The action will remove the specified Location from the catalog as well as all entities that were created when the Location was imported.\n\nOnce completed, all entities associated with the Location will be deleted from the catalog.\n`,\n schema: {\n input: z =>\n z.object({\n type: z\n .union([\n z.object({\n locationId: z\n .string()\n .describe(`Location ID of the Entity to unregister`),\n }),\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL of the catalog-info.yaml file to unregister for example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n ])\n .describe(\n 'Identifies the entity to unregister. Provide either locationId or locationUrl.',\n ),\n }),\n output: z => z.object({}),\n },\n action: async ({ input: { type }, credentials }) => {\n if ('locationId' in type) {\n await catalog.removeLocationById(type.locationId, {\n credentials,\n });\n } else {\n const locations = await catalog\n .getLocations(\n {},\n {\n credentials,\n },\n )\n .then(response =>\n response.items.filter(\n location =>\n location.target.toLowerCase() ===\n type.locationUrl.toLowerCase(),\n ),\n );\n\n if (locations.length === 0) {\n throw new NotFoundError(\n `Location with URL ${type.locationUrl} not found`,\n );\n }\n\n for (const location of locations) {\n await catalog.removeLocationById(location.id, {\n credentials,\n });\n }\n }\n\n return { output: {} };\n },\n });\n};\n"],"names":["NotFoundError"],"mappings":";;;;AAmBO,MAAM,wCAAwC,CAAC;AAAA,EACpD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,mBAAA;AAAA,IACN,KAAA,EAAO,oCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,IAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,EACH,KAAA,CAAM;AAAA,UACL,EAAE,MAAA,CAAO;AAAA,YACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,CAAA,uCAAA,CAAyC;AAAA,WACtD,CAAA;AAAA,UACD,EAAE,MAAA,CAAO;AAAA,YACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,cACC,CAAA,4HAAA;AAAA;AACF,WACH;AAAA,SACF,CAAA,CACA,QAAA;AAAA,UACC;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,CAAO,EAAE;AAAA,KAC1B;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAK,EAAG,aAAY,KAAM;AAClD,MAAA,IAAI,gBAAgB,IAAA,EAAM;AACxB,QAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,IAAA,CAAK,UAAA,EAAY;AAAA,UAChD;AAAA,SACD,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,MAAM,SAAA,GAAY,MAAM,OAAA,CACrB,YAAA;AAAA,UACC,EAAC;AAAA,UACD;AAAA,YACE;AAAA;AACF,SACF,CACC,IAAA;AAAA,UAAK,CAAA,QAAA,KACJ,SAAS,KAAA,CAAM,MAAA;AAAA,YACb,cACE,QAAA,CAAS,MAAA,CAAO,aAAY,KAC5B,IAAA,CAAK,YAAY,WAAA;AAAY;AACjC,SACF;AAEF,QAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,UAAA,MAAM,IAAIA,oBAAA;AAAA,YACR,CAAA,kBAAA,EAAqB,KAAK,WAAW,CAAA,UAAA;AAAA,WACvC;AAAA,QACF;AAEA,QAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,UAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,QAAA,CAAS,EAAA,EAAI;AAAA,YAC5C;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAE;AAAA,IACtB;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"createUnregisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createUnregisterCatalogEntitiesAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { NotFoundError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createUnregisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'unregister-entity',\n title: 'Unregister entity from the Catalog',\n attributes: {\n destructive: true,\n readOnly: false,\n idempotent: true,\n },\n description: `Unregisters a Location entity and all entities it owns from the Backstage catalog.\n\nThis action is similar to the \"Unregister location\" function in the Backstage UI, where you provide the unique identifier (locationId) of a Location entity. Alternatively, you can provide the URL used to register the location. The action will remove the specified Location from the catalog as well as all entities that were created when the Location was imported.\n\nOnce completed, all entities associated with the Location will be deleted from the catalog.\n`,\n schema: {\n input: z =>\n z.object({\n type: z\n .union([\n z.object({\n locationId: z\n .string()\n .describe(`Location ID of the Entity to unregister`),\n }),\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL of the catalog-info.yaml file to unregister for example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n ])\n .describe(\n 'Identifies the entity to unregister. Provide either locationId or locationUrl.',\n ),\n }),\n output: z => z.object({}),\n },\n action: async ({ input: { type }, credentials }) => {\n if ('locationId' in type) {\n await catalog.removeLocationById(type.locationId, {\n credentials,\n });\n } else {\n const locations = await catalog\n .getLocations(\n {},\n {\n credentials,\n },\n )\n .then(response =>\n response.items.filter(\n location =>\n location.target.toLowerCase() ===\n type.locationUrl.toLowerCase(),\n ),\n );\n\n if (locations.length === 0) {\n throw new NotFoundError(\n `Location with URL ${type.locationUrl} not found`,\n );\n }\n\n for (const location of locations) {\n await catalog.removeLocationById(location.id, {\n credentials,\n });\n }\n }\n\n return { output: {} };\n },\n });\n};\n"],"names":["NotFoundError"],"mappings":";;;;AAmBO,MAAM,wCAAwC,CAAC;AAAA,EACpD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,mBAAA;AAAA,IACN,KAAA,EAAO,oCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,IAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,EACH,KAAA,CAAM;AAAA,UACL,EAAE,MAAA,CAAO;AAAA,YACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,CAAA,uCAAA,CAAyC;AAAA,WACtD,CAAA;AAAA,UACD,EAAE,MAAA,CAAO;AAAA,YACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,cACC,CAAA,4HAAA;AAAA;AACF,WACH;AAAA,SACF,CAAA,CACA,QAAA;AAAA,UACC;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,CAAO,EAAE;AAAA,KAC1B;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAK,EAAG,aAAY,KAAM;AAClD,MAAA,IAAI,gBAAgB,IAAA,EAAM;AACxB,QAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,IAAA,CAAK,UAAA,EAAY;AAAA,UAChD;AAAA,SACD,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,MAAM,SAAA,GAAY,MAAM,OAAA,CACrB,YAAA;AAAA,UACC,EAAC;AAAA,UACD;AAAA,YACE;AAAA;AACF,SACF,CACC,IAAA;AAAA,UAAK,CAAA,QAAA,KACJ,SAAS,KAAA,CAAM,MAAA;AAAA,YACb,cACE,QAAA,CAAS,MAAA,CAAO,aAAY,KAC5B,IAAA,CAAK,YAAY,WAAA;AAAY;AACjC,SACF;AAEF,QAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,UAAA,MAAM,IAAIA,oBAAA;AAAA,YACR,CAAA,kBAAA,EAAqB,KAAK,WAAW,CAAA,UAAA;AAAA,WACvC;AAAA,QACF;AAEA,QAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,UAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,QAAA,CAAS,EAAA,EAAI;AAAA,YAC5C;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAE;AAAA,IACtB;AAAA,GACD,CAAA;AACH;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createValidateEntityAction.cjs.js","sources":["../../src/actions/createValidateEntityAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport yaml from 'yaml';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createValidateEntityAction = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n}) => {\n const { actionsRegistry, catalog } = options;\n actionsRegistry.register({\n name: 'validate-entity',\n title: 'Validate Catalog Entity',\n description: `\n This action can be used to validate catalog-info.yaml file contents meant to be used with the software catalog.\n It checks both that the YAML syntax is correct, and that the entity content is valid according to the catalog's rules.\n `,\n attributes: {\n readOnly: true,\n idempotent: true,\n destructive: false,\n },\n schema: {\n input: z =>\n z.object({\n entity: z.string().describe('Entity YAML content to validate'),\n location: z\n .string()\n .url()\n .optional()\n .describe('Location to validate'),\n }),\n output: z =>\n z.object({\n isValid: z.boolean().describe('Whether the entity is valid'),\n isValidYaml: z.boolean().describe('Whether the YAML syntax is valid'),\n errors: z.array(z.string()).describe('Array of validation errors'),\n entity: z\n .record(z.any())\n .optional()\n .describe('Parsed entity object if valid'),\n }),\n },\n action: async ({ input, credentials }) => {\n const { entity: content, location } = input;\n try {\n let entity;\n try {\n entity = yaml.parse(content);\n } catch (yamlError) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`YAML parsing error: ${yamlError.message}`],\n },\n };\n }\n\n const resp = await catalog.validateEntity(\n entity,\n location ?? 'url:https://localhost/entity-validator',\n { credentials },\n );\n\n return {\n output: {\n isValid: resp.valid,\n isValidYaml: true,\n errors: resp.valid ? [] : resp.errors.map(e => e.message),\n entity: resp.valid ? entity : undefined,\n },\n };\n } catch (error) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`Validation error: ${error.message}`],\n },\n };\n }\n },\n });\n};\n"],"names":["yaml"],"mappings":";;;;;;;;AAmBO,MAAM,0BAAA,GAA6B,CAAC,OAAA,KAGrC;AACJ,EAAA,MAAM,EAAE,eAAA,EAAiB,OAAA,EAAQ,GAAI,OAAA;AACrC,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,yBAAA;AAAA,IACP,WAAA,EAAa;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,IAIb,UAAA,EAAY;AAAA,MACV,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY,IAAA;AAAA,MACZ,WAAA,EAAa;AAAA,KACf;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC,CAAA;AAAA,QAC7D,QAAA,EAAU,EACP,MAAA,EAAO,CACP,KAAI,CACJ,QAAA,EAAS,CACT,QAAA,CAAS,sBAAsB;AAAA,OACnC,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,OAAA,EAAS,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,6BAA6B,CAAA;AAAA,QAC3D,WAAA,EAAa,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,kCAAkC,CAAA;AAAA,QACpE,MAAA,EAAQ,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,4BAA4B,CAAA;AAAA,QACjE,MAAA,EAAQ,CAAA,CACL,MAAA,CAAO,CAAA,CAAE,GAAA,EAAK,CAAA,CACd,QAAA,EAAS,CACT,QAAA,CAAS,+BAA+B;AAAA,OAC5C;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAS,GAAI,KAAA;AACtC,MAAA,IAAI;AACF,QAAA,IAAI,MAAA;AACJ,QAAA,IAAI;AACF,UAAA,MAAA,GAASA,qBAAA,CAAK,MAAM,OAAO,CAAA;AAAA,QAC7B,SAAS,SAAA,EAAW;AAClB,UAAA,OAAO;AAAA,YACL,MAAA,EAAQ;AAAA,cACN,OAAA,EAAS,KAAA;AAAA,cACT,WAAA,EAAa,KAAA;AAAA,cACb,MAAA,EAAQ,CAAC,CAAA,oBAAA,EAAuB,SAAA,CAAU,OAAO,CAAA,CAAE;AAAA;AACrD,WACF;AAAA,QACF;AAEA,QAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,cAAA;AAAA,UACzB,MAAA;AAAA,UACA,QAAA,IAAY,wCAAA;AAAA,UACZ,EAAE,WAAA;AAAY,SAChB;AAEA,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,SAAS,IAAA,CAAK,KAAA;AAAA,YACd,WAAA,EAAa,IAAA;AAAA,YACb,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,EAAC,GAAI,KAAK,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,CAAA;AAAA,YACxD,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,MAAA,GAAS,KAAA;AAAA;AAChC,SACF;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,OAAA,EAAS,KAAA;AAAA,YACT,WAAA,EAAa,KAAA;AAAA,YACb,MAAA,EAAQ,CAAC,CAAA,kBAAA,EAAqB,KAAA,CAAM,OAAO,CAAA,CAAE;AAAA;AAC/C,SACF;AAAA,MACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"createValidateEntityAction.cjs.js","sources":["../../src/actions/createValidateEntityAction.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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport yaml from 'yaml';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createValidateEntityAction = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n}) => {\n const { actionsRegistry, catalog } = options;\n actionsRegistry.register({\n name: 'validate-entity',\n title: 'Validate Catalog Entity',\n description: `\n This action can be used to validate catalog-info.yaml file contents meant to be used with the software catalog.\n It checks both that the YAML syntax is correct, and that the entity content is valid according to the catalog's rules.\n `,\n attributes: {\n readOnly: true,\n idempotent: true,\n destructive: false,\n },\n schema: {\n input: z =>\n z.object({\n entity: z.string().describe('Entity YAML content to validate'),\n location: z\n .string()\n .url()\n .optional()\n .describe('Location to validate'),\n }),\n output: z =>\n z.object({\n isValid: z.boolean().describe('Whether the entity is valid'),\n isValidYaml: z.boolean().describe('Whether the YAML syntax is valid'),\n errors: z.array(z.string()).describe('Array of validation errors'),\n entity: z\n .record(z.any())\n .optional()\n .describe('Parsed entity object if valid'),\n }),\n },\n action: async ({ input, credentials }) => {\n const { entity: content, location } = input;\n try {\n let entity;\n try {\n entity = yaml.parse(content);\n } catch (yamlError) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`YAML parsing error: ${yamlError.message}`],\n },\n };\n }\n\n const resp = await catalog.validateEntity(\n entity,\n location ?? 'url:https://localhost/entity-validator',\n { credentials },\n );\n\n return {\n output: {\n isValid: resp.valid,\n isValidYaml: true,\n errors: resp.valid ? [] : resp.errors.map(e => e.message),\n entity: resp.valid ? entity : undefined,\n },\n };\n } catch (error) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`Validation error: ${error.message}`],\n },\n };\n }\n },\n });\n};\n"],"names":["yaml"],"mappings":";;;;;;;;AAmBO,MAAM,0BAAA,GAA6B,CAAC,OAAA,KAGrC;AACJ,EAAA,MAAM,EAAE,eAAA,EAAiB,OAAA,EAAQ,GAAI,OAAA;AACrC,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,yBAAA;AAAA,IACP,WAAA,EAAa;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,IAIb,UAAA,EAAY;AAAA,MACV,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY,IAAA;AAAA,MACZ,WAAA,EAAa;AAAA,KACf;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC,CAAA;AAAA,QAC7D,QAAA,EAAU,EACP,MAAA,EAAO,CACP,KAAI,CACJ,QAAA,EAAS,CACT,QAAA,CAAS,sBAAsB;AAAA,OACnC,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,OAAA,EAAS,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,6BAA6B,CAAA;AAAA,QAC3D,WAAA,EAAa,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,kCAAkC,CAAA;AAAA,QACpE,MAAA,EAAQ,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,4BAA4B,CAAA;AAAA,QACjE,MAAA,EAAQ,CAAA,CACL,MAAA,CAAO,CAAA,CAAE,GAAA,EAAK,CAAA,CACd,QAAA,EAAS,CACT,QAAA,CAAS,+BAA+B;AAAA,OAC5C;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAS,GAAI,KAAA;AACtC,MAAA,IAAI;AACF,QAAA,IAAI,MAAA;AACJ,QAAA,IAAI;AACF,UAAA,MAAA,GAASA,qBAAA,CAAK,MAAM,OAAO,CAAA;AAAA,QAC7B,SAAS,SAAA,EAAW;AAClB,UAAA,OAAO;AAAA,YACL,MAAA,EAAQ;AAAA,cACN,OAAA,EAAS,KAAA;AAAA,cACT,WAAA,EAAa,KAAA;AAAA,cACb,MAAA,EAAQ,CAAC,CAAA,oBAAA,EAAuB,SAAA,CAAU,OAAO,CAAA,CAAE;AAAA;AACrD,WACF;AAAA,QACF;AAEA,QAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,cAAA;AAAA,UACzB,MAAA;AAAA,UACA,QAAA,IAAY,wCAAA;AAAA,UACZ,EAAE,WAAA;AAAY,SAChB;AAEA,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,SAAS,IAAA,CAAK,KAAA;AAAA,YACd,WAAA,EAAa,IAAA;AAAA,YACb,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,EAAC,GAAI,KAAK,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,CAAA;AAAA,YACxD,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,MAAA,GAAS,KAAA;AAAA;AAChC,SACF;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,OAAA,EAAS,KAAA;AAAA,YACT,WAAA,EAAa,KAAA;AAAA,YACb,MAAA,EAAQ,CAAC,CAAA,kBAAA,EAAqB,KAAA,CAAM,OAAO,CAAA,CAAE;AAAA;AAC/C,SACF;AAAA,MACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../src/actions/index.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 */\n\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { ModelHolder } from '../model/ModelHolder';\nimport { createGetCatalogModelDescriptionAction } from './createGetCatalogModelDescriptionAction.ts';\nimport { createGetCatalogEntityAction } from './createGetCatalogEntityAction.ts';\nimport { createValidateEntityAction } from './createValidateEntityAction.ts';\nimport { createRegisterCatalogEntitiesAction } from './createRegisterCatalogEntitiesAction.ts';\nimport { createUnregisterCatalogEntitiesAction } from './createUnregisterCatalogEntitiesAction.ts';\nimport { createQueryCatalogEntitiesAction } from './createQueryCatalogEntitiesAction.ts';\nimport { createRefreshCatalogEntityAction } from './createRefreshCatalogEntityAction.ts';\n\nexport const createCatalogActions = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n modelHolder: ModelHolder | undefined;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n createGetCatalogModelDescriptionAction(options);\n createGetCatalogEntityAction(options);\n createValidateEntityAction(options);\n createRegisterCatalogEntitiesAction(options);\n createUnregisterCatalogEntitiesAction(options);\n createQueryCatalogEntitiesAction(options);\n createRefreshCatalogEntityAction(options);\n};\n"],"names":["createGetCatalogModelDescriptionAction","createGetCatalogEntityAction","createValidateEntityAction","createRegisterCatalogEntitiesAction","createUnregisterCatalogEntitiesAction","createQueryCatalogEntitiesAction","createRefreshCatalogEntityAction"],"mappings":";;;;;;;;;;AA2BO,MAAM,oBAAA,GAAuB,CAAC,OAAA,KAK/B;AACJ,EAAAA,6EAAA,CAAuC,OAAO,CAAA;AAC9C,EAAAC,yDAAA,CAA6B,OAAO,CAAA;AACpC,EAAAC,qDAAA,CAA2B,OAAO,CAAA;AAClC,EAAAC,uEAAA,CAAoC,OAAO,CAAA;AAC3C,EAAAC,2EAAA,CAAsC,OAAO,CAAA;AAC7C,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AACxC,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AAC1C;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../src/actions/index.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 */\n\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { ModelHolder } from '../model/ModelHolder';\nimport { createGetCatalogModelDescriptionAction } from './createGetCatalogModelDescriptionAction.ts';\nimport { createGetCatalogEntityAction } from './createGetCatalogEntityAction.ts';\nimport { createValidateEntityAction } from './createValidateEntityAction.ts';\nimport { createRegisterCatalogEntitiesAction } from './createRegisterCatalogEntitiesAction.ts';\nimport { createUnregisterCatalogEntitiesAction } from './createUnregisterCatalogEntitiesAction.ts';\nimport { createQueryCatalogEntitiesAction } from './createQueryCatalogEntitiesAction.ts';\nimport { createRefreshCatalogEntityAction } from './createRefreshCatalogEntityAction.ts';\n\nexport const createCatalogActions = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n modelHolder: ModelHolder | undefined;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n createGetCatalogModelDescriptionAction(options);\n createGetCatalogEntityAction(options);\n createValidateEntityAction(options);\n createRegisterCatalogEntitiesAction(options);\n createUnregisterCatalogEntitiesAction(options);\n createQueryCatalogEntitiesAction(options);\n createRefreshCatalogEntityAction(options);\n};\n"],"names":["createGetCatalogModelDescriptionAction","createGetCatalogEntityAction","createValidateEntityAction","createRegisterCatalogEntitiesAction","createUnregisterCatalogEntitiesAction","createQueryCatalogEntitiesAction","createRefreshCatalogEntityAction"],"mappings":";;;;;;;;;;AA2BO,MAAM,oBAAA,GAAuB,CAAC,OAAA,KAK/B;AACJ,EAAAA,6EAAA,CAAuC,OAAO,CAAA;AAC9C,EAAAC,yDAAA,CAA6B,OAAO,CAAA;AACpC,EAAAC,qDAAA,CAA2B,OAAO,CAAA;AAClC,EAAAC,uEAAA,CAAoC,OAAO,CAAA;AAC3C,EAAAC,2EAAA,CAAsC,OAAO,CAAA;AAC7C,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AACxC,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AAC1C;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"constants.cjs.js","sources":["../src/constants.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\n/** @public */\nexport const CATALOG_CONFLICTS_TOPIC = 'experimental.catalog.conflict';\n/** @public */\nexport const CATALOG_ERRORS_TOPIC = 'experimental.catalog.errors';\n"],"names":[],"mappings":";;AAiBO,MAAM,uBAAA,GAA0B;AAEhC,MAAM,oBAAA,GAAuB;;;;;"}
1
+ {"version":3,"file":"constants.cjs.js","sources":["../src/constants.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\n/** @public */\nexport const CATALOG_CONFLICTS_TOPIC = 'experimental.catalog.conflict';\n/** @public */\nexport const CATALOG_ERRORS_TOPIC = 'experimental.catalog.errors';\n"],"names":[],"mappings":";;AAiBO,MAAM,uBAAA,GAA0B;AAEhC,MAAM,oBAAA,GAAuB;;;"}
@@ -8,6 +8,7 @@ class DefaultCatalogDatabase {
8
8
  constructor(options) {
9
9
  this.options = options;
10
10
  }
11
+ options;
11
12
  async transaction(fn) {
12
13
  try {
13
14
  let result = void 0;
@@ -30,7 +31,7 @@ class DefaultCatalogDatabase {
30
31
  const tx = txOpaque;
31
32
  const { entityRef } = options;
32
33
  const entityRefs = new Array();
33
- let currentRef = entityRef.toLocaleLowerCase("en-US");
34
+ let currentRef = entityRef.toLowerCase();
34
35
  for (let depth = 1; depth <= MAX_ANCESTOR_DEPTH; depth += 1) {
35
36
  const rows = await tx(
36
37
  "refresh_state_references"
@@ -57,7 +58,7 @@ class DefaultCatalogDatabase {
57
58
  async refresh(txOpaque, options) {
58
59
  const tx = txOpaque;
59
60
  const { entityRef } = options;
60
- const updateResult = await tx("refresh_state").where({ entity_ref: entityRef.toLocaleLowerCase("en-US") }).update({ next_update_at: tx.fn.now() });
61
+ const updateResult = await tx("refresh_state").where({ entity_ref: entityRef.toLowerCase() }).update({ next_update_at: tx.fn.now() });
61
62
  if (updateResult === 0) {
62
63
  throw new errors.NotFoundError(`Failed to schedule ${entityRef} for refresh`);
63
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultCatalogDatabase.cjs.js","sources":["../../src/database/DefaultCatalogDatabase.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 { NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport {\n CatalogDatabase,\n ListAncestorsOptions,\n ListAncestorsResult,\n RefreshOptions,\n Transaction,\n} from './types';\nimport { DbRefreshStateReferencesRow, DbRefreshStateRow } from './tables';\nimport { rethrowError } from './conversion';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nconst MAX_ANCESTOR_DEPTH = 32;\n\nexport class DefaultCatalogDatabase implements CatalogDatabase {\n constructor(\n private readonly options: {\n database: Knex;\n logger: LoggerService;\n },\n ) {}\n\n async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {\n try {\n let result: T | undefined = undefined;\n\n await this.options.database.transaction(\n async tx => {\n // We can't return here, as knex swallows the return type in case the transaction is rolled back:\n // https://github.com/knex/knex/blob/e37aeaa31c8ef9c1b07d2e4d3ec6607e557d800d/lib/transaction.js#L136\n result = await fn(tx);\n },\n {\n // If we explicitly trigger a rollback, don't fail.\n doNotRejectOnRollback: true,\n },\n );\n\n return result!;\n } catch (e) {\n this.options.logger.debug(`Error during transaction, ${e}`);\n throw rethrowError(e);\n }\n }\n\n async listAncestors(\n txOpaque: Transaction,\n options: ListAncestorsOptions,\n ): Promise<ListAncestorsResult> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n const entityRefs = new Array<string>();\n\n let currentRef = entityRef.toLocaleLowerCase('en-US');\n for (let depth = 1; depth <= MAX_ANCESTOR_DEPTH; depth += 1) {\n const rows = await tx<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .where({ target_entity_ref: currentRef })\n .select();\n\n if (rows.length === 0) {\n if (depth === 1) {\n throw new NotFoundError(`Entity ${currentRef} not found`);\n }\n throw new NotFoundError(\n `Entity ${entityRef} has a broken parent reference chain at ${currentRef}`,\n );\n }\n\n const parentRef = rows.find(r => r.source_entity_ref)?.source_entity_ref;\n if (!parentRef) {\n // We've reached the top of the tree which is the entityProvider.\n // In this case we refresh the entity itself.\n return { entityRefs };\n }\n entityRefs.push(parentRef);\n currentRef = parentRef;\n }\n throw new Error(\n `Unable receive ancestors for ${entityRef}, reached maximum depth of ${MAX_ANCESTOR_DEPTH}`,\n );\n }\n\n async refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n\n const updateResult = await tx<DbRefreshStateRow>('refresh_state')\n .where({ entity_ref: entityRef.toLocaleLowerCase('en-US') })\n .update({ next_update_at: tx.fn.now() });\n if (updateResult === 0) {\n throw new NotFoundError(`Failed to schedule ${entityRef} for refresh`);\n }\n }\n}\n"],"names":["rethrowError","NotFoundError"],"mappings":";;;;;AA6BA,MAAM,kBAAA,GAAqB,EAAA;AAEpB,MAAM,sBAAA,CAAkD;AAAA,EAC7D,YACmB,OAAA,EAIjB;AAJiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAIhB;AAAA,EAEH,MAAM,YAAe,EAAA,EAAiD;AACpE,IAAA,IAAI;AACF,MAAA,IAAI,MAAA,GAAwB,KAAA,CAAA;AAE5B,MAAA,MAAM,IAAA,CAAK,QAAQ,QAAA,CAAS,WAAA;AAAA,QAC1B,OAAM,EAAA,KAAM;AAGV,UAAA,MAAA,GAAS,MAAM,GAAG,EAAE,CAAA;AAAA,QACtB,CAAA;AAAA,QACA;AAAA;AAAA,UAEE,qBAAA,EAAuB;AAAA;AACzB,OACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,CAAA,EAAG;AACV,MAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,0BAAA,EAA6B,CAAC,CAAA,CAAE,CAAA;AAC1D,MAAA,MAAMA,wBAAa,CAAC,CAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,aAAA,CACJ,QAAA,EACA,OAAA,EAC8B;AAC9B,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAE,WAAU,GAAI,OAAA;AACtB,IAAA,MAAM,UAAA,GAAa,IAAI,KAAA,EAAc;AAErC,IAAA,IAAI,UAAA,GAAa,SAAA,CAAU,iBAAA,CAAkB,OAAO,CAAA;AACpD,IAAA,KAAA,IAAS,KAAA,GAAQ,CAAA,EAAG,KAAA,IAAS,kBAAA,EAAoB,SAAS,CAAA,EAAG;AAC3D,MAAA,MAAM,OAAO,MAAM,EAAA;AAAA,QACjB;AAAA,QAEC,KAAA,CAAM,EAAE,mBAAmB,UAAA,EAAY,EACvC,MAAA,EAAO;AAEV,MAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,QAAA,IAAI,UAAU,CAAA,EAAG;AACf,UAAA,MAAM,IAAIC,oBAAA,CAAc,CAAA,OAAA,EAAU,UAAU,CAAA,UAAA,CAAY,CAAA;AAAA,QAC1D;AACA,QAAA,MAAM,IAAIA,oBAAA;AAAA,UACR,CAAA,OAAA,EAAU,SAAS,CAAA,wCAAA,EAA2C,UAAU,CAAA;AAAA,SAC1E;AAAA,MACF;AAEA,MAAA,MAAM,YAAY,IAAA,CAAK,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,iBAAiB,CAAA,EAAG,iBAAA;AACvD,MAAA,IAAI,CAAC,SAAA,EAAW;AAGd,QAAA,OAAO,EAAE,UAAA,EAAW;AAAA,MACtB;AACA,MAAA,UAAA,CAAW,KAAK,SAAS,CAAA;AACzB,MAAA,UAAA,GAAa,SAAA;AAAA,IACf;AACA,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,6BAAA,EAAgC,SAAS,CAAA,2BAAA,EAA8B,kBAAkB,CAAA;AAAA,KAC3F;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,CAAQ,QAAA,EAAuB,OAAA,EAAwC;AAC3E,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAE,WAAU,GAAI,OAAA;AAEtB,IAAA,MAAM,YAAA,GAAe,MAAM,EAAA,CAAsB,eAAe,EAC7D,KAAA,CAAM,EAAE,YAAY,SAAA,CAAU,iBAAA,CAAkB,OAAO,CAAA,EAAG,EAC1D,MAAA,CAAO,EAAE,gBAAgB,EAAA,CAAG,EAAA,CAAG,GAAA,EAAI,EAAG,CAAA;AACzC,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAA,MAAM,IAAIA,oBAAA,CAAc,CAAA,mBAAA,EAAsB,SAAS,CAAA,YAAA,CAAc,CAAA;AAAA,IACvE;AAAA,EACF;AACF;;;;"}
1
+ {"version":3,"file":"DefaultCatalogDatabase.cjs.js","sources":["../../src/database/DefaultCatalogDatabase.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 { NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport {\n CatalogDatabase,\n ListAncestorsOptions,\n ListAncestorsResult,\n RefreshOptions,\n Transaction,\n} from './types';\nimport { DbRefreshStateReferencesRow, DbRefreshStateRow } from './tables';\nimport { rethrowError } from './conversion';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nconst MAX_ANCESTOR_DEPTH = 32;\n\nexport class DefaultCatalogDatabase implements CatalogDatabase {\n constructor(\n private readonly options: {\n database: Knex;\n logger: LoggerService;\n },\n ) {}\n\n async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {\n try {\n let result: T | undefined = undefined;\n\n await this.options.database.transaction(\n async tx => {\n // We can't return here, as knex swallows the return type in case the transaction is rolled back:\n // https://github.com/knex/knex/blob/e37aeaa31c8ef9c1b07d2e4d3ec6607e557d800d/lib/transaction.js#L136\n result = await fn(tx);\n },\n {\n // If we explicitly trigger a rollback, don't fail.\n doNotRejectOnRollback: true,\n },\n );\n\n return result!;\n } catch (e) {\n this.options.logger.debug(`Error during transaction, ${e}`);\n throw rethrowError(e);\n }\n }\n\n async listAncestors(\n txOpaque: Transaction,\n options: ListAncestorsOptions,\n ): Promise<ListAncestorsResult> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n const entityRefs = new Array<string>();\n\n let currentRef = entityRef.toLowerCase();\n for (let depth = 1; depth <= MAX_ANCESTOR_DEPTH; depth += 1) {\n const rows = await tx<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .where({ target_entity_ref: currentRef })\n .select();\n\n if (rows.length === 0) {\n if (depth === 1) {\n throw new NotFoundError(`Entity ${currentRef} not found`);\n }\n throw new NotFoundError(\n `Entity ${entityRef} has a broken parent reference chain at ${currentRef}`,\n );\n }\n\n const parentRef = rows.find(r => r.source_entity_ref)?.source_entity_ref;\n if (!parentRef) {\n // We've reached the top of the tree which is the entityProvider.\n // In this case we refresh the entity itself.\n return { entityRefs };\n }\n entityRefs.push(parentRef);\n currentRef = parentRef;\n }\n throw new Error(\n `Unable receive ancestors for ${entityRef}, reached maximum depth of ${MAX_ANCESTOR_DEPTH}`,\n );\n }\n\n async refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n\n const updateResult = await tx<DbRefreshStateRow>('refresh_state')\n .where({ entity_ref: entityRef.toLowerCase() })\n .update({ next_update_at: tx.fn.now() });\n if (updateResult === 0) {\n throw new NotFoundError(`Failed to schedule ${entityRef} for refresh`);\n }\n }\n}\n"],"names":["rethrowError","NotFoundError"],"mappings":";;;;;AA6BA,MAAM,kBAAA,GAAqB,EAAA;AAEpB,MAAM,sBAAA,CAAkD;AAAA,EAC7D,YACmB,OAAA,EAIjB;AAJiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAIhB;AAAA,EAJgB,OAAA;AAAA,EAMnB,MAAM,YAAe,EAAA,EAAiD;AACpE,IAAA,IAAI;AACF,MAAA,IAAI,MAAA,GAAwB,KAAA,CAAA;AAE5B,MAAA,MAAM,IAAA,CAAK,QAAQ,QAAA,CAAS,WAAA;AAAA,QAC1B,OAAM,EAAA,KAAM;AAGV,UAAA,MAAA,GAAS,MAAM,GAAG,EAAE,CAAA;AAAA,QACtB,CAAA;AAAA,QACA;AAAA;AAAA,UAEE,qBAAA,EAAuB;AAAA;AACzB,OACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,CAAA,EAAG;AACV,MAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,0BAAA,EAA6B,CAAC,CAAA,CAAE,CAAA;AAC1D,MAAA,MAAMA,wBAAa,CAAC,CAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,aAAA,CACJ,QAAA,EACA,OAAA,EAC8B;AAC9B,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAE,WAAU,GAAI,OAAA;AACtB,IAAA,MAAM,UAAA,GAAa,IAAI,KAAA,EAAc;AAErC,IAAA,IAAI,UAAA,GAAa,UAAU,WAAA,EAAY;AACvC,IAAA,KAAA,IAAS,KAAA,GAAQ,CAAA,EAAG,KAAA,IAAS,kBAAA,EAAoB,SAAS,CAAA,EAAG;AAC3D,MAAA,MAAM,OAAO,MAAM,EAAA;AAAA,QACjB;AAAA,QAEC,KAAA,CAAM,EAAE,mBAAmB,UAAA,EAAY,EACvC,MAAA,EAAO;AAEV,MAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,QAAA,IAAI,UAAU,CAAA,EAAG;AACf,UAAA,MAAM,IAAIC,oBAAA,CAAc,CAAA,OAAA,EAAU,UAAU,CAAA,UAAA,CAAY,CAAA;AAAA,QAC1D;AACA,QAAA,MAAM,IAAIA,oBAAA;AAAA,UACR,CAAA,OAAA,EAAU,SAAS,CAAA,wCAAA,EAA2C,UAAU,CAAA;AAAA,SAC1E;AAAA,MACF;AAEA,MAAA,MAAM,YAAY,IAAA,CAAK,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,iBAAiB,CAAA,EAAG,iBAAA;AACvD,MAAA,IAAI,CAAC,SAAA,EAAW;AAGd,QAAA,OAAO,EAAE,UAAA,EAAW;AAAA,MACtB;AACA,MAAA,UAAA,CAAW,KAAK,SAAS,CAAA;AACzB,MAAA,UAAA,GAAa,SAAA;AAAA,IACf;AACA,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,6BAAA,EAAgC,SAAS,CAAA,2BAAA,EAA8B,kBAAkB,CAAA;AAAA,KAC3F;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,CAAQ,QAAA,EAAuB,OAAA,EAAwC;AAC3E,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAE,WAAU,GAAI,OAAA;AAEtB,IAAA,MAAM,YAAA,GAAe,MAAM,EAAA,CAAsB,eAAe,EAC7D,KAAA,CAAM,EAAE,YAAY,SAAA,CAAU,WAAA,IAAe,CAAA,CAC7C,OAAO,EAAE,cAAA,EAAgB,GAAG,EAAA,CAAG,GAAA,IAAO,CAAA;AACzC,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAA,MAAM,IAAIA,oBAAA,CAAc,CAAA,mBAAA,EAAsB,SAAS,CAAA,YAAA,CAAc,CAAA;AAAA,IACvE;AAAA,EACF;AACF;;"}