Compare commits

..

31 Commits

Author SHA1 Message Date
Teffen Ellis
117bf97eaa web: Flesh out Playwright. 2025-08-01 17:49:12 +02:00
dependabot[bot]
7e4bdac093 web: bump @sentry/browser from 9.43.0 to 10.0.0 in /web in the sentry group across 1 directory (#15911)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 9.43.0 to 10.0.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/9.43.0...10.0.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 10.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:42:54 +02:00
dependabot[bot]
5f16ea4718 core: bump github.com/prometheus/client_golang from 1.22.0 to 1.23.0 (#15908)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 10:22:50 +00:00
dependabot[bot]
d50a266d74 website: bump the build group in /website with 3 updates (#15910)
Bumps the build group in /website with 3 updates: [@rspack/binding-darwin-arm64](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack), [@rspack/binding-linux-arm64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) and [@rspack/binding-linux-x64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack).


Updates `@rspack/binding-darwin-arm64` from 1.4.10 to 1.4.11
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.4.11/packages/rspack)

Updates `@rspack/binding-linux-arm64-gnu` from 1.4.10 to 1.4.11
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.4.11/packages/rspack)

Updates `@rspack/binding-linux-x64-gnu` from 1.4.10 to 1.4.11
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.4.11/packages/rspack)

---
updated-dependencies:
- dependency-name: "@rspack/binding-darwin-arm64"
  dependency-version: 1.4.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-arm64-gnu"
  dependency-version: 1.4.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-x64-gnu"
  dependency-version: 1.4.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:14:49 +02:00
dependabot[bot]
41ebfa24da web: bump the storybook group across 1 directory with 5 updates (#15912)
Bumps the storybook group with 4 updates in the /web directory: [@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs), [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links), [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) and [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite).


Updates `@storybook/addon-docs` from 9.0.18 to 9.1.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v9.1.0/code/addons/docs)

Updates `@storybook/addon-links` from 9.0.18 to 9.1.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v9.1.0/code/addons/links)

Updates `@storybook/web-components` from 9.0.18 to 9.1.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v9.1.0/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 9.0.18 to 9.1.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v9.1.0/code/frameworks/web-components-vite)

