PageSourceSearch

https://backstage.io/assets/js/6b3c62ea.cd2c3cb7.js

js backstage.io collected 2026-09-24 08:27:15 UTC 40,287 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkbackstage_microsite=self.webpackChunkbackstage_microsite||[]).push([["13120"],{274478(e,n,a){a.r(n),a.d(n,{assets:()=>d,contentTitle:()=>l,default:()=>o,frontMatter:()=>t,metadata:()=>s,toc:()=>r});var s=a(248333),c=a(474848),i=a(28453);let t={},l="Release v1.51.0-next.3",d={},r=[{value:"@backstage/[email protected]",id:"backstageplugin-scaffolder-backend400-next2",level:2},{value:"Major Changes",id:"major-changes",level:3},{value:"Minor Changes",id:"minor-changes",level:3},{value:"Patch Changes",id:"patch-changes",level:3},{value:"@backstage/[email protected]",id:"backstageintegration-aws-node020-next1",level:2},{value:"Minor Changes",id:"minor-changes-1",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-catalog-backend370-next2",level:2},{value:"Minor Changes",id:"minor-changes-2",level:3},{value:"Patch Changes",id:"patch-changes-1",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-scaffolder1370-next2",level:2},{value:"Minor Changes",id:"minor-changes-3",level:3},{value:"Patch Changes",id:"patch-changes-2",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-scaffolder-common220-next1",level:2},{value:"Minor Changes",id:"minor-changes-4",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-scaffolder-react1210-next1",level:2},{value:"Minor Changes",id:"minor-changes-5",level:3},{value:"Patch Changes",id:"patch-changes-3",level:3},{value:"@backstage/[email protected]",id:"backstagebackend-defaults0171-next2",level:2},{value:"Patch Changes",id:"patch-changes-4",level:3},{value:"@backstage/[email protected]",id:"backstagebackend-dynamic-feature-service082-next1",level:2},{value:"Patch Changes",id:"patch-changes-5",level:3},{value:"@backstage/[email protected]",id:"backstagebackend-plugin-api191-next1",level:2},{value:"Patch Changes",id:"patch-changes-6",level:3},{value:"@backstage/[email protected]",id:"backstagebackend-test-utils1113-next2",level:2},{value:"Patch Changes",id:"patch-changes-7",level:3},{value:"@backstage/[email protected]",id:"backstagecreate-app083-next3",level:2},{value:"Patch Changes",id:"patch-changes-8",level:3},{value:"@backstage/[email protected]",id:"backstageui0150-next3",level:2},{value:"Patch Changes",id:"patch-changes-9",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-app046-next2",level:2},{value:"Patch Changes",id:"patch-changes-10",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-auth018-next2",level:2},{value:"Patch Changes",id:"patch-changes-11",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-auth-backend0281-next2",level:2},{value:"Patch Changes",id:"patch-changes-12",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-catalog205-next1",level:2},{value:"Patch Changes",id:"patch-changes-13",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-catalog-backend-module-aws0423-next2",level:2},{value:"Patch Changes",id:"patch-changes-14",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-catalog-backend-module-gitlab083-next2",level:2},{value:"Patch Changes",id:"patch-changes-15",level:3},{value:"@backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.12-next.2",id:"backstageplugin-catalog-backend-module-incremental-ingestion0712-next2",level:2},{value:"Patch Changes",id:"patch-changes-16",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-catalog-backend-module-logs0122-next1",level:2},{value:"Patch Changes",id:"patch-changes-17",level:3},{value:"@backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.20-next.1",id:"backstageplugin-catalog-backend-module-scaffolder-entity-model0220-next1",level:2},{value:"Patch Changes",id:"patch-changes-18",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-kubernetes-backend0214-next1",level:2}
1,{value:"Patch Changes",id:"patch-changes-19",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-kubernetes-react0519-next1",level:2},{value:"Patch Changes",id:"patch-changes-20",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-notifications-backend-module-email0321-next1",level:2},{value:"Patch Changes",id:"patch-changes-21",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-scaffolder-node0133-next2",level:2},{value:"Patch Changes",id:"patch-changes-22",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-search-backend-module-elasticsearch183-next2",level:2},{value:"Patch Changes",id:"patch-changes-23",level:3},{value:"@backstage/[email protected]",id:"backstageplugin-techdocs-node1150-next2",level:2},{value:"Patch Changes",id:"patch-changes-24",level:3},{value:"[email protected]",id:"example-app0035-next1",level:2},{value:"Patch Changes",id:"patch-changes-25",level:3},{value:"[email protected]",id:"example-app-legacy02121-next1",level:2},{value:"Patch Changes",id:"patch-changes-26",level:3},{value:"[email protected]",id:"example-backend0050-next3",level:2},{value:"Patch Changes",id:"patch-changes-27",level:3},{value:"@internal/[email protected]",id:"internalscaffolder0021-next1",level:2},{value:"Patch Changes",id:"patch-changes-28",level:3}];function h(e){let n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(n.header,{children:(0,c.jsx)(n.h1,{id:"release-v1510-next3",children:"Release v1.51.0-next.3"})}),"\n",(0,c.jsxs)(n.p,{children:["Upgrade Helper: ",(0,c.jsx)(n.a,{href:"https://backstage.github.io/upgrade-helper/?to=1.51.0-next.3",children:"https://backstage.github.io/upgrade-helper/?to=1.51.0-next.3"})]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-scaffolder-backend400-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"major-changes",children:"Major Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"c78b3b6: Add explicit memory management to SecureTemplater usage"}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"minor-changes",children:"Minor Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["8006acf: The template parameter schema response now exposes a ",(0,c.jsx)(n.code,{children:"formDecorators"})," field\ninstead of ",(0,c.jsx)(n.code,{children:"EXPERIMENTAL_formDecorators"}),". Templates that still declare\n",(0,c.jsx)(n.code,{children:"spec.EXPERIMENTAL_formDecorators"})," are read transparently and surfaced under\nthe new field."]}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"1ecc3ca: Fixed spelling mistakes in internal code"}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageintegration-aws-node020-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"minor-changes-1",children:"Minor Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["8df06ec: Added ",(0,c.jsx)(n.code,{children:"webIdentityTokenFile"})," to ",(0,c.jsx)(n.code,{children:"AwsIntegrationAccountConfig"})," and\n",(0,c.jsx)(n.code,{children:"AwsIntegrationDefaultAccountConfig"}),". When set along with a ",(0,c.jsx)(n.code,{children:"roleName"}),",\n",(0,c.jsx)(n.code,{children:"DefaultAwsCredentialsManager"})," retrieves credentials by calling\n",(0,c.jsx)(n.code,{children:"AssumeRoleWithWebIdentity"})," (via ",(0,c.jsx)(n.code,{children:"fromTokenFile"}),") using the file's\ncontents as the web identity token. The file is re-read on each\ncredential refresh."]}
1),"\n",(0,c.jsxs)(n.p,{children:["The validator rejects combining ",(0,c.jsx)(n.code,{children:"webIdentityTokenFile"})," with\n",(0,c.jsx)(n.code,{children:"accessKeyId"}),"/",(0,c.jsx)(n.code,{children:"secretAccessKey"}),", ",(0,c.jsx)(n.code,{children:"profile"}),", or ",(0,c.jsx)(n.code,{children:"externalId"}),", and\nrejects setting it without a ",(0,c.jsx)(n.code,{children:"roleName"}),"."]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-catalog-backend370-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"minor-changes-2",children:"Minor Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["c2de113: ",(0,c.jsx)(n.strong,{children:"BREAKING"}),": When paginating entities with an order field via ",(0,c.jsx)(n.code,{children:"/entities/by-query"}),", entities that lack the order field are now excluded from both the result set and the ",(0,c.jsx)(n.code,{children:"totalItems"})," count. Previously these entities appeared at the end of the sorted result via ",(0,c.jsx)(n.code,{children:"NULLS LAST"}),", but cursor-based pagination could not actually reach them past the first page \u2014 the count over-reported the number of navigable entities. The new behavior aligns the count with what is actually returned."]}),"\n",(0,c.jsxs)(n.p,{children:["This also removes the ",(0,c.jsx)(n.code,{children:"DISTINCT"})," deduplication from the sort-field CTE, which is a prerequisite for the planner to use the ",(0,c.jsx)(n.code,{children:"(key, value, entity_id)"})," index in sort order and short-circuit on ",(0,c.jsx)(n.code,{children:"LIMIT"}),". Installations with duplicate search rows should land the search-table deduplication migration before adopting this change."]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-1",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["ccbad9d: Improved the performance of the ",(0,c.jsx)(n.code,{children:"catalog_entities_count"})," metric."]}),"\n",(0,c.jsxs)(n.p,{children:["The legacy Prometheus and OpenTelemetry observable gauges previously each ran their own copy of the per-kind count query against the ",(0,c.jsx)(n.code,{children:"search"})," table on every metrics scrape. On large catalogs this could pile up faster than the queries completed, contending for buffers and stalling the database."]}),"\n",(0,c.jsxs)(n.p,{children:["The two callbacks now share a single query result with a short in-process TTL cache, and the underlying query reads from ",(0,c.jsx)(n.code,{children:"final_entities"})," instead of ",(0,c.jsx)(n.code,{children:"search"}),", avoiding the bitmap heap scans that dominated the previous form. The emitted labels and values are unchanged."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["add5d1a: Restructured the entity listing endpoint so that, when a sort field is specified, the search-by-key index drives the query rather than being side-joined onto ",(0,c.jsx)(n.code,{children:"final_entities"}),". This lets PostgreSQL walk the ",(0,c.jsx)(n.code,{children:"(key, value, entity_id)"})," index in already-sorted order and short-circuit on ",(0,c.jsx)(n.code,{children:"LIMIT"}),", reducing typical broad-filter paginated list times from seconds to milliseconds. Entities that lack the sort field still appear at the end of sorted results (NULLS LAST semantics preserved), ordered by ",(0,c.jsx)(n.code,{children:"entity_id"}),"."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["387ea7d: Simplified the entity facets aggregation from ",(0,c.jsx)(n.code,{children:"COUNT(DISTINCT entity_id)"})," to ",(0,c.jsx)(n.code,{children:"COUNT(*)"}),". The unique constraint on ",(0,c.jsx)(n.code,{children:"(entity_id, key, value)"})," guarantees each entity appears at most once per search row group, making the ",(0,c.jsx)(n.code,{children:"DISTINCT"})," unnecessary. This allows the database to use a simpler aggregation plan."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["3f55b73: Improved the performance of the entity facets endpoint when filters are applied. The filtered entity set is now combined with the search table through an inner join rather than a ",(0,c.jsx)(n.code,{children:"WHERE entity_id IN (subquery)"}),". Results are unchanged; on large catalogs the query planner is able to choose dramatically cheaper plans, with measured improvements ranging from roughly 1.2\xd7 on already-fast cases to 7\xd7 or more on high-cardinality facets."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["cde3643: Added missing description to the ",(0,c.jsx)(n.code,{children:"type"})," parameter on the ",(0,c.jsx)(n.code,{children:"unregister-entity"})," MCP action."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["7445f0f: Added a migration that removes duplicate rows from the ",(0,c.jsx)(n.code,{children:"search"})," table, creates covering indices for improved query performance, and adds a ",(0,c.jsx)(n.code,{children:"UNIQUE"})," constraint on ",(0,c.jsx)(n.code,{children:"(entity_id, key, value)"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"This is a long-running migration on large catalogs. On PostgreSQL with millions of search rows, the index creation may take 5-15 minutes per index. During this time, other pods running the previous version will continue to serve traffic normally \u2014 the index creation does not block reads or writes. However, if a Kubernetes liveness probe kills the pod before the index build completes, the build is lost and the next startup will start over. On large tables this can repeat indefinitely."}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.strong,{children:"For large installations"}),", it is recommended to run the following SQL commands against your PostgreSQL database ",(0,c.jsx)(n.strong,{children:"before deploying"})," this version. Each index build takes a few minutes but does not block reads or writes. If these have already completed, the migration will detect the existing indices and skip all work \u2014 startup will be instant."]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-sql",children:"-- Step 1: Remove duplicate search rows\nWITH cte AS (\n  SELECT ctid, row_number() OVER (PARTITION BY entity_id, key, value) AS rn\n  FROM search\n)\nDELETE FROM search USING cte WHERE search.ctid = cte.ctid AND cte.rn > 1;\n\n-- Step 2: Create new indices (run each separately)\nCREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS\n  search_entity_ke
1y_value_idx ON search (entity_id, key, value);\nCREATE INDEX CONCURRENTLY IF NOT EXISTS\n  search_key_value_entity_idx ON search (key, value, entity_id);\nCREATE INDEX CONCURRENTLY IF NOT EXISTS\n  search_facets_covering_idx ON search (key, original_value, entity_id)\n  WHERE original_value IS NOT NULL;\n\n-- Step 3: Drop old indices that are no longer needed\nDROP INDEX CONCURRENTLY IF EXISTS search_key_value_idx;\nDROP INDEX CONCURRENTLY IF EXISTS search_key_original_value_idx;\n"})}),"\n",(0,c.jsxs)(n.p,{children:["Also fixed ",(0,c.jsx)(n.code,{children:"buildEntitySearch"})," to remove duplicate output for entities with duplicate array values, and added ",(0,c.jsx)(n.code,{children:"ON CONFLICT DO UPDATE"})," to ",(0,c.jsx)(n.code,{children:"syncSearchRows"})," so that concurrent stitching races are handled gracefully."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsx)(n.p,{children:"Updated dependencies"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-scaffolder1370-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"minor-changes-3",children:"Minor Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsx)(n.p,{children:"dbeb7aa: Added experimental BUI (Backstage UI) form theme for scaffolder forms. All default field extensions render BUI variants when enabled."}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Extension config:"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-yaml",children:"app:\n  extensions:\n    - sub-page:scaffolder/templates:\n        config:\n          enableBackstageUi: true\n"})}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"JSX props:"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-tsx",children:"<ScaffolderPage formProps={{ EXPERIMENTAL_theme: 'bui' }} />\n"})}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["8006acf: Promoted ",(0,c.jsx)(n.code,{children:"formDecoratorsApiRef"}),", ",(0,c.jsx)(n.code,{children:"ScaffolderFormDecoratorsApi"}),",\n",(0,c.jsx)(n.code,{children:"DefaultScaffolderFormDecoratorsApi"}),", and ",(0,c.jsx)(n.code,{children:"formDecoratorsApi"})," from ",(0,c.jsx)(n.code,{children:"@alpha"}),"\nto ",(0,c.jsx)(n.code,{children:"@public"}),"."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["d09c21c: The ",(0,c.jsx)(n.code,{children:"sub-page:scaffolder/templates"})," extension now accepts a ",(0,c.jsx)(n.code,{children:"groups"})," config\nfield that lets you define template groups on the template list page. Each group\nhas a ",(0,c.jsx)(n.code,{children:"title"})," and a ",(0,c.jsx)(n.code,{children:"filter"}),' predicate. Templates not matched by any\nconfigured group fall into an automatically appended "Other Templates" group.\nWith no groups configured, the page renders a single "Templates" group as\nbefore.']}),"\n",(0,c.jsx)(n.p,{children:"Example:"}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-yaml",children:"app:\n  extensions:\n    - sub-page:scaffolder/templates:\n        config:\n          groups:\n            - title: Recommended Services\n              filter:\n                spec.type: service\n            - title: Documentation\n              filter:\n                spec.type: documentation\n"})}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-2",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"1ecc3ca: Fixed spelling mistakes in internal code"}),"\n",(0,c.jsxs)(n.li,{children:["8006acf: Form decorator input is now parsed against the zod schema configured on the\ndecorator before the decorator runs, so defaults declared via ",(0,c.jsx)(n.code,{children:".default()"}),"\nare applied and invalid input is reported through the error API instead of\nsilently passing through."]}),"\n",(0,c.jsxs)(n.li,{children:["8006acf: The template wizard now reads form decorators from the new\n",(0,c.jsx)(n.code,{children:"spec.formDecorators"})," field on a template, falling back to the deprecated\n",(0,c.jsx)(n.code,{children:"spec.EXPERIMENTAL_formDecorators"})," for templates that have not been migrated."]}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-scaffolder-common220-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"minor-changes-4",children:"Minor Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["8006acf: Promote the ",(0,c.jsx)(n.code,{children:"formDecorators"})," field on the ",(0,c.jsx)(n.code,{children:"Template"})," spec out of experimental.\nThe previous ",(0,c.jsx)(n.code,{children:"EXPERIMENTAL_formDecorators"})," field continues to work and is\nkept as a deprecated alias."]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-scaffolder-react1210-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"minor-changes-5",children:"Minor Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsx)(n.p,{children:"dbeb7aa: Added experimental BUI (Backstage UI) form theme for scaffolder forms. All default field extensions render BUI variants when enabled."}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Extension config:"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-yaml",children:"app:\n  extensions:\n    - sub-page:scaffolder/templates:\n        config:\n          enableBackstageUi: true\n"})}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"JSX props:"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-tsx",children:"<ScaffolderPage formProps={{ EXPERIMENTAL_theme: 'bui' }} />\n"})}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["8006acf: Promoted ",(0,c.jsx)(n.code,{children:"FormDecoratorBlueprint"})," and ",(0,c.jsx)(n.code,{children:"ScaffolderFormDecorator"})," from ",(0,c.jsx)(n.code,{children:"@alpha"}),"\nto ",(0,c.jsx)(n.code,{children:"@public"}),"."]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["d09c21c: The ",(0,c.jsx)(n.code,{children:"TemplateCard"})," component is now a swappable component. Apps using the new\nfrontend system can replace it by registering a ",(0,c.jsx)(n.code,{children:"SwappableComponentBlueprint"}),"\nthat targets ",(0,c.jsx)(n.code,{children:"TemplateCard"}),". Components used as the swappable implementation\nreceive ",(0,c.jsx)(n.code,{children:"TemplateCardComponentProps"}),", where ",(0,c.jsx)(n.code,{children:"onSelected"})," is a zero-argument\ncallback bound to the rendered template. Existing usage continues to work\nunchanged."]}),"\n"]}),"\n"]}
1),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-3",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstagebackend-defaults0171-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-4",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["90b572e: Adds an alpha ",(0,c.jsx)(n.code,{children:"TracingService"})," to provide a unified interface for emitting trace spans across Backstage plugins."]}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstagebackend-dynamic-feature-service082-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-5",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstagebackend-plugin-api191-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-6",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["90b572e: Adds an alpha ",(0,c.jsx)(n.code,{children:"TracingService"})," to provide a unified interface for emitting trace spans across Backstage plugins."]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstagebackend-test-utils1113-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-7",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"7fb12b8: Added a new tracing service mock to be leveraged in tests"}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstagecreate-app083-next3",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-8",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["14e2056: Pinned the Jest version range in app templates to ",(0,c.jsx)(n.code,{children:"~30.2.0"})," to prevent automatic upgrades to Jest 30.4.x, which requires Node.js v24.9+ and breaks tests on Node 22."]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageui0150-next3",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-9",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsx)(n.p,{children:"4bb649d: Fixed Table with row selection creating phantom scroll height on ancestor elements by establishing a containing block for visually-hidden checkbox inputs."}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.strong,{children:"Affected components:"})," Table, TableRoot"]}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["d726bcd: Added new ",(0,c.jsx)(n.code,{children:"DatePicker"})," component \u2014 combines a date field and a calendar popover for selecting a date, built on React Aria with full keyboard and screen reader accessibility. Uses BUI design tokens throughout, including auto-incremented backgrounds via the bg consumer pattern."]}),"\n",(0,c.jsxs)(n.p,{children:[(0,c.jsx)(n.strong,{children:"Affected components:"})," DatePicker"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-app046-next2",children:"@backstage/[email protected]"}
1),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-10",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsxs)(n.p,{children:["a345820: The ",(0,c.jsx)(n.code,{children:"app/routes"})," redirect config now supports path parameter substitution in the ",(0,c.jsx)(n.code,{children:"to"})," target. Named params (",(0,c.jsx)(n.code,{children:":userId"}),") and splat params (",(0,c.jsx)(n.code,{children:"*"}),") captured by the ",(0,c.jsx)(n.code,{children:"from"})," path are replaced in the ",(0,c.jsx)(n.code,{children:"to"})," string before navigating, making it possible to express redirects like:"]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-yaml",children:"app:\n  extensions:\n    - app/routes:\n        config:\n          redirects:\n            - from: /users/:userId\n              to: /profile/:userId\n            - from: /old-docs\n              to: /docs/*\n"})}),"\n"]}),"\n",(0,c.jsxs)(n.li,{children:["\n",(0,c.jsx)(n.p,{children:"Updated dependencies"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-auth018-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-11",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"4f62755: Improved the OAuth consent dialog for MCP authorization by showing more client details, including the client metadata host for CIMD clients, the metadata URL, callback URL, and requested scopes."}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-auth-backend0281-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-12",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"4f62755: Improved the OAuth consent dialog for MCP authorization by showing more client details, including the client metadata host for CIMD clients, the metadata URL, callback URL, and requested scopes."}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-catalog205-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-13",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["728629c: Fixed an issue where navigating to an unknown sub-path on an entity page (for example ",(0,c.jsx)(n.code,{children:"/catalog/default/component/foo/blob"}),") would silently render the first available route. Unknown paths now show the standard not-found page instead."]}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-catalog-backend-module-aws0423-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-14",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-catalog-backend-module-gitlab083-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-15",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"1ecc3ca: Fixed spelling mistakes in internal 
1code"}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-catalog-backend-module-incremental-ingestion0712-next2",children:"@backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.12-next.2"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-16",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-catalog-backend-module-logs0122-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-17",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-catalog-backend-module-scaffolder-entity-model0220-next1",children:"@backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.20-next.1"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-18",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-kubernetes-backend0214-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-19",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"1ecc3ca: Fixed spelling mistakes in internal code"}),"\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-kubernetes-react0519-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-20",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"e68cb8a: Added optional clustersCacheTtlMs option to KubernetesBackendClient that caches getClusters() responses for the specified duration, avoiding repeated /clusters requests when multiple proxy calls resolve cluster auth in quick succession."}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-notifications-backend-module-email0321-next1",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-21",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-scaffolder-node0133-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-22",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-search-backend-module-elasticsearch183-next2",children:"@backstage/[email protected]"}
1),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-23",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"backstageplugin-techdocs-node1150-next2",children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-24",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"example-app0035-next1",children:"[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-25",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"example-app-legacy02121-next1",children:"[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-26",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"example-backend0050-next3",children:"[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-27",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n",(0,c.jsx)(n.li,{children:"@backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.20-next.1"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,c.jsx)(n.h2,{id:"internalscaffolder0021-next1",children:"@internal/[email protected]"}),"\n",(0,c.jsx)(n.h3,{id:"patch-changes-28",children:"Patch Changes"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsxs)(n.li,{children:["Updated dependencies\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"@backstage/[email protected]"}),"\n"]}),"\n"]}),"\n"]})]})}function o(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,c.jsx)(n,{...e,children:(0,c.jsx)(h,{...e})}):h(e)}},28453(e,n,a){a.d(n,{R:()=>t,x:()=>l});var s=a(296540);let c={},i=s.createContext(c);function t(e){let n=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(c):e.components||c:t(e.components),s.createElement(i.Provider,{value:n},e.children)}},248333(e){e.exports=JSON.parse('{"id":"releases/v1.51.0-next.3-changelog","title":"Release v1.51.0-next.3","description":"Upgrade Helper//backstage.github.io/upgrade-helper/?to=1.51.0-next.3","source":"@site/versioned_docs/version-stable/releases/v1.51.0-next.3-changelog.md","sourceDirName":"releases","slug":"/releases/v1.51.0-next.3-changelog","permalink":"/docs/releases/v1.51.0-next.3-changelog","draft":false,"unlisted":false,"editUrl":"https://github.com/backstage/backstage/edit/master/docs/releases/v1.51.0-next.3-changelog.md","tags":[],"version":"stable","frontMatter":{}}')}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.