diff --git a/.changeset/afraid-chairs-warn.md b/.changeset/afraid-chairs-warn.md deleted file mode 100644 index 1ae2a7513e..0000000000 --- a/.changeset/afraid-chairs-warn.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/errors': patch ---- - -Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`. -Rename the error name for unknown errors from `unknown` to `error`. diff --git a/.changeset/clever-singers-search.md b/.changeset/clever-singers-search.md new file mode 100644 index 0000000000..0750cea8a8 --- /dev/null +++ b/.changeset/clever-singers-search.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +Narrow the types returned by the request option functions, to only the specifics that they actually do return. The reason for this change is that a full `RequestInit` is unfortunate to return because it's different between `cross-fetch` and `node-fetch`. diff --git a/.changeset/curly-points-hide.md b/.changeset/curly-points-hide.md deleted file mode 100644 index ba513e5915..0000000000 --- a/.changeset/curly-points-hide.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-tech-insights-backend': patch -'@backstage/plugin-tech-insights-backend-module-jsonfc': patch ---- - -Update README docs to use correct function/parameter names diff --git a/.changeset/dry-pianos-brush.md b/.changeset/dry-pianos-brush.md new file mode 100644 index 0000000000..c749962a83 --- /dev/null +++ b/.changeset/dry-pianos-brush.md @@ -0,0 +1,51 @@ +--- +'@backstage/create-app': patch +--- + +Incorporate usage of the tokenManager into the backend created using `create-app`. + +In existing backends, update the `PluginEnvironment` to include a `tokenManager`: + +```diff +// packages/backend/src/types.ts + +... +import { + ... ++ TokenManager, +} from '@backstage/backend-common'; + +export type PluginEnvironment = { + ... ++ tokenManager: TokenManager; +}; +``` + +Then, create a `ServerTokenManager`. This can either be a `noop` that requires no secret and validates all requests by default, or one that uses a secret from your `app-config.yaml` to generate and validate tokens. + +```diff +// packages/backend/src/index.ts + +... +import { + ... ++ ServerTokenManager, +} from '@backstage/backend-common'; +... + +function makeCreateEnv(config: Config) { + ... + // CHOOSE ONE + // TokenManager not requiring a secret ++ const tokenManager = ServerTokenManager.noop(); + // OR TokenManager requiring a secret ++ const tokenManager = ServerTokenManager.fromConfig(config); + + ... + return (plugin: string): PluginEnvironment => { + ... +- return { logger, cache, database, config, reader, discovery }; ++ return { logger, cache, database, config, reader, discovery, tokenManager }; + }; +} +``` diff --git a/.changeset/early-dragons-wave.md b/.changeset/early-dragons-wave.md deleted file mode 100644 index 641f36a9f3..0000000000 --- a/.changeset/early-dragons-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Bump `react-jsonschema-form` diff --git a/.changeset/eleven-suns-type.md b/.changeset/eleven-suns-type.md deleted file mode 100644 index b16f26c968..0000000000 --- a/.changeset/eleven-suns-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins': patch ---- - -Fix Jenkins project table pagination. diff --git a/.changeset/forty-teachers-argue.md b/.changeset/forty-teachers-argue.md deleted file mode 100644 index 2e32719037..0000000000 --- a/.changeset/forty-teachers-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Switched to using the standardized JSON error responses for all provider endpoints. diff --git a/.changeset/fresh-pumas-collect.md b/.changeset/fresh-pumas-collect.md new file mode 100644 index 0000000000..15d36472ed --- /dev/null +++ b/.changeset/fresh-pumas-collect.md @@ -0,0 +1,19 @@ +--- +'@backstage/config-loader': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-badges-backend': patch +'@backstage/plugin-bitrise': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-code-coverage-backend': patch +'@backstage/plugin-fossa': patch +'@backstage/plugin-jenkins-backend': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-tech-insights-backend': patch +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-todo-backend': patch +--- + +Align on usage of `cross-fetch` vs `node-fetch` in frontend vs backend packages, and remove some unnecessary imports of either one of them diff --git a/.changeset/giant-bees-applaud.md b/.changeset/giant-bees-applaud.md deleted file mode 100644 index c4591dbc64..0000000000 --- a/.changeset/giant-bees-applaud.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Switch the default test coverage provider from the jest default one to `'v8'`, which provides much better coverage information when using the default Backstage test setup. This is considered a bug fix as the current coverage information is often very inaccurate. diff --git a/.changeset/green-bags-hug.md b/.changeset/green-bags-hug.md deleted file mode 100644 index 9d250062f7..0000000000 --- a/.changeset/green-bags-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Implement support for formatting OpenShift dashboard url links diff --git a/.changeset/green-toes-search.md b/.changeset/green-toes-search.md new file mode 100644 index 0000000000..2bdba7e31a --- /dev/null +++ b/.changeset/green-toes-search.md @@ -0,0 +1,28 @@ +--- +'@backstage/plugin-techdocs-backend': minor +--- + +**BREAKING** `DefaultTechDocsCollator` has a new required option `tokenManager`. See the create-app changelog for how to create a `tokenManager` and add it to the `PluginEnvironment`. It can then be passed to the collator in `createPlugin`: + +```diff +// packages/backend/src/plugins/search.ts + +... +export default async function createPlugin({ + ... ++ tokenManager, +}: PluginEnvironment) { + ... + + indexBuilder.addCollator({ + defaultRefreshIntervalSeconds: 600, + collator: DefaultTechDocsCollator.fromConfig(config, { + discovery, + logger, ++ tokenManager, + }), + }); + + ... +} +``` diff --git a/.changeset/heavy-tools-hang.md b/.changeset/heavy-tools-hang.md deleted file mode 100644 index 0c47202e46..0000000000 --- a/.changeset/heavy-tools-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-permission-node': minor ---- - -New package containing common permission and authorization utilities for backend plugins. For more information, see the [authorization PRFC](https://github.com/backstage/backstage/pull/7761). diff --git a/.changeset/large-mugs-repair.md b/.changeset/large-mugs-repair.md deleted file mode 100644 index 5043fb80e1..0000000000 --- a/.changeset/large-mugs-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Disable ES transforms in tests transformed by the `jestSucraseTransform.js`. This is not considered a breaking change since all code is already transpiled this way in the development setup. diff --git a/.changeset/large-pears-agree.md b/.changeset/large-pears-agree.md deleted file mode 100644 index 60f755eff0..0000000000 --- a/.changeset/large-pears-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -DefaultTechDocsCollator is now included in the search backend, and the Search Page updated with the SearchType component that includes the techdocs type diff --git a/.changeset/lemon-moons-stare.md b/.changeset/lemon-moons-stare.md new file mode 100644 index 0000000000..4b7818d403 --- /dev/null +++ b/.changeset/lemon-moons-stare.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Create a `TokenManager` interface and `ServerTokenManager` implementation to generate and validate server tokens for authenticated backend-to-backend API requests. diff --git a/.changeset/many-waves-visit.md b/.changeset/many-waves-visit.md deleted file mode 100644 index 4f4711f371..0000000000 --- a/.changeset/many-waves-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Providing an empty values array in an EntityFilter will now return no matches. diff --git a/.changeset/olive-rats-destroy.md b/.changeset/olive-rats-destroy.md new file mode 100644 index 0000000000..5908fc0f10 --- /dev/null +++ b/.changeset/olive-rats-destroy.md @@ -0,0 +1,6 @@ +--- +'@backstage/cli': patch +'@techdocs/cli': patch +--- + +Bump react-dev-utils to v12 diff --git a/.changeset/pretty-trains-appear.md b/.changeset/pretty-trains-appear.md deleted file mode 100644 index 9a7b313524..0000000000 --- a/.changeset/pretty-trains-appear.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/cli': patch -'@backstage/core-app-api': patch -'@backstage/create-app': patch -'@backstage/techdocs-common': patch -'@backstage/plugin-auth-backend': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-badges-backend': patch -'@backstage/plugin-bazaar-backend': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-code-coverage-backend': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-jenkins-backend': patch -'@backstage/plugin-proxy-backend': patch -'@backstage/plugin-rollbar-backend': patch -'@backstage/plugin-search-backend': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-techdocs-backend': patch ---- - -Change default port of backend from 7000 to 7007. - -This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. - -You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: - -``` -backend: - listen: 0.0.0.0:7123 - baseUrl: http://localhost:7123 -``` - -More information can be found here: https://backstage.io/docs/conf/writing diff --git a/.changeset/purple-grapes-attack.md b/.changeset/purple-grapes-attack.md deleted file mode 100644 index a2a99690e9..0000000000 --- a/.changeset/purple-grapes-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-permission-common': minor ---- - -Accept configApi rather than enabled flag in PermissionClient constructor. diff --git a/.changeset/rude-brooms-raise.md b/.changeset/rude-brooms-raise.md new file mode 100644 index 0000000000..6b5bd3ca3a --- /dev/null +++ b/.changeset/rude-brooms-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': patch +--- + +Tweaked the logged deprecation warning for `createRouteRef` to hopefully make it more clear. diff --git a/.changeset/sharp-carrots-press.md b/.changeset/sharp-carrots-press.md deleted file mode 100644 index a7c31d6759..0000000000 --- a/.changeset/sharp-carrots-press.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-config-schema': patch -'@backstage/plugin-scaffolder': patch ---- - -Fixed a missing `await` when throwing server side errors diff --git a/.changeset/shiny-insects-remember.md b/.changeset/shiny-insects-remember.md deleted file mode 100644 index a26882b65d..0000000000 --- a/.changeset/shiny-insects-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Fix bug where there was error log lines written when failing to `JSON.parse` things that were not `JSON` values. diff --git a/.changeset/slow-olives-confess.md b/.changeset/slow-olives-confess.md deleted file mode 100644 index 68b2539157..0000000000 --- a/.changeset/slow-olives-confess.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/core-plugin-api': patch ---- - -Deprecated `register` option of `createPlugin` and the `outputs` methods of the plugin instance. - -Introduces the `featureFlags` property to define your feature flags instead. diff --git a/.changeset/smart-fans-complain.md b/.changeset/smart-fans-complain.md deleted file mode 100644 index db404f110d..0000000000 --- a/.changeset/smart-fans-complain.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -**BREAKING** EntitiesSearchFilter fields have changed. - -EntitiesSearchFilter now has only two fields: `key` and `value`. The `matchValueIn` and `matchValueExists` fields are no longer are supported. Previous filters written using the `matchValueIn` and `matchValueExists` fields can be rewritten as follows: - -Filtering by existence of key only: - -```diff - filter: { - { - key: 'abc', -- matchValueExists: true, - }, - } -``` - -Filtering by key and values: - -```diff - filter: { - { - key: 'abc', -- matchValueExists: true, -- matchValueIn: ['xyz'], -+ values: ['xyz'], - }, - } -``` - -Negation of filters can now be achieved through a `not` object: - -``` -filter: { - not: { - key: 'abc', - values: ['xyz'], - }, -} -``` diff --git a/.changeset/smooth-vans-boil.md b/.changeset/smooth-vans-boil.md deleted file mode 100644 index 39b2ea386f..0000000000 --- a/.changeset/smooth-vans-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Index User entities by displayName to be able to search by full name. Added displayName (if present) to the 'text' field in the indexed document. diff --git a/.changeset/strong-seahorses-scream.md b/.changeset/strong-seahorses-scream.md deleted file mode 100644 index bbd1d44378..0000000000 --- a/.changeset/strong-seahorses-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Added Azure DevOps discovery processor diff --git a/.changeset/tasty-deers-play.md b/.changeset/tasty-deers-play.md new file mode 100644 index 0000000000..cf5322b0bd --- /dev/null +++ b/.changeset/tasty-deers-play.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Expose catalog lib in plugin-auth-backend, i.e `CatalogIdentityClient` class is exposed now. diff --git a/.changeset/thirty-fireants-occur.md b/.changeset/thirty-fireants-occur.md deleted file mode 100644 index dc9348ac6e..0000000000 --- a/.changeset/thirty-fireants-occur.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Detect a duplicate entities when adding locations through dry run diff --git a/.changeset/thirty-tigers-refuse.md b/.changeset/thirty-tigers-refuse.md deleted file mode 100644 index 0797d36747..0000000000 --- a/.changeset/thirty-tigers-refuse.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/create-app': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Bump gitbeaker to the latest version diff --git a/.changeset/weak-berries-sing.md b/.changeset/weak-berries-sing.md deleted file mode 100644 index 1b0cb8d719..0000000000 --- a/.changeset/weak-berries-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Update the default routes to use id instead of title diff --git a/.changeset/wet-seas-deliver.md b/.changeset/wet-seas-deliver.md deleted file mode 100644 index da5679eb8e..0000000000 --- a/.changeset/wet-seas-deliver.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -'@backstage/core-app-api': patch -'@backstage/test-utils': patch ---- - -The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`. - -These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs. - -When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code: - -```tsx -render( - - {...} - -) -``` - -Would be migrated to this: - -```tsx -render( - - {...} - -) -``` - -In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array. - -Usage that looks like this: - -```ts -const apis = ApiRegistry.with( - identityApiRef, - mockIdentityApi as unknown as IdentityApi, -).with(configApiRef, new ConfigReader({})); -``` - -OR like this: - -```ts -const apis = ApiRegistry.from([ - [identityApiRef, mockIdentityApi as unknown as IdentityApi], - [configApiRef, new ConfigReader({})], -]); -``` - -Would be migrated to this: - -```ts -const apis = TestApiRegistry.from( - [identityApiRef, mockIdentityApi], - [configApiRef, new ConfigReader({})], -); -``` - -If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`. diff --git a/.changeset/witty-cats-tell.md b/.changeset/witty-cats-tell.md new file mode 100644 index 0000000000..82e33bc0a6 --- /dev/null +++ b/.changeset/witty-cats-tell.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Add group filtering to the scaffolder page so that individuals can surface specific templates to end users ahead of others, or group templates together. This can be accomplished by passing in a `groups` prop to the `ScaffolderPage` + +``` + + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + ]} +/> +``` diff --git a/.changeset/yellow-apes-brush.md b/.changeset/yellow-apes-brush.md deleted file mode 100644 index 06b33e97d1..0000000000 --- a/.changeset/yellow-apes-brush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Adjusted some styles in the OpenAPI definition, for elements which were barely readable in dark mode. diff --git a/.changeset/yellow-pandas-draw.md b/.changeset/yellow-pandas-draw.md deleted file mode 100644 index 6d4f8116b3..0000000000 --- a/.changeset/yellow-pandas-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Pin sidebar by default for easier navigation diff --git a/.changeset/young-bikes-argue.md b/.changeset/young-bikes-argue.md new file mode 100644 index 0000000000..a9f09cd12b --- /dev/null +++ b/.changeset/young-bikes-argue.md @@ -0,0 +1,27 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +**BREAKING** `DefaultCatalogCollator` has a new required option `tokenManager`. See the create-app changelog for how to create a `tokenManager` and add it to the `PluginEnvironment`. It can then be passed to the collator in `createPlugin`: + +```diff +// packages/backend/src/plugins/search.ts + +... +export default async function createPlugin({ + ... ++ tokenManager, +}: PluginEnvironment) { + ... + + indexBuilder.addCollator({ + defaultRefreshIntervalSeconds: 600, + collator: DefaultCatalogCollator.fromConfig(config, { + discovery, ++ tokenManager, + }), + }); + + ... +} +``` diff --git a/.changeset/young-hats-shop.md b/.changeset/young-hats-shop.md deleted file mode 100644 index 4db46cf9d9..0000000000 --- a/.changeset/young-hats-shop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -Supply featureFlags using featureFlag config option. diff --git a/.changeset/young-sheep-impress.md b/.changeset/young-sheep-impress.md deleted file mode 100644 index b69447d4d4..0000000000 --- a/.changeset/young-sheep-impress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-graphiql': patch ---- - -Letting GraphiQL use headers diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9be92d294e..195abc6b11 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,6 +18,13 @@ /plugins/techdocs-backend @backstage/techdocs-core /plugins/ilert @backstage/reviewers @yacut /plugins/home @backstage/techdocs-core +/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin +/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin +/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin +/tech-insights-backend @backstage/reviewers @xantier @iain-b +/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b +/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b +/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b /packages/embedded-techdocs-app @backstage/techdocs-core /packages/search-common @backstage/techdocs-core /packages/techdocs-cli @backstage/techdocs-core diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 3cd0d53720..0000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 60 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security - - plugin - - help wanted - - good first issue - - rfc -# Label to use when marking an issue as stale -staleLabel: stale -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..96a2a20109 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,30 @@ +name: 'Stale workflow' +on: + workflow_dispatch: + schedule: + - cron: '*/10 * * * *' # run every 10 minutes as it also removes labels. + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@main + id: stale + with: + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + days-before-issue-stale: 60 + days-before-issue-close: 7 + exempt-issue-labels: 'pinned,security,plugin,help wanted,good first issue,rfc' + stale-issue-label: stale + stale-pr-message: > + This PR has been automatically marked as stale because it has not had + recent activity from the author. It will be closed if no further activity occurs. + If you are the author and the PR has been closed, feel free to re-open the PR and continue the contribution! + days-before-pr-stale: 7 + days-before-pr-close: 3 + exempt-pr-labels: reviewer-approved,awaiting-review + stale-pr-label: stale + operations-per-run: 100 diff --git a/app-config.yaml b/app-config.yaml index 50ddb7ce89..913a72dbab 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -23,6 +23,11 @@ app: title: '#backstage' backend: + # Used for enabling authentication, secret is shared by all backend plugins + # See backend-to-backend-auth.md in the docs for information on the format + # auth: + # keys: + # - secret: ${BACKEND_SECRET} baseUrl: http://localhost:7007 listen: port: 7007 diff --git a/docs/assets/software-templates/grouped-templates.png b/docs/assets/software-templates/grouped-templates.png new file mode 100644 index 0000000000..9a3689ad1e Binary files /dev/null and b/docs/assets/software-templates/grouped-templates.png differ diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md index 27219e529a..7c3717e22e 100644 --- a/docs/features/search/getting-started.md +++ b/docs/features/search/getting-started.md @@ -154,13 +154,17 @@ import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend'; export default async function createPlugin({ logger, discovery, + tokenManager, }: PluginEnvironment) { const searchEngine = new LunrSearchEngine({ logger }); const indexBuilder = new IndexBuilder({ logger, searchEngine }); indexBuilder.addCollator({ defaultRefreshIntervalSeconds: 600, - collator: new DefaultCatalogCollator({ discovery }), + collator: new DefaultCatalogCollator({ + discovery, + tokenManager, + }), }); const { scheduler } = await indexBuilder.build(); @@ -285,7 +289,10 @@ const indexBuilder = new IndexBuilder({ logger, searchEngine }); indexBuilder.addCollator({ defaultRefreshIntervalSeconds: 600, - collator: new DefaultCatalogCollator({ discovery }), + collator: new DefaultCatalogCollator({ + discovery, + tokenManager, + }), }); indexBuilder.addCollator({ @@ -303,6 +310,9 @@ its `defaultRefreshIntervalSeconds` value, like this: ```typescript {3} indexBuilder.addCollator({ defaultRefreshIntervalSeconds: 600, - collator: new DefaultCatalogCollator({ discovery }), + collator: new DefaultCatalogCollator({ + discovery, + tokenManager, + }), }); ``` diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md index 621d46b51e..4ba44b8ef4 100644 --- a/docs/features/search/how-to-guides.md +++ b/docs/features/search/how-to-guides.md @@ -2,7 +2,7 @@ id: how-to-guides title: Search "HOW TO" guides sidebar_label: "HOW TO" guides -description: Search "HOW TO" guides +description: Search "HOW TO" guides --- ## How to implement your own Search API @@ -74,6 +74,7 @@ indexBuilder.addCollator({ collator: DefaultTechDocsCollator.fromConfig(config, { discovery, logger, + tokenManager, }), }); ``` diff --git a/docs/features/software-templates/configuration.md b/docs/features/software-templates/configuration.md index 737f77f67b..b57bb96818 100644 --- a/docs/features/software-templates/configuration.md +++ b/docs/features/software-templates/configuration.md @@ -53,3 +53,30 @@ You can do so by including the following lines in the last step of your RUN apt-get update && apt-get install -y python3 python3-pip RUN pip3 install cookiecutter ``` + +### Customizing the ScaffolderPage with Grouping and Filtering + +Once you have more than a few software templates you may want to customize your +`ScaffolderPage` by grouping and surfacing certain templates together. You can +accomplish this by creating `groups` and passing them to your `ScaffolderPage` +like below + +``` + + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + ]} +/> +``` + +This code will group all templates with the 'recommended' tag together at the +top of the page above any other templates not filtered by this group or others. + +You can also further customize groups by passing in a `titleComponent` instead +of a `title` which will be a component to use as the header instead of just the +default `ContentHeader` with the `title` set as it's value. +![Grouped Templates](../../assets/software-templates/grouped-templates.png) diff --git a/docs/tutorials/backend-to-backend-auth.md b/docs/tutorials/backend-to-backend-auth.md new file mode 100644 index 0000000000..3cb72a4827 --- /dev/null +++ b/docs/tutorials/backend-to-backend-auth.md @@ -0,0 +1,68 @@ +--- +id: backend-to-backend-auth +title: Backend-to-Backend Authentication +description: + Guide for authenticating API requests between Backstage plugin backends +--- + +This tutorial describes the steps needed to handle _backend-to-backend +authentication_, which allows plugin backends to determine whether a given +request originates from a legitimate Backstage backend by verifying a token +signed with a shared secret. This system has limited use for now, but will be +needed to support the upcoming framework for permissions and authorization (see +[the PRFC on the topic](https://github.com/backstage/backstage/pull/7761) for +more details). + +Backends have no concept of a Backstage identity, so instead they use a token +generated using a shared key stored in config. You can generate a unique key for +your app in a terminal, and set the `BACKEND_SECRET` environment variable to the +resulting value. + +```bash +node -p 'require("crypto").randomBytes(24).toString("base64")' +``` + +Requests originating from a backend plugin can be authenticated by decorating +them with a backend token. Backend tokens can be generated using a +`TokenManager`, which can be passed to plugin backends via the +`PluginEnvironment`. The `TokenManager` provided in new Backstage instances +generated by `create-app` is a stub, which returns empty tokens and accepts any +input string as valid. To enable backend-to-backend authentication, you'll need +to instantiate a new one using the secret from your config instead: + +```diff +// packages/backend/src/index.ts + +function makeCreateEnv(config: Config) { + const root = getRootLogger(); + const reader = UrlReaders.default({ logger: root, config }); + const discovery = SingleHostDiscovery.fromConfig(config); + + root.info(`Created UrlReader ${reader}`); + + const cacheManager = CacheManager.fromConfig(config); + const databaseManager = DatabaseManager.fromConfig(config); +- const tokenManager = ServerTokenManager.noop(); ++ const tokenManager = ServerTokenManager.fromConfig(config); +``` + +With this `tokenManager`, you can then generate a server token for requests: + +```typescript +const { token } = await this.tokenManager.getToken(); + +const response = await fetch(pluginBackendApiUrl, { + method: 'GET', + headers: { + ...headers, + Authorization: `Bearer ${token}`, + }, +}); +``` + +You can use the same `tokenManager` to authenticate tokens supplied on incoming +requests: + +```typescript +await tokenManager.authenticate(token); // throws if token is invalid +``` diff --git a/microsite/package.json b/microsite/package.json index a2a2fd98de..a24de8935d 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -19,7 +19,7 @@ "@spotify/prettier-config": "^12.0.0", "docusaurus": "^2.0.0-alpha.70", "js-yaml": "^4.1.0", - "prettier": "^2.4.1", + "prettier": "^2.5.0", "yarn-lock-check": "^1.0.5" }, "prettier": "@spotify/prettier-config" diff --git a/microsite/yarn.lock b/microsite/yarn.lock index c03ecf989a..9946f4914d 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -5190,10 +5190,10 @@ prepend-http@^2.0.0: resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" - integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== +prettier@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz#a6370e2d4594e093270419d9cc47f7670488f893" + integrity sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg== prismjs@^1.22.0: version "1.25.0" diff --git a/package.json b/package.json index 16ba8461ac..9adbb1c821 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ "prettier:check": "prettier --check .", "lerna": "lerna", "storybook": "yarn workspace storybook start", + "snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false", + "snyk:test:package": "yarn snyk:test --include", "build-storybook": "yarn workspace storybook build-storybook", "techdocs-cli": "node scripts/techdocs-cli.js", "techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index ea5e4446eb..7e5d3db926 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -176,7 +176,20 @@ const routes = ( > {techDocsPage} - }> + + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + ]} + /> + } + > diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 3ab36cc679..869bf46019 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/backend-common +## 0.9.11 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/errors@0.1.5 + ## 0.9.10 ### Patch Changes diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index e3119e78a8..9c66c0573e 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -526,6 +526,20 @@ export type SearchResponseFile = { content(): Promise; }; +// @public +export class ServerTokenManager implements TokenManager { + // (undocumented) + authenticate(token: string): Promise; + // (undocumented) + static fromConfig(config: Config): ServerTokenManager; + // (undocumented) + getToken(): Promise<{ + token: string; + }>; + // (undocumented) + static noop(): TokenManager; +} + // @public (undocumented) export type ServiceBuilder = { loadConfig(config: Config): ServiceBuilder; @@ -583,6 +597,16 @@ export interface StatusCheckHandlerOptions { statusCheck?: StatusCheck; } +// @public +export interface TokenManager { + // (undocumented) + authenticate: (token: string) => Promise; + // (undocumented) + getToken: () => Promise<{ + token: string; + }>; +} + // @public export type UrlReader = { read(url: string): Promise; diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 3690069431..bdc7740c13 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -20,6 +20,20 @@ export interface Config { }; backend: { + /** Backend configuration for when request authentication is enabled */ + auth?: { + /** Keys shared by all backends for signing and validating backend tokens. */ + keys: { + /** + * Secret for generating tokens. Should be a base64 string, recommended + * length is 24 bytes. + * + * @visibility secret + */ + secret: string; + }[]; + }; + baseUrl: string; // defined in core, but repeated here without doc /** Address that the backend should listen to. */ diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 9e0187137d..7fb2de0fe0 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.9.10", + "version": "0.9.11", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -32,7 +32,7 @@ "@backstage/cli-common": "^0.1.6", "@backstage/config": "^0.1.11", "@backstage/config-loader": "^0.8.0", - "@backstage/errors": "^0.1.4", + "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.9", "@backstage/types": "^0.1.1", "@google-cloud/storage": "^5.8.0", @@ -54,6 +54,7 @@ "git-url-parse": "^11.6.0", "helmet": "^4.0.0", "isomorphic-git": "^1.8.0", + "jose": "^1.27.1", "keyv": "^4.0.3", "keyv-memcache": "^1.2.5", "knex": "^0.95.1", @@ -80,8 +81,8 @@ } }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/test-utils": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/test-utils": "^0.1.23", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", "@types/concat-stream": "^1.6.0", diff --git a/packages/backend-common/src/index.ts b/packages/backend-common/src/index.ts index c214961a2e..e430ddc1a4 100644 --- a/packages/backend-common/src/index.ts +++ b/packages/backend-common/src/index.ts @@ -31,4 +31,5 @@ export * from './paths'; export * from './reading'; export * from './scm'; export * from './service'; +export * from './tokens'; export * from './util'; diff --git a/packages/backend-common/src/tokens/ServerTokenManager.test.ts b/packages/backend-common/src/tokens/ServerTokenManager.test.ts new file mode 100644 index 0000000000..fe724a0a4b --- /dev/null +++ b/packages/backend-common/src/tokens/ServerTokenManager.test.ts @@ -0,0 +1,189 @@ +/* + * Copyright 2021 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 { ConfigReader } from '@backstage/config'; +import { TokenManager } from './types'; +import { ServerTokenManager } from './ServerTokenManager'; + +const emptyConfig = new ConfigReader({}); +const configWithSecret = new ConfigReader({ + backend: { auth: { keys: [{ secret: 'a-secret-key' }] } }, +}); + +describe('ServerTokenManager', () => { + it('should throw if secret in config does not exist', () => { + expect(() => ServerTokenManager.fromConfig(emptyConfig)).toThrowError(); + }); + + describe('getToken', () => { + it('should return a token if secret in config exists', async () => { + const tokenManager = ServerTokenManager.fromConfig(configWithSecret); + expect((await tokenManager.getToken()).token).toBeDefined(); + }); + + it('should return a token string if using a noop TokenManager', async () => { + const tokenManager = ServerTokenManager.noop(); + expect((await tokenManager.getToken()).token).toBeDefined(); + }); + }); + + describe('authenticate', () => { + it('should not throw if token is valid', async () => { + const tokenManager = ServerTokenManager.fromConfig(configWithSecret); + const { token } = await tokenManager.getToken(); + await expect(tokenManager.authenticate(token)).resolves.not.toThrow(); + }); + + it('should throw if token is invalid', async () => { + const tokenManager = ServerTokenManager.fromConfig(configWithSecret); + await expect( + tokenManager.authenticate('random-string'), + ).rejects.toThrowError(/invalid server token/i); + }); + + it('should validate server tokens created by a different instance using the same secret', async () => { + const tokenManager1 = ServerTokenManager.fromConfig(configWithSecret); + const tokenManager2 = ServerTokenManager.fromConfig(configWithSecret); + + const { token } = await tokenManager1.getToken(); + + await expect(tokenManager2.authenticate(token)).resolves.not.toThrow(); + }); + + it('should validate server tokens created using any of the secrets', async () => { + const tokenManager1 = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [{ secret: 'a1b2c3' }] } }, + }), + ); + const tokenManager2 = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [{ secret: 'd4e5f6' }] } }, + }), + ); + const tokenManager3 = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { + auth: { keys: [{ secret: 'a1b2c3' }, { secret: 'd4e5f6' }] }, + }, + }), + ); + + const { token: token1 } = await tokenManager1.getToken(); + await expect(tokenManager3.authenticate(token1)).resolves.not.toThrow(); + + const { token: token2 } = await tokenManager2.getToken(); + await expect(tokenManager3.authenticate(token2)).resolves.not.toThrow(); + }); + + it('should throw for server tokens created using a different secret', async () => { + const tokenManager1 = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [{ secret: 'a1b2c3' }] } }, + }), + ); + const tokenManager2 = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [{ secret: 'd4e5f6' }] } }, + }), + ); + + const { token } = await tokenManager1.getToken(); + + await expect(tokenManager2.authenticate(token)).rejects.toThrowError( + /invalid server token/i, + ); + }); + + it('should throw for server tokens created using a noop TokenManager', async () => { + const noopTokenManager = ServerTokenManager.noop(); + const tokenManager = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [{ secret: 'a1b2c3' }] } }, + }), + ); + + const { token } = await noopTokenManager.getToken(); + + await expect(tokenManager.authenticate(token)).rejects.toThrowError( + /invalid server token/i, + ); + }); + }); + + describe('ServerTokenManager.fromConfig', () => { + it('should throw if backend auth configuration is missing', () => { + expect(() => + ServerTokenManager.fromConfig(new ConfigReader({})), + ).toThrow(); + }); + + it('should throw if no keys are included in the configuration', () => { + expect(() => + ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [] } }, + }), + ), + ).toThrow(); + }); + + it('should throw if any key is missing a secret property', () => { + expect(() => + ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { + auth: { + keys: [{ secret: '123' }, {}, { secret: '789' }], + }, + }, + }), + ), + ).toThrow(); + }); + }); + + describe('ServerTokenManager.noop', () => { + let noopTokenManager: TokenManager; + + beforeEach(() => { + noopTokenManager = ServerTokenManager.noop(); + }); + + it('should accept tokens it generates', async () => { + const { token } = await noopTokenManager.getToken(); + + await expect(noopTokenManager.authenticate(token)).resolves.not.toThrow(); + }); + + it('should accept tokens generated by other noop token managers', async () => { + const noopTokenManager2 = ServerTokenManager.noop(); + await expect( + noopTokenManager.authenticate( + ( + await noopTokenManager2.getToken() + ).token, + ), + ).resolves.not.toThrow(); + }); + + it('should accept signed tokens', async () => { + const tokenManager = ServerTokenManager.fromConfig(configWithSecret); + await expect( + noopTokenManager.authenticate((await tokenManager.getToken()).token), + ).resolves.not.toThrow(); + }); + }); +}); diff --git a/packages/backend-common/src/tokens/ServerTokenManager.ts b/packages/backend-common/src/tokens/ServerTokenManager.ts new file mode 100644 index 0000000000..35a07509b6 --- /dev/null +++ b/packages/backend-common/src/tokens/ServerTokenManager.ts @@ -0,0 +1,80 @@ +/* + * Copyright 2021 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 { JWKS, JWK, JWT } from 'jose'; +import { Config } from '@backstage/config'; +import { AuthenticationError } from '@backstage/errors'; +import { TokenManager } from './types'; + +class NoopTokenManager implements TokenManager { + async getToken() { + return { token: '' }; + } + + async authenticate() {} +} + +/** + * Creates and validates tokens for use during backend-to-backend + * authentication. + * + * @public + */ +export class ServerTokenManager implements TokenManager { + private readonly verificationKeys: JWKS.KeyStore; + private readonly signingKey: JWK.Key; + + static noop(): TokenManager { + return new NoopTokenManager(); + } + + static fromConfig(config: Config) { + return new ServerTokenManager( + config + .getConfigArray('backend.auth.keys') + .map(key => key.getString('secret')), + ); + } + + private constructor(secrets?: string[]) { + if (!secrets?.length) { + throw new Error( + 'No secrets provided when constructing ServerTokenManager', + ); + } + + this.verificationKeys = new JWKS.KeyStore( + secrets.map(k => JWK.asKey({ kty: 'oct', k })), + ); + this.signingKey = this.verificationKeys.all()[0]; + } + + async getToken(): Promise<{ token: string }> { + const jwt = JWT.sign({ sub: 'backstage-server' }, this.signingKey, { + algorithm: 'HS256', + }); + + return { token: jwt }; + } + + async authenticate(token: string): Promise { + try { + JWT.verify(token, this.verificationKeys); + } catch (e) { + throw new AuthenticationError('Invalid server token'); + } + } +} diff --git a/packages/backend-common/src/tokens/index.ts b/packages/backend-common/src/tokens/index.ts new file mode 100644 index 0000000000..43ff12e597 --- /dev/null +++ b/packages/backend-common/src/tokens/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 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 { ServerTokenManager } from './ServerTokenManager'; +export type { TokenManager } from './types'; diff --git a/packages/backend-common/src/tokens/types.ts b/packages/backend-common/src/tokens/types.ts new file mode 100644 index 0000000000..1fea018db9 --- /dev/null +++ b/packages/backend-common/src/tokens/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 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. + */ + +/** + * Interface for creating and validating tokens. + * + * @public + */ +export interface TokenManager { + getToken: () => Promise<{ token: string }>; + authenticate: (token: string) => Promise; +} diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 7cf7d9ddaa..20e0d7885b 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,27 @@ # example-backend +## 0.2.54 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-backend@0.3.19 + - @backstage/plugin-tech-insights-backend@0.1.2 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.1 + - @backstage/plugin-auth-backend@0.4.9 + - @backstage/plugin-scaffolder-backend@0.15.14 + - @backstage/plugin-catalog-backend@0.18.0 + - @backstage/plugin-kafka-backend@0.2.12 + - @backstage/backend-common@0.9.11 + - @backstage/plugin-azure-devops-backend@0.2.2 + - @backstage/plugin-badges-backend@0.1.12 + - @backstage/plugin-code-coverage-backend@0.1.15 + - @backstage/plugin-jenkins-backend@0.1.8 + - @backstage/plugin-proxy-backend@0.2.14 + - @backstage/plugin-rollbar-backend@0.1.16 + - @backstage/plugin-search-backend@0.2.7 + - @backstage/plugin-techdocs-backend@0.10.9 + ## 0.2.52 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 1cef99c6a0..bf18728b9f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.52", + "version": "0.2.54", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,33 +24,33 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@backstage/integration": "^0.6.8", "@backstage/plugin-app-backend": "^0.3.19", - "@backstage/plugin-auth-backend": "^0.4.8", - "@backstage/plugin-azure-devops-backend": "^0.2.1", - "@backstage/plugin-badges-backend": "^0.1.11", - "@backstage/plugin-catalog-backend": "^0.17.4", - "@backstage/plugin-code-coverage-backend": "^0.1.14", + "@backstage/plugin-auth-backend": "^0.4.9", + "@backstage/plugin-azure-devops-backend": "^0.2.2", + "@backstage/plugin-badges-backend": "^0.1.12", + "@backstage/plugin-catalog-backend": "^0.18.0", + "@backstage/plugin-code-coverage-backend": "^0.1.15", "@backstage/plugin-graphql-backend": "^0.1.9", - "@backstage/plugin-jenkins-backend": "^0.1.7", - "@backstage/plugin-kubernetes-backend": "^0.3.18", - "@backstage/plugin-kafka-backend": "^0.2.11", - "@backstage/plugin-proxy-backend": "^0.2.13", - "@backstage/plugin-rollbar-backend": "^0.1.15", - "@backstage/plugin-scaffolder-backend": "^0.15.13", + "@backstage/plugin-jenkins-backend": "^0.1.8", + "@backstage/plugin-kubernetes-backend": "^0.3.19", + "@backstage/plugin-kafka-backend": "^0.2.12", + "@backstage/plugin-proxy-backend": "^0.2.14", + "@backstage/plugin-rollbar-backend": "^0.1.16", + "@backstage/plugin-scaffolder-backend": "^0.15.14", "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.7", - "@backstage/plugin-search-backend": "^0.2.6", + "@backstage/plugin-search-backend": "^0.2.7", "@backstage/plugin-search-backend-node": "^0.4.2", "@backstage/plugin-search-backend-module-elasticsearch": "^0.0.5", "@backstage/plugin-search-backend-module-pg": "^0.2.1", - "@backstage/plugin-techdocs-backend": "^0.10.8", - "@backstage/plugin-tech-insights-backend": "^0.1.1", + "@backstage/plugin-techdocs-backend": "^0.10.9", + "@backstage/plugin-tech-insights-backend": "^0.1.2", "@backstage/plugin-tech-insights-node": "^0.1.0", - "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.0", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.1", "@backstage/plugin-todo-backend": "^0.1.13", "@gitbeaker/node": "^34.6.0", "@octokit/rest": "^18.5.3", @@ -68,7 +68,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index f978e84da9..4426ed7767 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -33,6 +33,7 @@ import { SingleHostDiscovery, UrlReaders, useHotMemoize, + ServerTokenManager, } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import healthcheck from './plugins/healthcheck'; @@ -60,6 +61,7 @@ function makeCreateEnv(config: Config) { const root = getRootLogger(); const reader = UrlReaders.default({ logger: root, config }); const discovery = SingleHostDiscovery.fromConfig(config); + const tokenManager = ServerTokenManager.noop(); root.info(`Created UrlReader ${reader}`); @@ -70,7 +72,7 @@ function makeCreateEnv(config: Config) { const logger = root.child({ type: 'plugin', plugin }); const database = databaseManager.forPlugin(plugin); const cache = cacheManager.forPlugin(plugin); - return { logger, cache, database, config, reader, discovery }; + return { logger, cache, database, config, reader, discovery, tokenManager }; }; } diff --git a/packages/backend/src/plugins/search.ts b/packages/backend/src/plugins/search.ts index 4cf9c10021..9a8db0f0f9 100644 --- a/packages/backend/src/plugins/search.ts +++ b/packages/backend/src/plugins/search.ts @@ -59,6 +59,7 @@ export default async function createPlugin({ discovery, config, database, + tokenManager, }: PluginEnvironment) { // Initialize a connection to a search engine. const searchEngine = await createSearchEngine({ config, logger, database }); @@ -68,7 +69,10 @@ export default async function createPlugin({ // particular collator gathers entities from the software catalog. indexBuilder.addCollator({ defaultRefreshIntervalSeconds: 600, - collator: DefaultCatalogCollator.fromConfig(config, { discovery }), + collator: DefaultCatalogCollator.fromConfig(config, { + discovery, + tokenManager, + }), }); indexBuilder.addCollator({ @@ -76,6 +80,7 @@ export default async function createPlugin({ collator: DefaultTechDocsCollator.fromConfig(config, { discovery, logger, + tokenManager, }), }); diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 8290e569ef..4be9c036b3 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -20,6 +20,7 @@ import { PluginCacheManager, PluginDatabaseManager, PluginEndpointDiscovery, + TokenManager, UrlReader, } from '@backstage/backend-common'; @@ -30,4 +31,5 @@ export type PluginEnvironment = { config: Config; reader: UrlReader; discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; }; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 407c4fcbf2..ed8525a2cc 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/cli +## 0.9.1 + +### Patch Changes + +- dde216acf4: Switch the default test coverage provider from the jest default one to `'v8'`, which provides much better coverage information when using the default Backstage test setup. This is considered a bug fix as the current coverage information is often very inaccurate. +- 719cc87d2f: Disable ES transforms in tests transformed by the `jestSucraseTransform.js`. This is not considered a breaking change since all code is already transpiled this way in the development setup. +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- ee055cf6db: Update the default routes to use id instead of title +- Updated dependencies + - @backstage/errors@0.1.5 + ## 0.9.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 1eed5a742c..dd0a49cb7f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.9.0", + "version": "0.9.1", "private": false, "publishConfig": { "access": "public" @@ -31,7 +31,7 @@ "@backstage/cli-common": "^0.1.6", "@backstage/config": "^0.1.11", "@backstage/config-loader": "^0.8.0", - "@backstage/errors": "^0.1.4", + "@backstage/errors": "^0.1.5", "@backstage/types": "^0.1.1", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", @@ -90,7 +90,7 @@ "postcss": "^8.1.0", "process": "^0.11.10", "react": "^16.0.0", - "react-dev-utils": "^11.0.4", + "react-dev-utils": "^12.0.0-next.47", "react-hot-loader": "^4.12.21", "recursive-readdir": "^2.2.2", "replace-in-file": "^6.0.0", @@ -117,13 +117,13 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", - "@backstage/core-app-api": "^0.1.21", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@backstage/theme": "^0.2.13", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index bf0711ad6f..8c484fe2ac 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/codemods +## 0.1.23 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@0.1.23 + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.1.22 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index bfb8d4d4b3..e95367a5ac 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.22", + "version": "0.1.23", "private": false, "publishConfig": { "access": "public", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index b4c0350def..c275a1f045 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -41,10 +41,10 @@ "json-schema": "^0.4.0", "json-schema-merge-allof": "^0.8.1", "json-schema-traverse": "^1.0.0", + "node-fetch": "^2.6.1", "typescript-json-schema": "^0.51.0", "yaml": "^1.9.2", - "yup": "^0.32.9", - "node-fetch": "2.6.5" + "yup": "^0.32.9" }, "devDependencies": { "@types/jest": "^26.0.7", diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index b1d590a7bd..ad71ad6fe2 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,86 @@ # @backstage/core-app-api +## 0.1.23 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- 000190de69: The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`. + + These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs. + + When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code: + + ```tsx + render( + + {...} + + ) + ``` + + Would be migrated to this: + + ```tsx + render( + + {...} + + ) + ``` + + In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array. + + Usage that looks like this: + + ```ts + const apis = ApiRegistry.with( + identityApiRef, + mockIdentityApi as unknown as IdentityApi, + ).with(configApiRef, new ConfigReader({})); + ``` + + OR like this: + + ```ts + const apis = ApiRegistry.from([ + [identityApiRef, mockIdentityApi as unknown as IdentityApi], + [configApiRef, new ConfigReader({})], + ]); + ``` + + Would be migrated to this: + + ```ts + const apis = TestApiRegistry.from( + [identityApiRef, mockIdentityApi], + [configApiRef, new ConfigReader({})], + ); + ``` + + If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`. + +- Updated dependencies + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.1.22 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 96c021b1ee..6777088b68 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "0.1.22", + "version": "0.1.23", "private": false, "publishConfig": { "access": "public", @@ -30,9 +30,9 @@ }, "dependencies": { "@backstage/app-defaults": "^0.1.1", - "@backstage/core-components": "^0.7.4", + "@backstage/core-components": "^0.7.5", "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@backstage/version-bridge": "^0.1.0", @@ -47,8 +47,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/test-utils": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index c62ba9e90d..b4898fa82b 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/core-components +## 0.7.5 + +### Patch Changes + +- 157530187a: Pin sidebar by default for easier navigation +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/core-plugin-api@0.2.1 + ## 0.7.4 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index afa9af0341..896295038c 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.7.4", + "version": "0.7.5", "private": false, "publishConfig": { "access": "public", @@ -30,8 +30,8 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.2.0", - "@backstage/errors": "^0.1.4", + "@backstage/core-plugin-api": "^0.2.1", + "@backstage/errors": "^0.1.5", "@backstage/theme": "^0.2.13", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", @@ -67,9 +67,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/core-app-api": "^0.1.21", - "@backstage/cli": "^0.9.0", - "@backstage/test-utils": "^0.1.22", + "@backstage/core-app-api": "^0.1.23", + "@backstage/cli": "^0.9.1", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index a8d35e5a14..0022c99c68 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/core-plugin-api +## 0.2.1 + +### Patch Changes + +- 950b36393c: Deprecated `register` option of `createPlugin` and the `outputs` methods of the plugin instance. + + Introduces the `featureFlags` property to define your feature flags instead. + ## 0.2.0 ### Minor Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index e5b4572dff..b6567dcb89 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "0.2.0", + "version": "0.2.1", "private": false, "publishConfig": { "access": "public", @@ -43,9 +43,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.21", - "@backstage/test-utils": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/packages/core-plugin-api/src/routing/RouteRef.ts b/packages/core-plugin-api/src/routing/RouteRef.ts index 26e63c8f3a..8bd3d604f6 100644 --- a/packages/core-plugin-api/src/routing/RouteRef.ts +++ b/packages/core-plugin-api/src/routing/RouteRef.ts @@ -59,21 +59,21 @@ export class RouteRefImpl if (config.path) { // eslint-disable-next-line no-console console.warn( - `[core-plugin-api] - routeRefs no longer decide their own path, please remove the path for ${this.toString()}. This will be removed in upcoming versions.`, + `DEPRECATION WARNING: Passing a path to createRouteRef is deprecated, please remove the path for ${this}.`, ); } if (config.icon) { // eslint-disable-next-line no-console console.warn( - `[core-plugin-api] - routeRefs no longer decide their own icon, please remove the icon for ${this.toString()}. This will be removed in upcoming versions.`, + `DEPRECATION WARNING: Passing an icon to createRouteRef is deprecated, please remove the icon for ${this}.`, ); } if (config.title) { // eslint-disable-next-line no-console console.warn( - `[core-plugin-api] - routeRefs no longer decide their own title, please remove the title for ${this.toString()}. This will be removed in upcoming versions.`, + `DEPRECATION WARNING: Passing a title to createRouteRef is deprecated, please remove the title for ${this}.`, ); } } diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index af1fd58ead..97a5ef9588 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/create-app +## 0.4.5 + +### Patch Changes + +- dcaeaac174: Cleaned out the `peerDependencies` in the published version of the package, making it much quicker to run `npx @backstage/create-app` as it no longer needs to install a long list of unnecessary. +- a5a5d7e1f1: DefaultTechDocsCollator is now included in the search backend, and the Search Page updated with the SearchType component that includes the techdocs type +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- 42ebbc18c0: Bump gitbeaker to the latest version + ## 0.4.4 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index d5ee616ff5..49f6e87376 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.4.4", + "version": "0.4.5", "private": false, "publishConfig": { "access": "public" @@ -25,6 +25,8 @@ "lint": "backstage-cli lint", "test": "backstage-cli test", "clean": "backstage-cli clean", + "prepack": "node scripts/prepack.js", + "postpack": "node scripts/postpack.js", "start": "nodemon --" }, "dependencies": { diff --git a/packages/create-app/scripts/postpack.js b/packages/create-app/scripts/postpack.js new file mode 100644 index 0000000000..f13db11e76 --- /dev/null +++ b/packages/create-app/scripts/postpack.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node +/* + * Copyright 2021 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. + */ + +/* eslint-disable no-restricted-syntax */ + +const fs = require('fs-extra'); +const path = require('path'); + +async function main() { + const pkgPath = path.resolve(__dirname, '../package.json'); + const pkgBackupPath = path.resolve(__dirname, '../package.json-prepack'); + + try { + await fs.move(pkgBackupPath, pkgPath, { overwrite: true }); + } catch (err) { + console.error(`Failed to restore package.json during postpack, ${err}`); + } +} + +main().catch(err => { + console.error(err.stack); + process.exit(1); +}); diff --git a/packages/create-app/scripts/prepack.js b/packages/create-app/scripts/prepack.js new file mode 100644 index 0000000000..2caf865280 --- /dev/null +++ b/packages/create-app/scripts/prepack.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +/* + * Copyright 2021 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. + */ + +/* eslint-disable no-restricted-syntax */ + +const fs = require('fs-extra'); +const path = require('path'); + +async function main() { + const pkgPath = path.resolve(__dirname, '../package.json'); + const pkgBackupPath = path.resolve(__dirname, '../package.json-prepack'); + + const pkg = await fs.readJson(pkgPath); + await fs.writeJson(pkgBackupPath, pkg, { encoding: 'utf8', spaces: 2 }); + delete pkg.peerDependencies; + await fs.writeJson(pkgPath, pkg, { encoding: 'utf8', spaces: 2 }); +} + +main().catch(err => { + console.error(err.stack); + process.exit(1); +}); diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index ab4858edd6..be144d91f7 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -6,6 +6,11 @@ organization: name: My Company backend: + # Used for enabling authentication, secret is shared by all backend plugins + # See backend-to-backend-auth.md in the docs for information on the format + # auth: + # keys: + # - secret: ${BACKEND_SECRET} baseUrl: http://localhost:7007 listen: port: 7007 diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index f2b14b23f9..3f12122a3f 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -17,6 +17,7 @@ import { DatabaseManager, SingleHostDiscovery, UrlReaders, + ServerTokenManager, } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import app from './plugins/app'; @@ -37,12 +38,13 @@ function makeCreateEnv(config: Config) { const cacheManager = CacheManager.fromConfig(config); const databaseManager = DatabaseManager.fromConfig(config); + const tokenManager = ServerTokenManager.noop(); return (plugin: string): PluginEnvironment => { const logger = root.child({ type: 'plugin', plugin }); const database = databaseManager.forPlugin(plugin); const cache = cacheManager.forPlugin(plugin); - return { logger, database, cache, config, reader, discovery }; + return { logger, database, cache, config, reader, discovery, tokenManager }; }; } diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts index 63e196235c..f23b0c7bcf 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts @@ -12,6 +12,7 @@ export default async function createPlugin({ logger, discovery, config, + tokenManager, }: PluginEnvironment) { // Initialize a connection to a search engine. const searchEngine = new LunrSearchEngine({ logger }); @@ -21,13 +22,20 @@ export default async function createPlugin({ // collator gathers entities from the software catalog. indexBuilder.addCollator({ defaultRefreshIntervalSeconds: 600, - collator: DefaultCatalogCollator.fromConfig(config, { discovery }), + collator: DefaultCatalogCollator.fromConfig(config, { + discovery, + tokenManager, + }), }); // collator gathers entities from techdocs. indexBuilder.addCollator({ defaultRefreshIntervalSeconds: 600, - collator: DefaultTechDocsCollator.fromConfig(config, { discovery, logger }), + collator: DefaultTechDocsCollator.fromConfig(config, { + discovery, + logger, + tokenManager, + }), }); // The scheduler controls when documents are gathered from collators and sent diff --git a/packages/create-app/templates/default-app/packages/backend/src/types.ts b/packages/create-app/templates/default-app/packages/backend/src/types.ts index 6c78a2a90c..b1e2e0a1df 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/types.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/types.ts @@ -4,6 +4,7 @@ import { PluginCacheManager, PluginDatabaseManager, PluginEndpointDiscovery, + TokenManager, UrlReader, } from '@backstage/backend-common'; @@ -14,4 +15,5 @@ export type PluginEnvironment = { config: Config; reader: UrlReader; discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; }; diff --git a/packages/errors/CHANGELOG.md b/packages/errors/CHANGELOG.md index 9ea2f47cbb..02b28cdf1d 100644 --- a/packages/errors/CHANGELOG.md +++ b/packages/errors/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/errors +## 0.1.5 + +### Patch Changes + +- 4d09c60256: Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`. + Rename the error name for unknown errors from `unknown` to `error`. + ## 0.1.4 ### Patch Changes diff --git a/packages/errors/package.json b/packages/errors/package.json index 38fe6c634e..f49451a6a8 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/errors", "description": "Common utilities for error handling within Backstage", - "version": "0.1.4", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,7 +35,7 @@ "serialize-error": "^8.0.1" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/jest": "^26.0.7" }, "files": [ diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index 4c2258779a..fcaa39b285 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -112,7 +112,9 @@ export function getAzureFileFetchUrl(url: string): string; export function getAzureRequestOptions( config: AzureIntegrationConfig, additionalHeaders?: Record, -): RequestInit; +): { + headers: Record; +}; // @public export function getBitbucketDefaultBranch( @@ -135,7 +137,9 @@ export function getBitbucketFileFetchUrl( // @public export function getBitbucketRequestOptions( config: BitbucketIntegrationConfig, -): RequestInit; +): { + headers: Record; +}; // @public export function getGitHubFileFetchUrl( @@ -148,7 +152,9 @@ export function getGitHubFileFetchUrl( export function getGitHubRequestOptions( config: GitHubIntegrationConfig, credentials: GithubCredentials, -): RequestInit; +): { + headers: Record; +}; // @public export function getGitLabFileFetchUrl( @@ -157,9 +163,9 @@ export function getGitLabFileFetchUrl( ): Promise; // @public -export function getGitLabRequestOptions( - config: GitLabIntegrationConfig, -): RequestInit; +export function getGitLabRequestOptions(config: GitLabIntegrationConfig): { + headers: Record; +}; // @public export type GithubAppConfig = { diff --git a/packages/integration/src/azure/core.ts b/packages/integration/src/azure/core.ts index 7461954267..1d5c4c3c89 100644 --- a/packages/integration/src/azure/core.ts +++ b/packages/integration/src/azure/core.ts @@ -64,8 +64,8 @@ export function getAzureCommitsUrl(url: string): string { export function getAzureRequestOptions( config: AzureIntegrationConfig, additionalHeaders?: Record, -): RequestInit { - const headers: HeadersInit = additionalHeaders +): { headers: Record } { + const headers: Record = additionalHeaders ? { ...additionalHeaders } : {}; diff --git a/packages/integration/src/bitbucket/core.ts b/packages/integration/src/bitbucket/core.ts index afcb9fcfdb..da10152bf8 100644 --- a/packages/integration/src/bitbucket/core.ts +++ b/packages/integration/src/bitbucket/core.ts @@ -158,8 +158,8 @@ export function getBitbucketFileFetchUrl( */ export function getBitbucketRequestOptions( config: BitbucketIntegrationConfig, -): RequestInit { - const headers: HeadersInit = {}; +): { headers: Record } { + const headers: Record = {}; if (config.token) { headers.Authorization = `Bearer ${config.token}`; diff --git a/packages/integration/src/github/core.ts b/packages/integration/src/github/core.ts index 2c5e739a26..76e54fd544 100644 --- a/packages/integration/src/github/core.ts +++ b/packages/integration/src/github/core.ts @@ -73,8 +73,8 @@ export function getGitHubFileFetchUrl( export function getGitHubRequestOptions( config: GitHubIntegrationConfig, credentials: GithubCredentials, -): RequestInit { - const headers: HeadersInit = {}; +): { headers: Record } { + const headers: Record = {}; if (chooseEndpoint(config, credentials) === 'api') { headers.Accept = 'application/vnd.github.v3.raw'; diff --git a/packages/integration/src/gitlab/core.ts b/packages/integration/src/gitlab/core.ts index e6f4dbc910..c5b0713340 100644 --- a/packages/integration/src/gitlab/core.ts +++ b/packages/integration/src/gitlab/core.ts @@ -54,9 +54,9 @@ export async function getGitLabFileFetchUrl( * @param config - The relevant provider config * @public */ -export function getGitLabRequestOptions( - config: GitLabIntegrationConfig, -): RequestInit { +export function getGitLabRequestOptions(config: GitLabIntegrationConfig): { + headers: Record; +} { const { token = '' } = config; return { headers: { diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 70ca0a9668..05759e39d1 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -65,7 +65,7 @@ "dockerode": "^3.3.1", "fs-extra": "^9.0.1", "http-proxy": "^1.18.1", - "react-dev-utils": "^11.0.4", + "react-dev-utils": "^12.0.0-next.47", "serve-handler": "^6.1.3", "winston": "^3.2.1" } diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index 0207ea409d..386c584196 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/techdocs-common +## 0.10.8 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + ## 0.10.7 ### Patch Changes diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index b13b94a1a7..3f0d0edf5b 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.10.7", + "version": "0.10.8", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -38,10 +38,10 @@ "dependencies": { "@azure/identity": "^1.5.0", "@azure/storage-blob": "^12.5.0", - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/errors": "^0.1.4", + "@backstage/errors": "^0.1.5", "@backstage/search-common": "^0.2.1", "@backstage/integration": "^0.6.9", "@google-cloud/storage": "^5.6.0", @@ -60,7 +60,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/fs-extra": "^9.0.5", "@types/js-yaml": "^4.0.0", "@types/mime-types": "^2.1.0", diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 3460e6c714..fd8ce852db 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,72 @@ # @backstage/test-utils +## 0.1.23 + +### Patch Changes + +- 000190de69: The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`. + + These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs. + + When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code: + + ```tsx + render( + + {...} + + ) + ``` + + Would be migrated to this: + + ```tsx + render( + + {...} + + ) + ``` + + In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array. + + Usage that looks like this: + + ```ts + const apis = ApiRegistry.with( + identityApiRef, + mockIdentityApi as unknown as IdentityApi, + ).with(configApiRef, new ConfigReader({})); + ``` + + OR like this: + + ```ts + const apis = ApiRegistry.from([ + [identityApiRef, mockIdentityApi as unknown as IdentityApi], + [configApiRef, new ConfigReader({})], + ]); + ``` + + Would be migrated to this: + + ```ts + const apis = TestApiRegistry.from( + [identityApiRef, mockIdentityApi], + [configApiRef, new ConfigReader({})], + ); + ``` + + If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`. + +- Updated dependencies + - @backstage/core-app-api@0.1.23 + - @backstage/core-plugin-api@0.2.1 + ## 0.1.22 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 79667c236a..7e684adbfc 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.22", + "version": "0.1.23", "private": false, "publishConfig": { "access": "public", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-app-api": "^0.1.21", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-app-api": "^0.1.23", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", @@ -46,7 +46,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 9efe6132e8..33fc4db9d9 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -23,8 +23,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -36,10 +36,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index e0a32960c5..ffd6dd364c 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -34,10 +34,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 0b0bd4890c..c880dc983c 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-api-docs +## 0.6.15 + +### Patch Changes + +- c982fc12cb: Adjusted some styles in the OpenAPI definition, for elements which were barely readable in dark mode. +- Updated dependencies + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.6.14 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index a4aa15c419..9f8b5fbf84 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.6.14", + "version": "0.6.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ "dependencies": { "@asyncapi/react-component": "^0.23.0", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog": "^0.7.3", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", @@ -53,10 +53,10 @@ "swagger-ui-react": "^4.0.0-rc.3" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 5dc0cdb861..38eaf268e6 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-auth-backend +## 0.4.9 + +### Patch Changes + +- 9312572360: Switched to using the standardized JSON error responses for all provider endpoints. +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + - @backstage/test-utils@0.1.23 + ## 0.4.8 ### Patch Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 35c93e9537..eb7c53bd6b 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -175,6 +175,20 @@ export const bitbucketUserIdSignInResolver: SignInResolver // @public (undocumented) export const bitbucketUsernameSignInResolver: SignInResolver; +// Warning: (ae-missing-release-tag) "CatalogIdentityClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class CatalogIdentityClient { + constructor(options: { catalogApi: CatalogApi; tokenIssuer: TokenIssuer }); + // Warning: (ae-forgotten-export) The symbol "UserQuery" needs to be exported by the entry point index.d.ts + findUser(query: UserQuery): Promise; + // Warning: (ae-forgotten-export) The symbol "MemberClaimQuery" needs to be exported by the entry point index.d.ts + resolveCatalogMembership({ + entityRefs, + logger, + }: MemberClaimQuery): Promise; +} + // Warning: (ae-missing-release-tag) "createAtlassianProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -271,6 +285,12 @@ export const encodeState: (state: OAuthState) => string; // @public (undocumented) export const ensuresXRequestedWith: (req: express.Request) => boolean; +// Warning: (ae-forgotten-export) The symbol "TokenParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "getEntityClaims" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function getEntityClaims(entity: UserEntity): TokenParams['claims']; + // Warning: (ae-missing-release-tag) "GithubOAuthResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -578,7 +598,6 @@ export type WebMessageResponse = // Warnings were encountered during analysis: // -// src/identity/types.d.ts:25:5 - (ae-forgotten-export) The symbol "TokenParams" needs to be exported by the entry point index.d.ts // src/identity/types.d.ts:31:9 - (ae-forgotten-export) The symbol "AnyJWK" needs to be exported by the entry point index.d.ts // src/providers/atlassian/provider.d.ts:37:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts // src/providers/atlassian/provider.d.ts:42:9 - (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 3a9e8102eb..fecabf8ad1 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.4.8", + "version": "0.4.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,19 +30,18 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/errors": "^0.1.4", - "@backstage/test-utils": "^0.1.22", + "@backstage/errors": "^0.1.5", + "@backstage/test-utils": "^0.1.23", "@google-cloud/firestore": "^4.15.1", "@types/express": "^4.17.6", "@types/passport": "^1.0.3", "compression": "^1.7.4", "cookie-parser": "^1.4.5", "cors": "^2.8.5", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-session": "^1.17.1", @@ -56,6 +55,7 @@ "luxon": "^2.0.2", "minimatch": "^3.0.3", "morgan": "^1.10.0", + "node-fetch": "^2.6.1", "node-cache": "^5.1.2", "openid-client": "^4.2.1", "passport": "^0.4.1", @@ -73,7 +73,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/auth-backend/src/identity/IdentityClient.ts b/plugins/auth-backend/src/identity/IdentityClient.ts index 2af30e4b6a..d60829bf59 100644 --- a/plugins/auth-backend/src/identity/IdentityClient.ts +++ b/plugins/auth-backend/src/identity/IdentityClient.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import fetch from 'cross-fetch'; +import fetch from 'node-fetch'; import { JWK, JWT, JWKS, JSONWebKey } from 'jose'; import { BackstageIdentity } from '../providers'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; diff --git a/plugins/auth-backend/src/index.ts b/plugins/auth-backend/src/index.ts index 432da00142..894f8c1e55 100644 --- a/plugins/auth-backend/src/index.ts +++ b/plugins/auth-backend/src/index.ts @@ -31,3 +31,5 @@ export * from './lib/flow'; // OAuth wrapper over a passport or a custom `startegy`. export * from './lib/oauth'; + +export * from './lib/catalog'; diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.test.ts b/plugins/auth-backend/src/providers/aws-alb/provider.test.ts index 2f5ffa1833..3f72004d48 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.test.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.test.ts @@ -50,7 +50,7 @@ const mockClaims = { }; jest.mock('jose'); -jest.mock('cross-fetch', () => ({ +jest.mock('node-fetch', () => ({ __esModule: true, default: async () => { return { diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.ts b/plugins/auth-backend/src/providers/aws-alb/provider.ts index ac5db5e69f..b0b9070d50 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.ts @@ -21,7 +21,7 @@ import { SignInResolver, } from '../types'; import express from 'express'; -import fetch from 'cross-fetch'; +import fetch from 'node-fetch'; import * as crypto from 'crypto'; import { KeyObject } from 'crypto'; import { Logger } from 'winston'; diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index 06fc4833c4..26c64b3f9d 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-azure-devops-backend +## 0.2.2 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/backend-common@0.9.11 + ## 0.2.1 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 6d213cbce6..12f62355dd 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/config": "^0.1.11", "@backstage/plugin-azure-devops-common": "^0.1.0", "@types/express": "^4.17.6", @@ -31,7 +31,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/supertest": "^2.0.8", "supertest": "^4.0.2", "msw": "^0.35.0" diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 9d235b0641..2badcf63b5 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -28,8 +28,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.4", "@backstage/plugin-azure-devops-common": "^0.1.0", "@backstage/plugin-catalog-react": "^0.6.4", @@ -45,10 +45,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 5cd063d960..736d079969 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-badges-backend +## 0.1.12 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + ## 0.1.11 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 2186c8d52e..6f44e96b37 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.1.11", + "version": "0.1.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,22 +31,21 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/errors": "^0.1.3", + "@backstage/errors": "^0.1.5", "@types/express": "^4.17.6", "badge-maker": "^3.3.0", "cors": "^2.8.5", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "winston": "^3.2.1", "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 44bc597d0a..449352f87b 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-badges +## 0.2.15 + +### Patch Changes + +- 4149d74c10: Fix the path that the Badges client uses towards the `plugin-badges-backend` +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.2.14 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 4d02d64237..be38144ada 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.14", + "version": "0.2.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,9 +28,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", - "@backstage/errors": "^0.1.3", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", + "@backstage/errors": "^0.1.5", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -42,10 +42,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/badges/src/api/BadgesClient.ts b/plugins/badges/src/api/BadgesClient.ts index 432a485a6b..8ad5d313e4 100644 --- a/plugins/badges/src/api/BadgesClient.ts +++ b/plugins/badges/src/api/BadgesClient.ts @@ -52,7 +52,7 @@ export class BadgesClient implements BadgesApi { private async getEntityBadgeSpecsUrl(entity: Entity): Promise { const routeParams = this.getEntityRouteParams(entity); - const path = generatePath(`:kind/:namespace/:name`, routeParams); + const path = generatePath(`:namespace/:kind/:name`, routeParams); return `${await this.discoveryApi.getBaseUrl( 'badges', )}/entity/${path}/badge-specs`; diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index 88e646b5e3..812ae8f5e3 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-bazaar-backend +## 0.1.3 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/backend-common@0.9.11 + ## 0.1.2 ### Patch Changes diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index a6610493e2..9c6426fcba 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/backend-test-utils": "^0.1.9", "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", @@ -31,7 +31,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0" + "@backstage/cli": "^0.9.1" }, "files": [ "dist", diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index fd07a3b5c7..bb45a7b974 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -23,16 +23,16 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/cli": "^0.9.0", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog": "^0.7.3", "@backstage/plugin-catalog-react": "^0.6.4", + "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/pickers": "^3.3.10", "@testing-library/jest-dom": "^5.10.1", - "@date-io/luxon": "1.x", "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -41,7 +41,7 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@backstage/dev-utils": "^0.2.13", "@testing-library/jest-dom": "^5.10.1", "cross-fetch": "^3.0.6" diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 92cd9693e1..3af16837de 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -39,17 +39,16 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", "@types/recharts": "^1.8.15", - "cross-fetch": "^3.0.6", "msw": "^0.35.0" }, "files": [ diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index a776f3f329..9e769af873 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.3.7 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/plugin-catalog-backend@0.18.0 + ## 0.3.6 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 5e3cec24d9..36cacb6660 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend modules that helps integrate towards LDAP", - "version": "0.3.6", + "version": "0.3.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/errors": "^0.1.4", - "@backstage/plugin-catalog-backend": "^0.17.4", + "@backstage/errors": "^0.1.5", + "@backstage/plugin-catalog-backend": "^0.18.0", "@backstage/types": "^0.1.1", "@types/ldapjs": "^2.2.0", "ldapjs": "^2.2.0", @@ -40,7 +40,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index ee8aa49065..c2225b8945 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.2.10 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@0.18.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/api-report.md b/plugins/catalog-backend-module-msgraph/api-report.md index 1357659739..e1a837d0c1 100644 --- a/plugins/catalog-backend-module-msgraph/api-report.md +++ b/plugins/catalog-backend-module-msgraph/api-report.md @@ -13,6 +13,7 @@ import { LocationSpec } from '@backstage/catalog-model'; import { Logger as Logger_2 } from 'winston'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import * as msal from '@azure/msal-node'; +import { Response as Response_2 } from 'node-fetch'; import { UserEntity } from '@backstage/catalog-model'; // Warning: (ae-missing-release-tag) "defaultGroupTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -88,11 +89,11 @@ export class MicrosoftGraphClient { // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/plugin-catalog-backend-module-msgraph" does not have an export "ODataQuery" getUsers(query?: ODataQuery): AsyncIterable; // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/plugin-catalog-backend-module-msgraph" does not have an export "ODataQuery" - requestApi(path: string, query?: ODataQuery): Promise; + requestApi(path: string, query?: ODataQuery): Promise; // Warning: (ae-forgotten-export) The symbol "ODataQuery" needs to be exported by the entry point index.d.ts // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/plugin-catalog-backend-module-msgraph" does not have an export "ODataQuery" requestCollection(path: string, query?: ODataQuery): AsyncIterable; - requestRaw(url: string): Promise; + requestRaw(url: string): Promise; } // Warning: (ae-missing-release-tag) "MicrosoftGraphOrgEntityProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 222f56b022..391a7125de 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend modules that helps integrate towards Microsoft Graph", - "version": "0.2.9", + "version": "0.2.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,18 +32,19 @@ "@azure/msal-node": "^1.1.0", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/plugin-catalog-backend": "^0.17.4", + "@backstage/plugin-catalog-backend": "^0.18.0", "@microsoft/microsoft-graph-types": "^2.6.0", - "cross-fetch": "^3.0.6", + "@types/node-fetch": "^2.5.12", "lodash": "^4.17.21", + "node-fetch": "^2.6.1", "p-limit": "^3.0.2", "winston": "^3.2.1", "qs": "^6.9.4" }, "devDependencies": { - "@backstage/backend-common": "^0.9.10", - "@backstage/cli": "^0.9.0", - "@backstage/test-utils": "^0.1.22", + "@backstage/backend-common": "^0.9.11", + "@backstage/cli": "^0.9.1", + "@backstage/test-utils": "^0.1.23", "@types/lodash": "^4.14.151", "msw": "^0.35.0" }, diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts index 83b4371c0d..63f256afbd 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/client.ts @@ -16,7 +16,7 @@ import * as msal from '@azure/msal-node'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; -import fetch from 'cross-fetch'; +import fetch, { Response } from 'node-fetch'; import qs from 'qs'; import { MicrosoftGraphProviderConfig } from './config'; diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 92b57f3483..ce19314a50 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,72 @@ # @backstage/plugin-catalog-backend +## 0.18.0 + +### Minor Changes + +- 7f82ce9f51: **BREAKING** EntitiesSearchFilter fields have changed. + + EntitiesSearchFilter now has only two fields: `key` and `value`. The `matchValueIn` and `matchValueExists` fields are no longer are supported. Previous filters written using the `matchValueIn` and `matchValueExists` fields can be rewritten as follows: + + Filtering by existence of key only: + + ```diff + filter: { + { + key: 'abc', + - matchValueExists: true, + }, + } + ``` + + Filtering by key and values: + + ```diff + filter: { + { + key: 'abc', + - matchValueExists: true, + - matchValueIn: ['xyz'], + + values: ['xyz'], + }, + } + ``` + + Negation of filters can now be achieved through a `not` object: + + ``` + filter: { + not: { + key: 'abc', + values: ['xyz'], + }, + } + ``` + +### Patch Changes + +- 740f958290: Providing an empty values array in an EntityFilter will now return no matches. +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- eddb82ab7c: Index User entities by displayName to be able to search by full name. Added displayName (if present) to the 'text' field in the indexed document. +- 563b039f0b: Added Azure DevOps discovery processor +- 8866b62f3d: Detect a duplicate entities when adding locations through dry run +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + ## 0.17.4 ### Patch Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 7363a803c8..43eb8d61b7 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -31,6 +31,7 @@ import { ResourceEntityV1alpha1 } from '@backstage/catalog-model'; import { Router } from 'express'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { ScmIntegrations } from '@backstage/integration'; +import { TokenManager } from '@backstage/backend-common'; import { UrlReader } from '@backstage/backend-common'; import { Validators } from '@backstage/catalog-model'; @@ -756,8 +757,10 @@ export class DefaultCatalogCollator implements DocumentCollator { locationTemplate, filter, catalogClient, + tokenManager, }: { discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; locationTemplate?: string; filter?: CatalogEntitiesRequest['filter']; catalogClient?: CatalogApi; @@ -780,12 +783,15 @@ export class DefaultCatalogCollator implements DocumentCollator { _config: Config, options: { discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; filter?: CatalogEntitiesRequest['filter']; }, ): DefaultCatalogCollator; // (undocumented) protected locationTemplate: string; // (undocumented) + protected tokenManager: TokenManager; + // (undocumented) readonly type: string; } diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index a9a35d5858..0628969ea3 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "0.17.4", + "version": "0.18.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,11 +30,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/errors": "^0.1.4", + "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.9", "@backstage/search-common": "^0.2.1", "@backstage/types": "^0.1.1", @@ -43,7 +43,6 @@ "aws-sdk": "^2.840.0", "codeowners-utils": "^1.0.2", "core-js": "^3.6.5", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "fast-json-stable-stringify": "^2.1.0", @@ -53,6 +52,7 @@ "knex": "^0.95.1", "lodash": "^4.17.21", "luxon": "^2.0.2", + "node-fetch": "^2.6.1", "p-limit": "^3.0.2", "prom-client": "^13.2.0", "uuid": "^8.0.0", @@ -63,8 +63,8 @@ }, "devDependencies": { "@backstage/backend-test-utils": "^0.1.9", - "@backstage/cli": "^0.9.0", - "@backstage/test-utils": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/test-utils": "^0.1.23", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/lodash": "^4.14.151", diff --git a/plugins/catalog-backend/src/ingestion/processors/azure/azure.ts b/plugins/catalog-backend/src/ingestion/processors/azure/azure.ts index 0c7b17483a..316c9b0b8c 100644 --- a/plugins/catalog-backend/src/ingestion/processors/azure/azure.ts +++ b/plugins/catalog-backend/src/ingestion/processors/azure/azure.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import fetch from 'cross-fetch'; +import fetch from 'node-fetch'; import { AzureIntegrationConfig, getAzureRequestOptions, diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts index b578799229..0c132bb61b 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import fetch from 'cross-fetch'; +import fetch from 'node-fetch'; import { BitbucketIntegrationConfig, diff --git a/plugins/catalog-backend/src/ingestion/processors/gitlab/client.ts b/plugins/catalog-backend/src/ingestion/processors/gitlab/client.ts index 8781e071f7..d1765521a3 100644 --- a/plugins/catalog-backend/src/ingestion/processors/gitlab/client.ts +++ b/plugins/catalog-backend/src/ingestion/processors/gitlab/client.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import fetch from 'cross-fetch'; +import fetch from 'node-fetch'; import { getGitLabRequestOptions, GitLabIntegrationConfig, diff --git a/plugins/catalog-backend/src/search/DefaultCatalogCollator.test.ts b/plugins/catalog-backend/src/search/DefaultCatalogCollator.test.ts index 8f1af6f615..1360ca2647 100644 --- a/plugins/catalog-backend/src/search/DefaultCatalogCollator.test.ts +++ b/plugins/catalog-backend/src/search/DefaultCatalogCollator.test.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { + PluginEndpointDiscovery, + TokenManager, +} from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { DefaultCatalogCollator } from './DefaultCatalogCollator'; import { setupServer } from 'msw/node'; @@ -55,6 +58,7 @@ const expectedEntities: Entity[] = [ describe('DefaultCatalogCollator', () => { let mockDiscoveryApi: jest.Mocked; + let mockTokenManager: jest.Mocked; let collator: DefaultCatalogCollator; beforeAll(() => { @@ -62,7 +66,14 @@ describe('DefaultCatalogCollator', () => { getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007'), getExternalBaseUrl: jest.fn(), }; - collator = new DefaultCatalogCollator({ discovery: mockDiscoveryApi }); + mockTokenManager = { + getToken: jest.fn().mockResolvedValue({ token: '' }), + authenticate: jest.fn(), + }; + collator = new DefaultCatalogCollator({ + discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, + }); server.listen(); }); beforeEach(() => { @@ -118,6 +129,7 @@ describe('DefaultCatalogCollator', () => { // Provide an alternate location template. collator = new DefaultCatalogCollator({ discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, locationTemplate: '/software/:name', }); @@ -131,6 +143,7 @@ describe('DefaultCatalogCollator', () => { // Provide an alternate location template. collator = DefaultCatalogCollator.fromConfig(new ConfigReader({}), { discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, filter: { kind: ['Foo', 'Bar'], }, diff --git a/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts b/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts index 3e19b38ea6..4c6342c6c6 100644 --- a/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts +++ b/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { + PluginEndpointDiscovery, + TokenManager, +} from '@backstage/backend-common'; import { Entity, UserEntity } from '@backstage/catalog-model'; import { IndexableDocument, DocumentCollator } from '@backstage/search-common'; import { Config } from '@backstage/config'; @@ -38,11 +41,13 @@ export class DefaultCatalogCollator implements DocumentCollator { protected filter?: CatalogEntitiesRequest['filter']; protected readonly catalogClient: CatalogApi; public readonly type: string = 'software-catalog'; + protected tokenManager: TokenManager; static fromConfig( _config: Config, options: { discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; filter?: CatalogEntitiesRequest['filter']; }, ) { @@ -56,8 +61,10 @@ export class DefaultCatalogCollator implements DocumentCollator { locationTemplate, filter, catalogClient, + tokenManager, }: { discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; locationTemplate?: string; filter?: CatalogEntitiesRequest['filter']; catalogClient?: CatalogApi; @@ -68,6 +75,7 @@ export class DefaultCatalogCollator implements DocumentCollator { this.filter = filter; this.catalogClient = catalogClient || new CatalogClient({ discoveryApi: discovery }); + this.tokenManager = tokenManager; } protected applyArgsToFormat( @@ -100,9 +108,13 @@ export class DefaultCatalogCollator implements DocumentCollator { } async execute() { - const response = await this.catalogClient.getEntities({ - filter: this.filter, - }); + const { token } = await this.tokenManager.getToken(); + const response = await this.catalogClient.getEntities( + { + filter: this.filter, + }, + { token }, + ); return response.items.map((entity: Entity): CatalogEntityDocument => { return { title: entity.metadata.title ?? entity.metadata.name, diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 13869cfd38..16c942de8b 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -23,8 +23,8 @@ "dependencies": { "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -41,10 +41,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 933a84f13e..d46495a58b 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -33,8 +33,8 @@ "dependencies": { "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.8", "@backstage/integration-react": "^0.1.14", @@ -55,10 +55,10 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index ddee20c488..e0218090bd 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -33,8 +33,8 @@ "dependencies": { "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.3", "@backstage/integration-react": "^0.1.14", "@backstage/plugin-catalog-react": "^0.6.4", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 7228044f4f..6f3eaa39e3 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -52,10 +52,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 4125a8c82e..f66dcbed00 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/code-coverage-backend/CHANGELOG.md b/plugins/code-coverage-backend/CHANGELOG.md index 8e8f9ceac8..f1175172f5 100644 --- a/plugins/code-coverage-backend/CHANGELOG.md +++ b/plugins/code-coverage-backend/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-code-coverage-backend +## 0.1.15 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + ## 0.1.14 ### Patch Changes diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index 90320d0e41..1bc637146b 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage-backend", "description": "A Backstage backend plugin that helps you keep track of your code coverage", - "version": "0.1.14", + "version": "0.1.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,14 +20,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/errors": "^0.1.3", + "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.8", "@types/express": "^4.17.6", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-xml-bodyparser": "^0.3.0", @@ -37,7 +36,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/express-xml-bodyparser": "^0.3.2", "@types/supertest": "^2.0.8", "msw": "^0.35.0", diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index e5b51e90b4..3a45960684 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -23,8 +23,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.4", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", @@ -42,10 +42,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index ca889ebdbd..fc4f8697b8 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-config-schema +## 0.1.14 + +### Patch Changes + +- 9f21236a29: Fixed a missing `await` when throwing server side errors +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.1.13 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index c9abbc7e4e..0d0357cd7c 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-config-schema", "description": "A Backstage plugin that lets you browse the configuration schema of your app", - "version": "0.1.13", + "version": "0.1.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", - "@backstage/errors": "^0.1.4", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", + "@backstage/errors": "^0.1.5", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", @@ -37,10 +37,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index 7096af37c6..c43d7c0612 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-cost-insights +## 0.11.12 + +### Patch Changes + +- 950b36393c: Supply featureFlags using featureFlag config option. +- Updated dependencies + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.11.11 ### Patch Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index da9ba8b1f6..12133fcc64 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cost-insights", "description": "A Backstage plugin that helps you keep track of your cloud spend", - "version": "0.11.11", + "version": "0.11.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -55,10 +55,10 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index d9945edc51..8988a7f215 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/plugin-explore-react": "^0.0.7", "@backstage/theme": "^0.2.13", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index f332efc150..421ec10c1c 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -22,8 +22,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index cc0b486d60..3731d01661 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", @@ -49,16 +49,15 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "cross-fetch": "^3.0.6", "msw": "^0.35.0" }, "files": [ diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index cd1c3ac4a1..3068119de5 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 7f7ff6184d..fd1ef75ccc 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/integration": "^0.6.8", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -39,10 +39,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 5eed309d68..7992f201cf 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-github-actions +## 0.4.25 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.4.24 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index ae7f32be1f..ea5f104c96 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.4.24", + "version": "0.4.25", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,8 +34,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/integration": "^0.6.8", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", @@ -52,10 +52,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 062113a8ef..18c3cc3d05 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.8", "@backstage/integration-react": "^0.1.14", @@ -39,10 +39,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index dfbd6297cf..8456eb4919 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index f6c8b9fde8..4fbf6b0dab 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-graphiql +## 0.2.22 + +### Patch Changes + +- cd398cd4ab: Letting GraphiQL use headers +- Updated dependencies + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.2.21 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 5c72a4c728..a04f4d6992 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.21", + "version": "0.2.22", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/home/package.json b/plugins/home/package.json index ef13ce3a00..65f6fe2ee3 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index 6011dd4f46..4dddf17cd9 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", @@ -39,10 +39,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md index 48f4461f7f..5cc91e0d5d 100644 --- a/plugins/jenkins-backend/CHANGELOG.md +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-jenkins-backend +## 0.1.8 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/backend-common@0.9.11 + ## 0.1.7 ### Patch Changes diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index ac9efb3ffd..4db26d94c4 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins-backend", "description": "A Backstage backend plugin that integrates towards Jenkins", - "version": "0.1.7", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", "@types/express": "^4.17.6", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "jenkins": "^0.28.1", @@ -35,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/jenkins": "^0.23.1", "@types/supertest": "^2.0.8", "msw": "^0.35.0", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index 369676b195..1daba68a1d 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-jenkins +## 0.5.13 + +### Patch Changes + +- ad433b346f: Fix Jenkins project table pagination. +- Updated dependencies + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.5.12 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 4456b15284..18341f6eec 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins", "description": "A Backstage plugin that integrates towards Jenkins", - "version": "0.5.12", + "version": "0.5.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index 482c1f75b1..9a417b04b0 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kafka-backend +## 0.2.12 + +### Patch Changes + +- 4f81bfd356: Add ACL requirements for kafka-backend plugin +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + ## 0.2.11 ### Patch Changes diff --git a/plugins/kafka-backend/README.md b/plugins/kafka-backend/README.md index d18a232fa4..637a5773e0 100644 --- a/plugins/kafka-backend/README.md +++ b/plugins/kafka-backend/README.md @@ -49,3 +49,7 @@ kafka: username: my-username password: my-password ``` + +### ACLs + +If you are using ACLs on Kafka, you will need to have the `DESCRIBE` ACL on both consumer groups and topics. diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index dea803890f..a8f64b21e1 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka-backend", "description": "A Backstage backend plugin that integrates towards Kafka", - "version": "0.2.11", + "version": "0.2.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/errors": "^0.1.3", + "@backstage/errors": "^0.1.5", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -44,7 +44,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/jest-when": "^2.7.2", "@types/lodash": "^4.14.151", "jest-when": "^3.1.0", diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index a6fbe36e99..8da053ab50 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index d506571a3f..102c9c13e4 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-backend +## 0.3.19 + +### Patch Changes + +- 37dc844728: Include CronJobs and Jobs as default objects returned by the kubernetes backend and add/update relevant types. +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/plugin-kubernetes-common@0.1.6 + - @backstage/backend-common@0.9.11 + ## 0.3.18 ### Patch Changes diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index f34815f328..e1236e61c5 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -202,6 +202,8 @@ export type KubernetesObjectTypes = | 'deployments' | 'replicasets' | 'horizontalpodautoscalers' + | 'jobs' + | 'cronjobs' | 'ingresses' | 'customresources'; diff --git a/plugins/kubernetes-backend/examples/dice-roller/README.md b/plugins/kubernetes-backend/examples/dice-roller/README.md index 5f541bb61d..d4725e38c0 100644 --- a/plugins/kubernetes-backend/examples/dice-roller/README.md +++ b/plugins/kubernetes-backend/examples/dice-roller/README.md @@ -27,14 +27,15 @@ Add or update `app-config.local.yaml` with the following: ```yaml kubernetes: - serviceLocatorMethod: 'multiTenant' + serviceLocatorMethod: + type: 'multiTenant' clusterLocatorMethods: - - 'config' - clusters: - - url: - name: minikube - serviceAccountToken: - authProvider: 'serviceAccount' + - type: 'config' + clusters: + - url: + name: minikube + serviceAccountToken: + authProvider: 'serviceAccount' ``` ### Getting the service account token diff --git a/plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml b/plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml index 68ef489170..fb71037129 100644 --- a/plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml +++ b/plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml @@ -200,6 +200,35 @@ spec: maxReplicas: 15 targetCPUUtilizationPercentage: 50 +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: dice-roller-cronjob + labels: + 'backstage.io/kubernetes-id': dice-roller +spec: + schedule: '*/1 * * * *' + jobTemplate: + metadata: + labels: + 'backstage.io/kubernetes-id': dice-roller + spec: + template: + metadata: + labels: + 'backstage.io/kubernetes-id': dice-roller + spec: + containers: + - name: busybox + image: busybox + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - date; echo Rolling a die! + restartPolicy: OnFailure + --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 4edc7b238a..0b3f7cb3cb 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-backend", "description": "A Backstage backend plugin that integrates towards Kubernetes", - "version": "0.3.18", + "version": "0.3.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,11 +32,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/errors": "^0.1.3", - "@backstage/plugin-kubernetes-common": "^0.1.4", + "@backstage/errors": "^0.1.5", + "@backstage/plugin-kubernetes-common": "^0.1.6", "@google-cloud/container": "^2.2.0", "@kubernetes/client-node": "^0.15.0", "@types/express": "^4.17.6", @@ -55,7 +55,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/aws4": "^1.5.1", "supertest": "^6.1.3", "aws-sdk-mock": "^5.2.1", diff --git a/plugins/kubernetes-backend/schema.d.ts b/plugins/kubernetes-backend/schema.d.ts index 8b96d5ceca..e69ac052f1 100644 --- a/plugins/kubernetes-backend/schema.d.ts +++ b/plugins/kubernetes-backend/schema.d.ts @@ -23,6 +23,8 @@ export interface Config { | 'deployments' | 'replicasets' | 'horizontalpodautoscalers' + | 'jobs' + | 'cronjobs' | 'ingresses' >; serviceLocatorMethod: { diff --git a/plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts b/plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts index 4b2848508e..5f6825ada9 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts @@ -16,6 +16,7 @@ import { AppsV1Api, + BatchV1Api, AutoscalingV1Api, CoreV1Api, KubeConfig, @@ -74,6 +75,12 @@ export class KubernetesClientProvider { return kc.makeApiClient(AutoscalingV1Api); } + getBatchClientByClusterDetails(clusterDetails: ClusterDetails) { + const kc = this.getKubeConfig(clusterDetails); + + return kc.makeApiClient(BatchV1Api); + } + getNetworkingBeta1Client(clusterDetails: ClusterDetails) { const kc = this.getKubeConfig(clusterDetails); diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index 1f856e90a9..41d75a9835 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -68,6 +68,18 @@ export const DEFAULT_OBJECTS: ObjectToFetch[] = [ plural: 'horizontalpodautoscalers', objectType: 'horizontalpodautoscalers', }, + { + group: 'batch', + apiVersion: 'v1', + plural: 'jobs', + objectType: 'jobs', + }, + { + group: 'batch', + apiVersion: 'v1', + plural: 'cronjobs', + objectType: 'cronjobs', + }, { group: 'networking.k8s.io', apiVersion: 'v1', diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts index 308b7a10be..e845ebeaeb 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts @@ -17,6 +17,7 @@ import { AppsV1Api, AutoscalingV1Api, + BatchV1Api, CoreV1Api, NetworkingV1beta1Api, } from '@kubernetes/client-node'; @@ -41,6 +42,7 @@ export interface Clients { core: CoreV1Api; apps: AppsV1Api; autoscaling: AutoscalingV1Api; + batch: BatchV1Api; networkingBeta1: NetworkingV1beta1Api; } diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 90485fcaee..b8aa482e47 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -67,6 +67,8 @@ export type KubernetesObjectTypes = | 'deployments' | 'replicasets' | 'horizontalpodautoscalers' + | 'jobs' + | 'cronjobs' | 'ingresses' | 'customresources'; diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index 0f685bd4d4..b0e7aca129 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-kubernetes-common +## 0.1.6 + +### Patch Changes + +- 37dc844728: Include CronJobs and Jobs as default objects returned by the kubernetes backend and add/update relevant types. + ## 0.1.5 ### Patch Changes diff --git a/plugins/kubernetes-common/api-report.md b/plugins/kubernetes-common/api-report.md index 9f9669275f..7db05a7513 100644 --- a/plugins/kubernetes-common/api-report.md +++ b/plugins/kubernetes-common/api-report.md @@ -6,8 +6,10 @@ import { Entity } from '@backstage/catalog-model'; import { ExtensionsV1beta1Ingress } from '@kubernetes/client-node'; import { V1ConfigMap } from '@kubernetes/client-node'; +import { V1CronJob } from '@kubernetes/client-node'; import { V1Deployment } from '@kubernetes/client-node'; import { V1HorizontalPodAutoscaler } from '@kubernetes/client-node'; +import { V1Job } from '@kubernetes/client-node'; import { V1Pod } from '@kubernetes/client-node'; import { V1ReplicaSet } from '@kubernetes/client-node'; import { V1Service } from '@kubernetes/client-node'; @@ -48,6 +50,16 @@ export interface ConfigMapFetchResponse { type: 'configmaps'; } +// Warning: (ae-missing-release-tag) "CronJobsFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface CronJobsFetchResponse { + // (undocumented) + resources: Array; + // (undocumented) + type: 'cronjobs'; +} + // Warning: (ae-missing-release-tag) "CustomResourceFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -78,6 +90,8 @@ export type FetchResponse = | DeploymentFetchResponse | ReplicaSetsFetchResponse | HorizontalPodAutoscalersFetchResponse + | JobsFetchResponse + | CronJobsFetchResponse | IngressesFetchResponse | CustomResourceFetchResponse; @@ -101,6 +115,16 @@ export interface IngressesFetchResponse { type: 'ingresses'; } +// Warning: (ae-missing-release-tag) "JobsFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface JobsFetchResponse { + // (undocumented) + resources: Array; + // (undocumented) + type: 'jobs'; +} + // Warning: (ae-missing-release-tag) "KubernetesErrorTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 3c9cb5e357..a3ab7710c0 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-common", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", - "version": "0.1.5", + "version": "0.1.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -39,7 +39,7 @@ "@kubernetes/client-node": "^0.15.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0" + "@backstage/cli": "^0.9.1" }, "jest": { "roots": [ diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index add661a5f8..8f4daa111b 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -17,8 +17,10 @@ import { ExtensionsV1beta1Ingress, V1ConfigMap, + V1CronJob, V1Deployment, V1HorizontalPodAutoscaler, + V1Job, V1Pod, V1ReplicaSet, V1Service, @@ -83,6 +85,8 @@ export type FetchResponse = | DeploymentFetchResponse | ReplicaSetsFetchResponse | HorizontalPodAutoscalersFetchResponse + | JobsFetchResponse + | CronJobsFetchResponse | IngressesFetchResponse | CustomResourceFetchResponse; @@ -116,6 +120,16 @@ export interface HorizontalPodAutoscalersFetchResponse { resources: Array; } +export interface JobsFetchResponse { + type: 'jobs'; + resources: Array; +} + +export interface CronJobsFetchResponse { + type: 'cronjobs'; + resources: Array; +} + export interface IngressesFetchResponse { type: 'ingresses'; resources: Array; diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 0ae3a6c012..437ea327e4 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes +## 0.4.21 + +### Patch Changes + +- 3739d3f773: Implement support for formatting OpenShift dashboard url links +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.1.6 + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.4.20 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 618f814c21..0c78649cf7 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.4.20", + "version": "0.4.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,10 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", - "@backstage/plugin-kubernetes-common": "^0.1.5", + "@backstage/plugin-kubernetes-common": "^0.1.6", "@backstage/theme": "^0.2.13", "@kubernetes/client-node": "^0.15.0", "@material-ui/core": "^4.12.2", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index e53f6ee261..009252b774 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -34,8 +34,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 16029f1783..bd5e57ae66 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index f46095f46a..37c3320810 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-org +## 0.3.29 + +### Patch Changes + +- 2f4a686411: Use email links in the catalog's members list instead of text to display a member's email +- Updated dependencies + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.3.28 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index db6cfe4d0b..a9146c8f7a 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.3.28", + "version": "0.3.29", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -37,10 +37,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx index 0642e80a7b..7a15bd2122 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx @@ -28,14 +28,13 @@ import { Box, createStyles, Grid, - Link, makeStyles, Theme, Typography, } from '@material-ui/core'; import Pagination from '@material-ui/lab/Pagination'; import React from 'react'; -import { generatePath, Link as RouterLink } from 'react-router-dom'; +import { generatePath } from 'react-router-dom'; import { useAsync } from 'react-use'; import { @@ -43,6 +42,7 @@ import { InfoCard, Progress, ResponseErrorPanel, + Link, } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; @@ -90,7 +90,6 @@ const MemberComponent = ({ member }: { member: UserEntity }) => { { {displayName} - {profile?.email} + {profile?.email && ( + {profile.email} + )} diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 7981b2c104..d02c2df57b 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/permission-backend/.eslintrc.js b/plugins/permission-backend/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/permission-backend/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md new file mode 100644 index 0000000000..5df0a5826a --- /dev/null +++ b/plugins/permission-backend/CHANGELOG.md @@ -0,0 +1,15 @@ +# @backstage/plugin-permission-backend + +## 0.1.0 + +### Minor Changes + +- 7a8312f126: New package containing the backend for authorization and permissions. For more information, see the [authorization PRFC](https://github.com/backstage/backstage/pull/7761). + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.4.9 + - @backstage/plugin-permission-node@0.1.0 + - @backstage/backend-common@0.9.11 + - @backstage/plugin-permission-common@0.2.0 diff --git a/plugins/permission-backend/README.md b/plugins/permission-backend/README.md new file mode 100644 index 0000000000..59fe1730ba --- /dev/null +++ b/plugins/permission-backend/README.md @@ -0,0 +1,6 @@ +# @backstage/plugin-permission-backend + +> NOTE: THIS PACKAGE IS EXPERIMENTAL, HERE BE DRAGONS + +Backend for Backstage authorization and permissions. For more information, see +the [authorization PRFC](https://github.com/backstage/backstage/pull/7761). diff --git a/plugins/permission-backend/api-report.md b/plugins/permission-backend/api-report.md new file mode 100644 index 0000000000..4857c8b044 --- /dev/null +++ b/plugins/permission-backend/api-report.md @@ -0,0 +1,26 @@ +## API Report File for "@backstage/plugin-permission-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import express from 'express'; +import { IdentityClient } from '@backstage/plugin-auth-backend'; +import { Logger as Logger_2 } from 'winston'; +import { PermissionPolicy } from '@backstage/plugin-permission-node'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; + +// @public +export function createRouter(options: RouterOptions): Promise; + +// @public +export interface RouterOptions { + // (undocumented) + discovery: PluginEndpointDiscovery; + // (undocumented) + identity: IdentityClient; + // (undocumented) + logger: Logger_2; + // (undocumented) + policy: PermissionPolicy; +} +``` diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json new file mode 100644 index 0000000000..f335079d0b --- /dev/null +++ b/plugins/permission-backend/package.json @@ -0,0 +1,44 @@ +{ + "name": "@backstage/plugin-permission-backend", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.9.11", + "@backstage/config": "^0.1.11", + "@backstage/plugin-auth-backend": "^0.4.9", + "@backstage/plugin-permission-common": "^0.2.0", + "@backstage/plugin-permission-node": "^0.1.0", + "@types/express": "*", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "node-fetch": "^2.6.1", + "winston": "^3.2.1", + "yn": "^4.0.0", + "zod": "^3.11.6" + }, + "devDependencies": { + "@backstage/cli": "^0.9.1", + "@types/supertest": "^2.0.8", + "supertest": "^4.0.2", + "msw": "^0.35.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/permission-backend/src/index.ts b/plugins/permission-backend/src/index.ts new file mode 100644 index 0000000000..877b9070c3 --- /dev/null +++ b/plugins/permission-backend/src/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2021 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. + */ + +/** + * Backend for Backstage authorization and permissions. + * @packageDocumentation + */ +export * from './service'; diff --git a/plugins/permission-backend/src/service/PermissionIntegrationClient.test.ts b/plugins/permission-backend/src/service/PermissionIntegrationClient.test.ts new file mode 100644 index 0000000000..6a35488117 --- /dev/null +++ b/plugins/permission-backend/src/service/PermissionIntegrationClient.test.ts @@ -0,0 +1,292 @@ +/* + * Copyright 2021 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 { AddressInfo } from 'net'; +import { Server } from 'http'; +import express, { Router } from 'express'; +import { RestContext, rest } from 'msw'; +import { setupServer, SetupServerApi } from 'msw/node'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; +import { PermissionIntegrationClient } from './PermissionIntegrationClient'; + +describe('PermissionIntegrationClient', () => { + describe('applyConditions', () => { + let server: SetupServerApi; + + const mockConditions = { + not: { + allOf: [ + { rule: 'RULE_1', params: [] }, + { rule: 'RULE_2', params: ['abc'] }, + ], + }, + }; + + const mockApplyConditionsHandler = jest.fn( + (_req, res, { json }: RestContext) => { + return res(json({ result: AuthorizeResult.ALLOW })); + }, + ); + + const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base'; + const discovery: PluginEndpointDiscovery = { + async getBaseUrl() { + return mockBaseUrl; + }, + async getExternalBaseUrl() { + throw new Error('Not implemented.'); + }, + }; + + const client: PermissionIntegrationClient = new PermissionIntegrationClient( + { + discovery, + }, + ); + + beforeAll(() => { + server = setupServer(); + server.listen({ onUnhandledRequest: 'error' }); + server.use( + rest.post( + `${mockBaseUrl}/permissions/apply-conditions`, + mockApplyConditionsHandler, + ), + ); + }); + + afterAll(() => server.close()); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should make a POST request to the correct endpoint', async () => { + await client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }); + + expect(mockApplyConditionsHandler).toHaveBeenCalled(); + }); + + it('should include a request body', async () => { + await client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }); + + expect(mockApplyConditionsHandler).toHaveBeenCalledWith( + expect.objectContaining({ + body: { + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }, + }), + expect.anything(), + expect.anything(), + ); + }); + + it('should return the response from the fetch request', async () => { + const response = await client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }); + + expect(response).toEqual( + expect.objectContaining({ result: AuthorizeResult.ALLOW }), + ); + }); + + it('should not include authorization headers if no token is supplied', async () => { + await client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }); + + const request = mockApplyConditionsHandler.mock.calls[0][0]; + expect(request.headers.has('authorization')).toEqual(false); + }); + + it('should include correctly-constructed authorization header if token is supplied', async () => { + await client.applyConditions( + { + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }, + 'Bearer fake-token', + ); + + const request = mockApplyConditionsHandler.mock.calls[0][0]; + expect(request.headers.get('authorization')).toEqual('Bearer fake-token'); + }); + + it('should forward response errors', async () => { + mockApplyConditionsHandler.mockImplementationOnce( + (_req, res, { status }: RestContext) => { + return res(status(401)); + }, + ); + + await expect( + client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }), + ).rejects.toThrowError(/401/i); + }); + + it('should reject invalid responses', async () => { + mockApplyConditionsHandler.mockImplementationOnce( + (_req, res, { json }: RestContext) => { + return res(json({ outcome: AuthorizeResult.ALLOW })); + }, + ); + + await expect( + client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: mockConditions, + }), + ).rejects.toThrowError(/invalid input/i); + }); + }); + + describe('integration with @backstage/plugin-permission-node', () => { + let server: Server; + let client: PermissionIntegrationClient; + + beforeAll(async () => { + const router = Router(); + + router.use( + createPermissionIntegrationRouter({ + resourceType: 'test-resource', + getResource: async resourceRef => ({ id: resourceRef }), + rules: [ + { + name: 'RULE_1', + description: 'Test rule 1', + apply: (_resource: any, input: 'yes' | 'no') => input === 'yes', + toQuery: () => { + throw new Error('Not implemented'); + }, + }, + { + name: 'RULE_2', + description: 'Test rule 2', + apply: (_resource: any, input: 'yes' | 'no') => input === 'yes', + toQuery: () => { + throw new Error('Not implemented'); + }, + }, + ], + }), + ); + + const app = express(); + + app.use('/test-plugin', router); + + await new Promise(resolve => { + server = app.listen(resolve); + }); + + const discovery: PluginEndpointDiscovery = { + async getBaseUrl(pluginId: string) { + const listenPort = (server.address()! as AddressInfo).port; + + return `http://0.0.0.0:${listenPort}/${pluginId}`; + }, + async getExternalBaseUrl() { + throw new Error('Not implemented.'); + }, + }; + + client = new PermissionIntegrationClient({ + discovery, + }); + }); + + afterAll( + async () => + new Promise((resolve, reject) => + server.close(err => (err ? reject(err) : resolve())), + ), + ); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('works for simple conditions', async () => { + await expect( + client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: { rule: 'RULE_1', params: ['no'] }, + }), + ).resolves.toEqual({ result: AuthorizeResult.DENY }); + }); + + it('works for complex criteria', async () => { + await expect( + client.applyConditions({ + pluginId: 'test-plugin', + resourceRef: 'testResource1', + resourceType: 'test-resource', + conditions: { + allOf: [ + { + allOf: [ + { rule: 'RULE_1', params: ['yes'] }, + { not: { rule: 'RULE_2', params: ['no'] } }, + ], + }, + { + not: { + allOf: [ + { rule: 'RULE_1', params: ['no'] }, + { rule: 'RULE_2', params: ['yes'] }, + ], + }, + }, + ], + }, + }), + ).resolves.toEqual({ result: AuthorizeResult.ALLOW }); + }); + }); +}); diff --git a/plugins/permission-backend/src/service/PermissionIntegrationClient.ts b/plugins/permission-backend/src/service/PermissionIntegrationClient.ts new file mode 100644 index 0000000000..0d6017cfd3 --- /dev/null +++ b/plugins/permission-backend/src/service/PermissionIntegrationClient.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2021 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 fetch from 'node-fetch'; +import { z } from 'zod'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { + AuthorizeResult, + PermissionCondition, + PermissionCriteria, +} from '@backstage/plugin-permission-common'; +import { + ApplyConditionsRequest, + ApplyConditionsResponse, +} from '@backstage/plugin-permission-node'; + +const responseSchema = z.object({ + result: z.literal(AuthorizeResult.ALLOW).or(z.literal(AuthorizeResult.DENY)), +}); + +export class PermissionIntegrationClient { + private readonly discovery: PluginEndpointDiscovery; + + constructor(options: { discovery: PluginEndpointDiscovery }) { + this.discovery = options.discovery; + } + + async applyConditions( + { + pluginId, + resourceRef, + resourceType, + conditions, + }: { + resourceRef: string; + pluginId: string; + resourceType: string; + conditions: PermissionCriteria; + }, + authHeader?: string, + ): Promise { + const endpoint = `${await this.discovery.getBaseUrl( + pluginId, + )}/permissions/apply-conditions`; + + const request: ApplyConditionsRequest = { + resourceRef, + resourceType, + conditions, + }; + + const response = await fetch(endpoint, { + method: 'POST', + body: JSON.stringify(request), + headers: { + ...(authHeader ? { authorization: authHeader } : {}), + 'content-type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error( + `Unexpected response from plugin upstream when applying conditions. Expected 200 but got ${response.status} - ${response.statusText}`, + ); + } + + return responseSchema.parse(await response.json()); + } +} diff --git a/plugins/permission-backend/src/service/index.ts b/plugins/permission-backend/src/service/index.ts new file mode 100644 index 0000000000..3ce8cdbcc4 --- /dev/null +++ b/plugins/permission-backend/src/service/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 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 { createRouter } from './router'; +export type { RouterOptions } from './router'; diff --git a/plugins/permission-backend/src/service/router.test.ts b/plugins/permission-backend/src/service/router.test.ts new file mode 100644 index 0000000000..78e7121da6 --- /dev/null +++ b/plugins/permission-backend/src/service/router.test.ts @@ -0,0 +1,298 @@ +/* + * Copyright 2021 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 express from 'express'; +import request from 'supertest'; +import { getVoidLogger } from '@backstage/backend-common'; +import { IdentityClient } from '@backstage/plugin-auth-backend'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { ApplyConditionsResponse } from '@backstage/plugin-permission-node'; +import { PermissionIntegrationClient } from './PermissionIntegrationClient'; + +import { createRouter } from './router'; + +const mockApplyConditions: jest.MockedFunction< + InstanceType['applyConditions'] +> = jest.fn(); +jest.mock('./PermissionIntegrationClient', () => ({ + PermissionIntegrationClient: jest.fn(() => ({ + applyConditions: mockApplyConditions, + })), +})); + +const policy = { + handle: jest.fn().mockImplementation((_req, identity) => { + if (identity) { + return { result: AuthorizeResult.ALLOW }; + } + return { result: AuthorizeResult.DENY }; + }), +}; + +describe('createRouter', () => { + let app: express.Express; + + beforeAll(async () => { + const router = await createRouter({ + logger: getVoidLogger(), + discovery: { + getBaseUrl: jest.fn(), + getExternalBaseUrl: jest.fn(), + }, + identity: { + authenticate: jest.fn(token => { + if (!token) { + throw new Error('No token supplied!'); + } + + return Promise.resolve({ + id: 'test-user', + token, + }); + }), + } as unknown as IdentityClient, + policy, + }); + + app = express().use(router); + }); + + describe('GET /health', () => { + it('returns ok', async () => { + const response = await request(app).get('/health'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); + + describe('POST /authorize', () => { + it('calls the permission policy', async () => { + const response = await request(app) + .post('/authorize') + .send([ + { + id: '123', + permission: { + name: 'test.permission1', + attributes: {}, + }, + }, + { + id: '234', + permission: { + name: 'test.permission2', + attributes: {}, + }, + }, + ]); + + expect(response.status).toEqual(200); + + expect(policy.handle).toHaveBeenCalledWith( + { + permission: { + name: 'test.permission1', + attributes: {}, + }, + }, + undefined, + ); + expect(policy.handle).toHaveBeenCalledWith( + { + permission: { + name: 'test.permission2', + attributes: {}, + }, + }, + undefined, + ); + + expect(response.body).toEqual([ + { id: '123', result: AuthorizeResult.DENY }, + { id: '234', result: AuthorizeResult.DENY }, + ]); + }); + + it('resolves identity from the Authorization header', async () => { + const token = 'test-token'; + const response = await request(app) + .post('/authorize') + .auth(token, { type: 'bearer' }) + .send([ + { + id: '123', + permission: { + name: 'test.permission', + attributes: {}, + }, + }, + ]); + + expect(response.status).toEqual(200); + expect(policy.handle).toHaveBeenCalledWith( + { + permission: { + name: 'test.permission', + attributes: {}, + }, + }, + { id: 'test-user', token: 'test-token' }, + ); + expect(response.body).toEqual([ + { id: '123', result: AuthorizeResult.ALLOW }, + ]); + }); + + describe('conditional policy result', () => { + beforeEach(() => { + policy.handle.mockReturnValueOnce({ + result: AuthorizeResult.CONDITIONAL, + conditions: { + pluginId: 'test-plugin', + resourceType: 'test-resource-1', + conditions: { + anyOf: [{ rule: 'test-rule', params: ['abc'] }], + }, + }, + }); + }); + + it('returns conditions if no resourceRef is supplied', async () => { + const response = await request(app) + .post('/authorize') + .send([ + { + id: '123', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, + }, + ]); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: '123', + result: AuthorizeResult.CONDITIONAL, + conditions: { anyOf: [{ rule: 'test-rule', params: ['abc'] }] }, + }, + ]); + }); + + it.each([ + AuthorizeResult.ALLOW, + AuthorizeResult.DENY, + ])( + 'applies conditions and returns %s if resourceRef is supplied', + async result => { + mockApplyConditions.mockResolvedValueOnce({ + result, + }); + + const response = await request(app) + .post('/authorize') + .auth('test-token', { type: 'bearer' }) + .send([ + { + id: '123', + resourceRef: 'test/resource', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, + }, + ]); + + expect(mockApplyConditions).toHaveBeenCalledWith( + { + pluginId: 'test-plugin', + resourceType: 'test-resource-1', + resourceRef: 'test/resource', + conditions: { anyOf: [{ rule: 'test-rule', params: ['abc'] }] }, + }, + 'Bearer test-token', + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: '123', + result, + }, + ]); + }, + ); + }); + + it.each([ + undefined, + '', + {}, + [{ permission: { name: 'test.permission', attributes: {} } }], + [{ id: '123' }], + [{ id: '123', permission: { name: 'test.permission' } }], + [{ id: '123', permission: { attributes: { invalid: 'attribute' } } }], + ])('returns a 500 error for invalid request %#', async requestBody => { + const response = await request(app).post('/authorize').send(requestBody); + + expect(response.status).toEqual(500); + expect(response.body).toEqual( + expect.objectContaining({ + error: expect.objectContaining({ + message: expect.stringMatching(/invalid/i), + }), + }), + ); + }); + + it('returns a 500 error if the policy returns a different resourceType', async () => { + policy.handle.mockReturnValueOnce({ + result: AuthorizeResult.CONDITIONAL, + conditions: { + pluginId: 'test-plugin', + resourceType: 'test-resource-2', + conditions: {}, + }, + }); + + const response = await request(app) + .post('/authorize') + .send([ + { + id: '123', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, + }, + ]); + + expect(response.status).toEqual(500); + expect(response.body).toEqual( + expect.objectContaining({ + error: expect.objectContaining({ + message: expect.stringMatching(/invalid resource conditions/i), + }), + }), + ); + }); + }); +}); diff --git a/plugins/permission-backend/src/service/router.ts b/plugins/permission-backend/src/service/router.ts new file mode 100644 index 0000000000..8caba23b50 --- /dev/null +++ b/plugins/permission-backend/src/service/router.ts @@ -0,0 +1,166 @@ +/* + * Copyright 2021 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'; +import express, { Request, Response } from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; +import { + errorHandler, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { + BackstageIdentity, + IdentityClient, +} from '@backstage/plugin-auth-backend'; +import { + AuthorizeResult, + AuthorizeResponse, + AuthorizeRequest, + Identified, +} from '@backstage/plugin-permission-common'; +import { PermissionPolicy } from '@backstage/plugin-permission-node'; +import { PermissionIntegrationClient } from './PermissionIntegrationClient'; + +const requestSchema: z.ZodSchema[]> = z.array( + z.object({ + id: z.string(), + resourceRef: z.string().optional(), + permission: z.object({ + name: z.string(), + resourceType: z.string().optional(), + attributes: z.object({ + action: z + .union([ + z.literal('create'), + z.literal('read'), + z.literal('update'), + z.literal('delete'), + ]) + .optional(), + }), + }), + }), +); + +/** + * Options required when constructing a new {@link express#Router} using + * {@link createRouter}. + * + * @public + */ +export interface RouterOptions { + logger: Logger; + discovery: PluginEndpointDiscovery; + policy: PermissionPolicy; + identity: IdentityClient; +} + +const handleRequest = async ( + { id, resourceRef, ...request }: Identified, + user: BackstageIdentity | undefined, + policy: PermissionPolicy, + permissionIntegrationClient: PermissionIntegrationClient, + authHeader?: string, +): Promise> => { + const response = await policy.handle(request, user); + + if (response.result === AuthorizeResult.CONDITIONAL) { + // Sanity check that any resource provided matches the one expected by the permission + if (request.permission.resourceType !== response.conditions.resourceType) { + throw new Error( + `Invalid resource conditions returned from permission policy for permission ${request.permission.name}`, + ); + } + + if (resourceRef) { + return { + id, + ...(await permissionIntegrationClient.applyConditions( + { + resourceRef, + ...response.conditions, + }, + authHeader, + )), + }; + } + + return { + id, + result: AuthorizeResult.CONDITIONAL, + // TODO(mtlewis): this .conditions.conditions situation is a bit awkward. I think it's + // worth exploring a bit of reorganization of the ConditionalPolicyResult type so that + // the naming of property chains like this makes a bit more sense. + conditions: response.conditions.conditions, + }; + } + + return { id, ...response }; +}; + +/** + * Creates a new {@link express#Router} which provides the backend API + * for the permission system. + * + * @public + */ +export async function createRouter( + options: RouterOptions, +): Promise { + const { policy, discovery, identity } = options; + + const permissionIntegrationClient = new PermissionIntegrationClient({ + discovery, + }); + + const router = Router(); + router.use(express.json()); + + router.get('/health', (_, response) => { + response.send({ status: 'ok' }); + }); + + router.post( + '/authorize', + async ( + req: Request[]>, + res: Response[]>, + ) => { + const token = IdentityClient.getBearerToken(req.header('authorization')); + const user = token ? await identity.authenticate(token) : undefined; + + const body = requestSchema.parse(req.body); + + res.json( + await Promise.all( + body.map(request => + handleRequest( + request, + user, + policy, + permissionIntegrationClient, + req.header('authorization'), + ), + ), + ), + ); + }, + ); + + router.use(errorHandler()); + return router; +} diff --git a/plugins/permission-backend/src/setupTests.ts b/plugins/permission-backend/src/setupTests.ts new file mode 100644 index 0000000000..a330613afb --- /dev/null +++ b/plugins/permission-backend/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 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 {}; diff --git a/plugins/permission-common/CHANGELOG.md b/plugins/permission-common/CHANGELOG.md new file mode 100644 index 0000000000..c2ee5a3ecf --- /dev/null +++ b/plugins/permission-common/CHANGELOG.md @@ -0,0 +1,12 @@ +# @backstage/plugin-permission-common + +## 0.2.0 + +### Minor Changes + +- 92439056fb: Accept configApi rather than enabled flag in PermissionClient constructor. + +### Patch Changes + +- Updated dependencies + - @backstage/errors@0.1.5 diff --git a/plugins/permission-common/package.json b/plugins/permission-common/package.json index 9e1d60f782..725aef0dec 100644 --- a/plugins/permission-common/package.json +++ b/plugins/permission-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-common", "description": "Isomorphic types and client for Backstage permissions and authorization", - "version": "0.1.0", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -39,13 +39,13 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/errors": "^0.1.2", + "@backstage/errors": "^0.1.5", "cross-fetch": "^3.0.6", "uuid": "^8.0.0", "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/jest": "^26.0.7", "msw": "^0.35.0" } diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md new file mode 100644 index 0000000000..37c03b7050 --- /dev/null +++ b/plugins/permission-node/CHANGELOG.md @@ -0,0 +1,13 @@ +# @backstage/plugin-permission-node + +## 0.1.0 + +### Minor Changes + +- 44b46644d9: New package containing common permission and authorization utilities for backend plugins. For more information, see the [authorization PRFC](https://github.com/backstage/backstage/pull/7761). + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.4.9 + - @backstage/plugin-permission-common@0.2.0 diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index e22e2d2de4..2bddc21aca 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.0.0", + "version": "0.1.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,14 +29,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/plugin-auth-backend": "^0.4.6", - "@backstage/plugin-permission-common": "^0.1.0", + "@backstage/plugin-auth-backend": "^0.4.9", + "@backstage/plugin-permission-common": "^0.2.0", "@types/express": "^4.17.6", "express": "^4.17.1", "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index a7408c161f..57b3441fa5 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-proxy-backend +## 0.2.14 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/backend-common@0.9.11 + ## 0.2.13 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 05499d88e8..78586449c7 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-proxy-backend", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", - "version": "0.2.13", + "version": "0.2.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/config": "^0.1.8", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -43,7 +43,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index d2bc07ab7e..9c0f70ea6a 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-rollbar-backend +## 0.1.16 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/backend-common@0.9.11 + ## 0.1.15 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index be8b6e65e5..bb30692476 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar-backend", "description": "A Backstage backend plugin that integrates towards Rollbar", - "version": "0.1.15", + "version": "0.1.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/config": "^0.1.10", "@types/express": "^4.17.6", "axios": "^0.21.1", @@ -48,7 +48,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index ee3fc9f5a6..0e4086bd4a 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -49,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 90258d3c28..95dab84e88 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -29,7 +29,6 @@ "command-exists": "^1.2.9", "fs-extra": "10.0.0", "winston": "^3.2.1", - "cross-fetch": "^3.0.6", "yn": "^4.0.0" }, "devDependencies": { diff --git a/plugins/scaffolder-backend/.eslintrc.js b/plugins/scaffolder-backend/.eslintrc.js index 19c9ad7395..2632b5e089 100644 --- a/plugins/scaffolder-backend/.eslintrc.js +++ b/plugins/scaffolder-backend/.eslintrc.js @@ -1,8 +1,41 @@ +const parent = require('@backstage/cli/config/eslint.backend'); + module.exports = { extends: [require.resolve('@backstage/cli/config/eslint.backend')], ignorePatterns: ['sample-templates/'], rules: { 'no-console': 0, // Permitted in console programs 'new-cap': ['error', { capIsNew: false }], // Because Express constructs things e.g. like 'const r = express.Router()' + // Usage of path.resolve is extra sensitive in the scaffolder, so forbid it in non-test code + 'no-restricted-imports': [ + 'error', + { + ...parent.rules['no-restricted-imports'][1], + paths: [ + { + name: 'path', + importNames: ['resolve'], + message: + 'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-common` instead as it prevents security issues', + }, + ], + }, + ], + 'no-restricted-syntax': parent.rules['no-restricted-syntax'].concat([ + { + message: + 'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-common` instead as it prevents security issues', + selector: + 'MemberExpression[object.name="path"][property.name="resolve"]', + }, + ]), }, + overrides: [ + { + files: ['*.test.*', 'src/setupTests.*', 'dev/**'], + rules: { + 'no-restricted-imports': parent.rules['no-restricted-imports'], + }, + }, + ], }; diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index f71849a1d2..092a678fce 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend +## 0.15.14 + +### Patch Changes + +- a096e4c4d7: Switched to executing scaffolder templating in a secure context for any template based on nunjucks, as it is [not secure by default](https://mozilla.github.io/nunjucks/api.html#user-defined-templates-warning). +- f9352ab606: Removed all usages of `path.resolve` in order to ensure that template paths are resolved in a safe way. +- e634a47ce5: Fix bug where there was error log lines written when failing to `JSON.parse` things that were not `JSON` values. +- 42ebbc18c0: Bump gitbeaker to the latest version +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/plugin-catalog-backend@0.18.0 + - @backstage/backend-common@0.9.11 + ## 0.15.13 ### Patch Changes diff --git a/plugins/scaffolder-backend/assets/nunjucks.js.txt b/plugins/scaffolder-backend/assets/nunjucks.js.txt new file mode 100644 index 0000000000..b78d1d98ac --- /dev/null +++ b/plugins/scaffolder-backend/assets/nunjucks.js.txt @@ -0,0 +1,10385 @@ + +/** + * Copyright (c) 2012-2015, James Long + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var __commonJS = (callback, module2) => () => { + if (!module2) { + module2 = {exports: {}}; + callback(module2.exports, module2); + } + return module2.exports; +}; + +// ../../node_modules/nunjucks/src/lib.js +var require_lib = __commonJS((exports2, module2) => { + "use strict"; + var ArrayProto = Array.prototype; + var ObjProto = Object.prototype; + var escapeMap = { + "&": "&", + '"': """, + "'": "'", + "<": "<", + ">": ">" + }; + var escapeRegex = /[&"'<>]/g; + var _exports = module2.exports = {}; + function hasOwnProp(obj, k) { + return ObjProto.hasOwnProperty.call(obj, k); + } + _exports.hasOwnProp = hasOwnProp; + function lookupEscape(ch) { + return escapeMap[ch]; + } + function _prettifyError(path, withInternals, err) { + if (!err.Update) { + err = new _exports.TemplateError(err); + } + err.Update(path); + if (!withInternals) { + var old = err; + err = new Error(old.message); + err.name = old.name; + } + return err; + } + _exports._prettifyError = _prettifyError; + function TemplateError(message, lineno, colno) { + var err; + var cause; + if (message instanceof Error) { + cause = message; + message = cause.name + ": " + cause.message; + } + if (Object.setPrototypeOf) { + err = new Error(message); + Object.setPrototypeOf(err, TemplateError.prototype); + } else { + err = this; + Object.defineProperty(err, "message", { + enumerable: false, + writable: true, + value: message + }); + } + Object.defineProperty(err, "name", { + value: "Template render error" + }); + if (Error.captureStackTrace) { + Error.captureStackTrace(err, this.constructor); + } + var getStack; + if (cause) { + var stackDescriptor = Object.getOwnPropertyDescriptor(cause, "stack"); + getStack = stackDescriptor && (stackDescriptor.get || function() { + return stackDescriptor.value; + }); + if (!getStack) { + getStack = function getStack2() { + return cause.stack; + }; + } + } else { + var stack = new Error(message).stack; + getStack = function getStack2() { + return stack; + }; + } + Object.defineProperty(err, "stack", { + get: function get() { + return getStack.call(err); + } + }); + Object.defineProperty(err, "cause", { + value: cause + }); + err.lineno = lineno; + err.colno = colno; + err.firstUpdate = true; + err.Update = function Update(path) { + var msg = "(" + (path || "unknown path") + ")"; + if (this.firstUpdate) { + if (this.lineno && this.colno) { + msg += " [Line " + this.lineno + ", Column " + this.colno + "]"; + } else if (this.lineno) { + msg += " [Line " + this.lineno + "]"; + } + } + msg += "\n "; + if (this.firstUpdate) { + msg += " "; + } + this.message = msg + (this.message || ""); + this.firstUpdate = false; + return this; + }; + return err; + } + if (Object.setPrototypeOf) { + Object.setPrototypeOf(TemplateError.prototype, Error.prototype); + } else { + TemplateError.prototype = Object.create(Error.prototype, { + constructor: { + value: TemplateError + } + }); + } + _exports.TemplateError = TemplateError; + function escape(val) { + return val.replace(escapeRegex, lookupEscape); + } + _exports.escape = escape; + function isFunction(obj) { + return ObjProto.toString.call(obj) === "[object Function]"; + } + _exports.isFunction = isFunction; + function isArray(obj) { + return ObjProto.toString.call(obj) === "[object Array]"; + } + _exports.isArray = isArray; + function isString(obj) { + return ObjProto.toString.call(obj) === "[object String]"; + } + _exports.isString = isString; + function isObject(obj) { + return ObjProto.toString.call(obj) === "[object Object]"; + } + _exports.isObject = isObject; + function _prepareAttributeParts(attr) { + if (!attr) { + return []; + } + if (typeof attr === "string") { + return attr.split("."); + } + return [attr]; + } + function getAttrGetter(attribute) { + var parts = _prepareAttributeParts(attribute); + return function attrGetter(item) { + var _item = item; + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + if (hasOwnProp(_item, part)) { + _item = _item[part]; + } else { + return void 0; + } + } + return _item; + }; + } + _exports.getAttrGetter = getAttrGetter; + function groupBy(obj, val, throwOnUndefined) { + var result = {}; + var iterator = isFunction(val) ? val : getAttrGetter(val); + for (var i = 0; i < obj.length; i++) { + var value = obj[i]; + var key = iterator(value, i); + if (key === void 0 && throwOnUndefined === true) { + throw new TypeError('groupby: attribute "' + val + '" resolved to undefined'); + } + (result[key] || (result[key] = [])).push(value); + } + return result; + } + _exports.groupBy = groupBy; + function toArray(obj) { + return Array.prototype.slice.call(obj); + } + _exports.toArray = toArray; + function without(array) { + var result = []; + if (!array) { + return result; + } + var length = array.length; + var contains = toArray(arguments).slice(1); + var index = -1; + while (++index < length) { + if (indexOf(contains, array[index]) === -1) { + result.push(array[index]); + } + } + return result; + } + _exports.without = without; + function repeat(char_, n) { + var str = ""; + for (var i = 0; i < n; i++) { + str += char_; + } + return str; + } + _exports.repeat = repeat; + function each(obj, func, context) { + if (obj == null) { + return; + } + if (ArrayProto.forEach && obj.forEach === ArrayProto.forEach) { + obj.forEach(func, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + func.call(context, obj[i], i, obj); + } + } + } + _exports.each = each; + function map(obj, func) { + var results = []; + if (obj == null) { + return results; + } + if (ArrayProto.map && obj.map === ArrayProto.map) { + return obj.map(func); + } + for (var i = 0; i < obj.length; i++) { + results[results.length] = func(obj[i], i); + } + if (obj.length === +obj.length) { + results.length = obj.length; + } + return results; + } + _exports.map = map; + function asyncIter(arr, iter, cb) { + var i = -1; + function next() { + i++; + if (i < arr.length) { + iter(arr[i], i, next, cb); + } else { + cb(); + } + } + next(); + } + _exports.asyncIter = asyncIter; + function asyncFor(obj, iter, cb) { + var keys = keys_(obj || {}); + var len = keys.length; + var i = -1; + function next() { + i++; + var k = keys[i]; + if (i < len) { + iter(k, obj[k], i, len, next); + } else { + cb(); + } + } + next(); + } + _exports.asyncFor = asyncFor; + function indexOf(arr, searchElement, fromIndex) { + return Array.prototype.indexOf.call(arr || [], searchElement, fromIndex); + } + _exports.indexOf = indexOf; + function keys_(obj) { + var arr = []; + for (var k in obj) { + if (hasOwnProp(obj, k)) { + arr.push(k); + } + } + return arr; + } + _exports.keys = keys_; + function _entries(obj) { + return keys_(obj).map(function(k) { + return [k, obj[k]]; + }); + } + _exports._entries = _entries; + function _values(obj) { + return keys_(obj).map(function(k) { + return obj[k]; + }); + } + _exports._values = _values; + function extend(obj1, obj2) { + obj1 = obj1 || {}; + keys_(obj2).forEach(function(k) { + obj1[k] = obj2[k]; + }); + return obj1; + } + _exports._assign = _exports.extend = extend; + function inOperator(key, val) { + if (isArray(val) || isString(val)) { + return val.indexOf(key) !== -1; + } else if (isObject(val)) { + return key in val; + } + throw new Error('Cannot use "in" operator to search for "' + key + '" in unexpected types.'); + } + _exports.inOperator = inOperator; +}); + +// ../../node_modules/asap/raw.js +var require_raw = __commonJS((exports2, module2) => { + "use strict"; + var domain; + var hasSetImmediate = typeof setImmediate === "function"; + module2.exports = rawAsap; + function rawAsap(task) { + if (!queue.length) { + requestFlush(); + flushing = true; + } + queue[queue.length] = task; + } + var queue = []; + var flushing = false; + var index = 0; + var capacity = 1024; + function flush() { + while (index < queue.length) { + var currentIndex = index; + index = index + 1; + queue[currentIndex].call(); + if (index > capacity) { + for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) { + queue[scan] = queue[scan + index]; + } + queue.length -= index; + index = 0; + } + } + queue.length = 0; + index = 0; + flushing = false; + } + rawAsap.requestFlush = requestFlush; + function requestFlush() { + var parentDomain = process.domain; + if (parentDomain) { + if (!domain) { + domain = require("domain"); + } + domain.active = process.domain = null; + } + if (flushing && hasSetImmediate) { + setImmediate(flush); + } else { + process.nextTick(flush); + } + if (parentDomain) { + domain.active = process.domain = parentDomain; + } + } +}); + +// ../../node_modules/asap/asap.js +var require_asap = __commonJS((exports2, module2) => { + "use strict"; + var rawAsap = require_raw(); + var freeTasks = []; + module2.exports = asap; + function asap(task) { + var rawTask; + if (freeTasks.length) { + rawTask = freeTasks.pop(); + } else { + rawTask = new RawTask(); + } + rawTask.task = task; + rawTask.domain = process.domain; + rawAsap(rawTask); + } + function RawTask() { + this.task = null; + this.domain = null; + } + RawTask.prototype.call = function() { + if (this.domain) { + this.domain.enter(); + } + var threw = true; + try { + this.task.call(); + threw = false; + if (this.domain) { + this.domain.exit(); + } + } finally { + if (threw) { + rawAsap.requestFlush(); + } + this.task = null; + this.domain = null; + freeTasks.push(this); + } + }; +}); + +// ../../node_modules/a-sync-waterfall/index.js +var require_a_sync_waterfall = __commonJS((exports2, module2) => { + (function(globals) { + "use strict"; + var executeSync = function() { + var args = Array.prototype.slice.call(arguments); + if (typeof args[0] === "function") { + args[0].apply(null, args.splice(1)); + } + }; + var executeAsync = function(fn) { + if (typeof setImmediate === "function") { + setImmediate(fn); + } else if (typeof process !== "undefined" && process.nextTick) { + process.nextTick(fn); + } else { + setTimeout(fn, 0); + } + }; + var makeIterator = function(tasks) { + var makeCallback = function(index) { + var fn = function() { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function() { + return index < tasks.length - 1 ? makeCallback(index + 1) : null; + }; + return fn; + }; + return makeCallback(0); + }; + var _isArray = Array.isArray || function(maybeArray) { + return Object.prototype.toString.call(maybeArray) === "[object Array]"; + }; + var waterfall = function(tasks, callback, forceAsync) { + var nextTick = forceAsync ? executeAsync : executeSync; + callback = callback || function() { + }; + if (!_isArray(tasks)) { + var err = new Error("First argument to waterfall must be an array of functions"); + return callback(err); + } + if (!tasks.length) { + return callback(); + } + var wrapIterator = function(iterator) { + return function(err2) { + if (err2) { + callback.apply(null, arguments); + callback = function() { + }; + } else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } else { + args.push(callback); + } + nextTick(function() { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(makeIterator(tasks))(); + }; + if (typeof define !== "undefined" && define.amd) { + define([], function() { + return waterfall; + }); + } else if (typeof module2 !== "undefined" && module2.exports) { + module2.exports = waterfall; + } else { + globals.waterfall = waterfall; + } + })(exports2); +}); + +// ../../node_modules/nunjucks/src/lexer.js +var require_lexer = __commonJS((exports2, module2) => { + "use strict"; + var lib2 = require_lib(); + var whitespaceChars = " \n \r\xA0"; + var delimChars = "()[]{}%*-+~/#,:|.<>=!"; + var intChars = "0123456789"; + var BLOCK_START = "{%"; + var BLOCK_END = "%}"; + var VARIABLE_START = "{{"; + var VARIABLE_END = "}}"; + var COMMENT_START = "{#"; + var COMMENT_END = "#}"; + var TOKEN_STRING = "string"; + var TOKEN_WHITESPACE = "whitespace"; + var TOKEN_DATA = "data"; + var TOKEN_BLOCK_START = "block-start"; + var TOKEN_BLOCK_END = "block-end"; + var TOKEN_VARIABLE_START = "variable-start"; + var TOKEN_VARIABLE_END = "variable-end"; + var TOKEN_COMMENT = "comment"; + var TOKEN_LEFT_PAREN = "left-paren"; + var TOKEN_RIGHT_PAREN = "right-paren"; + var TOKEN_LEFT_BRACKET = "left-bracket"; + var TOKEN_RIGHT_BRACKET = "right-bracket"; + var TOKEN_LEFT_CURLY = "left-curly"; + var TOKEN_RIGHT_CURLY = "right-curly"; + var TOKEN_OPERATOR = "operator"; + var TOKEN_COMMA = "comma"; + var TOKEN_COLON = "colon"; + var TOKEN_TILDE = "tilde"; + var TOKEN_PIPE = "pipe"; + var TOKEN_INT = "int"; + var TOKEN_FLOAT = "float"; + var TOKEN_BOOLEAN = "boolean"; + var TOKEN_NONE = "none"; + var TOKEN_SYMBOL = "symbol"; + var TOKEN_SPECIAL = "special"; + var TOKEN_REGEX = "regex"; + function token(type, value, lineno, colno) { + return { + type, + value, + lineno, + colno + }; + } + var Tokenizer = /* @__PURE__ */ function() { + function Tokenizer2(str, opts) { + this.str = str; + this.index = 0; + this.len = str.length; + this.lineno = 0; + this.colno = 0; + this.in_code = false; + opts = opts || {}; + var tags = opts.tags || {}; + this.tags = { + BLOCK_START: tags.blockStart || BLOCK_START, + BLOCK_END: tags.blockEnd || BLOCK_END, + VARIABLE_START: tags.variableStart || VARIABLE_START, + VARIABLE_END: tags.variableEnd || VARIABLE_END, + COMMENT_START: tags.commentStart || COMMENT_START, + COMMENT_END: tags.commentEnd || COMMENT_END + }; + this.trimBlocks = !!opts.trimBlocks; + this.lstripBlocks = !!opts.lstripBlocks; + } + var _proto = Tokenizer2.prototype; + _proto.nextToken = function nextToken() { + var lineno = this.lineno; + var colno = this.colno; + var tok; + if (this.in_code) { + var cur = this.current(); + if (this.isFinished()) { + return null; + } else if (cur === '"' || cur === "'") { + return token(TOKEN_STRING, this._parseString(cur), lineno, colno); + } else if (tok = this._extract(whitespaceChars)) { + return token(TOKEN_WHITESPACE, tok, lineno, colno); + } else if ((tok = this._extractString(this.tags.BLOCK_END)) || (tok = this._extractString("-" + this.tags.BLOCK_END))) { + this.in_code = false; + if (this.trimBlocks) { + cur = this.current(); + if (cur === "\n") { + this.forward(); + } else if (cur === "\r") { + this.forward(); + cur = this.current(); + if (cur === "\n") { + this.forward(); + } else { + this.back(); + } + } + } + return token(TOKEN_BLOCK_END, tok, lineno, colno); + } else if ((tok = this._extractString(this.tags.VARIABLE_END)) || (tok = this._extractString("-" + this.tags.VARIABLE_END))) { + this.in_code = false; + return token(TOKEN_VARIABLE_END, tok, lineno, colno); + } else if (cur === "r" && this.str.charAt(this.index + 1) === "/") { + this.forwardN(2); + var regexBody = ""; + while (!this.isFinished()) { + if (this.current() === "/" && this.previous() !== "\\") { + this.forward(); + break; + } else { + regexBody += this.current(); + this.forward(); + } + } + var POSSIBLE_FLAGS = ["g", "i", "m", "y"]; + var regexFlags = ""; + while (!this.isFinished()) { + var isCurrentAFlag = POSSIBLE_FLAGS.indexOf(this.current()) !== -1; + if (isCurrentAFlag) { + regexFlags += this.current(); + this.forward(); + } else { + break; + } + } + return token(TOKEN_REGEX, { + body: regexBody, + flags: regexFlags + }, lineno, colno); + } else if (delimChars.indexOf(cur) !== -1) { + this.forward(); + var complexOps = ["==", "===", "!=", "!==", "<=", ">=", "//", "**"]; + var curComplex = cur + this.current(); + var type; + if (lib2.indexOf(complexOps, curComplex) !== -1) { + this.forward(); + cur = curComplex; + if (lib2.indexOf(complexOps, curComplex + this.current()) !== -1) { + cur = curComplex + this.current(); + this.forward(); + } + } + switch (cur) { + case "(": + type = TOKEN_LEFT_PAREN; + break; + case ")": + type = TOKEN_RIGHT_PAREN; + break; + case "[": + type = TOKEN_LEFT_BRACKET; + break; + case "]": + type = TOKEN_RIGHT_BRACKET; + break; + case "{": + type = TOKEN_LEFT_CURLY; + break; + case "}": + type = TOKEN_RIGHT_CURLY; + break; + case ",": + type = TOKEN_COMMA; + break; + case ":": + type = TOKEN_COLON; + break; + case "~": + type = TOKEN_TILDE; + break; + case "|": + type = TOKEN_PIPE; + break; + default: + type = TOKEN_OPERATOR; + } + return token(type, cur, lineno, colno); + } else { + tok = this._extractUntil(whitespaceChars + delimChars); + if (tok.match(/^[-+]?[0-9]+$/)) { + if (this.current() === ".") { + this.forward(); + var dec = this._extract(intChars); + return token(TOKEN_FLOAT, tok + "." + dec, lineno, colno); + } else { + return token(TOKEN_INT, tok, lineno, colno); + } + } else if (tok.match(/^(true|false)$/)) { + return token(TOKEN_BOOLEAN, tok, lineno, colno); + } else if (tok === "none") { + return token(TOKEN_NONE, tok, lineno, colno); + } else if (tok === "null") { + return token(TOKEN_NONE, tok, lineno, colno); + } else if (tok) { + return token(TOKEN_SYMBOL, tok, lineno, colno); + } else { + throw new Error("Unexpected value while parsing: " + tok); + } + } + } else { + var beginChars = this.tags.BLOCK_START.charAt(0) + this.tags.VARIABLE_START.charAt(0) + this.tags.COMMENT_START.charAt(0) + this.tags.COMMENT_END.charAt(0); + if (this.isFinished()) { + return null; + } else if ((tok = this._extractString(this.tags.BLOCK_START + "-")) || (tok = this._extractString(this.tags.BLOCK_START))) { + this.in_code = true; + return token(TOKEN_BLOCK_START, tok, lineno, colno); + } else if ((tok = this._extractString(this.tags.VARIABLE_START + "-")) || (tok = this._extractString(this.tags.VARIABLE_START))) { + this.in_code = true; + return token(TOKEN_VARIABLE_START, tok, lineno, colno); + } else { + tok = ""; + var data; + var inComment = false; + if (this._matches(this.tags.COMMENT_START)) { + inComment = true; + tok = this._extractString(this.tags.COMMENT_START); + } + while ((data = this._extractUntil(beginChars)) !== null) { + tok += data; + if ((this._matches(this.tags.BLOCK_START) || this._matches(this.tags.VARIABLE_START) || this._matches(this.tags.COMMENT_START)) && !inComment) { + if (this.lstripBlocks && this._matches(this.tags.BLOCK_START) && this.colno > 0 && this.colno <= tok.length) { + var lastLine = tok.slice(-this.colno); + if (/^\s+$/.test(lastLine)) { + tok = tok.slice(0, -this.colno); + if (!tok.length) { + return this.nextToken(); + } + } + } + break; + } else if (this._matches(this.tags.COMMENT_END)) { + if (!inComment) { + throw new Error("unexpected end of comment"); + } + tok += this._extractString(this.tags.COMMENT_END); + break; + } else { + tok += this.current(); + this.forward(); + } + } + if (data === null && inComment) { + throw new Error("expected end of comment, got end of file"); + } + return token(inComment ? TOKEN_COMMENT : TOKEN_DATA, tok, lineno, colno); + } + } + }; + _proto._parseString = function _parseString(delimiter) { + this.forward(); + var str = ""; + while (!this.isFinished() && this.current() !== delimiter) { + var cur = this.current(); + if (cur === "\\") { + this.forward(); + switch (this.current()) { + case "n": + str += "\n"; + break; + case "t": + str += " "; + break; + case "r": + str += "\r"; + break; + default: + str += this.current(); + } + this.forward(); + } else { + str += cur; + this.forward(); + } + } + this.forward(); + return str; + }; + _proto._matches = function _matches(str) { + if (this.index + str.length > this.len) { + return null; + } + var m = this.str.slice(this.index, this.index + str.length); + return m === str; + }; + _proto._extractString = function _extractString(str) { + if (this._matches(str)) { + this.forwardN(str.length); + return str; + } + return null; + }; + _proto._extractUntil = function _extractUntil(charString) { + return this._extractMatching(true, charString || ""); + }; + _proto._extract = function _extract(charString) { + return this._extractMatching(false, charString); + }; + _proto._extractMatching = function _extractMatching(breakOnMatch, charString) { + if (this.isFinished()) { + return null; + } + var first = charString.indexOf(this.current()); + if (breakOnMatch && first === -1 || !breakOnMatch && first !== -1) { + var t = this.current(); + this.forward(); + var idx = charString.indexOf(this.current()); + while ((breakOnMatch && idx === -1 || !breakOnMatch && idx !== -1) && !this.isFinished()) { + t += this.current(); + this.forward(); + idx = charString.indexOf(this.current()); + } + return t; + } + return ""; + }; + _proto._extractRegex = function _extractRegex(regex) { + var matches = this.currentStr().match(regex); + if (!matches) { + return null; + } + this.forwardN(matches[0].length); + return matches; + }; + _proto.isFinished = function isFinished() { + return this.index >= this.len; + }; + _proto.forwardN = function forwardN(n) { + for (var i = 0; i < n; i++) { + this.forward(); + } + }; + _proto.forward = function forward() { + this.index++; + if (this.previous() === "\n") { + this.lineno++; + this.colno = 0; + } else { + this.colno++; + } + }; + _proto.backN = function backN(n) { + for (var i = 0; i < n; i++) { + this.back(); + } + }; + _proto.back = function back() { + this.index--; + if (this.current() === "\n") { + this.lineno--; + var idx = this.src.lastIndexOf("\n", this.index - 1); + if (idx === -1) { + this.colno = this.index; + } else { + this.colno = this.index - idx; + } + } else { + this.colno--; + } + }; + _proto.current = function current() { + if (!this.isFinished()) { + return this.str.charAt(this.index); + } + return ""; + }; + _proto.currentStr = function currentStr() { + if (!this.isFinished()) { + return this.str.substr(this.index); + } + return ""; + }; + _proto.previous = function previous() { + return this.str.charAt(this.index - 1); + }; + return Tokenizer2; + }(); + module2.exports = { + lex: function lex(src, opts) { + return new Tokenizer(src, opts); + }, + TOKEN_STRING, + TOKEN_WHITESPACE, + TOKEN_DATA, + TOKEN_BLOCK_START, + TOKEN_BLOCK_END, + TOKEN_VARIABLE_START, + TOKEN_VARIABLE_END, + TOKEN_COMMENT, + TOKEN_LEFT_PAREN, + TOKEN_RIGHT_PAREN, + TOKEN_LEFT_BRACKET, + TOKEN_RIGHT_BRACKET, + TOKEN_LEFT_CURLY, + TOKEN_RIGHT_CURLY, + TOKEN_OPERATOR, + TOKEN_COMMA, + TOKEN_COLON, + TOKEN_TILDE, + TOKEN_PIPE, + TOKEN_INT, + TOKEN_FLOAT, + TOKEN_BOOLEAN, + TOKEN_NONE, + TOKEN_SYMBOL, + TOKEN_SPECIAL, + TOKEN_REGEX + }; +}); + +// ../../node_modules/nunjucks/src/object.js +var require_object = __commonJS((exports2, module2) => { + "use strict"; + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) + descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) + _defineProperties(Constructor.prototype, protoProps); + if (staticProps) + _defineProperties(Constructor, staticProps); + return Constructor; + } + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var EventEmitter = require("events"); + var lib2 = require_lib(); + function parentWrap(parent, prop) { + if (typeof parent !== "function" || typeof prop !== "function") { + return prop; + } + return function wrap() { + var tmp = this.parent; + this.parent = parent; + var res = prop.apply(this, arguments); + this.parent = tmp; + return res; + }; + } + function extendClass(cls, name, props) { + props = props || {}; + lib2.keys(props).forEach(function(k) { + props[k] = parentWrap(cls.prototype[k], props[k]); + }); + var subclass = /* @__PURE__ */ function(_cls) { + _inheritsLoose(subclass2, _cls); + function subclass2() { + return _cls.apply(this, arguments) || this; + } + _createClass(subclass2, [{ + key: "typename", + get: function get() { + return name; + } + }]); + return subclass2; + }(cls); + lib2._assign(subclass.prototype, props); + return subclass; + } + var Obj = /* @__PURE__ */ function() { + function Obj2() { + this.init.apply(this, arguments); + } + var _proto = Obj2.prototype; + _proto.init = function init() { + }; + Obj2.extend = function extend(name, props) { + if (typeof name === "object") { + props = name; + name = "anonymous"; + } + return extendClass(this, name, props); + }; + _createClass(Obj2, [{ + key: "typename", + get: function get() { + return this.constructor.name; + } + }]); + return Obj2; + }(); + var EmitterObj = /* @__PURE__ */ function(_EventEmitter) { + _inheritsLoose(EmitterObj2, _EventEmitter); + function EmitterObj2() { + var _this2; + var _this; + _this = _EventEmitter.call(this) || this; + (_this2 = _this).init.apply(_this2, arguments); + return _this; + } + var _proto2 = EmitterObj2.prototype; + _proto2.init = function init() { + }; + EmitterObj2.extend = function extend(name, props) { + if (typeof name === "object") { + props = name; + name = "anonymous"; + } + return extendClass(this, name, props); + }; + _createClass(EmitterObj2, [{ + key: "typename", + get: function get() { + return this.constructor.name; + } + }]); + return EmitterObj2; + }(EventEmitter); + module2.exports = { + Obj, + EmitterObj + }; +}); + +// ../../node_modules/nunjucks/src/nodes.js +var require_nodes = __commonJS((exports2, module2) => { + "use strict"; + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) + descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) + _defineProperties(Constructor.prototype, protoProps); + if (staticProps) + _defineProperties(Constructor, staticProps); + return Constructor; + } + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var _require2 = require_object(); + var Obj = _require2.Obj; + function traverseAndCheck(obj, type, results) { + if (obj instanceof type) { + results.push(obj); + } + if (obj instanceof Node) { + obj.findAll(type, results); + } + } + var Node = /* @__PURE__ */ function(_Obj) { + _inheritsLoose(Node2, _Obj); + function Node2() { + return _Obj.apply(this, arguments) || this; + } + var _proto = Node2.prototype; + _proto.init = function init(lineno, colno) { + var _arguments = arguments, _this = this; + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + this.lineno = lineno; + this.colno = colno; + this.fields.forEach(function(field, i) { + var val = _arguments[i + 2]; + if (val === void 0) { + val = null; + } + _this[field] = val; + }); + }; + _proto.findAll = function findAll(type, results) { + var _this2 = this; + results = results || []; + if (this instanceof NodeList) { + this.children.forEach(function(child) { + return traverseAndCheck(child, type, results); + }); + } else { + this.fields.forEach(function(field) { + return traverseAndCheck(_this2[field], type, results); + }); + } + return results; + }; + _proto.iterFields = function iterFields(func) { + var _this3 = this; + this.fields.forEach(function(field) { + func(_this3[field], field); + }); + }; + return Node2; + }(Obj); + var Value = /* @__PURE__ */ function(_Node) { + _inheritsLoose(Value2, _Node); + function Value2() { + return _Node.apply(this, arguments) || this; + } + _createClass(Value2, [{ + key: "typename", + get: function get() { + return "Value"; + } + }, { + key: "fields", + get: function get() { + return ["value"]; + } + }]); + return Value2; + }(Node); + var NodeList = /* @__PURE__ */ function(_Node2) { + _inheritsLoose(NodeList2, _Node2); + function NodeList2() { + return _Node2.apply(this, arguments) || this; + } + var _proto2 = NodeList2.prototype; + _proto2.init = function init(lineno, colno, nodes2) { + _Node2.prototype.init.call(this, lineno, colno, nodes2 || []); + }; + _proto2.addChild = function addChild(node) { + this.children.push(node); + }; + _createClass(NodeList2, [{ + key: "typename", + get: function get() { + return "NodeList"; + } + }, { + key: "fields", + get: function get() { + return ["children"]; + } + }]); + return NodeList2; + }(Node); + var Root = NodeList.extend("Root"); + var Literal = Value.extend("Literal"); + var Symbol2 = Value.extend("Symbol"); + var Group = NodeList.extend("Group"); + var ArrayNode = NodeList.extend("Array"); + var Pair = Node.extend("Pair", { + fields: ["key", "value"] + }); + var Dict = NodeList.extend("Dict"); + var LookupVal = Node.extend("LookupVal", { + fields: ["target", "val"] + }); + var If = Node.extend("If", { + fields: ["cond", "body", "else_"] + }); + var IfAsync = If.extend("IfAsync"); + var InlineIf = Node.extend("InlineIf", { + fields: ["cond", "body", "else_"] + }); + var For = Node.extend("For", { + fields: ["arr", "name", "body", "else_"] + }); + var AsyncEach = For.extend("AsyncEach"); + var AsyncAll = For.extend("AsyncAll"); + var Macro = Node.extend("Macro", { + fields: ["name", "args", "body"] + }); + var Caller = Macro.extend("Caller"); + var Import = Node.extend("Import", { + fields: ["template", "target", "withContext"] + }); + var FromImport = /* @__PURE__ */ function(_Node3) { + _inheritsLoose(FromImport2, _Node3); + function FromImport2() { + return _Node3.apply(this, arguments) || this; + } + var _proto3 = FromImport2.prototype; + _proto3.init = function init(lineno, colno, template, names, withContext) { + _Node3.prototype.init.call(this, lineno, colno, template, names || new NodeList(), withContext); + }; + _createClass(FromImport2, [{ + key: "typename", + get: function get() { + return "FromImport"; + } + }, { + key: "fields", + get: function get() { + return ["template", "names", "withContext"]; + } + }]); + return FromImport2; + }(Node); + var FunCall = Node.extend("FunCall", { + fields: ["name", "args"] + }); + var Filter = FunCall.extend("Filter"); + var FilterAsync = Filter.extend("FilterAsync", { + fields: ["name", "args", "symbol"] + }); + var KeywordArgs = Dict.extend("KeywordArgs"); + var Block = Node.extend("Block", { + fields: ["name", "body"] + }); + var Super = Node.extend("Super", { + fields: ["blockName", "symbol"] + }); + var TemplateRef = Node.extend("TemplateRef", { + fields: ["template"] + }); + var Extends = TemplateRef.extend("Extends"); + var Include = Node.extend("Include", { + fields: ["template", "ignoreMissing"] + }); + var Set2 = Node.extend("Set", { + fields: ["targets", "value"] + }); + var Switch = Node.extend("Switch", { + fields: ["expr", "cases", "default"] + }); + var Case = Node.extend("Case", { + fields: ["cond", "body"] + }); + var Output = NodeList.extend("Output"); + var Capture = Node.extend("Capture", { + fields: ["body"] + }); + var TemplateData = Literal.extend("TemplateData"); + var UnaryOp = Node.extend("UnaryOp", { + fields: ["target"] + }); + var BinOp = Node.extend("BinOp", { + fields: ["left", "right"] + }); + var In = BinOp.extend("In"); + var Is = BinOp.extend("Is"); + var Or = BinOp.extend("Or"); + var And = BinOp.extend("And"); + var Not = UnaryOp.extend("Not"); + var Add = BinOp.extend("Add"); + var Concat = BinOp.extend("Concat"); + var Sub = BinOp.extend("Sub"); + var Mul = BinOp.extend("Mul"); + var Div = BinOp.extend("Div"); + var FloorDiv = BinOp.extend("FloorDiv"); + var Mod = BinOp.extend("Mod"); + var Pow = BinOp.extend("Pow"); + var Neg = UnaryOp.extend("Neg"); + var Pos = UnaryOp.extend("Pos"); + var Compare = Node.extend("Compare", { + fields: ["expr", "ops"] + }); + var CompareOperand = Node.extend("CompareOperand", { + fields: ["expr", "type"] + }); + var CallExtension = Node.extend("CallExtension", { + init: function init(ext, prop, args, contentArgs) { + this.parent(); + this.extName = ext.__name || ext; + this.prop = prop; + this.args = args || new NodeList(); + this.contentArgs = contentArgs || []; + this.autoescape = ext.autoescape; + }, + fields: ["extName", "prop", "args", "contentArgs"] + }); + var CallExtensionAsync = CallExtension.extend("CallExtensionAsync"); + function print(str, indent, inline) { + var lines = str.split("\n"); + lines.forEach(function(line, i) { + if (line && (inline && i > 0 || !inline)) { + process.stdout.write(" ".repeat(indent)); + } + var nl = i === lines.length - 1 ? "" : "\n"; + process.stdout.write("" + line + nl); + }); + } + function printNodes(node, indent) { + indent = indent || 0; + print(node.typename + ": ", indent); + if (node instanceof NodeList) { + print("\n"); + node.children.forEach(function(n) { + printNodes(n, indent + 2); + }); + } else if (node instanceof CallExtension) { + print(node.extName + "." + node.prop + "\n"); + if (node.args) { + printNodes(node.args, indent + 2); + } + if (node.contentArgs) { + node.contentArgs.forEach(function(n) { + printNodes(n, indent + 2); + }); + } + } else { + var nodes2 = []; + var props = null; + node.iterFields(function(val, fieldName) { + if (val instanceof Node) { + nodes2.push([fieldName, val]); + } else { + props = props || {}; + props[fieldName] = val; + } + }); + if (props) { + print(JSON.stringify(props, null, 2) + "\n", null, true); + } else { + print("\n"); + } + nodes2.forEach(function(_ref) { + var fieldName = _ref[0], n = _ref[1]; + print("[" + fieldName + "] =>", indent + 2); + printNodes(n, indent + 4); + }); + } + } + module2.exports = { + Node, + Root, + NodeList, + Value, + Literal, + Symbol: Symbol2, + Group, + Array: ArrayNode, + Pair, + Dict, + Output, + Capture, + TemplateData, + If, + IfAsync, + InlineIf, + For, + AsyncEach, + AsyncAll, + Macro, + Caller, + Import, + FromImport, + FunCall, + Filter, + FilterAsync, + KeywordArgs, + Block, + Super, + Extends, + Include, + Set: Set2, + Switch, + Case, + LookupVal, + BinOp, + In, + Is, + Or, + And, + Not, + Add, + Concat, + Sub, + Mul, + Div, + FloorDiv, + Mod, + Pow, + Neg, + Pos, + Compare, + CompareOperand, + CallExtension, + CallExtensionAsync, + printNodes + }; +}); + +// ../../node_modules/nunjucks/src/parser.js +var require_parser = __commonJS((exports2, module2) => { + "use strict"; + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var lexer2 = require_lexer(); + var nodes2 = require_nodes(); + var Obj = require_object().Obj; + var lib2 = require_lib(); + var Parser = /* @__PURE__ */ function(_Obj) { + _inheritsLoose(Parser2, _Obj); + function Parser2() { + return _Obj.apply(this, arguments) || this; + } + var _proto = Parser2.prototype; + _proto.init = function init(tokens) { + this.tokens = tokens; + this.peeked = null; + this.breakOnBlocks = null; + this.dropLeadingWhitespace = false; + this.extensions = []; + }; + _proto.nextToken = function nextToken(withWhitespace) { + var tok; + if (this.peeked) { + if (!withWhitespace && this.peeked.type === lexer2.TOKEN_WHITESPACE) { + this.peeked = null; + } else { + tok = this.peeked; + this.peeked = null; + return tok; + } + } + tok = this.tokens.nextToken(); + if (!withWhitespace) { + while (tok && tok.type === lexer2.TOKEN_WHITESPACE) { + tok = this.tokens.nextToken(); + } + } + return tok; + }; + _proto.peekToken = function peekToken() { + this.peeked = this.peeked || this.nextToken(); + return this.peeked; + }; + _proto.pushToken = function pushToken(tok) { + if (this.peeked) { + throw new Error("pushToken: can only push one token on between reads"); + } + this.peeked = tok; + }; + _proto.error = function error(msg, lineno, colno) { + if (lineno === void 0 || colno === void 0) { + var tok = this.peekToken() || {}; + lineno = tok.lineno; + colno = tok.colno; + } + if (lineno !== void 0) { + lineno += 1; + } + if (colno !== void 0) { + colno += 1; + } + return new lib2.TemplateError(msg, lineno, colno); + }; + _proto.fail = function fail(msg, lineno, colno) { + throw this.error(msg, lineno, colno); + }; + _proto.skip = function skip(type) { + var tok = this.nextToken(); + if (!tok || tok.type !== type) { + this.pushToken(tok); + return false; + } + return true; + }; + _proto.expect = function expect(type) { + var tok = this.nextToken(); + if (tok.type !== type) { + this.fail("expected " + type + ", got " + tok.type, tok.lineno, tok.colno); + } + return tok; + }; + _proto.skipValue = function skipValue(type, val) { + var tok = this.nextToken(); + if (!tok || tok.type !== type || tok.value !== val) { + this.pushToken(tok); + return false; + } + return true; + }; + _proto.skipSymbol = function skipSymbol(val) { + return this.skipValue(lexer2.TOKEN_SYMBOL, val); + }; + _proto.advanceAfterBlockEnd = function advanceAfterBlockEnd(name) { + var tok; + if (!name) { + tok = this.peekToken(); + if (!tok) { + this.fail("unexpected end of file"); + } + if (tok.type !== lexer2.TOKEN_SYMBOL) { + this.fail("advanceAfterBlockEnd: expected symbol token or explicit name to be passed"); + } + name = this.nextToken().value; + } + tok = this.nextToken(); + if (tok && tok.type === lexer2.TOKEN_BLOCK_END) { + if (tok.value.charAt(0) === "-") { + this.dropLeadingWhitespace = true; + } + } else { + this.fail("expected block end in " + name + " statement"); + } + return tok; + }; + _proto.advanceAfterVariableEnd = function advanceAfterVariableEnd() { + var tok = this.nextToken(); + if (tok && tok.type === lexer2.TOKEN_VARIABLE_END) { + this.dropLeadingWhitespace = tok.value.charAt(tok.value.length - this.tokens.tags.VARIABLE_END.length - 1) === "-"; + } else { + this.pushToken(tok); + this.fail("expected variable end"); + } + }; + _proto.parseFor = function parseFor() { + var forTok = this.peekToken(); + var node; + var endBlock; + if (this.skipSymbol("for")) { + node = new nodes2.For(forTok.lineno, forTok.colno); + endBlock = "endfor"; + } else if (this.skipSymbol("asyncEach")) { + node = new nodes2.AsyncEach(forTok.lineno, forTok.colno); + endBlock = "endeach"; + } else if (this.skipSymbol("asyncAll")) { + node = new nodes2.AsyncAll(forTok.lineno, forTok.colno); + endBlock = "endall"; + } else { + this.fail("parseFor: expected for{Async}", forTok.lineno, forTok.colno); + } + node.name = this.parsePrimary(); + if (!(node.name instanceof nodes2.Symbol)) { + this.fail("parseFor: variable name expected for loop"); + } + var type = this.peekToken().type; + if (type === lexer2.TOKEN_COMMA) { + var key = node.name; + node.name = new nodes2.Array(key.lineno, key.colno); + node.name.addChild(key); + while (this.skip(lexer2.TOKEN_COMMA)) { + var prim = this.parsePrimary(); + node.name.addChild(prim); + } + } + if (!this.skipSymbol("in")) { + this.fail('parseFor: expected "in" keyword for loop', forTok.lineno, forTok.colno); + } + node.arr = this.parseExpression(); + this.advanceAfterBlockEnd(forTok.value); + node.body = this.parseUntilBlocks(endBlock, "else"); + if (this.skipSymbol("else")) { + this.advanceAfterBlockEnd("else"); + node.else_ = this.parseUntilBlocks(endBlock); + } + this.advanceAfterBlockEnd(); + return node; + }; + _proto.parseMacro = function parseMacro() { + var macroTok = this.peekToken(); + if (!this.skipSymbol("macro")) { + this.fail("expected macro"); + } + var name = this.parsePrimary(true); + var args = this.parseSignature(); + var node = new nodes2.Macro(macroTok.lineno, macroTok.colno, name, args); + this.advanceAfterBlockEnd(macroTok.value); + node.body = this.parseUntilBlocks("endmacro"); + this.advanceAfterBlockEnd(); + return node; + }; + _proto.parseCall = function parseCall() { + var callTok = this.peekToken(); + if (!this.skipSymbol("call")) { + this.fail("expected call"); + } + var callerArgs = this.parseSignature(true) || new nodes2.NodeList(); + var macroCall = this.parsePrimary(); + this.advanceAfterBlockEnd(callTok.value); + var body = this.parseUntilBlocks("endcall"); + this.advanceAfterBlockEnd(); + var callerName = new nodes2.Symbol(callTok.lineno, callTok.colno, "caller"); + var callerNode = new nodes2.Caller(callTok.lineno, callTok.colno, callerName, callerArgs, body); + var args = macroCall.args.children; + if (!(args[args.length - 1] instanceof nodes2.KeywordArgs)) { + args.push(new nodes2.KeywordArgs()); + } + var kwargs = args[args.length - 1]; + kwargs.addChild(new nodes2.Pair(callTok.lineno, callTok.colno, callerName, callerNode)); + return new nodes2.Output(callTok.lineno, callTok.colno, [macroCall]); + }; + _proto.parseWithContext = function parseWithContext() { + var tok = this.peekToken(); + var withContext = null; + if (this.skipSymbol("with")) { + withContext = true; + } else if (this.skipSymbol("without")) { + withContext = false; + } + if (withContext !== null) { + if (!this.skipSymbol("context")) { + this.fail("parseFrom: expected context after with/without", tok.lineno, tok.colno); + } + } + return withContext; + }; + _proto.parseImport = function parseImport() { + var importTok = this.peekToken(); + if (!this.skipSymbol("import")) { + this.fail("parseImport: expected import", importTok.lineno, importTok.colno); + } + var template = this.parseExpression(); + if (!this.skipSymbol("as")) { + this.fail('parseImport: expected "as" keyword', importTok.lineno, importTok.colno); + } + var target = this.parseExpression(); + var withContext = this.parseWithContext(); + var node = new nodes2.Import(importTok.lineno, importTok.colno, template, target, withContext); + this.advanceAfterBlockEnd(importTok.value); + return node; + }; + _proto.parseFrom = function parseFrom() { + var fromTok = this.peekToken(); + if (!this.skipSymbol("from")) { + this.fail("parseFrom: expected from"); + } + var template = this.parseExpression(); + if (!this.skipSymbol("import")) { + this.fail("parseFrom: expected import", fromTok.lineno, fromTok.colno); + } + var names = new nodes2.NodeList(); + var withContext; + while (1) { + var nextTok = this.peekToken(); + if (nextTok.type === lexer2.TOKEN_BLOCK_END) { + if (!names.children.length) { + this.fail("parseFrom: Expected at least one import name", fromTok.lineno, fromTok.colno); + } + if (nextTok.value.charAt(0) === "-") { + this.dropLeadingWhitespace = true; + } + this.nextToken(); + break; + } + if (names.children.length > 0 && !this.skip(lexer2.TOKEN_COMMA)) { + this.fail("parseFrom: expected comma", fromTok.lineno, fromTok.colno); + } + var name = this.parsePrimary(); + if (name.value.charAt(0) === "_") { + this.fail("parseFrom: names starting with an underscore cannot be imported", name.lineno, name.colno); + } + if (this.skipSymbol("as")) { + var alias = this.parsePrimary(); + names.addChild(new nodes2.Pair(name.lineno, name.colno, name, alias)); + } else { + names.addChild(name); + } + withContext = this.parseWithContext(); + } + return new nodes2.FromImport(fromTok.lineno, fromTok.colno, template, names, withContext); + }; + _proto.parseBlock = function parseBlock() { + var tag = this.peekToken(); + if (!this.skipSymbol("block")) { + this.fail("parseBlock: expected block", tag.lineno, tag.colno); + } + var node = new nodes2.Block(tag.lineno, tag.colno); + node.name = this.parsePrimary(); + if (!(node.name instanceof nodes2.Symbol)) { + this.fail("parseBlock: variable name expected", tag.lineno, tag.colno); + } + this.advanceAfterBlockEnd(tag.value); + node.body = this.parseUntilBlocks("endblock"); + this.skipSymbol("endblock"); + this.skipSymbol(node.name.value); + var tok = this.peekToken(); + if (!tok) { + this.fail("parseBlock: expected endblock, got end of file"); + } + this.advanceAfterBlockEnd(tok.value); + return node; + }; + _proto.parseExtends = function parseExtends() { + var tagName = "extends"; + var tag = this.peekToken(); + if (!this.skipSymbol(tagName)) { + this.fail("parseTemplateRef: expected " + tagName); + } + var node = new nodes2.Extends(tag.lineno, tag.colno); + node.template = this.parseExpression(); + this.advanceAfterBlockEnd(tag.value); + return node; + }; + _proto.parseInclude = function parseInclude() { + var tagName = "include"; + var tag = this.peekToken(); + if (!this.skipSymbol(tagName)) { + this.fail("parseInclude: expected " + tagName); + } + var node = new nodes2.Include(tag.lineno, tag.colno); + node.template = this.parseExpression(); + if (this.skipSymbol("ignore") && this.skipSymbol("missing")) { + node.ignoreMissing = true; + } + this.advanceAfterBlockEnd(tag.value); + return node; + }; + _proto.parseIf = function parseIf() { + var tag = this.peekToken(); + var node; + if (this.skipSymbol("if") || this.skipSymbol("elif") || this.skipSymbol("elseif")) { + node = new nodes2.If(tag.lineno, tag.colno); + } else if (this.skipSymbol("ifAsync")) { + node = new nodes2.IfAsync(tag.lineno, tag.colno); + } else { + this.fail("parseIf: expected if, elif, or elseif", tag.lineno, tag.colno); + } + node.cond = this.parseExpression(); + this.advanceAfterBlockEnd(tag.value); + node.body = this.parseUntilBlocks("elif", "elseif", "else", "endif"); + var tok = this.peekToken(); + switch (tok && tok.value) { + case "elseif": + case "elif": + node.else_ = this.parseIf(); + break; + case "else": + this.advanceAfterBlockEnd(); + node.else_ = this.parseUntilBlocks("endif"); + this.advanceAfterBlockEnd(); + break; + case "endif": + node.else_ = null; + this.advanceAfterBlockEnd(); + break; + default: + this.fail("parseIf: expected elif, else, or endif, got end of file"); + } + return node; + }; + _proto.parseSet = function parseSet() { + var tag = this.peekToken(); + if (!this.skipSymbol("set")) { + this.fail("parseSet: expected set", tag.lineno, tag.colno); + } + var node = new nodes2.Set(tag.lineno, tag.colno, []); + var target; + while (target = this.parsePrimary()) { + node.targets.push(target); + if (!this.skip(lexer2.TOKEN_COMMA)) { + break; + } + } + if (!this.skipValue(lexer2.TOKEN_OPERATOR, "=")) { + if (!this.skip(lexer2.TOKEN_BLOCK_END)) { + this.fail("parseSet: expected = or block end in set tag", tag.lineno, tag.colno); + } else { + node.body = new nodes2.Capture(tag.lineno, tag.colno, this.parseUntilBlocks("endset")); + node.value = null; + this.advanceAfterBlockEnd(); + } + } else { + node.value = this.parseExpression(); + this.advanceAfterBlockEnd(tag.value); + } + return node; + }; + _proto.parseSwitch = function parseSwitch() { + var switchStart = "switch"; + var switchEnd = "endswitch"; + var caseStart = "case"; + var caseDefault = "default"; + var tag = this.peekToken(); + if (!this.skipSymbol(switchStart) && !this.skipSymbol(caseStart) && !this.skipSymbol(caseDefault)) { + this.fail('parseSwitch: expected "switch," "case" or "default"', tag.lineno, tag.colno); + } + var expr = this.parseExpression(); + this.advanceAfterBlockEnd(switchStart); + this.parseUntilBlocks(caseStart, caseDefault, switchEnd); + var tok = this.peekToken(); + var cases = []; + var defaultCase; + do { + this.skipSymbol(caseStart); + var cond = this.parseExpression(); + this.advanceAfterBlockEnd(switchStart); + var body = this.parseUntilBlocks(caseStart, caseDefault, switchEnd); + cases.push(new nodes2.Case(tok.line, tok.col, cond, body)); + tok = this.peekToken(); + } while (tok && tok.value === caseStart); + switch (tok.value) { + case caseDefault: + this.advanceAfterBlockEnd(); + defaultCase = this.parseUntilBlocks(switchEnd); + this.advanceAfterBlockEnd(); + break; + case switchEnd: + this.advanceAfterBlockEnd(); + break; + default: + this.fail('parseSwitch: expected "case," "default" or "endswitch," got EOF.'); + } + return new nodes2.Switch(tag.lineno, tag.colno, expr, cases, defaultCase); + }; + _proto.parseStatement = function parseStatement() { + var tok = this.peekToken(); + var node; + if (tok.type !== lexer2.TOKEN_SYMBOL) { + this.fail("tag name expected", tok.lineno, tok.colno); + } + if (this.breakOnBlocks && lib2.indexOf(this.breakOnBlocks, tok.value) !== -1) { + return null; + } + switch (tok.value) { + case "raw": + return this.parseRaw(); + case "verbatim": + return this.parseRaw("verbatim"); + case "if": + case "ifAsync": + return this.parseIf(); + case "for": + case "asyncEach": + case "asyncAll": + return this.parseFor(); + case "block": + return this.parseBlock(); + case "extends": + return this.parseExtends(); + case "include": + return this.parseInclude(); + case "set": + return this.parseSet(); + case "macro": + return this.parseMacro(); + case "call": + return this.parseCall(); + case "import": + return this.parseImport(); + case "from": + return this.parseFrom(); + case "filter": + return this.parseFilterStatement(); + case "switch": + return this.parseSwitch(); + default: + if (this.extensions.length) { + for (var i = 0; i < this.extensions.length; i++) { + var ext = this.extensions[i]; + if (lib2.indexOf(ext.tags || [], tok.value) !== -1) { + return ext.parse(this, nodes2, lexer2); + } + } + } + this.fail("unknown block tag: " + tok.value, tok.lineno, tok.colno); + } + return node; + }; + _proto.parseRaw = function parseRaw(tagName) { + tagName = tagName || "raw"; + var endTagName = "end" + tagName; + var rawBlockRegex = new RegExp("([\\s\\S]*?){%\\s*(" + tagName + "|" + endTagName + ")\\s*(?=%})%}"); + var rawLevel = 1; + var str = ""; + var matches = null; + var begun = this.advanceAfterBlockEnd(); + while ((matches = this.tokens._extractRegex(rawBlockRegex)) && rawLevel > 0) { + var all = matches[0]; + var pre = matches[1]; + var blockName = matches[2]; + if (blockName === tagName) { + rawLevel += 1; + } else if (blockName === endTagName) { + rawLevel -= 1; + } + if (rawLevel === 0) { + str += pre; + this.tokens.backN(all.length - pre.length); + } else { + str += all; + } + } + return new nodes2.Output(begun.lineno, begun.colno, [new nodes2.TemplateData(begun.lineno, begun.colno, str)]); + }; + _proto.parsePostfix = function parsePostfix(node) { + var lookup; + var tok = this.peekToken(); + while (tok) { + if (tok.type === lexer2.TOKEN_LEFT_PAREN) { + node = new nodes2.FunCall(tok.lineno, tok.colno, node, this.parseSignature()); + } else if (tok.type === lexer2.TOKEN_LEFT_BRACKET) { + lookup = this.parseAggregate(); + if (lookup.children.length > 1) { + this.fail("invalid index"); + } + node = new nodes2.LookupVal(tok.lineno, tok.colno, node, lookup.children[0]); + } else if (tok.type === lexer2.TOKEN_OPERATOR && tok.value === ".") { + this.nextToken(); + var val = this.nextToken(); + if (val.type !== lexer2.TOKEN_SYMBOL) { + this.fail("expected name as lookup value, got " + val.value, val.lineno, val.colno); + } + lookup = new nodes2.Literal(val.lineno, val.colno, val.value); + node = new nodes2.LookupVal(tok.lineno, tok.colno, node, lookup); + } else { + break; + } + tok = this.peekToken(); + } + return node; + }; + _proto.parseExpression = function parseExpression() { + var node = this.parseInlineIf(); + return node; + }; + _proto.parseInlineIf = function parseInlineIf() { + var node = this.parseOr(); + if (this.skipSymbol("if")) { + var condNode = this.parseOr(); + var bodyNode = node; + node = new nodes2.InlineIf(node.lineno, node.colno); + node.body = bodyNode; + node.cond = condNode; + if (this.skipSymbol("else")) { + node.else_ = this.parseOr(); + } else { + node.else_ = null; + } + } + return node; + }; + _proto.parseOr = function parseOr() { + var node = this.parseAnd(); + while (this.skipSymbol("or")) { + var node2 = this.parseAnd(); + node = new nodes2.Or(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseAnd = function parseAnd() { + var node = this.parseNot(); + while (this.skipSymbol("and")) { + var node2 = this.parseNot(); + node = new nodes2.And(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseNot = function parseNot() { + var tok = this.peekToken(); + if (this.skipSymbol("not")) { + return new nodes2.Not(tok.lineno, tok.colno, this.parseNot()); + } + return this.parseIn(); + }; + _proto.parseIn = function parseIn() { + var node = this.parseIs(); + while (1) { + var tok = this.nextToken(); + if (!tok) { + break; + } + var invert = tok.type === lexer2.TOKEN_SYMBOL && tok.value === "not"; + if (!invert) { + this.pushToken(tok); + } + if (this.skipSymbol("in")) { + var node2 = this.parseIs(); + node = new nodes2.In(node.lineno, node.colno, node, node2); + if (invert) { + node = new nodes2.Not(node.lineno, node.colno, node); + } + } else { + if (invert) { + this.pushToken(tok); + } + break; + } + } + return node; + }; + _proto.parseIs = function parseIs() { + var node = this.parseCompare(); + if (this.skipSymbol("is")) { + var not = this.skipSymbol("not"); + var node2 = this.parseCompare(); + node = new nodes2.Is(node.lineno, node.colno, node, node2); + if (not) { + node = new nodes2.Not(node.lineno, node.colno, node); + } + } + return node; + }; + _proto.parseCompare = function parseCompare() { + var compareOps = ["==", "===", "!=", "!==", "<", ">", "<=", ">="]; + var expr = this.parseConcat(); + var ops = []; + while (1) { + var tok = this.nextToken(); + if (!tok) { + break; + } else if (compareOps.indexOf(tok.value) !== -1) { + ops.push(new nodes2.CompareOperand(tok.lineno, tok.colno, this.parseConcat(), tok.value)); + } else { + this.pushToken(tok); + break; + } + } + if (ops.length) { + return new nodes2.Compare(ops[0].lineno, ops[0].colno, expr, ops); + } else { + return expr; + } + }; + _proto.parseConcat = function parseConcat() { + var node = this.parseAdd(); + while (this.skipValue(lexer2.TOKEN_TILDE, "~")) { + var node2 = this.parseAdd(); + node = new nodes2.Concat(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseAdd = function parseAdd() { + var node = this.parseSub(); + while (this.skipValue(lexer2.TOKEN_OPERATOR, "+")) { + var node2 = this.parseSub(); + node = new nodes2.Add(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseSub = function parseSub() { + var node = this.parseMul(); + while (this.skipValue(lexer2.TOKEN_OPERATOR, "-")) { + var node2 = this.parseMul(); + node = new nodes2.Sub(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseMul = function parseMul() { + var node = this.parseDiv(); + while (this.skipValue(lexer2.TOKEN_OPERATOR, "*")) { + var node2 = this.parseDiv(); + node = new nodes2.Mul(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseDiv = function parseDiv() { + var node = this.parseFloorDiv(); + while (this.skipValue(lexer2.TOKEN_OPERATOR, "/")) { + var node2 = this.parseFloorDiv(); + node = new nodes2.Div(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseFloorDiv = function parseFloorDiv() { + var node = this.parseMod(); + while (this.skipValue(lexer2.TOKEN_OPERATOR, "//")) { + var node2 = this.parseMod(); + node = new nodes2.FloorDiv(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseMod = function parseMod() { + var node = this.parsePow(); + while (this.skipValue(lexer2.TOKEN_OPERATOR, "%")) { + var node2 = this.parsePow(); + node = new nodes2.Mod(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parsePow = function parsePow() { + var node = this.parseUnary(); + while (this.skipValue(lexer2.TOKEN_OPERATOR, "**")) { + var node2 = this.parseUnary(); + node = new nodes2.Pow(node.lineno, node.colno, node, node2); + } + return node; + }; + _proto.parseUnary = function parseUnary(noFilters) { + var tok = this.peekToken(); + var node; + if (this.skipValue(lexer2.TOKEN_OPERATOR, "-")) { + node = new nodes2.Neg(tok.lineno, tok.colno, this.parseUnary(true)); + } else if (this.skipValue(lexer2.TOKEN_OPERATOR, "+")) { + node = new nodes2.Pos(tok.lineno, tok.colno, this.parseUnary(true)); + } else { + node = this.parsePrimary(); + } + if (!noFilters) { + node = this.parseFilter(node); + } + return node; + }; + _proto.parsePrimary = function parsePrimary(noPostfix) { + var tok = this.nextToken(); + var val; + var node = null; + if (!tok) { + this.fail("expected expression, got end of file"); + } else if (tok.type === lexer2.TOKEN_STRING) { + val = tok.value; + } else if (tok.type === lexer2.TOKEN_INT) { + val = parseInt(tok.value, 10); + } else if (tok.type === lexer2.TOKEN_FLOAT) { + val = parseFloat(tok.value); + } else if (tok.type === lexer2.TOKEN_BOOLEAN) { + if (tok.value === "true") { + val = true; + } else if (tok.value === "false") { + val = false; + } else { + this.fail("invalid boolean: " + tok.value, tok.lineno, tok.colno); + } + } else if (tok.type === lexer2.TOKEN_NONE) { + val = null; + } else if (tok.type === lexer2.TOKEN_REGEX) { + val = new RegExp(tok.value.body, tok.value.flags); + } + if (val !== void 0) { + node = new nodes2.Literal(tok.lineno, tok.colno, val); + } else if (tok.type === lexer2.TOKEN_SYMBOL) { + node = new nodes2.Symbol(tok.lineno, tok.colno, tok.value); + } else { + this.pushToken(tok); + node = this.parseAggregate(); + } + if (!noPostfix) { + node = this.parsePostfix(node); + } + if (node) { + return node; + } else { + throw this.error("unexpected token: " + tok.value, tok.lineno, tok.colno); + } + }; + _proto.parseFilterName = function parseFilterName() { + var tok = this.expect(lexer2.TOKEN_SYMBOL); + var name = tok.value; + while (this.skipValue(lexer2.TOKEN_OPERATOR, ".")) { + name += "." + this.expect(lexer2.TOKEN_SYMBOL).value; + } + return new nodes2.Symbol(tok.lineno, tok.colno, name); + }; + _proto.parseFilterArgs = function parseFilterArgs(node) { + if (this.peekToken().type === lexer2.TOKEN_LEFT_PAREN) { + var call = this.parsePostfix(node); + return call.args.children; + } + return []; + }; + _proto.parseFilter = function parseFilter(node) { + while (this.skip(lexer2.TOKEN_PIPE)) { + var name = this.parseFilterName(); + node = new nodes2.Filter(name.lineno, name.colno, name, new nodes2.NodeList(name.lineno, name.colno, [node].concat(this.parseFilterArgs(node)))); + } + return node; + }; + _proto.parseFilterStatement = function parseFilterStatement() { + var filterTok = this.peekToken(); + if (!this.skipSymbol("filter")) { + this.fail("parseFilterStatement: expected filter"); + } + var name = this.parseFilterName(); + var args = this.parseFilterArgs(name); + this.advanceAfterBlockEnd(filterTok.value); + var body = new nodes2.Capture(name.lineno, name.colno, this.parseUntilBlocks("endfilter")); + this.advanceAfterBlockEnd(); + var node = new nodes2.Filter(name.lineno, name.colno, name, new nodes2.NodeList(name.lineno, name.colno, [body].concat(args))); + return new nodes2.Output(name.lineno, name.colno, [node]); + }; + _proto.parseAggregate = function parseAggregate() { + var tok = this.nextToken(); + var node; + switch (tok.type) { + case lexer2.TOKEN_LEFT_PAREN: + node = new nodes2.Group(tok.lineno, tok.colno); + break; + case lexer2.TOKEN_LEFT_BRACKET: + node = new nodes2.Array(tok.lineno, tok.colno); + break; + case lexer2.TOKEN_LEFT_CURLY: + node = new nodes2.Dict(tok.lineno, tok.colno); + break; + default: + return null; + } + while (1) { + var type = this.peekToken().type; + if (type === lexer2.TOKEN_RIGHT_PAREN || type === lexer2.TOKEN_RIGHT_BRACKET || type === lexer2.TOKEN_RIGHT_CURLY) { + this.nextToken(); + break; + } + if (node.children.length > 0) { + if (!this.skip(lexer2.TOKEN_COMMA)) { + this.fail("parseAggregate: expected comma after expression", tok.lineno, tok.colno); + } + } + if (node instanceof nodes2.Dict) { + var key = this.parsePrimary(); + if (!this.skip(lexer2.TOKEN_COLON)) { + this.fail("parseAggregate: expected colon after dict key", tok.lineno, tok.colno); + } + var value = this.parseExpression(); + node.addChild(new nodes2.Pair(key.lineno, key.colno, key, value)); + } else { + var expr = this.parseExpression(); + node.addChild(expr); + } + } + return node; + }; + _proto.parseSignature = function parseSignature(tolerant, noParens) { + var tok = this.peekToken(); + if (!noParens && tok.type !== lexer2.TOKEN_LEFT_PAREN) { + if (tolerant) { + return null; + } else { + this.fail("expected arguments", tok.lineno, tok.colno); + } + } + if (tok.type === lexer2.TOKEN_LEFT_PAREN) { + tok = this.nextToken(); + } + var args = new nodes2.NodeList(tok.lineno, tok.colno); + var kwargs = new nodes2.KeywordArgs(tok.lineno, tok.colno); + var checkComma = false; + while (1) { + tok = this.peekToken(); + if (!noParens && tok.type === lexer2.TOKEN_RIGHT_PAREN) { + this.nextToken(); + break; + } else if (noParens && tok.type === lexer2.TOKEN_BLOCK_END) { + break; + } + if (checkComma && !this.skip(lexer2.TOKEN_COMMA)) { + this.fail("parseSignature: expected comma after expression", tok.lineno, tok.colno); + } else { + var arg = this.parseExpression(); + if (this.skipValue(lexer2.TOKEN_OPERATOR, "=")) { + kwargs.addChild(new nodes2.Pair(arg.lineno, arg.colno, arg, this.parseExpression())); + } else { + args.addChild(arg); + } + } + checkComma = true; + } + if (kwargs.children.length) { + args.addChild(kwargs); + } + return args; + }; + _proto.parseUntilBlocks = function parseUntilBlocks() { + var prev = this.breakOnBlocks; + for (var _len = arguments.length, blockNames = new Array(_len), _key = 0; _key < _len; _key++) { + blockNames[_key] = arguments[_key]; + } + this.breakOnBlocks = blockNames; + var ret = this.parse(); + this.breakOnBlocks = prev; + return ret; + }; + _proto.parseNodes = function parseNodes() { + var tok; + var buf = []; + while (tok = this.nextToken()) { + if (tok.type === lexer2.TOKEN_DATA) { + var data = tok.value; + var nextToken = this.peekToken(); + var nextVal = nextToken && nextToken.value; + if (this.dropLeadingWhitespace) { + data = data.replace(/^\s*/, ""); + this.dropLeadingWhitespace = false; + } + if (nextToken && (nextToken.type === lexer2.TOKEN_BLOCK_START && nextVal.charAt(nextVal.length - 1) === "-" || nextToken.type === lexer2.TOKEN_VARIABLE_START && nextVal.charAt(this.tokens.tags.VARIABLE_START.length) === "-" || nextToken.type === lexer2.TOKEN_COMMENT && nextVal.charAt(this.tokens.tags.COMMENT_START.length) === "-")) { + data = data.replace(/\s*$/, ""); + } + buf.push(new nodes2.Output(tok.lineno, tok.colno, [new nodes2.TemplateData(tok.lineno, tok.colno, data)])); + } else if (tok.type === lexer2.TOKEN_BLOCK_START) { + this.dropLeadingWhitespace = false; + var n = this.parseStatement(); + if (!n) { + break; + } + buf.push(n); + } else if (tok.type === lexer2.TOKEN_VARIABLE_START) { + var e2 = this.parseExpression(); + this.dropLeadingWhitespace = false; + this.advanceAfterVariableEnd(); + buf.push(new nodes2.Output(tok.lineno, tok.colno, [e2])); + } else if (tok.type === lexer2.TOKEN_COMMENT) { + this.dropLeadingWhitespace = tok.value.charAt(tok.value.length - this.tokens.tags.COMMENT_END.length - 1) === "-"; + } else { + this.fail("Unexpected token at top-level: " + tok.type, tok.lineno, tok.colno); + } + } + return buf; + }; + _proto.parse = function parse() { + return new nodes2.NodeList(0, 0, this.parseNodes()); + }; + _proto.parseAsRoot = function parseAsRoot() { + return new nodes2.Root(0, 0, this.parseNodes()); + }; + return Parser2; + }(Obj); + module2.exports = { + parse: function parse(src, extensions, opts) { + var p = new Parser(lexer2.lex(src, opts)); + if (extensions !== void 0) { + p.extensions = extensions; + } + return p.parseAsRoot(); + }, + Parser + }; +}); + +// ../../node_modules/nunjucks/src/transformer.js +var require_transformer = __commonJS((exports2, module2) => { + "use strict"; + var nodes2 = require_nodes(); + var lib2 = require_lib(); + var sym = 0; + function gensym() { + return "hole_" + sym++; + } + function mapCOW(arr, func) { + var res = null; + for (var i = 0; i < arr.length; i++) { + var item = func(arr[i]); + if (item !== arr[i]) { + if (!res) { + res = arr.slice(); + } + res[i] = item; + } + } + return res || arr; + } + function walk(ast, func, depthFirst) { + if (!(ast instanceof nodes2.Node)) { + return ast; + } + if (!depthFirst) { + var astT = func(ast); + if (astT && astT !== ast) { + return astT; + } + } + if (ast instanceof nodes2.NodeList) { + var children = mapCOW(ast.children, function(node) { + return walk(node, func, depthFirst); + }); + if (children !== ast.children) { + ast = new nodes2[ast.typename](ast.lineno, ast.colno, children); + } + } else if (ast instanceof nodes2.CallExtension) { + var args = walk(ast.args, func, depthFirst); + var contentArgs = mapCOW(ast.contentArgs, function(node) { + return walk(node, func, depthFirst); + }); + if (args !== ast.args || contentArgs !== ast.contentArgs) { + ast = new nodes2[ast.typename](ast.extName, ast.prop, args, contentArgs); + } + } else { + var props = ast.fields.map(function(field) { + return ast[field]; + }); + var propsT = mapCOW(props, function(prop) { + return walk(prop, func, depthFirst); + }); + if (propsT !== props) { + ast = new nodes2[ast.typename](ast.lineno, ast.colno); + propsT.forEach(function(prop, i) { + ast[ast.fields[i]] = prop; + }); + } + } + return depthFirst ? func(ast) || ast : ast; + } + function depthWalk(ast, func) { + return walk(ast, func, true); + } + function _liftFilters(node, asyncFilters, prop) { + var children = []; + var walked = depthWalk(prop ? node[prop] : node, function(descNode) { + var symbol; + if (descNode instanceof nodes2.Block) { + return descNode; + } else if (descNode instanceof nodes2.Filter && lib2.indexOf(asyncFilters, descNode.name.value) !== -1 || descNode instanceof nodes2.CallExtensionAsync) { + symbol = new nodes2.Symbol(descNode.lineno, descNode.colno, gensym()); + children.push(new nodes2.FilterAsync(descNode.lineno, descNode.colno, descNode.name, descNode.args, symbol)); + } + return symbol; + }); + if (prop) { + node[prop] = walked; + } else { + node = walked; + } + if (children.length) { + children.push(node); + return new nodes2.NodeList(node.lineno, node.colno, children); + } else { + return node; + } + } + function liftFilters(ast, asyncFilters) { + return depthWalk(ast, function(node) { + if (node instanceof nodes2.Output) { + return _liftFilters(node, asyncFilters); + } else if (node instanceof nodes2.Set) { + return _liftFilters(node, asyncFilters, "value"); + } else if (node instanceof nodes2.For) { + return _liftFilters(node, asyncFilters, "arr"); + } else if (node instanceof nodes2.If) { + return _liftFilters(node, asyncFilters, "cond"); + } else if (node instanceof nodes2.CallExtension) { + return _liftFilters(node, asyncFilters, "args"); + } else { + return void 0; + } + }); + } + function liftSuper(ast) { + return walk(ast, function(blockNode) { + if (!(blockNode instanceof nodes2.Block)) { + return; + } + var hasSuper = false; + var symbol = gensym(); + blockNode.body = walk(blockNode.body, function(node) { + if (node instanceof nodes2.FunCall && node.name.value === "super") { + hasSuper = true; + return new nodes2.Symbol(node.lineno, node.colno, symbol); + } + }); + if (hasSuper) { + blockNode.body.children.unshift(new nodes2.Super(0, 0, blockNode.name, new nodes2.Symbol(0, 0, symbol))); + } + }); + } + function convertStatements(ast) { + return depthWalk(ast, function(node) { + if (!(node instanceof nodes2.If) && !(node instanceof nodes2.For)) { + return void 0; + } + var async = false; + walk(node, function(child) { + if (child instanceof nodes2.FilterAsync || child instanceof nodes2.IfAsync || child instanceof nodes2.AsyncEach || child instanceof nodes2.AsyncAll || child instanceof nodes2.CallExtensionAsync) { + async = true; + return child; + } + return void 0; + }); + if (async) { + if (node instanceof nodes2.If) { + return new nodes2.IfAsync(node.lineno, node.colno, node.cond, node.body, node.else_); + } else if (node instanceof nodes2.For && !(node instanceof nodes2.AsyncAll)) { + return new nodes2.AsyncEach(node.lineno, node.colno, node.arr, node.name, node.body, node.else_); + } + } + return void 0; + }); + } + function cps(ast, asyncFilters) { + return convertStatements(liftSuper(liftFilters(ast, asyncFilters))); + } + function transform(ast, asyncFilters) { + return cps(ast, asyncFilters || []); + } + module2.exports = { + transform + }; +}); + +// ../../node_modules/nunjucks/src/runtime.js +var require_runtime = __commonJS((exports2, module2) => { + "use strict"; + var lib2 = require_lib(); + var arrayFrom = Array.from; + var supportsIterators = typeof Symbol === "function" && Symbol.iterator && typeof arrayFrom === "function"; + var Frame = /* @__PURE__ */ function() { + function Frame2(parent, isolateWrites) { + this.variables = Object.create(null); + this.parent = parent; + this.topLevel = false; + this.isolateWrites = isolateWrites; + } + var _proto = Frame2.prototype; + _proto.set = function set(name, val, resolveUp) { + var parts = name.split("."); + var obj = this.variables; + var frame = this; + if (resolveUp) { + if (frame = this.resolve(parts[0], true)) { + frame.set(name, val); + return; + } + } + for (var i = 0; i < parts.length - 1; i++) { + var id = parts[i]; + if (!obj[id]) { + obj[id] = {}; + } + obj = obj[id]; + } + obj[parts[parts.length - 1]] = val; + }; + _proto.get = function get(name) { + var val = this.variables[name]; + if (val !== void 0) { + return val; + } + return null; + }; + _proto.lookup = function lookup(name) { + var p = this.parent; + var val = this.variables[name]; + if (val !== void 0) { + return val; + } + return p && p.lookup(name); + }; + _proto.resolve = function resolve(name, forWrite) { + var p = forWrite && this.isolateWrites ? void 0 : this.parent; + var val = this.variables[name]; + if (val !== void 0) { + return this; + } + return p && p.resolve(name); + }; + _proto.push = function push(isolateWrites) { + return new Frame2(this, isolateWrites); + }; + _proto.pop = function pop() { + return this.parent; + }; + return Frame2; + }(); + function makeMacro(argNames, kwargNames, func) { + return function macro() { + for (var _len = arguments.length, macroArgs = new Array(_len), _key = 0; _key < _len; _key++) { + macroArgs[_key] = arguments[_key]; + } + var argCount = numArgs(macroArgs); + var args; + var kwargs = getKeywordArgs(macroArgs); + if (argCount > argNames.length) { + args = macroArgs.slice(0, argNames.length); + macroArgs.slice(args.length, argCount).forEach(function(val, i2) { + if (i2 < kwargNames.length) { + kwargs[kwargNames[i2]] = val; + } + }); + args.push(kwargs); + } else if (argCount < argNames.length) { + args = macroArgs.slice(0, argCount); + for (var i = argCount; i < argNames.length; i++) { + var arg = argNames[i]; + args.push(kwargs[arg]); + delete kwargs[arg]; + } + args.push(kwargs); + } else { + args = macroArgs; + } + return func.apply(this, args); + }; + } + function makeKeywordArgs(obj) { + obj.__keywords = true; + return obj; + } + function isKeywordArgs(obj) { + return obj && Object.prototype.hasOwnProperty.call(obj, "__keywords"); + } + function getKeywordArgs(args) { + var len = args.length; + if (len) { + var lastArg = args[len - 1]; + if (isKeywordArgs(lastArg)) { + return lastArg; + } + } + return {}; + } + function numArgs(args) { + var len = args.length; + if (len === 0) { + return 0; + } + var lastArg = args[len - 1]; + if (isKeywordArgs(lastArg)) { + return len - 1; + } else { + return len; + } + } + function SafeString(val) { + if (typeof val !== "string") { + return val; + } + this.val = val; + this.length = val.length; + } + SafeString.prototype = Object.create(String.prototype, { + length: { + writable: true, + configurable: true, + value: 0 + } + }); + SafeString.prototype.valueOf = function valueOf() { + return this.val; + }; + SafeString.prototype.toString = function toString() { + return this.val; + }; + function copySafeness(dest, target) { + if (dest instanceof SafeString) { + return new SafeString(target); + } + return target.toString(); + } + function markSafe(val) { + var type = typeof val; + if (type === "string") { + return new SafeString(val); + } else if (type !== "function") { + return val; + } else { + return function wrapSafe(args) { + var ret = val.apply(this, arguments); + if (typeof ret === "string") { + return new SafeString(ret); + } + return ret; + }; + } + } + function suppressValue(val, autoescape) { + val = val !== void 0 && val !== null ? val : ""; + if (autoescape && !(val instanceof SafeString)) { + val = lib2.escape(val.toString()); + } + return val; + } + function ensureDefined(val, lineno, colno) { + if (val === null || val === void 0) { + throw new lib2.TemplateError("attempted to output null or undefined value", lineno + 1, colno + 1); + } + return val; + } + function memberLookup(obj, val) { + if (obj === void 0 || obj === null) { + return void 0; + } + if (typeof obj[val] === "function") { + return function() { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + return obj[val].apply(obj, args); + }; + } + return obj[val]; + } + function callWrap(obj, name, context, args) { + if (!obj) { + throw new Error("Unable to call `" + name + "`, which is undefined or falsey"); + } else if (typeof obj !== "function") { + throw new Error("Unable to call `" + name + "`, which is not a function"); + } + return obj.apply(context, args); + } + function contextOrFrameLookup(context, frame, name) { + var val = frame.lookup(name); + return val !== void 0 ? val : context.lookup(name); + } + function handleError(error, lineno, colno) { + if (error.lineno) { + return error; + } else { + return new lib2.TemplateError(error, lineno, colno); + } + } + function asyncEach(arr, dimen, iter, cb) { + if (lib2.isArray(arr)) { + var len = arr.length; + lib2.asyncIter(arr, function iterCallback(item, i, next) { + switch (dimen) { + case 1: + iter(item, i, len, next); + break; + case 2: + iter(item[0], item[1], i, len, next); + break; + case 3: + iter(item[0], item[1], item[2], i, len, next); + break; + default: + item.push(i, len, next); + iter.apply(this, item); + } + }, cb); + } else { + lib2.asyncFor(arr, function iterCallback(key, val, i, len2, next) { + iter(key, val, i, len2, next); + }, cb); + } + } + function asyncAll(arr, dimen, func, cb) { + var finished = 0; + var len; + var outputArr; + function done(i2, output) { + finished++; + outputArr[i2] = output; + if (finished === len) { + cb(null, outputArr.join("")); + } + } + if (lib2.isArray(arr)) { + len = arr.length; + outputArr = new Array(len); + if (len === 0) { + cb(null, ""); + } else { + for (var i = 0; i < arr.length; i++) { + var item = arr[i]; + switch (dimen) { + case 1: + func(item, i, len, done); + break; + case 2: + func(item[0], item[1], i, len, done); + break; + case 3: + func(item[0], item[1], item[2], i, len, done); + break; + default: + item.push(i, len, done); + func.apply(this, item); + } + } + } + } else { + var keys = lib2.keys(arr || {}); + len = keys.length; + outputArr = new Array(len); + if (len === 0) { + cb(null, ""); + } else { + for (var _i = 0; _i < keys.length; _i++) { + var k = keys[_i]; + func(k, arr[k], _i, len, done); + } + } + } + } + function fromIterator(arr) { + if (typeof arr !== "object" || arr === null || lib2.isArray(arr)) { + return arr; + } else if (supportsIterators && Symbol.iterator in arr) { + return arrayFrom(arr); + } else { + return arr; + } + } + module2.exports = { + Frame, + makeMacro, + makeKeywordArgs, + numArgs, + suppressValue, + ensureDefined, + memberLookup, + contextOrFrameLookup, + callWrap, + handleError, + isArray: lib2.isArray, + keys: lib2.keys, + SafeString, + copySafeness, + markSafe, + asyncEach, + asyncAll, + inOperator: lib2.inOperator, + fromIterator + }; +}); + +// ../../node_modules/nunjucks/src/compiler.js +var require_compiler = __commonJS((exports2, module2) => { + "use strict"; + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var parser2 = require_parser(); + var transformer = require_transformer(); + var nodes2 = require_nodes(); + var _require2 = require_lib(); + var TemplateError = _require2.TemplateError; + var _require22 = require_runtime(); + var Frame = _require22.Frame; + var _require3 = require_object(); + var Obj = _require3.Obj; + var compareOps = { + "==": "==", + "===": "===", + "!=": "!=", + "!==": "!==", + "<": "<", + ">": ">", + "<=": "<=", + ">=": ">=" + }; + var Compiler = /* @__PURE__ */ function(_Obj) { + _inheritsLoose(Compiler2, _Obj); + function Compiler2() { + return _Obj.apply(this, arguments) || this; + } + var _proto = Compiler2.prototype; + _proto.init = function init(templateName, throwOnUndefined) { + this.templateName = templateName; + this.codebuf = []; + this.lastId = 0; + this.buffer = null; + this.bufferStack = []; + this._scopeClosers = ""; + this.inBlock = false; + this.throwOnUndefined = throwOnUndefined; + }; + _proto.fail = function fail(msg, lineno, colno) { + if (lineno !== void 0) { + lineno += 1; + } + if (colno !== void 0) { + colno += 1; + } + throw new TemplateError(msg, lineno, colno); + }; + _proto._pushBuffer = function _pushBuffer() { + var id = this._tmpid(); + this.bufferStack.push(this.buffer); + this.buffer = id; + this._emit("var " + this.buffer + ' = "";'); + return id; + }; + _proto._popBuffer = function _popBuffer() { + this.buffer = this.bufferStack.pop(); + }; + _proto._emit = function _emit(code) { + this.codebuf.push(code); + }; + _proto._emitLine = function _emitLine(code) { + this._emit(code + "\n"); + }; + _proto._emitLines = function _emitLines() { + var _this = this; + for (var _len = arguments.length, lines = new Array(_len), _key = 0; _key < _len; _key++) { + lines[_key] = arguments[_key]; + } + lines.forEach(function(line) { + return _this._emitLine(line); + }); + }; + _proto._emitFuncBegin = function _emitFuncBegin(node, name) { + this.buffer = "output"; + this._scopeClosers = ""; + this._emitLine("function " + name + "(env, context, frame, runtime, cb) {"); + this._emitLine("var lineno = " + node.lineno + ";"); + this._emitLine("var colno = " + node.colno + ";"); + this._emitLine("var " + this.buffer + ' = "";'); + this._emitLine("try {"); + }; + _proto._emitFuncEnd = function _emitFuncEnd(noReturn) { + if (!noReturn) { + this._emitLine("cb(null, " + this.buffer + ");"); + } + this._closeScopeLevels(); + this._emitLine("} catch (e) {"); + this._emitLine(" cb(runtime.handleError(e, lineno, colno));"); + this._emitLine("}"); + this._emitLine("}"); + this.buffer = null; + }; + _proto._addScopeLevel = function _addScopeLevel() { + this._scopeClosers += "})"; + }; + _proto._closeScopeLevels = function _closeScopeLevels() { + this._emitLine(this._scopeClosers + ";"); + this._scopeClosers = ""; + }; + _proto._withScopedSyntax = function _withScopedSyntax(func) { + var _scopeClosers = this._scopeClosers; + this._scopeClosers = ""; + func.call(this); + this._closeScopeLevels(); + this._scopeClosers = _scopeClosers; + }; + _proto._makeCallback = function _makeCallback(res) { + var err = this._tmpid(); + return "function(" + err + (res ? "," + res : "") + ") {\nif(" + err + ") { cb(" + err + "); return; }"; + }; + _proto._tmpid = function _tmpid() { + this.lastId++; + return "t_" + this.lastId; + }; + _proto._templateName = function _templateName() { + return this.templateName == null ? "undefined" : JSON.stringify(this.templateName); + }; + _proto._compileChildren = function _compileChildren(node, frame) { + var _this2 = this; + node.children.forEach(function(child) { + _this2.compile(child, frame); + }); + }; + _proto._compileAggregate = function _compileAggregate(node, frame, startChar, endChar) { + var _this3 = this; + if (startChar) { + this._emit(startChar); + } + node.children.forEach(function(child, i) { + if (i > 0) { + _this3._emit(","); + } + _this3.compile(child, frame); + }); + if (endChar) { + this._emit(endChar); + } + }; + _proto._compileExpression = function _compileExpression(node, frame) { + this.assertType(node, nodes2.Literal, nodes2.Symbol, nodes2.Group, nodes2.Array, nodes2.Dict, nodes2.FunCall, nodes2.Caller, nodes2.Filter, nodes2.LookupVal, nodes2.Compare, nodes2.InlineIf, nodes2.In, nodes2.Is, nodes2.And, nodes2.Or, nodes2.Not, nodes2.Add, nodes2.Concat, nodes2.Sub, nodes2.Mul, nodes2.Div, nodes2.FloorDiv, nodes2.Mod, nodes2.Pow, nodes2.Neg, nodes2.Pos, nodes2.Compare, nodes2.NodeList); + this.compile(node, frame); + }; + _proto.assertType = function assertType(node) { + for (var _len2 = arguments.length, types = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + types[_key2 - 1] = arguments[_key2]; + } + if (!types.some(function(t) { + return node instanceof t; + })) { + this.fail("assertType: invalid type: " + node.typename, node.lineno, node.colno); + } + }; + _proto.compileCallExtension = function compileCallExtension(node, frame, async) { + var _this4 = this; + var args = node.args; + var contentArgs = node.contentArgs; + var autoescape = typeof node.autoescape === "boolean" ? node.autoescape : true; + if (!async) { + this._emit(this.buffer + " += runtime.suppressValue("); + } + this._emit('env.getExtension("' + node.extName + '")["' + node.prop + '"]('); + this._emit("context"); + if (args || contentArgs) { + this._emit(","); + } + if (args) { + if (!(args instanceof nodes2.NodeList)) { + this.fail("compileCallExtension: arguments must be a NodeList, use `parser.parseSignature`"); + } + args.children.forEach(function(arg, i) { + _this4._compileExpression(arg, frame); + if (i !== args.children.length - 1 || contentArgs.length) { + _this4._emit(","); + } + }); + } + if (contentArgs.length) { + contentArgs.forEach(function(arg, i) { + if (i > 0) { + _this4._emit(","); + } + if (arg) { + _this4._emitLine("function(cb) {"); + _this4._emitLine("if(!cb) { cb = function(err) { if(err) { throw err; }}}"); + var id = _this4._pushBuffer(); + _this4._withScopedSyntax(function() { + _this4.compile(arg, frame); + _this4._emitLine("cb(null, " + id + ");"); + }); + _this4._popBuffer(); + _this4._emitLine("return " + id + ";"); + _this4._emitLine("}"); + } else { + _this4._emit("null"); + } + }); + } + if (async) { + var res = this._tmpid(); + this._emitLine(", " + this._makeCallback(res)); + this._emitLine(this.buffer + " += runtime.suppressValue(" + res + ", " + autoescape + " && env.opts.autoescape);"); + this._addScopeLevel(); + } else { + this._emit(")"); + this._emit(", " + autoescape + " && env.opts.autoescape);\n"); + } + }; + _proto.compileCallExtensionAsync = function compileCallExtensionAsync(node, frame) { + this.compileCallExtension(node, frame, true); + }; + _proto.compileNodeList = function compileNodeList(node, frame) { + this._compileChildren(node, frame); + }; + _proto.compileLiteral = function compileLiteral(node) { + if (typeof node.value === "string") { + var val = node.value.replace(/\\/g, "\\\\"); + val = val.replace(/"/g, '\\"'); + val = val.replace(/\n/g, "\\n"); + val = val.replace(/\r/g, "\\r"); + val = val.replace(/\t/g, "\\t"); + val = val.replace(/\u2028/g, "\\u2028"); + this._emit('"' + val + '"'); + } else if (node.value === null) { + this._emit("null"); + } else { + this._emit(node.value.toString()); + } + }; + _proto.compileSymbol = function compileSymbol(node, frame) { + var name = node.value; + var v = frame.lookup(name); + if (v) { + this._emit(v); + } else { + this._emit('runtime.contextOrFrameLookup(context, frame, "' + name + '")'); + } + }; + _proto.compileGroup = function compileGroup(node, frame) { + this._compileAggregate(node, frame, "(", ")"); + }; + _proto.compileArray = function compileArray(node, frame) { + this._compileAggregate(node, frame, "[", "]"); + }; + _proto.compileDict = function compileDict(node, frame) { + this._compileAggregate(node, frame, "{", "}"); + }; + _proto.compilePair = function compilePair(node, frame) { + var key = node.key; + var val = node.value; + if (key instanceof nodes2.Symbol) { + key = new nodes2.Literal(key.lineno, key.colno, key.value); + } else if (!(key instanceof nodes2.Literal && typeof key.value === "string")) { + this.fail("compilePair: Dict keys must be strings or names", key.lineno, key.colno); + } + this.compile(key, frame); + this._emit(": "); + this._compileExpression(val, frame); + }; + _proto.compileInlineIf = function compileInlineIf(node, frame) { + this._emit("("); + this.compile(node.cond, frame); + this._emit("?"); + this.compile(node.body, frame); + this._emit(":"); + if (node.else_ !== null) { + this.compile(node.else_, frame); + } else { + this._emit('""'); + } + this._emit(")"); + }; + _proto.compileIn = function compileIn(node, frame) { + this._emit("runtime.inOperator("); + this.compile(node.left, frame); + this._emit(","); + this.compile(node.right, frame); + this._emit(")"); + }; + _proto.compileIs = function compileIs(node, frame) { + var right = node.right.name ? node.right.name.value : node.right.value; + this._emit('env.getTest("' + right + '").call(context, '); + this.compile(node.left, frame); + if (node.right.args) { + this._emit(","); + this.compile(node.right.args, frame); + } + this._emit(") === true"); + }; + _proto._binOpEmitter = function _binOpEmitter(node, frame, str) { + this.compile(node.left, frame); + this._emit(str); + this.compile(node.right, frame); + }; + _proto.compileOr = function compileOr(node, frame) { + return this._binOpEmitter(node, frame, " || "); + }; + _proto.compileAnd = function compileAnd(node, frame) { + return this._binOpEmitter(node, frame, " && "); + }; + _proto.compileAdd = function compileAdd(node, frame) { + return this._binOpEmitter(node, frame, " + "); + }; + _proto.compileConcat = function compileConcat(node, frame) { + return this._binOpEmitter(node, frame, ' + "" + '); + }; + _proto.compileSub = function compileSub(node, frame) { + return this._binOpEmitter(node, frame, " - "); + }; + _proto.compileMul = function compileMul(node, frame) { + return this._binOpEmitter(node, frame, " * "); + }; + _proto.compileDiv = function compileDiv(node, frame) { + return this._binOpEmitter(node, frame, " / "); + }; + _proto.compileMod = function compileMod(node, frame) { + return this._binOpEmitter(node, frame, " % "); + }; + _proto.compileNot = function compileNot(node, frame) { + this._emit("!"); + this.compile(node.target, frame); + }; + _proto.compileFloorDiv = function compileFloorDiv(node, frame) { + this._emit("Math.floor("); + this.compile(node.left, frame); + this._emit(" / "); + this.compile(node.right, frame); + this._emit(")"); + }; + _proto.compilePow = function compilePow(node, frame) { + this._emit("Math.pow("); + this.compile(node.left, frame); + this._emit(", "); + this.compile(node.right, frame); + this._emit(")"); + }; + _proto.compileNeg = function compileNeg(node, frame) { + this._emit("-"); + this.compile(node.target, frame); + }; + _proto.compilePos = function compilePos(node, frame) { + this._emit("+"); + this.compile(node.target, frame); + }; + _proto.compileCompare = function compileCompare(node, frame) { + var _this5 = this; + this.compile(node.expr, frame); + node.ops.forEach(function(op) { + _this5._emit(" " + compareOps[op.type] + " "); + _this5.compile(op.expr, frame); + }); + }; + _proto.compileLookupVal = function compileLookupVal(node, frame) { + this._emit("runtime.memberLookup(("); + this._compileExpression(node.target, frame); + this._emit("),"); + this._compileExpression(node.val, frame); + this._emit(")"); + }; + _proto._getNodeName = function _getNodeName(node) { + switch (node.typename) { + case "Symbol": + return node.value; + case "FunCall": + return "the return value of (" + this._getNodeName(node.name) + ")"; + case "LookupVal": + return this._getNodeName(node.target) + '["' + this._getNodeName(node.val) + '"]'; + case "Literal": + return node.value.toString(); + default: + return "--expression--"; + } + }; + _proto.compileFunCall = function compileFunCall(node, frame) { + this._emit("(lineno = " + node.lineno + ", colno = " + node.colno + ", "); + this._emit("runtime.callWrap("); + this._compileExpression(node.name, frame); + this._emit(', "' + this._getNodeName(node.name).replace(/"/g, '\\"') + '", context, '); + this._compileAggregate(node.args, frame, "[", "])"); + this._emit(")"); + }; + _proto.compileFilter = function compileFilter(node, frame) { + var name = node.name; + this.assertType(name, nodes2.Symbol); + this._emit('env.getFilter("' + name.value + '").call(context, '); + this._compileAggregate(node.args, frame); + this._emit(")"); + }; + _proto.compileFilterAsync = function compileFilterAsync(node, frame) { + var name = node.name; + var symbol = node.symbol.value; + this.assertType(name, nodes2.Symbol); + frame.set(symbol, symbol); + this._emit('env.getFilter("' + name.value + '").call(context, '); + this._compileAggregate(node.args, frame); + this._emitLine(", " + this._makeCallback(symbol)); + this._addScopeLevel(); + }; + _proto.compileKeywordArgs = function compileKeywordArgs(node, frame) { + this._emit("runtime.makeKeywordArgs("); + this.compileDict(node, frame); + this._emit(")"); + }; + _proto.compileSet = function compileSet(node, frame) { + var _this6 = this; + var ids = []; + node.targets.forEach(function(target) { + var name = target.value; + var id = frame.lookup(name); + if (id === null || id === void 0) { + id = _this6._tmpid(); + _this6._emitLine("var " + id + ";"); + } + ids.push(id); + }); + if (node.value) { + this._emit(ids.join(" = ") + " = "); + this._compileExpression(node.value, frame); + this._emitLine(";"); + } else { + this._emit(ids.join(" = ") + " = "); + this.compile(node.body, frame); + this._emitLine(";"); + } + node.targets.forEach(function(target, i) { + var id = ids[i]; + var name = target.value; + _this6._emitLine('frame.set("' + name + '", ' + id + ", true);"); + _this6._emitLine("if(frame.topLevel) {"); + _this6._emitLine('context.setVariable("' + name + '", ' + id + ");"); + _this6._emitLine("}"); + if (name.charAt(0) !== "_") { + _this6._emitLine("if(frame.topLevel) {"); + _this6._emitLine('context.addExport("' + name + '", ' + id + ");"); + _this6._emitLine("}"); + } + }); + }; + _proto.compileSwitch = function compileSwitch(node, frame) { + var _this7 = this; + this._emit("switch ("); + this.compile(node.expr, frame); + this._emit(") {"); + node.cases.forEach(function(c, i) { + _this7._emit("case "); + _this7.compile(c.cond, frame); + _this7._emit(": "); + _this7.compile(c.body, frame); + if (c.body.children.length) { + _this7._emitLine("break;"); + } + }); + if (node.default) { + this._emit("default:"); + this.compile(node.default, frame); + } + this._emit("}"); + }; + _proto.compileIf = function compileIf(node, frame, async) { + var _this8 = this; + this._emit("if("); + this._compileExpression(node.cond, frame); + this._emitLine(") {"); + this._withScopedSyntax(function() { + _this8.compile(node.body, frame); + if (async) { + _this8._emit("cb()"); + } + }); + if (node.else_) { + this._emitLine("}\nelse {"); + this._withScopedSyntax(function() { + _this8.compile(node.else_, frame); + if (async) { + _this8._emit("cb()"); + } + }); + } else if (async) { + this._emitLine("}\nelse {"); + this._emit("cb()"); + } + this._emitLine("}"); + }; + _proto.compileIfAsync = function compileIfAsync(node, frame) { + this._emit("(function(cb) {"); + this.compileIf(node, frame, true); + this._emit("})(" + this._makeCallback()); + this._addScopeLevel(); + }; + _proto._emitLoopBindings = function _emitLoopBindings(node, arr, i, len) { + var _this9 = this; + var bindings = [{ + name: "index", + val: i + " + 1" + }, { + name: "index0", + val: i + }, { + name: "revindex", + val: len + " - " + i + }, { + name: "revindex0", + val: len + " - " + i + " - 1" + }, { + name: "first", + val: i + " === 0" + }, { + name: "last", + val: i + " === " + len + " - 1" + }, { + name: "length", + val: len + }]; + bindings.forEach(function(b) { + _this9._emitLine('frame.set("loop.' + b.name + '", ' + b.val + ");"); + }); + }; + _proto.compileFor = function compileFor(node, frame) { + var _this10 = this; + var i = this._tmpid(); + var len = this._tmpid(); + var arr = this._tmpid(); + frame = frame.push(); + this._emitLine("frame = frame.push();"); + this._emit("var " + arr + " = "); + this._compileExpression(node.arr, frame); + this._emitLine(";"); + this._emit("if(" + arr + ") {"); + this._emitLine(arr + " = runtime.fromIterator(" + arr + ");"); + if (node.name instanceof nodes2.Array) { + this._emitLine("var " + i + ";"); + this._emitLine("if(runtime.isArray(" + arr + ")) {"); + this._emitLine("var " + len + " = " + arr + ".length;"); + this._emitLine("for(" + i + "=0; " + i + " < " + arr + ".length; " + i + "++) {"); + node.name.children.forEach(function(child, u) { + var tid = _this10._tmpid(); + _this10._emitLine("var " + tid + " = " + arr + "[" + i + "][" + u + "];"); + _this10._emitLine('frame.set("' + child + '", ' + arr + "[" + i + "][" + u + "]);"); + frame.set(node.name.children[u].value, tid); + }); + this._emitLoopBindings(node, arr, i, len); + this._withScopedSyntax(function() { + _this10.compile(node.body, frame); + }); + this._emitLine("}"); + this._emitLine("} else {"); + var _node$name$children = node.name.children, key = _node$name$children[0], val = _node$name$children[1]; + var k = this._tmpid(); + var v = this._tmpid(); + frame.set(key.value, k); + frame.set(val.value, v); + this._emitLine(i + " = -1;"); + this._emitLine("var " + len + " = runtime.keys(" + arr + ").length;"); + this._emitLine("for(var " + k + " in " + arr + ") {"); + this._emitLine(i + "++;"); + this._emitLine("var " + v + " = " + arr + "[" + k + "];"); + this._emitLine('frame.set("' + key.value + '", ' + k + ");"); + this._emitLine('frame.set("' + val.value + '", ' + v + ");"); + this._emitLoopBindings(node, arr, i, len); + this._withScopedSyntax(function() { + _this10.compile(node.body, frame); + }); + this._emitLine("}"); + this._emitLine("}"); + } else { + var _v = this._tmpid(); + frame.set(node.name.value, _v); + this._emitLine("var " + len + " = " + arr + ".length;"); + this._emitLine("for(var " + i + "=0; " + i + " < " + arr + ".length; " + i + "++) {"); + this._emitLine("var " + _v + " = " + arr + "[" + i + "];"); + this._emitLine('frame.set("' + node.name.value + '", ' + _v + ");"); + this._emitLoopBindings(node, arr, i, len); + this._withScopedSyntax(function() { + _this10.compile(node.body, frame); + }); + this._emitLine("}"); + } + this._emitLine("}"); + if (node.else_) { + this._emitLine("if (!" + len + ") {"); + this.compile(node.else_, frame); + this._emitLine("}"); + } + this._emitLine("frame = frame.pop();"); + }; + _proto._compileAsyncLoop = function _compileAsyncLoop(node, frame, parallel) { + var _this11 = this; + var i = this._tmpid(); + var len = this._tmpid(); + var arr = this._tmpid(); + var asyncMethod = parallel ? "asyncAll" : "asyncEach"; + frame = frame.push(); + this._emitLine("frame = frame.push();"); + this._emit("var " + arr + " = runtime.fromIterator("); + this._compileExpression(node.arr, frame); + this._emitLine(");"); + if (node.name instanceof nodes2.Array) { + var arrayLen = node.name.children.length; + this._emit("runtime." + asyncMethod + "(" + arr + ", " + arrayLen + ", function("); + node.name.children.forEach(function(name) { + _this11._emit(name.value + ","); + }); + this._emit(i + "," + len + ",next) {"); + node.name.children.forEach(function(name) { + var id2 = name.value; + frame.set(id2, id2); + _this11._emitLine('frame.set("' + id2 + '", ' + id2 + ");"); + }); + } else { + var id = node.name.value; + this._emitLine("runtime." + asyncMethod + "(" + arr + ", 1, function(" + id + ", " + i + ", " + len + ",next) {"); + this._emitLine('frame.set("' + id + '", ' + id + ");"); + frame.set(id, id); + } + this._emitLoopBindings(node, arr, i, len); + this._withScopedSyntax(function() { + var buf; + if (parallel) { + buf = _this11._pushBuffer(); + } + _this11.compile(node.body, frame); + _this11._emitLine("next(" + i + (buf ? "," + buf : "") + ");"); + if (parallel) { + _this11._popBuffer(); + } + }); + var output = this._tmpid(); + this._emitLine("}, " + this._makeCallback(output)); + this._addScopeLevel(); + if (parallel) { + this._emitLine(this.buffer + " += " + output + ";"); + } + if (node.else_) { + this._emitLine("if (!" + arr + ".length) {"); + this.compile(node.else_, frame); + this._emitLine("}"); + } + this._emitLine("frame = frame.pop();"); + }; + _proto.compileAsyncEach = function compileAsyncEach(node, frame) { + this._compileAsyncLoop(node, frame); + }; + _proto.compileAsyncAll = function compileAsyncAll(node, frame) { + this._compileAsyncLoop(node, frame, true); + }; + _proto._compileMacro = function _compileMacro(node, frame) { + var _this12 = this; + var args = []; + var kwargs = null; + var funcId = "macro_" + this._tmpid(); + var keepFrame = frame !== void 0; + node.args.children.forEach(function(arg, i) { + if (i === node.args.children.length - 1 && arg instanceof nodes2.Dict) { + kwargs = arg; + } else { + _this12.assertType(arg, nodes2.Symbol); + args.push(arg); + } + }); + var realNames = [].concat(args.map(function(n) { + return "l_" + n.value; + }), ["kwargs"]); + var argNames = args.map(function(n) { + return '"' + n.value + '"'; + }); + var kwargNames = (kwargs && kwargs.children || []).map(function(n) { + return '"' + n.key.value + '"'; + }); + var currFrame; + if (keepFrame) { + currFrame = frame.push(true); + } else { + currFrame = new Frame(); + } + this._emitLines("var " + funcId + " = runtime.makeMacro(", "[" + argNames.join(", ") + "], ", "[" + kwargNames.join(", ") + "], ", "function (" + realNames.join(", ") + ") {", "var callerFrame = frame;", "frame = " + (keepFrame ? "frame.push(true);" : "new runtime.Frame();"), "kwargs = kwargs || {};", 'if (Object.prototype.hasOwnProperty.call(kwargs, "caller")) {', 'frame.set("caller", kwargs.caller); }'); + args.forEach(function(arg) { + _this12._emitLine('frame.set("' + arg.value + '", l_' + arg.value + ");"); + currFrame.set(arg.value, "l_" + arg.value); + }); + if (kwargs) { + kwargs.children.forEach(function(pair) { + var name = pair.key.value; + _this12._emit('frame.set("' + name + '", '); + _this12._emit('Object.prototype.hasOwnProperty.call(kwargs, "' + name + '")'); + _this12._emit(' ? kwargs["' + name + '"] : '); + _this12._compileExpression(pair.value, currFrame); + _this12._emit(");"); + }); + } + var bufferId = this._pushBuffer(); + this._withScopedSyntax(function() { + _this12.compile(node.body, currFrame); + }); + this._emitLine("frame = " + (keepFrame ? "frame.pop();" : "callerFrame;")); + this._emitLine("return new runtime.SafeString(" + bufferId + ");"); + this._emitLine("});"); + this._popBuffer(); + return funcId; + }; + _proto.compileMacro = function compileMacro(node, frame) { + var funcId = this._compileMacro(node); + var name = node.name.value; + frame.set(name, funcId); + if (frame.parent) { + this._emitLine('frame.set("' + name + '", ' + funcId + ");"); + } else { + if (node.name.value.charAt(0) !== "_") { + this._emitLine('context.addExport("' + name + '");'); + } + this._emitLine('context.setVariable("' + name + '", ' + funcId + ");"); + } + }; + _proto.compileCaller = function compileCaller(node, frame) { + this._emit("(function (){"); + var funcId = this._compileMacro(node, frame); + this._emit("return " + funcId + ";})()"); + }; + _proto._compileGetTemplate = function _compileGetTemplate(node, frame, eagerCompile, ignoreMissing) { + var parentTemplateId = this._tmpid(); + var parentName = this._templateName(); + var cb = this._makeCallback(parentTemplateId); + var eagerCompileArg = eagerCompile ? "true" : "false"; + var ignoreMissingArg = ignoreMissing ? "true" : "false"; + this._emit("env.getTemplate("); + this._compileExpression(node.template, frame); + this._emitLine(", " + eagerCompileArg + ", " + parentName + ", " + ignoreMissingArg + ", " + cb); + return parentTemplateId; + }; + _proto.compileImport = function compileImport(node, frame) { + var target = node.target.value; + var id = this._compileGetTemplate(node, frame, false, false); + this._addScopeLevel(); + this._emitLine(id + ".getExported(" + (node.withContext ? "context.getVariables(), frame, " : "") + this._makeCallback(id)); + this._addScopeLevel(); + frame.set(target, id); + if (frame.parent) { + this._emitLine('frame.set("' + target + '", ' + id + ");"); + } else { + this._emitLine('context.setVariable("' + target + '", ' + id + ");"); + } + }; + _proto.compileFromImport = function compileFromImport(node, frame) { + var _this13 = this; + var importedId = this._compileGetTemplate(node, frame, false, false); + this._addScopeLevel(); + this._emitLine(importedId + ".getExported(" + (node.withContext ? "context.getVariables(), frame, " : "") + this._makeCallback(importedId)); + this._addScopeLevel(); + node.names.children.forEach(function(nameNode) { + var name; + var alias; + var id = _this13._tmpid(); + if (nameNode instanceof nodes2.Pair) { + name = nameNode.key.value; + alias = nameNode.value.value; + } else { + name = nameNode.value; + alias = name; + } + _this13._emitLine("if(Object.prototype.hasOwnProperty.call(" + importedId + ', "' + name + '")) {'); + _this13._emitLine("var " + id + " = " + importedId + "." + name + ";"); + _this13._emitLine("} else {"); + _this13._emitLine(`cb(new Error("cannot import '` + name + `'")); return;`); + _this13._emitLine("}"); + frame.set(alias, id); + if (frame.parent) { + _this13._emitLine('frame.set("' + alias + '", ' + id + ");"); + } else { + _this13._emitLine('context.setVariable("' + alias + '", ' + id + ");"); + } + }); + }; + _proto.compileBlock = function compileBlock(node) { + var id = this._tmpid(); + if (!this.inBlock) { + this._emit('(parentTemplate ? function(e, c, f, r, cb) { cb(""); } : '); + } + this._emit('context.getBlock("' + node.name.value + '")'); + if (!this.inBlock) { + this._emit(")"); + } + this._emitLine("(env, context, frame, runtime, " + this._makeCallback(id)); + this._emitLine(this.buffer + " += " + id + ";"); + this._addScopeLevel(); + }; + _proto.compileSuper = function compileSuper(node, frame) { + var name = node.blockName.value; + var id = node.symbol.value; + var cb = this._makeCallback(id); + this._emitLine('context.getSuper(env, "' + name + '", b_' + name + ", frame, runtime, " + cb); + this._emitLine(id + " = runtime.markSafe(" + id + ");"); + this._addScopeLevel(); + frame.set(id, id); + }; + _proto.compileExtends = function compileExtends(node, frame) { + var k = this._tmpid(); + var parentTemplateId = this._compileGetTemplate(node, frame, true, false); + this._emitLine("parentTemplate = " + parentTemplateId); + this._emitLine("for(var " + k + " in parentTemplate.blocks) {"); + this._emitLine("context.addBlock(" + k + ", parentTemplate.blocks[" + k + "]);"); + this._emitLine("}"); + this._addScopeLevel(); + }; + _proto.compileInclude = function compileInclude(node, frame) { + this._emitLine("var tasks = [];"); + this._emitLine("tasks.push("); + this._emitLine("function(callback) {"); + var id = this._compileGetTemplate(node, frame, false, node.ignoreMissing); + this._emitLine("callback(null," + id + ");});"); + this._emitLine("});"); + var id2 = this._tmpid(); + this._emitLine("tasks.push("); + this._emitLine("function(template, callback){"); + this._emitLine("template.render(context.getVariables(), frame, " + this._makeCallback(id2)); + this._emitLine("callback(null," + id2 + ");});"); + this._emitLine("});"); + this._emitLine("tasks.push("); + this._emitLine("function(result, callback){"); + this._emitLine(this.buffer + " += result;"); + this._emitLine("callback(null);"); + this._emitLine("});"); + this._emitLine("env.waterfall(tasks, function(){"); + this._addScopeLevel(); + }; + _proto.compileTemplateData = function compileTemplateData(node, frame) { + this.compileLiteral(node, frame); + }; + _proto.compileCapture = function compileCapture(node, frame) { + var _this14 = this; + var buffer = this.buffer; + this.buffer = "output"; + this._emitLine("(function() {"); + this._emitLine('var output = "";'); + this._withScopedSyntax(function() { + _this14.compile(node.body, frame); + }); + this._emitLine("return output;"); + this._emitLine("})()"); + this.buffer = buffer; + }; + _proto.compileOutput = function compileOutput(node, frame) { + var _this15 = this; + var children = node.children; + children.forEach(function(child) { + if (child instanceof nodes2.TemplateData) { + if (child.value) { + _this15._emit(_this15.buffer + " += "); + _this15.compileLiteral(child, frame); + _this15._emitLine(";"); + } + } else { + _this15._emit(_this15.buffer + " += runtime.suppressValue("); + if (_this15.throwOnUndefined) { + _this15._emit("runtime.ensureDefined("); + } + _this15.compile(child, frame); + if (_this15.throwOnUndefined) { + _this15._emit("," + node.lineno + "," + node.colno + ")"); + } + _this15._emit(", env.opts.autoescape);\n"); + } + }); + }; + _proto.compileRoot = function compileRoot(node, frame) { + var _this16 = this; + if (frame) { + this.fail("compileRoot: root node can't have frame"); + } + frame = new Frame(); + this._emitFuncBegin(node, "root"); + this._emitLine("var parentTemplate = null;"); + this._compileChildren(node, frame); + this._emitLine("if(parentTemplate) {"); + this._emitLine("parentTemplate.rootRenderFunc(env, context, frame, runtime, cb);"); + this._emitLine("} else {"); + this._emitLine("cb(null, " + this.buffer + ");"); + this._emitLine("}"); + this._emitFuncEnd(true); + this.inBlock = true; + var blockNames = []; + var blocks = node.findAll(nodes2.Block); + blocks.forEach(function(block, i) { + var name = block.name.value; + if (blockNames.indexOf(name) !== -1) { + throw new Error('Block "' + name + '" defined more than once.'); + } + blockNames.push(name); + _this16._emitFuncBegin(block, "b_" + name); + var tmpFrame = new Frame(); + _this16._emitLine("var frame = frame.push(true);"); + _this16.compile(block.body, tmpFrame); + _this16._emitFuncEnd(); + }); + this._emitLine("return {"); + blocks.forEach(function(block, i) { + var blockName = "b_" + block.name.value; + _this16._emitLine(blockName + ": " + blockName + ","); + }); + this._emitLine("root: root\n};"); + }; + _proto.compile = function compile2(node, frame) { + var _compile = this["compile" + node.typename]; + if (_compile) { + _compile.call(this, node, frame); + } else { + this.fail("compile: Cannot compile node: " + node.typename, node.lineno, node.colno); + } + }; + _proto.getCode = function getCode() { + return this.codebuf.join(""); + }; + return Compiler2; + }(Obj); + module2.exports = { + compile: function compile2(src, asyncFilters, extensions, name, opts) { + if (opts === void 0) { + opts = {}; + } + var c = new Compiler(name, opts.throwOnUndefined); + var preprocessors = (extensions || []).map(function(ext) { + return ext.preprocess; + }).filter(function(f) { + return !!f; + }); + var processedSrc = preprocessors.reduce(function(s, processor) { + return processor(s); + }, src); + c.compile(transformer.transform(parser2.parse(processedSrc, extensions, opts), asyncFilters, name)); + return c.getCode(); + }, + Compiler + }; +}); + +// ../../node_modules/nunjucks/src/filters.js +var require_filters = __commonJS((exports2, module2) => { + "use strict"; + var lib2 = require_lib(); + var r = require_runtime(); + var _exports = module2.exports = {}; + function normalize(value, defaultValue) { + if (value === null || value === void 0 || value === false) { + return defaultValue; + } + return value; + } + _exports.abs = Math.abs; + function isNaN2(num) { + return num !== num; + } + function batch(arr, linecount, fillWith) { + var i; + var res = []; + var tmp = []; + for (i = 0; i < arr.length; i++) { + if (i % linecount === 0 && tmp.length) { + res.push(tmp); + tmp = []; + } + tmp.push(arr[i]); + } + if (tmp.length) { + if (fillWith) { + for (i = tmp.length; i < linecount; i++) { + tmp.push(fillWith); + } + } + res.push(tmp); + } + return res; + } + _exports.batch = batch; + function capitalize(str) { + str = normalize(str, ""); + var ret = str.toLowerCase(); + return r.copySafeness(str, ret.charAt(0).toUpperCase() + ret.slice(1)); + } + _exports.capitalize = capitalize; + function center(str, width) { + str = normalize(str, ""); + width = width || 80; + if (str.length >= width) { + return str; + } + var spaces = width - str.length; + var pre = lib2.repeat(" ", spaces / 2 - spaces % 2); + var post = lib2.repeat(" ", spaces / 2); + return r.copySafeness(str, pre + str + post); + } + _exports.center = center; + function default_(val, def, bool) { + if (bool) { + return val || def; + } else { + return val !== void 0 ? val : def; + } + } + _exports["default"] = default_; + function dictsort(val, caseSensitive, by) { + if (!lib2.isObject(val)) { + throw new lib2.TemplateError("dictsort filter: val must be an object"); + } + var array = []; + for (var k in val) { + array.push([k, val[k]]); + } + var si; + if (by === void 0 || by === "key") { + si = 0; + } else if (by === "value") { + si = 1; + } else { + throw new lib2.TemplateError("dictsort filter: You can only sort by either key or value"); + } + array.sort(function(t1, t2) { + var a = t1[si]; + var b = t2[si]; + if (!caseSensitive) { + if (lib2.isString(a)) { + a = a.toUpperCase(); + } + if (lib2.isString(b)) { + b = b.toUpperCase(); + } + } + return a > b ? 1 : a === b ? 0 : -1; + }); + return array; + } + _exports.dictsort = dictsort; + function dump(obj, spaces) { + return JSON.stringify(obj, null, spaces); + } + _exports.dump = dump; + function escape(str) { + if (str instanceof r.SafeString) { + return str; + } + str = str === null || str === void 0 ? "" : str; + return r.markSafe(lib2.escape(str.toString())); + } + _exports.escape = escape; + function safe(str) { + if (str instanceof r.SafeString) { + return str; + } + str = str === null || str === void 0 ? "" : str; + return r.markSafe(str.toString()); + } + _exports.safe = safe; + function first(arr) { + return arr[0]; + } + _exports.first = first; + function forceescape(str) { + str = str === null || str === void 0 ? "" : str; + return r.markSafe(lib2.escape(str.toString())); + } + _exports.forceescape = forceescape; + function groupby(arr, attr) { + return lib2.groupBy(arr, attr, this.env.opts.throwOnUndefined); + } + _exports.groupby = groupby; + function indent(str, width, indentfirst) { + str = normalize(str, ""); + if (str === "") { + return ""; + } + width = width || 4; + var lines = str.split("\n"); + var sp = lib2.repeat(" ", width); + var res = lines.map(function(l, i) { + return i === 0 && !indentfirst ? l : "" + sp + l; + }).join("\n"); + return r.copySafeness(str, res); + } + _exports.indent = indent; + function join(arr, del, attr) { + del = del || ""; + if (attr) { + arr = lib2.map(arr, function(v) { + return v[attr]; + }); + } + return arr.join(del); + } + _exports.join = join; + function last(arr) { + return arr[arr.length - 1]; + } + _exports.last = last; + function lengthFilter(val) { + var value = normalize(val, ""); + if (value !== void 0) { + if (typeof Map === "function" && value instanceof Map || typeof Set === "function" && value instanceof Set) { + return value.size; + } + if (lib2.isObject(value) && !(value instanceof r.SafeString)) { + return lib2.keys(value).length; + } + return value.length; + } + return 0; + } + _exports.length = lengthFilter; + function list(val) { + if (lib2.isString(val)) { + return val.split(""); + } else if (lib2.isObject(val)) { + return lib2._entries(val || {}).map(function(_ref) { + var key = _ref[0], value = _ref[1]; + return { + key, + value + }; + }); + } else if (lib2.isArray(val)) { + return val; + } else { + throw new lib2.TemplateError("list filter: type not iterable"); + } + } + _exports.list = list; + function lower(str) { + str = normalize(str, ""); + return str.toLowerCase(); + } + _exports.lower = lower; + function nl2br(str) { + if (str === null || str === void 0) { + return ""; + } + return r.copySafeness(str, str.replace(/\r\n|\n/g, "
\n")); + } + _exports.nl2br = nl2br; + function random(arr) { + return arr[Math.floor(Math.random() * arr.length)]; + } + _exports.random = random; + function getSelectOrReject(expectedTestResult) { + function filter(arr, testName, secondArg) { + if (testName === void 0) { + testName = "truthy"; + } + var context = this; + var test = context.env.getTest(testName); + return lib2.toArray(arr).filter(function examineTestResult(item) { + return test.call(context, item, secondArg) === expectedTestResult; + }); + } + return filter; + } + _exports.reject = getSelectOrReject(false); + function rejectattr(arr, attr) { + return arr.filter(function(item) { + return !item[attr]; + }); + } + _exports.rejectattr = rejectattr; + _exports.select = getSelectOrReject(true); + function selectattr(arr, attr) { + return arr.filter(function(item) { + return !!item[attr]; + }); + } + _exports.selectattr = selectattr; + function replace(str, old, new_, maxCount) { + var originalStr = str; + if (old instanceof RegExp) { + return str.replace(old, new_); + } + if (typeof maxCount === "undefined") { + maxCount = -1; + } + var res = ""; + if (typeof old === "number") { + old = "" + old; + } else if (typeof old !== "string") { + return str; + } + if (typeof str === "number") { + str = "" + str; + } + if (typeof str !== "string" && !(str instanceof r.SafeString)) { + return str; + } + if (old === "") { + res = new_ + str.split("").join(new_) + new_; + return r.copySafeness(str, res); + } + var nextIndex = str.indexOf(old); + if (maxCount === 0 || nextIndex === -1) { + return str; + } + var pos = 0; + var count = 0; + while (nextIndex > -1 && (maxCount === -1 || count < maxCount)) { + res += str.substring(pos, nextIndex) + new_; + pos = nextIndex + old.length; + count++; + nextIndex = str.indexOf(old, pos); + } + if (pos < str.length) { + res += str.substring(pos); + } + return r.copySafeness(originalStr, res); + } + _exports.replace = replace; + function reverse(val) { + var arr; + if (lib2.isString(val)) { + arr = list(val); + } else { + arr = lib2.map(val, function(v) { + return v; + }); + } + arr.reverse(); + if (lib2.isString(val)) { + return r.copySafeness(val, arr.join("")); + } + return arr; + } + _exports.reverse = reverse; + function round(val, precision, method) { + precision = precision || 0; + var factor = Math.pow(10, precision); + var rounder; + if (method === "ceil") { + rounder = Math.ceil; + } else if (method === "floor") { + rounder = Math.floor; + } else { + rounder = Math.round; + } + return rounder(val * factor) / factor; + } + _exports.round = round; + function slice(arr, slices, fillWith) { + var sliceLength = Math.floor(arr.length / slices); + var extra = arr.length % slices; + var res = []; + var offset = 0; + for (var i = 0; i < slices; i++) { + var start = offset + i * sliceLength; + if (i < extra) { + offset++; + } + var end = offset + (i + 1) * sliceLength; + var currSlice = arr.slice(start, end); + if (fillWith && i >= extra) { + currSlice.push(fillWith); + } + res.push(currSlice); + } + return res; + } + _exports.slice = slice; + function sum(arr, attr, start) { + if (start === void 0) { + start = 0; + } + if (attr) { + arr = lib2.map(arr, function(v) { + return v[attr]; + }); + } + return start + arr.reduce(function(a, b) { + return a + b; + }, 0); + } + _exports.sum = sum; + _exports.sort = r.makeMacro(["value", "reverse", "case_sensitive", "attribute"], [], function sortFilter(arr, reversed, caseSens, attr) { + var _this = this; + var array = lib2.map(arr, function(v) { + return v; + }); + var getAttribute = lib2.getAttrGetter(attr); + array.sort(function(a, b) { + var x = attr ? getAttribute(a) : a; + var y = attr ? getAttribute(b) : b; + if (_this.env.opts.throwOnUndefined && attr && (x === void 0 || y === void 0)) { + throw new TypeError('sort: attribute "' + attr + '" resolved to undefined'); + } + if (!caseSens && lib2.isString(x) && lib2.isString(y)) { + x = x.toLowerCase(); + y = y.toLowerCase(); + } + if (x < y) { + return reversed ? 1 : -1; + } else if (x > y) { + return reversed ? -1 : 1; + } else { + return 0; + } + }); + return array; + }); + function string(obj) { + return r.copySafeness(obj, obj); + } + _exports.string = string; + function striptags(input, preserveLinebreaks) { + input = normalize(input, ""); + var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|/gi; + var trimmedInput = trim(input.replace(tags, "")); + var res = ""; + if (preserveLinebreaks) { + res = trimmedInput.replace(/^ +| +$/gm, "").replace(/ +/g, " ").replace(/(\r\n)/g, "\n").replace(/\n\n\n+/g, "\n\n"); + } else { + res = trimmedInput.replace(/\s+/gi, " "); + } + return r.copySafeness(input, res); + } + _exports.striptags = striptags; + function title(str) { + str = normalize(str, ""); + var words = str.split(" ").map(function(word) { + return capitalize(word); + }); + return r.copySafeness(str, words.join(" ")); + } + _exports.title = title; + function trim(str) { + return r.copySafeness(str, str.replace(/^\s*|\s*$/g, "")); + } + _exports.trim = trim; + function truncate(input, length, killwords, end) { + var orig = input; + input = normalize(input, ""); + length = length || 255; + if (input.length <= length) { + return input; + } + if (killwords) { + input = input.substring(0, length); + } else { + var idx = input.lastIndexOf(" ", length); + if (idx === -1) { + idx = length; + } + input = input.substring(0, idx); + } + input += end !== void 0 && end !== null ? end : "..."; + return r.copySafeness(orig, input); + } + _exports.truncate = truncate; + function upper(str) { + str = normalize(str, ""); + return str.toUpperCase(); + } + _exports.upper = upper; + function urlencode(obj) { + var enc = encodeURIComponent; + if (lib2.isString(obj)) { + return enc(obj); + } else { + var keyvals = lib2.isArray(obj) ? obj : lib2._entries(obj); + return keyvals.map(function(_ref2) { + var k = _ref2[0], v = _ref2[1]; + return enc(k) + "=" + enc(v); + }).join("&"); + } + } + _exports.urlencode = urlencode; + var puncRe = /^(?:\(|<|<)?(.*?)(?:\.|,|\)|\n|>)?$/; + var emailRe = /^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d\-]+(\.[a-z\d\-]+)+$/i; + var httpHttpsRe = /^https?:\/\/.*$/; + var wwwRe = /^www\./; + var tldRe = /\.(?:org|net|com)(?:\:|\/|$)/; + function urlize(str, length, nofollow) { + if (isNaN2(length)) { + length = Infinity; + } + var noFollowAttr = nofollow === true ? ' rel="nofollow"' : ""; + var words = str.split(/(\s+)/).filter(function(word) { + return word && word.length; + }).map(function(word) { + var matches = word.match(puncRe); + var possibleUrl = matches ? matches[1] : word; + var shortUrl = possibleUrl.substr(0, length); + if (httpHttpsRe.test(possibleUrl)) { + return '" + shortUrl + ""; + } + if (wwwRe.test(possibleUrl)) { + return '" + shortUrl + ""; + } + if (emailRe.test(possibleUrl)) { + return '' + possibleUrl + ""; + } + if (tldRe.test(possibleUrl)) { + return '" + shortUrl + ""; + } + return word; + }); + return words.join(""); + } + _exports.urlize = urlize; + function wordcount(str) { + str = normalize(str, ""); + var words = str ? str.match(/\w+/g) : null; + return words ? words.length : null; + } + _exports.wordcount = wordcount; + function float(val, def) { + var res = parseFloat(val); + return isNaN2(res) ? def : res; + } + _exports.float = float; + var intFilter = r.makeMacro(["value", "default", "base"], [], function doInt(value, defaultValue, base) { + if (base === void 0) { + base = 10; + } + var res = parseInt(value, base); + return isNaN2(res) ? defaultValue : res; + }); + _exports.int = intFilter; + _exports.d = _exports.default; + _exports.e = _exports.escape; +}); + +// ../../node_modules/nunjucks/src/loader.js +var require_loader = __commonJS((exports2, module2) => { + "use strict"; + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var path = require("path"); + var _require2 = require_object(); + var EmitterObj = _require2.EmitterObj; + module2.exports = /* @__PURE__ */ function(_EmitterObj) { + _inheritsLoose(Loader2, _EmitterObj); + function Loader2() { + return _EmitterObj.apply(this, arguments) || this; + } + var _proto = Loader2.prototype; + _proto.resolve = function resolve(from, to) { + return path.resolve(path.dirname(from), to); + }; + _proto.isRelative = function isRelative(filename) { + return filename.indexOf("./") === 0 || filename.indexOf("../") === 0; + }; + return Loader2; + }(EmitterObj); +}); + +// ../../node_modules/nunjucks/src/precompiled-loader.js +var require_precompiled_loader = __commonJS((exports2, module2) => { + "use strict"; + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var Loader2 = require_loader(); + var PrecompiledLoader = /* @__PURE__ */ function(_Loader) { + _inheritsLoose(PrecompiledLoader2, _Loader); + function PrecompiledLoader2(compiledTemplates) { + var _this; + _this = _Loader.call(this) || this; + _this.precompiled = compiledTemplates || {}; + return _this; + } + var _proto = PrecompiledLoader2.prototype; + _proto.getSource = function getSource(name) { + if (this.precompiled[name]) { + return { + src: { + type: "code", + obj: this.precompiled[name] + }, + path: name + }; + } + return null; + }; + return PrecompiledLoader2; + }(Loader2); + module2.exports = { + PrecompiledLoader + }; +}); + +// ../../node_modules/picomatch/lib/constants.js +var require_constants = __commonJS((exports2, module2) => { + "use strict"; + var path = require("path"); + var WIN_SLASH = "\\\\/"; + var WIN_NO_SLASH = `[^${WIN_SLASH}]`; + var DOT_LITERAL = "\\."; + var PLUS_LITERAL = "\\+"; + var QMARK_LITERAL = "\\?"; + var SLASH_LITERAL = "\\/"; + var ONE_CHAR = "(?=.)"; + var QMARK = "[^/]"; + var END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; + var START_ANCHOR = `(?:^|${SLASH_LITERAL})`; + var DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; + var NO_DOT = `(?!${DOT_LITERAL})`; + var NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; + var NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; + var NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; + var QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; + var STAR = `${QMARK}*?`; + var POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR + }; + var WINDOWS_CHARS = { + ...POSIX_CHARS, + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` + }; + var POSIX_REGEX_SOURCE = { + alnum: "a-zA-Z0-9", + alpha: "a-zA-Z", + ascii: "\\x00-\\x7F", + blank: " \\t", + cntrl: "\\x00-\\x1F\\x7F", + digit: "0-9", + graph: "\\x21-\\x7E", + lower: "a-z", + print: "\\x20-\\x7E ", + punct: "\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~", + space: " \\t\\r\\n\\v\\f", + upper: "A-Z", + word: "A-Za-z0-9_", + xdigit: "A-Fa-f0-9" + }; + module2.exports = { + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + REPLACEMENTS: { + "***": "*", + "**/**": "**", + "**/**/**": "**" + }, + CHAR_0: 48, + CHAR_9: 57, + CHAR_UPPERCASE_A: 65, + CHAR_LOWERCASE_A: 97, + CHAR_UPPERCASE_Z: 90, + CHAR_LOWERCASE_Z: 122, + CHAR_LEFT_PARENTHESES: 40, + CHAR_RIGHT_PARENTHESES: 41, + CHAR_ASTERISK: 42, + CHAR_AMPERSAND: 38, + CHAR_AT: 64, + CHAR_BACKWARD_SLASH: 92, + CHAR_CARRIAGE_RETURN: 13, + CHAR_CIRCUMFLEX_ACCENT: 94, + CHAR_COLON: 58, + CHAR_COMMA: 44, + CHAR_DOT: 46, + CHAR_DOUBLE_QUOTE: 34, + CHAR_EQUAL: 61, + CHAR_EXCLAMATION_MARK: 33, + CHAR_FORM_FEED: 12, + CHAR_FORWARD_SLASH: 47, + CHAR_GRAVE_ACCENT: 96, + CHAR_HASH: 35, + CHAR_HYPHEN_MINUS: 45, + CHAR_LEFT_ANGLE_BRACKET: 60, + CHAR_LEFT_CURLY_BRACE: 123, + CHAR_LEFT_SQUARE_BRACKET: 91, + CHAR_LINE_FEED: 10, + CHAR_NO_BREAK_SPACE: 160, + CHAR_PERCENT: 37, + CHAR_PLUS: 43, + CHAR_QUESTION_MARK: 63, + CHAR_RIGHT_ANGLE_BRACKET: 62, + CHAR_RIGHT_CURLY_BRACE: 125, + CHAR_RIGHT_SQUARE_BRACKET: 93, + CHAR_SEMICOLON: 59, + CHAR_SINGLE_QUOTE: 39, + CHAR_SPACE: 32, + CHAR_TAB: 9, + CHAR_UNDERSCORE: 95, + CHAR_VERTICAL_LINE: 124, + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, + SEP: path.sep, + extglobChars(chars) { + return { + "!": {type: "negate", open: "(?:(?!(?:", close: `))${chars.STAR})`}, + "?": {type: "qmark", open: "(?:", close: ")?"}, + "+": {type: "plus", open: "(?:", close: ")+"}, + "*": {type: "star", open: "(?:", close: ")*"}, + "@": {type: "at", open: "(?:", close: ")"} + }; + }, + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; + } + }; +}); + +// ../../node_modules/picomatch/lib/utils.js +var require_utils = __commonJS((exports2) => { + "use strict"; + var path = require("path"); + var win32 = process.platform === "win32"; + var { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL + } = require_constants(); + exports2.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + exports2.hasRegexChars = (str) => REGEX_SPECIAL_CHARS.test(str); + exports2.isRegexChar = (str) => str.length === 1 && exports2.hasRegexChars(str); + exports2.escapeRegex = (str) => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1"); + exports2.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/"); + exports2.removeBackslashes = (str) => { + return str.replace(REGEX_REMOVE_BACKSLASH, (match) => { + return match === "\\" ? "" : match; + }); + }; + exports2.supportsLookbehinds = () => { + const segs = process.version.slice(1).split(".").map(Number); + if (segs.length === 3 && segs[0] >= 9 || segs[0] === 8 && segs[1] >= 10) { + return true; + } + return false; + }; + exports2.isWindows = (options) => { + if (options && typeof options.windows === "boolean") { + return options.windows; + } + return win32 === true || path.sep === "\\"; + }; + exports2.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) + return input; + if (input[idx - 1] === "\\") + return exports2.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; + }; + exports2.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith("./")) { + output = output.slice(2); + state.prefix = "./"; + } + return output; + }; + exports2.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? "" : "^"; + const append = options.contains ? "" : "$"; + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; + } + return output; + }; +}); + +// ../../node_modules/picomatch/lib/scan.js +var require_scan = __commonJS((exports2, module2) => { + "use strict"; + var utils = require_utils(); + var { + CHAR_ASTERISK, + CHAR_AT, + CHAR_BACKWARD_SLASH, + CHAR_COMMA, + CHAR_DOT, + CHAR_EXCLAMATION_MARK, + CHAR_FORWARD_SLASH, + CHAR_LEFT_CURLY_BRACE, + CHAR_LEFT_PARENTHESES, + CHAR_LEFT_SQUARE_BRACKET, + CHAR_PLUS, + CHAR_QUESTION_MARK, + CHAR_RIGHT_CURLY_BRACE, + CHAR_RIGHT_PARENTHESES, + CHAR_RIGHT_SQUARE_BRACKET + } = require_constants(); + var isPathSeparator = (code) => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; + }; + var depth = (token) => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } + }; + var scan = (input, options) => { + const opts = options || {}; + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let negatedExtglob = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = {value: "", depth: 0, isGlob: false}; + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; + while (index < length) { + code = advance(); + let next; + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; + } + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = {value: "", depth: 0, isGlob: false}; + if (finished === true) + continue; + if (prev === CHAR_DOT && index === start + 1) { + start += 2; + continue; + } + lastIndex = index + 1; + continue; + } + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS || code === CHAR_AT || code === CHAR_ASTERISK || code === CHAR_QUESTION_MARK || code === CHAR_EXCLAMATION_MARK; + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; + } + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; + } + } + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) + isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + break; + } + } + if (scanToEnd === true) { + continue; + } + break; + } + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; + } + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } + continue; + } + break; + } + if (isGlob === true) { + finished = true; + if (scanToEnd === true) { + continue; + } + break; + } + } + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } + let base = str; + let prefix = ""; + let glob = ""; + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ""; + glob = str; + } else { + base = str; + } + if (base && base !== "" && base !== "/" && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); + } + } + if (opts.unescape === true) { + if (glob) + glob = utils.removeBackslashes(glob); + if (base && backslashes === true) { + base = utils.removeBackslashes(base); + } + } + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated, + negatedExtglob + }; + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); + } + state.tokens = tokens; + } + if (opts.parts === true || opts.tokens === true) { + let prevIndex; + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; + } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== "") { + parts.push(value); + } + prevIndex = i; + } + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; + } + } + state.slashes = slashes; + state.parts = parts; + } + return state; + }; + module2.exports = scan; +}); + +// ../../node_modules/picomatch/lib/parse.js +var require_parse = __commonJS((exports2, module2) => { + "use strict"; + var constants = require_constants(); + var utils = require_utils(); + var { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS + } = constants; + var expandRange = (args, options) => { + if (typeof options.expandRange === "function") { + return options.expandRange(...args, options); + } + args.sort(); + const value = `[${args.join("-")}]`; + try { + new RegExp(value); + } catch (ex) { + return args.map((v) => utils.escapeRegex(v)).join(".."); + } + return value; + }; + var syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; + }; + var parse = (input, options) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + input = REPLACEMENTS[input] || input; + const opts = {...options}; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + const bos = {type: "bos", value: "", output: opts.prepend || ""}; + const tokens = [bos]; + const capture = opts.capture ? "" : "?:"; + const win32 = utils.isWindows(options); + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; + const globstar = (opts2) => { + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const nodot = opts.dot ? "" : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; + if (opts.capture) { + star = `(${star})`; + } + if (typeof opts.noext === "boolean") { + opts.noextglob = opts.noext; + } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: "", + output: "", + prefix: "", + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; + input = utils.removePrefix(input, state); + len = input.length; + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index] || ""; + const remaining = () => input.slice(state.index + 1); + const consume = (value2 = "", num = 0) => { + state.consumed += value2; + state.index += num; + }; + const append = (token) => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; + const negate = () => { + let count = 1; + while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) { + advance(); + state.start++; + count++; + } + if (count % 2 === 0) { + return false; + } + state.negated = true; + state.start++; + return true; + }; + const increment = (type) => { + state[type]++; + stack.push(type); + }; + const decrement = (type) => { + state[type]--; + stack.pop(); + }; + const push = (tok) => { + if (prev.type === "globstar") { + const isBrace = state.braces > 0 && (tok.type === "comma" || tok.type === "brace"); + const isExtglob = tok.extglob === true || extglobs.length && (tok.type === "pipe" || tok.type === "paren"); + if (tok.type !== "slash" && tok.type !== "paren" && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = "star"; + prev.value = "*"; + prev.output = star; + state.output += prev.output; + } + } + if (extglobs.length && tok.type !== "paren") { + extglobs[extglobs.length - 1].inner += tok.value; + } + if (tok.value || tok.output) + append(tok); + if (prev && prev.type === "text" && tok.type === "text") { + prev.value += tok.value; + prev.output = (prev.output || "") + tok.value; + return; + } + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; + const extglobOpen = (type, value2) => { + const token = {...EXTGLOB_CHARS[value2], conditions: 1, inner: ""}; + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + const output = (opts.capture ? "(" : "") + token.open; + increment("parens"); + push({type, value: value2, output: state.output ? "" : ONE_CHAR}); + push({type: "paren", extglob: true, value: advance(), output}); + extglobs.push(token); + }; + const extglobClose = (token) => { + let output = token.close + (opts.capture ? ")" : ""); + let rest; + if (token.type === "negate") { + let extglobStar = star; + if (token.inner && token.inner.length > 1 && token.inner.includes("/")) { + extglobStar = globstar(opts); + } + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } + if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + output = token.close = `)${rest})${extglobStar})`; + } + if (token.prev.type === "bos") { + state.negatedExtglob = true; + } + } + push({type: "paren", extglob: true, value, output}); + decrement("parens"); + }; + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === "\\") { + backslashes = true; + return m; + } + if (first === "?") { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ""); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ""); + } + return QMARK.repeat(chars.length); + } + if (first === ".") { + return DOT_LITERAL.repeat(chars.length); + } + if (first === "*") { + if (esc) { + return esc + first + (rest ? star : ""); + } + return star; + } + return esc ? m : `\\${m}`; + }); + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ""); + } else { + output = output.replace(/\\+/g, (m) => { + return m.length % 2 === 0 ? "\\\\" : m ? "\\" : ""; + }); + } + } + if (output === input && opts.contains === true) { + state.output = input; + return state; + } + state.output = utils.wrapOutput(output, state, options); + return state; + } + while (!eos()) { + value = advance(); + if (value === "\0") { + continue; + } + if (value === "\\") { + const next = peek(); + if (next === "/" && opts.bash !== true) { + continue; + } + if (next === "." || next === ";") { + continue; + } + if (!next) { + value += "\\"; + push({type: "text", value}); + continue; + } + const match = /^\\+/.exec(remaining()); + let slashes = 0; + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += "\\"; + } + } + if (opts.unescape === true) { + value = advance(); + } else { + value += advance(); + } + if (state.brackets === 0) { + push({type: "text", value}); + continue; + } + } + if (state.brackets > 0 && (value !== "]" || prev.value === "[" || prev.value === "[^")) { + if (opts.posix !== false && value === ":") { + const inner = prev.value.slice(1); + if (inner.includes("[")) { + prev.posix = true; + if (inner.includes(":")) { + const idx = prev.value.lastIndexOf("["); + const pre = prev.value.slice(0, idx); + const rest2 = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest2]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } + if (value === "[" && peek() !== ":" || value === "-" && peek() === "]") { + value = `\\${value}`; + } + if (value === "]" && (prev.value === "[" || prev.value === "[^")) { + value = `\\${value}`; + } + if (opts.posix === true && value === "!" && prev.value === "[") { + value = "^"; + } + prev.value += value; + append({value}); + continue; + } + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({value}); + continue; + } + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({type: "text", value}); + } + continue; + } + if (value === "(") { + increment("parens"); + push({type: "paren", value}); + continue; + } + if (value === ")") { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "(")); + } + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } + push({type: "paren", value, output: state.parens ? ")" : "\\)"}); + decrement("parens"); + continue; + } + if (value === "[") { + if (opts.nobracket === true || !remaining().includes("]")) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("closing", "]")); + } + value = `\\${value}`; + } else { + increment("brackets"); + } + push({type: "bracket", value}); + continue; + } + if (value === "]") { + if (opts.nobracket === true || prev && prev.type === "bracket" && prev.value.length === 1) { + push({type: "text", value, output: `\\${value}`}); + continue; + } + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError("opening", "[")); + } + push({type: "text", value, output: `\\${value}`}); + continue; + } + decrement("brackets"); + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === "^" && !prevValue.includes("/")) { + value = `/${value}`; + } + prev.value += value; + append({value}); + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } + if (value === "{" && opts.nobrace !== true) { + increment("braces"); + const open = { + type: "brace", + value, + output: "(", + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; + braces.push(open); + push(open); + continue; + } + if (value === "}") { + const brace = braces[braces.length - 1]; + if (opts.nobrace === true || !brace) { + push({type: "text", value, output: value}); + continue; + } + let output = ")"; + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === "brace") { + break; + } + if (arr[i].type !== "dots") { + range.unshift(arr[i].value); + } + } + output = expandRange(range, opts); + state.backtrack = true; + } + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = "\\{"; + value = output = "\\}"; + state.output = out; + for (const t of toks) { + state.output += t.output || t.value; + } + } + push({type: "brace", value, output}); + decrement("braces"); + braces.pop(); + continue; + } + if (value === "|") { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({type: "text", value}); + continue; + } + if (value === ",") { + let output = value; + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === "braces") { + brace.comma = true; + output = "|"; + } + push({type: "comma", value, output}); + continue; + } + if (value === "/") { + if (prev.type === "dot" && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ""; + state.output = ""; + tokens.pop(); + prev = bos; + continue; + } + push({type: "slash", value, output: SLASH_LITERAL}); + continue; + } + if (value === ".") { + if (state.braces > 0 && prev.type === "dot") { + if (prev.value === ".") + prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = "dots"; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } + if (state.braces + state.parens === 0 && prev.type !== "bos" && prev.type !== "slash") { + push({type: "text", value, output: DOT_LITERAL}); + continue; + } + push({type: "dot", value, output: DOT_LITERAL}); + continue; + } + if (value === "?") { + const isGroup = prev && prev.value === "("; + if (!isGroup && opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("qmark", value); + continue; + } + if (prev && prev.type === "paren") { + const next = peek(); + let output = value; + if (next === "<" && !utils.supportsLookbehinds()) { + throw new Error("Node.js v10 or higher is required for regex lookbehinds"); + } + if (prev.value === "(" && !/[!=<:]/.test(next) || next === "<" && !/<([!=]|\w+>)/.test(remaining())) { + output = `\\${value}`; + } + push({type: "text", value, output}); + continue; + } + if (opts.dot !== true && (prev.type === "slash" || prev.type === "bos")) { + push({type: "qmark", value, output: QMARK_NO_DOT}); + continue; + } + push({type: "qmark", value, output: QMARK}); + continue; + } + if (value === "!") { + if (opts.noextglob !== true && peek() === "(") { + if (peek(2) !== "?" || !/[!=<:]/.test(peek(3))) { + extglobOpen("negate", value); + continue; + } + } + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } + if (value === "+") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + extglobOpen("plus", value); + continue; + } + if (prev && prev.value === "(" || opts.regex === false) { + push({type: "plus", value, output: PLUS_LITERAL}); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren" || prev.type === "brace") || state.parens > 0) { + push({type: "plus", value}); + continue; + } + push({type: "plus", value: PLUS_LITERAL}); + continue; + } + if (value === "@") { + if (opts.noextglob !== true && peek() === "(" && peek(2) !== "?") { + push({type: "at", extglob: true, value, output: ""}); + continue; + } + push({type: "text", value}); + continue; + } + if (value !== "*") { + if (value === "$" || value === "^") { + value = `\\${value}`; + } + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } + push({type: "text", value}); + continue; + } + if (prev && (prev.type === "globstar" || prev.star === true)) { + prev.type = "star"; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen("star", value); + continue; + } + if (prev.type === "star") { + if (opts.noglobstar === true) { + consume(value); + continue; + } + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === "slash" || prior.type === "bos"; + const afterStar = before && (before.type === "star" || before.type === "globstar"); + if (opts.bash === true && (!isStart || rest[0] && rest[0] !== "/")) { + push({type: "star", value, output: ""}); + continue; + } + const isBrace = state.braces > 0 && (prior.type === "comma" || prior.type === "brace"); + const isExtglob = extglobs.length && (prior.type === "pipe" || prior.type === "paren"); + if (!isStart && prior.type !== "paren" && !isBrace && !isExtglob) { + push({type: "star", value, output: ""}); + continue; + } + while (rest.slice(0, 3) === "/**") { + const after = input[state.index + 4]; + if (after && after !== "/") { + break; + } + rest = rest.slice(3); + consume("/**", 3); + } + if (prior.type === "bos" && eos()) { + prev.type = "globstar"; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = globstar(opts) + (opts.strictSlashes ? ")" : "|$)"); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } + if (prior.type === "slash" && prior.prev.type !== "bos" && rest[0] === "/") { + const end = rest[1] !== void 0 ? "|$" : ""; + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + prev.type = "globstar"; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; + state.output += prior.output + prev.output; + state.globstar = true; + consume(value + advance()); + push({type: "slash", value: "/", output: ""}); + continue; + } + if (prior.type === "bos" && rest[0] === "/") { + prev.type = "globstar"; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({type: "slash", value: "/", output: ""}); + continue; + } + state.output = state.output.slice(0, -prev.output.length); + prev.type = "globstar"; + prev.output = globstar(opts); + prev.value += value; + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } + const token = {type: "star", value, output: star}; + if (opts.bash === true) { + token.output = ".*?"; + if (prev.type === "bos" || prev.type === "slash") { + token.output = nodot + token.output; + } + push(token); + continue; + } + if (prev && (prev.type === "bracket" || prev.type === "paren") && opts.regex === true) { + token.output = value; + push(token); + continue; + } + if (state.index === state.start || prev.type === "slash" || prev.type === "dot") { + if (prev.type === "dot") { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; + } else { + state.output += nodot; + prev.output += nodot; + } + if (peek() !== "*") { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } + push(token); + } + while (state.brackets > 0) { + if (opts.strictBrackets === true) + throw new SyntaxError(syntaxError("closing", "]")); + state.output = utils.escapeLast(state.output, "["); + decrement("brackets"); + } + while (state.parens > 0) { + if (opts.strictBrackets === true) + throw new SyntaxError(syntaxError("closing", ")")); + state.output = utils.escapeLast(state.output, "("); + decrement("parens"); + } + while (state.braces > 0) { + if (opts.strictBrackets === true) + throw new SyntaxError(syntaxError("closing", "}")); + state.output = utils.escapeLast(state.output, "{"); + decrement("braces"); + } + if (opts.strictSlashes !== true && (prev.type === "star" || prev.type === "bracket")) { + push({type: "maybe_slash", value: "", output: `${SLASH_LITERAL}?`}); + } + if (state.backtrack === true) { + state.output = ""; + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; + if (token.suffix) { + state.output += token.suffix; + } + } + } + return state; + }; + parse.fastpaths = (input, options) => { + const opts = {...options}; + const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? "" : "?:"; + const state = {negated: false, prefix: ""}; + let star = opts.bash === true ? ".*?" : STAR; + if (opts.capture) { + star = `(${star})`; + } + const globstar = (opts2) => { + if (opts2.noglobstar === true) + return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const create = (str) => { + switch (str) { + case "*": + return `${nodot}${ONE_CHAR}${star}`; + case ".*": + return `${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*.*": + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "*/*": + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; + case "**": + return nodot + globstar(opts); + case "**/*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; + case "**/*.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; + case "**/.*": + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) + return; + const source2 = create(match[1]); + if (!source2) + return; + return source2 + DOT_LITERAL + match[2]; + } + } + }; + const output = utils.removePrefix(input, state); + let source = create(output); + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } + return source; + }; + module2.exports = parse; +}); + +// ../../node_modules/picomatch/lib/picomatch.js +var require_picomatch = __commonJS((exports2, module2) => { + "use strict"; + var path = require("path"); + var scan = require_scan(); + var parse = require_parse(); + var utils = require_utils(); + var constants = require_constants(); + var isObject = (val) => val && typeof val === "object" && !Array.isArray(val); + var picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map((input) => picomatch(input, options, returnState)); + const arrayMatcher = (str) => { + for (const isMatch of fns) { + const state2 = isMatch(str); + if (state2) + return state2; + } + return false; + }; + return arrayMatcher; + } + const isState = isObject(glob) && glob.tokens && glob.input; + if (glob === "" || typeof glob !== "string" && !isState) { + throw new TypeError("Expected pattern to be a non-empty string"); + } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState ? picomatch.compileRe(glob, options) : picomatch.makeRe(glob, options, false, true); + const state = regex.state; + delete regex.state; + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = {...options, ignore: null, onMatch: null, onResult: null}; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } + const matcher = (input, returnObject = false) => { + const {isMatch, match, output} = picomatch.test(input, regex, options, {glob, posix}); + const result = {glob, state, regex, posix, input, output, match, isMatch}; + if (typeof opts.onResult === "function") { + opts.onResult(result); + } + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } + if (isIgnored(input)) { + if (typeof opts.onIgnore === "function") { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } + if (typeof opts.onMatch === "function") { + opts.onMatch(result); + } + return returnObject ? result : true; + }; + if (returnState) { + matcher.state = state; + } + return matcher; + }; + picomatch.test = (input, regex, options, {glob, posix} = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected input to be a string"); + } + if (input === "") { + return {isMatch: false, output: ""}; + } + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = match && format ? format(input) : input; + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } + } + return {isMatch: Boolean(match), match, output}; + }; + picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path.basename(input)); + }; + picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) + return pattern.map((p) => picomatch.parse(p, options)); + return parse(pattern, {...options, fastpaths: false}); + }; + picomatch.scan = (input, options) => scan(input, options); + picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return state.output; + } + const opts = options || {}; + const prepend = opts.contains ? "" : "^"; + const append = opts.contains ? "" : "$"; + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { + source = `^(?!${source}).*$`; + } + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = state; + } + return regex; + }; + picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { + if (!input || typeof input !== "string") { + throw new TypeError("Expected a non-empty string"); + } + let parsed = {negated: false, fastpaths: true}; + if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) { + parsed.output = parse.fastpaths(input, options); + } + if (!parsed.output) { + parsed = parse(input, options); + } + return picomatch.compileRe(parsed, options, returnOutput, returnState); + }; + picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? "i" : "")); + } catch (err) { + if (options && options.debug === true) + throw err; + return /$^/; + } + }; + picomatch.constants = constants; + module2.exports = picomatch; +}); + +// ../../node_modules/picomatch/index.js +var require_picomatch2 = __commonJS((exports2, module2) => { + "use strict"; + module2.exports = require_picomatch(); +}); + +// ../../node_modules/readdirp/index.js +var require_readdirp = __commonJS((exports2, module2) => { + "use strict"; + var fs = require("fs"); + var {Readable} = require("stream"); + var sysPath = require("path"); + var {promisify} = require("util"); + var picomatch = require_picomatch2(); + var readdir = promisify(fs.readdir); + var stat = promisify(fs.stat); + var lstat = promisify(fs.lstat); + var realpath = promisify(fs.realpath); + var BANG = "!"; + var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR"; + var NORMAL_FLOW_ERRORS = new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]); + var FILE_TYPE = "files"; + var DIR_TYPE = "directories"; + var FILE_DIR_TYPE = "files_directories"; + var EVERYTHING_TYPE = "all"; + var ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE]; + var isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code); + var [maj, min] = process.versions.node.split(".").slice(0, 2).map((n) => Number.parseInt(n, 10)); + var wantBigintFsStats = process.platform === "win32" && (maj > 10 || maj === 10 && min >= 5); + var normalizeFilter = (filter) => { + if (filter === void 0) + return; + if (typeof filter === "function") + return filter; + if (typeof filter === "string") { + const glob = picomatch(filter.trim()); + return (entry) => glob(entry.basename); + } + if (Array.isArray(filter)) { + const positive = []; + const negative = []; + for (const item of filter) { + const trimmed = item.trim(); + if (trimmed.charAt(0) === BANG) { + negative.push(picomatch(trimmed.slice(1))); + } else { + positive.push(picomatch(trimmed)); + } + } + if (negative.length > 0) { + if (positive.length > 0) { + return (entry) => positive.some((f) => f(entry.basename)) && !negative.some((f) => f(entry.basename)); + } + return (entry) => !negative.some((f) => f(entry.basename)); + } + return (entry) => positive.some((f) => f(entry.basename)); + } + }; + var ReaddirpStream = class extends Readable { + static get defaultOptions() { + return { + root: ".", + fileFilter: (path) => true, + directoryFilter: (path) => true, + type: FILE_TYPE, + lstat: false, + depth: 2147483648, + alwaysStat: false + }; + } + constructor(options = {}) { + super({ + objectMode: true, + autoDestroy: true, + highWaterMark: options.highWaterMark || 4096 + }); + const opts = {...ReaddirpStream.defaultOptions, ...options}; + const {root, type} = opts; + this._fileFilter = normalizeFilter(opts.fileFilter); + this._directoryFilter = normalizeFilter(opts.directoryFilter); + const statMethod = opts.lstat ? lstat : stat; + if (wantBigintFsStats) { + this._stat = (path) => statMethod(path, {bigint: true}); + } else { + this._stat = statMethod; + } + this._maxDepth = opts.depth; + this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); + this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); + this._wantsEverything = type === EVERYTHING_TYPE; + this._root = sysPath.resolve(root); + this._isDirent = "Dirent" in fs && !opts.alwaysStat; + this._statsProp = this._isDirent ? "dirent" : "stats"; + this._rdOptions = {encoding: "utf8", withFileTypes: this._isDirent}; + this.parents = [this._exploreDir(root, 1)]; + this.reading = false; + this.parent = void 0; + } + async _read(batch) { + if (this.reading) + return; + this.reading = true; + try { + while (!this.destroyed && batch > 0) { + const {path, depth, files = []} = this.parent || {}; + if (files.length > 0) { + const slice = files.splice(0, batch).map((dirent) => this._formatEntry(dirent, path)); + for (const entry of await Promise.all(slice)) { + if (this.destroyed) + return; + const entryType = await this._getEntryType(entry); + if (entryType === "directory" && this._directoryFilter(entry)) { + if (depth <= this._maxDepth) { + this.parents.push(this._exploreDir(entry.fullPath, depth + 1)); + } + if (this._wantsDir) { + this.push(entry); + batch--; + } + } else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) { + if (this._wantsFile) { + this.push(entry); + batch--; + } + } + } + } else { + const parent = this.parents.pop(); + if (!parent) { + this.push(null); + break; + } + this.parent = await parent; + if (this.destroyed) + return; + } + } + } catch (error) { + this.destroy(error); + } finally { + this.reading = false; + } + } + async _exploreDir(path, depth) { + let files; + try { + files = await readdir(path, this._rdOptions); + } catch (error) { + this._onError(error); + } + return {files, depth, path}; + } + async _formatEntry(dirent, path) { + let entry; + try { + const basename = this._isDirent ? dirent.name : dirent; + const fullPath = sysPath.resolve(sysPath.join(path, basename)); + entry = {path: sysPath.relative(this._root, fullPath), fullPath, basename}; + entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath); + } catch (err) { + this._onError(err); + } + return entry; + } + _onError(err) { + if (isNormalFlowError(err) && !this.destroyed) { + this.emit("warn", err); + } else { + this.destroy(err); + } + } + async _getEntryType(entry) { + const stats = entry && entry[this._statsProp]; + if (!stats) { + return; + } + if (stats.isFile()) { + return "file"; + } + if (stats.isDirectory()) { + return "directory"; + } + if (stats && stats.isSymbolicLink()) { + const full = entry.fullPath; + try { + const entryRealPath = await realpath(full); + const entryRealPathStats = await lstat(entryRealPath); + if (entryRealPathStats.isFile()) { + return "file"; + } + if (entryRealPathStats.isDirectory()) { + const len = entryRealPath.length; + if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) { + const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`); + recursiveError.code = RECURSIVE_ERROR_CODE; + return this._onError(recursiveError); + } + return "directory"; + } + } catch (error) { + this._onError(error); + } + } + } + _includeAsFile(entry) { + const stats = entry && entry[this._statsProp]; + return stats && this._wantsEverything && !stats.isDirectory(); + } + }; + var readdirp = (root, options = {}) => { + let type = options.entryType || options.type; + if (type === "both") + type = FILE_DIR_TYPE; + if (type) + options.type = type; + if (!root) { + throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)"); + } else if (typeof root !== "string") { + throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)"); + } else if (type && !ALL_TYPES.includes(type)) { + throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`); + } + options.root = root; + return new ReaddirpStream(options); + }; + var readdirpPromise = (root, options = {}) => { + return new Promise((resolve, reject) => { + const files = []; + readdirp(root, options).on("data", (entry) => files.push(entry)).on("end", () => resolve(files)).on("error", (error) => reject(error)); + }); + }; + readdirp.promise = readdirpPromise; + readdirp.ReaddirpStream = ReaddirpStream; + readdirp.default = readdirp; + module2.exports = readdirp; +}); + +// ../../node_modules/normalize-path/index.js +var require_normalize_path = __commonJS((exports2, module2) => { + /*! + * normalize-path + * + * Copyright (c) 2014-2018, Jon Schlinkert. + * Released under the MIT License. + */ + module2.exports = function(path, stripTrailing) { + if (typeof path !== "string") { + throw new TypeError("expected path to be a string"); + } + if (path === "\\" || path === "/") + return "/"; + var len = path.length; + if (len <= 1) + return path; + var prefix = ""; + if (len > 4 && path[3] === "\\") { + var ch = path[2]; + if ((ch === "?" || ch === ".") && path.slice(0, 2) === "\\\\") { + path = path.slice(2); + prefix = "//"; + } + } + var segs = path.split(/[/\\]+/); + if (stripTrailing !== false && segs[segs.length - 1] === "") { + segs.pop(); + } + return prefix + segs.join("/"); + }; +}); + +// ../../node_modules/anymatch/index.js +var require_anymatch = __commonJS((exports2, module2) => { + "use strict"; + Object.defineProperty(exports2, "__esModule", {value: true}); + var picomatch = require_picomatch2(); + var normalizePath = require_normalize_path(); + var BANG = "!"; + var DEFAULT_OPTIONS = {returnIndex: false}; + var arrify = (item) => Array.isArray(item) ? item : [item]; + var createPattern = (matcher, options) => { + if (typeof matcher === "function") { + return matcher; + } + if (typeof matcher === "string") { + const glob = picomatch(matcher, options); + return (string) => matcher === string || glob(string); + } + if (matcher instanceof RegExp) { + return (string) => matcher.test(string); + } + return (string) => false; + }; + var matchPatterns = (patterns, negPatterns, args, returnIndex) => { + const isList = Array.isArray(args); + const _path = isList ? args[0] : args; + if (!isList && typeof _path !== "string") { + throw new TypeError("anymatch: second argument must be a string: got " + Object.prototype.toString.call(_path)); + } + const path = normalizePath(_path); + for (let index = 0; index < negPatterns.length; index++) { + const nglob = negPatterns[index]; + if (nglob(path)) { + return returnIndex ? -1 : false; + } + } + const applied = isList && [path].concat(args.slice(1)); + for (let index = 0; index < patterns.length; index++) { + const pattern = patterns[index]; + if (isList ? pattern(...applied) : pattern(path)) { + return returnIndex ? index : true; + } + } + return returnIndex ? -1 : false; + }; + var anymatch = (matchers, testString, options = DEFAULT_OPTIONS) => { + if (matchers == null) { + throw new TypeError("anymatch: specify first argument"); + } + const opts = typeof options === "boolean" ? {returnIndex: options} : options; + const returnIndex = opts.returnIndex || false; + const mtchers = arrify(matchers); + const negatedGlobs = mtchers.filter((item) => typeof item === "string" && item.charAt(0) === BANG).map((item) => item.slice(1)).map((item) => picomatch(item, opts)); + const patterns = mtchers.filter((item) => typeof item !== "string" || typeof item === "string" && item.charAt(0) !== BANG).map((matcher) => createPattern(matcher, opts)); + if (testString == null) { + return (testString2, ri = false) => { + const returnIndex2 = typeof ri === "boolean" ? ri : false; + return matchPatterns(patterns, negatedGlobs, testString2, returnIndex2); + }; + } + return matchPatterns(patterns, negatedGlobs, testString, returnIndex); + }; + anymatch.default = anymatch; + module2.exports = anymatch; +}); + +// ../../node_modules/is-extglob/index.js +var require_is_extglob = __commonJS((exports2, module2) => { + /*! + * is-extglob + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + */ + module2.exports = function isExtglob(str) { + if (typeof str !== "string" || str === "") { + return false; + } + var match; + while (match = /(\\).|([@?!+*]\(.*\))/g.exec(str)) { + if (match[2]) + return true; + str = str.slice(match.index + match[0].length); + } + return false; + }; +}); + +// ../../node_modules/is-glob/index.js +var require_is_glob = __commonJS((exports2, module2) => { + /*! + * is-glob + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + var isExtglob = require_is_extglob(); + var chars = {"{": "}", "(": ")", "[": "]"}; + var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; + var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; + module2.exports = function isGlob(str, options) { + if (typeof str !== "string" || str === "") { + return false; + } + if (isExtglob(str)) { + return true; + } + var regex = strictRegex; + var match; + if (options && options.strict === false) { + regex = relaxedRegex; + } + while (match = regex.exec(str)) { + if (match[2]) + return true; + var idx = match.index + match[0].length; + var open = match[1]; + var close = open ? chars[open] : null; + if (open && close) { + var n = str.indexOf(close, idx); + if (n !== -1) { + idx = n + 1; + } + } + str = str.slice(idx); + } + return false; + }; +}); + +// ../../node_modules/glob-parent/index.js +var require_glob_parent = __commonJS((exports2, module2) => { + "use strict"; + var isGlob = require_is_glob(); + var pathPosixDirname = require("path").posix.dirname; + var isWin32 = require("os").platform() === "win32"; + var slash = "/"; + var backslash = /\\/g; + var enclosure = /[\{\[].*[\}\]]$/; + var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; + var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; + module2.exports = function globParent(str, opts) { + var options = Object.assign({flipBackslashes: true}, opts); + if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { + str = str.replace(backslash, slash); + } + if (enclosure.test(str)) { + str += slash; + } + str += "a"; + do { + str = pathPosixDirname(str); + } while (isGlob(str) || globby.test(str)); + return str.replace(escaped, "$1"); + }; +}); + +// ../../node_modules/chokidar/node_modules/braces/lib/utils.js +var require_utils2 = __commonJS((exports2) => { + "use strict"; + exports2.isInteger = (num) => { + if (typeof num === "number") { + return Number.isInteger(num); + } + if (typeof num === "string" && num.trim() !== "") { + return Number.isInteger(Number(num)); + } + return false; + }; + exports2.find = (node, type) => node.nodes.find((node2) => node2.type === type); + exports2.exceedsLimit = (min, max, step = 1, limit) => { + if (limit === false) + return false; + if (!exports2.isInteger(min) || !exports2.isInteger(max)) + return false; + return (Number(max) - Number(min)) / Number(step) >= limit; + }; + exports2.escapeNode = (block, n = 0, type) => { + let node = block.nodes[n]; + if (!node) + return; + if (type && node.type === type || node.type === "open" || node.type === "close") { + if (node.escaped !== true) { + node.value = "\\" + node.value; + node.escaped = true; + } + } + }; + exports2.encloseBrace = (node) => { + if (node.type !== "brace") + return false; + if (node.commas >> 0 + node.ranges >> 0 === 0) { + node.invalid = true; + return true; + } + return false; + }; + exports2.isInvalidBrace = (block) => { + if (block.type !== "brace") + return false; + if (block.invalid === true || block.dollar) + return true; + if (block.commas >> 0 + block.ranges >> 0 === 0) { + block.invalid = true; + return true; + } + if (block.open !== true || block.close !== true) { + block.invalid = true; + return true; + } + return false; + }; + exports2.isOpenOrClose = (node) => { + if (node.type === "open" || node.type === "close") { + return true; + } + return node.open === true || node.close === true; + }; + exports2.reduce = (nodes2) => nodes2.reduce((acc, node) => { + if (node.type === "text") + acc.push(node.value); + if (node.type === "range") + node.type = "text"; + return acc; + }, []); + exports2.flatten = (...args) => { + const result = []; + const flat = (arr) => { + for (let i = 0; i < arr.length; i++) { + let ele = arr[i]; + Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele); + } + return result; + }; + flat(args); + return result; + }; +}); + +// ../../node_modules/chokidar/node_modules/braces/lib/stringify.js +var require_stringify = __commonJS((exports2, module2) => { + "use strict"; + var utils = require_utils2(); + module2.exports = (ast, options = {}) => { + let stringify = (node, parent = {}) => { + let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let output = ""; + if (node.value) { + if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) { + return "\\" + node.value; + } + return node.value; + } + if (node.value) { + return node.value; + } + if (node.nodes) { + for (let child of node.nodes) { + output += stringify(child); + } + } + return output; + }; + return stringify(ast); + }; +}); + +// ../../node_modules/chokidar/node_modules/is-number/index.js +var require_is_number = __commonJS((exports2, module2) => { + /*! + * is-number + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + */ + "use strict"; + module2.exports = function(num) { + if (typeof num === "number") { + return num - num === 0; + } + if (typeof num === "string" && num.trim() !== "") { + return Number.isFinite ? Number.isFinite(+num) : isFinite(+num); + } + return false; + }; +}); + +// ../../node_modules/chokidar/node_modules/to-regex-range/index.js +var require_to_regex_range = __commonJS((exports2, module2) => { + /*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + */ + "use strict"; + var isNumber = require_is_number(); + var toRegexRange = (min, max, options) => { + if (isNumber(min) === false) { + throw new TypeError("toRegexRange: expected the first argument to be a number"); + } + if (max === void 0 || min === max) { + return String(min); + } + if (isNumber(max) === false) { + throw new TypeError("toRegexRange: expected the second argument to be a number."); + } + let opts = {relaxZeros: true, ...options}; + if (typeof opts.strictZeros === "boolean") { + opts.relaxZeros = opts.strictZeros === false; + } + let relax = String(opts.relaxZeros); + let shorthand = String(opts.shorthand); + let capture = String(opts.capture); + let wrap = String(opts.wrap); + let cacheKey = min + ":" + max + "=" + relax + shorthand + capture + wrap; + if (toRegexRange.cache.hasOwnProperty(cacheKey)) { + return toRegexRange.cache[cacheKey].result; + } + let a = Math.min(min, max); + let b = Math.max(min, max); + if (Math.abs(a - b) === 1) { + let result = min + "|" + max; + if (opts.capture) { + return `(${result})`; + } + if (opts.wrap === false) { + return result; + } + return `(?:${result})`; + } + let isPadded = hasPadding(min) || hasPadding(max); + let state = {min, max, a, b}; + let positives = []; + let negatives = []; + if (isPadded) { + state.isPadded = isPadded; + state.maxLen = String(state.max).length; + } + if (a < 0) { + let newMin = b < 0 ? Math.abs(b) : 1; + negatives = splitToPatterns(newMin, Math.abs(a), state, opts); + a = state.a = 0; + } + if (b >= 0) { + positives = splitToPatterns(a, b, state, opts); + } + state.negatives = negatives; + state.positives = positives; + state.result = collatePatterns(negatives, positives, opts); + if (opts.capture === true) { + state.result = `(${state.result})`; + } else if (opts.wrap !== false && positives.length + negatives.length > 1) { + state.result = `(?:${state.result})`; + } + toRegexRange.cache[cacheKey] = state; + return state.result; + }; + function collatePatterns(neg, pos, options) { + let onlyNegative = filterPatterns(neg, pos, "-", false, options) || []; + let onlyPositive = filterPatterns(pos, neg, "", false, options) || []; + let intersected = filterPatterns(neg, pos, "-?", true, options) || []; + let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); + return subpatterns.join("|"); + } + function splitToRanges(min, max) { + let nines = 1; + let zeros = 1; + let stop = countNines(min, nines); + let stops = new Set([max]); + while (min <= stop && stop <= max) { + stops.add(stop); + nines += 1; + stop = countNines(min, nines); + } + stop = countZeros(max + 1, zeros) - 1; + while (min < stop && stop <= max) { + stops.add(stop); + zeros += 1; + stop = countZeros(max + 1, zeros) - 1; + } + stops = [...stops]; + stops.sort(compare); + return stops; + } + function rangeToPattern(start, stop, options) { + if (start === stop) { + return {pattern: start, count: [], digits: 0}; + } + let zipped = zip(start, stop); + let digits = zipped.length; + let pattern = ""; + let count = 0; + for (let i = 0; i < digits; i++) { + let [startDigit, stopDigit] = zipped[i]; + if (startDigit === stopDigit) { + pattern += startDigit; + } else if (startDigit !== "0" || stopDigit !== "9") { + pattern += toCharacterClass(startDigit, stopDigit, options); + } else { + count++; + } + } + if (count) { + pattern += options.shorthand === true ? "\\d" : "[0-9]"; + } + return {pattern, count: [count], digits}; + } + function splitToPatterns(min, max, tok, options) { + let ranges = splitToRanges(min, max); + let tokens = []; + let start = min; + let prev; + for (let i = 0; i < ranges.length; i++) { + let max2 = ranges[i]; + let obj = rangeToPattern(String(start), String(max2), options); + let zeros = ""; + if (!tok.isPadded && prev && prev.pattern === obj.pattern) { + if (prev.count.length > 1) { + prev.count.pop(); + } + prev.count.push(obj.count[0]); + prev.string = prev.pattern + toQuantifier(prev.count); + start = max2 + 1; + continue; + } + if (tok.isPadded) { + zeros = padZeros(max2, tok, options); + } + obj.string = zeros + obj.pattern + toQuantifier(obj.count); + tokens.push(obj); + start = max2 + 1; + prev = obj; + } + return tokens; + } + function filterPatterns(arr, comparison, prefix, intersection, options) { + let result = []; + for (let ele of arr) { + let {string} = ele; + if (!intersection && !contains(comparison, "string", string)) { + result.push(prefix + string); + } + if (intersection && contains(comparison, "string", string)) { + result.push(prefix + string); + } + } + return result; + } + function zip(a, b) { + let arr = []; + for (let i = 0; i < a.length; i++) + arr.push([a[i], b[i]]); + return arr; + } + function compare(a, b) { + return a > b ? 1 : b > a ? -1 : 0; + } + function contains(arr, key, val) { + return arr.some((ele) => ele[key] === val); + } + function countNines(min, len) { + return Number(String(min).slice(0, -len) + "9".repeat(len)); + } + function countZeros(integer, zeros) { + return integer - integer % Math.pow(10, zeros); + } + function toQuantifier(digits) { + let [start = 0, stop = ""] = digits; + if (stop || start > 1) { + return `{${start + (stop ? "," + stop : "")}}`; + } + return ""; + } + function toCharacterClass(a, b, options) { + return `[${a}${b - a === 1 ? "" : "-"}${b}]`; + } + function hasPadding(str) { + return /^-?(0+)\d/.test(str); + } + function padZeros(value, tok, options) { + if (!tok.isPadded) { + return value; + } + let diff = Math.abs(tok.maxLen - String(value).length); + let relax = options.relaxZeros !== false; + switch (diff) { + case 0: + return ""; + case 1: + return relax ? "0?" : "0"; + case 2: + return relax ? "0{0,2}" : "00"; + default: { + return relax ? `0{0,${diff}}` : `0{${diff}}`; + } + } + } + toRegexRange.cache = {}; + toRegexRange.clearCache = () => toRegexRange.cache = {}; + module2.exports = toRegexRange; +}); + +// ../../node_modules/chokidar/node_modules/fill-range/index.js +var require_fill_range = __commonJS((exports2, module2) => { + /*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + */ + "use strict"; + var util = require("util"); + var toRegexRange = require_to_regex_range(); + var isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val); + var transform = (toNumber) => { + return (value) => toNumber === true ? Number(value) : String(value); + }; + var isValidValue = (value) => { + return typeof value === "number" || typeof value === "string" && value !== ""; + }; + var isNumber = (num) => Number.isInteger(+num); + var zeros = (input) => { + let value = `${input}`; + let index = -1; + if (value[0] === "-") + value = value.slice(1); + if (value === "0") + return false; + while (value[++index] === "0") + ; + return index > 0; + }; + var stringify = (start, end, options) => { + if (typeof start === "string" || typeof end === "string") { + return true; + } + return options.stringify === true; + }; + var pad = (input, maxLength, toNumber) => { + if (maxLength > 0) { + let dash = input[0] === "-" ? "-" : ""; + if (dash) + input = input.slice(1); + input = dash + input.padStart(dash ? maxLength - 1 : maxLength, "0"); + } + if (toNumber === false) { + return String(input); + } + return input; + }; + var toMaxLen = (input, maxLength) => { + let negative = input[0] === "-" ? "-" : ""; + if (negative) { + input = input.slice(1); + maxLength--; + } + while (input.length < maxLength) + input = "0" + input; + return negative ? "-" + input : input; + }; + var toSequence = (parts, options) => { + parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + let prefix = options.capture ? "" : "?:"; + let positives = ""; + let negatives = ""; + let result; + if (parts.positives.length) { + positives = parts.positives.join("|"); + } + if (parts.negatives.length) { + negatives = `-(${prefix}${parts.negatives.join("|")})`; + } + if (positives && negatives) { + result = `${positives}|${negatives}`; + } else { + result = positives || negatives; + } + if (options.wrap) { + return `(${prefix}${result})`; + } + return result; + }; + var toRange = (a, b, isNumbers, options) => { + if (isNumbers) { + return toRegexRange(a, b, {wrap: false, ...options}); + } + let start = String.fromCharCode(a); + if (a === b) + return start; + let stop = String.fromCharCode(b); + return `[${start}-${stop}]`; + }; + var toRegex = (start, end, options) => { + if (Array.isArray(start)) { + let wrap = options.wrap === true; + let prefix = options.capture ? "" : "?:"; + return wrap ? `(${prefix}${start.join("|")})` : start.join("|"); + } + return toRegexRange(start, end, options); + }; + var rangeError = (...args) => { + return new RangeError("Invalid range arguments: " + util.inspect(...args)); + }; + var invalidRange = (start, end, options) => { + if (options.strictRanges === true) + throw rangeError([start, end]); + return []; + }; + var invalidStep = (step, options) => { + if (options.strictRanges === true) { + throw new TypeError(`Expected step "${step}" to be a number`); + } + return []; + }; + var fillNumbers = (start, end, step = 1, options = {}) => { + let a = Number(start); + let b = Number(end); + if (!Number.isInteger(a) || !Number.isInteger(b)) { + if (options.strictRanges === true) + throw rangeError([start, end]); + return []; + } + if (a === 0) + a = 0; + if (b === 0) + b = 0; + let descending = a > b; + let startString = String(start); + let endString = String(end); + let stepString = String(step); + step = Math.max(Math.abs(step), 1); + let padded = zeros(startString) || zeros(endString) || zeros(stepString); + let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; + let toNumber = padded === false && stringify(start, end, options) === false; + let format = options.transform || transform(toNumber); + if (options.toRegex && step === 1) { + return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); + } + let parts = {negatives: [], positives: []}; + let push = (num) => parts[num < 0 ? "negatives" : "positives"].push(Math.abs(num)); + let range = []; + let index = 0; + while (descending ? a >= b : a <= b) { + if (options.toRegex === true && step > 1) { + push(a); + } else { + range.push(pad(format(a, index), maxLen, toNumber)); + } + a = descending ? a - step : a + step; + index++; + } + if (options.toRegex === true) { + return step > 1 ? toSequence(parts, options) : toRegex(range, null, {wrap: false, ...options}); + } + return range; + }; + var fillLetters = (start, end, step = 1, options = {}) => { + if (!isNumber(start) && start.length > 1 || !isNumber(end) && end.length > 1) { + return invalidRange(start, end, options); + } + let format = options.transform || ((val) => String.fromCharCode(val)); + let a = `${start}`.charCodeAt(0); + let b = `${end}`.charCodeAt(0); + let descending = a > b; + let min = Math.min(a, b); + let max = Math.max(a, b); + if (options.toRegex && step === 1) { + return toRange(min, max, false, options); + } + let range = []; + let index = 0; + while (descending ? a >= b : a <= b) { + range.push(format(a, index)); + a = descending ? a - step : a + step; + index++; + } + if (options.toRegex === true) { + return toRegex(range, null, {wrap: false, options}); + } + return range; + }; + var fill = (start, end, step, options = {}) => { + if (end == null && isValidValue(start)) { + return [start]; + } + if (!isValidValue(start) || !isValidValue(end)) { + return invalidRange(start, end, options); + } + if (typeof step === "function") { + return fill(start, end, 1, {transform: step}); + } + if (isObject(step)) { + return fill(start, end, 0, step); + } + let opts = {...options}; + if (opts.capture === true) + opts.wrap = true; + step = step || opts.step || 1; + if (!isNumber(step)) { + if (step != null && !isObject(step)) + return invalidStep(step, opts); + return fill(start, end, 1, step); + } + if (isNumber(start) && isNumber(end)) { + return fillNumbers(start, end, step, opts); + } + return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); + }; + module2.exports = fill; +}); + +// ../../node_modules/chokidar/node_modules/braces/lib/compile.js +var require_compile = __commonJS((exports2, module2) => { + "use strict"; + var fill = require_fill_range(); + var utils = require_utils2(); + var compile2 = (ast, options = {}) => { + let walk = (node, parent = {}) => { + let invalidBlock = utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let invalid = invalidBlock === true || invalidNode === true; + let prefix = options.escapeInvalid === true ? "\\" : ""; + let output = ""; + if (node.isOpen === true) { + return prefix + node.value; + } + if (node.isClose === true) { + return prefix + node.value; + } + if (node.type === "open") { + return invalid ? prefix + node.value : "("; + } + if (node.type === "close") { + return invalid ? prefix + node.value : ")"; + } + if (node.type === "comma") { + return node.prev.type === "comma" ? "" : invalid ? node.value : "|"; + } + if (node.value) { + return node.value; + } + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); + let range = fill(...args, {...options, wrap: false, toRegex: true}); + if (range.length !== 0) { + return args.length > 1 && range.length > 1 ? `(${range})` : range; + } + } + if (node.nodes) { + for (let child of node.nodes) { + output += walk(child, node); + } + } + return output; + }; + return walk(ast); + }; + module2.exports = compile2; +}); + +// ../../node_modules/chokidar/node_modules/braces/lib/expand.js +var require_expand = __commonJS((exports2, module2) => { + "use strict"; + var fill = require_fill_range(); + var stringify = require_stringify(); + var utils = require_utils2(); + var append = (queue = "", stash = "", enclose = false) => { + let result = []; + queue = [].concat(queue); + stash = [].concat(stash); + if (!stash.length) + return queue; + if (!queue.length) { + return enclose ? utils.flatten(stash).map((ele) => `{${ele}}`) : stash; + } + for (let item of queue) { + if (Array.isArray(item)) { + for (let value of item) { + result.push(append(value, stash, enclose)); + } + } else { + for (let ele of stash) { + if (enclose === true && typeof ele === "string") + ele = `{${ele}}`; + result.push(Array.isArray(ele) ? append(item, ele, enclose) : item + ele); + } + } + } + return utils.flatten(result); + }; + var expand = (ast, options = {}) => { + let rangeLimit = options.rangeLimit === void 0 ? 1e3 : options.rangeLimit; + let walk = (node, parent = {}) => { + node.queue = []; + let p = parent; + let q = parent.queue; + while (p.type !== "brace" && p.type !== "root" && p.parent) { + p = p.parent; + q = p.queue; + } + if (node.invalid || node.dollar) { + q.push(append(q.pop(), stringify(node, options))); + return; + } + if (node.type === "brace" && node.invalid !== true && node.nodes.length === 2) { + q.push(append(q.pop(), ["{}"])); + return; + } + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); + if (utils.exceedsLimit(...args, options.step, rangeLimit)) { + throw new RangeError("expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit."); + } + let range = fill(...args, options); + if (range.length === 0) { + range = stringify(node, options); + } + q.push(append(q.pop(), range)); + node.nodes = []; + return; + } + let enclose = utils.encloseBrace(node); + let queue = node.queue; + let block = node; + while (block.type !== "brace" && block.type !== "root" && block.parent) { + block = block.parent; + queue = block.queue; + } + for (let i = 0; i < node.nodes.length; i++) { + let child = node.nodes[i]; + if (child.type === "comma" && node.type === "brace") { + if (i === 1) + queue.push(""); + queue.push(""); + continue; + } + if (child.type === "close") { + q.push(append(q.pop(), queue, enclose)); + continue; + } + if (child.value && child.type !== "open") { + queue.push(append(queue.pop(), child.value)); + continue; + } + if (child.nodes) { + walk(child, node); + } + } + return queue; + }; + return utils.flatten(walk(ast)); + }; + module2.exports = expand; +}); + +// ../../node_modules/chokidar/node_modules/braces/lib/constants.js +var require_constants2 = __commonJS((exports2, module2) => { + "use strict"; + module2.exports = { + MAX_LENGTH: 1024 * 64, + CHAR_0: "0", + CHAR_9: "9", + CHAR_UPPERCASE_A: "A", + CHAR_LOWERCASE_A: "a", + CHAR_UPPERCASE_Z: "Z", + CHAR_LOWERCASE_Z: "z", + CHAR_LEFT_PARENTHESES: "(", + CHAR_RIGHT_PARENTHESES: ")", + CHAR_ASTERISK: "*", + CHAR_AMPERSAND: "&", + CHAR_AT: "@", + CHAR_BACKSLASH: "\\", + CHAR_BACKTICK: "`", + CHAR_CARRIAGE_RETURN: "\r", + CHAR_CIRCUMFLEX_ACCENT: "^", + CHAR_COLON: ":", + CHAR_COMMA: ",", + CHAR_DOLLAR: "$", + CHAR_DOT: ".", + CHAR_DOUBLE_QUOTE: '"', + CHAR_EQUAL: "=", + CHAR_EXCLAMATION_MARK: "!", + CHAR_FORM_FEED: "\f", + CHAR_FORWARD_SLASH: "/", + CHAR_HASH: "#", + CHAR_HYPHEN_MINUS: "-", + CHAR_LEFT_ANGLE_BRACKET: "<", + CHAR_LEFT_CURLY_BRACE: "{", + CHAR_LEFT_SQUARE_BRACKET: "[", + CHAR_LINE_FEED: "\n", + CHAR_NO_BREAK_SPACE: "\xA0", + CHAR_PERCENT: "%", + CHAR_PLUS: "+", + CHAR_QUESTION_MARK: "?", + CHAR_RIGHT_ANGLE_BRACKET: ">", + CHAR_RIGHT_CURLY_BRACE: "}", + CHAR_RIGHT_SQUARE_BRACKET: "]", + CHAR_SEMICOLON: ";", + CHAR_SINGLE_QUOTE: "'", + CHAR_SPACE: " ", + CHAR_TAB: " ", + CHAR_UNDERSCORE: "_", + CHAR_VERTICAL_LINE: "|", + CHAR_ZERO_WIDTH_NOBREAK_SPACE: "\uFEFF" + }; +}); + +// ../../node_modules/chokidar/node_modules/braces/lib/parse.js +var require_parse2 = __commonJS((exports2, module2) => { + "use strict"; + var stringify = require_stringify(); + var { + MAX_LENGTH, + CHAR_BACKSLASH, + CHAR_BACKTICK, + CHAR_COMMA, + CHAR_DOT, + CHAR_LEFT_PARENTHESES, + CHAR_RIGHT_PARENTHESES, + CHAR_LEFT_CURLY_BRACE, + CHAR_RIGHT_CURLY_BRACE, + CHAR_LEFT_SQUARE_BRACKET, + CHAR_RIGHT_SQUARE_BRACKET, + CHAR_DOUBLE_QUOTE, + CHAR_SINGLE_QUOTE, + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE + } = require_constants2(); + var parse = (input, options = {}) => { + if (typeof input !== "string") { + throw new TypeError("Expected a string"); + } + let opts = options || {}; + let max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + if (input.length > max) { + throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`); + } + let ast = {type: "root", input, nodes: []}; + let stack = [ast]; + let block = ast; + let prev = ast; + let brackets = 0; + let length = input.length; + let index = 0; + let depth = 0; + let value; + let memo = {}; + const advance = () => input[index++]; + const push = (node) => { + if (node.type === "text" && prev.type === "dot") { + prev.type = "text"; + } + if (prev && prev.type === "text" && node.type === "text") { + prev.value += node.value; + return; + } + block.nodes.push(node); + node.parent = block; + node.prev = prev; + prev = node; + return node; + }; + push({type: "bos"}); + while (index < length) { + block = stack[stack.length - 1]; + value = advance(); + if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) { + continue; + } + if (value === CHAR_BACKSLASH) { + push({type: "text", value: (options.keepEscaping ? value : "") + advance()}); + continue; + } + if (value === CHAR_RIGHT_SQUARE_BRACKET) { + push({type: "text", value: "\\" + value}); + continue; + } + if (value === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + let closed = true; + let next; + while (index < length && (next = advance())) { + value += next; + if (next === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + continue; + } + if (next === CHAR_BACKSLASH) { + value += advance(); + continue; + } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + brackets--; + if (brackets === 0) { + break; + } + } + } + push({type: "text", value}); + continue; + } + if (value === CHAR_LEFT_PARENTHESES) { + block = push({type: "paren", nodes: []}); + stack.push(block); + push({type: "text", value}); + continue; + } + if (value === CHAR_RIGHT_PARENTHESES) { + if (block.type !== "paren") { + push({type: "text", value}); + continue; + } + block = stack.pop(); + push({type: "text", value}); + block = stack[stack.length - 1]; + continue; + } + if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) { + let open = value; + let next; + if (options.keepQuotes !== true) { + value = ""; + } + while (index < length && (next = advance())) { + if (next === CHAR_BACKSLASH) { + value += next + advance(); + continue; + } + if (next === open) { + if (options.keepQuotes === true) + value += next; + break; + } + value += next; + } + push({type: "text", value}); + continue; + } + if (value === CHAR_LEFT_CURLY_BRACE) { + depth++; + let dollar = prev.value && prev.value.slice(-1) === "$" || block.dollar === true; + let brace = { + type: "brace", + open: true, + close: false, + dollar, + depth, + commas: 0, + ranges: 0, + nodes: [] + }; + block = push(brace); + stack.push(block); + push({type: "open", value}); + continue; + } + if (value === CHAR_RIGHT_CURLY_BRACE) { + if (block.type !== "brace") { + push({type: "text", value}); + continue; + } + let type = "close"; + block = stack.pop(); + block.close = true; + push({type, value}); + depth--; + block = stack[stack.length - 1]; + continue; + } + if (value === CHAR_COMMA && depth > 0) { + if (block.ranges > 0) { + block.ranges = 0; + let open = block.nodes.shift(); + block.nodes = [open, {type: "text", value: stringify(block)}]; + } + push({type: "comma", value}); + block.commas++; + continue; + } + if (value === CHAR_DOT && depth > 0 && block.commas === 0) { + let siblings = block.nodes; + if (depth === 0 || siblings.length === 0) { + push({type: "text", value}); + continue; + } + if (prev.type === "dot") { + block.range = []; + prev.value += value; + prev.type = "range"; + if (block.nodes.length !== 3 && block.nodes.length !== 5) { + block.invalid = true; + block.ranges = 0; + prev.type = "text"; + continue; + } + block.ranges++; + block.args = []; + continue; + } + if (prev.type === "range") { + siblings.pop(); + let before = siblings[siblings.length - 1]; + before.value += prev.value + value; + prev = before; + block.ranges--; + continue; + } + push({type: "dot", value}); + continue; + } + push({type: "text", value}); + } + do { + block = stack.pop(); + if (block.type !== "root") { + block.nodes.forEach((node) => { + if (!node.nodes) { + if (node.type === "open") + node.isOpen = true; + if (node.type === "close") + node.isClose = true; + if (!node.nodes) + node.type = "text"; + node.invalid = true; + } + }); + let parent = stack[stack.length - 1]; + let index2 = parent.nodes.indexOf(block); + parent.nodes.splice(index2, 1, ...block.nodes); + } + } while (stack.length > 0); + push({type: "eos"}); + return ast; + }; + module2.exports = parse; +}); + +// ../../node_modules/chokidar/node_modules/braces/index.js +var require_braces = __commonJS((exports2, module2) => { + "use strict"; + var stringify = require_stringify(); + var compile2 = require_compile(); + var expand = require_expand(); + var parse = require_parse2(); + var braces = (input, options = {}) => { + let output = []; + if (Array.isArray(input)) { + for (let pattern of input) { + let result = braces.create(pattern, options); + if (Array.isArray(result)) { + output.push(...result); + } else { + output.push(result); + } + } + } else { + output = [].concat(braces.create(input, options)); + } + if (options && options.expand === true && options.nodupes === true) { + output = [...new Set(output)]; + } + return output; + }; + braces.parse = (input, options = {}) => parse(input, options); + braces.stringify = (input, options = {}) => { + if (typeof input === "string") { + return stringify(braces.parse(input, options), options); + } + return stringify(input, options); + }; + braces.compile = (input, options = {}) => { + if (typeof input === "string") { + input = braces.parse(input, options); + } + return compile2(input, options); + }; + braces.expand = (input, options = {}) => { + if (typeof input === "string") { + input = braces.parse(input, options); + } + let result = expand(input, options); + if (options.noempty === true) { + result = result.filter(Boolean); + } + if (options.nodupes === true) { + result = [...new Set(result)]; + } + return result; + }; + braces.create = (input, options = {}) => { + if (input === "" || input.length < 3) { + return [input]; + } + return options.expand !== true ? braces.compile(input, options) : braces.expand(input, options); + }; + module2.exports = braces; +}); + +// ../../node_modules/binary-extensions/binary-extensions.json +var require_binary_extensions = __commonJS((exports2, module2) => { + module2.exports = [ + "3dm", + "3ds", + "3g2", + "3gp", + "7z", + "a", + "aac", + "adp", + "ai", + "aif", + "aiff", + "alz", + "ape", + "apk", + "ar", + "arj", + "asf", + "au", + "avi", + "bak", + "baml", + "bh", + "bin", + "bk", + "bmp", + "btif", + "bz2", + "bzip2", + "cab", + "caf", + "cgm", + "class", + "cmx", + "cpio", + "cr2", + "cur", + "dat", + "dcm", + "deb", + "dex", + "djvu", + "dll", + "dmg", + "dng", + "doc", + "docm", + "docx", + "dot", + "dotm", + "dra", + "DS_Store", + "dsk", + "dts", + "dtshd", + "dvb", + "dwg", + "dxf", + "ecelp4800", + "ecelp7470", + "ecelp9600", + "egg", + "eol", + "eot", + "epub", + "exe", + "f4v", + "fbs", + "fh", + "fla", + "flac", + "fli", + "flv", + "fpx", + "fst", + "fvt", + "g3", + "gh", + "gif", + "graffle", + "gz", + "gzip", + "h261", + "h263", + "h264", + "icns", + "ico", + "ief", + "img", + "ipa", + "iso", + "jar", + "jpeg", + "jpg", + "jpgv", + "jpm", + "jxr", + "key", + "ktx", + "lha", + "lib", + "lvp", + "lz", + "lzh", + "lzma", + "lzo", + "m3u", + "m4a", + "m4v", + "mar", + "mdi", + "mht", + "mid", + "midi", + "mj2", + "mka", + "mkv", + "mmr", + "mng", + "mobi", + "mov", + "movie", + "mp3", + "mp4", + "mp4a", + "mpeg", + "mpg", + "mpga", + "mxu", + "nef", + "npx", + "numbers", + "nupkg", + "o", + "oga", + "ogg", + "ogv", + "otf", + "pages", + "pbm", + "pcx", + "pdb", + "pdf", + "pea", + "pgm", + "pic", + "png", + "pnm", + "pot", + "potm", + "potx", + "ppa", + "ppam", + "ppm", + "pps", + "ppsm", + "ppsx", + "ppt", + "pptm", + "pptx", + "psd", + "pya", + "pyc", + "pyo", + "pyv", + "qt", + "rar", + "ras", + "raw", + "resources", + "rgb", + "rip", + "rlc", + "rmf", + "rmvb", + "rtf", + "rz", + "s3m", + "s7z", + "scpt", + "sgi", + "shar", + "sil", + "sketch", + "slk", + "smv", + "snk", + "so", + "stl", + "suo", + "sub", + "swf", + "tar", + "tbz", + "tbz2", + "tga", + "tgz", + "thmx", + "tif", + "tiff", + "tlz", + "ttc", + "ttf", + "txz", + "udf", + "uvh", + "uvi", + "uvm", + "uvp", + "uvs", + "uvu", + "viv", + "vob", + "war", + "wav", + "wax", + "wbmp", + "wdp", + "weba", + "webm", + "webp", + "whl", + "wim", + "wm", + "wma", + "wmv", + "wmx", + "woff", + "woff2", + "wrm", + "wvx", + "xbm", + "xif", + "xla", + "xlam", + "xls", + "xlsb", + "xlsm", + "xlsx", + "xlt", + "xltm", + "xltx", + "xm", + "xmind", + "xpi", + "xpm", + "xwd", + "xz", + "z", + "zip", + "zipx" + ]; +}); + +// ../../node_modules/binary-extensions/index.js +var require_binary_extensions2 = __commonJS((exports2, module2) => { + module2.exports = require_binary_extensions(); +}); + +// ../../node_modules/is-binary-path/index.js +var require_is_binary_path = __commonJS((exports2, module2) => { + "use strict"; + var path = require("path"); + var binaryExtensions = require_binary_extensions2(); + var extensions = new Set(binaryExtensions); + module2.exports = (filePath) => extensions.has(path.extname(filePath).slice(1).toLowerCase()); +}); + +// ../../node_modules/chokidar/lib/constants.js +var require_constants3 = __commonJS((exports2) => { + "use strict"; + var {sep} = require("path"); + var {platform} = process; + var os = require("os"); + exports2.EV_ALL = "all"; + exports2.EV_READY = "ready"; + exports2.EV_ADD = "add"; + exports2.EV_CHANGE = "change"; + exports2.EV_ADD_DIR = "addDir"; + exports2.EV_UNLINK = "unlink"; + exports2.EV_UNLINK_DIR = "unlinkDir"; + exports2.EV_RAW = "raw"; + exports2.EV_ERROR = "error"; + exports2.STR_DATA = "data"; + exports2.STR_END = "end"; + exports2.STR_CLOSE = "close"; + exports2.FSEVENT_CREATED = "created"; + exports2.FSEVENT_MODIFIED = "modified"; + exports2.FSEVENT_DELETED = "deleted"; + exports2.FSEVENT_MOVED = "moved"; + exports2.FSEVENT_CLONED = "cloned"; + exports2.FSEVENT_UNKNOWN = "unknown"; + exports2.FSEVENT_TYPE_FILE = "file"; + exports2.FSEVENT_TYPE_DIRECTORY = "directory"; + exports2.FSEVENT_TYPE_SYMLINK = "symlink"; + exports2.KEY_LISTENERS = "listeners"; + exports2.KEY_ERR = "errHandlers"; + exports2.KEY_RAW = "rawEmitters"; + exports2.HANDLER_KEYS = [exports2.KEY_LISTENERS, exports2.KEY_ERR, exports2.KEY_RAW]; + exports2.DOT_SLASH = `.${sep}`; + exports2.BACK_SLASH_RE = /\\/g; + exports2.DOUBLE_SLASH_RE = /\/\//; + exports2.SLASH_OR_BACK_SLASH_RE = /[/\\]/; + exports2.DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/; + exports2.REPLACER_RE = /^\.[/\\]/; + exports2.SLASH = "/"; + exports2.SLASH_SLASH = "//"; + exports2.BRACE_START = "{"; + exports2.BANG = "!"; + exports2.ONE_DOT = "."; + exports2.TWO_DOTS = ".."; + exports2.STAR = "*"; + exports2.GLOBSTAR = "**"; + exports2.ROOT_GLOBSTAR = "/**/*"; + exports2.SLASH_GLOBSTAR = "/**"; + exports2.DIR_SUFFIX = "Dir"; + exports2.ANYMATCH_OPTS = {dot: true}; + exports2.STRING_TYPE = "string"; + exports2.FUNCTION_TYPE = "function"; + exports2.EMPTY_STR = ""; + exports2.EMPTY_FN = () => { + }; + exports2.IDENTITY_FN = (val) => val; + exports2.isWindows = platform === "win32"; + exports2.isMacos = platform === "darwin"; + exports2.isLinux = platform === "linux"; + exports2.isIBMi = os.type() === "OS400"; +}); + +// ../../node_modules/chokidar/lib/nodefs-handler.js +var require_nodefs_handler = __commonJS((exports2, module2) => { + "use strict"; + var fs = require("fs"); + var sysPath = require("path"); + var {promisify} = require("util"); + var isBinaryPath = require_is_binary_path(); + var { + isWindows, + isLinux, + EMPTY_FN, + EMPTY_STR, + KEY_LISTENERS, + KEY_ERR, + KEY_RAW, + HANDLER_KEYS, + EV_CHANGE, + EV_ADD, + EV_ADD_DIR, + EV_ERROR, + STR_DATA, + STR_END, + BRACE_START, + STAR + } = require_constants3(); + var THROTTLE_MODE_WATCH = "watch"; + var open = promisify(fs.open); + var stat = promisify(fs.stat); + var lstat = promisify(fs.lstat); + var close = promisify(fs.close); + var fsrealpath = promisify(fs.realpath); + var statMethods = {lstat, stat}; + var foreach = (val, fn) => { + if (val instanceof Set) { + val.forEach(fn); + } else { + fn(val); + } + }; + var addAndConvert = (main, prop, item) => { + let container = main[prop]; + if (!(container instanceof Set)) { + main[prop] = container = new Set([container]); + } + container.add(item); + }; + var clearItem = (cont) => (key) => { + const set = cont[key]; + if (set instanceof Set) { + set.clear(); + } else { + delete cont[key]; + } + }; + var delFromSet = (main, prop, item) => { + const container = main[prop]; + if (container instanceof Set) { + container.delete(item); + } else if (container === item) { + delete main[prop]; + } + }; + var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val; + var FsWatchInstances = new Map(); + function createFsWatchInstance(path, options, listener, errHandler, emitRaw) { + const handleEvent = (rawEvent, evPath) => { + listener(path); + emitRaw(rawEvent, evPath, {watchedPath: path}); + if (evPath && path !== evPath) { + fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath)); + } + }; + try { + return fs.watch(path, options, handleEvent); + } catch (error) { + errHandler(error); + } + } + var fsWatchBroadcast = (fullPath, type, val1, val2, val3) => { + const cont = FsWatchInstances.get(fullPath); + if (!cont) + return; + foreach(cont[type], (listener) => { + listener(val1, val2, val3); + }); + }; + var setFsWatchListener = (path, fullPath, options, handlers) => { + const {listener, errHandler, rawEmitter} = handlers; + let cont = FsWatchInstances.get(fullPath); + let watcher; + if (!options.persistent) { + watcher = createFsWatchInstance(path, options, listener, errHandler, rawEmitter); + return watcher.close.bind(watcher); + } + if (cont) { + addAndConvert(cont, KEY_LISTENERS, listener); + addAndConvert(cont, KEY_ERR, errHandler); + addAndConvert(cont, KEY_RAW, rawEmitter); + } else { + watcher = createFsWatchInstance(path, options, fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS), errHandler, fsWatchBroadcast.bind(null, fullPath, KEY_RAW)); + if (!watcher) + return; + watcher.on(EV_ERROR, async (error) => { + const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR); + cont.watcherUnusable = true; + if (isWindows && error.code === "EPERM") { + try { + const fd = await open(path, "r"); + await close(fd); + broadcastErr(error); + } catch (err) { + } + } else { + broadcastErr(error); + } + }); + cont = { + listeners: listener, + errHandlers: errHandler, + rawEmitters: rawEmitter, + watcher + }; + FsWatchInstances.set(fullPath, cont); + } + return () => { + delFromSet(cont, KEY_LISTENERS, listener); + delFromSet(cont, KEY_ERR, errHandler); + delFromSet(cont, KEY_RAW, rawEmitter); + if (isEmptySet(cont.listeners)) { + cont.watcher.close(); + FsWatchInstances.delete(fullPath); + HANDLER_KEYS.forEach(clearItem(cont)); + cont.watcher = void 0; + Object.freeze(cont); + } + }; + }; + var FsWatchFileInstances = new Map(); + var setFsWatchFileListener = (path, fullPath, options, handlers) => { + const {listener, rawEmitter} = handlers; + let cont = FsWatchFileInstances.get(fullPath); + let listeners = new Set(); + let rawEmitters = new Set(); + const copts = cont && cont.options; + if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) { + listeners = cont.listeners; + rawEmitters = cont.rawEmitters; + fs.unwatchFile(fullPath); + cont = void 0; + } + if (cont) { + addAndConvert(cont, KEY_LISTENERS, listener); + addAndConvert(cont, KEY_RAW, rawEmitter); + } else { + cont = { + listeners: listener, + rawEmitters: rawEmitter, + options, + watcher: fs.watchFile(fullPath, options, (curr, prev) => { + foreach(cont.rawEmitters, (rawEmitter2) => { + rawEmitter2(EV_CHANGE, fullPath, {curr, prev}); + }); + const currmtime = curr.mtimeMs; + if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) { + foreach(cont.listeners, (listener2) => listener2(path, curr)); + } + }) + }; + FsWatchFileInstances.set(fullPath, cont); + } + return () => { + delFromSet(cont, KEY_LISTENERS, listener); + delFromSet(cont, KEY_RAW, rawEmitter); + if (isEmptySet(cont.listeners)) { + FsWatchFileInstances.delete(fullPath); + fs.unwatchFile(fullPath); + cont.options = cont.watcher = void 0; + Object.freeze(cont); + } + }; + }; + var NodeFsHandler = class { + constructor(fsW) { + this.fsw = fsW; + this._boundHandleError = (error) => fsW._handleError(error); + } + _watchWithNodeFs(path, listener) { + const opts = this.fsw.options; + const directory = sysPath.dirname(path); + const basename = sysPath.basename(path); + const parent = this.fsw._getWatchedDir(directory); + parent.add(basename); + const absolutePath = sysPath.resolve(path); + const options = {persistent: opts.persistent}; + if (!listener) + listener = EMPTY_FN; + let closer; + if (opts.usePolling) { + options.interval = opts.enableBinaryInterval && isBinaryPath(basename) ? opts.binaryInterval : opts.interval; + closer = setFsWatchFileListener(path, absolutePath, options, { + listener, + rawEmitter: this.fsw._emitRaw + }); + } else { + closer = setFsWatchListener(path, absolutePath, options, { + listener, + errHandler: this._boundHandleError, + rawEmitter: this.fsw._emitRaw + }); + } + return closer; + } + _handleFile(file, stats, initialAdd) { + if (this.fsw.closed) { + return; + } + const dirname = sysPath.dirname(file); + const basename = sysPath.basename(file); + const parent = this.fsw._getWatchedDir(dirname); + let prevStats = stats; + if (parent.has(basename)) + return; + const listener = async (path, newStats) => { + if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5)) + return; + if (!newStats || newStats.mtimeMs === 0) { + try { + const newStats2 = await stat(file); + if (this.fsw.closed) + return; + const at = newStats2.atimeMs; + const mt = newStats2.mtimeMs; + if (!at || at <= mt || mt !== prevStats.mtimeMs) { + this.fsw._emit(EV_CHANGE, file, newStats2); + } + if (isLinux && prevStats.ino !== newStats2.ino) { + this.fsw._closeFile(path); + prevStats = newStats2; + this.fsw._addPathCloser(path, this._watchWithNodeFs(file, listener)); + } else { + prevStats = newStats2; + } + } catch (error) { + this.fsw._remove(dirname, basename); + } + } else if (parent.has(basename)) { + const at = newStats.atimeMs; + const mt = newStats.mtimeMs; + if (!at || at <= mt || mt !== prevStats.mtimeMs) { + this.fsw._emit(EV_CHANGE, file, newStats); + } + prevStats = newStats; + } + }; + const closer = this._watchWithNodeFs(file, listener); + if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) { + if (!this.fsw._throttle(EV_ADD, file, 0)) + return; + this.fsw._emit(EV_ADD, file, stats); + } + return closer; + } + async _handleSymlink(entry, directory, path, item) { + if (this.fsw.closed) { + return; + } + const full = entry.fullPath; + const dir = this.fsw._getWatchedDir(directory); + if (!this.fsw.options.followSymlinks) { + this.fsw._incrReadyCount(); + const linkPath = await fsrealpath(path); + if (this.fsw.closed) + return; + if (dir.has(item)) { + if (this.fsw._symlinkPaths.get(full) !== linkPath) { + this.fsw._symlinkPaths.set(full, linkPath); + this.fsw._emit(EV_CHANGE, path, entry.stats); + } + } else { + dir.add(item); + this.fsw._symlinkPaths.set(full, linkPath); + this.fsw._emit(EV_ADD, path, entry.stats); + } + this.fsw._emitReady(); + return true; + } + if (this.fsw._symlinkPaths.has(full)) { + return true; + } + this.fsw._symlinkPaths.set(full, true); + } + _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) { + directory = sysPath.join(directory, EMPTY_STR); + if (!wh.hasGlob) { + throttler = this.fsw._throttle("readdir", directory, 1e3); + if (!throttler) + return; + } + const previous = this.fsw._getWatchedDir(wh.path); + const current = new Set(); + let stream = this.fsw._readdirp(directory, { + fileFilter: (entry) => wh.filterPath(entry), + directoryFilter: (entry) => wh.filterDir(entry), + depth: 0 + }).on(STR_DATA, async (entry) => { + if (this.fsw.closed) { + stream = void 0; + return; + } + const item = entry.path; + let path = sysPath.join(directory, item); + current.add(item); + if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) { + return; + } + if (this.fsw.closed) { + stream = void 0; + return; + } + if (item === target || !target && !previous.has(item)) { + this.fsw._incrReadyCount(); + path = sysPath.join(dir, sysPath.relative(dir, path)); + this._addToNodeFs(path, initialAdd, wh, depth + 1); + } + }).on(EV_ERROR, this._boundHandleError); + return new Promise((resolve) => stream.once(STR_END, () => { + if (this.fsw.closed) { + stream = void 0; + return; + } + const wasThrottled = throttler ? throttler.clear() : false; + resolve(); + previous.getChildren().filter((item) => { + return item !== directory && !current.has(item) && (!wh.hasGlob || wh.filterPath({ + fullPath: sysPath.resolve(directory, item) + })); + }).forEach((item) => { + this.fsw._remove(directory, item); + }); + stream = void 0; + if (wasThrottled) + this._handleRead(directory, false, wh, target, dir, depth, throttler); + })); + } + async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath) { + const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir)); + const tracked = parentDir.has(sysPath.basename(dir)); + if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) { + if (!wh.hasGlob || wh.globFilter(dir)) + this.fsw._emit(EV_ADD_DIR, dir, stats); + } + parentDir.add(sysPath.basename(dir)); + this.fsw._getWatchedDir(dir); + let throttler; + let closer; + const oDepth = this.fsw.options.depth; + if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath)) { + if (!target) { + await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler); + if (this.fsw.closed) + return; + } + closer = this._watchWithNodeFs(dir, (dirPath, stats2) => { + if (stats2 && stats2.mtimeMs === 0) + return; + this._handleRead(dirPath, false, wh, target, dir, depth, throttler); + }); + } + return closer; + } + async _addToNodeFs(path, initialAdd, priorWh, depth, target) { + const ready = this.fsw._emitReady; + if (this.fsw._isIgnored(path) || this.fsw.closed) { + ready(); + return false; + } + const wh = this.fsw._getWatchHelpers(path, depth); + if (!wh.hasGlob && priorWh) { + wh.hasGlob = priorWh.hasGlob; + wh.globFilter = priorWh.globFilter; + wh.filterPath = (entry) => priorWh.filterPath(entry); + wh.filterDir = (entry) => priorWh.filterDir(entry); + } + try { + const stats = await statMethods[wh.statMethod](wh.watchPath); + if (this.fsw.closed) + return; + if (this.fsw._isIgnored(wh.watchPath, stats)) { + ready(); + return false; + } + const follow = this.fsw.options.followSymlinks && !path.includes(STAR) && !path.includes(BRACE_START); + let closer; + if (stats.isDirectory()) { + const absPath = sysPath.resolve(path); + const targetPath = follow ? await fsrealpath(path) : path; + if (this.fsw.closed) + return; + closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath); + if (this.fsw.closed) + return; + if (absPath !== targetPath && targetPath !== void 0) { + this.fsw._symlinkPaths.set(absPath, targetPath); + } + } else if (stats.isSymbolicLink()) { + const targetPath = follow ? await fsrealpath(path) : path; + if (this.fsw.closed) + return; + const parent = sysPath.dirname(wh.watchPath); + this.fsw._getWatchedDir(parent).add(wh.watchPath); + this.fsw._emit(EV_ADD, wh.watchPath, stats); + closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath); + if (this.fsw.closed) + return; + if (targetPath !== void 0) { + this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath); + } + } else { + closer = this._handleFile(wh.watchPath, stats, initialAdd); + } + ready(); + this.fsw._addPathCloser(path, closer); + return false; + } catch (error) { + if (this.fsw._handleError(error)) { + ready(); + return path; + } + } + } + }; + module2.exports = NodeFsHandler; +}); + +// ../../node_modules/chokidar/lib/fsevents-handler.js +var require_fsevents_handler = __commonJS((exports2, module2) => { + "use strict"; + var fs = require("fs"); + var sysPath = require("path"); + var {promisify} = require("util"); + var fsevents; + try { + fsevents = require("fsevents"); + } catch (error) { + if (process.env.CHOKIDAR_PRINT_FSEVENTS_REQUIRE_ERROR) + console.error(error); + } + if (fsevents) { + const mtch = process.version.match(/v(\d+)\.(\d+)/); + if (mtch && mtch[1] && mtch[2]) { + const maj = Number.parseInt(mtch[1], 10); + const min = Number.parseInt(mtch[2], 10); + if (maj === 8 && min < 16) { + fsevents = void 0; + } + } + } + var { + EV_ADD, + EV_CHANGE, + EV_ADD_DIR, + EV_UNLINK, + EV_ERROR, + STR_DATA, + STR_END, + FSEVENT_CREATED, + FSEVENT_MODIFIED, + FSEVENT_DELETED, + FSEVENT_MOVED, + FSEVENT_UNKNOWN, + FSEVENT_TYPE_FILE, + FSEVENT_TYPE_DIRECTORY, + FSEVENT_TYPE_SYMLINK, + ROOT_GLOBSTAR, + DIR_SUFFIX, + DOT_SLASH, + FUNCTION_TYPE, + EMPTY_FN, + IDENTITY_FN + } = require_constants3(); + var Depth = (value) => isNaN(value) ? {} : {depth: value}; + var stat = promisify(fs.stat); + var lstat = promisify(fs.lstat); + var realpath = promisify(fs.realpath); + var statMethods = {stat, lstat}; + var FSEventsWatchers = new Map(); + var consolidateThreshhold = 10; + var wrongEventFlags = new Set([ + 69888, + 70400, + 71424, + 72704, + 73472, + 131328, + 131840, + 262912 + ]); + var createFSEventsInstance = (path, callback) => { + const stop = fsevents.watch(path, callback); + return {stop}; + }; + function setFSEventsListener(path, realPath, listener, rawEmitter) { + let watchPath = sysPath.extname(realPath) ? sysPath.dirname(realPath) : realPath; + const parentPath = sysPath.dirname(watchPath); + let cont = FSEventsWatchers.get(watchPath); + if (couldConsolidate(parentPath)) { + watchPath = parentPath; + } + const resolvedPath = sysPath.resolve(path); + const hasSymlink = resolvedPath !== realPath; + const filteredListener = (fullPath, flags, info) => { + if (hasSymlink) + fullPath = fullPath.replace(realPath, resolvedPath); + if (fullPath === resolvedPath || !fullPath.indexOf(resolvedPath + sysPath.sep)) + listener(fullPath, flags, info); + }; + let watchedParent = false; + for (const watchedPath of FSEventsWatchers.keys()) { + if (realPath.indexOf(sysPath.resolve(watchedPath) + sysPath.sep) === 0) { + watchPath = watchedPath; + cont = FSEventsWatchers.get(watchPath); + watchedParent = true; + break; + } + } + if (cont || watchedParent) { + cont.listeners.add(filteredListener); + } else { + cont = { + listeners: new Set([filteredListener]), + rawEmitter, + watcher: createFSEventsInstance(watchPath, (fullPath, flags) => { + if (!cont.listeners.size) + return; + const info = fsevents.getInfo(fullPath, flags); + cont.listeners.forEach((list) => { + list(fullPath, flags, info); + }); + cont.rawEmitter(info.event, fullPath, info); + }) + }; + FSEventsWatchers.set(watchPath, cont); + } + return () => { + const lst = cont.listeners; + lst.delete(filteredListener); + if (!lst.size) { + FSEventsWatchers.delete(watchPath); + if (cont.watcher) + return cont.watcher.stop().then(() => { + cont.rawEmitter = cont.watcher = void 0; + Object.freeze(cont); + }); + } + }; + } + var couldConsolidate = (path) => { + let count = 0; + for (const watchPath of FSEventsWatchers.keys()) { + if (watchPath.indexOf(path) === 0) { + count++; + if (count >= consolidateThreshhold) { + return true; + } + } + } + return false; + }; + var canUse = () => fsevents && FSEventsWatchers.size < 128; + var calcDepth = (path, root) => { + let i = 0; + while (!path.indexOf(root) && (path = sysPath.dirname(path)) !== root) + i++; + return i; + }; + var sameTypes = (info, stats) => info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() || info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() || info.type === FSEVENT_TYPE_FILE && stats.isFile(); + var FsEventsHandler = class { + constructor(fsw) { + this.fsw = fsw; + } + checkIgnored(path, stats) { + const ipaths = this.fsw._ignoredPaths; + if (this.fsw._isIgnored(path, stats)) { + ipaths.add(path); + if (stats && stats.isDirectory()) { + ipaths.add(path + ROOT_GLOBSTAR); + } + return true; + } + ipaths.delete(path); + ipaths.delete(path + ROOT_GLOBSTAR); + } + addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts) { + const event = watchedDir.has(item) ? EV_CHANGE : EV_ADD; + this.handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts); + } + async checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts) { + try { + const stats = await stat(path); + if (this.fsw.closed) + return; + if (sameTypes(info, stats)) { + this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } catch (error) { + if (error.code === "EACCES") { + this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } + } + handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts) { + if (this.fsw.closed || this.checkIgnored(path)) + return; + if (event === EV_UNLINK) { + const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY; + if (isDirectory || watchedDir.has(item)) { + this.fsw._remove(parent, item, isDirectory); + } + } else { + if (event === EV_ADD) { + if (info.type === FSEVENT_TYPE_DIRECTORY) + this.fsw._getWatchedDir(path); + if (info.type === FSEVENT_TYPE_SYMLINK && opts.followSymlinks) { + const curDepth = opts.depth === void 0 ? void 0 : calcDepth(fullPath, realPath) + 1; + return this._addToFsEvents(path, false, true, curDepth); + } + this.fsw._getWatchedDir(parent).add(item); + } + const eventName = info.type === FSEVENT_TYPE_DIRECTORY ? event + DIR_SUFFIX : event; + this.fsw._emit(eventName, path); + if (eventName === EV_ADD_DIR) + this._addToFsEvents(path, false, true); + } + } + _watchWithFsEvents(watchPath, realPath, transform, globFilter) { + if (this.fsw.closed || this.fsw._isIgnored(watchPath)) + return; + const opts = this.fsw.options; + const watchCallback = async (fullPath, flags, info) => { + if (this.fsw.closed) + return; + if (opts.depth !== void 0 && calcDepth(fullPath, realPath) > opts.depth) + return; + const path = transform(sysPath.join(watchPath, sysPath.relative(watchPath, fullPath))); + if (globFilter && !globFilter(path)) + return; + const parent = sysPath.dirname(path); + const item = sysPath.basename(path); + const watchedDir = this.fsw._getWatchedDir(info.type === FSEVENT_TYPE_DIRECTORY ? path : parent); + if (wrongEventFlags.has(flags) || info.event === FSEVENT_UNKNOWN) { + if (typeof opts.ignored === FUNCTION_TYPE) { + let stats; + try { + stats = await stat(path); + } catch (error) { + } + if (this.fsw.closed) + return; + if (this.checkIgnored(path, stats)) + return; + if (sameTypes(info, stats)) { + this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } else { + this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } else { + switch (info.event) { + case FSEVENT_CREATED: + case FSEVENT_MODIFIED: + return this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); + case FSEVENT_DELETED: + case FSEVENT_MOVED: + return this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts); + } + } + }; + const closer = setFSEventsListener(watchPath, realPath, watchCallback, this.fsw._emitRaw); + this.fsw._emitReady(); + return closer; + } + async _handleFsEventsSymlink(linkPath, fullPath, transform, curDepth) { + if (this.fsw.closed || this.fsw._symlinkPaths.has(fullPath)) + return; + this.fsw._symlinkPaths.set(fullPath, true); + this.fsw._incrReadyCount(); + try { + const linkTarget = await realpath(linkPath); + if (this.fsw.closed) + return; + if (this.fsw._isIgnored(linkTarget)) { + return this.fsw._emitReady(); + } + this.fsw._incrReadyCount(); + this._addToFsEvents(linkTarget || linkPath, (path) => { + let aliasedPath = linkPath; + if (linkTarget && linkTarget !== DOT_SLASH) { + aliasedPath = path.replace(linkTarget, linkPath); + } else if (path !== DOT_SLASH) { + aliasedPath = sysPath.join(linkPath, path); + } + return transform(aliasedPath); + }, false, curDepth); + } catch (error) { + if (this.fsw._handleError(error)) { + return this.fsw._emitReady(); + } + } + } + emitAdd(newPath, stats, processPath, opts, forceAdd) { + const pp = processPath(newPath); + const isDir = stats.isDirectory(); + const dirObj = this.fsw._getWatchedDir(sysPath.dirname(pp)); + const base = sysPath.basename(pp); + if (isDir) + this.fsw._getWatchedDir(pp); + if (dirObj.has(base)) + return; + dirObj.add(base); + if (!opts.ignoreInitial || forceAdd === true) { + this.fsw._emit(isDir ? EV_ADD_DIR : EV_ADD, pp, stats); + } + } + initWatch(realPath, path, wh, processPath) { + if (this.fsw.closed) + return; + const closer = this._watchWithFsEvents(wh.watchPath, sysPath.resolve(realPath || wh.watchPath), processPath, wh.globFilter); + this.fsw._addPathCloser(path, closer); + } + async _addToFsEvents(path, transform, forceAdd, priorDepth) { + if (this.fsw.closed) { + return; + } + const opts = this.fsw.options; + const processPath = typeof transform === FUNCTION_TYPE ? transform : IDENTITY_FN; + const wh = this.fsw._getWatchHelpers(path); + try { + const stats = await statMethods[wh.statMethod](wh.watchPath); + if (this.fsw.closed) + return; + if (this.fsw._isIgnored(wh.watchPath, stats)) { + throw null; + } + if (stats.isDirectory()) { + if (!wh.globFilter) + this.emitAdd(processPath(path), stats, processPath, opts, forceAdd); + if (priorDepth && priorDepth > opts.depth) + return; + this.fsw._readdirp(wh.watchPath, { + fileFilter: (entry) => wh.filterPath(entry), + directoryFilter: (entry) => wh.filterDir(entry), + ...Depth(opts.depth - (priorDepth || 0)) + }).on(STR_DATA, (entry) => { + if (this.fsw.closed) { + return; + } + if (entry.stats.isDirectory() && !wh.filterPath(entry)) + return; + const joinedPath = sysPath.join(wh.watchPath, entry.path); + const {fullPath} = entry; + if (wh.followSymlinks && entry.stats.isSymbolicLink()) { + const curDepth = opts.depth === void 0 ? void 0 : calcDepth(joinedPath, sysPath.resolve(wh.watchPath)) + 1; + this._handleFsEventsSymlink(joinedPath, fullPath, processPath, curDepth); + } else { + this.emitAdd(joinedPath, entry.stats, processPath, opts, forceAdd); + } + }).on(EV_ERROR, EMPTY_FN).on(STR_END, () => { + this.fsw._emitReady(); + }); + } else { + this.emitAdd(wh.watchPath, stats, processPath, opts, forceAdd); + this.fsw._emitReady(); + } + } catch (error) { + if (!error || this.fsw._handleError(error)) { + this.fsw._emitReady(); + this.fsw._emitReady(); + } + } + if (opts.persistent && forceAdd !== true) { + if (typeof transform === FUNCTION_TYPE) { + this.initWatch(void 0, path, wh, processPath); + } else { + let realPath; + try { + realPath = await realpath(wh.watchPath); + } catch (e2) { + } + this.initWatch(realPath, path, wh, processPath); + } + } + } + }; + module2.exports = FsEventsHandler; + module2.exports.canUse = canUse; +}); + +// ../../node_modules/chokidar/index.js +var require_chokidar = __commonJS((exports2) => { + "use strict"; + var {EventEmitter} = require("events"); + var fs = require("fs"); + var sysPath = require("path"); + var {promisify} = require("util"); + var readdirp = require_readdirp(); + var anymatch = require_anymatch().default; + var globParent = require_glob_parent(); + var isGlob = require_is_glob(); + var braces = require_braces(); + var normalizePath = require_normalize_path(); + var NodeFsHandler = require_nodefs_handler(); + var FsEventsHandler = require_fsevents_handler(); + var { + EV_ALL, + EV_READY, + EV_ADD, + EV_CHANGE, + EV_UNLINK, + EV_ADD_DIR, + EV_UNLINK_DIR, + EV_RAW, + EV_ERROR, + STR_CLOSE, + STR_END, + BACK_SLASH_RE, + DOUBLE_SLASH_RE, + SLASH_OR_BACK_SLASH_RE, + DOT_RE, + REPLACER_RE, + SLASH, + SLASH_SLASH, + BRACE_START, + BANG, + ONE_DOT, + TWO_DOTS, + GLOBSTAR, + SLASH_GLOBSTAR, + ANYMATCH_OPTS, + STRING_TYPE, + FUNCTION_TYPE, + EMPTY_STR, + EMPTY_FN, + isWindows, + isMacos, + isIBMi + } = require_constants3(); + var stat = promisify(fs.stat); + var readdir = promisify(fs.readdir); + var arrify = (value = []) => Array.isArray(value) ? value : [value]; + var flatten = (list, result = []) => { + list.forEach((item) => { + if (Array.isArray(item)) { + flatten(item, result); + } else { + result.push(item); + } + }); + return result; + }; + var unifyPaths = (paths_) => { + const paths = flatten(arrify(paths_)); + if (!paths.every((p) => typeof p === STRING_TYPE)) { + throw new TypeError(`Non-string provided as watch path: ${paths}`); + } + return paths.map(normalizePathToUnix); + }; + var toUnix = (string) => { + let str = string.replace(BACK_SLASH_RE, SLASH); + let prepend = false; + if (str.startsWith(SLASH_SLASH)) { + prepend = true; + } + while (str.match(DOUBLE_SLASH_RE)) { + str = str.replace(DOUBLE_SLASH_RE, SLASH); + } + if (prepend) { + str = SLASH + str; + } + return str; + }; + var normalizePathToUnix = (path) => toUnix(sysPath.normalize(toUnix(path))); + var normalizeIgnored = (cwd = EMPTY_STR) => (path) => { + if (typeof path !== STRING_TYPE) + return path; + return normalizePathToUnix(sysPath.isAbsolute(path) ? path : sysPath.join(cwd, path)); + }; + var getAbsolutePath = (path, cwd) => { + if (sysPath.isAbsolute(path)) { + return path; + } + if (path.startsWith(BANG)) { + return BANG + sysPath.join(cwd, path.slice(1)); + } + return sysPath.join(cwd, path); + }; + var undef = (opts, key) => opts[key] === void 0; + var DirEntry = class { + constructor(dir, removeWatcher) { + this.path = dir; + this._removeWatcher = removeWatcher; + this.items = new Set(); + } + add(item) { + const {items} = this; + if (!items) + return; + if (item !== ONE_DOT && item !== TWO_DOTS) + items.add(item); + } + async remove(item) { + const {items} = this; + if (!items) + return; + items.delete(item); + if (items.size > 0) + return; + const dir = this.path; + try { + await readdir(dir); + } catch (err) { + if (this._removeWatcher) { + this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir)); + } + } + } + has(item) { + const {items} = this; + if (!items) + return; + return items.has(item); + } + getChildren() { + const {items} = this; + if (!items) + return; + return [...items.values()]; + } + dispose() { + this.items.clear(); + delete this.path; + delete this._removeWatcher; + delete this.items; + Object.freeze(this); + } + }; + var STAT_METHOD_F = "stat"; + var STAT_METHOD_L = "lstat"; + var WatchHelper = class { + constructor(path, watchPath, follow, fsw) { + this.fsw = fsw; + this.path = path = path.replace(REPLACER_RE, EMPTY_STR); + this.watchPath = watchPath; + this.fullWatchPath = sysPath.resolve(watchPath); + this.hasGlob = watchPath !== path; + if (path === EMPTY_STR) + this.hasGlob = false; + this.globSymlink = this.hasGlob && follow ? void 0 : false; + this.globFilter = this.hasGlob ? anymatch(path, void 0, ANYMATCH_OPTS) : false; + this.dirParts = this.getDirParts(path); + this.dirParts.forEach((parts) => { + if (parts.length > 1) + parts.pop(); + }); + this.followSymlinks = follow; + this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L; + } + checkGlobSymlink(entry) { + if (this.globSymlink === void 0) { + this.globSymlink = entry.fullParentDir === this.fullWatchPath ? false : {realPath: entry.fullParentDir, linkPath: this.fullWatchPath}; + } + if (this.globSymlink) { + return entry.fullPath.replace(this.globSymlink.realPath, this.globSymlink.linkPath); + } + return entry.fullPath; + } + entryPath(entry) { + return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, this.checkGlobSymlink(entry))); + } + filterPath(entry) { + const {stats} = entry; + if (stats && stats.isSymbolicLink()) + return this.filterDir(entry); + const resolvedPath = this.entryPath(entry); + const matchesGlob = this.hasGlob && typeof this.globFilter === FUNCTION_TYPE ? this.globFilter(resolvedPath) : true; + return matchesGlob && this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats); + } + getDirParts(path) { + if (!this.hasGlob) + return []; + const parts = []; + const expandedPath = path.includes(BRACE_START) ? braces.expand(path) : [path]; + expandedPath.forEach((path2) => { + parts.push(sysPath.relative(this.watchPath, path2).split(SLASH_OR_BACK_SLASH_RE)); + }); + return parts; + } + filterDir(entry) { + if (this.hasGlob) { + const entryParts = this.getDirParts(this.checkGlobSymlink(entry)); + let globstar = false; + this.unmatchedGlob = !this.dirParts.some((parts) => { + return parts.every((part, i) => { + if (part === GLOBSTAR) + globstar = true; + return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i], ANYMATCH_OPTS); + }); + }); + } + return !this.unmatchedGlob && this.fsw._isntIgnored(this.entryPath(entry), entry.stats); + } + }; + var FSWatcher = class extends EventEmitter { + constructor(_opts) { + super(); + const opts = {}; + if (_opts) + Object.assign(opts, _opts); + this._watched = new Map(); + this._closers = new Map(); + this._ignoredPaths = new Set(); + this._throttled = new Map(); + this._symlinkPaths = new Map(); + this._streams = new Set(); + this.closed = false; + if (undef(opts, "persistent")) + opts.persistent = true; + if (undef(opts, "ignoreInitial")) + opts.ignoreInitial = false; + if (undef(opts, "ignorePermissionErrors")) + opts.ignorePermissionErrors = false; + if (undef(opts, "interval")) + opts.interval = 100; + if (undef(opts, "binaryInterval")) + opts.binaryInterval = 300; + if (undef(opts, "disableGlobbing")) + opts.disableGlobbing = false; + opts.enableBinaryInterval = opts.binaryInterval !== opts.interval; + if (undef(opts, "useFsEvents")) + opts.useFsEvents = !opts.usePolling; + const canUseFsEvents = FsEventsHandler.canUse(); + if (!canUseFsEvents) + opts.useFsEvents = false; + if (undef(opts, "usePolling") && !opts.useFsEvents) { + opts.usePolling = isMacos; + } + if (isIBMi) { + opts.usePolling = true; + } + const envPoll = process.env.CHOKIDAR_USEPOLLING; + if (envPoll !== void 0) { + const envLower = envPoll.toLowerCase(); + if (envLower === "false" || envLower === "0") { + opts.usePolling = false; + } else if (envLower === "true" || envLower === "1") { + opts.usePolling = true; + } else { + opts.usePolling = !!envLower; + } + } + const envInterval = process.env.CHOKIDAR_INTERVAL; + if (envInterval) { + opts.interval = Number.parseInt(envInterval, 10); + } + if (undef(opts, "atomic")) + opts.atomic = !opts.usePolling && !opts.useFsEvents; + if (opts.atomic) + this._pendingUnlinks = new Map(); + if (undef(opts, "followSymlinks")) + opts.followSymlinks = true; + if (undef(opts, "awaitWriteFinish")) + opts.awaitWriteFinish = false; + if (opts.awaitWriteFinish === true) + opts.awaitWriteFinish = {}; + const awf = opts.awaitWriteFinish; + if (awf) { + if (!awf.stabilityThreshold) + awf.stabilityThreshold = 2e3; + if (!awf.pollInterval) + awf.pollInterval = 100; + this._pendingWrites = new Map(); + } + if (opts.ignored) + opts.ignored = arrify(opts.ignored); + let readyCalls = 0; + this._emitReady = () => { + readyCalls++; + if (readyCalls >= this._readyCount) { + this._emitReady = EMPTY_FN; + this._readyEmitted = true; + process.nextTick(() => this.emit(EV_READY)); + } + }; + this._emitRaw = (...args) => this.emit(EV_RAW, ...args); + this._readyEmitted = false; + this.options = opts; + if (opts.useFsEvents) { + this._fsEventsHandler = new FsEventsHandler(this); + } else { + this._nodeFsHandler = new NodeFsHandler(this); + } + Object.freeze(opts); + } + add(paths_, _origAdd, _internal) { + const {cwd, disableGlobbing} = this.options; + this.closed = false; + let paths = unifyPaths(paths_); + if (cwd) { + paths = paths.map((path) => { + const absPath = getAbsolutePath(path, cwd); + if (disableGlobbing || !isGlob(path)) { + return absPath; + } + return normalizePath(absPath); + }); + } + paths = paths.filter((path) => { + if (path.startsWith(BANG)) { + this._ignoredPaths.add(path.slice(1)); + return false; + } + this._ignoredPaths.delete(path); + this._ignoredPaths.delete(path + SLASH_GLOBSTAR); + this._userIgnored = void 0; + return true; + }); + if (this.options.useFsEvents && this._fsEventsHandler) { + if (!this._readyCount) + this._readyCount = paths.length; + if (this.options.persistent) + this._readyCount *= 2; + paths.forEach((path) => this._fsEventsHandler._addToFsEvents(path)); + } else { + if (!this._readyCount) + this._readyCount = 0; + this._readyCount += paths.length; + Promise.all(paths.map(async (path) => { + const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, 0, 0, _origAdd); + if (res) + this._emitReady(); + return res; + })).then((results) => { + if (this.closed) + return; + results.filter((item) => item).forEach((item) => { + this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item)); + }); + }); + } + return this; + } + unwatch(paths_) { + if (this.closed) + return this; + const paths = unifyPaths(paths_); + const {cwd} = this.options; + paths.forEach((path) => { + if (!sysPath.isAbsolute(path) && !this._closers.has(path)) { + if (cwd) + path = sysPath.join(cwd, path); + path = sysPath.resolve(path); + } + this._closePath(path); + this._ignoredPaths.add(path); + if (this._watched.has(path)) { + this._ignoredPaths.add(path + SLASH_GLOBSTAR); + } + this._userIgnored = void 0; + }); + return this; + } + close() { + if (this.closed) + return this._closePromise; + this.closed = true; + this.removeAllListeners(); + const closers = []; + this._closers.forEach((closerList) => closerList.forEach((closer) => { + const promise = closer(); + if (promise instanceof Promise) + closers.push(promise); + })); + this._streams.forEach((stream) => stream.destroy()); + this._userIgnored = void 0; + this._readyCount = 0; + this._readyEmitted = false; + this._watched.forEach((dirent) => dirent.dispose()); + ["closers", "watched", "streams", "symlinkPaths", "throttled"].forEach((key) => { + this[`_${key}`].clear(); + }); + this._closePromise = closers.length ? Promise.all(closers).then(() => void 0) : Promise.resolve(); + return this._closePromise; + } + getWatched() { + const watchList = {}; + this._watched.forEach((entry, dir) => { + const key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir; + watchList[key || ONE_DOT] = entry.getChildren().sort(); + }); + return watchList; + } + emitWithAll(event, args) { + this.emit(...args); + if (event !== EV_ERROR) + this.emit(EV_ALL, ...args); + } + async _emit(event, path, val1, val2, val3) { + if (this.closed) + return; + const opts = this.options; + if (isWindows) + path = sysPath.normalize(path); + if (opts.cwd) + path = sysPath.relative(opts.cwd, path); + const args = [event, path]; + if (val3 !== void 0) + args.push(val1, val2, val3); + else if (val2 !== void 0) + args.push(val1, val2); + else if (val1 !== void 0) + args.push(val1); + const awf = opts.awaitWriteFinish; + let pw; + if (awf && (pw = this._pendingWrites.get(path))) { + pw.lastChange = new Date(); + return this; + } + if (opts.atomic) { + if (event === EV_UNLINK) { + this._pendingUnlinks.set(path, args); + setTimeout(() => { + this._pendingUnlinks.forEach((entry, path2) => { + this.emit(...entry); + this.emit(EV_ALL, ...entry); + this._pendingUnlinks.delete(path2); + }); + }, typeof opts.atomic === "number" ? opts.atomic : 100); + return this; + } + if (event === EV_ADD && this._pendingUnlinks.has(path)) { + event = args[0] = EV_CHANGE; + this._pendingUnlinks.delete(path); + } + } + if (awf && (event === EV_ADD || event === EV_CHANGE) && this._readyEmitted) { + const awfEmit = (err, stats) => { + if (err) { + event = args[0] = EV_ERROR; + args[1] = err; + this.emitWithAll(event, args); + } else if (stats) { + if (args.length > 2) { + args[2] = stats; + } else { + args.push(stats); + } + this.emitWithAll(event, args); + } + }; + this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit); + return this; + } + if (event === EV_CHANGE) { + const isThrottled = !this._throttle(EV_CHANGE, path, 50); + if (isThrottled) + return this; + } + if (opts.alwaysStat && val1 === void 0 && (event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE)) { + const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path; + let stats; + try { + stats = await stat(fullPath); + } catch (err) { + } + if (!stats || this.closed) + return; + args.push(stats); + } + this.emitWithAll(event, args); + return this; + } + _handleError(error) { + const code = error && error.code; + if (error && code !== "ENOENT" && code !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code !== "EPERM" && code !== "EACCES")) { + this.emit(EV_ERROR, error); + } + return error || this.closed; + } + _throttle(actionType, path, timeout) { + if (!this._throttled.has(actionType)) { + this._throttled.set(actionType, new Map()); + } + const action = this._throttled.get(actionType); + const actionPath = action.get(path); + if (actionPath) { + actionPath.count++; + return false; + } + let timeoutObject; + const clear = () => { + const item = action.get(path); + const count = item ? item.count : 0; + action.delete(path); + clearTimeout(timeoutObject); + if (item) + clearTimeout(item.timeoutObject); + return count; + }; + timeoutObject = setTimeout(clear, timeout); + const thr = {timeoutObject, clear, count: 0}; + action.set(path, thr); + return thr; + } + _incrReadyCount() { + return this._readyCount++; + } + _awaitWriteFinish(path, threshold, event, awfEmit) { + let timeoutHandler; + let fullPath = path; + if (this.options.cwd && !sysPath.isAbsolute(path)) { + fullPath = sysPath.join(this.options.cwd, path); + } + const now = new Date(); + const awaitWriteFinish = (prevStat) => { + fs.stat(fullPath, (err, curStat) => { + if (err || !this._pendingWrites.has(path)) { + if (err && err.code !== "ENOENT") + awfEmit(err); + return; + } + const now2 = Number(new Date()); + if (prevStat && curStat.size !== prevStat.size) { + this._pendingWrites.get(path).lastChange = now2; + } + const pw = this._pendingWrites.get(path); + const df = now2 - pw.lastChange; + if (df >= threshold) { + this._pendingWrites.delete(path); + awfEmit(void 0, curStat); + } else { + timeoutHandler = setTimeout(awaitWriteFinish, this.options.awaitWriteFinish.pollInterval, curStat); + } + }); + }; + if (!this._pendingWrites.has(path)) { + this._pendingWrites.set(path, { + lastChange: now, + cancelWait: () => { + this._pendingWrites.delete(path); + clearTimeout(timeoutHandler); + return event; + } + }); + timeoutHandler = setTimeout(awaitWriteFinish, this.options.awaitWriteFinish.pollInterval); + } + } + _getGlobIgnored() { + return [...this._ignoredPaths.values()]; + } + _isIgnored(path, stats) { + if (this.options.atomic && DOT_RE.test(path)) + return true; + if (!this._userIgnored) { + const {cwd} = this.options; + const ign = this.options.ignored; + const ignored = ign && ign.map(normalizeIgnored(cwd)); + const paths = arrify(ignored).filter((path2) => typeof path2 === STRING_TYPE && !isGlob(path2)).map((path2) => path2 + SLASH_GLOBSTAR); + const list = this._getGlobIgnored().map(normalizeIgnored(cwd)).concat(ignored, paths); + this._userIgnored = anymatch(list, void 0, ANYMATCH_OPTS); + } + return this._userIgnored([path, stats]); + } + _isntIgnored(path, stat2) { + return !this._isIgnored(path, stat2); + } + _getWatchHelpers(path, depth) { + const watchPath = depth || this.options.disableGlobbing || !isGlob(path) ? path : globParent(path); + const follow = this.options.followSymlinks; + return new WatchHelper(path, watchPath, follow, this); + } + _getWatchedDir(directory) { + if (!this._boundRemove) + this._boundRemove = this._remove.bind(this); + const dir = sysPath.resolve(directory); + if (!this._watched.has(dir)) + this._watched.set(dir, new DirEntry(dir, this._boundRemove)); + return this._watched.get(dir); + } + _hasReadPermissions(stats) { + if (this.options.ignorePermissionErrors) + return true; + const md = stats && Number.parseInt(stats.mode, 10); + const st = md & 511; + const it = Number.parseInt(st.toString(8)[0], 10); + return Boolean(4 & it); + } + _remove(directory, item, isDirectory) { + const path = sysPath.join(directory, item); + const fullPath = sysPath.resolve(path); + isDirectory = isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath); + if (!this._throttle("remove", path, 100)) + return; + if (!isDirectory && !this.options.useFsEvents && this._watched.size === 1) { + this.add(directory, item, true); + } + const wp = this._getWatchedDir(path); + const nestedDirectoryChildren = wp.getChildren(); + nestedDirectoryChildren.forEach((nested) => this._remove(path, nested)); + const parent = this._getWatchedDir(directory); + const wasTracked = parent.has(item); + parent.remove(item); + if (this._symlinkPaths.has(fullPath)) { + this._symlinkPaths.delete(fullPath); + } + let relPath = path; + if (this.options.cwd) + relPath = sysPath.relative(this.options.cwd, path); + if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) { + const event = this._pendingWrites.get(relPath).cancelWait(); + if (event === EV_ADD) + return; + } + this._watched.delete(path); + this._watched.delete(fullPath); + const eventName = isDirectory ? EV_UNLINK_DIR : EV_UNLINK; + if (wasTracked && !this._isIgnored(path)) + this._emit(eventName, path); + if (!this.options.useFsEvents) { + this._closePath(path); + } + } + _closePath(path) { + this._closeFile(path); + const dir = sysPath.dirname(path); + this._getWatchedDir(dir).remove(sysPath.basename(path)); + } + _closeFile(path) { + const closers = this._closers.get(path); + if (!closers) + return; + closers.forEach((closer) => closer()); + this._closers.delete(path); + } + _addPathCloser(path, closer) { + if (!closer) + return; + let list = this._closers.get(path); + if (!list) { + list = []; + this._closers.set(path, list); + } + list.push(closer); + } + _readdirp(root, opts) { + if (this.closed) + return; + const options = {type: EV_ALL, alwaysStat: true, lstat: true, ...opts}; + let stream = readdirp(root, options); + this._streams.add(stream); + stream.once(STR_CLOSE, () => { + stream = void 0; + }); + stream.once(STR_END, () => { + if (stream) { + this._streams.delete(stream); + stream = void 0; + } + }); + return stream; + } + }; + exports2.FSWatcher = FSWatcher; + var watch = (paths, options) => { + const watcher = new FSWatcher(options); + watcher.add(paths); + return watcher; + }; + exports2.watch = watch; +}); + +// ../../node_modules/nunjucks/src/node-loaders.js +var require_node_loaders = __commonJS((exports2, module2) => { + "use strict"; + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var fs = require("fs"); + var path = require("path"); + var Loader2 = require_loader(); + var _require2 = require_precompiled_loader(); + var PrecompiledLoader = _require2.PrecompiledLoader; + var chokidar; + var FileSystemLoader = /* @__PURE__ */ function(_Loader) { + _inheritsLoose(FileSystemLoader2, _Loader); + function FileSystemLoader2(searchPaths, opts) { + var _this; + _this = _Loader.call(this) || this; + if (typeof opts === "boolean") { + console.log("[nunjucks] Warning: you passed a boolean as the second argument to FileSystemLoader, but it now takes an options object. See http://mozilla.github.io/nunjucks/api.html#filesystemloader"); + } + opts = opts || {}; + _this.pathsToNames = {}; + _this.noCache = !!opts.noCache; + if (searchPaths) { + searchPaths = Array.isArray(searchPaths) ? searchPaths : [searchPaths]; + _this.searchPaths = searchPaths.map(path.normalize); + } else { + _this.searchPaths = ["."]; + } + if (opts.watch) { + try { + chokidar = require_chokidar(); + } catch (e2) { + throw new Error("watch requires chokidar to be installed"); + } + var paths = _this.searchPaths.filter(fs.existsSync); + var watcher = chokidar.watch(paths); + watcher.on("all", function(event, fullname) { + fullname = path.resolve(fullname); + if (event === "change" && fullname in _this.pathsToNames) { + _this.emit("update", _this.pathsToNames[fullname], fullname); + } + }); + watcher.on("error", function(error) { + console.log("Watcher error: " + error); + }); + } + return _this; + } + var _proto = FileSystemLoader2.prototype; + _proto.getSource = function getSource(name) { + var fullpath = null; + var paths = this.searchPaths; + for (var i = 0; i < paths.length; i++) { + var basePath = path.resolve(paths[i]); + var p = path.resolve(paths[i], name); + if (p.indexOf(basePath) === 0 && fs.existsSync(p)) { + fullpath = p; + break; + } + } + if (!fullpath) { + return null; + } + this.pathsToNames[fullpath] = name; + var source = { + src: fs.readFileSync(fullpath, "utf-8"), + path: fullpath, + noCache: this.noCache + }; + this.emit("load", name, source); + return source; + }; + return FileSystemLoader2; + }(Loader2); + var NodeResolveLoader = /* @__PURE__ */ function(_Loader2) { + _inheritsLoose(NodeResolveLoader2, _Loader2); + function NodeResolveLoader2(opts) { + var _this2; + _this2 = _Loader2.call(this) || this; + opts = opts || {}; + _this2.pathsToNames = {}; + _this2.noCache = !!opts.noCache; + if (opts.watch) { + try { + chokidar = require_chokidar(); + } catch (e2) { + throw new Error("watch requires chokidar to be installed"); + } + _this2.watcher = chokidar.watch(); + _this2.watcher.on("change", function(fullname) { + _this2.emit("update", _this2.pathsToNames[fullname], fullname); + }); + _this2.watcher.on("error", function(error) { + console.log("Watcher error: " + error); + }); + _this2.on("load", function(name, source) { + _this2.watcher.add(source.path); + }); + } + return _this2; + } + var _proto2 = NodeResolveLoader2.prototype; + _proto2.getSource = function getSource(name) { + if (/^\.?\.?(\/|\\)/.test(name)) { + return null; + } + if (/^[A-Z]:/.test(name)) { + return null; + } + var fullpath; + try { + fullpath = require.resolve(name); + } catch (e2) { + return null; + } + this.pathsToNames[fullpath] = name; + var source = { + src: fs.readFileSync(fullpath, "utf-8"), + path: fullpath, + noCache: this.noCache + }; + this.emit("load", name, source); + return source; + }; + return NodeResolveLoader2; + }(Loader2); + module2.exports = { + FileSystemLoader, + PrecompiledLoader, + NodeResolveLoader + }; +}); + +// ../../node_modules/nunjucks/src/loaders.js +var require_loaders = __commonJS((exports2, module2) => { + "use strict"; + module2.exports = require_node_loaders(); +}); + +// ../../node_modules/nunjucks/src/tests.js +var require_tests = __commonJS((exports2) => { + "use strict"; + var SafeString = require_runtime().SafeString; + function callable(value) { + return typeof value === "function"; + } + exports2.callable = callable; + function defined(value) { + return value !== void 0; + } + exports2.defined = defined; + function divisibleby(one, two) { + return one % two === 0; + } + exports2.divisibleby = divisibleby; + function escaped(value) { + return value instanceof SafeString; + } + exports2.escaped = escaped; + function equalto(one, two) { + return one === two; + } + exports2.equalto = equalto; + exports2.eq = exports2.equalto; + exports2.sameas = exports2.equalto; + function even(value) { + return value % 2 === 0; + } + exports2.even = even; + function falsy(value) { + return !value; + } + exports2.falsy = falsy; + function ge(one, two) { + return one >= two; + } + exports2.ge = ge; + function greaterthan(one, two) { + return one > two; + } + exports2.greaterthan = greaterthan; + exports2.gt = exports2.greaterthan; + function le(one, two) { + return one <= two; + } + exports2.le = le; + function lessthan(one, two) { + return one < two; + } + exports2.lessthan = lessthan; + exports2.lt = exports2.lessthan; + function lower(value) { + return value.toLowerCase() === value; + } + exports2.lower = lower; + function ne(one, two) { + return one !== two; + } + exports2.ne = ne; + function nullTest(value) { + return value === null; + } + exports2.null = nullTest; + function number(value) { + return typeof value === "number"; + } + exports2.number = number; + function odd(value) { + return value % 2 === 1; + } + exports2.odd = odd; + function string(value) { + return typeof value === "string"; + } + exports2.string = string; + function truthy(value) { + return !!value; + } + exports2.truthy = truthy; + function undefinedTest(value) { + return value === void 0; + } + exports2.undefined = undefinedTest; + function upper(value) { + return value.toUpperCase() === value; + } + exports2.upper = upper; + function iterable(value) { + if (typeof Symbol !== "undefined") { + return !!value[Symbol.iterator]; + } else { + return Array.isArray(value) || typeof value === "string"; + } + } + exports2.iterable = iterable; + function mapping(value) { + var bool = value !== null && value !== void 0 && typeof value === "object" && !Array.isArray(value); + if (Set) { + return bool && !(value instanceof Set); + } else { + return bool; + } + } + exports2.mapping = mapping; +}); + +// ../../node_modules/nunjucks/src/globals.js +var require_globals = __commonJS((exports2, module2) => { + "use strict"; + function _cycler(items) { + var index = -1; + return { + current: null, + reset: function reset2() { + index = -1; + this.current = null; + }, + next: function next() { + index++; + if (index >= items.length) { + index = 0; + } + this.current = items[index]; + return this.current; + } + }; + } + function _joiner(sep) { + sep = sep || ","; + var first = true; + return function() { + var val = first ? "" : sep; + first = false; + return val; + }; + } + function globals() { + return { + range: function range(start, stop, step) { + if (typeof stop === "undefined") { + stop = start; + start = 0; + step = 1; + } else if (!step) { + step = 1; + } + var arr = []; + if (step > 0) { + for (var i = start; i < stop; i += step) { + arr.push(i); + } + } else { + for (var _i = start; _i > stop; _i += step) { + arr.push(_i); + } + } + return arr; + }, + cycler: function cycler() { + return _cycler(Array.prototype.slice.call(arguments)); + }, + joiner: function joiner(sep) { + return _joiner(sep); + } + }; + } + module2.exports = globals; +}); + +// ../../node_modules/nunjucks/src/express-app.js +var require_express_app = __commonJS((exports2, module2) => { + "use strict"; + var path = require("path"); + module2.exports = function express(env, app) { + function NunjucksView(name, opts) { + this.name = name; + this.path = name; + this.defaultEngine = opts.defaultEngine; + this.ext = path.extname(name); + if (!this.ext && !this.defaultEngine) { + throw new Error("No default engine was specified and no extension was provided."); + } + if (!this.ext) { + this.name += this.ext = (this.defaultEngine[0] !== "." ? "." : "") + this.defaultEngine; + } + } + NunjucksView.prototype.render = function render2(opts, cb) { + env.render(this.name, opts, cb); + }; + app.set("view", NunjucksView); + app.set("nunjucksEnv", env); + return env; + }; +}); + +// ../../node_modules/nunjucks/src/environment.js +var require_environment = __commonJS((exports2, module2) => { + "use strict"; + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + _setPrototypeOf(subClass, superClass); + } + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) { + o2.__proto__ = p2; + return o2; + }; + return _setPrototypeOf(o, p); + } + var asap = require_asap(); + var _waterfall = require_a_sync_waterfall(); + var lib2 = require_lib(); + var compiler2 = require_compiler(); + var filters = require_filters(); + var _require2 = require_loaders(); + var FileSystemLoader = _require2.FileSystemLoader; + var WebLoader = _require2.WebLoader; + var PrecompiledLoader = _require2.PrecompiledLoader; + var tests = require_tests(); + var globals = require_globals(); + var _require22 = require_object(); + var Obj = _require22.Obj; + var EmitterObj = _require22.EmitterObj; + var globalRuntime = require_runtime(); + var handleError = globalRuntime.handleError; + var Frame = globalRuntime.Frame; + var expressApp = require_express_app(); + function callbackAsap(cb, err, res) { + asap(function() { + cb(err, res); + }); + } + var noopTmplSrc = { + type: "code", + obj: { + root: function root(env, context, frame, runtime2, cb) { + try { + cb(null, ""); + } catch (e2) { + cb(handleError(e2, null, null)); + } + } + } + }; + var Environment2 = /* @__PURE__ */ function(_EmitterObj) { + _inheritsLoose(Environment3, _EmitterObj); + function Environment3() { + return _EmitterObj.apply(this, arguments) || this; + } + var _proto = Environment3.prototype; + _proto.init = function init(loaders2, opts) { + var _this = this; + opts = this.opts = opts || {}; + this.opts.dev = !!opts.dev; + this.opts.autoescape = opts.autoescape != null ? opts.autoescape : true; + this.opts.throwOnUndefined = !!opts.throwOnUndefined; + this.opts.trimBlocks = !!opts.trimBlocks; + this.opts.lstripBlocks = !!opts.lstripBlocks; + this.loaders = []; + if (!loaders2) { + if (FileSystemLoader) { + this.loaders = [new FileSystemLoader("views")]; + } else if (WebLoader) { + this.loaders = [new WebLoader("/views")]; + } + } else { + this.loaders = lib2.isArray(loaders2) ? loaders2 : [loaders2]; + } + if (typeof window !== "undefined" && window.nunjucksPrecompiled) { + this.loaders.unshift(new PrecompiledLoader(window.nunjucksPrecompiled)); + } + this._initLoaders(); + this.globals = globals(); + this.filters = {}; + this.tests = {}; + this.asyncFilters = []; + this.extensions = {}; + this.extensionsList = []; + lib2._entries(filters).forEach(function(_ref) { + var name = _ref[0], filter = _ref[1]; + return _this.addFilter(name, filter); + }); + lib2._entries(tests).forEach(function(_ref2) { + var name = _ref2[0], test = _ref2[1]; + return _this.addTest(name, test); + }); + }; + _proto._initLoaders = function _initLoaders() { + var _this2 = this; + this.loaders.forEach(function(loader) { + loader.cache = {}; + if (typeof loader.on === "function") { + loader.on("update", function(name, fullname) { + loader.cache[name] = null; + _this2.emit("update", name, fullname, loader); + }); + loader.on("load", function(name, source) { + _this2.emit("load", name, source, loader); + }); + } + }); + }; + _proto.invalidateCache = function invalidateCache() { + this.loaders.forEach(function(loader) { + loader.cache = {}; + }); + }; + _proto.addExtension = function addExtension(name, extension) { + extension.__name = name; + this.extensions[name] = extension; + this.extensionsList.push(extension); + return this; + }; + _proto.removeExtension = function removeExtension(name) { + var extension = this.getExtension(name); + if (!extension) { + return; + } + this.extensionsList = lib2.without(this.extensionsList, extension); + delete this.extensions[name]; + }; + _proto.getExtension = function getExtension(name) { + return this.extensions[name]; + }; + _proto.hasExtension = function hasExtension(name) { + return !!this.extensions[name]; + }; + _proto.addGlobal = function addGlobal(name, value) { + this.globals[name] = value; + return this; + }; + _proto.getGlobal = function getGlobal(name) { + if (typeof this.globals[name] === "undefined") { + throw new Error("global not found: " + name); + } + return this.globals[name]; + }; + _proto.addFilter = function addFilter(name, func, async) { + var wrapped = func; + if (async) { + this.asyncFilters.push(name); + } + this.filters[name] = wrapped; + return this; + }; + _proto.getFilter = function getFilter(name) { + if (!this.filters[name]) { + throw new Error("filter not found: " + name); + } + return this.filters[name]; + }; + _proto.addTest = function addTest(name, func) { + this.tests[name] = func; + return this; + }; + _proto.getTest = function getTest(name) { + if (!this.tests[name]) { + throw new Error("test not found: " + name); + } + return this.tests[name]; + }; + _proto.resolveTemplate = function resolveTemplate(loader, parentName, filename) { + var isRelative = loader.isRelative && parentName ? loader.isRelative(filename) : false; + return isRelative && loader.resolve ? loader.resolve(parentName, filename) : filename; + }; + _proto.getTemplate = function getTemplate(name, eagerCompile, parentName, ignoreMissing, cb) { + var _this3 = this; + var that = this; + var tmpl = null; + if (name && name.raw) { + name = name.raw; + } + if (lib2.isFunction(parentName)) { + cb = parentName; + parentName = null; + eagerCompile = eagerCompile || false; + } + if (lib2.isFunction(eagerCompile)) { + cb = eagerCompile; + eagerCompile = false; + } + if (name instanceof Template2) { + tmpl = name; + } else if (typeof name !== "string") { + throw new Error("template names must be a string: " + name); + } else { + for (var i = 0; i < this.loaders.length; i++) { + var loader = this.loaders[i]; + tmpl = loader.cache[this.resolveTemplate(loader, parentName, name)]; + if (tmpl) { + break; + } + } + } + if (tmpl) { + if (eagerCompile) { + tmpl.compile(); + } + if (cb) { + cb(null, tmpl); + return void 0; + } else { + return tmpl; + } + } + var syncResult; + var createTemplate = function createTemplate2(err, info) { + if (!info && !err && !ignoreMissing) { + err = new Error("template not found: " + name); + } + if (err) { + if (cb) { + cb(err); + return; + } else { + throw err; + } + } + var newTmpl; + if (!info) { + newTmpl = new Template2(noopTmplSrc, _this3, "", eagerCompile); + } else { + newTmpl = new Template2(info.src, _this3, info.path, eagerCompile); + if (!info.noCache) { + info.loader.cache[name] = newTmpl; + } + } + if (cb) { + cb(null, newTmpl); + } else { + syncResult = newTmpl; + } + }; + lib2.asyncIter(this.loaders, function(loader2, i2, next, done) { + function handle(err, src) { + if (err) { + done(err); + } else if (src) { + src.loader = loader2; + done(null, src); + } else { + next(); + } + } + name = that.resolveTemplate(loader2, parentName, name); + if (loader2.async) { + loader2.getSource(name, handle); + } else { + handle(null, loader2.getSource(name)); + } + }, createTemplate); + return syncResult; + }; + _proto.express = function express(app) { + return expressApp(this, app); + }; + _proto.render = function render2(name, ctx, cb) { + if (lib2.isFunction(ctx)) { + cb = ctx; + ctx = null; + } + var syncResult = null; + this.getTemplate(name, function(err, tmpl) { + if (err && cb) { + callbackAsap(cb, err); + } else if (err) { + throw err; + } else { + syncResult = tmpl.render(ctx, cb); + } + }); + return syncResult; + }; + _proto.renderString = function renderString2(src, ctx, opts, cb) { + if (lib2.isFunction(opts)) { + cb = opts; + opts = {}; + } + opts = opts || {}; + var tmpl = new Template2(src, this, opts.path); + return tmpl.render(ctx, cb); + }; + _proto.waterfall = function waterfall(tasks, callback, forceAsync) { + return _waterfall(tasks, callback, forceAsync); + }; + return Environment3; + }(EmitterObj); + var Context = /* @__PURE__ */ function(_Obj) { + _inheritsLoose(Context2, _Obj); + function Context2() { + return _Obj.apply(this, arguments) || this; + } + var _proto2 = Context2.prototype; + _proto2.init = function init(ctx, blocks, env) { + var _this4 = this; + this.env = env || new Environment2(); + this.ctx = lib2.extend({}, ctx); + this.blocks = {}; + this.exported = []; + lib2.keys(blocks).forEach(function(name) { + _this4.addBlock(name, blocks[name]); + }); + }; + _proto2.lookup = function lookup(name) { + if (name in this.env.globals && !(name in this.ctx)) { + return this.env.globals[name]; + } else { + return this.ctx[name]; + } + }; + _proto2.setVariable = function setVariable(name, val) { + this.ctx[name] = val; + }; + _proto2.getVariables = function getVariables() { + return this.ctx; + }; + _proto2.addBlock = function addBlock(name, block) { + this.blocks[name] = this.blocks[name] || []; + this.blocks[name].push(block); + return this; + }; + _proto2.getBlock = function getBlock(name) { + if (!this.blocks[name]) { + throw new Error('unknown block "' + name + '"'); + } + return this.blocks[name][0]; + }; + _proto2.getSuper = function getSuper(env, name, block, frame, runtime2, cb) { + var idx = lib2.indexOf(this.blocks[name] || [], block); + var blk = this.blocks[name][idx + 1]; + var context = this; + if (idx === -1 || !blk) { + throw new Error('no super block available for "' + name + '"'); + } + blk(env, context, frame, runtime2, cb); + }; + _proto2.addExport = function addExport(name) { + this.exported.push(name); + }; + _proto2.getExported = function getExported() { + var _this5 = this; + var exported = {}; + this.exported.forEach(function(name) { + exported[name] = _this5.ctx[name]; + }); + return exported; + }; + return Context2; + }(Obj); + var Template2 = /* @__PURE__ */ function(_Obj2) { + _inheritsLoose(Template3, _Obj2); + function Template3() { + return _Obj2.apply(this, arguments) || this; + } + var _proto3 = Template3.prototype; + _proto3.init = function init(src, env, path, eagerCompile) { + this.env = env || new Environment2(); + if (lib2.isObject(src)) { + switch (src.type) { + case "code": + this.tmplProps = src.obj; + break; + case "string": + this.tmplStr = src.obj; + break; + default: + throw new Error("Unexpected template object type " + src.type + "; expected 'code', or 'string'"); + } + } else if (lib2.isString(src)) { + this.tmplStr = src; + } else { + throw new Error("src must be a string or an object describing the source"); + } + this.path = path; + if (eagerCompile) { + try { + this._compile(); + } catch (err) { + throw lib2._prettifyError(this.path, this.env.opts.dev, err); + } + } else { + this.compiled = false; + } + }; + _proto3.render = function render2(ctx, parentFrame, cb) { + var _this6 = this; + if (typeof ctx === "function") { + cb = ctx; + ctx = {}; + } else if (typeof parentFrame === "function") { + cb = parentFrame; + parentFrame = null; + } + var forceAsync = !parentFrame; + try { + this.compile(); + } catch (e2) { + var err = lib2._prettifyError(this.path, this.env.opts.dev, e2); + if (cb) { + return callbackAsap(cb, err); + } else { + throw err; + } + } + var context = new Context(ctx || {}, this.blocks, this.env); + var frame = parentFrame ? parentFrame.push(true) : new Frame(); + frame.topLevel = true; + var syncResult = null; + var didError = false; + this.rootRenderFunc(this.env, context, frame, globalRuntime, function(err2, res) { + if (didError && cb && typeof res !== "undefined") { + return; + } + if (err2) { + err2 = lib2._prettifyError(_this6.path, _this6.env.opts.dev, err2); + didError = true; + } + if (cb) { + if (forceAsync) { + callbackAsap(cb, err2, res); + } else { + cb(err2, res); + } + } else { + if (err2) { + throw err2; + } + syncResult = res; + } + }); + return syncResult; + }; + _proto3.getExported = function getExported(ctx, parentFrame, cb) { + if (typeof ctx === "function") { + cb = ctx; + ctx = {}; + } + if (typeof parentFrame === "function") { + cb = parentFrame; + parentFrame = null; + } + try { + this.compile(); + } catch (e2) { + if (cb) { + return cb(e2); + } else { + throw e2; + } + } + var frame = parentFrame ? parentFrame.push() : new Frame(); + frame.topLevel = true; + var context = new Context(ctx || {}, this.blocks, this.env); + this.rootRenderFunc(this.env, context, frame, globalRuntime, function(err) { + if (err) { + cb(err, null); + } else { + cb(null, context.getExported()); + } + }); + }; + _proto3.compile = function compile2() { + if (!this.compiled) { + this._compile(); + } + }; + _proto3._compile = function _compile() { + var props; + if (this.tmplProps) { + props = this.tmplProps; + } else { + var source = compiler2.compile(this.tmplStr, this.env.asyncFilters, this.env.extensionsList, this.path, this.env.opts); + var func = new Function(source); + props = func(); + } + this.blocks = this._getBlocks(props); + this.rootRenderFunc = props.root; + this.compiled = true; + }; + _proto3._getBlocks = function _getBlocks(props) { + var blocks = {}; + lib2.keys(props).forEach(function(k) { + if (k.slice(0, 2) === "b_") { + blocks[k.slice(2)] = props[k]; + } + }); + return blocks; + }; + return Template3; + }(Obj); + module2.exports = { + Environment: Environment2, + Template: Template2 + }; +}); + +// ../../node_modules/nunjucks/src/precompile-global.js +var require_precompile_global = __commonJS((exports2, module2) => { + "use strict"; + function precompileGlobal(templates, opts) { + var out = ""; + opts = opts || {}; + for (var i = 0; i < templates.length; i++) { + var name = JSON.stringify(templates[i].name); + var template = templates[i].template; + out += "(function() {(window.nunjucksPrecompiled = window.nunjucksPrecompiled || {})[" + name + "] = (function() {\n" + template + "\n})();\n"; + if (opts.asFunction) { + out += "return function(ctx, cb) { return nunjucks.render(" + name + ", ctx, cb); }\n"; + } + out += "})();\n"; + } + return out; + } + module2.exports = precompileGlobal; +}); + +// ../../node_modules/nunjucks/src/precompile.js +var require_precompile = __commonJS((exports2, module2) => { + "use strict"; + var fs = require("fs"); + var path = require("path"); + var _require2 = require_lib(); + var _prettifyError = _require2._prettifyError; + var compiler2 = require_compiler(); + var _require22 = require_environment(); + var Environment2 = _require22.Environment; + var precompileGlobal = require_precompile_global(); + function match(filename, patterns) { + if (!Array.isArray(patterns)) { + return false; + } + return patterns.some(function(pattern) { + return filename.match(pattern); + }); + } + function precompileString(str, opts) { + opts = opts || {}; + opts.isString = true; + var env = opts.env || new Environment2([]); + var wrapper = opts.wrapper || precompileGlobal; + if (!opts.name) { + throw new Error('the "name" option is required when compiling a string'); + } + return wrapper([_precompile(str, opts.name, env)], opts); + } + function precompile2(input, opts) { + opts = opts || {}; + var env = opts.env || new Environment2([]); + var wrapper = opts.wrapper || precompileGlobal; + if (opts.isString) { + return precompileString(input, opts); + } + var pathStats = fs.existsSync(input) && fs.statSync(input); + var precompiled = []; + var templates = []; + function addTemplates(dir) { + fs.readdirSync(dir).forEach(function(file) { + var filepath = path.join(dir, file); + var subpath = filepath.substr(path.join(input, "/").length); + var stat = fs.statSync(filepath); + if (stat && stat.isDirectory()) { + subpath += "/"; + if (!match(subpath, opts.exclude)) { + addTemplates(filepath); + } + } else if (match(subpath, opts.include)) { + templates.push(filepath); + } + }); + } + if (pathStats.isFile()) { + precompiled.push(_precompile(fs.readFileSync(input, "utf-8"), opts.name || input, env)); + } else if (pathStats.isDirectory()) { + addTemplates(input); + for (var i = 0; i < templates.length; i++) { + var name = templates[i].replace(path.join(input, "/"), ""); + try { + precompiled.push(_precompile(fs.readFileSync(templates[i], "utf-8"), name, env)); + } catch (e2) { + if (opts.force) { + console.error(e2); + } else { + throw e2; + } + } + } + } + return wrapper(precompiled, opts); + } + function _precompile(str, name, env) { + env = env || new Environment2([]); + var asyncFilters = env.asyncFilters; + var extensions = env.extensionsList; + var template; + name = name.replace(/\\/g, "/"); + try { + template = compiler2.compile(str, asyncFilters, extensions, name, env.opts); + } catch (err) { + throw _prettifyError(name, false, err); + } + return { + name, + template + }; + } + module2.exports = { + precompile: precompile2, + precompileString + }; +}); + +// ../../node_modules/nunjucks/src/jinja-compat.js +var require_jinja_compat = __commonJS((exports2, module2) => { + "use strict"; + function installCompat() { + "use strict"; + var runtime2 = this.runtime; + var lib2 = this.lib; + var Compiler = this.compiler.Compiler; + var Parser = this.parser.Parser; + var nodes2 = this.nodes; + var lexer2 = this.lexer; + var orig_contextOrFrameLookup = runtime2.contextOrFrameLookup; + var orig_memberLookup = runtime2.memberLookup; + var orig_Compiler_assertType; + var orig_Parser_parseAggregate; + if (Compiler) { + orig_Compiler_assertType = Compiler.prototype.assertType; + } + if (Parser) { + orig_Parser_parseAggregate = Parser.prototype.parseAggregate; + } + function uninstall() { + runtime2.contextOrFrameLookup = orig_contextOrFrameLookup; + runtime2.memberLookup = orig_memberLookup; + if (Compiler) { + Compiler.prototype.assertType = orig_Compiler_assertType; + } + if (Parser) { + Parser.prototype.parseAggregate = orig_Parser_parseAggregate; + } + } + runtime2.contextOrFrameLookup = function contextOrFrameLookup(context, frame, key) { + var val = orig_contextOrFrameLookup.apply(this, arguments); + if (val !== void 0) { + return val; + } + switch (key) { + case "True": + return true; + case "False": + return false; + case "None": + return null; + default: + return void 0; + } + }; + function getTokensState(tokens) { + return { + index: tokens.index, + lineno: tokens.lineno, + colno: tokens.colno + }; + } + if (process.env.BUILD_TYPE !== "SLIM" && nodes2 && Compiler && Parser) { + var Slice = nodes2.Node.extend("Slice", { + fields: ["start", "stop", "step"], + init: function init(lineno, colno, start, stop, step) { + start = start || new nodes2.Literal(lineno, colno, null); + stop = stop || new nodes2.Literal(lineno, colno, null); + step = step || new nodes2.Literal(lineno, colno, 1); + this.parent(lineno, colno, start, stop, step); + } + }); + Compiler.prototype.assertType = function assertType(node) { + if (node instanceof Slice) { + return; + } + orig_Compiler_assertType.apply(this, arguments); + }; + Compiler.prototype.compileSlice = function compileSlice(node, frame) { + this._emit("("); + this._compileExpression(node.start, frame); + this._emit("),("); + this._compileExpression(node.stop, frame); + this._emit("),("); + this._compileExpression(node.step, frame); + this._emit(")"); + }; + Parser.prototype.parseAggregate = function parseAggregate() { + var _this = this; + var origState = getTokensState(this.tokens); + origState.colno--; + origState.index--; + try { + return orig_Parser_parseAggregate.apply(this); + } catch (e2) { + var errState = getTokensState(this.tokens); + var rethrow = function rethrow2() { + lib2._assign(_this.tokens, errState); + return e2; + }; + lib2._assign(this.tokens, origState); + this.peeked = false; + var tok = this.peekToken(); + if (tok.type !== lexer2.TOKEN_LEFT_BRACKET) { + throw rethrow(); + } else { + this.nextToken(); + } + var node = new Slice(tok.lineno, tok.colno); + var isSlice = false; + for (var i = 0; i <= node.fields.length; i++) { + if (this.skip(lexer2.TOKEN_RIGHT_BRACKET)) { + break; + } + if (i === node.fields.length) { + if (isSlice) { + this.fail("parseSlice: too many slice components", tok.lineno, tok.colno); + } else { + break; + } + } + if (this.skip(lexer2.TOKEN_COLON)) { + isSlice = true; + } else { + var field = node.fields[i]; + node[field] = this.parseExpression(); + isSlice = this.skip(lexer2.TOKEN_COLON) || isSlice; + } + } + if (!isSlice) { + throw rethrow(); + } + return new nodes2.Array(tok.lineno, tok.colno, [node]); + } + }; + } + function sliceLookup(obj, start, stop, step) { + obj = obj || []; + if (start === null) { + start = step < 0 ? obj.length - 1 : 0; + } + if (stop === null) { + stop = step < 0 ? -1 : obj.length; + } else if (stop < 0) { + stop += obj.length; + } + if (start < 0) { + start += obj.length; + } + var results = []; + for (var i = start; ; i += step) { + if (i < 0 || i > obj.length) { + break; + } + if (step > 0 && i >= stop) { + break; + } + if (step < 0 && i <= stop) { + break; + } + results.push(runtime2.memberLookup(obj, i)); + } + return results; + } + function hasOwnProp(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + } + var ARRAY_MEMBERS = { + pop: function pop(index) { + if (index === void 0) { + return this.pop(); + } + if (index >= this.length || index < 0) { + throw new Error("KeyError"); + } + return this.splice(index, 1); + }, + append: function append(element) { + return this.push(element); + }, + remove: function remove(element) { + for (var i = 0; i < this.length; i++) { + if (this[i] === element) { + return this.splice(i, 1); + } + } + throw new Error("ValueError"); + }, + count: function count(element) { + var count2 = 0; + for (var i = 0; i < this.length; i++) { + if (this[i] === element) { + count2++; + } + } + return count2; + }, + index: function index(element) { + var i; + if ((i = this.indexOf(element)) === -1) { + throw new Error("ValueError"); + } + return i; + }, + find: function find(element) { + return this.indexOf(element); + }, + insert: function insert(index, elem) { + return this.splice(index, 0, elem); + } + }; + var OBJECT_MEMBERS = { + items: function items() { + return lib2._entries(this); + }, + values: function values() { + return lib2._values(this); + }, + keys: function keys() { + return lib2.keys(this); + }, + get: function get(key, def) { + var output = this[key]; + if (output === void 0) { + output = def; + } + return output; + }, + has_key: function has_key(key) { + return hasOwnProp(this, key); + }, + pop: function pop(key, def) { + var output = this[key]; + if (output === void 0 && def !== void 0) { + output = def; + } else if (output === void 0) { + throw new Error("KeyError"); + } else { + delete this[key]; + } + return output; + }, + popitem: function popitem() { + var keys = lib2.keys(this); + if (!keys.length) { + throw new Error("KeyError"); + } + var k = keys[0]; + var val = this[k]; + delete this[k]; + return [k, val]; + }, + setdefault: function setdefault(key, def) { + if (def === void 0) { + def = null; + } + if (!(key in this)) { + this[key] = def; + } + return this[key]; + }, + update: function update(kwargs) { + lib2._assign(this, kwargs); + return null; + } + }; + OBJECT_MEMBERS.iteritems = OBJECT_MEMBERS.items; + OBJECT_MEMBERS.itervalues = OBJECT_MEMBERS.values; + OBJECT_MEMBERS.iterkeys = OBJECT_MEMBERS.keys; + runtime2.memberLookup = function memberLookup(obj, val, autoescape) { + if (arguments.length === 4) { + return sliceLookup.apply(this, arguments); + } + obj = obj || {}; + if (lib2.isArray(obj) && hasOwnProp(ARRAY_MEMBERS, val)) { + return ARRAY_MEMBERS[val].bind(obj); + } + if (lib2.isObject(obj) && hasOwnProp(OBJECT_MEMBERS, val)) { + return OBJECT_MEMBERS[val].bind(obj); + } + return orig_memberLookup.apply(this, arguments); + }; + return uninstall; + } + module2.exports = installCompat; +}); + +// ../../node_modules/nunjucks/index.js +"use strict"; +var lib = require_lib(); +var _require = require_environment(); +var Environment = _require.Environment; +var Template = _require.Template; +var Loader = require_loader(); +var loaders = require_loaders(); +var precompile = require_precompile(); +var compiler = require_compiler(); +var parser = require_parser(); +var lexer = require_lexer(); +var runtime = require_runtime(); +var nodes = require_nodes(); +var installJinjaCompat = require_jinja_compat(); +var e; +function configure(templatesPath, opts) { + opts = opts || {}; + if (lib.isObject(templatesPath)) { + opts = templatesPath; + templatesPath = null; + } + var TemplateLoader; + if (loaders.FileSystemLoader) { + TemplateLoader = new loaders.FileSystemLoader(templatesPath, { + watch: opts.watch, + noCache: opts.noCache + }); + } else if (loaders.WebLoader) { + TemplateLoader = new loaders.WebLoader(templatesPath, { + useCache: opts.web && opts.web.useCache, + async: opts.web && opts.web.async + }); + } + e = new Environment(TemplateLoader, opts); + if (opts && opts.express) { + e.express(opts.express); + } + return e; +} +module.exports = { + Environment, + Template, + Loader, + FileSystemLoader: loaders.FileSystemLoader, + NodeResolveLoader: loaders.NodeResolveLoader, + PrecompiledLoader: loaders.PrecompiledLoader, + WebLoader: loaders.WebLoader, + compiler, + parser, + lexer, + runtime, + lib, + nodes, + installJinjaCompat, + configure, + reset: function reset() { + e = void 0; + }, + compile: function compile(src, env, path, eagerCompile) { + if (!e) { + configure(); + } + return new Template(src, env, path, eagerCompile); + }, + render: function render(name, ctx, cb) { + if (!e) { + configure(); + } + return e.render(name, ctx, cb); + }, + renderString: function renderString(src, ctx, cb) { + if (!e) { + configure(); + } + return e.renderString(src, ctx, cb); + }, + precompile: precompile ? precompile.precompile : void 0, + precompileString: precompile ? precompile.precompileString : void 0 +}; diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 36ac76c95f..602028f9b3 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "0.15.13", + "version": "0.15.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,16 +27,17 @@ "test": "backstage-cli test", "prepack": "backstage-cli prepack", "postpack": "backstage-cli postpack", - "clean": "backstage-cli clean" + "clean": "backstage-cli clean", + "build:assets": "node scripts/build-nunjucks.js" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/errors": "^0.1.4", + "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.9", - "@backstage/plugin-catalog-backend": "^0.17.4", + "@backstage/plugin-catalog-backend": "^0.18.0", "@backstage/plugin-scaffolder-common": "^0.1.1", "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.4", "@backstage/types": "^0.1.1", @@ -49,7 +50,6 @@ "command-exists": "^1.2.9", "compression": "^1.7.4", "cors": "^2.8.5", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "10.0.0", @@ -64,21 +64,24 @@ "lodash": "^4.17.21", "luxon": "^2.0.2", "morgan": "^1.10.0", + "node-fetch": "^2.6.1", "nunjucks": "^3.2.3", "octokit-plugin-create-pull-request": "^3.10.0", "uuid": "^8.2.0", "winston": "^3.2.1", - "yaml": "^1.10.0" + "yaml": "^1.10.0", + "vm2": "^3.9.5" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/test-utils": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/test-utils": "^0.1.23", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", "@types/mock-fs": "^4.13.0", "@types/nunjucks": "^3.1.4", "@types/supertest": "^2.0.8", + "esbuild": "^0.13.14", "jest-when": "^3.1.0", "mock-fs": "^5.1.0", "msw": "^0.35.0", @@ -88,7 +91,8 @@ "files": [ "dist", "migrations", - "config.d.ts" + "config.d.ts", + "assets" ], "configSchema": "config.d.ts" } diff --git a/plugins/scaffolder-backend/scripts/build-nunjucks.js b/plugins/scaffolder-backend/scripts/build-nunjucks.js new file mode 100755 index 0000000000..b195f96159 --- /dev/null +++ b/plugins/scaffolder-backend/scripts/build-nunjucks.js @@ -0,0 +1,69 @@ +#!/usr/bin/env node +/* + * Copyright 2021 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. + */ + +/* eslint-disable no-restricted-syntax */ +/* eslint-disable import/no-extraneous-dependencies */ + +const path = require('path'); + +const NUNJUCKS_LICENSE = `/** + * Copyright (c) 2012-2015, James Long + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +`; + +// This script is used to bundle nunjucks into a single script that is +// loaded into a sandbox for executing templates. +require('esbuild') + .build({ + entryPoints: [require.resolve('nunjucks')], + bundle: true, + format: 'cjs', + platform: 'node', + target: 'node14', + banner: { js: NUNJUCKS_LICENSE }, + external: ['fsevents'], + outfile: path.resolve(__dirname, '../assets/nunjucks.js.txt'), + }) + .catch(err => { + console.log(err.stack); + process.exit(1); + }); diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.test.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.test.ts new file mode 100644 index 0000000000..134b877efe --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.test.ts @@ -0,0 +1,145 @@ +/* + * Copyright 2021 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 { SecureTemplater } from './SecureTemplater'; + +describe('SecureTemplater', () => { + it('should render some templates', async () => { + const render = await SecureTemplater.loadRenderer(); + expect(render('${{ test }}', { test: 'my-value' })).toBe('my-value'); + + expect(render('${{ test | dump }}', { test: 'my-value' })).toBe( + '"my-value"', + ); + + expect( + render('${{ test | replace("my-", "our-") }}', { + test: 'my-value', + }), + ).toBe('our-value'); + + expect(() => + render('${{ invalid...syntax }}', { + test: 'my-value', + }), + ).toThrow(/expected name as lookup value, got ./); + }); + + it('should make cookiecutter compatibility available when requested', async () => { + const renderWith = await SecureTemplater.loadRenderer({ + cookiecutterCompat: true, + }); + const renderWithout = await SecureTemplater.loadRenderer(); + + // Same two tests repeated to make sure switching back and forth works + expect(renderWith('{{ 1 | jsonify }}', {})).toBe('1'); + expect(renderWith('{{ 1 | jsonify }}', {})).toBe('1'); + expect(() => renderWithout('${{ 1 | jsonify }}', {})).toThrow( + /Error: filter not found: jsonify/, + ); + expect(renderWith('{{ 1 | jsonify }}', {})).toBe('1'); + expect(() => renderWithout('${{ 1 | jsonify }}', {})).toThrow( + /Error: filter not found: jsonify/, + ); + expect(() => renderWithout('${{ 1 | jsonify }}', {})).toThrow( + /Error: filter not found: jsonify/, + ); + expect(() => renderWithout('${{ 1 | jsonify }}', {})).toThrow( + /Error: filter not found: jsonify/, + ); + expect(renderWith('{{ 1 | jsonify }}', {})).toBe('1'); + }); + + it('should make parseRepoUrl available when requested', async () => { + const parseRepoUrl = jest.fn(() => ({ + repo: 'my-repo', + owner: 'my-owner', + host: 'my-host.com', + })); + const renderWith = await SecureTemplater.loadRenderer({ parseRepoUrl }); + const renderWithout = await SecureTemplater.loadRenderer(); + + const ctx = { + repoUrl: 'https://my-host.com/my-owner/my-repo', + }; + + expect(renderWith('${{ repoUrl | parseRepoUrl | dump }}', ctx)).toBe( + JSON.stringify({ + repo: 'my-repo', + owner: 'my-owner', + host: 'my-host.com', + }), + ); + expect(renderWith('${{ repoUrl | projectSlug }}', ctx)).toBe( + 'my-owner/my-repo', + ); + expect(() => + renderWithout('${{ repoUrl | parseRepoUrl | dump }}', ctx), + ).toThrow(/Error: filter not found: parseRepoUrl/); + expect(() => renderWithout('${{ repoUrl | projectSlug }}', ctx)).toThrow( + /Error: filter not found: projectSlug/, + ); + + expect(parseRepoUrl.mock.calls).toEqual([ + ['https://my-host.com/my-owner/my-repo'], + ['https://my-host.com/my-owner/my-repo'], + ]); + }); + + it('should not allow helpers to be rewritten', async () => { + const render = await SecureTemplater.loadRenderer({ + parseRepoUrl: () => ({ + repo: 'my-repo', + owner: 'my-owner', + host: 'my-host.com', + }), + }); + + const ctx = { + repoUrl: 'https://my-host.com/my-owner/my-repo', + }; + expect( + render( + '${{ ({}).constructor.constructor("parseRepoUrl = () => JSON.stringify(`inject`)")() }}', + ctx, + ), + ).toBe(''); + + expect(render('${{ repoUrl | parseRepoUrl | dump }}', ctx)).toBe( + JSON.stringify({ + repo: 'my-repo', + owner: 'my-owner', + host: 'my-host.com', + }), + ); + }); + + it('allows pollution during a single template execution', async () => { + const render = await SecureTemplater.loadRenderer(); + + const ctx = { + x: 'foo', + }; + expect(render('${{ x }}', ctx)).toBe('foo'); + expect( + render( + '${{ ({}).constructor.constructor("Array.prototype.forEach = () => {}")() }}', + ctx, + ), + ).toBe(''); + expect(() => render('${{ x }}', ctx)).toThrow(); + }); +}); diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts new file mode 100644 index 0000000000..54c9166020 --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts @@ -0,0 +1,141 @@ +/* + * Copyright 2021 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 { VM } from 'vm2'; +import { resolvePackagePath } from '@backstage/backend-common'; +import fs from 'fs-extra'; +import { RepoSpec } from '../../scaffolder/actions/builtin/publish/util'; + +const mkScript = (nunjucksSource: string) => ` +const { render, renderCompat } = (() => { + const module = {}; + const process = { env: {} }; + const require = (pkg) => { if (pkg === 'events') { return function (){}; }}; + + ${nunjucksSource} + + const env = module.exports.configure({ + autoescape: false, + tags: { + variableStart: '\${{', + variableEnd: '}}', + }, + }); + + const compatEnv = module.exports.configure({ + autoescape: false, + tags: { + variableStart: '{{', + variableEnd: '}}', + }, + }); + compatEnv.addFilter('jsonify', compatEnv.getFilter('dump')); + + if (typeof parseRepoUrl !== 'undefined') { + const safeHelperRef = parseRepoUrl; + + env.addFilter('parseRepoUrl', repoUrl => { + return JSON.parse(safeHelperRef(repoUrl)) + }); + env.addFilter('projectSlug', repoUrl => { + const { owner, repo } = JSON.parse(safeHelperRef(repoUrl)); + return owner + '/' + repo; + }); + } + + let uninstallCompat = undefined; + + function render(str, values) { + try { + if (uninstallCompat) { + uninstallCompat(); + uninstallCompat = undefined; + } + return env.renderString(str, JSON.parse(values)); + } catch (error) { + // Make sure errors don't leak anything + throw new Error(String(error.message)); + } + } + + function renderCompat(str, values) { + try { + if (!uninstallCompat) { + uninstallCompat = module.exports.installJinjaCompat(); + } + return compatEnv.renderString(str, JSON.parse(values)); + } catch (error) { + // Make sure errors don't leak anything + throw new Error(String(error.message)); + } + } + + return { render, renderCompat }; +})(); +`; + +export interface SecureTemplaterOptions { + /* Optional implementation of the parseRepoUrl filter */ + parseRepoUrl?(repoUrl: string): RepoSpec; + + /* Enables jinja compatibility and the "jsonify" filter */ + cookiecutterCompat?: boolean; +} + +export type SecureTemplateRenderer = ( + template: string, + values: unknown, +) => string; + +export class SecureTemplater { + static async loadRenderer(options: SecureTemplaterOptions = {}) { + const { parseRepoUrl, cookiecutterCompat } = options; + let sandbox = undefined; + + if (parseRepoUrl) { + sandbox = { + parseRepoUrl: (url: string) => JSON.stringify(parseRepoUrl(url)), + }; + } + + const vm = new VM({ sandbox }); + + const nunjucksSource = await fs.readFile( + resolvePackagePath( + '@backstage/plugin-scaffolder-backend', + 'assets/nunjucks.js.txt', + ), + 'utf-8', + ); + + vm.run(mkScript(nunjucksSource)); + + const render: SecureTemplateRenderer = (template, values) => { + if (!vm) { + throw new Error('SecureTemplater has not been initialized'); + } + vm.setGlobal('templateStr', template); + vm.setGlobal('templateValues', JSON.stringify(values)); + + if (cookiecutterCompat) { + return vm.run(`renderCompat(templateStr, templateValues)`); + } + + return vm.run(`render(templateStr, templateValues)`); + }; + return render; + } +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts index 45481b47e6..b813244020 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts @@ -15,10 +15,10 @@ */ import fs from 'fs-extra'; -import { resolve as resolvePath } from 'path'; import { createTemplateAction } from '../../createTemplateAction'; import * as yaml from 'yaml'; import { Entity } from '@backstage/catalog-model'; +import { resolveSafeChildPath } from '@backstage/backend-common'; export function createCatalogWriteAction() { return createTemplateAction<{ name?: string; entity: Entity }>({ @@ -42,7 +42,7 @@ export function createCatalogWriteAction() { const { entity } = ctx.input; await fs.writeFile( - resolvePath(ctx.workspacePath, 'catalog-info.yaml'), + resolveSafeChildPath(ctx.workspacePath, 'catalog-info.yaml'), yaml.stringify(entity), ); }, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts index 9bf9b7fb90..36ea842a66 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts @@ -15,7 +15,7 @@ */ import { readdir, stat } from 'fs-extra'; -import { relative, resolve } from 'path'; +import { relative, join } from 'path'; import { createTemplateAction } from '../../createTemplateAction'; /** @@ -68,7 +68,7 @@ export async function recursiveReadDir(dir: string): Promise { const subdirs = await readdir(dir); const files = await Promise.all( subdirs.map(async subdir => { - const res = resolve(dir, subdir); + const res = join(dir, subdir); return (await stat(res)).isDirectory() ? recursiveReadDir(res) : [res]; }), ); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts index 3946110d42..b56bab0d53 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts @@ -19,7 +19,7 @@ import { JsonValue } from '@backstage/types'; import { InputError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; import fs from 'fs-extra'; -import * as path from 'path'; +import path from 'path'; export async function fetchContents({ reader, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts index 755b435149..cf1662307c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -33,6 +33,17 @@ jest.mock('./helpers', () => ({ fetchContents: jest.fn(), })); +const realFiles = Object.fromEntries( + [ + require.resolve('vm2/lib/fixasync'), + resolvePackagePath( + '@backstage/plugin-scaffolder-backend', + 'assets', + 'nunjucks.js.txt', + ), + ].map(k => [k, mockFs.load(k)]), +); + const aBinaryFile = fs.readFileSync( resolvePackagePath( '@backstage/plugin-scaffolder-backend', @@ -76,7 +87,9 @@ describe('fetch:template', () => { }); beforeEach(() => { - mockFs(); + mockFs({ + ...realFiles, + }); action = createFetchTemplateAction({ reader: Symbol('UrlReader') as unknown as UrlReader, @@ -150,6 +163,7 @@ describe('fetch:template', () => { mockFetchContents.mockImplementation(({ outputPath }) => { mockFs({ + ...realFiles, [outputPath]: { 'an-executable.sh': mockFs.file({ content: '#!/usr/bin/env bash', @@ -259,6 +273,7 @@ describe('fetch:template', () => { mockFetchContents.mockImplementation(({ outputPath }) => { mockFs({ + ...realFiles, [outputPath]: { processed: { 'templated-content-${{ values.name }}.txt': @@ -312,6 +327,7 @@ describe('fetch:template', () => { mockFetchContents.mockImplementation(({ outputPath }) => { mockFs({ + ...realFiles, [outputPath]: { '{{ cookiecutter.name }}.txt': 'static content', subdir: { @@ -366,6 +382,7 @@ describe('fetch:template', () => { mockFetchContents.mockImplementation(({ outputPath }) => { mockFs({ + ...realFiles, [outputPath]: { 'empty-dir-${{ values.count }}': {}, 'static.txt': 'static content', @@ -447,6 +464,7 @@ describe('fetch:template', () => { mockFetchContents.mockImplementation(({ outputPath }) => { mockFs({ + ...realFiles, [outputPath]: { '${{ values.name }}.njk': '${{ values.name }}: ${{ values.count }}', '${{ values.name }}.txt.jinja2': diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts index 47bfcccfdf..2e25547e61 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -14,29 +14,16 @@ * limitations under the License. */ -import { resolve as resolvePath, extname } from 'path'; +import { extname } from 'path'; import { resolveSafeChildPath, UrlReader } from '@backstage/backend-common'; import { InputError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; import { fetchContents } from './helpers'; import { createTemplateAction } from '../../createTemplateAction'; import globby from 'globby'; -import nunjucks from 'nunjucks'; import fs from 'fs-extra'; import { isBinaryFile } from 'isbinaryfile'; - -/* - * Maximise compatibility with Jinja (and therefore cookiecutter) - * using nunjucks jinja compat mode. Since this method mutates - * the global nunjucks instance, we can't enable this per-template, - * or only for templates with cookiecutter compat enabled, so the - * next best option is to explicitly enable it globally and allow - * folks to rely on jinja compatibility behaviour in fetch:template - * templates if they wish. - * - * cf. https://mozilla.github.io/nunjucks/api.html#installjinjacompat - */ -nunjucks.installJinjaCompat(); +import { SecureTemplater } from '../../../../lib/templating/SecureTemplater'; type CookieCompatInput = { copyWithoutRender?: string[]; @@ -114,7 +101,7 @@ export function createFetchTemplateAction(options: { ctx.logger.info('Fetching template content from remote URL'); const workDir = await ctx.createTemporaryDirectory(); - const templateDir = resolvePath(workDir, 'template'); + const templateDir = resolveSafeChildPath(workDir, 'template'); const targetPath = ctx.input.targetPath ?? './'; const outputDir = resolveSafeChildPath(ctx.workspacePath, targetPath); @@ -179,36 +166,6 @@ export function createFetchTemplateAction(options: { ).flat(), ); - // Create a templater - const templater = nunjucks.configure({ - ...(ctx.input.cookiecutterCompat - ? {} - : { - tags: { - // TODO(mtlewis/orkohunter): Document Why we are changing the literals? Not here, but on scaffolder docs. ADR? - variableStart: '${{', - variableEnd: '}}', - }, - }), - // We don't want this builtin auto-escaping, since uses HTML escape sequences - // like `"` - the correct way to escape strings in our case depends on - // the file type. - autoescape: false, - }); - - if (ctx.input.cookiecutterCompat) { - // The "jsonify" filter built into cookiecutter is common - // in fetch:cookiecutter templates, so when compat mode - // is enabled we alias the "dump" filter from nunjucks as - // jsonify. Dump accepts an optional `spaces` parameter - // which enables indented output, but when this parameter - // is not supplied it works identically to jsonify. - // - // cf. https://cookiecutter.readthedocs.io/en/latest/advanced/template_extensions.html?highlight=jsonify#jsonify-extension - // cf. https://mozilla.github.io/nunjucks/templating.html#dump - templater.addFilter('jsonify', templater.getFilter('dump')); - } - // Cookiecutter prefixes all parameters in templates with // `cookiecutter.`. To replicate this, we wrap our parameters // in an object with a `cookiecutter` property when compat @@ -223,6 +180,10 @@ export function createFetchTemplateAction(options: { ctx.input.values, ); + const renderTemplate = await SecureTemplater.loadRenderer({ + cookiecutterCompat: ctx.input.cookiecutterCompat, + }); + for (const location of allEntriesInTemplate) { let renderFilename: boolean; let renderContents: boolean; @@ -238,9 +199,9 @@ export function createFetchTemplateAction(options: { renderFilename = renderContents = !nonTemplatedEntries.has(location); } if (renderFilename) { - localOutputPath = templater.renderString(localOutputPath, context); + localOutputPath = renderTemplate(localOutputPath, context); } - const outputPath = resolvePath(outputDir, localOutputPath); + const outputPath = resolveSafeChildPath(outputDir, localOutputPath); // variables have been expanded to make an empty file name // this is due to a conditional like if values.my_condition then file-name.txt else empty string so skip if (outputDir === outputPath) { @@ -259,7 +220,7 @@ export function createFetchTemplateAction(options: { ); await fs.ensureDir(outputPath); } else { - const inputFilePath = resolvePath(templateDir, location); + const inputFilePath = resolveSafeChildPath(templateDir, location); if (await isBinaryFile(inputFilePath)) { ctx.logger.info( @@ -275,7 +236,7 @@ export function createFetchTemplateAction(options: { await fs.outputFile( outputPath, renderContents - ? templater.renderString(inputFileContents, context) + ? renderTemplate(inputFileContents, context) : inputFileContents, { mode: statsObj.mode }, ); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts index daa6bc249b..fbfe87b076 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts @@ -19,7 +19,7 @@ import { BitbucketIntegrationConfig, ScmIntegrationRegistry, } from '@backstage/integration'; -import fetch from 'cross-fetch'; +import fetch, { Response, RequestInit } from 'node-fetch'; import { initRepoAndPush } from '../helpers'; import { createTemplateAction } from '../../createTemplateAction'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts index 0cceb47d71..3a9dd01686 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -15,7 +15,6 @@ */ import fs from 'fs-extra'; -import path from 'path'; import { parseRepoUrl, isExecutable } from './util'; import { @@ -197,7 +196,7 @@ export const createPublishGithubPullRequestAction = ({ const fileContents = await Promise.all( localFilePaths.map(filePath => { - const absPath = path.resolve(fileRoot, filePath); + const absPath = resolveSafeChildPath(fileRoot, filePath); const base64EncodedContent = fs .readFileSync(absPath) .toString('base64'); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts index b2e2e85a6c..b933990953 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts @@ -17,13 +17,24 @@ import mockFs from 'mock-fs'; import * as winston from 'winston'; -import { getVoidLogger } from '@backstage/backend-common'; +import { getVoidLogger, resolvePackagePath } from '@backstage/backend-common'; import { NunjucksWorkflowRunner } from './NunjucksWorkflowRunner'; import { TemplateActionRegistry } from '../actions'; import { ScmIntegrations } from '@backstage/integration'; import { ConfigReader } from '@backstage/config'; import { TaskContext, TaskSpec } from './types'; +const realFiles = Object.fromEntries( + [ + require.resolve('vm2/lib/fixasync'), + resolvePackagePath( + '@backstage/plugin-scaffolder-backend', + 'assets', + 'nunjucks.js.txt', + ), + ].map(k => [k, mockFs.load(k)]), +); + describe('DefaultWorkflowRunner', () => { const logger = getVoidLogger(); let actionRegistry = new TemplateActionRegistry(); @@ -50,6 +61,7 @@ describe('DefaultWorkflowRunner', () => { winston.format.simple(); // put logform the require cache before mocking fs mockFs({ '/tmp': mockFs.directory(), + ...realFiles, }); jest.resetAllMocks(); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index 5b42eef459..a3e30c65fe 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { ScmIntegrations } from '@backstage/integration'; import { TaskContext, @@ -23,9 +24,9 @@ import { WorkflowRunner, } from './types'; import * as winston from 'winston'; -import nunjucks from 'nunjucks'; import fs from 'fs-extra'; import path from 'path'; +import nunjucks from 'nunjucks'; import { JsonObject, JsonValue } from '@backstage/types'; import { InputError } from '@backstage/errors'; import { PassThrough } from 'stream'; @@ -33,6 +34,10 @@ import { isTruthy } from './helper'; import { validate as validateJsonSchema } from 'jsonschema'; import { parseRepoUrl } from '../actions/builtin/publish/util'; import { TemplateActionRegistry } from '../actions'; +import { + SecureTemplater, + SecureTemplateRenderer, +} from '../../lib/templating/SecureTemplater'; type NunjucksWorkflowRunnerOptions = { workingDirectory: string; @@ -84,36 +89,31 @@ const createStepLogger = ({ }; export class NunjucksWorkflowRunner implements WorkflowRunner { - private readonly nunjucks: nunjucks.Environment; - - private readonly nunjucksOptions: nunjucks.ConfigureOptions = { - autoescape: false, - tags: { - variableStart: '${{', - variableEnd: '}}', - }, - }; - - constructor(private readonly options: NunjucksWorkflowRunnerOptions) { - this.nunjucks = nunjucks.configure(this.nunjucksOptions); - - // TODO(blam): let's work out how we can deprecate these. - // We shouldn't really need to be exposing these now we can deal with - // objects in the params block. - // Maybe we can expose a new RepoUrlPicker with secrets for V3 that provides an object already. - this.nunjucks.addFilter('parseRepoUrl', repoUrl => { - return parseRepoUrl(repoUrl, this.options.integrations); - }); - - this.nunjucks.addFilter('projectSlug', repoUrl => { - const { owner, repo } = parseRepoUrl(repoUrl, this.options.integrations); - return `${owner}/${repo}`; - }); - } + constructor(private readonly options: NunjucksWorkflowRunnerOptions) {} private isSingleTemplateString(input: string) { - const { parser, nodes } = require('nunjucks'); - const parsed = parser.parse(input, {}, this.nunjucksOptions); + const { parser, nodes } = nunjucks as unknown as { + parser: { + parse( + template: string, + ctx: object, + options: nunjucks.ConfigureOptions, + ): { children: { children?: unknown[] }[] }; + }; + nodes: { TemplateData: Function }; + }; + + const parsed = parser.parse( + input, + {}, + { + autoescape: false, + tags: { + variableStart: '${{', + variableEnd: '}}', + }, + }, + ); return ( parsed.children.length === 1 && @@ -121,7 +121,11 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { ); } - private render(input: T, context: TemplateContext): T { + private render( + input: T, + context: TemplateContext, + renderTemplate: SecureTemplateRenderer, + ): T { return JSON.parse(JSON.stringify(input), (_key, value) => { try { if (typeof value === 'string') { @@ -134,10 +138,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { ); // Run the templating - const templated = this.nunjucks.renderString( - wrappedDumped, - context, - ); + const templated = renderTemplate(wrappedDumped, context); // If there's an empty string returned, then it's undefined if (templated === '') { @@ -154,7 +155,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { } // Fallback to default behaviour - const templated = this.nunjucks.renderString(value, context); + const templated = renderTemplate(value, context); if (templated === '') { return undefined; @@ -179,6 +180,18 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { this.options.workingDirectory, await task.getWorkspaceName(), ); + + const { integrations } = this.options; + const renderTemplate = await SecureTemplater.loadRenderer({ + // TODO(blam): let's work out how we can deprecate this. + // We shouldn't really need to be exposing these now we can deal with + // objects in the params block. + // Maybe we can expose a new RepoUrlPicker with secrets for V3 that provides an object already. + parseRepoUrl(url: string) { + return parseRepoUrl(url, integrations); + }, + }); + try { await fs.ensureDir(workspacePath); await task.emitLog( @@ -193,7 +206,11 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { for (const step of task.spec.steps) { try { if (step.if) { - const ifResult = await this.render(step.if, context); + const ifResult = await this.render( + step.if, + context, + renderTemplate, + ); if (!isTruthy(ifResult)) { await task.emitLog( `Skipping step ${step.id} because it's if condition was false`, @@ -211,7 +228,9 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { const action = this.options.actionRegistry.get(step.action); const { taskLogger, streamLogger } = createStepLogger({ task, step }); - const input = (step.input && this.render(step.input, context)) ?? {}; + const input = + (step.input && this.render(step.input, context, renderTemplate)) ?? + {}; if (action.schema?.input) { const validateResult = validateJsonSchema( @@ -274,7 +293,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { } } - const output = this.render(task.spec.output, context); + const output = this.render(task.spec.output, context, renderTemplate); return { output }; } finally { diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 8c43d8d010..e809ae6c71 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder +## 0.11.12 + +### Patch Changes + +- 2d7d165737: Bump `react-jsonschema-form` +- 9f21236a29: Fixed a missing `await` when throwing server side errors +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.11.11 ### Patch Changes diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 0ac6910264..73fd8fae72 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -24,6 +24,7 @@ import { JSONSchema } from '@backstage/catalog-model'; import { JsonValue } from '@backstage/types'; import { Observable } from '@backstage/types'; import { default as React_2 } from 'react'; +import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TemplateEntityV1beta2 } from '@backstage/catalog-model'; @@ -213,12 +214,20 @@ export const ScaffolderFieldExtensions: React_2.ComponentType; // @public (undocumented) export const ScaffolderPage: ({ TemplateCardComponent, + groups, }: { TemplateCardComponent?: | ComponentType<{ template: TemplateEntityV1beta2; }> | undefined; + groups?: + | { + title?: string | undefined; + titleComponent?: ReactNode; + filter: (entity: Entity) => boolean; + }[] + | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "scaffolderPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -240,7 +249,8 @@ export { scaffolderPlugin }; // @public (undocumented) export const TemplateList: ({ TemplateCardComponent, -}: TemplateListProps) => JSX.Element; + group, +}: TemplateListProps) => JSX.Element | null; // Warning: (ae-missing-release-tag) "TemplateListProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -251,6 +261,11 @@ export type TemplateListProps = { template: TemplateEntityV1beta2; }> | undefined; + group?: { + title?: string; + titleComponent?: React_2.ReactNode; + filter: (entity: Entity) => boolean; + }; }; // Warning: (ae-missing-release-tag) "TemplateTypePicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index ce185403bb..2cd341d5b9 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "0.11.11", + "version": "0.11.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,9 +34,9 @@ "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", - "@backstage/errors": "^0.1.4", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", + "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.9", "@backstage/integration-react": "^0.1.14", "@backstage/plugin-catalog-react": "^0.6.4", @@ -66,10 +66,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 9e4736ab9f..e667c676f2 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -16,7 +16,7 @@ import React, { ComponentType } from 'react'; import { Routes, Route, useOutlet } from 'react-router'; -import { TemplateEntityV1beta2 } from '@backstage/catalog-model'; +import { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model'; import { ScaffolderPage } from './ScaffolderPage'; import { TemplatePage } from './TemplatePage'; import { TaskPage } from './TaskPage'; @@ -34,9 +34,14 @@ type RouterProps = { TemplateCardComponent?: | ComponentType<{ template: TemplateEntityV1beta2 }> | undefined; + groups?: Array<{ + title?: string; + titleComponent?: React.ReactNode; + filter: (entity: Entity) => boolean; + }>; }; -export const Router = ({ TemplateCardComponent }: RouterProps) => { +export const Router = ({ TemplateCardComponent, groups }: RouterProps) => { const outlet = useOutlet(); const customFieldExtensions = useElementFilter(outlet, elements => @@ -64,7 +69,10 @@ export const Router = ({ TemplateCardComponent }: RouterProps) => { + } /> | undefined; + groups?: Array<{ + title?: string; + titleComponent?: React.ReactNode; + filter: (entity: Entity) => boolean; + }>; }; export const ScaffolderPageContents = ({ TemplateCardComponent, + groups, }: ScaffolderPageProps) => { const styles = useStyles(); - const registerComponentLink = useRouteRef(registerComponentRouteRef); + const otherTemplatesGroup = { + title: groups ? 'Other Templates' : 'Templates', + filter: (entity: Entity) => { + const filtered = (groups ?? []).map(group => group.filter(entity)); + return !filtered.some(result => result === true); + }, + }; return ( @@ -96,7 +108,17 @@ export const ScaffolderPageContents = ({
- + {groups && + groups.map(group => ( + + ))} +
@@ -106,8 +128,12 @@ export const ScaffolderPageContents = ({ export const ScaffolderPage = ({ TemplateCardComponent, + groups, }: ScaffolderPageProps) => ( - + ); diff --git a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx index dcf708db10..9b66a0a9b6 100644 --- a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx +++ b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx @@ -16,10 +16,13 @@ import React, { ComponentType } from 'react'; import { + Entity, stringifyEntityRef, TemplateEntityV1beta2, } from '@backstage/catalog-model'; import { + Content, + ContentHeader, ItemCardGrid, Progress, WarningPanel, @@ -32,11 +35,31 @@ export type TemplateListProps = { TemplateCardComponent?: | ComponentType<{ template: TemplateEntityV1beta2 }> | undefined; + group?: { + title?: string; + titleComponent?: React.ReactNode; + filter: (entity: Entity) => boolean; + }; }; -export const TemplateList = ({ TemplateCardComponent }: TemplateListProps) => { +export const TemplateList = ({ + TemplateCardComponent, + group, +}: TemplateListProps) => { const { loading, error, entities } = useEntityListProvider(); const Card = TemplateCardComponent || TemplateCard; + const maybeFilteredEntities = group + ? entities.filter(e => group.filter(e)) + : entities; + const title = group ? ( + group.titleComponent || + ) : ( + + ); + + if (group && maybeFilteredEntities.length === 0) { + return null; + } return ( <> {loading && } @@ -57,16 +80,19 @@ export const TemplateList = ({ TemplateCardComponent }: TemplateListProps) => { )} - - {entities && - entities?.length > 0 && - entities.map(template => ( - - ))} - + + {title} + + {maybeFilteredEntities && + maybeFilteredEntities?.length > 0 && + maybeFilteredEntities.map((template: Entity) => ( + + ))} + + ); }; diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index af8fcfa79a..bf0c2df174 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-search-backend +## 0.2.7 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/backend-common@0.9.11 + ## 0.2.6 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 2df51e1bb4..3767944a25 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend", "description": "The Backstage backend plugin that provides your backstage app with search", - "version": "0.2.6", + "version": "0.2.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/search-common": "^0.2.0", "@backstage/plugin-search-backend-node": "^0.4.2", "@types/express": "^4.17.6", @@ -30,7 +30,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/search/package.json b/plugins/search/package.json index aaddf130bf..a032f5ef26 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -32,8 +32,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.4", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/search-common": "^0.2.1", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index a04043c789..2ea32c4f22 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index f0139d5728..0569585733 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", @@ -38,10 +38,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index f92c21ad7e..2c72306efc 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -34,8 +34,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -49,16 +49,15 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "cross-fetch": "^3.0.6", "msw": "^0.35.0" }, "files": [ diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 10eb807ba1..ac24c463c6 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md new file mode 100644 index 0000000000..92bd7ebf33 --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md @@ -0,0 +1,10 @@ +# @backstage/plugin-tech-insights-backend-module-jsonfc + +## 0.1.1 + +### Patch Changes + +- 2017de90da: Update README docs to use correct function/parameter names +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index 5504cb2d81..c7086cf98c 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend-module-jsonfc", - "version": "0.1.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/config": "^0.1.8", - "@backstage/errors": "^0.1.1", + "@backstage/errors": "^0.1.5", "@backstage/plugin-tech-insights-common": "^0.1.0", "@backstage/plugin-tech-insights-node": "^0.1.0", "ajv": "^7.0.3", @@ -43,7 +43,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/node-cron": "^2.0.4" }, "files": [ diff --git a/plugins/tech-insights-backend/CHANGELOG.md b/plugins/tech-insights-backend/CHANGELOG.md index abb8eaac6f..0982a3cdc8 100644 --- a/plugins/tech-insights-backend/CHANGELOG.md +++ b/plugins/tech-insights-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-tech-insights-backend +## 0.1.2 + +### Patch Changes + +- 2017de90da: Update README docs to use correct function/parameter names +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + ## 0.1.1 ### Patch Changes diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 5bc616db71..5852702cd6 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,15 +31,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.8", - "@backstage/errors": "^0.1.1", + "@backstage/errors": "^0.1.5", "@backstage/plugin-tech-insights-common": "^0.1.0", "@backstage/plugin-tech-insights-node": "^0.1.0", "@types/express": "^4.17.6", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "knex": "^0.95.1", @@ -53,7 +52,7 @@ }, "devDependencies": { "@backstage/backend-test-utils": "^0.1.9", - "@backstage/cli": "^0.9.0", + "@backstage/cli": "^0.9.1", "@types/supertest": "^2.0.8", "@types/node-cron": "^3.0.0", "@types/semver": "^7.3.8", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 99fce10345..5b11fbbe7c 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -31,8 +31,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -45,10 +45,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index ad7a8af85e..7c937e0bad 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-techdocs-backend +## 0.10.9 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/backend-common@0.9.11 + - @backstage/techdocs-common@0.10.8 + ## 0.10.8 ### Patch Changes diff --git a/plugins/techdocs-backend/api-report.md b/plugins/techdocs-backend/api-report.md index a6094c2bdd..f9be757e6a 100644 --- a/plugins/techdocs-backend/api-report.md +++ b/plugins/techdocs-backend/api-report.md @@ -14,6 +14,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PreparerBuilder } from '@backstage/techdocs-common'; import { PublisherBase } from '@backstage/techdocs-common'; import { TechDocsDocument } from '@backstage/techdocs-common'; +import { TokenManager } from '@backstage/backend-common'; // Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -31,6 +32,7 @@ export class DefaultTechDocsCollator implements DocumentCollator { locationTemplate, logger, catalogClient, + tokenManager, parallelismLimit, legacyPathCasing, }: TechDocsCollatorOptions); @@ -60,6 +62,7 @@ export class DefaultTechDocsCollator implements DocumentCollator { export type TechDocsCollatorOptions = { discovery: PluginEndpointDiscovery; logger: Logger_2; + tokenManager: TokenManager; locationTemplate?: string; catalogClient?: CatalogApi; parallelismLimit?: number; diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 332884c694..ffad4bc4c1 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-backend", "description": "The Backstage backend plugin that renders technical documentation for your components", - "version": "0.10.8", + "version": "0.10.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,28 +31,28 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.10", + "@backstage/backend-common": "^0.9.11", "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/errors": "^0.1.4", + "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.9", "@backstage/search-common": "^0.2.1", - "@backstage/techdocs-common": "^0.10.7", + "@backstage/techdocs-common": "^0.10.8", "@types/express": "^4.17.6", - "cross-fetch": "^3.0.6", "dockerode": "^3.3.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "9.1.0", "knex": "^0.95.1", "lodash": "^4.17.21", + "node-fetch": "^2.6.1", "p-limit": "^3.1.0", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/test-utils": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/test-utils": "^0.1.23", "@types/dockerode": "^3.3.0", "msw": "^0.35.0", "supertest": "^6.1.3" diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts index 21be228636..5f0bed55dd 100644 --- a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts +++ b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts @@ -17,6 +17,7 @@ import { PluginEndpointDiscovery, getVoidLogger, + TokenManager, } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { DefaultTechDocsCollator } from './DefaultTechDocsCollator'; @@ -87,6 +88,7 @@ const expectedEntities: Entity[] = [ describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => { let mockDiscoveryApi: jest.Mocked; + let mockTokenManager: jest.Mocked; let collator: DefaultTechDocsCollator; const worker = setupServer(); @@ -96,6 +98,10 @@ describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => { getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), getExternalBaseUrl: jest.fn(), }; + mockTokenManager = { + getToken: jest.fn().mockResolvedValue({ token: '' }), + authenticate: jest.fn(), + }; const mockConfig = new ConfigReader({ techdocs: { legacyUseCaseSensitiveTripletPaths: true, @@ -103,6 +109,7 @@ describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => { }); collator = DefaultTechDocsCollator.fromConfig(mockConfig, { discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, logger, legacyPathCasing: true, }); @@ -147,6 +154,7 @@ describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => { describe('DefaultTechDocsCollator', () => { let mockDiscoveryApi: jest.Mocked; + let mockTokenManager: jest.Mocked; let collator: DefaultTechDocsCollator; const worker = setupServer(); @@ -156,8 +164,13 @@ describe('DefaultTechDocsCollator', () => { getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), getExternalBaseUrl: jest.fn(), }; + mockTokenManager = { + getToken: jest.fn().mockResolvedValue({ token: '' }), + authenticate: jest.fn(), + }; collator = DefaultTechDocsCollator.fromConfig(new ConfigReader({}), { discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, logger, }); @@ -195,6 +208,7 @@ describe('DefaultTechDocsCollator', () => { // Provide an alternate location template. collator = new DefaultTechDocsCollator({ discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, locationTemplate: '/software/:name', logger, }); diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts index 121ef6e46d..283d0264e4 100644 --- a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts +++ b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts @@ -14,10 +14,13 @@ * limitations under the License. */ -import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { + PluginEndpointDiscovery, + TokenManager, +} from '@backstage/backend-common'; import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; import { DocumentCollator } from '@backstage/search-common'; -import fetch from 'cross-fetch'; +import fetch from 'node-fetch'; import unescape from 'lodash/unescape'; import { Logger } from 'winston'; import pLimit from 'p-limit'; @@ -34,6 +37,7 @@ interface MkSearchIndexDoc { export type TechDocsCollatorOptions = { discovery: PluginEndpointDiscovery; logger: Logger; + tokenManager: TokenManager; locationTemplate?: string; catalogClient?: CatalogApi; parallelismLimit?: number; @@ -51,6 +55,7 @@ export class DefaultTechDocsCollator implements DocumentCollator { protected locationTemplate: string; private readonly logger: Logger; private readonly catalogClient: CatalogApi; + private readonly tokenManager: TokenManager; private readonly parallelismLimit: number; private readonly legacyPathCasing: boolean; public readonly type: string = 'techdocs'; @@ -63,6 +68,7 @@ export class DefaultTechDocsCollator implements DocumentCollator { locationTemplate, logger, catalogClient, + tokenManager, parallelismLimit = 10, legacyPathCasing = false, }: TechDocsCollatorOptions) { @@ -74,6 +80,7 @@ export class DefaultTechDocsCollator implements DocumentCollator { catalogClient || new CatalogClient({ discoveryApi: discovery }); this.parallelismLimit = parallelismLimit; this.legacyPathCasing = legacyPathCasing; + this.tokenManager = tokenManager; } static fromConfig(config: Config, options: TechDocsCollatorOptions) { @@ -87,19 +94,23 @@ export class DefaultTechDocsCollator implements DocumentCollator { async execute() { const limit = pLimit(this.parallelismLimit); const techDocsBaseUrl = await this.discovery.getBaseUrl('techdocs'); - const entities = await this.catalogClient.getEntities({ - fields: [ - 'kind', - 'namespace', - 'metadata.annotations', - 'metadata.name', - 'metadata.title', - 'metadata.namespace', - 'spec.type', - 'spec.lifecycle', - 'relations', - ], - }); + const { token } = await this.tokenManager.getToken(); + const entities = await this.catalogClient.getEntities( + { + fields: [ + 'kind', + 'namespace', + 'metadata.annotations', + 'metadata.name', + 'metadata.title', + 'metadata.namespace', + 'spec.type', + 'spec.lifecycle', + 'relations', + ], + }, + { token }, + ); const docPromises = entities.items .filter(it => it.metadata?.annotations?.['backstage.io/techdocs-ref']) .map((entity: Entity) => diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index e560a895b6..d53a2faed4 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -24,7 +24,7 @@ import { PreparerBuilder, PublisherBase, } from '@backstage/techdocs-common'; -import fetch from 'cross-fetch'; +import fetch from 'node-fetch'; import express, { Response } from 'express'; import Router from 'express-promise-router'; import { Knex } from 'knex'; diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 6b8a8f0ff9..2755daea4a 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-techdocs +## 0.12.7 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + ``` + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + ``` + + More information can be found here: https://backstage.io/docs/conf/writing + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + ## 0.12.6 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 663c83118e..1677d43095 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs", "description": "The Backstage plugin that renders technical documentation for your components", - "version": "0.12.6", + "version": "0.12.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,9 +34,9 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", - "@backstage/errors": "^0.1.4", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", + "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.9", "@backstage/integration-react": "^0.1.14", "@backstage/plugin-catalog": "^0.7.3", @@ -61,10 +61,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^7.0.2", diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index be1dc658f1..abda1076ae 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -32,7 +32,6 @@ "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.7", "@types/express": "^4.17.6", - "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "leasot": "^12.0.0", diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 22a8e6b1e2..9a6098799f 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -28,8 +28,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.4", "@backstage/theme": "^0.2.13", @@ -41,10 +41,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index def12ac1d2..15b1771bde 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index f29f763666..f14735a448 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.4", - "@backstage/core-plugin-api": "^0.2.0", + "@backstage/core-components": "^0.7.5", + "@backstage/core-plugin-api": "^0.2.1", "@backstage/errors": "^0.1.3", "@backstage/theme": "^0.2.13", "@material-ui/core": "^4.12.2", @@ -36,10 +36,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.9.0", - "@backstage/core-app-api": "^0.1.22", + "@backstage/cli": "^0.9.1", + "@backstage/core-app-api": "^0.1.23", "@backstage/dev-utils": "^0.2.13", - "@backstage/test-utils": "^0.1.22", + "@backstage/test-utils": "^0.1.23", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/scripts/api-extractor.ts b/scripts/api-extractor.ts index 22eae267ee..8ca7743cf2 100644 --- a/scripts/api-extractor.ts +++ b/scripts/api-extractor.ts @@ -35,6 +35,7 @@ import { import { Program } from 'typescript'; import { DocNode, + DocSection, IDocNodeContainerParameters, TSDocTagSyntaxKind, } from '@microsoft/tsdoc'; @@ -50,6 +51,7 @@ import { DocHeading } from '@microsoft/api-documenter/lib/nodes/DocHeading'; import { CustomMarkdownEmitter } from '@microsoft/api-documenter/lib/markdown/CustomMarkdownEmitter'; import { IMarkdownEmitterContext } from '@microsoft/api-documenter/lib/markdown/MarkdownEmitter'; import { AstDeclaration } from '@microsoft/api-extractor/lib/analyzer/AstDeclaration'; +import { DocTableCell } from '@microsoft/api-documenter/lib/nodes/DocTableCell'; const tmpDir = resolvePath(__dirname, '../node_modules/.cache/api-extractor'); @@ -603,9 +605,18 @@ async function buildDocs({ }); for (const apiMember of apiModel.members) { + // This is a workaround for this check failing: https://github.com/microsoft/rushstack/blob/915aca8d8847b65981892f44f0544ccb00752792/apps/api-documenter/src/documenters/MarkdownDocumenter.ts#L991 + const description = new DocSection({ configuration }); + if (apiMember.tsdocComment !== undefined) { + this._appendAndMergeSection( + description, + apiMember.tsdocComment.summarySection, + ); + } + const row = new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), - this._createDescriptionCell(apiMember), + new DocTableCell({ configuration }, description.nodes), ]); if (apiMember.kind === 'Package') { diff --git a/yarn.lock b/yarn.lock index 1e4d402072..1628c2eea1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7617,6 +7617,14 @@ "@types/node" "*" form-data "^3.0.0" +"@types/node-fetch@^2.5.12": + version "2.5.12" + resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" + integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^14.0.10", "@types/node@^14.14.31", "@types/node@^14.14.32": version "14.17.8" resolved "https://registry.npmjs.org/@types/node/-/node-14.17.8.tgz#813b73ab7d82ac06ddfd2458b13c88459a3b319f" @@ -8832,7 +8840,7 @@ add-stream@^1.0.0: resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= -address@1.1.2, address@^1.0.1: +address@1.1.2, address@^1.0.1, address@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== @@ -10399,7 +10407,7 @@ browserslist@4.14.2: escalade "^3.0.2" node-releases "^1.1.61" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6: +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.5, browserslist@^4.16.6: version "4.18.1" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" integrity sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ== @@ -12976,7 +12984,7 @@ detect-node@^2.0.4: resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== -detect-port-alt@1.1.6: +detect-port-alt@1.1.6, detect-port-alt@^1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== @@ -13699,6 +13707,114 @@ es6-weak-map@^2.0.3: es6-iterator "^2.0.3" es6-symbol "^3.1.1" +esbuild-android-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.14.tgz#c85083ece26be3d67e6c720e088968a98409e023" + integrity sha512-Q+Xhfp827r+ma8/DJgpMRUbDZfefsk13oePFEXEIJ4gxFbNv5+vyiYXYuKm43/+++EJXpnaYmEnu4hAKbAWYbA== + +esbuild-darwin-64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.14.tgz#8e4e237ad847cc54a1d3a5caee26a746b9f0b81f" + integrity sha512-YmOhRns6QBNSjpVdTahi/yZ8dscx9ai7a6OY6z5ACgOuQuaQ2Qk2qgJ0/siZ6LgD0gJFMV8UINFV5oky5TFNQQ== + +esbuild-darwin-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.14.tgz#b3b5ebd40b2cb06ee0f6fb342dd4bdcca54ad273" + integrity sha512-Lp00VTli2jqZghSa68fx3fEFCPsO1hK59RMo1PRap5RUjhf55OmaZTZYnCDI0FVlCtt+gBwX5qwFt4lc6tI1xg== + +esbuild-freebsd-64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.14.tgz#175ecb2fa8141428cf70ea2d5f4c27534bad53e0" + integrity sha512-BKosI3jtvTfnmsCW37B1TyxMUjkRWKqopR0CE9AF2ratdpkxdR24Vpe3gLKNyWiZ7BE96/SO5/YfhbPUzY8wKw== + +esbuild-freebsd-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.14.tgz#a7d64e41d1fa581f8db7775e5200f18e67d70c4d" + integrity sha512-yd2uh0yf+fWv5114+SYTl4/1oDWtr4nN5Op+PGxAkMqHfYfLjFKpcxwCo/QOS/0NWqPVE8O41IYZlFhbEN2B8Q== + +esbuild-linux-32@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.14.tgz#14bdd4f6b6cfd35c65c835894651ba335c2117da" + integrity sha512-a8rOnS1oWSfkkYWXoD2yXNV4BdbDKA7PNVQ1klqkY9SoSApL7io66w5H44mTLsfyw7G6Z2vLlaLI2nz9MMAowA== + +esbuild-linux-64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.14.tgz#7fd56851b2982fdd0cd8447ee9858c2c5711708a" + integrity sha512-yPZSoMs9W2MC3Dw+6kflKt5FfQm6Dicex9dGIr1OlHRsn3Hm7yGMUTctlkW53KknnZdOdcdd5upxvbxqymczVQ== + +esbuild-linux-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.14.tgz#a55634d70679ba509adeafd68eebb9fd1ec5af6c" + integrity sha512-Lvo391ln9PzC334e+jJ2S0Rt0cxP47eoH5gFyv/E8HhOnEJTvm7A+RRnMjjHnejELacTTfYgFGQYPjLsi/jObQ== + +esbuild-linux-arm@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.14.tgz#bb96a99677e608b31ff61f37564326d38e846ca2" + integrity sha512-8chZE4pkKRvJ/M/iwsNQ1KqsRg2RyU5eT/x2flNt/f8F2TVrDreR7I0HEeCR50wLla3B1C3wTIOzQBmjuc6uWg== + +esbuild-linux-mips64le@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.14.tgz#6a55362a8fd1e593dea2ecc41877beed8b8184b9" + integrity sha512-MZhgxbmrWbpY3TOE029O6l5tokG9+Yoj2hW7vdit/d/VnmneqeGrSHADuDL6qXM8L5jaCiaivb4VhsyVCpdAbQ== + +esbuild-linux-ppc64le@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.14.tgz#9e0048587ece0a7f184ab147f20d077098045e7f" + integrity sha512-un7KMwS7fX1Un6BjfSZxTT8L5cV/8Uf4SAhM7WYy2XF8o8TI+uRxxD03svZnRNIPsN2J5cl6qV4n7Iwz+yhhVw== + +esbuild-netbsd-64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.14.tgz#dcab16a4bbcfa16e2e8535dadc5f64fdc891c63b" + integrity sha512-5ekKx/YbOmmlTeNxBjh38Uh5TGn5C4uyqN17i67k18pS3J+U2hTVD7rCxcFcRS1AjNWumkVL3jWqYXadFwMS0Q== + +esbuild-openbsd-64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.14.tgz#3c7453b155ebb68dc34d5aec3bd6505337bdda08" + integrity sha512-9bzvwewHjct2Cv5XcVoE1yW5YTW12Sk838EYfA46abgnhxGoFSD1mFcaztp5HHC43AsF+hQxbSFG/RilONARUA== + +esbuild-sunos-64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.14.tgz#85addf5fef6b5db154a955d4f2e88953359d75ce" + integrity sha512-mjMrZB76M6FmoiTvj/RGWilrioR7gVwtFBRVugr9qLarXMIU1W/pQx+ieEOtflrW61xo8w1fcxyHsVVGRvoQ0w== + +esbuild-windows-32@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.14.tgz#f77f98f30a5c636c44db2428ecdf9bcbbaedb1a7" + integrity sha512-GZa6mrx2rgfbH/5uHg0Rdw50TuOKbdoKCpEBitzmG5tsXBdce+cOL+iFO5joZc6fDVCLW3Y6tjxmSXRk/v20Hg== + +esbuild-windows-64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.14.tgz#bc778674c40d65150d12385e0f23eb3a0badbd0d" + integrity sha512-Lsgqah24bT7ClHjLp/Pj3A9wxjhIAJyWQcrOV4jqXAFikmrp2CspA8IkJgw7HFjx6QrJuhpcKVbCAe/xw0i2yw== + +esbuild-windows-arm64@0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.14.tgz#91a8dad35ab2c4dd27cd83860742955b25a354d7" + integrity sha512-KP8FHVlWGhM7nzYtURsGnskXb/cBCPTfj0gOKfjKq2tHtYnhDZywsUG57nk7TKhhK0fL11LcejHG3LRW9RF/9A== + +esbuild@^0.13.14: + version "0.13.14" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.13.14.tgz#98a3f7f42809abdc2b57c84565d0f713382dc1a5" + integrity sha512-xu4D+1ji9x53ocuomcY+KOrwAnWzhBu/wTEjpdgZ8I1c8i5vboYIeigMdzgY1UowYBKa2vZgVgUB32bu7gkxeg== + optionalDependencies: + esbuild-android-arm64 "0.13.14" + esbuild-darwin-64 "0.13.14" + esbuild-darwin-arm64 "0.13.14" + esbuild-freebsd-64 "0.13.14" + esbuild-freebsd-arm64 "0.13.14" + esbuild-linux-32 "0.13.14" + esbuild-linux-64 "0.13.14" + esbuild-linux-arm "0.13.14" + esbuild-linux-arm64 "0.13.14" + esbuild-linux-mips64le "0.13.14" + esbuild-linux-ppc64le "0.13.14" + esbuild-netbsd-64 "0.13.14" + esbuild-openbsd-64 "0.13.14" + esbuild-sunos-64 "0.13.14" + esbuild-windows-32 "0.13.14" + esbuild-windows-64 "0.13.14" + esbuild-windows-arm64 "0.13.14" + esbuild@^0.8.56: version "0.8.57" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.8.57.tgz#a42d02bc2b57c70bcd0ef897fe244766bb6dd926" @@ -14651,6 +14767,11 @@ filesize@6.1.0: resolved "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg== +filesize@^6.1.0: + version "6.4.0" + resolved "https://registry.npmjs.org/filesize/-/filesize-6.4.0.tgz#914f50471dd66fdca3cefe628bd0cde4ef769bcd" + integrity sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -14885,6 +15006,25 @@ fork-ts-checker-webpack-plugin@^6.0.4: semver "^7.3.2" tapable "^1.0.0" +fork-ts-checker-webpack-plugin@^6.0.5: + version "6.4.2" + resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.4.2.tgz#6d307fb4072ce4abe4d56a89c8ef060066f33d81" + integrity sha512-EqtzzRdx2mldr0KEydSN9jaNrf419gMpwkloumG6K/S7jtJc9Fl7wMJ+y+o7DLLGMMU/kouYr06agTD/YkxzIQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + form-data-encoder@^1.4.3: version "1.6.0" resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.6.0.tgz#9dd1f479836c1b1b47201667c68f8daafa800943" @@ -15463,7 +15603,7 @@ global-dirs@^3.0.0: dependencies: ini "2.0.0" -global-modules@2.0.0: +global-modules@2.0.0, global-modules@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== @@ -15907,7 +16047,7 @@ gud@^1.0.0: resolved "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== -gzip-size@5.1.1: +gzip-size@5.1.1, gzip-size@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== @@ -16577,10 +16717,10 @@ immer@8.0.1: resolved "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== -immer@^9.0.1: - version "9.0.6" - resolved "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73" - integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== +immer@^9.0.1, immer@^9.0.6: + version "9.0.7" + resolved "https://registry.npmjs.org/immer/-/immer-9.0.7.tgz#b6156bd7db55db7abc73fd2fdadf4e579a701075" + integrity sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA== immutable@>=3.8.2, immutable@^3.8.2, immutable@^3.x.x: version "3.8.2" @@ -17363,7 +17503,7 @@ is-resolvable@^1.1.0: resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-root@2.1.0: +is-root@2.1.0, is-root@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== @@ -21079,13 +21219,6 @@ node-fetch@2.6.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-fetch@2.6.5: - version "2.6.5" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" - integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== - dependencies: - whatwg-url "^5.0.0" - node-forge@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -22713,7 +22846,7 @@ pkg-dir@^5.0.0: dependencies: find-up "^5.0.0" -pkg-up@3.1.0: +pkg-up@3.1.0, pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== @@ -23791,7 +23924,7 @@ react-debounce-input@=3.2.4: lodash.debounce "^4" prop-types "^15.7.2" -react-dev-utils@^11.0.3, react-dev-utils@^11.0.4: +react-dev-utils@^11.0.3: version "11.0.4" resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" integrity sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A== @@ -23821,6 +23954,36 @@ react-dev-utils@^11.0.3, react-dev-utils@^11.0.4: strip-ansi "6.0.0" text-table "0.2.0" +react-dev-utils@^12.0.0-next.47: + version "12.0.0-next.47" + resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.0-next.47.tgz#e55c31a05eb30cfd69ca516e8b87d61724e880fb" + integrity sha512-PsE71vP15TZMmp/RZKOJC4fYD5Pvt0+wCoyG3QHclto0d4FyIJI78xGRICOOThZFROqgXYlZP6ddmeybm+jO4w== + dependencies: + "@babel/code-frame" "^7.10.4" + address "^1.1.2" + browserslist "^4.16.5" + chalk "^2.4.2" + cross-spawn "^7.0.3" + detect-port-alt "^1.1.6" + escape-string-regexp "^2.0.0" + filesize "^6.1.0" + find-up "^4.1.0" + fork-ts-checker-webpack-plugin "^6.0.5" + global-modules "^2.0.0" + globby "^11.0.1" + gzip-size "^5.1.1" + immer "^9.0.6" + is-root "^2.1.0" + loader-utils "^2.0.0" + open "^7.0.2" + pkg-up "^3.1.0" + prompts "^2.4.0" + react-error-overlay "7.0.0-next.54+1465357b" + recursive-readdir "^2.2.2" + shell-quote "^1.7.2" + strip-ansi "^6.0.0" + text-table "^0.2.0" + react-docgen-typescript@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.1.0.tgz#20db64a7fd62e63a8a9469fb4abd90600878cbb2" @@ -23870,6 +24033,11 @@ react-error-boundary@^3.1.0: dependencies: "@babel/runtime" "^7.12.5" +react-error-overlay@7.0.0-next.54+1465357b: + version "7.0.0-next.54" + resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-7.0.0-next.54.tgz#c1eb5ab86aee15e9552e6d97897b08f2bd06d140" + integrity sha512-b96CiTnZahXPDNH9MKplvt5+jD+BkxDw7q5R3jnkUXze/ux1pLv32BBZmlj0OfCUeMqyz4sAmF+0ccJGVMlpXw== + react-error-overlay@^6.0.9: version "6.0.9" resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" @@ -25685,6 +25853,11 @@ shell-quote@1.7.2: resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== +shell-quote@^1.7.2: + version "1.7.3" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + shelljs@^0.8.3, shelljs@^0.8.4: version "0.8.4" resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" @@ -27462,11 +27635,6 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - "traverse@>=0.3.0 <0.4": version "0.3.9" resolved "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" @@ -28599,6 +28767,11 @@ vm-browserify@^1.0.1: resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +vm2@^3.9.5: + version "3.9.5" + resolved "https://registry.npmjs.org/vm2/-/vm2-3.9.5.tgz#5288044860b4bbace443101fcd3bddb2a0aa2496" + integrity sha512-LuCAHZN75H9tdrAiLFf030oW7nJV5xwNMuk1ymOZwopmuK3d2H4L1Kv4+GFHgarKiLfXXLFU+7LDABHnwOkWng== + vscode-languageserver-types@^3.15.1: version "3.15.1" resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" @@ -28703,11 +28876,6 @@ web-streams-polyfill@4.0.0-beta.1: resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz#3b19b9817374b7cee06d374ba7eeb3aeb80e8c95" integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ== -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -28923,14 +29091,6 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - whatwg-url@^8.0.0, whatwg-url@^8.4.0: version "8.4.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837"