Updates `storybook` from 9.0.18 to 9.1.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v9.1.0/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:14:23 +02:00
dependabot[bot]
8b7cc18988 web: bump typescript from 5.8.3 to 5.9.2 in /packages/docusaurus-config (#15913)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.8.3 to 5.9.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.3...v5.9.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:14:08 +02:00
dependabot[bot]
e780b7d519 web: bump typescript from 5.8.3 to 5.9.2 in /packages/esbuild-plugin-live-reload (#15914)
web: bump typescript in /packages/esbuild-plugin-live-reload

Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.8.3 to 5.9.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.3...v5.9.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:13:57 +02:00
dependabot[bot]
25894592ae web: bump typescript from 5.8.3 to 5.9.2 in /packages/eslint-config (#15915)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.8.3 to 5.9.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.3...v5.9.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:13:48 +02:00
dependabot[bot]
5021d08c69 web: bump typescript from 5.8.3 to 5.9.2 in /packages/prettier-config (#15916)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.8.3 to 5.9.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.3...v5.9.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:13:35 +02:00
authentik-automation[bot]
cb74b47674 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#15906)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-08-01 12:06:19 +02:00
dependabot[bot]
aafd81ca09 core: bump github.com/getsentry/sentry-go from 0.34.1 to 0.35.0 (#15909)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 12:05:29 +02:00
Dominic R
a4f8e15f91 website/integrations: kimai: fix order and var (#15907)
Signed-off-by: Dominic R <dominic@sdko.org>
2025-08-01 08:30:31 +01:00
Jens L.
0c20169739 web/common: fix form element alignment (#15904)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-07-31 18:18:01 +02:00
Jens L.
24ca89c439 web/flows: fix flow inspector button always showing (#15893)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-07-31 17:19:35 +02:00
dependabot[bot]
0352d31af0 web: bump @floating-ui/dom from 1.7.2 to 1.7.3 in /web (#15899)
Bumps [@floating-ui/dom](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/dom) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/dom/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/dom@1.7.3/packages/dom)

---
updated-dependencies:
- dependency-name: "@floating-ui/dom"
  dependency-version: 1.7.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 15:54:06 +02:00
dependabot[bot]
5bdbf06351 web: bump chromedriver from 138.0.4 to 138.0.5 in /web (#15900)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 138.0.4 to 138.0.5.
- [Commits](https://github.com/giggio/node-chromedriver/compare/138.0.4...138.0.5)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-version: 138.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 15:53:55 +02:00
dependabot[bot]
b3f1e7b1a2 core: bump github.com/golang-jwt/jwt/v5 from 5.2.3 to 5.3.0 (#15896)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 14:18:18 +02:00
dependabot[bot]
2dfda8833d core: bump github.com/coreos/go-oidc/v3 from 3.14.1 to 3.15.0 (#15897)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 14:18:01 +02:00
dependabot[bot]
9094b30860 core: bump astral-sh/uv from 0.8.3 to 0.8.4 (#15898)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 14:16:39 +02:00
authentik-automation[bot]
7e52e932fc core, web: update translations (#15895)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-07-31 14:15:25 +02:00
Teffen Ellis
b5fc28a3fd website: Ignore legacy API docs. (#15891) 2025-07-31 11:24:27 +00:00
dependabot[bot]
df49dd4ec8 web: bump the rollup group across 1 directory with 4 updates (#15884)
Bumps the rollup group with 4 updates in the /web directory: [@rollup/rollup-darwin-arm64](https://github.com/rollup/rollup), [@rollup/rollup-linux-arm64-gnu](https://github.com/rollup/rollup), [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) and [rollup](https://github.com/rollup/rollup).


Updates `@rollup/rollup-darwin-arm64` from 4.46.1 to 4.46.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.46.1...v4.46.2)

Updates `@rollup/rollup-linux-arm64-gnu` from 4.46.1 to 4.46.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.46.1...v4.46.2)

Updates `@rollup/rollup-linux-x64-gnu` from 4.46.1 to 4.46.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.46.1...v4.46.2)

Updates `rollup` from 4.46.1 to 4.46.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.46.1...v4.46.2)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-darwin-arm64"
  dependency-version: 4.46.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-arm64-gnu"
  dependency-version: 4.46.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.46.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: rollup
  dependency-version: 4.46.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 01:37:58 +02:00
dependabot[bot]
3f4c58a05b lifecycle/aws: bump aws-cdk from 2.1022.0 to 2.1023.0 in /lifecycle/aws (#15881)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1022.0 to 2.1023.0.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1023.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1023.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 01:37:50 +02:00
dependabot[bot]
46c9bfb0aa website: bump the build group in /website with 2 updates (#15882)
Bumps the build group in /website with 2 updates: [@swc/core-darwin-arm64](https://github.com/swc-project/swc) and [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc).


Updates `@swc/core-darwin-arm64` from 1.13.2 to 1.13.3
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.2...v1.13.3)

Updates `@swc/core-linux-arm64-gnu` from 1.13.2 to 1.13.3
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.13.2...v1.13.3)

---
updated-dependencies:
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.13.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.13.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 01:37:39 +02:00
dependabot[bot]
6d325d566c web: bump @sentry/browser from 9.42.1 to 9.43.0 in /web in the sentry group across 1 directory (#15883)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 9.42.1 to 9.43.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/9.43.0/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/9.42.1...9.43.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 9.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 01:37:21 +02:00
dependabot[bot]
5689336f61 web: bump typedoc-plugin-markdown from 4.7.1 to 4.8.0 in /packages/esbuild-plugin-live-reload (#15885)
web: bump typedoc-plugin-markdown

Bumps [typedoc-plugin-markdown](https://github.com/typedoc2md/typedoc-plugin-markdown/tree/HEAD/packages/typedoc-plugin-markdown) from 4.7.1 to 4.8.0.
- [Release notes](https://github.com/typedoc2md/typedoc-plugin-markdown/releases)
- [Changelog](https://github.com/typedoc2md/typedoc-plugin-markdown/blob/main/packages/typedoc-plugin-markdown/CHANGELOG.md)
- [Commits](https://github.com/typedoc2md/typedoc-plugin-markdown/commits/typedoc-plugin-markdown@4.8.0/packages/typedoc-plugin-markdown)

---
updated-dependencies:
- dependency-name: typedoc-plugin-markdown
  dependency-version: 4.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 01:37:07 +02:00
Teffen Ellis
e04ca70cb2 web/a11y: Tables & Modals (#15877)
web: Prep for a11y, tables, modals.
2025-07-30 19:27:11 -04:00
Teffen Ellis
6a5342f621 web/a11y: Form Inputs (#15878)
web: Prep form inputs for a11y.
2025-07-30 19:27:03 -04:00
Teffen Ellis
e250c8f514 web/a11y: Navigation Banner (#15880)
web: Prepare navbar for a11y.
2025-07-30 19:26:34 -04:00
Teffen Ellis
a4e7aa0adc web/a11y: License notice ARIA attributes. (#15872)
web: Add ARIA attributes.
2025-07-30 19:26:23 -04:00
authentik-automation[bot]
ac79acd2bc core, web: update translations (#15873)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-07-30 10:34:47 +02:00
72 changed files with 3621 additions and 1555 deletions

View File

@@ -76,7 +76,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.8.3 AS uv
FROM ghcr.io/astral-sh/uv:0.8.4 AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.5-slim-bookworm-fips AS python-base

File diff suppressed because one or more lines are too long

17
go.mod
View File

@@ -5,12 +5,12 @@ go 1.24.0
require (
beryju.io/ldap v0.1.0
github.com/avast/retry-go/v4 v4.6.1
github.com/coreos/go-oidc/v3 v3.14.1
github.com/getsentry/sentry-go v0.34.1
github.com/coreos/go-oidc/v3 v3.15.0
github.com/getsentry/sentry-go v0.35.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-openapi/runtime v0.28.0
github.com/golang-jwt/jwt/v5 v5.2.3
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
@@ -22,7 +22,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.1
github.com/prometheus/client_golang v1.22.0
github.com/prometheus/client_golang v1.23.0
github.com/redis/go-redis/v9 v9.11.0
github.com/sethvargo/go-envconfig v1.3.0
github.com/sirupsen/logrus v1.9.3
@@ -69,18 +69,17 @@ require (
github.com/oklog/ulid v1.3.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

32
go.sum
View File

@@ -16,8 +16,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -26,8 +26,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/getsentry/sentry-go v0.34.1 h1:HSjc1C/OsnZttohEPrrqKH42Iud0HuLCXpv8cU1pWcw=
github.com/getsentry/sentry-go v0.34.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
github.com/getsentry/sentry-go v0.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY=
github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
@@ -67,8 +67,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
@@ -140,14 +140,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs=
github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
@@ -211,8 +211,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1022.0",
"aws-cdk": "^2.1023.0",
"cross-env": "^10.0.0"
},
"engines": {
@@ -24,9 +24,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1022.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1022.0.tgz",
"integrity": "sha512-GHCu+tDtYMqCiElCl7Fad2/Bt2GmtXEV3dynudoAsV9PlL5ETeLmEN7jflDQxhmr7KhKpQeZJo/PM0DoWCvoHw==",
"version": "2.1023.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1023.0.tgz",
"integrity": "sha512-DWMA+IrAsBUNF2RvH7ujpDp7wSJkqTkRL8yfK4AYpEjoGY1KMaKIfxz3M3+Nk3ogM7VhZiW3OGWEOgyDF47HOQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -10,7 +10,7 @@
"node": ">=20"
},
"devDependencies": {
"aws-cdk": "^2.1022.0",
"aws-cdk": "^2.1023.0",
"cross-env": "^10.0.0"
}
}

View File

@@ -17958,9 +17958,9 @@
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -2728,9 +2728,9 @@
}
},
"node_modules/typedoc-plugin-markdown": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.7.1.tgz",
"integrity": "sha512-HN/fHLm2S6MD4HX8txfB4eWvVBzX/mEYy5U5s1KTAdh3E5uX5/lilswqTzZlPTT6fNZInAboAdFGpbAuBKnE4A==",
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.0.tgz",
"integrity": "sha512-BQqXnT9PETe6WEFf8bcsvvGEGQHbwTo/BFyY+RUIsSB05Y0Wn56iF+fK1PY2OKJJIhV4kp4dp7osaP9Bm5a0Zw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2741,9 +2741,9 @@
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -500,93 +500,6 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
"integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/type-utils": "8.38.0",
"@typescript-eslint/utils": "8.38.0",
"@typescript-eslint/visitor-keys": "8.38.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.38.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
"integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/typescript-estree": "8.38.0",
"@typescript-eslint/visitor-keys": "8.38.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.38.0",
"@typescript-eslint/types": "^8.38.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
@@ -605,48 +518,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz",
"integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/typescript-estree": "8.38.0",
"@typescript-eslint/utils": "8.38.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
@@ -661,98 +532,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.38.0",
"@typescript-eslint/tsconfig-utils": "8.38.0",
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/visitor-keys": "8.38.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/typescript-estree": "8.38.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
@@ -4694,9 +4473,9 @@
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4731,6 +4510,227 @@
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
"integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/type-utils": "8.38.0",
"@typescript-eslint/utils": "8.38.0",
"@typescript-eslint/visitor-keys": "8.38.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.38.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz",
"integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/typescript-estree": "8.38.0",
"@typescript-eslint/utils": "8.38.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
"integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/typescript-estree": "8.38.0",
"@typescript-eslint/visitor-keys": "8.38.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.38.0",
"@typescript-eslint/tsconfig-utils": "8.38.0",
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/visitor-keys": "8.38.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.38.0",
"@typescript-eslint/types": "^8.38.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/types": "8.38.0",
"@typescript-eslint/typescript-estree": "8.38.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/typescript-eslint/node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/typescript-eslint/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/typescript-eslint/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/typescript-eslint/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/unbox-primitive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",

View File

@@ -1711,9 +1711,9 @@
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",

View File

@@ -0,0 +1,175 @@
import { PageFixture } from "#e2e/fixtures/PageFixture";
import type { LocatorContext } from "#e2e/selectors/types";
import { expect, Page } from "@playwright/test";
export class FormFixture extends PageFixture {
static fixtureName = "Form";
//#region Selector Methods
//#endregion
//#region Field Methods
/**
* Set the value of a text input.
*
* @param fieldName The name of the form element.
* @param value the value to set.
*/
public fill = async (
fieldName: string,
value: string,
parent: LocatorContext = this.page,
): Promise<void> => {
const control = parent
.getByRole("textbox", {
name: fieldName,
})
.or(
parent.getByRole("spinbutton", {
name: fieldName,
}),
)
.first();
await expect(control, `Field (${fieldName}) should be visible`).toBeVisible();
await control.fill(value);
};
/**
* Set the value of a radio or checkbox input.
*
* @param fieldName The name of the form element.
* @param value the value to set.
*/
public setInputCheck = async (
fieldName: string,
value: boolean = true,
parent: LocatorContext = this.page,
): Promise<void> => {
const control = parent.locator("ak-switch-input", {
hasText: fieldName,
});
await control.scrollIntoViewIfNeeded();
await expect(control, `Field (${fieldName}) should be visible`).toBeVisible();
const currentChecked = await control
.getAttribute("checked")
.then((value) => value !== null);
if (currentChecked === value) {
return;
}
await control.click();
};
/**
* Set the value of a radio or checkbox input.
*
* @param fieldName The name of the form element.
* @param pattern the value to set.
*/
public setRadio = async (
groupName: string,
fieldName: string,
parent: LocatorContext = this.page,
): Promise<void> => {
const group = parent.getByRole("group", { name: groupName });
await expect(group, `Field "${groupName}" should be visible`).toBeVisible();
const control = parent.getByRole("radio", { name: fieldName });
await control.setChecked(true, {
force: true,
});
};
/**
* Set the value of a search select input.
*
* @param fieldLabel The name of the search select element.
* @param pattern The text to match against the search select entry.
*/
public selectSearchValue = async (
fieldLabel: string,
pattern: string | RegExp,
parent: LocatorContext = this.page,
): Promise<void> => {
const control = parent.getByRole("textbox", { name: fieldLabel });
await expect(
control,
`Search select control (${fieldLabel}) should be visible`,
).toBeVisible();
const fieldName = await control.getAttribute("name");
if (!fieldName) {
throw new Error(`Unable to find name attribute on search select (${fieldLabel})`);
}
// Find the search select input control and activate it.
await control.click();
const button = this.page
// ---
.locator(`div[data-managed-for*="${fieldName}"] button`, {
hasText: pattern,
});
if (!button) {
throw new Error(
`Unable to find an ak-search-select entry matching ${fieldLabel}:${pattern.toString()}`,
);
}
await button.click();
await this.page.keyboard.press("Tab");
await control.blur();
};
public setFormGroup = async (
pattern: string | RegExp,
value: boolean = true,
parent: LocatorContext = this.page,
) => {
const control = parent
.locator("ak-form-group", {
hasText: pattern,
})
.first();
const currentOpen = await control.getAttribute("open").then((value) => value !== null);
if (currentOpen === value) {
this.logger.debug(`Form group ${pattern} is already ${value ? "open" : "closed"}`);
return;
}
this.logger.debug(`Toggling form group ${pattern} to ${value ? "open" : "closed"}`);
await control.click();
if (value) {
await expect(control).toHaveAttribute("open");
} else {
await expect(control).not.toHaveAttribute("open");
}
};
//#endregion
//#region Lifecycle
constructor(page: Page, testName: string) {
super({ page, testName });
}
//#endregion
}

View File

@@ -0,0 +1,30 @@
import { ConsoleLogger, FixtureLogger } from "#logger/node";
import { Page } from "@playwright/test";
export interface PageFixtureOptions {
page: Page;
testName: string;
}
export abstract class PageFixture {
/**
* The name of the fixture.
*
* Used for logging.
*/
static fixtureName: string;
protected readonly logger: FixtureLogger;
protected readonly page: Page;
protected readonly testName: string;
constructor({ page, testName }: PageFixtureOptions) {
this.page = page;
this.testName = testName;
const Constructor = this.constructor as typeof PageFixture;
this.logger = ConsoleLogger.fixture(Constructor.fixtureName, this.testName);
}
}

View File

@@ -0,0 +1,42 @@
import { PageFixture } from "#e2e/fixtures/PageFixture";
import type { LocatorContext } from "#e2e/selectors/types";
import { Page } from "@playwright/test";
export type GetByRoleParameters = Parameters<Page["getByRole"]>;
export type ARIARole = GetByRoleParameters[0];
export type ARIAOptions = GetByRoleParameters[1];
export type ClickByName = (name: string) => Promise<void>;
export type ClickByRole = (
role: ARIARole,
options?: ARIAOptions,
context?: LocatorContext,
) => Promise<void>;
export class PointerFixture extends PageFixture {
public static fixtureName = "Pointer";
public click = (
name: string,
optionsOrRole?: ARIAOptions | ARIARole,
context: LocatorContext = this.page,
): Promise<void> => {
if (typeof optionsOrRole === "string") {
return context.getByRole(optionsOrRole, { name }).click();
}
const options = {
...optionsOrRole,
name,
};
return (
context
// ---
.getByRole("button", options)
.or(context.getByRole("link", options))
.click()
);
};
}

View File

@@ -0,0 +1,119 @@
import { PageFixture } from "#e2e/fixtures/PageFixture";
import { expect, Page } from "@playwright/test";
export const GOOD_USERNAME = "test-admin@goauthentik.io";
export const GOOD_PASSWORD = "test-runner";
export const BAD_USERNAME = "bad-username@bad-login.io";
export const BAD_PASSWORD = "-this-is-a-bad-password-";
export interface LoginInit {
username?: string;
password?: string;
to?: URL | string;
}
export class SessionFixture extends PageFixture {
static fixtureName = "Session";
public static readonly pathname = "/if/flow/default-authentication-flow/";
//#region Selectors
public $identificationStage = this.page.locator("ak-stage-identification");
/**
* The username field on the login page.
*/
public $usernameField = this.$identificationStage.locator('input[name="uidField"]');
/**
* The button to continue with the login process,
* typically to the password flow stage.
*/
public $submitUsernameStageButton = this.$identificationStage.locator('button[type="submit"]');
public $passwordStage = this.page.locator("ak-stage-password");
public $passwordField = this.$passwordStage.locator('input[name="password"]');
/**
* The button to submit the the login flow,
* typically redirecting to the authenticated interface.
*/
public $submitPasswordStageButton = this.$passwordStage.locator('button[type="submit"]');
/**
* A possible authentication failure message.
*/
public $authFailureMessage = this.page.locator(".pf-m-error");
//#endregion
constructor(page: Page, testName: string) {
super({ page, testName });
}
//#region Specific interactions
public async submitUsernameStage(username: string) {
this.logger.info("Submitting username stage", username);
await this.$usernameField.fill(username);
await expect(this.$submitUsernameStageButton).toBeEnabled();
await this.$submitUsernameStageButton.click();
}
public async submitPasswordStage(password: string) {
this.logger.info("Submitting password stage");
await this.$passwordField.fill(password);
await expect(this.$submitPasswordStageButton).toBeEnabled();
await this.$submitPasswordStageButton.click();
}
public checkAuthenticated = async (): Promise<boolean> => {
// TODO: Check if the user is authenticated via API
return true;
};
/**
* Log into the application.
*/
public async login({
username = GOOD_USERNAME,
password = GOOD_PASSWORD,
to = SessionFixture.pathname,
}: LoginInit = {}) {
this.logger.info("Logging in...");
const initialURL = new URL(this.page.url());
if (initialURL.pathname === SessionFixture.pathname) {
this.logger.info("Skipping navigation because we're already in a authentication flow");
} else {
await this.page.goto(to.toString());
}
await this.submitUsernameStage(username);
await this.$passwordField.waitFor({ state: "visible" });
await this.submitPasswordStage(password);
const expectedPathname = typeof to === "string" ? to : to.pathname;
await this.page.waitForURL(`**${expectedPathname}`);
}
//#endregion
//#region Navigation
public async toLoginPage() {
await this.page.goto(SessionFixture.pathname);
}
}

56
web/e2e/index.ts Normal file
View File

@@ -0,0 +1,56 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { createLocatorProxy, DeepLocatorProxy } from "#e2e/elements/proxy";
import { FormFixture } from "#e2e/fixtures/FormFixture";
import { PointerFixture } from "#e2e/fixtures/PointerFixture";
import { SessionFixture } from "#e2e/fixtures/SessionFixture";
import { createOUIDNameEngine } from "#e2e/selectors/ouid";
import { test as base } from "@playwright/test";
export { expect } from "@playwright/test";
type TestIDLocatorProxy = DeepLocatorProxy<TestIDSelectorMap>;
interface E2EFixturesTestScope {
/**
* A proxy to retrieve elements by test ID.
*
* ```ts
* const $button = $.button;
* ```
*/
$: TestIDLocatorProxy;
session: SessionFixture;
pointer: PointerFixture;
form: FormFixture;
}
interface E2EWorkerScope {
selectorRegistration: void;
}
export const test = base.extend<E2EFixturesTestScope, E2EWorkerScope>({
selectorRegistration: [
async ({ playwright }, use) => {
await playwright.selectors.register("ouid", createOUIDNameEngine);
await use();
},
{ auto: true, scope: "worker" },
],
$: async ({ page }, use) => {
await use(createLocatorProxy<TestIDSelectorMap>(page));
},
session: async ({ page }, use, { title }) => {
await use(new SessionFixture(page, title));
},
form: async ({ page }, use, { title }) => {
await use(new FormFixture(page, title));
},
pointer: async ({ page }, use, { title }) => {
await use(new PointerFixture({ page, testName: title }));
},
});

44
web/e2e/selectors/ouid.ts Normal file
View File

@@ -0,0 +1,44 @@
/* eslint-disable no-console */
type SelectorRoot = Document | ShadowRoot;
export function createOUIDNameEngine() {
const attributeName = "data-ouid-component-name";
console.log("Creating OUID selector engine!!");
return {
// Returns all elements matching given selector in the root's subtree.
queryAll(scope: SelectorRoot, componentName: string) {
const result: Element[] = [];
const match = (element: Element) => {
const name = element.getAttribute(attributeName);
if (name === componentName) {
result.push(element);
}
};
const query = (root: Element | ShadowRoot | Document) => {
const shadows: ShadowRoot[] = [];
if ((root as Element).shadowRoot) {
shadows.push((root as Element).shadowRoot!);
}
for (const element of root.querySelectorAll("*")) {
match(element);
if (element.shadowRoot) {
shadows.push(element.shadowRoot);
}
}
shadows.forEach(query);
};
query(scope);
return result;
},
};
}

View File

@@ -0,0 +1,13 @@
import type { Locator } from "@playwright/test";
export type LocatorContext = Pick<
Locator,
| "locator"
| "getByRole"
| "getByTestId"
| "getByText"
| "getByLabel"
| "getByAltText"
| "getByTitle"
| "getByPlaceholder"
>;

View File

@@ -0,0 +1,60 @@
import { IDGenerator } from "@goauthentik/core/id";
import {
adjectives,
colors,
Config as NameConfig,
uniqueNamesGenerator,
} from "unique-names-generator";
/**
* Given a dictionary of words, slice the dictionary to only include words that start with the given letter.
*/
export function alliterate(dictionary: string[], letter: string): string[] {
let firstIndex = 0;
for (let i = 0; i < dictionary.length; i++) {
if (dictionary[i][0] === letter) {
firstIndex = i;
break;
}
}
let lastIndex = firstIndex;
for (let i = firstIndex; i < dictionary.length; i++) {
if (dictionary[i][0] !== letter) {
lastIndex = i;
break;
}
}
return dictionary.slice(firstIndex, lastIndex);
}
export function createRandomName({
seed = IDGenerator.randomID(),
...config
}: Partial<NameConfig> = {}) {
const randomLetterIndex =
typeof seed === "number"
? seed
: Array.from(seed).reduce((acc, char) => acc + char.charCodeAt(0), 0);
const letter = adjectives[randomLetterIndex % adjectives.length][0];
const availableAdjectives = alliterate(adjectives, letter);
const availableColors = alliterate(colors, letter);
const name = uniqueNamesGenerator({
dictionaries: [availableAdjectives, availableAdjectives, availableColors],
style: "capital",
separator: " ",
length: 3,
seed,
...config,
});
return name;
}

102
web/logger/node.js Normal file
View File

@@ -0,0 +1,102 @@
/**
* Application logger.
*
* @import { LoggerOptions, Logger, Level, ChildLoggerOptions } from "pino"
* @import { PrettyOptions } from "pino-pretty"
*/
import { pino } from "pino";
//#region Constants
/**
* Default options for creating a Pino logger.
*
* @category Logger
* @satisfies {LoggerOptions<never, false>}
*/
export const DEFAULT_PINO_LOGGER_OPTIONS = {
enabled: true,
level: "info",
transport: {
target: "./transport.js",
options: /** @satisfies {PrettyOptions} */ ({
colorize: true,
}),
},
};
//#endregion
//#region Functions
/**
* Read the log level from the environment.
* @return {Level}
*/
export function readLogLevel() {
return process.env.AK_LOG_LEVEL || DEFAULT_PINO_LOGGER_OPTIONS.level;
}
/**
* @typedef {Logger} FixtureLogger
*/
/**
* @this {Logger}
* @param {string} fixtureName
* @param {string} [testName]
* @param {ChildLoggerOptions} [options]
* @returns {FixtureLogger}
*/
function createFixtureLogger(fixtureName, testName, options) {
return this.child(
{ name: fixtureName },
{
msgPrefix: `[${testName}] `,
...options,
},
);
}
/**
* @typedef {object} CustomLoggerMethods
* @property {typeof createFixtureLogger} fixture
*/
/**
* @typedef {Logger & CustomLoggerMethods} ConsoleLogger
*/
/**
* A singleton logger instance for Node.js.
*
* ```js
* import { ConsoleLogger } from "#logger/node";
*
* ConsoleLogger.info("Hello, world!");
* ```
*
* @runtime node
* @type {ConsoleLogger}
*/
export const ConsoleLogger = Object.assign(
pino({
...DEFAULT_PINO_LOGGER_OPTIONS,
level: readLogLevel(),
}),
{ fixture: createFixtureLogger },
);
/**
* @typedef {ReturnType<ConsoleLogger['child']>} ChildConsoleLogger
*/
//#region Aliases
export const info = ConsoleLogger.info.bind(ConsoleLogger);
export const debug = ConsoleLogger.debug.bind(ConsoleLogger);
export const warn = ConsoleLogger.warn.bind(ConsoleLogger);
export const error = ConsoleLogger.error.bind(ConsoleLogger);
//#endregion

22
web/logger/transport.js Normal file
View File

@@ -0,0 +1,22 @@
/**
* @file Pretty transport for Pino
*
* @import { PrettyOptions } from "pino-pretty"
*/
import PinoPretty from "pino-pretty";
/**
* @param {PrettyOptions} options
*/
function prettyTransporter(options) {
const pretty = PinoPretty({
...options,
ignore: "pid,hostname",
translateTime: "SYS:HH:MM:ss",
});
return pretty;
}
export default prettyTransporter;

1762
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -91,7 +91,7 @@
"@codemirror/legacy-modes": "^6.5.1",
"@codemirror/theme-one-dark": "^6.1.3",
"@eslint/js": "^9.31.0",
"@floating-ui/dom": "^1.7.2",
"@floating-ui/dom": "^1.7.3",
"@formatjs/intl-listformat": "^7.7.11",
"@fortawesome/fontawesome-free": "^7.0.0",
"@goauthentik/api": "^2025.6.4-1753714826",
@@ -113,12 +113,12 @@
"@openlayers-elements/maps": "^0.4.0",
"@patternfly/elements": "^4.1.0",
"@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^9.42.1",
"@sentry/browser": "^10.0.0",
"@spotlightjs/spotlight": "^3.0.1",
"@storybook/addon-docs": "^9.0.18",
"@storybook/addon-links": "^9.0.18",
"@storybook/web-components": "^9.0.18",
"@storybook/web-components-vite": "^9.0.18",
"@storybook/addon-docs": "^9.1.0",
"@storybook/addon-links": "^9.1.0",
"@storybook/web-components": "^9.1.0",
"@storybook/web-components-vite": "^9.1.0",
"@types/codemirror": "^5.60.16",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.3",
@@ -137,7 +137,7 @@
"change-case": "^5.4.4",
"chart.js": "^4.5.0",
"chartjs-adapter-date-fns": "^3.0.0",
"chromedriver": "^138.0.4",
"chromedriver": "^138.0.5",
"codemirror": "^6.0.2",
"construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.44.0",
@@ -184,6 +184,8 @@
"typescript": "^5.8.3",
"typescript-eslint": "^8.38.0",
"unist-util-visit": "^5.0.0",
"vite": "^7.0.6",
"vitest": "^3.2.4",
"webcomponent-qr-code": "^1.3.0",
"wireit": "^0.14.12",
"yaml": "^2.8.0"
@@ -192,9 +194,9 @@
"@esbuild/darwin-arm64": "^0.25.4",
"@esbuild/linux-arm64": "^0.25.4",
"@esbuild/linux-x64": "^0.25.4",
"@rollup/rollup-darwin-arm64": "^4.46.1",
"@rollup/rollup-linux-arm64-gnu": "^4.46.1",
"@rollup/rollup-linux-x64-gnu": "^4.46.1"
"@rollup/rollup-darwin-arm64": "^4.46.2",
"@rollup/rollup-linux-arm64-gnu": "^4.46.2",
"@rollup/rollup-linux-x64-gnu": "^4.46.2"
},
"wireit": {
"build": {

View File

@@ -23,7 +23,7 @@
"formdata-polyfill": "^4.0.10",
"jquery": "^3.7.1",
"prettier": "^3.5.3",
"rollup": "^4.46.1",
"rollup": "^4.46.2",
"rollup-plugin-copy": "^3.5.0",
"weakmap-polyfill": "^2.0.4"
},

94
web/playwright.config.js Normal file
View File

@@ -0,0 +1,94 @@
/**
* @file Playwright configuration.
*
* @see https://playwright.dev/docs/test-configuration
*
* @import { LogFn, Logger } from "pino"
*/
import { ConsoleLogger } from "#logger/node";
import { defineConfig, devices } from "@playwright/test";
const CI = !!process.env.CI;
/**
* @type {Map<string, Logger>}
*/
const LoggerCache = new Map();
const baseURL = process.env.AK_TEST_RUNNER_PAGE_URL ?? "http://localhost:9000";
export default defineConfig({
testDir: "./test/browser",
fullyParallel: true,
forbidOnly: CI,
retries: CI ? 2 : 0,
workers: CI ? 1 : undefined,
reporter: CI
? "github"
: [
// ---
["list", { printSteps: true }],
["html", { open: "never" }],
],
use: {
testIdAttribute: "data-test-id",
baseURL,
trace: "on-first-retry",
launchOptions: {
logger: {
isEnabled() {
return true;
},
log: (name, severity, message, args) => {
let logger = LoggerCache.get(name);
if (!logger) {
logger = ConsoleLogger.child({
name: `Playwright ${name.toUpperCase()}`,
});
LoggerCache.set(name, logger);
}
/**
* @type {LogFn}
*/
let log;
switch (severity) {
case "verbose":
log = logger.debug;
break;
case "warning":
log = logger.warn;
break;
case "error":
log = logger.error;
break;
default:
log = logger.info;
break;
}
if (name === "api") {
log = logger.debug;
}
log.call(logger, message.toString(), args);
},
},
},
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
},
},
],
});

View File

@@ -10,25 +10,35 @@ import { html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
@customElement("ak-license-notice")
export class AkLicenceNotice extends WithLicenseSummary(AKElement) {
export class AKLicenceNotice extends WithLicenseSummary(AKElement) {
static styles = [$PFBase];
@property()
notice = msg("Enterprise only");
public label = msg("Enterprise only");
@property()
public description = msg("Learn more about the enterprise license.");
render() {
return this.hasEnterpriseLicense
? nothing
: html`
<ak-alert class="pf-c-radio__description" inline plain>
<a href="#/enterprise/licenses">${this.notice}</a>
</ak-alert>
`;
if (this.hasEnterpriseLicense) {
return nothing;
}
return html`
<ak-alert class="pf-c-radio__description" inline plain>
<a
aria-label="${this.label}"
aria-description="${this.description}"
href="#/enterprise/licenses"
>${this.label}</a
>
</ak-alert>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-license-notice": AkLicenceNotice;
"ak-license-notice": AKLicenceNotice;
}
}

View File

@@ -3,7 +3,7 @@ import { ModelForm } from "#elements/forms/ModelForm";
import { msg } from "@lit/localize";
export abstract class BaseProviderForm<T> extends ModelForm<T, number> {
getSuccessMessage(): string {
public override getSuccessMessage(): string {
return this.instance
? msg("Successfully updated provider.")
: msg("Successfully created provider.");

View File

@@ -29,32 +29,38 @@ import { customElement, property } from "lit/decorators.js";
@customElement("ak-provider-list")
export class ProviderListPage extends TablePage<Provider> {
searchEnabled(): boolean {
override searchEnabled(): boolean {
return true;
}
pageTitle(): string {
override pageTitle(): string {
return msg("Providers");
}
pageDescription(): string {
override pageDescription(): string {
return msg("Provide support for protocols like SAML and OAuth to assigned applications.");
}
pageIcon(): string {
override pageIcon(): string {
return "pf-icon pf-icon-integration";
}
checkbox = true;
clearOnRefresh = true;
override checkbox = true;
override clearOnRefresh = true;
@property()
order = "name";
public order = "name";
async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
public searchLabel = msg("Provider name");
public searchPlaceholder = msg("Search for providers…");
override async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
return new ProvidersApi(DEFAULT_CONFIG).providersAllList(
await this.defaultEndpointConfig(),
);
}
columns(): TableColumn[] {
override columns(): TableColumn[] {
return [
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Application")),
@@ -63,8 +69,9 @@ export class ProviderListPage extends TablePage<Provider> {
];
}
renderToolbarSelected(): TemplateResult {
override renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Provider(s)")}
.objects=${this.selectedElements}
@@ -85,7 +92,7 @@ export class ProviderListPage extends TablePage<Provider> {
</ak-forms-delete-bulk>`;
}
rowApp(item: Provider): TemplateResult {
#rowApp(item: Provider): TemplateResult {
if (item.assignedApplicationName) {
return html`<i class="pf-icon pf-icon-ok pf-m-success"></i>
${msg("Assigned to application ")}
@@ -93,6 +100,7 @@ export class ProviderListPage extends TablePage<Provider> {
>${item.assignedApplicationName}</a
>`;
}
if (item.assignedBackchannelApplicationName) {
return html`<i class="pf-icon pf-icon-ok pf-m-success"></i>
${msg("Assigned to application (backchannel) ")}
@@ -100,15 +108,15 @@ export class ProviderListPage extends TablePage<Provider> {
>${item.assignedBackchannelApplicationName}</a
>`;
}
return html`<i class="pf-icon pf-icon-warning-triangle pf-m-warning"></i> ${msg(
"Warning: Provider not assigned to any application.",
)}`;
return html`<i aria-hidden="true" class="pf-icon pf-icon-warning-triangle pf-m-warning"></i>
${msg("Warning: Provider not assigned to any application.")}`;
}
row(item: Provider): TemplateResult[] {
override row(item: Provider): TemplateResult[] {
return [
html`<a href="#/core/providers/${item.pk}"> ${item.name} </a>`,
this.rowApp(item),
this.#rowApp(item),
html`${item.verboseName}`,
html`<ak-forms-modal>
<span slot="submit"> ${msg("Update")} </span>
@@ -121,16 +129,20 @@ export class ProviderListPage extends TablePage<Provider> {
type=${item.component}
>
</ak-proxy-form>
<button slot="trigger" class="pf-c-button pf-m-plain">
<button
aria-label=${msg("Edit provider")}
slot="trigger"
class="pf-c-button pf-m-plain"
>
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i>
<i aria-hidden="true" class="fas fa-edit"></i>
</pf-tooltip>
</button>
</ak-forms-modal>`,
];
}
renderObjectCreate(): TemplateResult {
override renderObjectCreate(): TemplateResult {
return html`<ak-provider-wizard> </ak-provider-wizard> `;
}
}

View File

@@ -413,7 +413,7 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
`;
}
renderSidebarBefore(): TemplateResult {
protected renderSidebarBefore(): TemplateResult {
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
<div class="pf-c-card">
<div class="pf-c-card__title">${msg("User folders")}</div>

View File

@@ -35,11 +35,6 @@
--ak-navbar--height: 7rem;
}
.pf-c-form__group {
--pf-c-form--m-horizontal__group-label--md--GridColumnWidth: minmax(max-content, 9.375rem);
column-gap: var(--pf-global--spacer--md);
}
@supports selector(::-webkit-scrollbar) {
::-webkit-scrollbar {
width: 5px;

View File

@@ -1,18 +1,21 @@
import "#elements/forms/HorizontalFormElement";
import { SlottedTemplateResult } from "../elements/types";
import { AKElement, type AKElementProps } from "#elements/Base";
import { IDGenerator } from "@goauthentik/core/id";
import { html, nothing, TemplateResult } from "lit";
import { property } from "lit/decorators.js";
type HelpType = TemplateResult | typeof nothing;
import { ifDefined } from "lit/directives/if-defined.js";
export interface HorizontalLightComponentProps<T> extends AKElementProps {
name: string;
label?: string;
required?: boolean;
help?: string;
bighelp?: TemplateResult | TemplateResult[];
bighelp?: SlottedTemplateResult | SlottedTemplateResult[];
hidden?: boolean;
invalid?: boolean;
errorMessages?: string[];
@@ -20,7 +23,10 @@ export interface HorizontalLightComponentProps<T> extends AKElementProps {
inputHint?: string;
}
export class HorizontalLightComponent<T> extends AKElement {
export abstract class HorizontalLightComponent<T>
extends AKElement
implements HorizontalLightComponentProps<T>
{
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
// we're not actually using that and, for the meantime, we need the form handlers to be able to
// find the children of this component.
@@ -46,7 +52,7 @@ export class HorizontalLightComponent<T> extends AKElement {
* @attribute
*/
@property({ type: String, reflect: true })
label = "";
label?: string;
/**
* @property
@@ -104,16 +110,19 @@ export class HorizontalLightComponent<T> extends AKElement {
* @attribute
*/
@property({ type: String, attribute: "input-hint" })
inputHint = "";
inputHint?: string;
protected renderControl() {
throw new Error("Must be implemented in a subclass");
}
renderHelp(): HelpType[] {
const bigHelp: HelpType[] = Array.isArray(this.bighelp)
protected fieldID = IDGenerator.elementID().toString();
protected renderHelp(): SlottedTemplateResult | SlottedTemplateResult[] {
const bigHelp: SlottedTemplateResult[] = Array.isArray(this.bighelp)
? this.bighelp
: [this.bighelp ?? nothing];
return [
this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing,
...bigHelp,
@@ -121,17 +130,16 @@ export class HorizontalLightComponent<T> extends AKElement {
}
render() {
// prettier-ignore
return html`<ak-form-element-horizontal
label=${this.label}
fieldID=${this.fieldID}
label=${ifDefined(this.label)}
?required=${this.required}
?hidden=${this.hidden}
name=${this.name}
.errorMessages=${this.errorMessages}
?invalid=${this.invalid}
>
${this.renderControl()}
${this.renderHelp()}
>
${this.renderControl()} ${this.renderHelp()}
</ak-form-element-horizontal> `;
}
}

View File

@@ -6,8 +6,6 @@ import {
HorizontalLightComponentProps,
} from "./HorizontalLightComponent.js";
import { bound } from "#elements/decorators/bound";
import { msg } from "@lit/localize";
import { css, html } from "lit";
import { customElement, property, query } from "lit/decorators.js";
@@ -24,6 +22,8 @@ export interface AkHiddenTextInputProps extends BaseProps {
export type InputLike = HTMLTextAreaElement | HTMLInputElement;
export type InputListener = (ev: InputEvent) => void;
/**
* @element ak-hidden-text-input
* @class AkHiddenTextInput
@@ -72,6 +72,15 @@ export class AkHiddenTextInput<T extends InputLike = HTMLInputElement>
@property({ type: String })
public placeholder?: string;
/**
* Text for when the input has no set value
*
* @property
* @attribute
*/
@property({ type: String })
public label?: string;
/**
* Specify kind of help the browser should try to provide
*
@@ -98,28 +107,16 @@ export class AkHiddenTextInput<T extends InputLike = HTMLInputElement>
@query("#main > input")
protected inputField!: T;
@bound
private handleToggleVisibility() {
this.revealed = !this.revealed;
// Maintain focus on input after toggle
this.updateComplete.then(() => {
if (this.inputField && document.activeElement === this) {
this.inputField.focus();
}
});
}
// TODO: Because of the peculiarities of how HorizontalLightComponent works, keeping its content
// in the LightDom so the inner components actually inherit styling, the normal `css` options
// aren't available. Embedding styles is bad styling, and we'll fix it in the next style
// refresh.
protected renderInputField(setValue: (ev: InputEvent) => void, code: boolean) {
protected renderInputField(setValue: InputListener, code: boolean) {
return html` <input
style="flex: 1 1 auto; min-width: 0;"
part="input"
autocomplete=${ifDefined(this.autocomplete)}
type=${this.revealed ? "text" : "password"}
aria-label=${ifDefined(this.label)}
@input=${setValue}
value=${ifDefined(this.value)}
placeholder=${ifDefined(this.placeholder)}
@@ -134,12 +131,12 @@ export class AkHiddenTextInput<T extends InputLike = HTMLInputElement>
protected override renderControl() {
const code = this.inputHint === "code";
const setValue = (ev: InputEvent) => {
const setValue: InputListener = (ev) => {
this.value = (ev.target as T).value;
};
return html` <div style="display: flex; gap: 0.25rem">
${this.renderInputField(setValue, code)}
<!-- -->
<ak-visibility-toggle
part="toggle"
style="flex: 0 0 auto; align-self: flex-start"

View File

@@ -1,4 +1,8 @@
import { AkHiddenTextInput, type AkHiddenTextInputProps } from "./ak-hidden-text-input.js";
import {
AkHiddenTextInput,
type AkHiddenTextInputProps,
InputListener,
} from "./ak-hidden-text-input.js";
import { html } from "lit";
import { customElement, property, query } from "lit/decorators.js";
@@ -96,7 +100,7 @@ export class AkHiddenTextAreaInput
// in the LightDom so the inner components actually inherit styling, the normal `css` options
// aren't available. Embedding styles is bad styling, and we'll fix it in the next style
// refresh.
protected override renderInputField(setValue: (ev: InputEvent) => void, code: boolean) {
protected override renderInputField(setValue: InputListener, code: boolean) {
const wrap = this.revealed ? this.wrap : "soft";
return html`
@@ -105,6 +109,7 @@ export class AkHiddenTextAreaInput
part="textarea"
@input=${setValue}
placeholder=${ifDefined(this.placeholder)}
aria-label=${ifDefined(this.label)}
rows=${ifDefined(this.rows)}
cols=${ifDefined(this.cols)}
wrap=${ifDefined(wrap)}

View File

@@ -175,6 +175,7 @@ export class NavigationButtons extends AKElement {
return html`<img
class="pf-c-page__header-tools-item pf-c-avatar pf-m-hidden pf-m-visible-on-xl"
src=${ifDefined(this.me?.user.avatar)}
aria-hidden="true"
alt="${msg("Avatar image")}"
/>`;
}
@@ -189,7 +190,7 @@ export class NavigationButtons extends AKElement {
}
render() {
return html`<div class="pf-c-page__header-tools">
return html`<div role="presentation" class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
${this.renderApiDrawerTrigger()}
<!-- -->

View File

@@ -21,6 +21,7 @@ export class AkNumberInput extends HorizontalLightComponent<number> {
return html`<input
type="number"
@input=${setValue}
aria-label=${ifDefined(this.label)}
value=${ifDefined(this.value)}
min=${ifDefined(this.min)}
class="pf-c-form-control"

View File

@@ -88,7 +88,7 @@ export class AKPageNavbar
color: var(--ak-dark-foreground);
}
navbar {
.main-content {
border-bottom: var(--pf-global--BorderWidth--sm);
border-bottom-style: solid;
border-bottom-color: var(--pf-global--BorderColor--100);
@@ -350,7 +350,12 @@ export class AKPageNavbar
renderIcon() {
if (this.icon) {
if (this.iconImage && !this.icon.startsWith("fa://")) {
return html`<img class="accent-icon pf-icon" src="${this.icon}" alt="page icon" />`;
return html`<img
aria-hidden="true"
class="accent-icon pf-icon"
src="${this.icon}"
alt="page icon"
/>`;
}
const icon = this.icon.replaceAll("fa://", "fa ");
@@ -362,9 +367,9 @@ export class AKPageNavbar
render(): TemplateResult {
return html` <slot></slot>
<navbar aria-label="Main" class="navbar">
<aside class="brand ${this.open ? "" : "pf-m-collapsed"}">
<a href="#/">
<div role="banner" aria-label="Main" class="main-content">
<aside role="presentation" class="brand ${this.open ? "" : "pf-m-collapsed"}">
<a aria-label="${msg("Home")}" href="#/">
<div class="logo">
<img
src=${themeImage(this.brandingLogo)}
@@ -375,31 +380,35 @@ export class AKPageNavbar
</a>
</aside>
<button
aria-controls="global-nav"
class="sidebar-trigger pf-c-button pf-m-plain"
@click=${this.#toggleSidebar}
aria-label=${msg("Toggle sidebar")}
aria-label=${this.open ? msg("Collapse navigation") : msg("Expand navigation")}
aria-expanded=${this.open ? "true" : "false"}
>
<i class="fas fa-bars"></i>
<i aria-hidden="true" class="fas fa-bars"></i>
</button>
<section
class="items primary pf-c-content ${this.description ? "block-sibling" : ""}"
>
<h1 class="page-title">
<div class="items primary pf-c-content ${this.description ? "block-sibling" : ""}">
<h1 aria-labelledby="page-navbar-heading" class="page-title">
${this.hasIcon
? html`<slot name="icon">${this.renderIcon()}</slot>`
? html`<slot aria-hidden="true" name="icon">${this.renderIcon()}</slot>`
: nothing}
${this.header}
<span id="page-navbar-heading">${this.header}</span>
</h1>
</section>
</div>
${this.description
? html`<section class="items page-description pf-c-content">
? html`<div
role="heading"
aria-level="2"
aria-label="${this.description}"
class="items page-description pf-c-content"
>
<p>${this.description}</p>
</section>`
</div>`
: nothing}
<section class="items secondary">
<div class="items secondary">
<div class="pf-c-page__header-tools-group">
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.session}>
<a
@@ -411,8 +420,8 @@ export class AKPageNavbar
</a>
</ak-nav-buttons>
</div>
</section>
</navbar>`;
</div>
</div>`;
}
//#endregion

View File

@@ -3,9 +3,11 @@ import "#elements/forms/Radio";
import { HorizontalLightComponent } from "./HorizontalLightComponent.js";
import { RadioOption } from "#elements/forms/Radio";
import { SlottedTemplateResult } from "#elements/types";
import { html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-radio-input")
export class AkRadioInput<T> extends HorizontalLightComponent<T> {
@@ -21,20 +23,20 @@ export class AkRadioInput<T> extends HorizontalLightComponent<T> {
}
}
renderHelp() {
// This is weird, but Typescript says it's necessary?
return [nothing as typeof nothing];
protected override renderHelp(): SlottedTemplateResult {
return nothing;
}
renderControl() {
const helpText = this.help.trim();
return html`<ak-radio
label=${ifDefined(this.label)}
.options=${this.options}
.value=${this.value}
@input=${this.handleInput}
></ak-radio>
${this.help.trim()
? html`<p class="pf-c-form__helper-radio">${this.help}</p>`
: nothing}`;
${helpText ? html`<p class="pf-c-form__helper-radio">${helpText}</p>` : nothing}`;
}
}

View File

@@ -1,7 +1,10 @@
import { AKElement } from "#elements/Base";
import { IDGenerator } from "@goauthentik/core/id";
import { html, nothing } from "lit";
import { customElement, property, query } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
@customElement("ak-switch-input")
export class AkSwitchInput extends AKElement {
@@ -34,12 +37,21 @@ export class AkSwitchInput extends AKElement {
@query("input.pf-c-switch__input[type=checkbox]")
checkbox!: HTMLInputElement;
#fieldID: string = IDGenerator.randomID();
render() {
const doCheck = this.checked ? this.checked : undefined;
const helpText = this.help.trim();
return html` <ak-form-element-horizontal name=${this.name} ?required=${this.required}>
<label class="pf-c-switch">
<input class="pf-c-switch__input" type="checkbox" ?checked=${doCheck} />
<label class="pf-c-switch" for="${this.#fieldID}">
<input
id="${this.#fieldID}"
class="pf-c-switch__input"
type="checkbox"
?checked=${doCheck}
aria-label=${ifDefined(this.label)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
@@ -47,7 +59,7 @@ export class AkSwitchInput extends AKElement {
</span>
<span class="pf-c-switch__label">${this.label}</span>
</label>
${this.help.trim() ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing}
${helpText ? html`<p class="pf-c-form__helper-text">${helpText}</p>` : nothing}
</ak-form-element-horizontal>`;
}
}

View File

@@ -10,6 +10,9 @@ export class AkTextInput extends HorizontalLightComponent<string> {
@property({ type: String, reflect: true })
value = "";
@property({ type: String })
autocomplete?: string;
@property({ type: String })
placeholder?: string;
@@ -22,14 +25,17 @@ export class AkTextInput extends HorizontalLightComponent<string> {
return html` <input
type="text"
role="textbox"
id=${ifDefined(this.fieldID)}
@input=${setValue}
value=${ifDefined(this.value)}
class="${classMap({
"pf-c-form-control": true,
"pf-m-monospace": code,
})}"
autocomplete=${ifDefined(code ? "off" : undefined)}
autocomplete=${ifDefined(code ? "off" : this.autocomplete)}
spellcheck=${ifDefined(code ? "false" : undefined)}
aria-label=${ifDefined(this.placeholder || this.label)}
placeholder=${ifDefined(this.placeholder)}
?required=${this.required}
/>`;

View File

@@ -44,12 +44,20 @@ const testOptions = [
];
export const CheckboxGroup = () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const displayChange = (ev: any) => {
document.getElementById("check-message-pad")!.innerHTML = `
<p>Values selected on target: ${ev.target.value.join(", ")}</p>
<p>Values sent in event: ${ev.detail.join(", ")}</p>
<p>Values present as data-ak-control: <kbd>${JSON.stringify(ev.target.json, null)}</kbd></p>`;
const displayChange = (event: CustomEvent<string[]>) => {
const target = event.target as AkCheckboxGroup;
document.getElementById("check-message-pad")!.innerHTML = /*html*/ `
<p>
Values selected on target: ${target.value.join(", ")}
</p>
<p>
Values sent in event: ${event.detail.join(", ")}
</p>
<p>
Values present as data-ak-control: <kbd>${JSON.stringify(target.json(), null)}</kbd>
</p>
`;
};
return container(
@@ -66,28 +74,32 @@ export const CheckboxGroup = () => {
);
};
type FDType = [string, string | FormDataEntryValue];
type FDType = [key: string, value: string | FormDataEntryValue];
export const FormCheckboxGroup = () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const displayChange = (ev: any) => {
ev.preventDefault();
const formData = new FormData(ev.target);
const displayChange = (event: SubmitEvent) => {
event.preventDefault();
const valList = Array.from(formData)
.map(([_key, val]: FDType) => val)
.join(", ");
if (!(event.target instanceof HTMLFormElement)) {
throw new Error("Expected target to be a form element");
}
const fdList = Array.from(formData)
.map(
([key, val]: FDType) =>
`${encodeURIComponent(key)}=${encodeURIComponent(val as string)}`,
)
.join("&");
const formData = new FormData(event.target);
document.getElementById("check-message-pad")!.innerHTML = `
<p>Values as seen in \`form.formData\`: ${valList}</p>
<p>Values as seen in x-form-encoded format: <kbd>${fdList}</kbd></p>`;
const valList = Array.from(formData.values()).join(", ");
const fdList = Array.from(formData, ([key, val]: FDType) => {
return `${encodeURIComponent(key)}=${encodeURIComponent(val as string)}`;
}).join("&");
document.getElementById("check-message-pad")!.innerHTML = /*html*/ `
<p>
Values as seen in ${"`form.formData`"}: ${valList}
</p>
<p>
Values as seen in x-form-encoded format: <kbd>${fdList}</kbd>
</p>
`;
};
return container(
@@ -95,9 +107,9 @@ export const FormCheckboxGroup = () => {
FormData example. This variant emits the same events and exhibits the same behavior
as the above, but instead of monitoring for 'change' events on the checkbox group,
we monitor for the user pressing the 'submit' button. What is displayed is the
values as understood by the &lt;form&gt; object, via its internal \`formData\`
field, to demonstrate that this component works with forms as if it were a native
form element.
values as understood by the &lt;form&gt; object, via its internal
${"`form.formData`"} field, to demonstrate that this component works with forms as
if it were a native form element.
</p>
<form @submit=${displayChange}>

View File

@@ -116,8 +116,8 @@ export abstract class ModalButton extends AKElement {
* @abstract
*/
protected renderModal(): SlottedTemplateResult {
return html`<div class="pf-c-backdrop" @click=${this.#backdropListener}>
<div class="pf-l-bullseye">
return html`<div class="pf-c-backdrop" @click=${this.#backdropListener} role="presentation">
<div class="pf-l-bullseye" role="presentation">
<div
class="pf-c-modal-box ${this.size} ${this.locked ? "locked" : ""}"
role="dialog"

View File

@@ -20,7 +20,7 @@ import PFList from "@patternfly/patternfly/components/List/list.css";
type BulkDeleteMetadata = { key: string; value: string }[];
@customElement("ak-delete-objects-table")
export class DeleteObjectsTable<T> extends Table<T> {
export class DeleteObjectsTable<T extends object> extends Table<T> {
paginated = false;
@property({ attribute: false })
@@ -246,7 +246,7 @@ export class DeleteBulkForm<T> extends ModalButton {
declare global {
interface HTMLElementTagNameMap {
"ak-delete-objects-table": DeleteObjectsTable<unknown>;
"ak-forms-delete-bulk": DeleteBulkForm<unknown>;
"ak-delete-objects-table": DeleteObjectsTable<object>;
"ak-forms-delete-bulk": DeleteBulkForm<object>;
}
}

View File

@@ -58,10 +58,6 @@ export class HorizontalFormElement extends AKElement {
grid-template-columns:
var(--pf-c-form--m-horizontal__group-label--md--GridColumnWidth)
var(--pf-c-form--m-horizontal__group-control--md--GridColumnWidth);
&[data-flow-direction="row"] {
grid-template-columns: 1fr;
}
}
.pf-c-form__group-label {

View File

@@ -100,7 +100,7 @@ export class ModalForm extends ModalButton {
);
};
renderModalInner(): TemplateResult {
protected renderModalInner(): TemplateResult {
return html`${this.loading
? html`<ak-loading-overlay topmost></ak-loading-overlay>`
: nothing}

View File

@@ -1,8 +1,8 @@
import { randomId } from "../utils/randomId.js";
import { AKElement } from "#elements/Base";
import { CustomEmitterElement } from "#elements/utils/eventEmitter";
import { IDGenerator } from "@goauthentik/core/id";
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { map } from "lit/directives/map.js";
@@ -22,15 +22,15 @@ export interface RadioOption<T> {
@customElement("ak-radio")
export class Radio<T> extends CustomEmitterElement(AKElement) {
@property({ attribute: false })
options: RadioOption<T>[] = [];
public options: RadioOption<T>[] = [];
@property()
name = "";
public name = "";
@property({ attribute: false })
value?: T;
public value?: T;
internalId: string;
#fieldID: string = this.name || IDGenerator.randomID();
static styles: CSSResult[] = [
PFBase,
@@ -46,16 +46,13 @@ export class Radio<T> extends CustomEmitterElement(AKElement) {
.pf-c-radio span {
user-select: none;
}
.pf-c-radio__description {
text-wrap: balance;
}
`,
];
constructor() {
super();
this.renderRadio = this.renderRadio.bind(this);
this.buildChangeHandler = this.buildChangeHandler.bind(this);
this.internalId = this.name || `radio-${randomId(8)}`;
}
// Set the value if it's not set already. Property changes inside the `willUpdate()` method do
// not trigger an element update.
willUpdate() {
@@ -71,42 +68,48 @@ export class Radio<T> extends CustomEmitterElement(AKElement) {
// radio loses its setting, and the selected radio gains its setting. We want radio buttons to
// present a unified event interface, so we prevent the event from triggering if the value is
// already set.
buildChangeHandler(option: RadioOption<T>) {
#buildChangeListener = (option: RadioOption<T>) => {
return (ev: Event) => {
// This is a controlled input. Stop the native event from escaping or affecting the
// value. We'll do that ourselves.
// value. We'll do that ourselves.
ev.stopPropagation();
if (option.disabled) {
return;
}
this.value = option.value;
this.dispatchCustomEvent("change", { value: option.value });
this.dispatchCustomEvent("input", { value: option.value });
};
}
};
renderRadio(option: RadioOption<T>, index: number) {
const elId = `${this.internalId}-${index}`;
const handler = this.buildChangeHandler(option);
return html`<div class="pf-c-radio" @click=${handler}>
#renderRadio = (option: RadioOption<T>, index: number) => {
const id = `${this.#fieldID}-${index}`;
const changeListener = this.#buildChangeListener(option);
return html`<div class="pf-c-radio" @click=${changeListener}>
<input
class="pf-c-radio__input"
type="radio"
name="${this.name}"
id=${elId}
aria-label=${option.label}
id=${id}
.checked=${option.value === this.value}
.disabled=${option.disabled}
/>
<label class="pf-c-radio__label" for=${elId}>${option.label}</label>
<label class="pf-c-radio__label" for=${id}>${option.label}</label>
${option.description
? html`<span class="pf-c-radio__description">${option.description}</span>`
: nothing}
</div>`;
}
};
render() {
return html`<div class="pf-c-form__group-control pf-m-stack">
${map(this.options, this.renderRadio)}
${map(this.options, this.#renderRadio)}
</div>`;
}
}

View File

@@ -5,6 +5,9 @@ import "#elements/chips/ChipGroup";
import "#elements/table/TablePagination";
import "#elements/table/TableSearch";
import { TableLike } from "./shared.js";
import { TableColumn } from "./TableColumn.js";
import { EVENT_REFRESH } from "#common/constants";
import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
import { uiConfig } from "#common/ui/config";
@@ -17,7 +20,7 @@ import { SlottedTemplateResult } from "#elements/types";
import { Pagination } from "@goauthentik/api";
import { msg } from "@lit/localize";
import { msg, str } from "@lit/localize";
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
import { property, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
@@ -32,10 +35,8 @@ import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";
import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
export interface TableLike {
order?: string;
fetch: () => void;
}
export * from "./shared.js";
export * from "./TableColumn.js";
export interface PaginatedResponse<T> {
pagination: Pagination;
@@ -44,138 +45,10 @@ export interface PaginatedResponse<T> {
results: Array<T>;
}
export class TableColumn {
title: string;
orderBy?: string;
onClick?: () => void;
constructor(title: string, orderBy?: string) {
this.title = title;
this.orderBy = orderBy;
}
headerClickHandler(table: TableLike): void {
if (!this.orderBy) {
return;
}
table.order = table.order === this.orderBy ? `-${this.orderBy}` : this.orderBy;
table.fetch();
}
private getSortIndicator(table: TableLike): string {
switch (table.order) {
case this.orderBy:
return "fa-long-arrow-alt-down";
case `-${this.orderBy}`:
return "fa-long-arrow-alt-up";
default:
return "fa-arrows-alt-v";
}
}
renderSortable(table: TableLike): TemplateResult {
return html` <button
class="pf-c-table__button"
@click=${() => this.headerClickHandler(table)}
>
<div class="pf-c-table__button-content">
<span class="pf-c-table__text">${this.title}</span>
<span class="pf-c-table__sort-indicator">
<i class="fas ${this.getSortIndicator(table)}"></i>
</span>
</div>
</button>`;
}
render(table: TableLike): TemplateResult {
const classes = {
"pf-c-table__sort": !!this.orderBy,
"pf-m-selected": table.order === this.orderBy || table.order === `-${this.orderBy}`,
};
return html`<th role="columnheader" scope="col" class="${classMap(classes)}">
${this.orderBy ? this.renderSortable(table) : html`${this.title}`}
</th>`;
}
}
export abstract class Table<T> extends WithLicenseSummary(AKElement) implements TableLike {
abstract apiEndpoint(): Promise<PaginatedResponse<T>>;
abstract columns(): TableColumn[];
abstract row(item: T): SlottedTemplateResult[];
private isLoading = false;
#pageParam = `${this.tagName.toLowerCase()}-page`;
#searchParam = `${this.tagName.toLowerCase()}-search`;
@property({ type: Boolean })
supportsQL: boolean = false;
searchEnabled(): boolean {
return false;
}
renderExpanded(_item: T): SlottedTemplateResult {
if (this.expandable) {
throw new Error("Expandable is enabled but renderExpanded is not overridden!");
}
return nothing;
}
@property({ attribute: false })
data?: PaginatedResponse<T>;
@property({ type: Number })
page = getURLParam(this.#pageParam, 1);
/**
* Set if your `selectedElements` use of the selection box is to enable bulk-delete,
* so that stale data is cleared out when the API returns a new list minus the deleted entries.
*
* @prop
*/
@property({ attribute: "clear-on-refresh", type: Boolean, reflect: true })
clearOnRefresh = false;
@property({ type: String })
order?: string;
@property({ type: String })
search: string = "";
@property({ type: Boolean })
checkbox = false;
@property({ type: Boolean })
clickable = false;
@property({ attribute: false })
clickHandler: (item: T) => void = () => {};
@property({ type: Boolean })
radioSelect = false;
@property({ type: Boolean })
checkboxChip = false;
@property({ attribute: false })
selectedElements: T[] = [];
@property({ type: Boolean })
paginated = true;
@property({ type: Boolean })
expandable = false;
@property({ attribute: false })
expandedElements: T[] = [];
@state()
error?: APIError;
export abstract class Table<T extends object>
extends WithLicenseSummary(AKElement)
implements TableLike
{
static styles: CSSResult[] = [
PFBase,
PFTable,
@@ -213,16 +86,124 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
`,
];
constructor() {
super();
this.addEventListener(EVENT_REFRESH, async () => {
await this.fetch();
});
abstract apiEndpoint(): Promise<PaginatedResponse<T>>;
abstract columns(): TableColumn[];
abstract row(item: T): SlottedTemplateResult[];
#loading = false;
#pageParam = `${this.tagName.toLowerCase()}-page`;
#searchParam = `${this.tagName.toLowerCase()}-search`;
@property({ type: Boolean })
public supportsQL: boolean = false;
//#region Properties
@property({ type: String })
public toolbarLabel = msg("Table actions");
@property({ type: String })
public label?: string;
@property({ attribute: false })
public data?: PaginatedResponse<T>;
@property({ type: Number })
public page = getURLParam(this.#pageParam, 1);
/**
* Set if your `selectedElements` use of the selection box is to enable bulk-delete,
* so that stale data is cleared out when the API returns a new list minus the deleted entries.
*
* @prop
*/
@property({ attribute: "clear-on-refresh", type: Boolean, reflect: true })
public clearOnRefresh = false;
@property({ type: String })
public order?: string;
@property({ type: String })
public search: string = "";
@property({ type: Boolean })
public checkbox = false;
@property({ type: Boolean })
public clickable = false;
@property({ attribute: false })
public clickHandler: (item: T) => void = () => {};
@property({ type: Boolean })
public radioSelect = false;
@property({ type: Boolean })
public checkboxChip = false;
@property({ attribute: false })
public selectedElements: T[] = [];
@property({ type: Boolean })
public paginated = true;
@property({ type: Boolean })
public expandable = false;
@property({ attribute: false })
public expandedElements: T[] = [];
@property({ attribute: false })
public searchLabel?: string;
@property({ attribute: false })
public searchPlaceholder?: string;
//#endregion
//#region Lifecycle
@state()
protected error?: APIError;
#refreshListener = () => {
return this.fetch();
};
public override connectedCallback(): void {
super.connectedCallback();
this.addEventListener(EVENT_REFRESH, this.#refreshListener);
if (this.searchEnabled()) {
this.search = getURLParam(this.#searchParam, "");
}
}
public override disconnectedCallback(): void {
super.disconnectedCallback();
this.removeEventListener(EVENT_REFRESH, this.#refreshListener);
}
protected willUpdate(changedProperties: PropertyValues<this>): void {
if (changedProperties.has("page")) {
updateURLParams({
[this.#pageParam]: this.page,
});
}
if (changedProperties.has("search")) {
updateURLParams({
[this.#searchParam]: this.search,
});
}
}
firstUpdated(): void {
this.fetch();
}
//#endregion
async defaultEndpointConfig() {
return {
ordering: this.order,
@@ -232,16 +213,12 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
};
}
public groupBy(items: T[]): [SlottedTemplateResult, T[]][] {
return groupBy(items, () => {
return "";
});
}
public fetch(): Promise<void> {
if (this.#loading) {
return Promise.resolve();
}
public async fetch(): Promise<void> {
if (this.isLoading) return;
this.isLoading = true;
this.#loading = true;
return this.apiEndpoint()
.then((data) => {
@@ -289,12 +266,14 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
this.error = await parseAPIResponseError(error);
})
.finally(() => {
this.isLoading = false;
this.#loading = false;
this.requestUpdate();
});
}
private renderLoading(): TemplateResult {
//#region Render
protected renderLoading(): TemplateResult {
return html`<tr role="row">
<td role="cell" colspan="25">
<div class="pf-l-bullseye">
@@ -320,11 +299,21 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
</tbody>`;
}
renderObjectCreate(): SlottedTemplateResult {
/**
* Render the create object button.
*
* @abstract
*/
protected renderObjectCreate(): SlottedTemplateResult {
return nothing;
}
renderError(): SlottedTemplateResult {
/**
* Render the error state.
*
* @abstract
*/
protected renderError(): SlottedTemplateResult {
if (!this.error) return nothing;
return html`<ak-empty-state icon="fa-ban"
@@ -333,11 +322,27 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
</ak-empty-state>`;
}
//#region Rows
/**
* Render a row for a given item.
*
* @param item The item to render.
*/
protected rowLabel<T extends object>(item: T): string | typeof nothing {
const name = "name" in item && typeof item.name === "string" ? item.name.trim() : null;
if (!name) {
return nothing;
}
return msg(str`${name}`);
}
private renderRows(): TemplateResult[] | undefined {
if (this.error) {
return [this.renderEmpty(this.renderError())];
}
if (!this.data || this.isLoading) {
if (!this.data || this.#loading) {
return [this.renderLoading()];
}
if (this.data.pagination.count === 0) {
@@ -357,7 +362,23 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
});
}
//#region Grouping
public groupBy(items: T[]): [SlottedTemplateResult, T[]][] {
return groupBy(items, () => "");
}
renderExpanded(_item: T): SlottedTemplateResult {
if (this.expandable) {
throw new Error("Expandable is enabled but renderExpanded is not overridden!");
}
return nothing;
}
private renderRowGroup(items: T[]): TemplateResult[] {
const columns = this.columns();
return items.map((item) => {
const itemSelectHandler = (ev: InputEvent | PointerEvent) => {
const target = ev.target as HTMLElement;
@@ -388,7 +409,7 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
};
const renderCheckbox = () =>
html`<td class="pf-c-table__check" role="cell">
html`<td aria-label="${msg("Select row")}" class="pf-c-table__check" role="button">
<label class="ignore-click"
><input
type="checkbox"
@@ -428,9 +449,9 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
</td>`;
};
return html`<tbody role="rowgroup" class="${classMap(expandedClass)}">
return html`<tbody class="${classMap(expandedClass)}">
<tr
role="row"
aria-label="${this.rowLabel(item)}"
class="${this.checkbox || this.clickable ? "pf-m-hoverable" : ""}"
@click=${this.clickable
? () => {
@@ -441,7 +462,13 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
${this.checkbox ? renderCheckbox() : nothing}
${this.expandable ? renderExpansion() : nothing}
${this.row(item).map((column, columnIndex) => {
return html`<td data-column-index="${columnIndex}" role="cell">
const columnLabel = columns[columnIndex]?.title;
return html`<td
aria-label=${ifDefined(columnLabel)}
data-column-index="${columnIndex}"
role="cell"
>
${column}
</td>`;
})}
@@ -454,7 +481,11 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
});
}
renderToolbar(): TemplateResult {
//#endregion
//#region Toolbar
protected renderToolbar(): TemplateResult {
return html` ${this.renderObjectCreate()}
<ak-spinner-button
.callAction=${() => {
@@ -474,57 +505,64 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
return nothing;
}
protected willUpdate(changedProperties: PropertyValues<this>): void {
if (changedProperties.has("page")) {
updateURLParams({
[this.#pageParam]: this.page,
});
}
if (changedProperties.has("search")) {
updateURLParams({
[this.#searchParam]: this.search,
});
}
}
renderSearch(): TemplateResult {
const runSearch = (value: string) => {
this.search = value;
this.page = 1;
this.fetch();
};
const isQL = this.supportsQL && this.hasEnterpriseLicense;
return !this.searchEnabled()
? html``
: html`<div class="pf-c-toolbar__group pf-m-search-filter ${isQL ? "ql" : ""}">
<ak-table-search
?supportsQL=${this.supportsQL}
class="pf-c-toolbar__item pf-m-search-filter ${isQL ? "ql" : ""}"
value=${ifDefined(this.search)}
.onSearch=${runSearch}
.apiResponse=${this.data}
>
</ak-table-search>
</div>`;
}
protected renderToolbarContainer(): SlottedTemplateResult {
return html`<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
return html`<header class="pf-c-toolbar" role="toolbar" aria-label="${this.toolbarLabel}">
<div role="presentation" class="pf-c-toolbar__content">
${this.renderSearch()}
<div class="pf-c-toolbar__bulk-select">${this.renderToolbar()}</div>
<div class="pf-c-toolbar__group">${this.renderToolbarAfter()}</div>
<div class="pf-c-toolbar__group">${this.renderToolbarSelected()}</div>
${this.paginated ? this.renderTablePagination() : html``}
<div role="presentation" class="pf-c-toolbar__bulk-select">
${this.renderToolbar()}
</div>
<div role="presentation" class="pf-c-toolbar__group">
${this.renderToolbarAfter()}
</div>
<div role="presentation" class="pf-c-toolbar__group">
${this.renderToolbarSelected()}
</div>
${this.paginated ? this.renderTablePagination() : nothing}
</div>
</header>`;
}
//#endregion
//#region Search
#searchListener = (value: string) => {
this.search = value;
this.page = 1;
this.fetch();
};
protected searchEnabled(): boolean {
return false;
}
protected renderSearch(): SlottedTemplateResult {
if (!this.searchEnabled()) {
return nothing;
}
const isQL = this.supportsQL && this.hasEnterpriseLicense;
return html`<div class="pf-c-toolbar__group pf-m-search-filter ${isQL ? "ql" : ""}">
<ak-table-search
class="pf-c-toolbar__item pf-m-search-filter ${isQL ? "ql" : ""}"
value=${ifDefined(this.search)}
label=${ifDefined(this.searchLabel)}
placeholder=${ifDefined(this.searchPlaceholder)}
.onSearch=${this.#searchListener}
>
</ak-table-search>
</div>`;
}
firstUpdated(): void {
this.fetch();
}
//#endregion
/* The checkbox on the table header row that allows the user to "activate all on this page,"
//#region Chips
/**
* The checkbox on the table header row that allows the user to
* "activate all on this page,"
* "deactivate all on this page" with a single click.
*/
renderAllOnThisPageCheckbox(): TemplateResult {
@@ -549,11 +587,13 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
</td>`;
}
/* For very large tables where the user is selecting a limited number of entries, we provide a
* chip-based subtable at the top that shows the list of selected entries. Long text result in
* ellipsized chips, which is sub-optimal.
/**
* For very large tables where the user is selecting a limited number of entries,
* we provide a chip-based subtable at the top that shows the list of selected entries.
*
* Long text result in ellipsized chips, which is sub-optimal.
*/
renderSelectedChip(_item: T): SlottedTemplateResult {
protected renderSelectedChip(_item: T): SlottedTemplateResult {
// Override this for chip-based displays
return nothing;
}
@@ -570,7 +610,9 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
</ak-chip-group>`;
}
/* A simple pagination display, shown at both the top and bottom of the page. */
/**
* A simple pagination display, shown at both the top and bottom of the page.
*/
protected renderTablePagination(): SlottedTemplateResult {
const handler = (page: number) => {
this.page = page;
@@ -591,19 +633,22 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
const renderBottomPagination = () =>
html`<div class="pf-c-pagination pf-m-bottom">${this.renderTablePagination()}</div>`;
return html`${this.needChipGroup ? this.renderChipGroup() : html``}
return html`${this.needChipGroup ? this.renderChipGroup() : nothing}
${this.renderToolbarContainer()}
<table class="pf-c-table pf-m-compact pf-m-grid-md pf-m-expandable">
<thead>
<tr role="row" class="pf-c-table__header-row">
${this.checkbox ? this.renderAllOnThisPageCheckbox() : html``}
${this.expandable ? html`<td role="cell"></td>` : html``}
<table
aria-label=${this.label ? msg(str`Table of ${this.label}`) : msg("Table content")}
class="pf-c-table pf-m-compact pf-m-grid-md pf-m-expandable"
>
<thead aria-label=${msg("Table actions")}>
<tr role="presentation" class="pf-c-table__header-row">
${this.checkbox ? this.renderAllOnThisPageCheckbox() : nothing}
${this.expandable ? html`<td role="cell"></td>` : nothing}
${this.columns().map((col) => col.render(this))}
</tr>
</thead>
${this.renderRows()}
</table>
${this.paginated ? renderBottomPagination() : html``}`;
${this.paginated ? renderBottomPagination() : nothing}`;
}
render(): TemplateResult {

View File

@@ -0,0 +1,81 @@
import { TableLike } from "#elements/table/shared";
import { html, TemplateResult } from "lit";
import { classMap } from "lit/directives/class-map.js";
type ARIASort = "ascending" | "descending" | "none" | "other";
export class TableColumn {
title: string;
orderBy?: string;
onClick?: () => void;
constructor(title: string, orderBy?: string) {
this.title = title;
this.orderBy = orderBy;
}
//#region Sorting
#sortButtonListener(table: TableLike): void {
if (!this.orderBy) {
return;
}
table.order = table.order === this.orderBy ? `-${this.orderBy}` : this.orderBy;
table.fetch();
}
private getSortIndicator(table: TableLike): string {
switch (this.getARIASort(table)) {
case "ascending":
return "fa-long-arrow-alt-up";
case "descending":
return "fa-long-arrow-alt-down";
default:
return "fa-arrows-alt-v";
}
}
public getARIASort(table: TableLike): ARIASort {
switch (table.order) {
case this.orderBy:
return "ascending";
case `-${this.orderBy}`:
return "descending";
default:
return "none";
}
}
protected renderSortable(table: TableLike): TemplateResult {
return html` <button
class="pf-c-table__button"
@click=${() => this.#sortButtonListener(table)}
>
<div class="pf-c-table__button-content">
<span class="pf-c-table__text">${this.title}</span>
<span class="pf-c-table__sort-indicator">
<i aria-hidden="true" class="fas ${this.getSortIndicator(table)}"></i>
</span>
</div>
</button>`;
}
public render(table: TableLike): TemplateResult {
const classes = {
"pf-c-table__sort": !!this.orderBy,
"pf-m-selected": table.order === this.orderBy || table.order === `-${this.orderBy}`,
};
return html`<th
role="columnheader"
scope="col"
aria-sort=${this.getARIASort(table)}
class="${classMap(classes)}"
>
${this.orderBy ? this.renderSortable(table) : html`${this.title}`}
</th>`;
}
}

View File

@@ -49,10 +49,11 @@ export abstract class TableModal<T extends object> extends Table<T> {
MODAL_BUTTON_STYLES,
];
public async fetch(): Promise<void> {
public override async fetch(): Promise<void> {
if (!this.open) {
return;
}
return super.fetch();
}

View File

@@ -3,22 +3,25 @@ import { AKElement } from "#elements/Base";
import { Pagination } from "@goauthentik/api";
import { msg, str } from "@lit/localize";
import { css, CSSResult, html, TemplateResult } from "lit";
import { css, CSSResult, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
export type TablePageChangeListener = (page: number) => void;
@customElement("ak-table-pagination")
export class TablePagination extends AKElement {
@property({ type: String })
label?: string;
@property({ attribute: false })
pages?: Pagination;
@property({ attribute: false })
pageChangeHandler: (page: number) => void = () => {
return;
};
onPageChange?: TablePageChangeListener;
static styles: CSSResult[] = [
PFBase,
@@ -35,28 +38,38 @@ export class TablePagination extends AKElement {
`,
];
render(): TemplateResult {
#navigatePrevious = () => {
this.onPageChange?.(this.pages?.previous || 0);
};
#navigateNext = () => {
this.onPageChange?.(this.pages?.next || 0);
};
render() {
if (!this.pages) {
return html``;
return nothing;
}
return html` <div class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md">
return html` <nav
aria-label=${this.label || msg("Table pagination")}
class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md"
>
<div class="pf-c-pagination pf-m-compact pf-m-compact pf-m-hidden pf-m-visible-on-md">
<div class="pf-c-options-menu">
<div class="pf-c-options-menu__toggle pf-m-text pf-m-plain">
<span class="pf-c-options-menu__toggle-text">
<span role="heading" aria-level="4" class="pf-c-options-menu__toggle-text">
${msg(
str`${this.pages?.startIndex} - ${this.pages?.endIndex} of ${this.pages?.count}`,
)}
</span>
</div>
</div>
<nav class="pf-c-pagination__nav" aria-label=${msg("Pagination")}>
<div class="pf-c-pagination__nav">
<div class="pf-c-pagination__nav-control pf-m-prev">
<button
class="pf-c-button pf-m-plain"
@click=${() => {
this.pageChangeHandler(this.pages?.previous || 0);
}}
@click=${this.#navigatePrevious}
?disabled="${(this.pages?.previous || 0) < 1}"
aria-label="${msg("Go to previous page")}"
>
@@ -66,18 +79,16 @@ export class TablePagination extends AKElement {
<div class="pf-c-pagination__nav-control pf-m-next">
<button
class="pf-c-button pf-m-plain"
@click=${() => {
this.pageChangeHandler(this.pages?.next || 0);
}}
@click=${this.#navigateNext}
?disabled="${(this.pages?.next || 0) <= 0}"
aria-label="${msg("Go to next page")}"
>
<i class="fas fa-angle-right" aria-hidden="true"></i>
</button>
</div>
</nav>
</div>
</div>
</div>`;
</nav>`;
}
}

View File

@@ -18,16 +18,16 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-table-search")
export class TableSearch extends WithLicenseSummary(AKElement) {
@property()
value?: string;
public value?: string;
@property({ type: Boolean })
supportsQL: boolean = false;
public supportsQL: boolean = false;
@property({ attribute: false })
apiResponse?: PaginatedResponse<unknown>;
public apiResponse?: PaginatedResponse<unknown>;
@property()
onSearch?: (value: string) => void;
public onSearch?: (value: string) => void;
static styles: CSSResult[] = [
PFBase,
@@ -45,6 +45,29 @@ export class TableSearch extends WithLicenseSummary(AKElement) {
`,
];
public reset = () => {
if (!this.onSearch) return;
this.value = "";
this.onSearch("");
};
#submitListener = (event: SubmitEvent) => {
event.preventDefault();
if (!this.onSearch) return;
const form = event.target as HTMLFormElement;
const data = new FormData(form);
const value = data.get("search")?.toString().trim();
if (!value) {
return;
}
this.onSearch(value);
};
renderInput(): TemplateResult {
if (this.supportsQL && this.hasEnterpriseLicense) {
return html`<ak-search-ql
@@ -57,51 +80,28 @@ export class TableSearch extends WithLicenseSummary(AKElement) {
name="search"
></ak-search-ql>`;
}
return html`<input
class="pf-c-form-control"
name="search"
type="search"
placeholder=${msg("Search...")}
value="${ifDefined(this.value)}"
@search=${(ev: Event) => {
if (!this.onSearch) return;
this.onSearch((ev.target as HTMLInputElement).value);
}}
/>`;
}
render(): TemplateResult {
return html`<form
class="pf-c-input-group"
method="get"
@submit=${(event: SubmitEvent) => {
event.preventDefault();
if (!this.onSearch) return;
const el = this.shadowRoot?.querySelector<HTMLInputElement | HTMLTextAreaElement>(
"[name=search]",
);
if (!el) return;
if (el.value === "") return;
this.onSearch(el?.value);
}}
>
return html`<form class="pf-c-input-group" method="get" @submit=${this.#submitListener}>
${this.renderInput()}
<button
aria-label=${msg("Clear search")}
class="pf-c-button pf-m-control"
type="reset"
@click=${() => {
if (!this.onSearch) return;
this.value = "";
this.onSearch("");
}}
@click=${this.reset}
>
<i class="fas fa-times" aria-hidden="true"></i>
</button>
<button class="pf-c-button pf-m-control" type="submit">
<button aria-label=${msg("Search")} type="submit" class="pf-c-button pf-m-control">
<i class="fas fa-search" aria-hidden="true"></i>
</button>
</form>`;

View File

@@ -0,0 +1,13 @@
import { Pagination } from "@goauthentik/api";
export interface TableLike {
order?: string;
fetch: () => void;
}
export interface PaginatedResponse<T> {
pagination: Pagination;
autocomplete?: { [key: string]: string };
results: Array<T>;
}

View File

@@ -20,7 +20,7 @@ import { WithBrandConfig } from "#elements/mixins/branding";
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import { themeImage } from "#elements/utils/images";
import { BaseStage, StageHost, SubmitOptions } from "#flow/stages/base";
import { StageHost, SubmitOptions } from "#flow/stages/base";
import {
CapabilitiesEnum,
@@ -34,15 +34,7 @@ import {
} from "@goauthentik/api";
import { msg } from "@lit/localize";
import {
css,
CSSResult,
html,
MaybeCompiledTemplateResult,
nothing,
PropertyValues,
TemplateResult,
} from "lit";
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { until } from "lit/directives/until.js";
@@ -381,104 +373,167 @@ export class FlowExecutor
}
}
//#region Render Challenge
async #registerChallengeComponent(component: ChallengeTypes["component"]) {
switch (component) {
//#region Stages
case "ak-stage-access-denied":
return import("#flow/stages/access_denied/AccessDeniedStage");
case "ak-stage-identification":
return import("#flow/stages/identification/IdentificationStage");
case "ak-stage-password":
return import("#flow/stages/password/PasswordStage");
case "ak-stage-captcha":
return import("#flow/stages/captcha/CaptchaStage");
case "ak-stage-consent":
return import("#flow/stages/consent/ConsentStage");
case "ak-stage-dummy":
return import("#flow/stages/dummy/DummyStage");
case "ak-stage-email":
return import("#flow/stages/email/EmailStage");
case "ak-stage-autosubmit":
return import("#flow/stages/autosubmit/AutosubmitStage");
case "ak-stage-prompt":
return import("#flow/stages/prompt/PromptStage");
case "ak-stage-authenticator-totp":
return import("#flow/stages/authenticator_totp/AuthenticatorTOTPStage");
case "ak-stage-authenticator-duo":
return import("#flow/stages/authenticator_duo/AuthenticatorDuoStage");
case "ak-stage-authenticator-static":
return import("#flow/stages/authenticator_static/AuthenticatorStaticStage");
case "ak-stage-authenticator-email":
return import("#flow/stages/authenticator_email/AuthenticatorEmailStage");
case "ak-stage-authenticator-sms":
return import("#flow/stages/authenticator_sms/AuthenticatorSMSStage");
case "ak-stage-authenticator-validate":
return import("#flow/stages/authenticator_validate/AuthenticatorValidateStage");
case "ak-stage-user-login":
return import("#flow/stages/user_login/UserLoginStage");
case "ak-stage-session-end":
return import("#flow/providers/SessionEnd");
//#endregion
//#region Providers
case "ak-provider-oauth2-device-code":
return import("#flow/providers/oauth2/DeviceCode");
case "ak-provider-oauth2-device-code-finish":
return import("#flow/providers/oauth2/DeviceCodeFinish");
//#endregion
}
}
async renderChallenge(): Promise<MaybeCompiledTemplateResult | HTMLElement> {
const { challenge } = this;
if (!challenge) {
async renderChallenge(): Promise<TemplateResult> {
if (!this.challenge) {
return html`<ak-flow-card loading></ak-flow-card>`;
}
const { component } = challenge;
await this.#registerChallengeComponent(component);
switch (component) {
switch (this.challenge?.component) {
case "ak-stage-access-denied":
await import("#flow/stages/access_denied/AccessDeniedStage");
return html`<ak-stage-access-denied
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-access-denied>`;
case "ak-stage-identification":
await import("#flow/stages/identification/IdentificationStage");
return html`<ak-stage-identification
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-identification>`;
case "ak-stage-password":
await import("#flow/stages/password/PasswordStage");
return html`<ak-stage-password
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-password>`;
case "ak-stage-captcha":
await import("#flow/stages/captcha/CaptchaStage");
return html`<ak-stage-captcha
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-captcha>`;
case "ak-stage-consent":
await import("#flow/stages/consent/ConsentStage");
return html`<ak-stage-consent
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-consent>`;
case "ak-stage-dummy":
await import("#flow/stages/dummy/DummyStage");
return html`<ak-stage-dummy
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-dummy>`;
case "ak-stage-email":
await import("#flow/stages/email/EmailStage");
return html`<ak-stage-email
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-email>`;
case "ak-stage-autosubmit":
await import("#flow/stages/autosubmit/AutosubmitStage");
return html`<ak-stage-autosubmit
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-autosubmit>`;
case "ak-stage-prompt":
await import("#flow/stages/prompt/PromptStage");
return html`<ak-stage-prompt
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-prompt>`;
case "ak-stage-authenticator-totp":
await import("#flow/stages/authenticator_totp/AuthenticatorTOTPStage");
return html`<ak-stage-authenticator-totp
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-totp>`;
case "ak-stage-authenticator-duo":
await import("#flow/stages/authenticator_duo/AuthenticatorDuoStage");
return html`<ak-stage-authenticator-duo
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-duo>`;
case "ak-stage-authenticator-static":
await import("#flow/stages/authenticator_static/AuthenticatorStaticStage");
return html`<ak-stage-authenticator-static
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-static>`;
case "ak-stage-authenticator-webauthn":
return html`<ak-stage-authenticator-webauthn
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-webauthn>`;
case "ak-stage-authenticator-email":
await import("#flow/stages/authenticator_email/AuthenticatorEmailStage");
return html`<ak-stage-authenticator-email
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-email>`;
case "ak-stage-authenticator-sms":
await import("#flow/stages/authenticator_sms/AuthenticatorSMSStage");
return html`<ak-stage-authenticator-sms
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-sms>`;
case "ak-stage-authenticator-validate":
await import("#flow/stages/authenticator_validate/AuthenticatorValidateStage");
return html`<ak-stage-authenticator-validate
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-validate>`;
case "ak-stage-user-login":
await import("#flow/stages/user_login/UserLoginStage");
return html`<ak-stage-user-login
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-user-login>`;
// Sources
case "ak-source-plex":
return html`<ak-flow-source-plex
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-source-plex>`;
case "ak-source-oauth-apple":
return html`<ak-flow-source-oauth-apple
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-source-oauth-apple>`;
// Providers
case "ak-provider-oauth2-device-code":
await import("#flow/providers/oauth2/DeviceCode");
return html`<ak-flow-provider-oauth2-code
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-provider-oauth2-code>`;
case "ak-provider-oauth2-device-code-finish":
await import("#flow/providers/oauth2/DeviceCodeFinish");
return html`<ak-flow-provider-oauth2-code-finish
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-provider-oauth2-code-finish>`;
case "ak-stage-session-end":
await import("#flow/providers/SessionEnd");
return html`<ak-stage-session-end
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-session-end>`;
// Internal stages
case "ak-stage-flow-error":
return html`<ak-stage-flow-error
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-flow-error>`;
case "xak-flow-redirect":
return html`<ak-stage-redirect
.host=${this}
.challenge=${challenge}
.host=${this as StageHost}
.challenge=${this.challenge}
?promptUser=${this.inspectorOpen}
>
</ak-stage-redirect>`;
case "xak-flow-shell":
return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`;
case "xak-flow-frame":
return html`<xak-flow-frame
.host=${this}
.challenge=${challenge}
.host=${this as StageHost}
.challenge=${this.challenge}
></xak-flow-frame>`;
case "xak-flow-shell":
return html`${unsafeHTML((challenge as ShellChallenge).body)}`;
default:
return html`Invalid native challenge element`;
}
const ElementConstructor = customElements.get(component);
if (!ElementConstructor) {
return html`Invalid native challenge element "${component}"`;
}
const element = document.createElement(component) as BaseStage<ChallengeTypes, unknown>;
element.host = this;
element.challenge = this.challenge!;
return element;
}
//#endregion
async renderInspector() {
if (!this.inspectorOpen) {
return nothing;
@@ -525,7 +580,7 @@ export class FlowExecutor
</div>
</div>
</div>
${(this.inspectorAvailable ?? !this.inspectorOpen)
${this.inspectorAvailable || !this.inspectorOpen
? html`<button
class="inspector-toggle pf-c-button pf-m-primary"
@click=${() => {

View File

@@ -1,3 +1,5 @@
/// <reference types="vitest/config" />
import { createBundleDefinitions } from "#bundler/utils/node";
import { inlineCSSPlugin } from "#bundler/vite-plugin-lit-css/node";
@@ -9,4 +11,41 @@ export default defineConfig({
// ---
inlineCSSPlugin(),
],
test: {
dir: "./test",
exclude: [
"**/node_modules/**",
"**/dist/**",
"**/out/**",
"**/.{idea,git,cache,output,temp}/**",
"**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*",
],
projects: [
{
test: {
include: ["./unit/**/*.{test,spec}.ts", "**/*.unit.{test,spec}.ts"],
name: "unit",
environment: "node",
},
},
{
test: {
setupFiles: ["./test/lit/setup.js"],
include: ["./browser/**/*.{test,spec}.ts", "**/*.browser.{test,spec}.ts"],
name: "browser",
browser: {
enabled: true,
provider: "playwright",
instances: [
{
browser: "chromium",
},
],
},
},
},
],
},
});

View File

@@ -6620,7 +6620,7 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9667,10 +9667,6 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
<source>Number of previous passwords to check</source>
<target>Anzahl der vorherigen Passwörter, die geprüft werden sollen</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
<target>Seitenleiste umschalten</target>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
<target>Wähle einen Provider</target>
@@ -9976,6 +9972,42 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -5299,7 +5299,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>Change password</target>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -7590,9 +7590,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -7853,6 +7850,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file target-language="es" source-language="en" original="lit-localize-inputs" datatype="plaintext">
<body>
<trans-unit id="s4caed5b7a7e5d89b">
@@ -596,9 +596,9 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source>
<target>El URL &quot;
<x id="0" equiv-text="${this.url}"/>&quot; no fue encontrado.</target>
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>El URL "
<x id="0" equiv-text="${this.url}"/>" no fue encontrado.</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@@ -1693,7 +1693,7 @@
</trans-unit>
<trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source>
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
<target>Ingrese una URL completa, una ruta relativa o use 'fa: //fa-test' para usar el ícono Font Awesome «fa-test».</target>
</trans-unit>
@@ -3732,10 +3732,10 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit>
<trans-unit id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</source>
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
<target>¿Estás seguro de que deseas actualizar
<x id="0" equiv-text="${this.objectLabel}"/>&quot;
<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</target>
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>"?</target>
</trans-unit>
<trans-unit id="sc92d7cfb6ee1fec6">
@@ -4790,8 +4790,8 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit>
<trans-unit id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source>
<target>Un autenticador &quot;roaming&quot;, como una YubiKey</target>
<source>A "roaming" authenticator, like a YubiKey</source>
<target>Un autenticador "roaming", como una YubiKey</target>
</trans-unit>
<trans-unit id="sfffba7b23d8fb40c">
@@ -5149,8 +5149,8 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
</trans-unit>
<trans-unit id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, which will extend their session by the time specified here.</source>
<target>Si se establece en una duración mayor a 0, el usuario tendrá la opción de &quot;mantener la sesión iniciada&quot;, lo que extenderá su sesión por el tiempo especificado aquí.</target>
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
<target>Si se establece en una duración mayor a 0, el usuario tendrá la opción de "mantener la sesión iniciada", lo que extenderá su sesión por el tiempo especificado aquí.</target>
</trans-unit>
<trans-unit id="s542a71bb8f41e057">
@@ -6625,7 +6625,7 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -7398,7 +7398,7 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
<target>Usuario creado correctamente y agregado al grupo <x id="0" equiv-text="${this.group.name}"/></target>
</trans-unit>
<trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source>
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>Este usuario se agregará al grupo. &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;.</target>
</trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca">
@@ -8660,7 +8660,7 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
<target>Sincronizar Grupo</target>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${p.name}"/> (&amp;quot;<x id="1" equiv-text="${p.fieldKey}"/>&amp;quot;, of type <x id="2" equiv-text="${p.type}"/>)</target>
</trans-unit>
<trans-unit id="s25bacc19d98b444e">
@@ -8908,8 +8908,8 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
<target>URI de redirección válidas tras un flujo de autorización exitoso. Especifique también aquí los orígenes de los flujos implícitos.</target>
</trans-unit>
<trans-unit id="s4c49d27de60a532b">
<source>To allow any redirect URI, set the mode to Regex and the value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source>
<target>Para permitir cualquier URI de redirección, configure el modo en Expresión Regular y el valor en &quot;.*&quot;. Tenga en cuenta las posibles implicaciones de seguridad que esto puede tener.</target>
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
<target>Para permitir cualquier URI de redirección, configure el modo en Expresión Regular y el valor en ".*". Tenga en cuenta las posibles implicaciones de seguridad que esto puede tener.</target>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
@@ -9655,8 +9655,8 @@ Si se deja vacío, AuthnContextClassRef se establecerá según los métodos de a
<target>Cómo realizar la autenticación durante un flujo de solicitud de token de código de autorización</target>
</trans-unit>
<trans-unit id="s844baf19a6c4a9b4">
<source>Enable &quot;Remember me on this device&quot;</source>
<target>Habilita &quot;Recordarme en este dispositivo&quot;</target>
<source>Enable "Remember me on this device"</source>
<target>Habilita "Recordarme en este dispositivo"</target>
</trans-unit>
<trans-unit id="sfa72bca733f40692">
<source>When enabled, the user can save their username in a cookie, allowing them to skip directly to entering their password.</source>
@@ -9674,10 +9674,6 @@ Si se deja vacío, AuthnContextClassRef se establecerá según los métodos de a
<source>Number of previous passwords to check</source>
<target>Número de contraseñas anteriores a verificar</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
<target>Alternar barra lateral</target>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
<target>Elige un Proveedor</target>
@@ -10024,7 +10020,43 @@ El valor de este campo se compara con el atributo de pertenencia del usuario.</t
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>
</xliff>
</xliff>

View File

@@ -6623,7 +6623,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9671,10 +9671,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<source>Number of previous passwords to check</source>
<target>Nombre d'anciens mots de passe à vérifier</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
<target>Afficher/masquer la barre latérale</target>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
<target>Choisir un fournisseur</target>
@@ -9994,6 +9990,42 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -6624,7 +6624,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9672,10 +9672,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Number of previous passwords to check</source>
<target>Numero di password precedenti da controllare</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
<target>Attiva/disattiva la barra laterale</target>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
<target>Scegli un Provider</target>
@@ -9980,6 +9976,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -6553,7 +6553,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9038,9 +9038,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -9301,6 +9298,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -6561,7 +6561,7 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -8942,9 +8942,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -9205,6 +9202,42 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -6625,7 +6625,7 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9359,9 +9359,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -9622,6 +9619,42 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -6584,7 +6584,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target><x id="0" equiv-text="${prompt.label}"/></target>
</trans-unit>
@@ -9367,9 +9367,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -9631,4 +9628,40 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body></file></xliff>

View File

@@ -6624,7 +6624,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9450,9 +9450,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -9713,6 +9710,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -6585,7 +6585,7 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9423,9 +9423,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -9686,6 +9683,42 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -4617,7 +4617,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Change password</source>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
</trans-unit>
<trans-unit id="s33f85f24c0f5f008">
<source>Save</source>
@@ -6214,9 +6214,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -6478,6 +6475,42 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -6624,7 +6624,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9672,10 +9672,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Number of previous passwords to check</source>
<target>检查历史密码数量</target>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
<target>切换侧边栏</target>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
<target>选择提供程序</target>
@@ -9981,6 +9977,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -5010,7 +5010,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>修改密码</target>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -7296,9 +7296,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -7559,6 +7556,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -6543,7 +6543,7 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="saf63d34c8601dd41">
<source><x id="0" equiv-text="${prompt.label}"/></source>
<source><x id="0" equiv-text="${name}"/></source>
<target>
<x id="0" equiv-text="${prompt.label}"/>
</target>
@@ -9018,9 +9018,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s79b3fcd40dd63921">
<source>Number of previous passwords to check</source>
</trans-unit>
<trans-unit id="sdd66c5a2e706fb81">
<source>Toggle sidebar</source>
</trans-unit>
<trans-unit id="s7d4ec232535a36f0">
<source>Choose a Provider</source>
</trans-unit>
@@ -9281,6 +9278,42 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s5e13dff03b580216">
<source>Previous executions logs</source>
</trans-unit>
<trans-unit id="s6abb1cd87fe0114e">
<source>Home</source>
</trans-unit>
<trans-unit id="se58e6ed983bf34b0">
<source>Collapse navigation</source>
</trans-unit>
<trans-unit id="sc6ef25894ed00175">
<source>Expand navigation</source>
</trans-unit>
<trans-unit id="s148b5e365440a7c1">
<source>Table pagination</source>
</trans-unit>
<trans-unit id="s5d929ff1619ac0c9">
<source>Search</source>
</trans-unit>
<trans-unit id="sd2c2366d13599d8c">
<source>Table actions</source>
</trans-unit>
<trans-unit id="s3d195621e562d805">
<source>Select row</source>
</trans-unit>
<trans-unit id="s572d21b6a41e24fa">
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
</trans-unit>
<trans-unit id="sa25b60b4fac481aa">
<source>Table content</source>
</trans-unit>
<trans-unit id="s5eba8fa19126f70a">
<source>Learn more about the enterprise license.</source>
</trans-unit>
<trans-unit id="s9db1679f3b234d4e">
<source>Search for providers…</source>
</trans-unit>
<trans-unit id="s76790480b7b28ad2">
<source>Edit provider</source>
</trans-unit>
</body>
</file>

View File

@@ -45,11 +45,11 @@
"typescript": "^5.8.3"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.4.10",
"@rspack/binding-linux-arm64-gnu": "1.4.10",
"@rspack/binding-linux-x64-gnu": "1.4.10",
"@swc/core-darwin-arm64": "1.13.2",
"@swc/core-linux-arm64-gnu": "1.13.2",
"@rspack/binding-darwin-arm64": "1.4.11",
"@rspack/binding-linux-arm64-gnu": "1.4.11",
"@rspack/binding-linux-x64-gnu": "1.4.11",
"@swc/core-darwin-arm64": "1.13.3",
"@swc/core-linux-arm64-gnu": "1.13.3",
"@swc/core-linux-x64-gnu": "1.13.3",
"@swc/html-darwin-arm64": "1.13.3",
"@swc/html-linux-arm64-gnu": "1.13.3",

View File

@@ -56,6 +56,14 @@ export default createDocusaurusConfig(
path: "pages",
},
docs: {
exclude: [
/**
* Exclude previously generated API docs.
*
* @expires 2025-12-01
*/
"**/developer-docs/api/reference/**",
],
routeBasePath: "/docs",
path: ".",

View File

@@ -35,9 +35,9 @@ To support the integration of Kimai with authentik, you need to create an applic
- **Choose a Provider type**: select **SAML Provider** as the provider type.
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
- Set the **ACS URL** to `https://kimai.company/auth/saml/acs`.
- Set the **Audience** to `https://kimai.companyauth/saml`.
- Set the **Issuer** to `https://authentik.company`.
- Set the **Service Provider Binding** to `Post`.
- Set the **Audience** to `https://kimai.company/auth/saml`.
- Under **Advanced protocol settings**, select an available signing certificate.
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.

View File

@@ -19,7 +19,7 @@
"@goauthentik/eslint-config": "^1.0.5",
"@goauthentik/prettier-config": "^3.1.0",
"@goauthentik/tsconfig": "^1.0.4",
"@swc/html-linux-x64-gnu": "1.13.3",
"@rspack/binding-linux-x64-gnu": "1.4.11",
"@types/node": "^24.1.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
@@ -33,11 +33,11 @@
"node": ">=24"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.4.10",
"@rspack/binding-linux-arm64-gnu": "1.4.10",
"@rspack/binding-linux-x64-gnu": "1.4.10",
"@swc/core-darwin-arm64": "1.13.2",
"@swc/core-linux-arm64-gnu": "1.13.2",
"@rspack/binding-darwin-arm64": "1.4.11",
"@rspack/binding-linux-arm64-gnu": "1.4.11",
"@rspack/binding-linux-x64-gnu": "1.4.11",
"@swc/core-darwin-arm64": "1.13.3",
"@swc/core-linux-arm64-gnu": "1.13.3",
"@swc/core-linux-x64-gnu": "1.13.3",
"@swc/html-darwin-arm64": "1.13.3",
"@swc/html-linux-arm64-gnu": "1.13.3",
@@ -77,11 +77,11 @@
"typescript": "^5.8.3"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.4.10",
"@rspack/binding-linux-arm64-gnu": "1.4.10",
"@rspack/binding-linux-x64-gnu": "1.4.10",
"@swc/core-darwin-arm64": "1.13.2",
"@swc/core-linux-arm64-gnu": "1.13.2",
"@rspack/binding-darwin-arm64": "1.4.11",
"@rspack/binding-linux-arm64-gnu": "1.4.11",
"@rspack/binding-linux-x64-gnu": "1.4.11",
"@swc/core-darwin-arm64": "1.13.3",
"@swc/core-linux-arm64-gnu": "1.13.3",
"@swc/core-linux-x64-gnu": "1.13.3",
"@swc/html-darwin-arm64": "1.13.3",
"@swc/html-linux-arm64-gnu": "1.13.3",
@@ -5776,9 +5776,9 @@
}
},
"node_modules/@rspack/binding-darwin-arm64": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.4.10.tgz",
"integrity": "sha512-PraYGuVSzvEwdoYC8T70qI/8j1QeUe2sysiWmjSdxUpxJsDfw35hK9TfxULeAJULlAUAiiXs03hdZk29DBc3ow==",
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.4.11.tgz",
"integrity": "sha512-PrmBVhR8MC269jo6uQ+BMy1uwIDx0HAJYLQRQur8gXiehWabUBCRg/d4U9KR7rLzdaSScRyc5JWXR52T7/4MfA==",
"cpu": [
"arm64"
],
@@ -5802,9 +5802,9 @@
]
},
"node_modules/@rspack/binding-linux-arm64-gnu": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.4.10.tgz",
"integrity": "sha512-cs6yu250FzRU1hl+02VLoJRdzbAveTOqvREeHgqL5AiTc6q1dQo1IZ16/Qt4+g0DMjnvM66pELRIO2nphXL8aA==",
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.4.11.tgz",
"integrity": "sha512-ms6uwECUIcu+6e82C5HJhRMHnfsI+l33v7XQezntzRPN0+sG3EpikEoT7SGbgt4vDwaWLR7wS20suN4qd5r3GA==",
"cpu": [
"arm64"
],
@@ -5828,9 +5828,9 @@
]
},
"node_modules/@rspack/binding-linux-x64-gnu": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.4.10.tgz",
"integrity": "sha512-FcaBqMclADWiqX+Mez15kggwaVYZkoEqDiQwYRpYDbBMsiJEtfp41GnNRstTWxYxFbcmuWoZl2cYy+LepR21ag==",
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.4.11.tgz",
"integrity": "sha512-bHYFLxPPYBOSaHdQbEoCYGMQ1gOrEWj7Mro/DLfSHZi1a0okcQ2Q1y0i1DczReim3ZhLGNrK7k1IpFXCRbAobQ==",
"cpu": [
"x64"
],
@@ -6327,9 +6327,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.2.tgz",
"integrity": "sha512-44p7ivuLSGFJ15Vly4ivLJjg3ARo4879LtEBAabcHhSZygpmkP8eyjyWxrH3OxkY1eRZSIJe8yRZPFw4kPXFPw==",
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.3.tgz",
"integrity": "sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==",
"cpu": [
"arm64"
],
@@ -6375,9 +6375,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.2.tgz",
"integrity": "sha512-KJUSl56DBk7AWMAIEcU83zl5mg3vlQYhLELhjwRFkGFMvghQvdqQ3zFOYa4TexKA7noBZa3C8fb24rI5sw9Exg==",
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.3.tgz",
"integrity": "sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==",
"cpu": [
"arm64"
],

View File

@@ -32,11 +32,11 @@
"typescript-eslint": "^8.38.0"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.4.10",
"@rspack/binding-linux-arm64-gnu": "1.4.10",
"@rspack/binding-linux-x64-gnu": "1.4.10",
"@swc/core-darwin-arm64": "1.13.2",
"@swc/core-linux-arm64-gnu": "1.13.2",
"@rspack/binding-darwin-arm64": "1.4.11",
"@rspack/binding-linux-arm64-gnu": "1.4.11",
"@rspack/binding-linux-x64-gnu": "1.4.11",
"@swc/core-darwin-arm64": "1.13.3",
"@swc/core-linux-arm64-gnu": "1.13.3",
"@swc/core-linux-x64-gnu": "1.13.3",
"@swc/html-darwin-arm64": "1.13.3",
"@swc/html-linux-arm64-gnu": "1.13.3",