Merge branch 'master' into caon-docs-compose-stories
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Fixed an issue causing the `repo lint` command to fail when the `--max-warnings` option was used.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-sentry': patch
|
||||
---
|
||||
|
||||
Made "sentry:project:create" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-azure': patch
|
||||
---
|
||||
|
||||
Made "publish:azure" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
Scaffolding - Template card - button to show template entity detail
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-bitbucket': patch
|
||||
---
|
||||
|
||||
Made "publish:bitbucket" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
---
|
||||
|
||||
Added `validateLocationsExist` to the config definition where it was missing.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': patch
|
||||
---
|
||||
|
||||
Made "github:autolinks:create" action idempotent
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
---
|
||||
|
||||
Document the internal built-in filters, and ensure that the types are validated when using `createTemplateFilter` and `createTemplateGlobalFunction` from the `zod` schema.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Add `operation` to alpha `defaultEntityContentGroups`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch
|
||||
---
|
||||
|
||||
Made "publish:bitbucketCloud" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': patch
|
||||
---
|
||||
|
||||
Made "github:branch-protection:create" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': patch
|
||||
---
|
||||
|
||||
Made "publish:github" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-notifications': patch
|
||||
---
|
||||
|
||||
Made "notification:send" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Resolved a problem where the `start` command did not correctly handle multiple `--require` flags, ensuring all specified modules are now properly loaded.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-home-react': patch
|
||||
---
|
||||
|
||||
Fixes auto-hiding of content divider when title not specified
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Fix offset pagination to reset when updating filters in `useEntityList`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': patch
|
||||
---
|
||||
|
||||
Made "github:actions:dispatch" action idempotent
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': minor
|
||||
---
|
||||
|
||||
Internal update to move the `clean`, `pre/postpack` and `fix` commands into their own separate module.
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
cat ${{ github.event_path }} > event.json
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: preview-spec
|
||||
path: |
|
||||
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
run: yarn build:api-docs
|
||||
|
||||
- name: upload API reference
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: stable-reference
|
||||
path: docs/reference/
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
run: yarn docusaurus gen-api-docs all
|
||||
|
||||
- name: upload OpenAPI API docs
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: stable-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
run: yarn build:api-docs
|
||||
|
||||
- name: upload API reference
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: next-reference
|
||||
path: docs/reference/
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
working-directory: storybook
|
||||
|
||||
- name: storybook upload
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: storybook
|
||||
path: storybook/dist/
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
run: yarn docusaurus gen-api-docs all
|
||||
|
||||
- name: upload OpenAPI API docs
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: next-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
@@ -211,13 +211,13 @@ jobs:
|
||||
working-directory: microsite
|
||||
|
||||
- name: download stable reference
|
||||
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
name: stable-reference
|
||||
path: docs/reference
|
||||
|
||||
- name: download stable OpenAPI API docs
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
name: stable-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
@@ -235,7 +235,7 @@ jobs:
|
||||
run: rm -r docs/reference
|
||||
|
||||
- name: clear OpenAPI reference
|
||||
run: find . -name '*.api.mdx' -delete
|
||||
run: find . -name '*.api.mdx' -type f -delete
|
||||
|
||||
# Next docs
|
||||
- name: checkout master
|
||||
@@ -248,13 +248,13 @@ jobs:
|
||||
working-directory: microsite
|
||||
|
||||
- name: download next reference
|
||||
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
name: next-reference
|
||||
path: docs/reference
|
||||
|
||||
- name: download next OpenAPI API docs
|
||||
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4
|
||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
name: next-openapi-docs
|
||||
path: docs/**/*.api.mdx
|
||||
@@ -263,7 +263,7 @@ jobs:
|
||||
run: yarn build
|
||||
working-directory: microsite
|
||||
|
||||
- uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4
|
||||
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
||||
with:
|
||||
name: storybook
|
||||
path: microsite/build/storybook
|
||||
|
||||
+48
-50
@@ -1754,12 +1754,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "detect-libc@npm:1.0.3"
|
||||
bin:
|
||||
detect-libc: ./bin/detect-libc.js
|
||||
checksum: daaaed925ffa7889bd91d56e9624e6c8033911bb60f3a50a74a87500680652969dbaab9526d1e200a4c94acf80fc862a22131841145a0a8482d60a99c24f4a3e
|
||||
"detect-libc@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "detect-libc@npm:2.0.3"
|
||||
checksum: 2ba6a939ae55f189aea996ac67afceb650413c7a34726ee92c40fb0deb2400d57ef94631a8a3f052055eea7efb0f99a9b5e6ce923415daa3e68221f963cfc27d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -3424,91 +3422,91 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-darwin-arm64@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-darwin-arm64@npm:1.29.1"
|
||||
"lightningcss-darwin-arm64@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-darwin-arm64@npm:1.29.3"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-darwin-x64@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-darwin-x64@npm:1.29.1"
|
||||
"lightningcss-darwin-x64@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-darwin-x64@npm:1.29.3"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-freebsd-x64@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-freebsd-x64@npm:1.29.1"
|
||||
"lightningcss-freebsd-x64@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-freebsd-x64@npm:1.29.3"
|
||||
conditions: os=freebsd & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-linux-arm-gnueabihf@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-linux-arm-gnueabihf@npm:1.29.1"
|
||||
"lightningcss-linux-arm-gnueabihf@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-linux-arm-gnueabihf@npm:1.29.3"
|
||||
conditions: os=linux & cpu=arm
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-linux-arm64-gnu@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-linux-arm64-gnu@npm:1.29.1"
|
||||
"lightningcss-linux-arm64-gnu@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-linux-arm64-gnu@npm:1.29.3"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-linux-arm64-musl@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-linux-arm64-musl@npm:1.29.1"
|
||||
"lightningcss-linux-arm64-musl@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-linux-arm64-musl@npm:1.29.3"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-linux-x64-gnu@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-linux-x64-gnu@npm:1.29.1"
|
||||
"lightningcss-linux-x64-gnu@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-linux-x64-gnu@npm:1.29.3"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-linux-x64-musl@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-linux-x64-musl@npm:1.29.1"
|
||||
"lightningcss-linux-x64-musl@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-linux-x64-musl@npm:1.29.3"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-win32-arm64-msvc@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-win32-arm64-msvc@npm:1.29.1"
|
||||
"lightningcss-win32-arm64-msvc@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-win32-arm64-msvc@npm:1.29.3"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss-win32-x64-msvc@npm:1.29.1":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss-win32-x64-msvc@npm:1.29.1"
|
||||
"lightningcss-win32-x64-msvc@npm:1.29.3":
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss-win32-x64-msvc@npm:1.29.3"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lightningcss@npm:^1.28.2":
|
||||
version: 1.29.1
|
||||
resolution: "lightningcss@npm:1.29.1"
|
||||
version: 1.29.3
|
||||
resolution: "lightningcss@npm:1.29.3"
|
||||
dependencies:
|
||||
detect-libc: ^1.0.3
|
||||
lightningcss-darwin-arm64: 1.29.1
|
||||
lightningcss-darwin-x64: 1.29.1
|
||||
lightningcss-freebsd-x64: 1.29.1
|
||||
lightningcss-linux-arm-gnueabihf: 1.29.1
|
||||
lightningcss-linux-arm64-gnu: 1.29.1
|
||||
lightningcss-linux-arm64-musl: 1.29.1
|
||||
lightningcss-linux-x64-gnu: 1.29.1
|
||||
lightningcss-linux-x64-musl: 1.29.1
|
||||
lightningcss-win32-arm64-msvc: 1.29.1
|
||||
lightningcss-win32-x64-msvc: 1.29.1
|
||||
detect-libc: ^2.0.3
|
||||
lightningcss-darwin-arm64: 1.29.3
|
||||
lightningcss-darwin-x64: 1.29.3
|
||||
lightningcss-freebsd-x64: 1.29.3
|
||||
lightningcss-linux-arm-gnueabihf: 1.29.3
|
||||
lightningcss-linux-arm64-gnu: 1.29.3
|
||||
lightningcss-linux-arm64-musl: 1.29.3
|
||||
lightningcss-linux-x64-gnu: 1.29.3
|
||||
lightningcss-linux-x64-musl: 1.29.3
|
||||
lightningcss-win32-arm64-msvc: 1.29.3
|
||||
lightningcss-win32-x64-msvc: 1.29.3
|
||||
dependenciesMeta:
|
||||
lightningcss-darwin-arm64:
|
||||
optional: true
|
||||
@@ -3530,7 +3528,7 @@ __metadata:
|
||||
optional: true
|
||||
lightningcss-win32-x64-msvc:
|
||||
optional: true
|
||||
checksum: d1c4dba66dfe7f6a76532bdb84c35742bee61149550e5eb5b0e84e282f21aecd335f917ca9619bb7ca95fc1eb3092dc7e22f2c16b01e9a0ee472b76452343cce
|
||||
checksum: 80cad5e1eb1c5eaae22ca6f17b127d65b5c93e859da124d57a90e256d7cb7802afa8dc7a6e1ec1e6aa208d0b23c8b9ec07078e69e61b10748bffdc8cadebed7e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ description: Documentation for the Auditor service
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the Auditor Service, a software service designed to record and report on security-relevant events within an application. This service utilizes the `winston` library for logging and provides a flexible way to capture and format audit events.
|
||||
This document describes the Auditor Service, a core service designed to record and report on security-relevant events within an application. By default, this service utilizes the `rootLogger` core service for logging.
|
||||
|
||||
## Key Features
|
||||
|
||||
@@ -16,15 +16,13 @@ This document describes the Auditor Service, a software service designed to reco
|
||||
- Supports detailed metadata for each event.
|
||||
- Offers success/failure reporting for events.
|
||||
- Integrates with authentication and plugin services for enhanced context.
|
||||
- Uses `winston` for flexible log formatting and transport.
|
||||
- Provides a service factory for easy integration with Backstage plugins.
|
||||
- Supports configurable log transports (console, file).
|
||||
|
||||
## How it Works
|
||||
|
||||
The Auditor Service defines a core class, `Auditor`, which implements the `AuditorService` interface. This class uses `winston` to log audit events with varying levels of severity and associated metadata. It also integrates with authentication and plugin services to capture actor details and plugin context.
|
||||
The Auditor Service defines a class, `Auditor`, which implements the `AuditorService` interface. This class uses a `logFn` to log audit events with varying levels of severity and associated metadata. It also integrates with authentication and plugin services to capture actor details and plugin context.
|
||||
|
||||
The `auditorServiceFactory` creates an `Auditor` instance for the root context and provides a factory function for creating child loggers for individual plugins. This allows each plugin to have its own logger with inherited and additional metadata.
|
||||
The `auditorServiceFactory` wraps the `rootLogger` core service and provides a factory function for creating child loggers for individual plugins. This allows each plugin to have its own logger with inherited and additional metadata.
|
||||
|
||||
## Usage Guidance
|
||||
|
||||
@@ -78,10 +76,78 @@ In this example, an audit event is created for each request to `/my-endpoint`. T
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
When defining `eventId` and `subEventId` for your audit events, follow these guidelines:
|
||||
When defining audit events, follow these guidelines to ensure consistency and clarity:
|
||||
|
||||
- Use kebab-case (e.g., `user-login`, `file-download`, `fetch`, `entity-create`, `entity-update`).
|
||||
- The `eventId` represents a logical group of similar events or operations. For example, "fetch" could be used as an `eventId` encompassing various fetch methods like `by-id` or `by-location`.
|
||||
- Use `subEventId` to further categorize events within a logical group. For example, if the `eventId` is "fetch", the `subEventId` could be "by-id" or "by-location" to specify the method used for fetching.
|
||||
- Avoid redundant prefixes related to the plugin ID, as that context is already provided.
|
||||
- Choose names that clearly and concisely describe the event being audited.
|
||||
- **Use kebab-case:** Event IDs should be in kebab-case (e.g., `user-session`, `file-download`, `entity-fetch`).
|
||||
- **`eventId` for Logical Grouping:** The `eventId` represents a broad category or logical group of related operations. For example, `entity-fetch` would group all entity retrieval events. `location-mutate` would group all actions that mutate a location.
|
||||
- **`meta.queryType` (or related field) for Specific Actions within a Group:** Use a `meta` field (like `queryType`, `actionType` or similar) to specify the particular action or query that occurred within the broader `eventId` group.
|
||||
- For instance, with `eventId: entity-fetch`, use `meta: { queryType: 'by-id' }` to represent fetching an entity by its ID. Other examples could be:
|
||||
- `meta: { queryType: 'all' }` for fetching all entities.
|
||||
- `meta: { queryType: 'by-query' }` for fetching entities by a query.
|
||||
- `meta: { actionType: 'delete' }` for `eventId: entity-mutate` when an entity was deleted.
|
||||
- `meta: { actionType: 'create' }` for `eventId: location-mutate` when a location was added.
|
||||
- Use `meta` fields to add more context to the event being tracked.
|
||||
- **Avoid Redundant Prefixes:** Do not include redundant prefixes related to the plugin ID in your event names. The plugin context is already provided separately.
|
||||
- **Clear and Concise:** Choose names that clearly and concisely describe the event being audited.
|
||||
|
||||
## Common Meta Keys and Values
|
||||
|
||||
The following table details common keys found within the `meta` object of audit events and their formats:
|
||||
|
||||
| Key | Description | Format | Example(s) |
|
||||
| ------------- | ------------------------------------------------------------------------------ | ------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| `queryType` | Specifies the type of query performed when fetching data. | A kebab-case string | `all`, `by-id`, `by-name`, `by-query`, `by-refs`, `ancestry`, `by-entity` |
|
||||
| `actionType` | Specifies the type of action performed when modifying data. | A kebab-case string | `create`, `delete`, `refresh` |
|
||||
| `entityRef` | The full reference of an entity, including kind, namespace, and name. | `[kind]:[namespace]/[name]` | `component:default/my-component`, `group:my-org/team-a` |
|
||||
| `locationRef` | A specific reference to a location being operated on. | Any string representing the location. | `url:https://example.com/catalog-info.yaml`, `custom:default/my-location` |
|
||||
| `uid` | The unique identifier of a location or other object involved in the operation. | Any valid unique ID string | `9a4e740b-e557-427f-b9f2-0d4f092b1c1e` |
|
||||
|
||||
By following these conventions, you create a more structured and informative audit trail that is easier to search, filter, and understand. This allows you to better group and understand the events being logged.
|
||||
|
||||
## Audit Event Examples
|
||||
|
||||
To illustrate how these naming conventions and the meta field are used in practice, the following examples demonstrate typical audit events for common operations.
|
||||
|
||||
**Typical Read Operation Example:**
|
||||
|
||||
For an operation that fetches all entities, a typical audit event would look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"eventId": "entity-fetch",
|
||||
"meta": {
|
||||
"queryType": "all"
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Typical Write Operation Example:**
|
||||
|
||||
For an operation that deletes an entity, a typical audit event would look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"eventId": "entity-mutate",
|
||||
"meta": {
|
||||
"actionType": "delete",
|
||||
"uid": "some-entity-uid",
|
||||
"entityRef": "component:default/petstore"
|
||||
},
|
||||
"severityLevel": "medium"
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Practical Examples for Auditor Implementation
|
||||
|
||||
To clarify how to utilize the Auditor feature effectively, we recommend exploring the Catalog Backend. It offers two valuable resources:
|
||||
|
||||
- **Code Implementation Example (createRouter.ts):**
|
||||
- The [`createRouter.ts`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/service/createRouter.ts) file within the Catalog Backend showcases a practical integration of the `AuditorService` within a Backstage backend plugin.
|
||||
- Specifically, the lines that demonstrate the creation of an audit event. This includes setting critical parameters such as `eventId` and `severityLevel`, as well as incorporating relevant metadata like `queryType` and `entityRef`.
|
||||
- **Documentation Example (README.md):**
|
||||
- The "Audit Events" section of the Catalog Backend's [`README.md`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/README.md#audit-events) provides a well-structured example of documenting emitted audit events.
|
||||
- It illustrates how to detail various `eventId` values and their corresponding `meta` fields (e.g., `queryType`, `actionType`) for different plugin operations.
|
||||
|
||||
These examples provide both a code-level demonstration and a documentation guideline for effectively utilizing the `AuditorService` to manage audit events within your Backstage plugins.
|
||||
|
||||
@@ -561,7 +561,7 @@ catalogFilter:
|
||||
metadata.annotations.github.com/team-slug: { exists: true }
|
||||
```
|
||||
|
||||
#### Custom validation messages
|
||||
### Custom validation messages
|
||||
|
||||
You may specify custom JSON Schema validation messages as supported by the
|
||||
[ajv-errors](https://github.com/ajv-validator/ajv-errors) plugin library to [ajv](https://github.com/ajv-validator/ajv).
|
||||
|
||||
@@ -131,7 +131,7 @@ export class MyAwesomeApiClient implements MyAwesomeApi {
|
||||
const proxyUri = `${await this.discoveryApi.getBaseUrl('proxy')}/<your-proxy-uri>`;
|
||||
|
||||
const resp = await this.fetchApi.fetch(`${proxyUri}${input}`, init);
|
||||
if (!resp.ok) throw new Error(resp);
|
||||
if (!resp.ok) throw new Error(resp.statusText);
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
@@ -151,8 +151,9 @@ export class MyAwesomeApiClient implements MyAwesomeApi {
|
||||
}
|
||||
```
|
||||
|
||||
> For more information on the DiscoveryApi check out the
|
||||
> [docs](../reference/core-plugin-api.discoveryapi.md)
|
||||
> Check out the docs for more information on the
|
||||
> [DiscoveryApi](../reference/core-plugin-api.discoveryapi.md) or the
|
||||
> [FetchApi](../reference/core-plugin-api.fetchapi.md)
|
||||
|
||||
## Bundling your ApiRef with your plugin
|
||||
|
||||
@@ -171,6 +172,7 @@ import {
|
||||
createRoutableExtension,
|
||||
createComponentExtension,
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
//...
|
||||
@@ -182,8 +184,12 @@ export const myCustomPlugin = createPlugin({
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: myAwesomeApiRef,
|
||||
deps: { discoveryApi: discoveryApiRef },
|
||||
factory: ({ discoveryApi }) => new MyAwesomeApiClient({ discoveryApi }),
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
fetchApi: fetchApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, fetchApi }) =>
|
||||
new MyAwesomeApiClient({ discoveryApi, fetchApi }),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Amplication Resources
|
||||
author: Amplication
|
||||
authorUrl: https://amplication.com
|
||||
category: Development
|
||||
description: A plugin that integrates Amplication services into Backstage, enabling seamless development and management of your applications.
|
||||
documentation: https://docs.amplication.com/day-zero/integrate-with-backstage
|
||||
iconUrl: /img/amplication_logo.png
|
||||
npmPackageName: '@backstage-community/plugin-scaffolder-backend-module-amplication'
|
||||
addedDate: '2025-03-07'
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
+3
-3
@@ -1637,11 +1637,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
|
||||
version: 7.26.7
|
||||
resolution: "@babel/runtime@npm:7.26.7"
|
||||
version: 7.26.10
|
||||
resolution: "@babel/runtime@npm:7.26.10"
|
||||
dependencies:
|
||||
regenerator-runtime: ^0.14.0
|
||||
checksum: a1664a08f3f4854b895b540cca2f5f5c6c1993b5fb788c9615d70fc201e16bb254df8e0550c83eaf2749a14d87775e11a7c9ded6161203e9da7a4a323d546925
|
||||
checksum: 22d2e0abb86e90de489ab16bb578db6fe2b63a88696db431198b24963749820c723f1982298cdbbea187f7b2b80fb4d98a514faf114ddb2fdc14a4b96277b955
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -200,10 +200,10 @@ Commands:
|
||||
start [options]
|
||||
build [options]
|
||||
test
|
||||
lint [options] [directories...]
|
||||
clean
|
||||
prepack
|
||||
postpack
|
||||
lint [options] [directories...]
|
||||
help [command]
|
||||
```
|
||||
|
||||
@@ -272,7 +272,7 @@ Options:
|
||||
--check
|
||||
--inspect [host]
|
||||
--inspect-brk [host]
|
||||
--require <path>
|
||||
--require <path...>
|
||||
--link <path>
|
||||
-h, --help
|
||||
```
|
||||
@@ -452,6 +452,7 @@ Options:
|
||||
--since <ref>
|
||||
--successCache
|
||||
--successCacheDir <path>
|
||||
--max-warnings <number>
|
||||
--fix
|
||||
-h, --help
|
||||
```
|
||||
|
||||
@@ -35,6 +35,10 @@ import {
|
||||
registerPackageCommands as registerPackageLintCommands,
|
||||
registerRepoCommands as registerRepoLintCommands,
|
||||
} from '../modules/lint';
|
||||
import {
|
||||
registerPackageCommands as registerMaintenancePackageCommands,
|
||||
registerRepoCommands as registerMaintenanceRepoCommands,
|
||||
} from '../modules/maintenance';
|
||||
|
||||
export function registerRepoCommand(program: Command) {
|
||||
const command = program
|
||||
@@ -44,30 +48,7 @@ export function registerRepoCommand(program: Command) {
|
||||
registerRepoBuildCommands(command);
|
||||
registerRepoTestCommands(command);
|
||||
registerRepoLintCommands(command);
|
||||
|
||||
command
|
||||
.command('fix')
|
||||
.description('Automatically fix packages in the project')
|
||||
.option(
|
||||
'--publish',
|
||||
'Enable additional fixes that only apply when publishing packages',
|
||||
)
|
||||
.option(
|
||||
'--check',
|
||||
'Fail if any packages would have been changed by the command',
|
||||
)
|
||||
.action(lazy(() => import('./repo/fix'), 'command'));
|
||||
|
||||
command
|
||||
.command('clean')
|
||||
.description('Delete cache and output directories')
|
||||
.action(lazy(() => import('./repo/clean'), 'command'));
|
||||
|
||||
command
|
||||
.command('list-deprecations')
|
||||
.description('List deprecations')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(lazy(() => import('./repo/list-deprecations'), 'command'));
|
||||
registerMaintenanceRepoCommands(command);
|
||||
}
|
||||
|
||||
export function registerScriptCommand(program: Command) {
|
||||
@@ -86,28 +67,17 @@ export function registerScriptCommand(program: Command) {
|
||||
'--inspect-brk [host]',
|
||||
'Enable debugger in Node.js environments, breaking before code starts',
|
||||
)
|
||||
.option('--require <path>', 'Add a --require argument to the node process')
|
||||
.option(
|
||||
'--require <path...>',
|
||||
'Add a --require argument to the node process',
|
||||
)
|
||||
.option('--link <path>', 'Link an external workspace for module resolution')
|
||||
.action(lazy(() => import('./start'), 'command'));
|
||||
|
||||
registerPackageBuildCommands(command);
|
||||
registerPackageTestCommands(command);
|
||||
|
||||
registerMaintenancePackageCommands(command);
|
||||
registerPackageLintCommands(command);
|
||||
command
|
||||
.command('clean')
|
||||
.description('Delete cache directories')
|
||||
.action(lazy(() => import('./clean/clean'), 'default'));
|
||||
|
||||
command
|
||||
.command('prepack')
|
||||
.description('Prepares a package for packaging before publishing')
|
||||
.action(lazy(() => import('./pack'), 'pre'));
|
||||
|
||||
command
|
||||
.command('postpack')
|
||||
.description('Restores the changes made by the prepack command')
|
||||
.action(lazy(() => import('./pack'), 'post'));
|
||||
}
|
||||
|
||||
export function registerCommands(program: Command) {
|
||||
@@ -153,7 +123,6 @@ export function registerCommands(program: Command) {
|
||||
registerMigrateCommand(program);
|
||||
registerBuildCommands(program);
|
||||
registerInfoCommands(program);
|
||||
|
||||
program
|
||||
.command('create-github-app <github-org>')
|
||||
.description('Create new GitHub App in your organization.')
|
||||
|
||||
@@ -39,7 +39,7 @@ export type RunBackendOptions = {
|
||||
/** Whether to forward the --inspect-brk flag to the node process */
|
||||
inspectBrkEnabled: boolean;
|
||||
/** Additional module to require via the --require flag to the node process */
|
||||
require?: string;
|
||||
require?: string | string[];
|
||||
/** An external linked workspace to override module resolution towards */
|
||||
linkedWorkspace?: string;
|
||||
};
|
||||
@@ -107,7 +107,10 @@ export async function runBackend(options: RunBackendOptions) {
|
||||
optionArgs.push(inspect);
|
||||
}
|
||||
if (options.require) {
|
||||
optionArgs.push(`--require=${options.require}`);
|
||||
const requires = [options.require].flat();
|
||||
for (const r of requires) {
|
||||
optionArgs.push(`--require=${r}`);
|
||||
}
|
||||
}
|
||||
|
||||
const userArgs = process.argv
|
||||
|
||||
@@ -38,7 +38,7 @@ export default createCliPlugin({
|
||||
);
|
||||
command.option(
|
||||
'--max-warnings <number>',
|
||||
'Fail if more than this number of warnings. -1 allows warnings. (default: 0)',
|
||||
'Fail if more than this number of warnings. -1 allows warnings. (default: -1)',
|
||||
);
|
||||
command.description('Lint a package');
|
||||
command.action(
|
||||
@@ -76,6 +76,10 @@ export default createCliPlugin({
|
||||
'--since <ref>',
|
||||
'Only lint packages that changed since the specified ref',
|
||||
);
|
||||
command.option(
|
||||
'--max-warnings <number>',
|
||||
'Fail if more than this number of warnings. -1 allows warnings. (default: -1)',
|
||||
);
|
||||
command.description('Lint a repository');
|
||||
command.action(lazy(() => import('./commands/repo/lint'), 'command'));
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
fix: Boolean(opts.fix),
|
||||
format: opts.format as string | undefined,
|
||||
shouldCache: Boolean(cacheContext),
|
||||
maxWarnings: opts.maxWarnings ?? -1,
|
||||
successCache: cacheContext?.entries,
|
||||
rootDir: paths.targetRoot,
|
||||
},
|
||||
@@ -120,6 +121,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
shouldCache,
|
||||
successCache,
|
||||
rootDir,
|
||||
maxWarnings,
|
||||
}) => {
|
||||
const { ESLint } = require('eslint') as typeof import('eslint');
|
||||
const crypto = require('crypto') as typeof import('crypto');
|
||||
@@ -131,7 +133,6 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
return async ({
|
||||
fullDir,
|
||||
relativeDir,
|
||||
lintOptions,
|
||||
parentHash,
|
||||
}): Promise<{
|
||||
relativeDir: string;
|
||||
@@ -199,7 +200,6 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
await ESLint.outputFixes(results);
|
||||
}
|
||||
|
||||
const maxWarnings = lintOptions?.maxWarnings ?? 0;
|
||||
const ignoreWarnings = +maxWarnings === -1;
|
||||
|
||||
const resultText = formatter.format(results) as string;
|
||||
|
||||
@@ -31,7 +31,7 @@ export function registerPackageCommands(command: Command) {
|
||||
.option('--fix', 'Attempt to automatically fix violations')
|
||||
.option(
|
||||
'--max-warnings <number>',
|
||||
'Fail if more than this number of warnings. -1 allows warnings. (default: 0)',
|
||||
'Fail if more than this number of warnings. -1 allows warnings. (default: -1)',
|
||||
)
|
||||
.description('Lint a package')
|
||||
.action(lazy(() => import('./commands/package/lint'), 'default'));
|
||||
@@ -62,6 +62,10 @@ export function registerRepoCommands(command: Command) {
|
||||
'--successCacheDir <path>',
|
||||
'Set the success cache location, (default: node_modules/.cache/backstage-cli)',
|
||||
)
|
||||
.option(
|
||||
'--max-warnings <number>',
|
||||
'Fail if more than this number of warnings. -1 allows warnings. (default: -1)',
|
||||
)
|
||||
.option('--fix', 'Attempt to automatically fix violations')
|
||||
.action(lazy(() => import('./commands/repo/lint'), 'command'));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Command } from 'commander';
|
||||
import { createCliPlugin } from '../../wiring/factory';
|
||||
import { lazy } from '../../lib/lazy';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'maintenance',
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['package', 'clean'],
|
||||
description: 'Delete cache directories',
|
||||
execute: async ({ args }) => {
|
||||
const command = new Command();
|
||||
const defaultCommand = command.action(
|
||||
lazy(() => import('./commands/package/clean'), 'default'),
|
||||
);
|
||||
|
||||
await defaultCommand.parseAsync(args, { from: 'user' });
|
||||
},
|
||||
});
|
||||
|
||||
reg.addCommand({
|
||||
path: ['package', 'prepack'],
|
||||
description: 'Prepares a package for packaging before publishing',
|
||||
execute: async ({ args }) => {
|
||||
const command = new Command();
|
||||
const defaultCommand = command.action(
|
||||
lazy(() => import('./commands/package/pack'), 'pre'),
|
||||
);
|
||||
|
||||
await defaultCommand.parseAsync(args, { from: 'user' });
|
||||
},
|
||||
});
|
||||
|
||||
reg.addCommand({
|
||||
path: ['package', 'postpack'],
|
||||
description: 'Restores the changes made by the prepack command',
|
||||
execute: async ({ args }) => {
|
||||
const command = new Command();
|
||||
const defaultCommand = command.action(
|
||||
lazy(() => import('./commands/package/pack'), 'post'),
|
||||
);
|
||||
|
||||
await defaultCommand.parseAsync(args, { from: 'user' });
|
||||
},
|
||||
});
|
||||
|
||||
reg.addCommand({
|
||||
path: ['repo', 'fix'],
|
||||
description: 'Automatically fix packages in the project',
|
||||
execute: async ({ args }) => {
|
||||
const command = new Command();
|
||||
const defaultCommand = command
|
||||
.option(
|
||||
'--publish',
|
||||
'Enable additional fixes that only apply when publishing packages',
|
||||
)
|
||||
.option(
|
||||
'--check',
|
||||
'Fail if any packages would have been changed by the command',
|
||||
)
|
||||
.action(lazy(() => import('./commands/repo/fix'), 'command'));
|
||||
|
||||
await defaultCommand.parseAsync(args, { from: 'user' });
|
||||
},
|
||||
});
|
||||
|
||||
reg.addCommand({
|
||||
path: ['repo', 'clean'],
|
||||
description: 'Delete cache and output directories',
|
||||
execute: async ({ args }) => {
|
||||
const command = new Command();
|
||||
const defaultCommand = command.action(
|
||||
lazy(() => import('./commands/repo/clean'), 'command'),
|
||||
);
|
||||
|
||||
await defaultCommand.parseAsync(args, { from: 'user' });
|
||||
},
|
||||
});
|
||||
|
||||
reg.addCommand({
|
||||
path: ['repo', 'list-deprecations'],
|
||||
description: 'List deprecations',
|
||||
execute: async ({ args }) => {
|
||||
const command = new Command();
|
||||
const defaultCommand = command
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(
|
||||
lazy(() => import('./commands/repo/list-deprecations'), 'command'),
|
||||
);
|
||||
|
||||
await defaultCommand.parseAsync(args, { from: 'user' });
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { paths } from '../../../../lib/paths';
|
||||
|
||||
export default async function clean() {
|
||||
await fs.remove(paths.resolveTarget('dist'));
|
||||
+4
-4
@@ -17,11 +17,11 @@
|
||||
import {
|
||||
productionPack,
|
||||
revertProductionPack,
|
||||
} from '../modules/build/lib/packager/productionPack';
|
||||
import { paths } from '../lib/paths';
|
||||
} from '../../../../modules/build/lib/packager/productionPack';
|
||||
import { paths } from '../../../../lib/paths';
|
||||
import fs from 'fs-extra';
|
||||
import { publishPreflightCheck } from '../lib/publishing';
|
||||
import { createTypeDistProject } from '../lib/typeDistProject';
|
||||
import { publishPreflightCheck } from '../../../../lib/publishing';
|
||||
import { createTypeDistProject } from '../../../../lib/typeDistProject';
|
||||
|
||||
export const pre = async () => {
|
||||
publishPreflightCheck({
|
||||
+1
-1
@@ -19,7 +19,7 @@ import fs from 'fs-extra';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { promisify } from 'util';
|
||||
import { PackageGraph } from '@backstage/cli-node';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { paths } from '../../../../lib/paths';
|
||||
|
||||
const execFile = promisify(execFileCb);
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ import {
|
||||
import { OptionValues } from 'commander';
|
||||
import fs from 'fs-extra';
|
||||
import { resolve as resolvePath, posix, relative as relativePath } from 'path';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { publishPreflightCheck } from '../../lib/publishing';
|
||||
import { paths } from '../../../../lib/paths';
|
||||
import { publishPreflightCheck } from '../../../../lib/publishing';
|
||||
|
||||
/**
|
||||
* A mutable object representing a package.json file with potential fixes.
|
||||
+1
-1
@@ -19,7 +19,7 @@ import { ESLint } from 'eslint';
|
||||
import { OptionValues } from 'commander';
|
||||
import { relative as relativePath } from 'path';
|
||||
import { PackageGraph } from '@backstage/cli-node';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { paths } from '../../../../lib/paths';
|
||||
|
||||
export async function command(opts: OptionValues) {
|
||||
const packages = await PackageGraph.listTargetPackages();
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Command } from 'commander';
|
||||
import { lazy } from '../../lib/lazy';
|
||||
|
||||
export function registerPackageCommands(command: Command) {
|
||||
command
|
||||
.command('clean')
|
||||
.description('Delete cache directories')
|
||||
.action(lazy(() => import('./commands/package/clean'), 'default'));
|
||||
|
||||
command
|
||||
.command('prepack')
|
||||
.description('Prepares a package for packaging before publishing')
|
||||
.action(lazy(() => import('./commands/package/pack'), 'pre'));
|
||||
|
||||
command
|
||||
.command('postpack')
|
||||
.description('Restores the changes made by the prepack command')
|
||||
.action(lazy(() => import('./commands/package/pack'), 'post'));
|
||||
}
|
||||
|
||||
export function registerRepoCommands(command: Command) {
|
||||
command
|
||||
.command('fix')
|
||||
.description('Automatically fix packages in the project')
|
||||
.option(
|
||||
'--publish',
|
||||
'Enable additional fixes that only apply when publishing packages',
|
||||
)
|
||||
.option(
|
||||
'--check',
|
||||
'Fail if any packages would have been changed by the command',
|
||||
)
|
||||
.action(lazy(() => import('./commands/repo/fix'), 'command'));
|
||||
|
||||
command
|
||||
.command('clean')
|
||||
.description('Delete cache and output directories')
|
||||
.action(lazy(() => import('./commands/repo/clean'), 'command'));
|
||||
|
||||
command
|
||||
.command('list-deprecations')
|
||||
.description('List deprecations')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(lazy(() => import('./commands/repo/list-deprecations'), 'command'));
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
fixPackageExports,
|
||||
readFixablePackages,
|
||||
writeFixedPackages,
|
||||
} from '../../../commands/repo/fix';
|
||||
} from '../../maintenance/commands/repo/fix';
|
||||
|
||||
export async function command() {
|
||||
console.log(
|
||||
|
||||
@@ -71,6 +71,11 @@ export interface Config {
|
||||
* Default: `/catalog-info.yaml`.
|
||||
*/
|
||||
catalogPath?: string;
|
||||
/**
|
||||
* (Optional) Whether to validate locations that exist before emitting them.
|
||||
* Default: `false`.
|
||||
*/
|
||||
validateLocationsExist?: boolean;
|
||||
/**
|
||||
* (Optional) Filter configuration.
|
||||
*/
|
||||
|
||||
@@ -99,6 +99,7 @@ export const defaultEntityContentGroups: {
|
||||
documentation: string;
|
||||
development: string;
|
||||
deployment: string;
|
||||
operation: string;
|
||||
observability: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ export const defaultEntityContentGroups = {
|
||||
documentation: 'Documentation',
|
||||
development: 'Development',
|
||||
deployment: 'Deployment',
|
||||
operation: 'Operation',
|
||||
observability: 'Observability',
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
identityApiRef,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { TestApiProvider, mockApis } from '@backstage/test-utils';
|
||||
import { mockApis, TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import qs from 'qs';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
@@ -593,6 +593,7 @@ describe(`<EntityListProvider pagination={{ mode: 'offset' }} />`, () => {
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
|
||||
filter: { kind: 'component' },
|
||||
limit,
|
||||
offset: 0,
|
||||
orderFields,
|
||||
fullTextFilter: {
|
||||
term: '2',
|
||||
@@ -620,6 +621,7 @@ describe(`<EntityListProvider pagination={{ mode: 'offset' }} />`, () => {
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
|
||||
filter: { kind: 'component' },
|
||||
limit,
|
||||
offset: 0,
|
||||
orderFields,
|
||||
});
|
||||
});
|
||||
@@ -746,6 +748,7 @@ describe(`<EntityListProvider pagination={{ mode: 'offset' }} />`, () => {
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenNthCalledWith(2, {
|
||||
filter: { kind: 'api', 'spec.type': ['service'] },
|
||||
limit,
|
||||
offset: 0,
|
||||
orderFields,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -379,14 +379,19 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(
|
||||
// TODO(vinzscam): this is currently causing issues at page reload
|
||||
// where the state is not kept. Unfortunately we need to rethink
|
||||
// the way filters work in order to fix this.
|
||||
setCursor(undefined);
|
||||
if (paginationMode === 'cursor') {
|
||||
setCursor(undefined);
|
||||
} else if (paginationMode === 'offset') {
|
||||
// Same thing with offset
|
||||
setOffset(0);
|
||||
}
|
||||
setRequestedFilters(prevFilters => {
|
||||
const newFilters =
|
||||
typeof update === 'function' ? update(prevFilters) : update;
|
||||
return { ...prevFilters, ...newFilters };
|
||||
});
|
||||
},
|
||||
[],
|
||||
[paginationMode],
|
||||
);
|
||||
|
||||
const pageInfo = useMemo(() => {
|
||||
|
||||
@@ -152,7 +152,8 @@ function CardExtension<T>(props: CardExtensionComponentProps<T>) {
|
||||
}
|
||||
|
||||
const cardProps = {
|
||||
...(title && { title, divider: !!title }),
|
||||
divider: !!title,
|
||||
...(title && { title }),
|
||||
...(Settings && !isCustomizable
|
||||
? {
|
||||
action: (
|
||||
|
||||
@@ -174,37 +174,49 @@ export function createPublishAzureAction(options: {
|
||||
const webApi = new WebApi(url, authHandler);
|
||||
const client = await webApi.getGitApi();
|
||||
const createOptions: GitRepositoryCreateOptions = { name: repo };
|
||||
const returnedRepo = await client.createRepository(
|
||||
createOptions,
|
||||
project,
|
||||
);
|
||||
|
||||
if (!returnedRepo) {
|
||||
throw new InputError(
|
||||
`Unable to create the repository with Organization ${organization}, Project ${project} and Repo ${repo}.
|
||||
const { remoteUrl, repositoryId, repoContentsUrl } = await ctx.checkpoint(
|
||||
{
|
||||
key: `create.repo.${repo}`,
|
||||
fn: async () => {
|
||||
const returnedRepo = await client.createRepository(
|
||||
createOptions,
|
||||
project,
|
||||
);
|
||||
|
||||
if (!returnedRepo) {
|
||||
throw new InputError(
|
||||
`Unable to create the repository with Organization ${organization}, Project ${project} and Repo ${repo}.
|
||||
Please make sure that both the Org and Project are typed corrected and exist.`,
|
||||
);
|
||||
}
|
||||
const remoteUrl = returnedRepo.remoteUrl;
|
||||
);
|
||||
}
|
||||
|
||||
if (!remoteUrl) {
|
||||
throw new InputError(
|
||||
'No remote URL returned from create repository for Azure',
|
||||
);
|
||||
}
|
||||
const repositoryId = returnedRepo.id;
|
||||
if (!returnedRepo.remoteUrl) {
|
||||
throw new InputError(
|
||||
'No remote URL returned from create repository for Azure',
|
||||
);
|
||||
}
|
||||
|
||||
if (!repositoryId) {
|
||||
throw new InputError('No Id returned from create repository for Azure');
|
||||
}
|
||||
if (!returnedRepo.id) {
|
||||
throw new InputError(
|
||||
'No Id returned from create repository for Azure',
|
||||
);
|
||||
}
|
||||
|
||||
const repoContentsUrl = returnedRepo.webUrl;
|
||||
if (!returnedRepo.webUrl) {
|
||||
throw new InputError(
|
||||
'No web URL returned from create repository for Azure',
|
||||
);
|
||||
}
|
||||
|
||||
if (!repoContentsUrl) {
|
||||
throw new InputError(
|
||||
'No web URL returned from create repository for Azure',
|
||||
);
|
||||
}
|
||||
return {
|
||||
remoteUrl: returnedRepo.remoteUrl,
|
||||
repositoryId: returnedRepo.id,
|
||||
repoContentsUrl: returnedRepo.webUrl,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const gitAuthorInfo = {
|
||||
name: gitAuthorName
|
||||
@@ -229,20 +241,30 @@ export function createPublishAzureAction(options: {
|
||||
);
|
||||
}
|
||||
|
||||
const commitResult = await initRepoAndPush({
|
||||
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
||||
remoteUrl,
|
||||
defaultBranch,
|
||||
auth: auth,
|
||||
logger: ctx.logger,
|
||||
commitMessage: gitCommitMessage
|
||||
? gitCommitMessage
|
||||
: config.getOptionalString('scaffolder.defaultCommitMessage'),
|
||||
gitAuthorInfo,
|
||||
signingKey: signCommit ? signingKey : undefined,
|
||||
const commitHash = await ctx.checkpoint({
|
||||
key: `init.repo.and.push.${remoteUrl}`,
|
||||
fn: async () => {
|
||||
const commitResult = await initRepoAndPush({
|
||||
dir: getRepoSourceDirectory(
|
||||
ctx.workspacePath,
|
||||
ctx.input.sourcePath,
|
||||
),
|
||||
remoteUrl,
|
||||
defaultBranch,
|
||||
auth: auth,
|
||||
logger: ctx.logger,
|
||||
commitMessage: gitCommitMessage
|
||||
? gitCommitMessage
|
||||
: config.getOptionalString('scaffolder.defaultCommitMessage'),
|
||||
gitAuthorInfo,
|
||||
signingKey: signCommit ? signingKey : undefined,
|
||||
});
|
||||
|
||||
return commitResult?.commitHash;
|
||||
},
|
||||
});
|
||||
|
||||
ctx.output('commitHash', commitResult?.commitHash);
|
||||
ctx.output('commitHash', commitHash);
|
||||
ctx.output('remoteUrl', remoteUrl);
|
||||
ctx.output('repoContentsUrl', repoContentsUrl);
|
||||
ctx.output('repositoryId', repositoryId);
|
||||
|
||||
@@ -224,15 +224,19 @@ export function createPublishBitbucketCloudAction(options: {
|
||||
|
||||
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
||||
|
||||
const { remoteUrl, repoContentsUrl } = await createRepository({
|
||||
authorization,
|
||||
workspace: workspace || '',
|
||||
project,
|
||||
repo,
|
||||
repoVisibility,
|
||||
mainBranch: defaultBranch,
|
||||
description,
|
||||
apiBaseUrl,
|
||||
const { remoteUrl, repoContentsUrl } = await ctx.checkpoint({
|
||||
key: `create.repo.${host}.${repo}`,
|
||||
fn: async () =>
|
||||
await createRepository({
|
||||
authorization,
|
||||
workspace: workspace || '',
|
||||
project,
|
||||
repo,
|
||||
repoVisibility,
|
||||
mainBranch: defaultBranch,
|
||||
description,
|
||||
apiBaseUrl,
|
||||
}),
|
||||
});
|
||||
|
||||
const gitAuthorInfo = {
|
||||
@@ -272,20 +276,29 @@ export function createPublishBitbucketCloudAction(options: {
|
||||
);
|
||||
}
|
||||
|
||||
const commitResult = await initRepoAndPush({
|
||||
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
||||
remoteUrl,
|
||||
auth,
|
||||
defaultBranch,
|
||||
logger: ctx.logger,
|
||||
commitMessage:
|
||||
gitCommitMessage ||
|
||||
config.getOptionalString('scaffolder.defaultCommitMessage'),
|
||||
gitAuthorInfo,
|
||||
signingKey: signCommit ? signingKey : undefined,
|
||||
const commitHash = await ctx.checkpoint({
|
||||
key: `init.repo.and.push${host}.${repo}`,
|
||||
fn: async () => {
|
||||
const commitResult = await initRepoAndPush({
|
||||
dir: getRepoSourceDirectory(
|
||||
ctx.workspacePath,
|
||||
ctx.input.sourcePath,
|
||||
),
|
||||
remoteUrl,
|
||||
auth,
|
||||
defaultBranch,
|
||||
logger: ctx.logger,
|
||||
commitMessage:
|
||||
gitCommitMessage ||
|
||||
config.getOptionalString('scaffolder.defaultCommitMessage'),
|
||||
gitAuthorInfo,
|
||||
signingKey: signCommit ? signingKey : undefined,
|
||||
});
|
||||
return commitResult?.commitHash;
|
||||
},
|
||||
});
|
||||
|
||||
ctx.output('commitHash', commitResult?.commitHash);
|
||||
ctx.output('commitHash', commitHash);
|
||||
ctx.output('remoteUrl', remoteUrl);
|
||||
ctx.output('repoContentsUrl', repoContentsUrl);
|
||||
},
|
||||
|
||||
@@ -368,15 +368,19 @@ export function createPublishBitbucketAction(options: {
|
||||
? createBitbucketCloudRepository
|
||||
: createBitbucketServerRepository;
|
||||
|
||||
const { remoteUrl, repoContentsUrl } = await createMethod({
|
||||
authorization,
|
||||
workspace: workspace || '',
|
||||
project,
|
||||
repo,
|
||||
repoVisibility,
|
||||
mainBranch: defaultBranch,
|
||||
description,
|
||||
apiBaseUrl,
|
||||
const { remoteUrl, repoContentsUrl } = await ctx.checkpoint({
|
||||
key: `create.repo.${host}.${repo}`,
|
||||
fn: async () =>
|
||||
createMethod({
|
||||
authorization,
|
||||
workspace: workspace || '',
|
||||
project,
|
||||
repo,
|
||||
repoVisibility,
|
||||
mainBranch: defaultBranch,
|
||||
description,
|
||||
apiBaseUrl,
|
||||
}),
|
||||
});
|
||||
|
||||
const gitAuthorInfo = {
|
||||
@@ -414,24 +418,33 @@ export function createPublishBitbucketAction(options: {
|
||||
};
|
||||
}
|
||||
|
||||
const commitResult = await initRepoAndPush({
|
||||
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
|
||||
remoteUrl,
|
||||
auth,
|
||||
defaultBranch,
|
||||
logger: ctx.logger,
|
||||
commitMessage: gitCommitMessage
|
||||
? gitCommitMessage
|
||||
: config.getOptionalString('scaffolder.defaultCommitMessage'),
|
||||
gitAuthorInfo,
|
||||
signingKey: signCommit ? signingKey : undefined,
|
||||
const commitHash = await ctx.checkpoint({
|
||||
key: `init.repo.and.push${host}.${repo}`,
|
||||
fn: async () => {
|
||||
const commitResult = await initRepoAndPush({
|
||||
dir: getRepoSourceDirectory(
|
||||
ctx.workspacePath,
|
||||
ctx.input.sourcePath,
|
||||
),
|
||||
remoteUrl,
|
||||
auth,
|
||||
defaultBranch,
|
||||
logger: ctx.logger,
|
||||
commitMessage: gitCommitMessage
|
||||
? gitCommitMessage
|
||||
: config.getOptionalString('scaffolder.defaultCommitMessage'),
|
||||
gitAuthorInfo,
|
||||
signingKey: signCommit ? signingKey : undefined,
|
||||
});
|
||||
return commitResult?.commitHash;
|
||||
},
|
||||
});
|
||||
|
||||
if (enableLFS && host !== 'bitbucket.org') {
|
||||
await performEnableLFS({ authorization, host, project, repo });
|
||||
}
|
||||
|
||||
ctx.output('commitHash', commitResult?.commitHash);
|
||||
ctx.output('commitHash', commitHash);
|
||||
ctx.output('remoteUrl', remoteUrl);
|
||||
ctx.output('repoContentsUrl', repoContentsUrl);
|
||||
},
|
||||
|
||||
@@ -241,36 +241,43 @@ export function createPublishGithubAction(options: {
|
||||
});
|
||||
const client = new Octokit(octokitOptions);
|
||||
|
||||
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(
|
||||
client,
|
||||
repo,
|
||||
owner,
|
||||
repoVisibility,
|
||||
description,
|
||||
homepage,
|
||||
deleteBranchOnMerge,
|
||||
allowMergeCommit,
|
||||
allowSquashMerge,
|
||||
squashMergeCommitTitle,
|
||||
squashMergeCommitMessage,
|
||||
allowRebaseMerge,
|
||||
allowAutoMerge,
|
||||
access,
|
||||
collaborators,
|
||||
hasProjects,
|
||||
hasWiki,
|
||||
hasIssues,
|
||||
topics,
|
||||
repoVariables,
|
||||
secrets,
|
||||
oidcCustomization,
|
||||
customProperties,
|
||||
subscribe,
|
||||
ctx.logger,
|
||||
);
|
||||
const { remoteUrl, repoContentsUrl } = await ctx.checkpoint({
|
||||
key: `create.github.repo.${owner}.${repo}`,
|
||||
fn: async () => {
|
||||
const newRepo = await createGithubRepoWithCollaboratorsAndTopics(
|
||||
client,
|
||||
repo,
|
||||
owner,
|
||||
repoVisibility,
|
||||
description,
|
||||
homepage,
|
||||
deleteBranchOnMerge,
|
||||
allowMergeCommit,
|
||||
allowSquashMerge,
|
||||
squashMergeCommitTitle,
|
||||
squashMergeCommitMessage,
|
||||
allowRebaseMerge,
|
||||
allowAutoMerge,
|
||||
access,
|
||||
collaborators,
|
||||
hasProjects,
|
||||
hasWiki,
|
||||
hasIssues,
|
||||
topics,
|
||||
repoVariables,
|
||||
secrets,
|
||||
oidcCustomization,
|
||||
customProperties,
|
||||
subscribe,
|
||||
ctx.logger,
|
||||
);
|
||||
|
||||
const remoteUrl = newRepo.clone_url;
|
||||
const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`;
|
||||
return {
|
||||
remoteUrl: newRepo.clone_url,
|
||||
repoContentsUrl: `${newRepo.html_url}/blob/${defaultBranch}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const commitResult = await initRepoPushAndProtect(
|
||||
remoteUrl,
|
||||
|
||||
@@ -115,15 +115,20 @@ export function createGithubActionsDispatchAction(options: {
|
||||
}),
|
||||
);
|
||||
|
||||
await client.rest.actions.createWorkflowDispatch({
|
||||
owner,
|
||||
repo,
|
||||
workflow_id: workflowId,
|
||||
ref: branchOrTagName,
|
||||
inputs: workflowInputs,
|
||||
});
|
||||
await ctx.checkpoint({
|
||||
key: `create.workflow.dispatch.${owner}.${repo}.${workflowId}`,
|
||||
fn: async () => {
|
||||
await client.rest.actions.createWorkflowDispatch({
|
||||
owner,
|
||||
repo,
|
||||
workflow_id: workflowId,
|
||||
ref: branchOrTagName,
|
||||
inputs: workflowInputs,
|
||||
});
|
||||
|
||||
ctx.logger.info(`Workflow ${workflowId} dispatched successfully`);
|
||||
ctx.logger.info(`Workflow ${workflowId} dispatched successfully`);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,15 +108,20 @@ export function createGithubAutolinksAction(options: {
|
||||
}),
|
||||
);
|
||||
|
||||
await client.rest.repos.createAutolink({
|
||||
owner,
|
||||
repo,
|
||||
key_prefix: keyPrefix,
|
||||
url_template: urlTemplate,
|
||||
is_alphanumeric: isAlphanumeric,
|
||||
});
|
||||
await ctx.checkpoint({
|
||||
key: `create.auto.link.${owner}.${repo}`,
|
||||
fn: async () => {
|
||||
await client.rest.repos.createAutolink({
|
||||
owner,
|
||||
repo,
|
||||
key_prefix: keyPrefix,
|
||||
url_template: urlTemplate,
|
||||
is_alphanumeric: isAlphanumeric,
|
||||
});
|
||||
|
||||
ctx.logger.info(`Autolink reference created successfully`);
|
||||
ctx.logger.info(`Autolink reference created successfully`);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,29 +130,40 @@ export function createGithubBranchProtectionAction(options: {
|
||||
});
|
||||
const client = new Octokit(octokitOptions);
|
||||
|
||||
const repository = await client.rest.repos.get({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
const defaultBranch = await ctx.checkpoint({
|
||||
key: `read.default.branch.${owner}.${repo}`,
|
||||
fn: async () => {
|
||||
const repository = await client.rest.repos.get({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
});
|
||||
return repository.data.default_branch;
|
||||
},
|
||||
});
|
||||
|
||||
await enableBranchProtectionOnDefaultRepoBranch({
|
||||
repoName: repo,
|
||||
client,
|
||||
owner,
|
||||
logger: ctx.logger,
|
||||
requireCodeOwnerReviews,
|
||||
bypassPullRequestAllowances,
|
||||
requiredApprovingReviewCount,
|
||||
restrictions,
|
||||
requiredStatusCheckContexts,
|
||||
requireBranchesToBeUpToDate,
|
||||
requiredConversationResolution,
|
||||
requireLastPushApproval,
|
||||
defaultBranch: branch ?? repository.data.default_branch,
|
||||
enforceAdmins,
|
||||
dismissStaleReviews,
|
||||
requiredCommitSigning,
|
||||
requiredLinearHistory,
|
||||
await ctx.checkpoint({
|
||||
key: `enable.branch.protection.${owner}.${repo}`,
|
||||
fn: async () => {
|
||||
await enableBranchProtectionOnDefaultRepoBranch({
|
||||
repoName: repo,
|
||||
client,
|
||||
owner,
|
||||
logger: ctx.logger,
|
||||
requireCodeOwnerReviews,
|
||||
bypassPullRequestAllowances,
|
||||
requiredApprovingReviewCount,
|
||||
restrictions,
|
||||
requiredStatusCheckContexts,
|
||||
requireBranchesToBeUpToDate,
|
||||
requiredConversationResolution,
|
||||
requireLastPushApproval,
|
||||
defaultBranch: branch ?? defaultBranch,
|
||||
enforceAdmins,
|
||||
dismissStaleReviews,
|
||||
requiredCommitSigning,
|
||||
requiredLinearHistory,
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -133,9 +133,14 @@ export function createSendNotificationAction(options: {
|
||||
};
|
||||
|
||||
try {
|
||||
await notifications.send({
|
||||
recipients: notificationRecipients,
|
||||
payload,
|
||||
await ctx.checkpoint({
|
||||
key: `send.notification.${payload.title}`,
|
||||
fn: async () => {
|
||||
await notifications.send({
|
||||
recipients: notificationRecipients,
|
||||
payload,
|
||||
});
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
ctx.logger.error(`Failed to send notification: ${e}`);
|
||||
|
||||
@@ -88,32 +88,41 @@ export function createSentryCreateProjectAction(options: { config: Config }) {
|
||||
throw new InputError(`No valid sentry token given`);
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://sentry.io/api/0/teams/${organizationSlug}/${teamSlug}/projects/`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
const { result } = await ctx.checkpoint({
|
||||
key: `create.project.${organizationSlug}.${teamSlug}`,
|
||||
fn: async () => {
|
||||
const response = await fetch(
|
||||
`https://sentry.io/api/0/teams/${organizationSlug}/${teamSlug}/projects/`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
);
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (contentType !== 'application/json') {
|
||||
throw new InputError(
|
||||
`Unexpected Sentry Response Type: ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const res = await response.json();
|
||||
|
||||
if (response.status !== 201) {
|
||||
throw new InputError(`Sentry Response was: ${await res.detail}`);
|
||||
}
|
||||
|
||||
return {
|
||||
code: response.status,
|
||||
result: res as { id: string },
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (contentType !== 'application/json') {
|
||||
throw new InputError(
|
||||
`Unexpected Sentry Response Type: ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const code = response.status;
|
||||
const result = await response.json();
|
||||
|
||||
if (code !== 201) {
|
||||
throw new InputError(`Sentry Response was: ${await result.detail}`);
|
||||
}
|
||||
});
|
||||
|
||||
ctx.output('id', result.id);
|
||||
ctx.output('result', result);
|
||||
|
||||
@@ -529,7 +529,7 @@ export interface RouterOptions {
|
||||
// (undocumented)
|
||||
additionalTemplateFilters?:
|
||||
| Record<string, TemplateFilter_2>
|
||||
| CreatedTemplateFilter[];
|
||||
| CreatedTemplateFilter<any, any>[];
|
||||
// (undocumented)
|
||||
additionalTemplateGlobals?:
|
||||
| Record<string, TemplateGlobal_2>
|
||||
|
||||
@@ -82,7 +82,7 @@ export const scaffolderPlugin = createBackendPlugin({
|
||||
},
|
||||
});
|
||||
|
||||
const additionalTemplateFilters: CreatedTemplateFilter[] = [];
|
||||
const additionalTemplateFilters: CreatedTemplateFilter<any, any>[] = [];
|
||||
const additionalTemplateGlobals: CreatedTemplateGlobal[] = [];
|
||||
|
||||
env.registerExtensionPoint(scaffolderTemplatingExtensionPoint, {
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { parseEntityRef } from '@backstage/catalog-model';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import type { JsonObject, JsonValue } from '@backstage/types';
|
||||
import {
|
||||
parseRepoUrl,
|
||||
TemplateFilter,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export default ({
|
||||
integrations,
|
||||
}: {
|
||||
integrations: ScmIntegrations;
|
||||
}): Record<string, TemplateFilter> => {
|
||||
return {
|
||||
parseRepoUrl: url => parseRepoUrl(url as string, integrations),
|
||||
parseEntityRef: (ref: JsonValue, context?: JsonValue) =>
|
||||
parseEntityRef(ref as string, context as JsonObject),
|
||||
pick: (obj: JsonValue, key: JsonValue) => get(obj, key as string),
|
||||
projectSlug: repoUrl => {
|
||||
const { owner, repo } = parseRepoUrl(repoUrl as string, integrations);
|
||||
return `${owner}/${repo}`;
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { createParseRepoUrl } from './parseRepoUrl';
|
||||
import { parseEntityRef } from './parseEntityRef';
|
||||
import { pick } from './pick';
|
||||
import { createProjectSlug } from './projectSlug';
|
||||
|
||||
export const createDefaultFilters = (options: {
|
||||
integrations: ScmIntegrations;
|
||||
}) => [
|
||||
createParseRepoUrl(options.integrations),
|
||||
parseEntityRef,
|
||||
pick,
|
||||
createProjectSlug(options.integrations),
|
||||
];
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TemplateFilterExample } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
export const examples: TemplateFilterExample[] = [
|
||||
{
|
||||
description: 'Without context',
|
||||
example: `\
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
message: \${{ parameters.owner | parseEntityRef }}
|
||||
`,
|
||||
notes: `\
|
||||
- **Input**: \`group:techdocs\`
|
||||
- **Output**: \`{"kind": "group", "namespace": "default", "name": "techdocs"}\`
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: 'With context',
|
||||
example: `\
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
message: \${{ parameters.owner | parseEntityRef({ defaultKind:"group", defaultNamespace:"another-namespace" }) }}
|
||||
`,
|
||||
notes: `\
|
||||
- **Input**: \`techdocs\`
|
||||
- **Arguments:**: \`[{ "defaultKind": "group", "defaultNamespace": "another-namespace" }]\`
|
||||
- **Output**: \`{"kind": "group", "namespace": "another-namespace", "name": "techdocs"}\`
|
||||
`,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { parseEntityRef as filter } from '@backstage/catalog-model';
|
||||
import { createTemplateFilter } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { examples } from './examples';
|
||||
|
||||
export const parseEntityRef = createTemplateFilter({
|
||||
id: 'parseEntityRef',
|
||||
description:
|
||||
'Extracts the parts of an entity reference, such as the kind, namespace, and name.',
|
||||
schema: z =>
|
||||
z
|
||||
.function()
|
||||
.args(
|
||||
z.union([
|
||||
z.string().describe('compact entity reference'),
|
||||
z
|
||||
.object({
|
||||
kind: z.string().optional(),
|
||||
namespace: z.string().optional(),
|
||||
name: z.string(),
|
||||
})
|
||||
.describe('`CompoundEntityRef`'),
|
||||
]),
|
||||
z
|
||||
.object({
|
||||
defaultKind: z
|
||||
.string()
|
||||
.describe('The default kind, if none is given in the reference'),
|
||||
defaultNamespace: z
|
||||
.string()
|
||||
.describe(
|
||||
'The default namespace, if none is given in the reference',
|
||||
),
|
||||
})
|
||||
.partial()
|
||||
.optional(),
|
||||
)
|
||||
.returns(
|
||||
z
|
||||
.object({
|
||||
kind: z.string(),
|
||||
namespace: z.string(),
|
||||
name: z.string(),
|
||||
})
|
||||
.describe('`CompoundEntityRef`'),
|
||||
),
|
||||
examples,
|
||||
filter,
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { parseEntityRef } from './filter';
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TemplateFilterExample } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
export const examples: TemplateFilterExample[] = [
|
||||
{
|
||||
example: `\
|
||||
- id: log
|
||||
name: Parse Repo URL
|
||||
action: debug:log
|
||||
input:
|
||||
message: \${{ parameters.repoUrl | parseRepoUrl }}`,
|
||||
notes: ` - **Input**: \`github.com?repo=backstage&owner=backstage\`
|
||||
- **Output**: \`{"host":"github.com","owner":"backstage","repo":"backstage"}\`
|
||||
`,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { parseRepoUrl } from '@backstage/plugin-scaffolder-node';
|
||||
import { createTemplateFilter } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { examples } from './examples';
|
||||
|
||||
export const createParseRepoUrl = (integrations: ScmIntegrations) =>
|
||||
createTemplateFilter({
|
||||
id: 'parseRepoUrl',
|
||||
description:
|
||||
'Parses a repository URL into its constituent parts: owner, repository name, etc.',
|
||||
schema: z =>
|
||||
z
|
||||
.function()
|
||||
.args(
|
||||
z.string().describe('repo URL as collected from repository picker'),
|
||||
)
|
||||
.returns(
|
||||
z
|
||||
.object({
|
||||
repo: z.string(),
|
||||
host: z.string(),
|
||||
})
|
||||
.merge(
|
||||
z
|
||||
.object({
|
||||
owner: z.string(),
|
||||
organization: z.string(),
|
||||
workspace: z.string(),
|
||||
project: z.string(),
|
||||
})
|
||||
.partial(),
|
||||
)
|
||||
.describe('`RepoSpec`'),
|
||||
),
|
||||
examples,
|
||||
filter: url => parseRepoUrl(url, integrations),
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { createParseRepoUrl } from './filter';
|
||||
@@ -0,0 +1,31 @@
|
||||
import { TemplateFilterExample } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export const examples: TemplateFilterExample[] = [
|
||||
{
|
||||
example: `\
|
||||
- id: log
|
||||
name: Pick
|
||||
action: debug:log
|
||||
input:
|
||||
message: \${{ parameters.owner | parseEntityRef | pick('name') }}`,
|
||||
notes: `\
|
||||
- **Input**: \`{ kind: 'Group', namespace: 'default', name: 'techdocs'\` }
|
||||
- **Output**: \`techdocs\`
|
||||
`,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createTemplateFilter } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { get as filter } from 'lodash';
|
||||
import { examples } from './examples';
|
||||
|
||||
export const pick = createTemplateFilter({
|
||||
id: 'pick',
|
||||
description:
|
||||
'Selects a specific property (e.g. kind, namespace, name) from an object.',
|
||||
schema: z =>
|
||||
z
|
||||
.function()
|
||||
.args(z.any(), z.string().describe('Property'))
|
||||
.returns(z.any().describe('Selected property')),
|
||||
examples,
|
||||
filter,
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { pick } from './filter';
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TemplateFilterExample } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
|
||||
export const examples: TemplateFilterExample[] = [
|
||||
{
|
||||
example: `\
|
||||
- id: log
|
||||
name: Project Slug
|
||||
action: debug:log
|
||||
input:
|
||||
message: \${{ parameters.repoUrl | projectSlug }}
|
||||
`,
|
||||
notes: `\
|
||||
- **Input**: \`github.com?repo=backstage&owner=backstage\`
|
||||
- **Output**: backstage/backstage
|
||||
`,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { parseRepoUrl } from '@backstage/plugin-scaffolder-node';
|
||||
import { createTemplateFilter } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { examples } from './examples';
|
||||
|
||||
export const createProjectSlug = (integrations: ScmIntegrations) =>
|
||||
createTemplateFilter({
|
||||
id: 'projectSlug',
|
||||
description: 'Generates a project slug from a repository URL.',
|
||||
schema: z =>
|
||||
z
|
||||
.function()
|
||||
.args(
|
||||
z.string().describe('repo URL as collected from repository picker'),
|
||||
)
|
||||
.returns(z.string()),
|
||||
examples,
|
||||
filter: repoUrl => {
|
||||
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
||||
return `${owner}/${repo}`;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { createProjectSlug } from './filter';
|
||||
@@ -29,7 +29,7 @@ import globby from 'globby';
|
||||
import fs from 'fs-extra';
|
||||
import { isBinaryFile } from 'isbinaryfile';
|
||||
import { SecureTemplater } from '../../../../lib/templating/SecureTemplater';
|
||||
import createDefaultFilters from '../../../../lib/templating/filters';
|
||||
import { createDefaultFilters } from '../../../../lib/templating/filters/createDefaultFilters';
|
||||
import { examples } from './template.examples';
|
||||
import { convertFiltersToRecord } from '../../../../util/templating';
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
TemplateGlobal,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { SecureTemplater } from '../../../../lib/templating/SecureTemplater';
|
||||
import createDefaultFilters from '../../../../lib/templating/filters';
|
||||
import { createDefaultFilters } from '../../../../lib/templating/filters/createDefaultFilters';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { convertFiltersToRecord } from '../../../../util/templating';
|
||||
|
||||
@@ -55,7 +55,7 @@ import {
|
||||
TemplateFilter,
|
||||
TemplateGlobal,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import createDefaultFilters from '../../lib/templating/filters';
|
||||
import { createDefaultFilters } from '../../lib/templating/filters/createDefaultFilters';
|
||||
import { scaffolderActionRules } from '../../service/rules';
|
||||
import { createCounterMetric, createHistogramMetric } from '../../util/metrics';
|
||||
import { BackstageLoggerTransport, WinstonLogger } from './logger';
|
||||
|
||||
@@ -179,7 +179,7 @@ export interface RouterOptions {
|
||||
taskBroker?: TaskBroker;
|
||||
additionalTemplateFilters?:
|
||||
| Record<string, TemplateFilter>
|
||||
| CreatedTemplateFilter[];
|
||||
| CreatedTemplateFilter<any, any>[];
|
||||
additionalTemplateGlobals?:
|
||||
| Record<string, TemplateGlobal>
|
||||
| CreatedTemplateGlobal[];
|
||||
|
||||
@@ -28,14 +28,14 @@ import {
|
||||
extractGlobalValueMetadata,
|
||||
} from './templating';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import builtInFilters from '../lib/templating/filters';
|
||||
import { createDefaultFilters } from '../lib/templating/filters/createDefaultFilters';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
|
||||
describe('templating utilities', () => {
|
||||
describe('built-in filters', () => {
|
||||
const integrations = ScmIntegrations.fromConfig(new ConfigReader({}));
|
||||
const filters = builtInFilters({ integrations });
|
||||
const filters = createDefaultFilters({ integrations });
|
||||
it('generates equivalent filter metadata', () => {
|
||||
const metadata = extractFilterMetadata(filters);
|
||||
expect(metadata).toMatchObject(extractFilterMetadata(filters));
|
||||
@@ -64,7 +64,7 @@ describe('templating utilities', () => {
|
||||
z.string().describe('separator').optional(),
|
||||
)
|
||||
.returns(z.string()),
|
||||
filter: (input: string, times: number, separator?: string) =>
|
||||
filter: (input, times, separator) =>
|
||||
Array(times)
|
||||
.fill(input)
|
||||
.join(separator ?? ''),
|
||||
@@ -149,7 +149,7 @@ describe('templating utilities', () => {
|
||||
id: 'respond',
|
||||
schema: z =>
|
||||
z.function().args(z.string().describe('prompt')).returns(z.string()),
|
||||
fn: (prompt: string) =>
|
||||
fn: prompt =>
|
||||
prompt === 'knock knock' ? "who's there?" : "nobody's home",
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
CreatedTemplateGlobal,
|
||||
CreatedTemplateGlobalFunction,
|
||||
CreatedTemplateGlobalValue,
|
||||
TemplateGlobalFunctionSchema,
|
||||
ZodFunctionSchema,
|
||||
} from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { Schema } from 'jsonschema';
|
||||
@@ -63,8 +63,12 @@ type ExportFilterSchema = {
|
||||
* Converts a Zod function schema to JSON schema
|
||||
*/
|
||||
function convertZodFunctionToJsonSchema(
|
||||
t: ReturnType<TemplateGlobalFunctionSchema<any, any>>,
|
||||
t: ReturnType<ZodFunctionSchema<any, any>>,
|
||||
): ExportFunctionSchema {
|
||||
if (!('parameters' in t) || !('returnType' in t)) {
|
||||
throw new Error('Invalid Zod function schema');
|
||||
}
|
||||
|
||||
const args = (t.parameters().items as ZodType[]).map(
|
||||
zt => zodToJsonSchema(zt) as Schema,
|
||||
);
|
||||
@@ -142,7 +146,9 @@ export function extractFilterMetadata(
|
||||
|
||||
if (filter.schema) {
|
||||
metadata.schema = convertToFilterSchema(
|
||||
convertZodFunctionToJsonSchema(filter.schema(z)),
|
||||
convertZodFunctionToJsonSchema(
|
||||
filter.schema(z) as z.ZodFunction<any, any>,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -167,10 +173,7 @@ export function extractFilterMetadata(
|
||||
*/
|
||||
function isGlobalFunction(
|
||||
global: CreatedTemplateGlobal,
|
||||
): global is CreatedTemplateGlobalFunction<
|
||||
TemplateGlobalFunctionSchema<any, any> | undefined,
|
||||
any
|
||||
> {
|
||||
): global is CreatedTemplateGlobalFunction<any, any> {
|
||||
return 'fn' in global;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,56 +29,31 @@ export type AutocompleteHandler = ({
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type CreatedTemplateFilter<
|
||||
TSchema extends
|
||||
| TemplateFilterSchema<any, any>
|
||||
| undefined
|
||||
| unknown = unknown,
|
||||
TFilterSchema extends TSchema extends TemplateFilterSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: TemplateFilter = TSchema extends TemplateFilterSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: TemplateFilter,
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
> = {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateFilterExample[];
|
||||
schema?: TSchema;
|
||||
filter: TFilterSchema;
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
filter: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type CreatedTemplateGlobal =
|
||||
| CreatedTemplateGlobalValue
|
||||
| CreatedTemplateGlobalFunction<unknown, unknown>;
|
||||
| CreatedTemplateGlobalFunction<any, any>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type CreatedTemplateGlobalFunction<
|
||||
TSchema extends
|
||||
| TemplateGlobalFunctionSchema<any, any>
|
||||
| undefined
|
||||
| unknown = unknown,
|
||||
TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: Exclude<
|
||||
TemplateGlobal,
|
||||
JsonValue
|
||||
> = TSchema extends TemplateGlobalFunctionSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: Exclude<TemplateGlobal, JsonValue>,
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
> = {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateGlobalFunctionExample[];
|
||||
schema?: TSchema;
|
||||
fn: TFilterSchema;
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
fn: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
@@ -90,23 +65,27 @@ export type CreatedTemplateGlobalValue<T extends JsonValue = JsonValue> = {
|
||||
|
||||
// @alpha
|
||||
export const createTemplateFilter: <
|
||||
TSchema extends TemplateFilterSchema<any, any> | undefined,
|
||||
TFunctionSchema extends TSchema extends TemplateFilterSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: (arg: JsonValue, ...rest: JsonValue[]) => JsonValue | undefined,
|
||||
>(
|
||||
filter: CreatedTemplateFilter<TSchema, TFunctionSchema>,
|
||||
) => CreatedTemplateFilter<unknown, unknown>;
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
>(options: {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateFilterExample[];
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
filter: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
}) => CreatedTemplateFilter<TFunctionArgs, TReturnType>;
|
||||
|
||||
// @alpha
|
||||
export const createTemplateGlobalFunction: <
|
||||
TSchema extends TemplateGlobalFunctionSchema<any, any> | undefined,
|
||||
TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: (...args: JsonValue[]) => JsonValue | undefined,
|
||||
>(
|
||||
fn: CreatedTemplateGlobalFunction<TSchema, TFilterSchema>,
|
||||
) => CreatedTemplateGlobalFunction<any, any>;
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
>(options: {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateGlobalFunctionExample[];
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
fn: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
}) => CreatedTemplateGlobalFunction<TFunctionArgs, TReturnType>;
|
||||
|
||||
// @alpha
|
||||
export const createTemplateGlobalValue: (
|
||||
@@ -156,7 +135,9 @@ export const scaffolderTaskBrokerExtensionPoint: ExtensionPoint<ScaffolderTaskBr
|
||||
export interface ScaffolderTemplatingExtensionPoint {
|
||||
// (undocumented)
|
||||
addTemplateFilters(
|
||||
filters: Record<string, TemplateFilter_2> | CreatedTemplateFilter[],
|
||||
filters:
|
||||
| Record<string, TemplateFilter_2>
|
||||
| CreatedTemplateFilter<any, any>[],
|
||||
): void;
|
||||
// (undocumented)
|
||||
addTemplateGlobals(
|
||||
@@ -194,16 +175,6 @@ export type TemplateFilterExample = {
|
||||
notes?: string;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type TemplateFilterSchema<
|
||||
Args extends z.ZodTuple<
|
||||
| [z.ZodType<JsonValue>]
|
||||
| [z.ZodType<JsonValue>, ...(z.ZodType<JsonValue> | z.ZodUnknown)[]],
|
||||
z.ZodType<JsonValue> | z.ZodUnknown | null
|
||||
>,
|
||||
Result extends z.ZodType<JsonValue> | z.ZodUndefined,
|
||||
> = (zod: typeof z) => z.ZodFunction<Args, Result>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TemplateGlobal =
|
||||
| ((...args: JsonValue[]) => JsonValue | undefined)
|
||||
@@ -216,15 +187,6 @@ export type TemplateGlobalFunctionExample = {
|
||||
notes?: string;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type TemplateGlobalFunctionSchema<
|
||||
Args extends z.ZodTuple<
|
||||
[] | [z.ZodType<JsonValue>, ...(z.ZodType<JsonValue> | z.ZodUnknown)[]],
|
||||
z.ZodType<JsonValue> | z.ZodUnknown | null
|
||||
>,
|
||||
Result extends z.ZodType<JsonValue> | z.ZodUndefined,
|
||||
> = (zod: typeof z) => z.ZodFunction<Args, Result>;
|
||||
|
||||
// @alpha
|
||||
export interface WorkspaceProvider {
|
||||
// (undocumented)
|
||||
@@ -244,5 +206,17 @@ export interface WorkspaceProvider {
|
||||
}): Promise<void>;
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type ZodFunctionSchema<
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
> = (
|
||||
zod: typeof z,
|
||||
) =>
|
||||
| z.ZodFunction<z.ZodTuple<TFunctionArgs, null>, TReturnType>
|
||||
| z.ZodType<
|
||||
(...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>
|
||||
>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { CreatedTemplateFilter, TemplateFilterSchema } from './types';
|
||||
import { ZodFunctionSchema } from '../types';
|
||||
import { CreatedTemplateFilter, TemplateFilterExample } from './types';
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
@@ -23,10 +23,12 @@ import { z } from 'zod';
|
||||
* @alpha
|
||||
*/
|
||||
export const createTemplateFilter = <
|
||||
TSchema extends TemplateFilterSchema<any, any> | undefined,
|
||||
TFunctionSchema extends TSchema extends TemplateFilterSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: (arg: JsonValue, ...rest: JsonValue[]) => JsonValue | undefined,
|
||||
>(
|
||||
filter: CreatedTemplateFilter<TSchema, TFunctionSchema>,
|
||||
): CreatedTemplateFilter<unknown, unknown> => filter;
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
>(options: {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateFilterExample[];
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
filter: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
}): CreatedTemplateFilter<TFunctionArgs, TReturnType> => options;
|
||||
|
||||
@@ -14,21 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { z } from 'zod';
|
||||
import { TemplateFilter } from '../../types';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { ZodFunctionSchema } from '../types';
|
||||
|
||||
export type { TemplateFilter } from '../../types';
|
||||
|
||||
/** @alpha */
|
||||
export type TemplateFilterSchema<
|
||||
Args extends z.ZodTuple<
|
||||
| [z.ZodType<JsonValue>]
|
||||
| [z.ZodType<JsonValue>, ...(z.ZodType<JsonValue> | z.ZodUnknown)[]],
|
||||
z.ZodType<JsonValue> | z.ZodUnknown | null
|
||||
>,
|
||||
Result extends z.ZodType<JsonValue> | z.ZodUndefined,
|
||||
> = (zod: typeof z) => z.ZodFunction<Args, Result>;
|
||||
|
||||
/** @alpha */
|
||||
export type TemplateFilterExample = {
|
||||
description?: string;
|
||||
@@ -38,23 +27,12 @@ export type TemplateFilterExample = {
|
||||
|
||||
/** @alpha */
|
||||
export type CreatedTemplateFilter<
|
||||
TSchema extends
|
||||
| TemplateFilterSchema<any, any>
|
||||
| undefined
|
||||
| unknown = unknown,
|
||||
TFilterSchema extends TSchema extends TemplateFilterSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: TemplateFilter = TSchema extends TemplateFilterSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: TemplateFilter,
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
> = {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateFilterExample[];
|
||||
schema?: TSchema;
|
||||
filter: TFilterSchema;
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
filter: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
};
|
||||
|
||||
@@ -18,9 +18,9 @@ import { z } from 'zod';
|
||||
import {
|
||||
CreatedTemplateGlobalFunction,
|
||||
CreatedTemplateGlobalValue,
|
||||
TemplateGlobalFunctionSchema,
|
||||
TemplateGlobalFunctionExample,
|
||||
} from './types';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { ZodFunctionSchema } from '../types';
|
||||
|
||||
/**
|
||||
* This function is used to create new template global values in type-safe manner.
|
||||
@@ -39,10 +39,12 @@ export const createTemplateGlobalValue = (
|
||||
* @alpha
|
||||
*/
|
||||
export const createTemplateGlobalFunction = <
|
||||
TSchema extends TemplateGlobalFunctionSchema<any, any> | undefined,
|
||||
TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: (...args: JsonValue[]) => JsonValue | undefined,
|
||||
>(
|
||||
fn: CreatedTemplateGlobalFunction<TSchema, TFilterSchema>,
|
||||
): CreatedTemplateGlobalFunction<any, any> => fn;
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
>(options: {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateGlobalFunctionExample[];
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
fn: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
}): CreatedTemplateGlobalFunction<TFunctionArgs, TReturnType> => options;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { z } from 'zod';
|
||||
import { TemplateGlobal } from '../../types';
|
||||
import { ZodFunctionSchema } from '../types';
|
||||
|
||||
export type { TemplateGlobal } from '../../types';
|
||||
|
||||
@@ -26,15 +26,6 @@ export type CreatedTemplateGlobalValue<T extends JsonValue = JsonValue> = {
|
||||
description?: string;
|
||||
};
|
||||
|
||||
/** @alpha */
|
||||
export type TemplateGlobalFunctionSchema<
|
||||
Args extends z.ZodTuple<
|
||||
[] | [z.ZodType<JsonValue>, ...(z.ZodType<JsonValue> | z.ZodUnknown)[]],
|
||||
z.ZodType<JsonValue> | z.ZodUnknown | null
|
||||
>,
|
||||
Result extends z.ZodType<JsonValue> | z.ZodUndefined,
|
||||
> = (zod: typeof z) => z.ZodFunction<Args, Result>;
|
||||
|
||||
/** @alpha */
|
||||
export type TemplateGlobalFunctionExample = {
|
||||
description?: string;
|
||||
@@ -44,31 +35,17 @@ export type TemplateGlobalFunctionExample = {
|
||||
|
||||
/** @alpha */
|
||||
export type CreatedTemplateGlobalFunction<
|
||||
TSchema extends
|
||||
| TemplateGlobalFunctionSchema<any, any>
|
||||
| undefined
|
||||
| unknown = unknown,
|
||||
TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: Exclude<
|
||||
TemplateGlobal,
|
||||
JsonValue
|
||||
> = TSchema extends TemplateGlobalFunctionSchema<any, any>
|
||||
? z.infer<ReturnType<TSchema>>
|
||||
: TSchema extends unknown
|
||||
? unknown
|
||||
: Exclude<TemplateGlobal, JsonValue>,
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
> = {
|
||||
id: string;
|
||||
description?: string;
|
||||
examples?: TemplateGlobalFunctionExample[];
|
||||
schema?: TSchema;
|
||||
fn: TFilterSchema;
|
||||
schema?: ZodFunctionSchema<TFunctionArgs, TReturnType>;
|
||||
fn: (...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>;
|
||||
};
|
||||
|
||||
/** @alpha */
|
||||
export type CreatedTemplateGlobal =
|
||||
| CreatedTemplateGlobalValue
|
||||
| CreatedTemplateGlobalFunction<unknown, unknown>;
|
||||
| CreatedTemplateGlobalFunction<any, any>;
|
||||
|
||||
@@ -27,6 +27,7 @@ import { CreatedTemplateGlobal } from './globals';
|
||||
export * from '../tasks/alpha';
|
||||
export * from './filters';
|
||||
export * from './globals';
|
||||
export * from './types';
|
||||
|
||||
/**
|
||||
* Extension point for managing scaffolder actions.
|
||||
@@ -73,7 +74,7 @@ export const scaffolderTaskBrokerExtensionPoint =
|
||||
*/
|
||||
export interface ScaffolderTemplatingExtensionPoint {
|
||||
addTemplateFilters(
|
||||
filters: Record<string, TemplateFilter> | CreatedTemplateFilter[],
|
||||
filters: Record<string, TemplateFilter> | CreatedTemplateFilter<any, any>[],
|
||||
): void;
|
||||
|
||||
addTemplateGlobals(
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export type ZodFunctionSchema<
|
||||
TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]],
|
||||
TReturnType extends z.ZodTypeAny,
|
||||
> = (
|
||||
zod: typeof z,
|
||||
) =>
|
||||
| z.ZodFunction<z.ZodTuple<TFunctionArgs, null>, TReturnType>
|
||||
| z.ZodType<
|
||||
(...args: z.infer<z.ZodTuple<TFunctionArgs>>) => z.infer<TReturnType>
|
||||
>;
|
||||
@@ -347,6 +347,7 @@ export const scaffolderReactTranslationRef: TranslationRef<
|
||||
readonly 'templateCategoryPicker.title': 'Categories';
|
||||
readonly 'templateCard.noDescription': 'No description';
|
||||
readonly 'templateCard.chooseButtonText': 'Choose';
|
||||
readonly 'cardHeader.detailBtnTitle': 'Show template entity details';
|
||||
readonly 'templateOutputs.title': 'Text Output';
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -24,12 +24,21 @@ import {
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
} from '@backstage/test-utils';
|
||||
import { starredEntitiesApiRef } from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
entityRouteRef,
|
||||
starredEntitiesApiRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { DefaultStarredEntitiesApi } from '@backstage/plugin-catalog';
|
||||
import Observable from 'zen-observable';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
|
||||
const mountedRoutes = {
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
};
|
||||
|
||||
describe('CardHeader', () => {
|
||||
it('should select the correct theme from the theme provider from the header', async () => {
|
||||
// Can't really test what we want here.
|
||||
@@ -64,6 +73,7 @@ describe('CardHeader', () => {
|
||||
/>
|
||||
</ThemeProvider>
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(mockTheme.getPageTheme).toHaveBeenCalledWith({ themeId: 'service' });
|
||||
@@ -93,6 +103,7 @@ describe('CardHeader', () => {
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('service')).toBeInTheDocument();
|
||||
@@ -118,6 +129,7 @@ describe('CardHeader', () => {
|
||||
<TestApiProvider apis={[[starredEntitiesApiRef, starredEntitiesApi]]}>
|
||||
<CardHeader template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
const favorite = getByRole('button', { name: 'Add to favorites' });
|
||||
@@ -129,6 +141,38 @@ describe('CardHeader', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('renders TemplateDetailButton with link to entity page', async () => {
|
||||
const { getByTitle } = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
starredEntitiesApiRef,
|
||||
new DefaultStarredEntitiesApi({
|
||||
storageApi: mockApis.storage(),
|
||||
}),
|
||||
],
|
||||
]}
|
||||
>
|
||||
<CardHeader
|
||||
template={{
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
kind: 'Template',
|
||||
metadata: { name: 'test-template', namespace: 'default' },
|
||||
spec: {
|
||||
steps: [],
|
||||
type: 'service',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
const detailButton = getByTitle('Show template entity details');
|
||||
const link = detailButton.querySelector('a');
|
||||
expect(link).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the name of the entity', async () => {
|
||||
const { getByText } = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
@@ -153,6 +197,7 @@ describe('CardHeader', () => {
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('bob')).toBeInTheDocument();
|
||||
@@ -182,6 +227,7 @@ describe('CardHeader', () => {
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('Iamtitle')).toBeInTheDocument();
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Theme, makeStyles, useTheme } from '@material-ui/core/styles';
|
||||
import { makeStyles, Theme, useTheme } from '@material-ui/core/styles';
|
||||
import { ItemCardHeader } from '@backstage/core-components';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { FavoriteEntity } from '@backstage/plugin-catalog-react';
|
||||
import { TemplateDetailButton } from './TemplateDetailButton.tsx';
|
||||
|
||||
const useStyles = makeStyles<
|
||||
Theme,
|
||||
@@ -66,7 +67,11 @@ export const CardHeader = (props: CardHeaderProps) => {
|
||||
<div className={styles.subtitleWrapper}>
|
||||
<div>{type}</div>
|
||||
<div>
|
||||
<FavoriteEntity entity={props.template} style={{ padding: 0 }} />
|
||||
<TemplateDetailButton template={props.template} />
|
||||
<FavoriteEntity
|
||||
entity={props.template}
|
||||
style={{ padding: 0, marginLeft: 6 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -33,6 +33,12 @@ import { permissionApiRef } from '@backstage/plugin-permission-react';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { SWRConfig } from 'swr';
|
||||
|
||||
const mountedRoutes = {
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
};
|
||||
|
||||
describe('TemplateCard', () => {
|
||||
it('should render the card title', async () => {
|
||||
const mockTemplate: TemplateEntityV1beta3 = {
|
||||
@@ -59,6 +65,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('bob')).toBeInTheDocument();
|
||||
@@ -89,6 +96,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
const description = getByText('hello');
|
||||
@@ -121,6 +129,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('No description')).toBeInTheDocument();
|
||||
@@ -151,6 +160,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(queryByTestId('template-card-separator')).toBeInTheDocument();
|
||||
@@ -187,6 +197,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
for (const tag of mockTemplate.metadata.tags!) {
|
||||
@@ -227,11 +238,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:kind/:namespace/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(queryByTestId('template-card-separator')).toBeInTheDocument();
|
||||
@@ -272,11 +279,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} additionalLinks={[]} />
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:kind/:namespace/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(queryByTestId('template-card-separator')).toBeInTheDocument();
|
||||
@@ -321,11 +324,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} additionalLinks={[]} />
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:kind/:namespace/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(queryByTestId('template-card-separator')).not.toBeInTheDocument();
|
||||
@@ -364,17 +363,13 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} />
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:kind/:namespace/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByRole('link', { name: /.*my-test-user$/ })).toBeInTheDocument();
|
||||
expect(getByRole('link', { name: /.*my-test-user$/ })).toHaveAttribute(
|
||||
'href',
|
||||
'/catalog/group/default/my-test-user',
|
||||
'/catalog/default/group/my-test-user',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -404,11 +399,7 @@ describe('TemplateCard', () => {
|
||||
>
|
||||
<TemplateCard template={mockTemplate} onSelected={mockOnSelected} />
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:kind/:namespace/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByRole('button', { name: 'Choose' })).toBeInTheDocument();
|
||||
@@ -448,11 +439,7 @@ describe('TemplateCard', () => {
|
||||
<TemplateCard template={mockTemplate} onSelected={mockOnSelected} />
|
||||
</TestApiProvider>
|
||||
</SWRConfig>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:kind/:namespace/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(queryByText('Choose')).toBeNull();
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import DescriptionIcon from '@material-ui/icons/Description';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import {
|
||||
entityRouteParams,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { useApp, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { scaffolderReactTranslationRef } from '../../../translation';
|
||||
import { useTranslationRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
export interface TemplateDetailButtonProps {
|
||||
template: Entity;
|
||||
}
|
||||
|
||||
export const TemplateDetailButton = ({
|
||||
template,
|
||||
}: TemplateDetailButtonProps) => {
|
||||
const catalogEntityRoute = useRouteRef(entityRouteRef);
|
||||
const { t } = useTranslationRef(scaffolderReactTranslationRef);
|
||||
const entityRef = stringifyEntityRef(template);
|
||||
|
||||
const app = useApp();
|
||||
const TemplateIcon = app.getSystemIcon('kind:template') || DescriptionIcon;
|
||||
|
||||
return (
|
||||
<Tooltip id={`tooltip-${entityRef}`} title={t('cardHeader.detailBtnTitle')}>
|
||||
<IconButton
|
||||
aria-label={t('cardHeader.detailBtnTitle')}
|
||||
id={`viewDetail-${entityRef}`}
|
||||
style={{ padding: 0 }}
|
||||
color="inherit"
|
||||
>
|
||||
<Typography component="span">
|
||||
<Link
|
||||
to={catalogEntityRoute(entityRouteParams(template))}
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
<TemplateIcon />
|
||||
</Link>
|
||||
</Typography>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -44,6 +44,9 @@ export const scaffolderReactTranslationRef = createTranslationRef({
|
||||
noDescription: 'No description',
|
||||
chooseButtonText: 'Choose',
|
||||
},
|
||||
cardHeader: {
|
||||
detailBtnTitle: 'Show template entity details',
|
||||
},
|
||||
templateOutputs: {
|
||||
title: 'Text Output',
|
||||
},
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { DefaultStarredEntitiesApi } from '@backstage/plugin-catalog';
|
||||
import {
|
||||
catalogApiRef,
|
||||
entityRouteRef,
|
||||
starredEntitiesApiRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
|
||||
@@ -30,6 +31,13 @@ import React from 'react';
|
||||
import { rootRouteRef } from '../../../routes';
|
||||
import { TemplateListPage } from './TemplateListPage';
|
||||
|
||||
const mountedRoutes = {
|
||||
mountedRoutes: {
|
||||
'/': rootRouteRef,
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
};
|
||||
|
||||
describe('TemplateListPage', () => {
|
||||
const mockCatalogApi = catalogApiMock({
|
||||
entities: [
|
||||
@@ -60,7 +68,7 @@ describe('TemplateListPage', () => {
|
||||
>
|
||||
<TemplateListPage />
|
||||
</TestApiProvider>,
|
||||
{ mountedRoutes: { '/': rootRouteRef } },
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByPlaceholderText('Search')).toBeInTheDocument();
|
||||
@@ -82,7 +90,7 @@ describe('TemplateListPage', () => {
|
||||
>
|
||||
<TemplateListPage />
|
||||
</TestApiProvider>,
|
||||
{ mountedRoutes: { '/': rootRouteRef } },
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByRole('menuitem', { name: /All/ })).toBeInTheDocument();
|
||||
@@ -105,7 +113,7 @@ describe('TemplateListPage', () => {
|
||||
>
|
||||
<TemplateListPage />
|
||||
</TestApiProvider>,
|
||||
{ mountedRoutes: { '/': rootRouteRef } },
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('Categories')).toBeInTheDocument();
|
||||
@@ -127,7 +135,7 @@ describe('TemplateListPage', () => {
|
||||
>
|
||||
<TemplateListPage />
|
||||
</TestApiProvider>,
|
||||
{ mountedRoutes: { '/': rootRouteRef } },
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('Owner')).toBeInTheDocument();
|
||||
@@ -150,6 +158,7 @@ describe('TemplateListPage', () => {
|
||||
>
|
||||
<TemplateListPage />
|
||||
</TestApiProvider>,
|
||||
mountedRoutes,
|
||||
);
|
||||
|
||||
expect(getByText('Tags')).toBeInTheDocument();
|
||||
@@ -172,7 +181,7 @@ describe('TemplateListPage', () => {
|
||||
>
|
||||
<TemplateListPage />
|
||||
</TestApiProvider>,
|
||||
{ mountedRoutes: { '/': rootRouteRef } },
|
||||
mountedRoutes,
|
||||
);
|
||||
expect(queryByTestId('menu-button')).toBeInTheDocument();
|
||||
});
|
||||
@@ -199,7 +208,7 @@ describe('TemplateListPage', () => {
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
{ mountedRoutes: { '/': rootRouteRef } },
|
||||
mountedRoutes,
|
||||
);
|
||||
expect(queryByTestId('menu-button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
+163
-163
@@ -970,9 +970,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-actions@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-actions@npm:8.6.4"
|
||||
"@storybook/addon-actions@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-actions@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
"@types/uuid": ^9.0.1
|
||||
@@ -980,231 +980,231 @@ __metadata:
|
||||
polished: ^4.2.2
|
||||
uuid: ^9.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: b08f73070ac90f2c0d404b63f3148ebe11d4683f0728fd04459df62b521422d8c17b3619f6c012a90571a2d5520ee34d35e02fd104c0298580177dd57e045312
|
||||
storybook: ^8.6.7
|
||||
checksum: 28add3c30e397894bb13d1d492b88e331cec2a24b47ad9e865002e54851a17a0916387dca02074b834c6d665748bd2a5ec88904ad08795645d1b8a9345fb1328
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-backgrounds@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-backgrounds@npm:8.6.4"
|
||||
"@storybook/addon-backgrounds@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-backgrounds@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
memoizerific: ^1.11.3
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 53a67d385d6241730d0d992552de58316cfa4c46f56fa5a81df8647c76356778164783b8a872e31b3624692f456186074e06407778cf4442ffc8596cdaded49e
|
||||
storybook: ^8.6.7
|
||||
checksum: 574b634e49225f52800a7ed0ea7b120f15198ae91068c06842b16e2d197a076bf94c1b42dc05fc0daac328169317768f28294260a14451f928cb0641010bec0d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-controls@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-controls@npm:8.6.4"
|
||||
"@storybook/addon-controls@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-controls@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
dequal: ^2.0.2
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: a25c7f2ae7c3b4aee89ba865e5a305293d54bc4723f4e8e4f22d3c42a626d09b83a69521e728e2b3b35107a7709702801c1f5a4fe96e16871d77108282b8a8a2
|
||||
storybook: ^8.6.7
|
||||
checksum: 002184ac5444ea21bb76e2737f596920d1d06fbf2f7eab75d12d8f4b4f176439132e81e7433ad46f968e379bfb5224992a0a351b19f7673a96bbd74212d6d6c6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-docs@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-docs@npm:8.6.4"
|
||||
"@storybook/addon-docs@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-docs@npm:8.6.7"
|
||||
dependencies:
|
||||
"@mdx-js/react": ^3.0.0
|
||||
"@storybook/blocks": 8.6.4
|
||||
"@storybook/csf-plugin": 8.6.4
|
||||
"@storybook/react-dom-shim": 8.6.4
|
||||
"@storybook/blocks": 8.6.7
|
||||
"@storybook/csf-plugin": 8.6.7
|
||||
"@storybook/react-dom-shim": 8.6.7
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 28705136eb3117fcc46b4038a6fb8bf24207fa39b2b5043ab3dd124a3a186d02d36afdb03a456638ec652e1944679de0721361bca27caa5920b234e92c4e9149
|
||||
storybook: ^8.6.7
|
||||
checksum: 764c3fddbbb92a2c4c7df4d96d943f9511a96a40e87f94b084dbaed789bec4de8d87a83e3877cb7648aafb1fa61940be8d52c2910c037ade89c6ef02beb3d745
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-essentials@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-essentials@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-essentials@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/addon-actions": 8.6.4
|
||||
"@storybook/addon-backgrounds": 8.6.4
|
||||
"@storybook/addon-controls": 8.6.4
|
||||
"@storybook/addon-docs": 8.6.4
|
||||
"@storybook/addon-highlight": 8.6.4
|
||||
"@storybook/addon-measure": 8.6.4
|
||||
"@storybook/addon-outline": 8.6.4
|
||||
"@storybook/addon-toolbars": 8.6.4
|
||||
"@storybook/addon-viewport": 8.6.4
|
||||
"@storybook/addon-actions": 8.6.7
|
||||
"@storybook/addon-backgrounds": 8.6.7
|
||||
"@storybook/addon-controls": 8.6.7
|
||||
"@storybook/addon-docs": 8.6.7
|
||||
"@storybook/addon-highlight": 8.6.7
|
||||
"@storybook/addon-measure": 8.6.7
|
||||
"@storybook/addon-outline": 8.6.7
|
||||
"@storybook/addon-toolbars": 8.6.7
|
||||
"@storybook/addon-viewport": 8.6.7
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 564cc7e6f4559572f12c7f015e40a3995c55c681704e4082ef80d9db0c7b06433a67473e810ea9d67a46121f3a4aca8d7851953bc7f109f54e96c069b05230e0
|
||||
storybook: ^8.6.7
|
||||
checksum: 774b50e98888ece9536ec2a7f491c8486055d3c7515ac9a7f0264916f684c6c4608e95da7db14bceb65dfdf05f3fc93011a0b74a690eec2d44b6c969511b9313
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-highlight@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-highlight@npm:8.6.4"
|
||||
"@storybook/addon-highlight@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-highlight@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: e080b543bd3fbe0e8d6e916f2abd562b7136371dff64e96d196532dca4958016b7890c6dcd48545f11c2ac31aa0d3a713b98d6fd2cde2a1f94b1ebaf7031095d
|
||||
storybook: ^8.6.7
|
||||
checksum: 7baae25d40e717e5138d10b4ea7ef424b55500ab7496ef38738ea0fb99f910bdf3a56651efbfa85d3e4218382498b045894653b47cdd066c6b16ed0076537940
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-interactions@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-interactions@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-interactions@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
"@storybook/instrumenter": 8.6.4
|
||||
"@storybook/test": 8.6.4
|
||||
"@storybook/instrumenter": 8.6.7
|
||||
"@storybook/test": 8.6.7
|
||||
polished: ^4.2.2
|
||||
ts-dedent: ^2.2.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 6abfd78b17aef00708eb6221d3fdfc45fb738e1fd206a5c08c3909476abe6f042a507b6226ff99708538196294f37aea1d743ca7c33ea270ca0456c0f332ab55
|
||||
storybook: ^8.6.7
|
||||
checksum: f7a66307aa4e83b1a230d0103123eef15ce7d27aa25c47270624b9ddb6653293e9d0dfe9bb93d3c348604ec05c774716bd513f4d3987ae0b9818a0f865fa0d69
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-links@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-links@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-links@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
storybook: ^8.6.4
|
||||
storybook: ^8.6.7
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
checksum: 92ffa01adda87133dd97623e3beb69b1095929a8ec79c00191b816b4dcdf35d4dcad33b2dfa16791647f85357b8fa8f662cdc382772a6c9376953c8f49564dde
|
||||
checksum: 3770196105742335eb862eea9e096a7be4cb056067b9bd72a1616ca8b1033d2549199494e017f252342959e51ebb4205b1562c0db17b800aceb4656c5bbe2fa3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-measure@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-measure@npm:8.6.4"
|
||||
"@storybook/addon-measure@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-measure@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
tiny-invariant: ^1.3.1
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: b020d052c87aab591b4b7e31dd60764af9f70140ad4d529d468cdf0c8eb45d02ad7ec2bc01a83d88a1547cf567da8db5e4ba0b4c7294e968df2bace3f07f4fd7
|
||||
storybook: ^8.6.7
|
||||
checksum: 38c51463366405216aa21f5e99a5c66acf8a626084427a439bbbdf76f41119cf66a5fd25e1b27f7859e180101b4a536ee058a3978b03cbe6a48f2e0807927c83
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-outline@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-outline@npm:8.6.4"
|
||||
"@storybook/addon-outline@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-outline@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: d705c2dda8fc4b9af2ecb8217a0463f0f8213f836150627f67e4702d449071e5982580617ff75ae8c48960ca27e957fe3700c17cd854e05d326ceeaf622202c2
|
||||
storybook: ^8.6.7
|
||||
checksum: 8919842d53079cb6ae8efcecc5673d1fcd3ec8f9cf2dbf2f44de0fa2648ef684bd626c9be187ef570e7ff2b21eb92bfe1759b8e72bd41f9f0cb82e2e57073cc7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-storysource@npm:^8.3.6":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-storysource@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-storysource@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/source-loader": 8.6.4
|
||||
"@storybook/source-loader": 8.6.7
|
||||
estraverse: ^5.2.0
|
||||
tiny-invariant: ^1.3.1
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 6172f5ea802a5081a6e777597ca68c2e3bddb58be2bcc7b52f7646bdd2216902a2bb426b3fd8e2a6a8710741d9e72d569a279fb6e0aadadae47fbb881e72308f
|
||||
storybook: ^8.6.7
|
||||
checksum: 3aecdc32867b7ecf0a38efffa80caae6f0249391b8e21ea7a7434ba6dd8f054cd918b4669e799b40fe2f654084c114ab20d411d233e540d46b8c208f9d6f5c2a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-themes@npm:^8.3.6":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-themes@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-themes@npm:8.6.7"
|
||||
dependencies:
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 11ae08e570df4d94ed452c0be6b15773d51c0a8003c6a238156262fafbb8e094fb5c3d20c11bceaae68461ba12fe305732e4d3ea53bf16c8e115ec2ef6fd1f00
|
||||
storybook: ^8.6.7
|
||||
checksum: 209793b93774e3e1696e33fcf3a62dd63a4ca60920e45b465a86a2504545d00463ebc683f4ff65606b502175d575a969698efe96538daebc5254adfe9b767929
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-toolbars@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-toolbars@npm:8.6.4"
|
||||
"@storybook/addon-toolbars@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-toolbars@npm:8.6.7"
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: c15ef11c50ac067e5b226dba30db956405d94b5ff56d67279697bcdcc63367ef0ab523f091b8755f8b8ab5c1ac6e55c3ded9cef6f5b917261ea9a232bcf9a8dc
|
||||
storybook: ^8.6.7
|
||||
checksum: 72de8200fcbd95714b79deae7194451725abfd75dac56862b76d9f19150aa844829d7e2c99597fc3be650c6cd952c3fe4789866512314fb26f5d112620ae8145
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/addon-viewport@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/addon-viewport@npm:8.6.4"
|
||||
"@storybook/addon-viewport@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/addon-viewport@npm:8.6.7"
|
||||
dependencies:
|
||||
memoizerific: ^1.11.3
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 746cd771f371b825da4c5b974602a1c14287212c19ddff9f593e77e9867a3efe76ccc9ee3e503efdcf91949bf3a03979c8f37aa2fe62eb888a3eb11dd208def2
|
||||
storybook: ^8.6.7
|
||||
checksum: 2a83a8683ca8306e13578266aeffbc24968f9bf11b1b71c19a5aba0239e0365f19d335950ec261f11c4907577a9600ab6182ccc0ad8e60c13f9a9ea02b6b0adc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/blocks@npm:8.6.4, @storybook/blocks@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/blocks@npm:8.6.4"
|
||||
"@storybook/blocks@npm:8.6.7, @storybook/blocks@npm:^8.3.5":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/blocks@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/icons": ^1.2.12
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
storybook: ^8.6.4
|
||||
storybook: ^8.6.7
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
checksum: fb891491f99fa63c02c90e00d166c9299cf93512d2d6036a932a1c243cd568f7ca45af961b89a19ba1c1c51c4ad76d0de8d687249f1dcb8b3411fbec2b7cf238
|
||||
checksum: 89b600680772712500318d82195d2a78885d84127a05ab3e2e6474170f871239ab5de72669d0bfdeede99186f8487c48e197ed33fc3e7fc17d0aea228f915bff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/builder-vite@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/builder-vite@npm:8.6.4"
|
||||
"@storybook/builder-vite@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/builder-vite@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/csf-plugin": 8.6.4
|
||||
"@storybook/csf-plugin": 8.6.7
|
||||
browser-assert: ^1.2.1
|
||||
ts-dedent: ^2.0.0
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
storybook: ^8.6.7
|
||||
vite: ^4.0.0 || ^5.0.0 || ^6.0.0
|
||||
checksum: e3e6c1674af25a51b87c084476a2f6e07245a4206493ca7758f116588efc3986e1200b45a9e626406e31a37f001f9c8abf0c77e83f02a14a713c5ac407196775
|
||||
checksum: 8ffc8b2f60c4f1f88d99becd35f75cf34e6b7506b2a25ba6ec054a98758f3fada2a054a475c0f8fd74a7baba84fa1d799b9b843352df4cafab7e3d7a96d74348
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/components@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/components@npm:8.6.4"
|
||||
"@storybook/components@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/components@npm:8.6.7"
|
||||
peerDependencies:
|
||||
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
|
||||
checksum: b50923e759eea99320fdf8946056fb33614b9d993ca567207cd0d714e5fae83da485b64b0d74a18b84355396b33c28b5eea050484c40967873c540f83d314b9a
|
||||
checksum: cb20f8baa76deef1af1739d6c1b1a9f57eb22d158fa046d9919a91840e7a00f1b48e96b7ffe7994365b60808f864c963a3476ee98b4c8b63968a3a877068da3a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/core@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/core@npm:8.6.4"
|
||||
"@storybook/core@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/core@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/theming": 8.6.4
|
||||
"@storybook/theming": 8.6.7
|
||||
better-opn: ^3.0.2
|
||||
browser-assert: ^1.2.1
|
||||
esbuild: ^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0
|
||||
@@ -1220,18 +1220,18 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
prettier:
|
||||
optional: true
|
||||
checksum: 8352b0f65bd7c0462471409a7f086125f4e1a93e1777fa20268beb78109f9784cc1d75536a4f9136368db87ba3c5c11987c95835937ba4c2776f968365795d8f
|
||||
checksum: b150b7472766fccbbc1dec74cba3c16e009a9f24c13f01198923f553d12e2a2070ee2a35f5514f9af350b79e34577946ea65a4a495fd1fa18a27655f897c013a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/csf-plugin@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/csf-plugin@npm:8.6.4"
|
||||
"@storybook/csf-plugin@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/csf-plugin@npm:8.6.7"
|
||||
dependencies:
|
||||
unplugin: ^1.3.1
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: f1750183ad0c83438803da83d705a0e73819ad7e0cfd010182cc504d8a37ab2f500df4c6ea61ec82b464b13e992a2a4cf6756aabb2a66e12e8c2ee3f22553929
|
||||
storybook: ^8.6.7
|
||||
checksum: 75c799fd222552f447e24c852b696bc00f1399a631c6a29e4da1b0be97600aa6085f0ebb56beb8c22731b6099fdddaf52edf01c0d0803ba8e22dd04f79bf483a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1261,143 +1261,143 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/instrumenter@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/instrumenter@npm:8.6.4"
|
||||
"@storybook/instrumenter@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/instrumenter@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
"@vitest/utils": ^2.1.1
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: acfc58fd5c29ec78a00498441c97d4528e8f915956c67600c75e7a30d240fb86551e9ce0dad8c0f83c78a828417a64d8aff38db754b87b854c70507d8d3638d0
|
||||
storybook: ^8.6.7
|
||||
checksum: 8b5bbdd73637233ddebf6d3f14d3904c6f122f29f2efe4a0172a1f03e066a88a3453ae3eef140b227eb626ee959c0c3522f62944407f4530e24e6a6ac804598b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/manager-api@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/manager-api@npm:8.6.4"
|
||||
"@storybook/manager-api@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/manager-api@npm:8.6.7"
|
||||
peerDependencies:
|
||||
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
|
||||
checksum: 50447d3961dfab2b2f36afa76aaeb779a9b51ca66a912bc763103f18f2f71514191388b101ec83093ac9dee6ab509b955da41325f15d88cfe1c480e7d7d1f9eb
|
||||
checksum: 6bb0860c3795945189832613da5866e5159b2adb376478a2b596c113928c64f5962efc325f927a6bcb7a48d7257f42cf6489b0c44397876129626ac68c44aa28
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/preview-api@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/preview-api@npm:8.6.4"
|
||||
"@storybook/preview-api@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/preview-api@npm:8.6.7"
|
||||
peerDependencies:
|
||||
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
|
||||
checksum: d8f642de8053686192e28f7ec748944c99c9e4d0cef7f13d96e840fd159c8bea2e3139b88a84eb16009fbda3152a61d9e075777211c10773e4d01fd103bf0e28
|
||||
checksum: 0d24d62444b7b153bde89d3949ab6539a163e15bc80c5bfcc73b7d49bbb34fb02eb74a99434793b8a6446aeec491e07f006c0f4e404f98486c6d1d614d01defa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/react-dom-shim@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/react-dom-shim@npm:8.6.4"
|
||||
"@storybook/react-dom-shim@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/react-dom-shim@npm:8.6.7"
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
storybook: ^8.6.4
|
||||
checksum: 0062191f312c0bf32e622904b9da7176c961cec99258c6b230c3fb6758c1cca95ac4142d4701628dc8eb922601d74c350549b78e146b5af1747edf8cb3def756
|
||||
storybook: ^8.6.7
|
||||
checksum: 51c20259ac4485d9a996266341a4d9898c949049e2a9af9a08bec58c2ace3a40be1a534005c52fc2db2c81bb010dbd36030bb70581b36c05cf4b34d58f140d3a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/react-vite@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/react-vite@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/react-vite@npm:8.6.7"
|
||||
dependencies:
|
||||
"@joshwooding/vite-plugin-react-docgen-typescript": 0.5.0
|
||||
"@rollup/pluginutils": ^5.0.2
|
||||
"@storybook/builder-vite": 8.6.4
|
||||
"@storybook/react": 8.6.4
|
||||
"@storybook/builder-vite": 8.6.7
|
||||
"@storybook/react": 8.6.7
|
||||
find-up: ^5.0.0
|
||||
magic-string: ^0.30.0
|
||||
react-docgen: ^7.0.0
|
||||
resolve: ^1.22.8
|
||||
tsconfig-paths: ^4.2.0
|
||||
peerDependencies:
|
||||
"@storybook/test": 8.6.4
|
||||
"@storybook/test": 8.6.7
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
storybook: ^8.6.4
|
||||
storybook: ^8.6.7
|
||||
vite: ^4.0.0 || ^5.0.0 || ^6.0.0
|
||||
peerDependenciesMeta:
|
||||
"@storybook/test":
|
||||
optional: true
|
||||
checksum: 877ea89891e18a860b598b48df086db58d161bb840093dc4563d470eab61a5042730de356a306d7a2b9de96afe522caa8ebceb0a4210e9e1b924a2548c451d3c
|
||||
checksum: 75ed76ac6293520aaab02365a12ba93f360cb2f97b632e1c213de535e1d01e93c202f4a3795863ad273246b2bf1096e5b696ea531902d490534db470a712349f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/react@npm:8.6.4, @storybook/react@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/react@npm:8.6.4"
|
||||
"@storybook/react@npm:8.6.7, @storybook/react@npm:^8.3.5":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/react@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/components": 8.6.4
|
||||
"@storybook/components": 8.6.7
|
||||
"@storybook/global": ^5.0.0
|
||||
"@storybook/manager-api": 8.6.4
|
||||
"@storybook/preview-api": 8.6.4
|
||||
"@storybook/react-dom-shim": 8.6.4
|
||||
"@storybook/theming": 8.6.4
|
||||
"@storybook/manager-api": 8.6.7
|
||||
"@storybook/preview-api": 8.6.7
|
||||
"@storybook/react-dom-shim": 8.6.7
|
||||
"@storybook/theming": 8.6.7
|
||||
peerDependencies:
|
||||
"@storybook/test": 8.6.4
|
||||
"@storybook/test": 8.6.7
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
|
||||
storybook: ^8.6.4
|
||||
storybook: ^8.6.7
|
||||
typescript: ">= 4.2.x"
|
||||
peerDependenciesMeta:
|
||||
"@storybook/test":
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 4e9860c8e177d0dd496e7d6c53927daa8429b82f9c02d5b004de61244573901160a936ba37ce69810fb13e3602391e18723237a901c57e1bd19cf9f58241c30c
|
||||
checksum: c98438ddfd764300e1a805623fab976f9ef94a2d939984d6caf98ce92ed29d8a7307389b98e2df2d1302b302dc549c7c95050464731d9c588347cd154c360834
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/source-loader@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/source-loader@npm:8.6.4"
|
||||
"@storybook/source-loader@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/source-loader@npm:8.6.7"
|
||||
dependencies:
|
||||
es-toolkit: ^1.22.0
|
||||
estraverse: ^5.2.0
|
||||
prettier: ^3.1.1
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 3336f11b99014bb55a7ad88eabc8149ed0ffa4e4cc3d64e22cfc7d7e7f0b36987eda0ca55b6b20d805e89305f277104db6c60c2100eea439bc385808f6193bc2
|
||||
storybook: ^8.6.7
|
||||
checksum: 74d5dd20384c0e17720ba3492458362bfd2f527614f37cffef438b202d12f4f45c411d2568308975286c34eea05d7bcb1baeaf6605337b7906725f7257b542c6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/test@npm:8.6.4, @storybook/test@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/test@npm:8.6.4"
|
||||
"@storybook/test@npm:8.6.7, @storybook/test@npm:^8.3.5":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/test@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/global": ^5.0.0
|
||||
"@storybook/instrumenter": 8.6.4
|
||||
"@storybook/instrumenter": 8.6.7
|
||||
"@testing-library/dom": 10.4.0
|
||||
"@testing-library/jest-dom": 6.5.0
|
||||
"@testing-library/user-event": 14.5.2
|
||||
"@vitest/expect": 2.0.5
|
||||
"@vitest/spy": 2.0.5
|
||||
peerDependencies:
|
||||
storybook: ^8.6.4
|
||||
checksum: 2c16f1cc8e0f6e57f5972a664839885467c7a8b01cf807b32ea7cdd3508354cd408603f05b3f6d1c118e4560f4224102df6dfc0242deb0d2ea670895e8cdcc81
|
||||
storybook: ^8.6.7
|
||||
checksum: d269423580cbcc61de74a3940504f00bb580b389b04432aa6a0a7e8fed54536782ef254e8edcf6bac3ce98d35e439fdfd86316cf53f3435396f12cbc2e060541
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/theming@npm:8.6.4":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/theming@npm:8.6.4"
|
||||
"@storybook/theming@npm:8.6.7":
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/theming@npm:8.6.7"
|
||||
peerDependencies:
|
||||
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
|
||||
checksum: b7e13026c381e18e7ad2064e1528876eb01b7563ad16aababcac531ce76f5e59b985a72603f57d75465a611ea564bd46b9b1760634211faf4307f9265f06e044
|
||||
checksum: 747a2377df65d5f0962dc8606d06247ad89fb7ecabab0bd9611055d599b5bd0fa15769e4c76e948ae34ae232ba756bf6795ccb3467b1adf35ff961db7350f4de
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@storybook/types@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "@storybook/types@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "@storybook/types@npm:8.6.7"
|
||||
peerDependencies:
|
||||
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
|
||||
checksum: 99c59e960b9b4500c1bfcdcaedf0070fc7eb500c1d3226e39bc17c7ab3671fca89645abe30baf4dcfd65764d4955db6b2c416049aef78ab81535a27a4c671eb5
|
||||
checksum: bae7a202bda4f83f8f8d9a176ff79a033f2e67f5db4d9ee05cd468c3921d8bcc2a44d93a11c8401ad7b3a08f6a54510013791ce932117f10fc6b289642be18aa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4451,10 +4451,10 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"storybook@npm:^8.3.5":
|
||||
version: 8.6.4
|
||||
resolution: "storybook@npm:8.6.4"
|
||||
version: 8.6.7
|
||||
resolution: "storybook@npm:8.6.7"
|
||||
dependencies:
|
||||
"@storybook/core": 8.6.4
|
||||
"@storybook/core": 8.6.7
|
||||
peerDependencies:
|
||||
prettier: ^2 || ^3
|
||||
peerDependenciesMeta:
|
||||
@@ -4464,7 +4464,7 @@ __metadata:
|
||||
getstorybook: ./bin/index.cjs
|
||||
sb: ./bin/index.cjs
|
||||
storybook: ./bin/index.cjs
|
||||
checksum: 96c59b5c6ac27dfb9fb85d110b0257a77264015454dd0ca7893ae9df04dbf31e523ff351ba45283f33a58ecb28fd985349aa9c1fb83ab51d0c781a4d5809fa15
|
||||
checksum: fe21ce4d9cd17069a5b409ed518603f97ec6ec421b7f41cff3e43b409820fcbc2c8dbcb3aed99dd7d0a99a0c6138c7576ddf72f78186b6f86085d503b3c8b32f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -14589,13 +14589,13 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@playwright/test@npm:^1.32.3":
|
||||
version: 1.51.0
|
||||
resolution: "@playwright/test@npm:1.51.0"
|
||||
version: 1.51.1
|
||||
resolution: "@playwright/test@npm:1.51.1"
|
||||
dependencies:
|
||||
playwright: 1.51.0
|
||||
playwright: 1.51.1
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: 14959f930db55882ff45ba660dfd37e066bea2607619549f85b6c9c0591a895f4dcbc5419e2fc4069b03b02fef6d00628bc9382b65216c99735d6d93eb578374
|
||||
checksum: c3c14f37451cc0323234e37fa559a075e5aee9f5e6ca06fa3b4a132ec6c809909b22877daca1fa48c95a1112d859346c10111e7003f44582a05100dd5385bbef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -20229,12 +20229,12 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:^18.0.0":
|
||||
version: 18.3.18
|
||||
resolution: "@types/react@npm:18.3.18"
|
||||
version: 18.3.19
|
||||
resolution: "@types/react@npm:18.3.19"
|
||||
dependencies:
|
||||
"@types/prop-types": "*"
|
||||
csstype: ^3.0.2
|
||||
checksum: 5933597bc9f53e282f0438f0bb76d0f0fab60faabe760ea806e05ffe6f5c61b9b4d363e1a03a8fea47c510d493c6cf926cdeeba9f7074fa97b61940c350245e7
|
||||
checksum: 41305614686cfc618ef154f06d3f80e7b2df4673ebbc474880c524ecbbaa48f14620b3b1c6cff9f225176932d362c62fe617020027069157cb040752902c35cb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -39310,27 +39310,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright-core@npm:1.51.0":
|
||||
version: 1.51.0
|
||||
resolution: "playwright-core@npm:1.51.0"
|
||||
"playwright-core@npm:1.51.1":
|
||||
version: 1.51.1
|
||||
resolution: "playwright-core@npm:1.51.1"
|
||||
bin:
|
||||
playwright-core: cli.js
|
||||
checksum: e14798a7323539e1d6f076a6aa0492ea7c5fa89f4db85a53b27001b0f84bde2a5f8b14e25e05c5396594c89173a48398728522932bb7bd2006dd4bb48c8b0979
|
||||
checksum: 1eb37e22e97435a5ed6389b4caa666fbe618348861cae97e67586e20c8fed9ac3d3dc899ff3b9237d0ddfcf087d5b552b80be247e246fc45b75282f96be714bb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright@npm:1.51.0":
|
||||
version: 1.51.0
|
||||
resolution: "playwright@npm:1.51.0"
|
||||
"playwright@npm:1.51.1":
|
||||
version: 1.51.1
|
||||
resolution: "playwright@npm:1.51.1"
|
||||
dependencies:
|
||||
fsevents: 2.3.2
|
||||
playwright-core: 1.51.0
|
||||
playwright-core: 1.51.1
|
||||
dependenciesMeta:
|
||||
fsevents:
|
||||
optional: true
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: 02ec1077e2997872415fd40c9a5df6fd796d9cc15dade24bff9a88a16a451217744d850bdfc09efd4812d05c2fc856f92bfe106aae036fe21defa62c6d5b8ea5
|
||||
checksum: 2d78f53eed438d26df9743f98227d2e33bb5a378c8d0144a4ed75b4010c9c517d5a6ee6c1c9245ad61dc615054c5e19d2ded5a6e285e35d2bca39b05bc9c4a35
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user