Compare commits

..

44 Commits

Author SHA1 Message Date
Teffen Ellis
57c3ceae77 web: Flesh out e2e. 2025-08-18 15:00:00 +02:00
Teffen Ellis
b58821cb49 web: Flesh out JSX components. 2025-08-17 17:54:30 +02:00
dependabot[bot]
e50cf1c150 web: bump @sentry/browser from 10.3.0 to 10.4.0 in /web in the sentry group across 1 directory (#16122)
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 10.3.0 to 10.4.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/10.3.0...10.4.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 10.4.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-08-15 17:37:25 +01:00
authentik-automation[bot]
4178717386 core, web: update translations (#16195)
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-15 17:36:59 +01:00
M-Slanec
20d068f767 policies/password: Fix amount_uppercase in password policy check (#16197)
Fix amount_uppercase in password policy check

Co-authored-by: Matthew Slanec <matthewslanec@Matthews-MacBook-Pro.local>
2025-08-15 13:51:43 +01:00
authentik-automation[bot]
5b7a42e6d6 web: bump API Client version (#16203)
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-15 13:04:59 +01:00
dependabot[bot]
1398561142 core: bump astral-sh/uv from 0.8.10 to 0.8.11 (#16201) 2025-08-15 12:27:42 +01:00
dependabot[bot]
55657e149b web: bump chromedriver from 139.0.0 to 139.0.1 in /web (#16200)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 139.0.0 to 139.0.1.
- [Commits](https://github.com/giggio/node-chromedriver/compare/139.0.0...139.0.1)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-version: 139.0.1
  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-08-15 11:47:52 +01:00
authentik-automation[bot]
d5d7140631 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#16196)
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-15 11:46:40 +01:00
Marcelo Elizeche Landó
17ff12f68f core: Add email template selector (#16170)
* Unify all email templates under stages.email.models.EmailTemplates

* Add template selector to Email Authenticator Stage

* fix tests

* Add email_template field to events.notificationtransport

* update schemas

* Make email_template default as None, add UI to notif transports

* Add showEmail and fix default selection in TransportForm

* fix required field for emailtemplate and webhookurl in TransportForm

* use switch because typescript is whinning

* Add email_subject_prefix to NotificationTransport

* Add tests, update migration

* remove duplicate API

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* rename template name

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* move send_once up

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* better defaults

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* no null

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update test for email templates endpoint

* fix test url

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-08-15 11:44:18 +01:00
Tana M Berry
9c9a6e3d66 website/docs: add content about new Advanced Query searches (#16019)
Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-08-14 17:31:36 +02:00
dependabot[bot]
2cd81b2e78 web: bump the eslint group across 2 directories with 3 updates (#16133)
Bumps the eslint group with 1 update in the /packages/eslint-config directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).
Bumps the eslint group with 1 update in the /web directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `typescript-eslint` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/parser)

Updates `typescript-eslint` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/parser)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.39.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.39.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.39.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 14:31:52 +01:00
dependabot[bot]
bad426f694 web: bump @types/react from 19.1.8 to 19.1.10 in /web (#16125)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.1.8 to 19.1.10.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.1.10
  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-08-14 14:31:44 +01:00
dependabot[bot]
6404fba2e4 web: bump core-js from 3.44.0 to 3.45.0 in /web (#16123)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.44.0 to 3.45.0.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.45.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-version: 3.45.0
  dependency-type: direct:production
  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-14 14:31:28 +01:00
Marc 'risson' Schmitt
c33b9f2d3f web/admin: fix settings saving (#16184)
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-08-14 12:33:33 +00:00
dependabot[bot]
bac6e965f4 website: bump the eslint group in /website with 3 updates (#16124)
Bumps the eslint group in /website with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/parser)

Updates `typescript-eslint` from 8.39.0 to 8.39.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 12:01:48 +01:00
dependabot[bot]
36cb4dc750 web: bump typedoc-plugin-markdown from 4.8.0 to 4.8.1 in /packages/esbuild-plugin-live-reload (#16126)
web: bump typedoc-plugin-markdown

Bumps [typedoc-plugin-markdown](https://github.com/typedoc2md/typedoc-plugin-markdown/tree/HEAD/packages/typedoc-plugin-markdown) from 4.8.0 to 4.8.1.
- [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.1/packages/typedoc-plugin-markdown)

---
updated-dependencies:
- dependency-name: typedoc-plugin-markdown
  dependency-version: 4.8.1
  dependency-type: direct:development
  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-08-14 12:01:34 +01:00
dependabot[bot]
45d9945a3a web: bump the storybook group across 1 directory with 5 updates (#16134)
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.1.1 to 9.1.2
- [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.2/code/addons/docs)

Updates `@storybook/addon-links` from 9.1.1 to 9.1.2
- [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.2/code/addons/links)

Updates `@storybook/web-components` from 9.1.1 to 9.1.2
- [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.2/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 9.1.1 to 9.1.2
- [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.2/code/frameworks/web-components-vite)

Updates `storybook` from 9.1.1 to 9.1.2
- [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.2/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 9.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 9.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 9.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 9.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 9.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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-14 11:59:51 +01:00
dependabot[bot]
23285ad664 core: bump goauthentik.io/api/v3 from 3.2025064.8 to 3.2025100.1 (#16161)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025064.8 to 3.2025100.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2025064.8...v3.2025100.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2025100.1
  dependency-type: direct:production
  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-14 11:59:24 +01:00
dependabot[bot]
91ab9503fd web: bump the esbuild group across 2 directories with 4 updates (#16162)
Bumps the esbuild group with 1 update in the /packages/esbuild-plugin-live-reload directory: [esbuild](https://github.com/evanw/esbuild).
Bumps the esbuild group with 1 update in the /web directory: [esbuild](https://github.com/evanw/esbuild).


Updates `esbuild` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

Updates `@esbuild/darwin-arm64` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

Updates `@esbuild/linux-arm64` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

Updates `@esbuild/linux-x64` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

Updates `esbuild` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

Updates `@esbuild/darwin-arm64` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

Updates `@esbuild/linux-arm64` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

Updates `@esbuild/linux-x64` from 0.25.8 to 0.25.9
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.8...v0.25.9)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: esbuild
- dependency-name: "@esbuild/darwin-arm64"
  dependency-version: 0.25.9
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: esbuild
- dependency-name: "@esbuild/linux-arm64"
  dependency-version: 0.25.9
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: esbuild
- dependency-name: "@esbuild/linux-x64"
  dependency-version: 0.25.9
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: esbuild
- dependency-name: esbuild
  dependency-version: 0.25.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: esbuild
- dependency-name: "@esbuild/darwin-arm64"
  dependency-version: 0.25.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: esbuild
- dependency-name: "@esbuild/linux-arm64"
  dependency-version: 0.25.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: esbuild
- dependency-name: "@esbuild/linux-x64"
  dependency-version: 0.25.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: esbuild
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 11:58:58 +01:00
dependabot[bot]
fb7802e6af web: bump the wdio group across 1 directory with 3 updates (#16163)
Bumps the wdio group with 3 updates in the /web directory: [@wdio/browser-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-browser-runner), [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli) and [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter).


Updates `@wdio/browser-runner` from 9.19.0 to 9.19.1
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.19.1/packages/wdio-browser-runner)

Updates `@wdio/cli` from 9.19.0 to 9.19.1
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.19.1/packages/wdio-cli)

Updates `@wdio/spec-reporter` from 9.19.0 to 9.19.1
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.19.1/packages/wdio-spec-reporter)

---
updated-dependencies:
- dependency-name: "@wdio/browser-runner"
  dependency-version: 9.19.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/cli"
  dependency-version: 9.19.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/spec-reporter"
  dependency-version: 9.19.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: wdio
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 11:58:46 +01:00
authentik-automation[bot]
0f13a63528 core, web: update translations (#16175)
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-14 11:57:52 +01:00
dependabot[bot]
36daf4b519 core: bump github.com/getsentry/sentry-go from 0.35.0 to 0.35.1 (#16176)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.35.0 to 0.35.1.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.35.0...v0.35.1)

---
updated-dependencies:
- dependency-name: github.com/getsentry/sentry-go
  dependency-version: 0.35.1
  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-08-14 11:57:44 +01:00
dependabot[bot]
5cc4793b84 lifecycle/aws: bump aws-cdk from 2.1024.0 to 2.1025.0 in /lifecycle/aws (#16177)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1024.0 to 2.1025.0.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1025.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1025.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-08-14 11:57:36 +01:00
dependabot[bot]
a6063d4af4 core: bump library/golang from 1.24-bookworm to 1.25-bookworm (#16178)
Bumps library/golang from 1.24-bookworm to 1.25-bookworm.

---
updated-dependencies:
- dependency-name: library/golang
  dependency-version: 1.25-bookworm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-14 11:57:24 +01:00
dependabot[bot]
8f450e6e14 core: bump astral-sh/uv from 0.8.9 to 0.8.10 (#16179)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.8.9 to 0.8.10.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.8.9...0.8.10)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.8.10
  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-08-14 11:57:15 +01:00
Dominic R
a1fc0605e2 website/integrations: nextcloud: Cleanup SAML service config (#16066)
* wip

* wip

* wip
2025-08-14 10:44:34 +01:00
Teffen Ellis
c886e4ff6b web: Fix issue where clicking a list item scrolls container. (#16174) 2025-08-13 21:30:58 +00:00
Marc 'risson' Schmitt
f91ebc2ad5 ci: release tag: fix missing env variables (#16172) 2025-08-13 21:09:41 +01:00
Jens L.
dbe7bfe58b tasks: add sentry dramatiq integration (#16167) 2025-08-13 18:53:12 +01:00
Teffen Ellis
05d4d207d7 web: Fix hidden textarea required attribute. (#16168)
* web: Fix hidden textarea `required` attribute.

* web: Fix missing flag property.

* web: Clarify field error reporting.
2025-08-13 12:31:00 -04:00
Marc 'risson' Schmitt
11efc75451 ci: release: consolidation bump version and on tag (#16164)
Co-authored-by: Dominic R <dominic@sdko.org>
2025-08-13 18:21:12 +02:00
authentik-automation[bot]
4d2d020be1 web: bump API Client version (#16152)
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-13 14:48:57 +01:00
Marc 'risson' Schmitt
9c0905d76d ci: fix docker hub credentials (#16165) 2025-08-13 15:18:39 +02:00
Marc 'risson' Schmitt
3ca94b2198 root: fix custom packages installation in docker (#16157) 2025-08-13 12:24:21 +00:00
Marc 'risson' Schmitt
dbf51fb11f ci: release publish: fix missing permissions (#16155) 2025-08-13 13:22:35 +01:00
Marc 'risson' Schmitt
ad69eb955f ci: docker push: fix version missing dash (#16153) 2025-08-13 13:22:14 +01:00
transifex-integration[bot]
c867ebc014 translate: Updates for file web/xliff/en.xlf in fr (#16160)
Translate web/xliff/en.xlf in fr

100% translated source file: 'web/xliff/en.xlf'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-08-13 12:06:38 +00:00
authentik-automation[bot]
adea1e460c core, web: update translations (#16159)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-08-13 00:38:30 +00:00
Marc 'risson' Schmitt
846c58e617 root: fix custom packages installation in docker (#16150) 2025-08-12 21:29:56 +00:00
Marcelo Elizeche Landó
352079fc3c core: bump redis from 6.2.0 to v6.3.0 (#15983) 2025-08-12 23:20:40 +02:00
dependabot[bot]
6786391732 core: bump github.com/redis/go-redis/v9 from 9.11.0 to 9.12.1 (#16121)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 23:19:52 +02:00
dependabot[bot]
4b3d08154d core: bump astral-sh/uv from 0.8.8 to 0.8.9 (#16120)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 23:19:33 +02:00
authentik-automation[bot]
130fe4cac7 root: bump version to 2025.10.0-rc1 (#16149)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-08-12 21:17:14 +00:00
133 changed files with 11584 additions and 2570 deletions

View File

@@ -26,7 +26,7 @@ RUN npm run build && \
npm run build:sfe
# Stage 2: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS go-builder
ARG TARGETOS
ARG TARGETARCH
@@ -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.8 AS uv
FROM ghcr.io/astral-sh/uv:0.8.11 AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.6-slim-bookworm-fips AS python-base

View File

@@ -20,8 +20,8 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
| Version | Supported |
| --------- | --------- |
| 2025.4.x | ✅ |
| 2025.6.x | ✅ |
| 2025.8.x | ✅ |
## Reporting a Vulnerability

View File

@@ -3,7 +3,7 @@
from functools import lru_cache
from os import environ
VERSION = "2025.8.1"
VERSION = "2025.10.0-rc1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@@ -63,6 +63,28 @@ class TestBrands(APITestCase):
},
)
def test_brand_subdomain_same_suffix(self):
"""Test Current brand API"""
Brand.objects.all().delete()
Brand.objects.create(domain="bar.baz", branding_title="custom")
Brand.objects.create(domain="foo.bar.baz", branding_title="custom")
self.assertJSONEqual(
self.client.get(
reverse("authentik_api:brand-current"), HTTP_HOST="foo.bar.baz"
).content.decode(),
{
"branding_logo": "/static/dist/assets/icons/icon_left_brand.svg",
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "custom",
"branding_custom_css": "",
"matched_domain": "foo.bar.baz",
"ui_footer_links": [],
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
"flags": self.default_flags,
},
)
def test_fallback(self):
"""Test fallback brand"""
Brand.objects.all().delete()

View File

@@ -4,6 +4,7 @@ from typing import Any
from django.db.models import F, Q
from django.db.models import Value as V
from django.db.models.functions import Length
from django.http.request import HttpRequest
from django.utils.html import _json_script_escapes
from django.utils.safestring import mark_safe
@@ -20,9 +21,9 @@ DEFAULT_BRAND = Brand(domain="fallback")
def get_brand_for_request(request: HttpRequest) -> Brand:
"""Get brand object for current request"""
db_brands = (
Brand.objects.annotate(host_domain=V(request.get_host()))
Brand.objects.annotate(host_domain=V(request.get_host()), match_length=Length("domain"))
.filter(Q(host_domain__iendswith=F("domain")) | _q_default)
.order_by("default")
.order_by("-match_length", "default")
)
brands = list(db_brands.all())
if len(brands) < 1:

View File

@@ -154,7 +154,6 @@ worker:
consumer_listen_timeout: "seconds=30"
task_max_retries: 20
task_default_time_limit: "minutes=10"
lock_purge_interval: "minutes=1"
task_purge_interval: "days=1"
task_expiration: "days=30"
scheduler_interval: "seconds=60"

View File

@@ -76,7 +76,6 @@ class OutpostConfig:
kubernetes_ingress_annotations: dict[str, str] = field(default_factory=dict)
kubernetes_ingress_secret_name: str = field(default="authentik-outpost-tls")
kubernetes_ingress_class_name: str | None = field(default=None)
kubernetes_ingress_path_type: str | None = field(default=None)
kubernetes_httproute_annotations: dict[str, str] = field(default_factory=dict)
kubernetes_httproute_parent_refs: list[dict[str, str]] = field(default_factory=list)
kubernetes_service_type: str = field(default="ClusterIP")
@@ -152,7 +151,7 @@ class OutpostServiceConnection(ScheduledModel, models.Model):
state = cache.get(self.state_key, None)
if not state:
outpost_service_connection_monitor.send_with_options(args=(self.pk,), rel_obj=self)
outpost_service_connection_monitor.send_with_options(args=(self.pk), rel_obj=self)
return OutpostServiceConnectionState("", False)
return state

View File

@@ -23,12 +23,7 @@ def user_session_deleted_oauth_backchannel_logout_and_tokens_removal(
backchannel_logout_notification_dispatch.send(
revocations=[
(
token.provider_id,
token.id_token.iss,
token.id_token.sub,
instance.session.session_key,
)
(token.provider_id, token.id_token.iss, token.session.user.uid)
for token in access_tokens
],
)

View File

@@ -14,19 +14,13 @@ LOGGER = get_logger()
@actor(description=_("Send a back-channel logout request to the registered client"))
def send_backchannel_logout_request(
provider_pk: int,
iss: str,
sub: str | None = None,
session_key: str | None = None,
) -> bool:
def send_backchannel_logout_request(provider_pk: int, iss: str, sub: str = None) -> bool:
"""Send a back-channel logout request to the registered client
Args:
provider_pk: The OAuth2 provider's primary key
iss: The issuer URL for the logout token
sub: The subject identifier to include in the logout token
session_key: The authentik session key to hash and include in the logout token
Returns:
bool: True if the request was sent successfully, False otherwise
@@ -39,10 +33,11 @@ def send_backchannel_logout_request(
return
# Generate the logout token
logout_token = create_logout_token(provider, iss, sub, session_key)
logout_token = create_logout_token(iss, provider, None, sub)
# Get the back-channel logout URI from the provider's dedicated backchannel_logout_uri field
# Back-channel logout requires explicit configuration - no fallback to redirect URIs
backchannel_logout_uri = provider.backchannel_logout_uri
if not backchannel_logout_uri:
self.info("No back-channel logout URI found for provider")
@@ -65,9 +60,9 @@ def send_backchannel_logout_request(
def backchannel_logout_notification_dispatch(revocations: list, **kwargs):
"""Handle backchannel logout notifications dispatched via signal"""
for revocation in revocations:
provider_pk, iss, sub, session_key = revocation
provider_pk, iss, sub = revocation
provider = OAuth2Provider.objects.filter(pk=provider_pk).first()
send_backchannel_logout_request.send_with_options(
args=(provider_pk, iss, sub, session_key),
args=(provider_pk, iss, sub),
rel_obj=provider,
)

View File

@@ -217,17 +217,17 @@ class HttpResponseRedirectScheme(HttpResponseRedirect):
def create_logout_token(
provider: OAuth2Provider,
iss: str,
sub: str | None = None,
provider: OAuth2Provider,
session_key: str | None = None,
sub: str | None = None,
) -> str:
"""Create a logout token for Back-Channel Logout
As per https://openid.net/specs/openid-connect-backchannel-1_0.html
"""
LOGGER.debug("Creating logout token", provider=provider, sub=sub)
LOGGER.debug("Creating logout token", provider=provider, session_key=session_key, sub=sub)
# Create the logout token payload
payload = {

View File

@@ -127,9 +127,6 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
and self.controller.outpost.config.kubernetes_ingress_secret_name
):
tls_hosts.append(external_host_name.hostname)
path_type = "Prefix"
if self.controller.outpost.config.kubernetes_ingress_path_type:
path_type = self.controller.outpost.config.kubernetes_ingress_path_type
if proxy_provider.mode in [
ProxyMode.FORWARD_SINGLE,
ProxyMode.FORWARD_DOMAIN,
@@ -146,7 +143,7 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
),
),
path="/outpost.goauthentik.io",
path_type=path_type,
path_type="Prefix",
)
]
),
@@ -164,7 +161,7 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
),
),
path="/",
path_type=path_type,
path_type="Prefix",
)
]
),

View File

@@ -4,6 +4,7 @@ import importlib
from collections import OrderedDict
from hashlib import sha512
from pathlib import Path
from tempfile import gettempdir
import orjson
from sentry_sdk import set_tag
@@ -367,9 +368,6 @@ DRAMATIQ = {
"broker_class": "authentik.tasks.broker.Broker",
"channel_prefix": "authentik",
"task_model": "authentik.tasks.models.Task",
"lock_purge_interval": timedelta_from_string(
CONFIG.get("worker.lock_purge_interval")
).total_seconds(),
"task_purge_interval": timedelta_from_string(
CONFIG.get("worker.task_purge_interval")
).total_seconds(),
@@ -426,6 +424,7 @@ DRAMATIQ = {
(
"authentik.tasks.middleware.MetricsMiddleware",
{
"multiproc_dir": str(Path(gettempdir()) / "authentik_prometheus_tmp"),
"prefix": "authentik",
},
),

View File

@@ -35,12 +35,7 @@ class Command(TenantCommand):
template_context={},
)
try:
if not stage.use_global_settings:
message.from_email = stage.from_address
send_mail.send(message.__dict__, stage.pk).get_result(block=True)
self.stdout.write(self.style.SUCCESS(f"Test email sent to {options['to']}"))
send_mail(message.__dict__, stage.pk)
finally:
if delete_stage:
stage.delete()

View File

@@ -1,66 +0,0 @@
"""Test email management commands"""
from unittest.mock import patch
from django.core import mail
from django.core.mail.backends.locmem import EmailBackend
from django.core.management import call_command
from django.test import TestCase
from authentik.core.tests.utils import create_test_admin_user
from authentik.stages.email.models import EmailStage
class TestEmailManagementCommands(TestCase):
"""Test email management commands"""
def setUp(self):
self.user = create_test_admin_user()
def test_test_email_command_with_stage(self):
"""Test test_email command with specified stage"""
EmailStage.objects.create(
name="test-stage",
from_address="test@authentik.local",
host="localhost",
port=25,
)
with patch("authentik.stages.email.models.EmailStage.backend_class", EmailBackend):
call_command("test_email", "test@example.com", stage="test-stage")
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "authentik Test-Email")
self.assertEqual(mail.outbox[0].to, ["test@example.com"])
def test_test_email_command_with_global_settings(self):
"""Test test_email command with global settings"""
# Mock the backend to use Django's locmem backend
with patch("authentik.stages.email.models.EmailStage.backend_class", EmailBackend):
call_command("test_email", "test@example.com")
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "authentik Test-Email")
self.assertEqual(mail.outbox[0].to, ["test@example.com"])
def test_test_email_command_invalid_stage(self):
"""Test test_email command with invalid stage"""
call_command("test_email", "test@example.com", stage="nonexistent")
self.assertEqual(len(mail.outbox), 0)
def test_test_email_command_with_custom_from(self):
"""Test test_email command respects custom from address"""
EmailStage.objects.create(
name="test-stage",
from_address="custom@authentik.local",
host="localhost",
port=25,
)
with patch("authentik.stages.email.models.EmailStage.backend_class", EmailBackend):
call_command("test_email", "test@example.com", stage="test-stage")
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].from_email, "custom@authentik.local")
self.assertEqual(mail.outbox[0].to, ["test@example.com"])

View File

@@ -100,15 +100,10 @@ class MessagesMiddleware(Middleware):
TaskStatus.ERROR,
exception,
)
event_kwargs = {
"actor": task.actor_name,
}
if task.rel_obj:
event_kwargs["rel_obj"] = task.rel_obj
Event.new(
EventAction.SYSTEM_TASK_EXCEPTION,
message=f"Task {task.actor_name} encountered an error",
**event_kwargs,
actor=task.actor_name,
).with_exception(exception).save()
def after_skip_message(self, broker: Broker, message: Message):
@@ -156,6 +151,7 @@ class DescriptionMiddleware(Middleware):
class _healthcheck_handler(BaseHTTPRequestHandler):
def log_request(self, code="-", size="-"):
HEALTHCHECK_LOGGER.info(
self.path,

View File

@@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2025.8.1 Blueprint schema",
"title": "authentik 2025.10.0-rc1 Blueprint schema",
"required": [
"version",
"entries"

View File

@@ -5,7 +5,7 @@ metadata:
blueprints.goauthentik.io/system-bootstrap: "true"
blueprints.goauthentik.io/system: "true"
blueprints.goauthentik.io/description: |
This blueprint configures the default admin user and group, and configures them for the [Automated install](https://docs.goauthentik.io/docs/install-config/automated-install?utm_source=bootstrap_blueprint).
This blueprint configures the default admin user and group, and configures them for the [Automated install](https://goauthentik.io/docs/installation/automated-install).
context:
username: akadmin
group_name: authentik Admins

View File

@@ -48,7 +48,7 @@ services:
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.8.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.10.0-rc1}
ports:
- ${COMPOSE_PORT_HTTP:-9000}:9000
- ${COMPOSE_PORT_HTTPS:-9443}:9443
@@ -72,7 +72,7 @@ services:
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.8.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.10.0-rc1}
restart: unless-stopped
user: root
volumes:

6
go.mod
View File

@@ -6,7 +6,7 @@ require (
beryju.io/ldap v0.1.0
github.com/avast/retry-go/v4 v4.6.1
github.com/coreos/go-oidc/v3 v3.15.0
github.com/getsentry/sentry-go v0.35.0
github.com/getsentry/sentry-go v0.35.1
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
@@ -23,13 +23,13 @@ require (
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.1
github.com/prometheus/client_golang v1.23.0
github.com/redis/go-redis/v9 v9.11.0
github.com/redis/go-redis/v9 v9.12.1
github.com/sethvargo/go-envconfig v1.3.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025064.8
goauthentik.io/api/v3 v3.2025100.1
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.16.0

12
go.sum
View File

@@ -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.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY=
github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ=
github.com/getsentry/sentry-go v0.35.1/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=
@@ -148,8 +148,8 @@ github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2
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/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -185,8 +185,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2025064.8 h1:wgegkPUtGSrOR7+Rnd0cxLVU0cEea87BatjESa6BJv0=
goauthentik.io/api/v3 v3.2025064.8/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
goauthentik.io/api/v3 v3.2025100.1 h1:xOMnQ2j1MtrYJlO+8bHJ8MdFPyymqTZcLQ+5PTspdqA=
goauthentik.io/api/v3 v3.2025100.1/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=

View File

@@ -1 +1 @@
2025.8.1
2025.10.0-rc1

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -68,11 +68,6 @@ function prepare_debug {
chown authentik:authentik /unittest.xml
}
if [[ -z "${PROMETHEUS_MULTIPROC_DIR}" ]]; then
export PROMETHEUS_MULTIPROC_DIR="${TMPDIR:-/tmp}"
fi
mkdir -p "${PROMETHEUS_MULTIPROC_DIR}"
if [[ "$(python -m authentik.lib.config debugger 2>/dev/null)" == "True" ]]; then
prepare_debug
fi

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1024.0",
"aws-cdk": "^2.1025.0",
"cross-env": "^10.0.0"
},
"engines": {
@@ -24,9 +24,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1024.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1024.0.tgz",
"integrity": "sha512-hY0iVT2gPX/QOQXL7RSP2sqIRI/4BYU27vSmbhZxLEj//c3pkMkd9QpIHj7gOhyWC2gf6n5JuYPw27Dgw8FEdA==",
"version": "2.1025.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1025.0.tgz",
"integrity": "sha512-qKYM+RG5+U/UbGpjTt8ZaxBEfKJMPdOmtPtFNidsIGlrdIWSIFdNcFYi13zo33FkMk6ZFA6yBnjfDry3fNR+hQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

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

View File

@@ -26,7 +26,7 @@ Parameters:
Description: authentik Docker image
AuthentikVersion:
Type: String
Default: 2025.8.1
Default: 2025.10.0-rc1
Description: authentik Docker image tag
AuthentikServerCPU:
Type: Number

View File

@@ -33,12 +33,15 @@ wait_for_db()
_tmp = Path(gettempdir())
worker_class = "lifecycle.worker.DjangoUvicornWorker"
worker_tmp_dir = str(_tmp.joinpath("authentik_gunicorn_tmp"))
prometheus_tmp_dir = str(_tmp.joinpath("authentik_prometheus_tmp"))
os.makedirs(worker_tmp_dir, exist_ok=True)
os.makedirs(prometheus_tmp_dir, exist_ok=True)
bind = f"unix://{str(_tmp.joinpath('authentik-core.sock'))}"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", prometheus_tmp_dir)
preload_app = True

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/authentik",
"version": "2025.8.1",
"version": "2025.10.0-rc1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/authentik",
"version": "2025.8.1",
"version": "2025.10.0-rc1",
"dependencies": {
"@eslint/js": "^9.31.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/authentik",
"version": "2025.8.1",
"version": "2025.10.0-rc1",
"private": true,
"type": "module",
"dependencies": {

View File

@@ -237,9 +237,6 @@ class _PostgresConsumer(Consumer):
# Override because dramatiq doesn't allow us setting this manually
self.timeout = Conf().worker["consumer_listen_timeout"]
self.lock_purge_interval = timezone.timedelta(seconds=Conf().lock_purge_interval)
self.lock_purge_last_run = timezone.now()
self.task_purge_interval = timezone.timedelta(seconds=Conf().task_purge_interval)
self.task_purge_last_run = timezone.now() - self.task_purge_interval
@@ -381,8 +378,6 @@ class _PostgresConsumer(Consumer):
# Force creation of listen connection
_ = self.listen_connection
self._purge_locks()
processing = len(self.in_processing)
if processing >= self.prefetch:
# Wait and don't consume the message, other worker will be faster
@@ -420,26 +415,24 @@ class _PostgresConsumer(Consumer):
)
# No message to process
self._purge_locks()
self._auto_purge()
self._scheduler()
return None
def _purge_locks(self):
if timezone.now() - self.lock_purge_last_run < self.lock_purge_interval:
return
while True:
try:
message_id = self.unlock_queue.get(block=False)
except Empty:
break
return
self.logger.debug("Unlocking message", message_id=message_id)
with self.connection.cursor() as cursor:
cursor.execute(
"SELECT pg_advisory_unlock(%s)", (self._get_message_lock_id(message_id),)
)
self.unlock_queue.task_done()
self.lock_purge_last_run = timezone.now()
def _auto_purge(self):
if timezone.now() - self.task_purge_last_run < self.task_purge_interval:
@@ -451,7 +444,6 @@ class _PostgresConsumer(Consumer):
result_expiry__lte=timezone.now(),
).delete()
self.logger.info("Purged messages in all queues", count=count)
self.task_purge_last_run = timezone.now()
def _scheduler(self):
if not self.scheduler:
@@ -459,7 +451,6 @@ class _PostgresConsumer(Consumer):
if timezone.now() - self.scheduler_last_run < self.scheduler_interval:
return
self.scheduler.run()
self.schedule_last_run = timezone.now()
@raise_connection_error
def close(self):
@@ -474,7 +465,4 @@ class _PostgresConsumer(Consumer):
if self._listen_connection is not None:
conn = self._listen_connection
self._listen_connection = None
try:
conn.close()
except DatabaseError:
pass
conn.close()

View File

@@ -56,10 +56,6 @@ class Conf:
def task_model(self) -> str:
return self.conf["task_model"]
@property
def lock_purge_interval(self) -> int:
return self.conf.get("lock_purge_interval", 60)
@property
def task_purge_interval(self) -> int:
# 24 hours

View File

@@ -26,7 +26,7 @@ class HTTPServer(BaseHTTPServer):
self.socket.close()
host, port = self.server_address[:2]
if host == "0.0.0.0" and socket.has_dualstack_ipv6(): # nosec
if host == "0.0.0.0": # nosec
host = "::" # nosec
# Strip IPv6 brackets
@@ -36,9 +36,7 @@ class HTTPServer(BaseHTTPServer):
self.server_address = (host, port)
self.address_family = (
socket.AF_INET6
if socket.has_dualstack_ipv6() and isinstance(ip_address(host), IPv6Address)
else socket.AF_INET
socket.AF_INET6 if isinstance(ip_address(host), IPv6Address) else socket.AF_INET
)
self.socket = socket.create_server(
@@ -143,6 +141,7 @@ class MetricsMiddleware(Middleware):
def __init__(
self,
prefix: str,
multiproc_dir: str,
labels: list[str] | None = None,
):
super().__init__()
@@ -152,6 +151,9 @@ class MetricsMiddleware(Middleware):
self.delayed_messages = set()
self.message_start_times = {}
os.makedirs(multiproc_dir, exist_ok=True)
os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", multiproc_dir)
@property
def forks(self):
from django_dramatiq_postgres.forks import worker_metrics

View File

@@ -132,9 +132,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
"cpu": [
"ppc64"
],
@@ -149,9 +149,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
"cpu": [
"arm"
],
@@ -166,9 +166,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
"cpu": [
"arm64"
],
@@ -183,9 +183,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
"cpu": [
"x64"
],
@@ -200,9 +200,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
"cpu": [
"arm64"
],
@@ -217,9 +217,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
"cpu": [
"x64"
],
@@ -234,9 +234,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
"cpu": [
"arm64"
],
@@ -251,9 +251,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
"cpu": [
"x64"
],
@@ -268,9 +268,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
"cpu": [
"arm"
],
@@ -285,9 +285,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
"cpu": [
"arm64"
],
@@ -302,9 +302,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
"cpu": [
"ia32"
],
@@ -319,9 +319,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
"cpu": [
"loong64"
],
@@ -336,9 +336,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
"cpu": [
"mips64el"
],
@@ -353,9 +353,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
"cpu": [
"ppc64"
],
@@ -370,9 +370,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
"cpu": [
"riscv64"
],
@@ -387,9 +387,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
"cpu": [
"s390x"
],
@@ -404,9 +404,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
"cpu": [
"x64"
],
@@ -421,9 +421,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
"cpu": [
"arm64"
],
@@ -438,9 +438,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
"cpu": [
"x64"
],
@@ -455,9 +455,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
"cpu": [
"arm64"
],
@@ -472,9 +472,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
"cpu": [
"x64"
],
@@ -489,9 +489,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
"cpu": [
"arm64"
],
@@ -506,9 +506,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
"cpu": [
"x64"
],
@@ -523,9 +523,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
"cpu": [
"arm64"
],
@@ -540,9 +540,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
"cpu": [
"ia32"
],
@@ -557,9 +557,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
"integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
"cpu": [
"x64"
],
@@ -1269,9 +1269,9 @@
}
},
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1282,32 +1282,32 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.8",
"@esbuild/android-arm": "0.25.8",
"@esbuild/android-arm64": "0.25.8",
"@esbuild/android-x64": "0.25.8",
"@esbuild/darwin-arm64": "0.25.8",
"@esbuild/darwin-x64": "0.25.8",
"@esbuild/freebsd-arm64": "0.25.8",
"@esbuild/freebsd-x64": "0.25.8",
"@esbuild/linux-arm": "0.25.8",
"@esbuild/linux-arm64": "0.25.8",
"@esbuild/linux-ia32": "0.25.8",
"@esbuild/linux-loong64": "0.25.8",
"@esbuild/linux-mips64el": "0.25.8",
"@esbuild/linux-ppc64": "0.25.8",
"@esbuild/linux-riscv64": "0.25.8",
"@esbuild/linux-s390x": "0.25.8",
"@esbuild/linux-x64": "0.25.8",
"@esbuild/netbsd-arm64": "0.25.8",
"@esbuild/netbsd-x64": "0.25.8",
"@esbuild/openbsd-arm64": "0.25.8",
"@esbuild/openbsd-x64": "0.25.8",
"@esbuild/openharmony-arm64": "0.25.8",
"@esbuild/sunos-x64": "0.25.8",
"@esbuild/win32-arm64": "0.25.8",
"@esbuild/win32-ia32": "0.25.8",
"@esbuild/win32-x64": "0.25.8"
"@esbuild/aix-ppc64": "0.25.9",
"@esbuild/android-arm": "0.25.9",
"@esbuild/android-arm64": "0.25.9",
"@esbuild/android-x64": "0.25.9",
"@esbuild/darwin-arm64": "0.25.9",
"@esbuild/darwin-x64": "0.25.9",
"@esbuild/freebsd-arm64": "0.25.9",
"@esbuild/freebsd-x64": "0.25.9",
"@esbuild/linux-arm": "0.25.9",
"@esbuild/linux-arm64": "0.25.9",
"@esbuild/linux-ia32": "0.25.9",
"@esbuild/linux-loong64": "0.25.9",
"@esbuild/linux-mips64el": "0.25.9",
"@esbuild/linux-ppc64": "0.25.9",
"@esbuild/linux-riscv64": "0.25.9",
"@esbuild/linux-s390x": "0.25.9",
"@esbuild/linux-x64": "0.25.9",
"@esbuild/netbsd-arm64": "0.25.9",
"@esbuild/netbsd-x64": "0.25.9",
"@esbuild/openbsd-arm64": "0.25.9",
"@esbuild/openbsd-x64": "0.25.9",
"@esbuild/openharmony-arm64": "0.25.9",
"@esbuild/sunos-x64": "0.25.9",
"@esbuild/win32-arm64": "0.25.9",
"@esbuild/win32-ia32": "0.25.9",
"@esbuild/win32-x64": "0.25.9"
}
},
"node_modules/escape-string-regexp": {
@@ -2728,9 +2728,9 @@
}
},
"node_modules/typedoc-plugin-markdown": {
"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==",
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.1.tgz",
"integrity": "sha512-ug7fc4j0SiJxSwBGLncpSo8tLvrT9VONvPUQqQDTKPxCoFQBADLli832RGPtj6sfSVJebNSrHZQRUdEryYH/7g==",
"dev": true,
"license": "MIT",
"engines": {

View File

@@ -502,17 +502,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz",
"integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz",
"integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/type-utils": "8.39.0",
"@typescript-eslint/utils": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0",
"@typescript-eslint/scope-manager": "8.39.1",
"@typescript-eslint/type-utils": "8.39.1",
"@typescript-eslint/utils": "8.39.1",
"@typescript-eslint/visitor-keys": "8.39.1",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -526,7 +526,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.39.0",
"@typescript-eslint/parser": "^8.39.1",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -542,16 +542,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz",
"integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz",
"integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0",
"@typescript-eslint/scope-manager": "8.39.1",
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/typescript-estree": "8.39.1",
"@typescript-eslint/visitor-keys": "8.39.1",
"debug": "^4.3.4"
},
"engines": {
@@ -567,14 +567,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz",
"integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz",
"integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.39.0",
"@typescript-eslint/types": "^8.39.0",
"@typescript-eslint/tsconfig-utils": "^8.39.1",
"@typescript-eslint/types": "^8.39.1",
"debug": "^4.3.4"
},
"engines": {
@@ -589,14 +589,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz",
"integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz",
"integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0"
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/visitor-keys": "8.39.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -607,9 +607,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz",
"integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz",
"integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -624,15 +624,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz",
"integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz",
"integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0",
"@typescript-eslint/utils": "8.39.0",
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/typescript-estree": "8.39.1",
"@typescript-eslint/utils": "8.39.1",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -649,9 +649,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
"integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz",
"integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -663,16 +663,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz",
"integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz",
"integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.39.0",
"@typescript-eslint/tsconfig-utils": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0",
"@typescript-eslint/project-service": "8.39.1",
"@typescript-eslint/tsconfig-utils": "8.39.1",
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/visitor-keys": "8.39.1",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -731,16 +731,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz",
"integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz",
"integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0"
"@typescript-eslint/scope-manager": "8.39.1",
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/typescript-estree": "8.39.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -755,13 +755,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz",
"integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz",
"integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/types": "8.39.1",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -4709,16 +4709,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz",
"integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.1.tgz",
"integrity": "sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.39.0",
"@typescript-eslint/parser": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0",
"@typescript-eslint/utils": "8.39.0"
"@typescript-eslint/eslint-plugin": "8.39.1",
"@typescript-eslint/parser": "8.39.1",
"@typescript-eslint/typescript-estree": "8.39.1",
"@typescript-eslint/utils": "8.39.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View File

@@ -0,0 +1,67 @@
import type { LitNode } from "../types/lit-jsx.js";
import { test as base } from "@playwright/experimental-ct-react";
import { Locator, Page } from "playwright/test";
export { expect } from "@playwright/test";
/* eslint-disable react-hooks/rules-of-hooks */
export interface InnerMountOptions {}
async function innerMount(page: Page, componentRef: unknown, options = {}) {
await page.waitForFunction(() => {
// @ts-ignore
return !!window.playwrightMount;
});
const selector = await page.evaluate(
async ({ component: component2 }) => {
let rootElement = document.getElementById("root");
if (!rootElement) {
rootElement = document.createElement("div");
rootElement.id = "root";
document.body.appendChild(rootElement);
}
rootElement.textContent = "Test 123";
return "#root >> internal:control=component";
},
{ component: componentRef },
);
return selector;
}
interface E2EFixturesTestScope {
render: (component: LitNode, options?: any) => Promise<Locator>;
}
interface E2EWorkerScope {
renderWorker: void;
}
export const test = base.extend<E2EFixturesTestScope, E2EWorkerScope>({
// renderWorker: [async ({ browser }, use, workerInfo) => {}, { scope: "worker" }],
render: async ({ page }, use) => {
await use(async (componentRef, options) => {
const selector = await innerMount(page, componentRef, options);
const locator = page.locator(selector);
// Object.assign(locator, {
// unmount: async () => {
// await locator.evaluate(async () => {
// const rootElement = document.getElementById("root");
// await window.playwrightUnmount(rootElement);
// });
// },
// update: async (options2) => {
// if (isJsxComponent(options2)) return await innerUpdate(page, options2);
// await innerUpdate(page, componentRef, options2);
// },
// });
return locator;
});
},
});

View File

View File

@@ -0,0 +1,39 @@
import { isCustomElementConstructor, isElementFactory } from "./predicates.js";
import { ComponentProps, createElement } from "./utils.js";
import type * as Lit from "@goauthentik/lit-jsx/types/lit-jsx.d.ts";
/**
* JSX factory for Lit elements.
*/
export function jsx(
elementLike: Lit.ElementType | Lit.ElementFactoryLike,
props: ComponentProps,
): Lit.LitNode {
console.log({ elementLike, props });
if (isElementFactory(elementLike)) {
if (isCustomElementConstructor(elementLike)) {
const tagName = customElements.getName(elementLike);
if (!tagName) {
throw new Error(`Custom element ${elementLike.name} is not registered`);
}
// Render the custom web component as any other html element.
return createElement(tagName, props);
}
return elementLike(props);
}
return createElement(elementLike, props);
}
export { jsx as jsxAttr, jsx as jsxDEV, jsx as jsxEscape, jsx as jsxs, jsx as jsxTemplate };
/**
* HTML Fragment factory for Lit elements.
*/
export function Fragment(fragment: Lit.Fragment): Lit.ElementType[] {
return Array.isArray(fragment.children) ? fragment.children : [fragment.children];
}

View File

@@ -0,0 +1,15 @@
/**
* Type predicate to check if a given value is a custom element constructor.
*/
export function isElementFactory(elementLike: unknown): elementLike is Lit.ElementFactoryLike {
return typeof elementLike === "function" && elementLike.prototype instanceof HTMLElement;
}
/**
* Type predicate to check if a given value is a custom element constructor.
*/
export function isCustomElementConstructor<P = unknown, T extends HTMLElement = HTMLElement>(
elementLike: Lit.ElementFactoryLike<P>,
): elementLike is Lit.CustomElementConstructor<P, T> {
return elementLike.prototype instanceof HTMLElement;
}

View File

@@ -0,0 +1,69 @@
import { spread } from "@open-wc/lit-helpers";
import { ifDefined } from "lit/directives/if-defined.js";
import { ref, RefOrCallback } from "lit/directives/ref.js";
import { styleMap } from "lit/directives/style-map.js";
import { html, unsafeStatic } from "lit/static-html.js";
export type CSSProperties = Record<string, string | number>;
/**
*
*/
export function parseProps(tagName: string, props: Record<PropertyKey, unknown>) {
const spreadable: Record<PropertyKey, unknown> = {};
const ElementConstructor = customElements.get(tagName);
for (const [propName, value] of Object.entries(props)) {
if (propName === "htmlFor") {
spreadable.for = value;
continue;
}
if (propName.startsWith("on")) {
const eventName = propName.slice(2).toLowerCase();
spreadable[`@${eventName}`] = value;
} else if (typeof value === "boolean") {
spreadable[`?${propName}`] = value;
} else if (ElementConstructor) {
spreadable[`.${propName}`] = value;
} else {
spreadable[`${propName}`] = value;
}
}
console.log(spreadable);
return spreadable;
}
export interface ComponentProps {
className?: string;
children?: unknown;
ref?: RefOrCallback;
style?: CSSProperties;
[key: PropertyKey]: unknown;
}
/**
*
* @returns
*/
export function createElement<P = unknown>(
tagName: string,
{ className, children, ref: refProp, style, ...props }: ComponentProps & P,
) {
const tag = unsafeStatic(tagName);
const result = html`
<${tag} class=${ifDefined(className)}
${ref(refProp)}
${spread(parseProps(tagName, props))}
style=${ifDefined(style ? styleMap(style) : null)}
>
${children}
</${tag}>
`;
return result;
}

8135
packages/lit-jsx/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
{
"name": "@goauthentik/lit-jsx",
"version": "1.0.0",
"description": "JSX Runtime for Lit",
"license": "MIT",
"scripts": {
"compile": "tsc -b",
"lint": "run-s lint:prettier:check lint:eslint:check",
"lint:eslint:check": "eslint .",
"lint:eslint:fix": "eslint --fix .",
"lint:fix": "run-s lint:prettier:fix lint:eslint:fix",
"lint:prettier": "eslint .",
"lint:prettier:check": "prettier --cache --check -u .",
"lint:prettier:fix": "prettier --cache --write -u .",
"test": "vitest"
},
"main": "tsconfig.json",
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"types": "./lib/types/lit-jsx.d.ts"
},
"./jsx-runtime": {
"import": "./out/lib/jsx-runtime.js",
"types": "./types/jsx-runtime.d.ts"
},
"./jsx-dev-runtime": {
"import": "./out/lib/jsx-runtime.js",
"types": "./types/jsx-runtime.d.ts"
},
"./types/*": {
"types": "./types/*"
}
},
"imports": {
"#e2e": "./e2e/index.ts",
"#e2e/*": "./e2e/*.ts"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
"@goauthentik/eslint-config": "^1.0.5",
"@goauthentik/prettier-config": "^3.1.0",
"@goauthentik/tsconfig": "^1.0.4",
"@lit-labs/ssr": "^3.3.1",
"@open-wc/lit-helpers": "^0.7.0",
"@playwright/experimental-ct-react": "^1.54.2",
"@playwright/test": "^1.54.2",
"@types/jsdom": "^21.1.7",
"@types/node": "^24.3.0",
"@typescript-eslint/eslint-plugin": "^8.39.1",
"@typescript-eslint/parser": "^8.39.1",
"dompurify": "^3.2.6",
"eslint": "^9.33.0",
"jsdom": "^26.1.0",
"lit": "^3.3.1",
"lit-html": "^3.3.1",
"npm-run-all": "^4.1.5",
"playwright": "^1.54.2",
"prettier": "^3.6.2",
"prettier-plugin-packagejson": "^2.5.19",
"trusted-types": "^2.0.0",
"typescript": "^5.8.3",
"typescript-eslint": "^8.39.1",
"vite": "^7.1.2",
"vitest": "^3.2.4"
},
"peerDependencies": {
"lit": "^3.3.1"
},
"engines": {
"node": ">=24"
},
"prettier": "@goauthentik/prettier-config",
"overrides": {
"format-imports": {
"eslint": "^9.31.0"
}
},
"publishConfig": {
"access": "public"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,43 @@
/**
* @file Playwright configuration.
*
* @see https://playwright.dev/docs/test-configuration
*
*/
import { defineConfig, devices } from "@playwright/test";
const CI = !!process.env.CI;
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",
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
},
},
],
});

View File

@@ -0,0 +1,6 @@
<html lang="en">
<body>
<div id="root"></div>
<script type="module" src="./index.ts"></script>
</body>
</html>

View File

@@ -0,0 +1 @@
console.log("Preparing Playwright");

View File

@@ -0,0 +1,12 @@
import { expect, test } from "#e2e";
test.describe("JSX Rendering", () => {
test("Simple static markup can be rendered", async ({ page, render }) => {
await render(
// @ts-ignore - testing
<p style={{ color: "red", fontSize: "12px" }}>Hello World</p>,
);
await expect(page.getByText("Test 123")).toBeVisible();
});
});

View File

@@ -0,0 +1,48 @@
/** @jsxImportSource @goauthentik/lit-jsx */
import { renderVariants } from "../utils.js";
import { test } from "vitest";
import { html } from "@lit-labs/ssr";
import { styleMap } from "lit/directives/style-map.js";
test("Simple static markup can be rendered", async ({ expect }) => {
const [result, comparision] = await renderVariants(
// @ts-ignore - testing
<div>Hello World</div>,
html`<div>Hello World</div>`,
);
expect(result, "JSX element serialized to a matching string").toBe(comparision);
});
test("`className` is rendered to the correct attribute", async ({ expect }) => {
const [result, comparision] = await renderVariants(
// @ts-ignore - testing
<p className="one two">Hello World</p>,
html`<p class="one two">Hello World</p>`,
);
expect(result, "`className` is rendered to the correct attribute").toBe(comparision);
});
test("`style` is rendered to the correct attribute", async ({ expect }) => {
const [result, comparision] = await renderVariants(
// @ts-ignore - testing
<p style={{ color: "red", fontSize: "12px" }}>Hello World</p>,
html`<p style=${styleMap({ color: "red", fontSize: "12px" })}>Hello World</p>`,
);
expect(result, "style is rendered to the correct attribute").toBe(comparision);
});
test("`style` is rendered to the correct attribute", async ({ expect }) => {
const [result, comparision] = await renderVariants(
// @ts-ignore - testing
<p style={{ color: "red", fontSize: "12px" }}>Hello World</p>,
html`<p style=${styleMap({ color: "red", fontSize: "12px" })}>Hello World</p>`,
);
expect(result, "style is rendered to the correct attribute").toBe(comparision);
});

View File

@@ -0,0 +1,36 @@
import type * as Lit from "@goauthentik/lit-jsx/types/lit-jsx.d.ts";
import createDOMPurify, { Config as DOMPurifyConfig, WindowLike } from "dompurify";
import { JSDOM } from "jsdom";
import { format } from "prettier";
import { render, ServerRenderedTemplate } from "@lit-labs/ssr";
import { collectResult } from "@lit-labs/ssr/lib/render-result.js";
export class Purifier {
#window = new JSDOM("").window;
#DOMPurify = createDOMPurify(this.#window as WindowLike);
public sanitize(html: string, config?: DOMPurifyConfig) {
return this.#DOMPurify.sanitize(html, config);
}
}
const purifier = new Purifier();
export async function renderStaticLit(value: unknown) {
const result = await collectResult(render(value));
const sanitized = purifier.sanitize(result);
const formatted = await format(sanitized, {
parser: "html",
});
return formatted.trim();
}
export function renderVariants(
...inputs: Array<ServerRenderedTemplate | Lit.LitNode>
): Promise<string[]> {
return Promise.all(inputs.map((input) => renderStaticLit(input)));
}

View File

@@ -0,0 +1,15 @@
{
"extends": "@goauthentik/tsconfig",
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"checkJs": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"]
},
"exclude": [
// ---
"**/out/**/*",
"**/dist/**/*",
"storybook-static"
]
}

75
packages/lit-jsx/types/jsx-runtime.d.ts vendored Normal file
View File

@@ -0,0 +1,75 @@
/**
* @file JSX runtime types for Lit.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
// /* eslint-disable @typescript-eslint/no-empty-object-type */
// /// <reference types="./lit-jsx-runtime.d.ts" />
// /**
// * JSX runtime types for Lit.
// */
// export namespace JSX {
// // type ElementType = Lit.JSX.ElementType;
// // interface Element extends Lit.JSX.Element {}
// // interface ElementClass extends Lit.JSX.ElementClass {}
// // interface ElementAttributesProperty extends Lit.JSX.ElementAttributesProperty {}
// // interface ElementChildrenAttribute extends Lit.JSX.ElementChildrenAttribute {}
// // type LibraryManagedAttributes<C, P> = Lit.JSX.LibraryManagedAttributes<C, P>;
// // interface IntrinsicAttributes extends Lit.JSX.IntrinsicAttributes {}
// // interface IntrinsicClassAttributes<T> extends Lit.JSX.IntrinsicClassAttributes<T> {}
// interface IntrinsicElements extends Lit.JSX.IntrinsicElements {}
// }
import { JSX } from "./lit-jsx.js";
import { Attributes, ComponentChild, ComponentChildren, ComponentType, VNode } from "preact";
export { Fragment } from "preact";
export function jsx(
type: string,
props: JSX.HTMLAttributes &
JSX.SVGAttributes &
Record<string, any> & { children?: ComponentChild },
key?: string,
): VNode<any>;
export function jsx<P>(
type: ComponentType<P>,
props: Attributes & P & { children?: ComponentChild },
key?: string,
): VNode<any>;
export function jsxs(
type: string,
props: JSX.HTMLAttributes &
JSX.SVGAttributes &
Record<string, any> & { children?: ComponentChild[] },
key?: string,
): VNode<any>;
export function jsxs<P>(
type: ComponentType<P>,
props: Attributes & P & { children?: ComponentChild[] },
key?: string,
): VNode<any>;
export function jsxDEV(
type: string,
props: JSX.HTMLAttributes &
JSX.SVGAttributes &
Record<string, any> & { children?: ComponentChildren },
key?: string,
): VNode<any>;
export function jsxDEV<P>(
type: ComponentType<P>,
props: Attributes & P & { children?: ComponentChildren },
key?: string,
): VNode<any>;
// These are not expected to be used manually, but by a JSX transform
export function jsxTemplate(template: string[], ...expressions: any[]): VNode<any>;
export function jsxAttr(name: string, value: any): string | null;
export function jsxEscape<T>(value: T): string | null | VNode<any> | Array<string | null | VNode>;
export { JSX };

318
packages/lit-jsx/types/lit-jsx.d.ts vendored Normal file
View File

@@ -0,0 +1,318 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* @file JSX runtime types for Lit.
*/
import { nothing, TemplateResult } from "lit";
export = Lit;
export as namespace Lit;
declare namespace Lit {
namespace JSX {
interface Element {
type: string;
}
interface IntrinsicElements {
div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
}
}
/**
* A constructable element which returns a valid HTMLElement.
*/
type CustomElementConstructor<P = unknown, T extends HTMLElement = HTMLElement> = new (
...args: unknown[]
) => T;
/**
* A function which given a props object, returns a Lit element.
*/
type ElementFactory<P = unknown> = (props: P) => ElementType;
/**
* Either a constructor, or a function which returns an HTML element.
*/
type ElementFactoryLike<P = unknown> = CustomElementConstructor<P> | ElementFactory<P>;
type ElementType<
P = unknown,
Tag extends keyof JSX.IntrinsicElements = keyof JSX.IntrinsicElements,
> =
| { [K in Tag]: P extends JSX.IntrinsicElements[K] ? K : never }[Tag]
| CustomElementConstructor<P>;
interface Fragment {
children: Lit.ElementType | Lit.ElementType[];
}
type LitNode = JSX.Element | ElementType | string | TemplateResult | typeof nothing;
type FC<P = unknown> = (props: P) => LitNode | LitNode[];
// Keep in sync with JSX namespace in ./jsx-runtime.d.ts and ./jsx-dev-runtime.d.ts
}
// Users who only use Preact for SSR might not specify "dom" in their lib in tsconfig.json
// import * as preact from "preact";
// type Defaultize<Props, Defaults> =
// // Distribute over unions
// Props extends any // Make any properties included in Default optional
// ? Partial<Pick<Props, Extract<keyof Props, keyof Defaults>>> & // Include the remaining properties from Props
// Pick<Props, Exclude<keyof Props, keyof Defaults>>
// : never;
// declare namespace JSX {
// export type LibraryManagedAttributes<Component, Props> = Component extends {
// defaultProps: infer Defaults;
// }
// ? Defaultize<Props, Defaults>
// : Props;
// export interface IntrinsicAttributes {
// key?: any;
// }
// export type ElementType<P = any> =
// | {
// [K in keyof IntrinsicElements]: P extends IntrinsicElements[K] ? K : never;
// }[keyof IntrinsicElements]
// | preact.ComponentType<P>;
// export interface Element extends preact.VNode<any> {}
// export type ElementClass = preact.Component<any, any> | preact.FunctionComponent<any>;
// export interface ElementAttributesProperty {
// props: any;
// }
// export interface ElementChildrenAttribute {
// children: any;
// }
// export interface IntrinsicSVGElements {
// svg: preact.SVGAttributes<SVGSVGElement>;
// animate: preact.SVGAttributes<SVGAnimateElement>;
// circle: preact.SVGAttributes<SVGCircleElement>;
// animateMotion: preact.SVGAttributes<SVGAnimateMotionElement>;
// animateTransform: preact.SVGAttributes<SVGAnimateTransformElement>;
// clipPath: preact.SVGAttributes<SVGClipPathElement>;
// defs: preact.SVGAttributes<SVGDefsElement>;
// desc: preact.SVGAttributes<SVGDescElement>;
// ellipse: preact.SVGAttributes<SVGEllipseElement>;
// feBlend: preact.SVGAttributes<SVGFEBlendElement>;
// feColorMatrix: preact.SVGAttributes<SVGFEColorMatrixElement>;
// feComponentTransfer: preact.SVGAttributes<SVGFEComponentTransferElement>;
// feComposite: preact.SVGAttributes<SVGFECompositeElement>;
// feConvolveMatrix: preact.SVGAttributes<SVGFEConvolveMatrixElement>;
// feDiffuseLighting: preact.SVGAttributes<SVGFEDiffuseLightingElement>;
// feDisplacementMap: preact.SVGAttributes<SVGFEDisplacementMapElement>;
// feDistantLight: preact.SVGAttributes<SVGFEDistantLightElement>;
// feDropShadow: preact.SVGAttributes<SVGFEDropShadowElement>;
// feFlood: preact.SVGAttributes<SVGFEFloodElement>;
// feFuncA: preact.SVGAttributes<SVGFEFuncAElement>;
// feFuncB: preact.SVGAttributes<SVGFEFuncBElement>;
// feFuncG: preact.SVGAttributes<SVGFEFuncGElement>;
// feFuncR: preact.SVGAttributes<SVGFEFuncRElement>;
// feGaussianBlur: preact.SVGAttributes<SVGFEGaussianBlurElement>;
// feImage: preact.SVGAttributes<SVGFEImageElement>;
// feMerge: preact.SVGAttributes<SVGFEMergeElement>;
// feMergeNode: preact.SVGAttributes<SVGFEMergeNodeElement>;
// feMorphology: preact.SVGAttributes<SVGFEMorphologyElement>;
// feOffset: preact.SVGAttributes<SVGFEOffsetElement>;
// fePointLight: preact.SVGAttributes<SVGFEPointLightElement>;
// feSpecularLighting: preact.SVGAttributes<SVGFESpecularLightingElement>;
// feSpotLight: preact.SVGAttributes<SVGFESpotLightElement>;
// feTile: preact.SVGAttributes<SVGFETileElement>;
// feTurbulence: preact.SVGAttributes<SVGFETurbulenceElement>;
// filter: preact.SVGAttributes<SVGFilterElement>;
// foreignObject: preact.SVGAttributes<SVGForeignObjectElement>;
// g: preact.SVGAttributes<SVGGElement>;
// image: preact.SVGAttributes<SVGImageElement>;
// line: preact.SVGAttributes<SVGLineElement>;
// linearGradient: preact.SVGAttributes<SVGLinearGradientElement>;
// marker: preact.SVGAttributes<SVGMarkerElement>;
// mask: preact.SVGAttributes<SVGMaskElement>;
// metadata: preact.SVGAttributes<SVGMetadataElement>;
// mpath: preact.SVGAttributes<SVGMPathElement>;
// path: preact.SVGAttributes<SVGPathElement>;
// pattern: preact.SVGAttributes<SVGPatternElement>;
// polygon: preact.SVGAttributes<SVGPolygonElement>;
// polyline: preact.SVGAttributes<SVGPolylineElement>;
// radialGradient: preact.SVGAttributes<SVGRadialGradientElement>;
// rect: preact.SVGAttributes<SVGRectElement>;
// set: preact.SVGAttributes<SVGSetElement>;
// stop: preact.SVGAttributes<SVGStopElement>;
// switch: preact.SVGAttributes<SVGSwitchElement>;
// symbol: preact.SVGAttributes<SVGSymbolElement>;
// text: preact.SVGAttributes<SVGTextElement>;
// textPath: preact.SVGAttributes<SVGTextPathElement>;
// tspan: preact.SVGAttributes<SVGTSpanElement>;
// use: preact.SVGAttributes<SVGUseElement>;
// view: preact.SVGAttributes<SVGViewElement>;
// }
// export interface IntrinsicMathMLElements {
// "annotation": preact.AnnotationMathMLAttributes<MathMLElement>;
// "annotation-xml": preact.AnnotationXmlMathMLAttributes<MathMLElement>;
// /** @deprecated See https://developer.mozilla.org/en-US/docs/Web/MathML/Element/maction */
// "maction": preact.MActionMathMLAttributes<MathMLElement>;
// "math": preact.MathMathMLAttributes<MathMLElement>;
// /** This feature is non-standard. See https://developer.mozilla.org/en-US/docs/Web/MathML/Element/menclose */
// "menclose": preact.MEncloseMathMLAttributes<MathMLElement>;
// "merror": preact.MErrorMathMLAttributes<MathMLElement>;
// /** @deprecated See https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfenced */
// "mfenced": preact.MFencedMathMLAttributes<MathMLElement>;
// "mfrac": preact.MFracMathMLAttributes<MathMLElement>;
// "mi": preact.MiMathMLAttributes<MathMLElement>;
// "mmultiscripts": preact.MmultiScriptsMathMLAttributes<MathMLElement>;
// "mn": preact.MNMathMLAttributes<MathMLElement>;
// "mo": preact.MOMathMLAttributes<MathMLElement>;
// "mover": preact.MOverMathMLAttributes<MathMLElement>;
// "mpadded": preact.MPaddedMathMLAttributes<MathMLElement>;
// "mphantom": preact.MPhantomMathMLAttributes<MathMLElement>;
// "mprescripts": preact.MPrescriptsMathMLAttributes<MathMLElement>;
// "mroot": preact.MRootMathMLAttributes<MathMLElement>;
// "mrow": preact.MRowMathMLAttributes<MathMLElement>;
// "ms": preact.MSMathMLAttributes<MathMLElement>;
// "mspace": preact.MSpaceMathMLAttributes<MathMLElement>;
// "msqrt": preact.MSqrtMathMLAttributes<MathMLElement>;
// "mstyle": preact.MStyleMathMLAttributes<MathMLElement>;
// "msub": preact.MSubMathMLAttributes<MathMLElement>;
// "msubsup": preact.MSubsupMathMLAttributes<MathMLElement>;
// "msup": preact.MSupMathMLAttributes<MathMLElement>;
// "mtable": preact.MTableMathMLAttributes<MathMLElement>;
// "mtd": preact.MTdMathMLAttributes<MathMLElement>;
// "mtext": preact.MTextMathMLAttributes<MathMLElement>;
// "mtr": preact.MTrMathMLAttributes<MathMLElement>;
// "munder": preact.MUnderMathMLAttributes<MathMLElement>;
// "munderover": preact.MUnderMathMLAttributes<MathMLElement>;
// "semantics": preact.SemanticsMathMLAttributes<MathMLElement>;
// }
// export interface IntrinsicHTMLElements {
// a: preact.AccessibleAnchorHTMLAttributes<HTMLAnchorElement>;
// abbr: preact.HTMLAttributes<HTMLElement>;
// address: preact.HTMLAttributes<HTMLElement>;
// area: preact.AccessibleAreaHTMLAttributes<HTMLAreaElement>;
// article: preact.ArticleHTMLAttributes<HTMLElement>;
// aside: preact.AsideHTMLAttributes<HTMLElement>;
// audio: preact.AudioHTMLAttributes<HTMLAudioElement>;
// b: preact.HTMLAttributes<HTMLElement>;
// base: preact.BaseHTMLAttributes<HTMLBaseElement>;
// bdi: preact.HTMLAttributes<HTMLElement>;
// bdo: preact.HTMLAttributes<HTMLElement>;
// big: preact.HTMLAttributes<HTMLElement>;
// blockquote: preact.BlockquoteHTMLAttributes<HTMLQuoteElement>;
// body: preact.HTMLAttributes<HTMLBodyElement>;
// br: preact.BrHTMLAttributes<HTMLBRElement>;
// button: preact.ButtonHTMLAttributes<HTMLButtonElement>;
// canvas: preact.CanvasHTMLAttributes<HTMLCanvasElement>;
// caption: preact.CaptionHTMLAttributes<HTMLTableCaptionElement>;
// cite: preact.HTMLAttributes<HTMLElement>;
// code: preact.HTMLAttributes<HTMLElement>;
// col: preact.ColHTMLAttributes<HTMLTableColElement>;
// colgroup: preact.ColgroupHTMLAttributes<HTMLTableColElement>;
// data: preact.DataHTMLAttributes<HTMLDataElement>;
// datalist: preact.DataListHTMLAttributes<HTMLDataListElement>;
// dd: preact.DdHTMLAttributes<HTMLElement>;
// del: preact.DelHTMLAttributes<HTMLModElement>;
// details: preact.DetailsHTMLAttributes<HTMLDetailsElement>;
// dfn: preact.HTMLAttributes<HTMLElement>;
// dialog: preact.DialogHTMLAttributes<HTMLDialogElement>;
// div: preact.HTMLAttributes<HTMLDivElement>;
// dl: preact.DlHTMLAttributes<HTMLDListElement>;
// dt: preact.DtHTMLAttributes<HTMLElement>;
// em: preact.HTMLAttributes<HTMLElement>;
// embed: preact.EmbedHTMLAttributes<HTMLEmbedElement>;
// fieldset: preact.FieldsetHTMLAttributes<HTMLFieldSetElement>;
// figcaption: preact.FigcaptionHTMLAttributes<HTMLElement>;
// figure: preact.HTMLAttributes<HTMLElement>;
// footer: preact.FooterHTMLAttributes<HTMLElement>;
// form: preact.FormHTMLAttributes<HTMLFormElement>;
// h1: preact.HeadingHTMLAttributes<HTMLHeadingElement>;
// h2: preact.HeadingHTMLAttributes<HTMLHeadingElement>;
// h3: preact.HeadingHTMLAttributes<HTMLHeadingElement>;
// h4: preact.HeadingHTMLAttributes<HTMLHeadingElement>;
// h5: preact.HeadingHTMLAttributes<HTMLHeadingElement>;
// h6: preact.HeadingHTMLAttributes<HTMLHeadingElement>;
// head: preact.HeadHTMLAttributes<HTMLHeadElement>;
// header: preact.HeaderHTMLAttributes<HTMLElement>;
// hgroup: preact.HTMLAttributes<HTMLElement>;
// hr: preact.HrHTMLAttributes<HTMLHRElement>;
// html: preact.HtmlHTMLAttributes<HTMLHtmlElement>;
// i: preact.HTMLAttributes<HTMLElement>;
// iframe: preact.IframeHTMLAttributes<HTMLIFrameElement>;
// img: preact.AccessibleImgHTMLAttributes<HTMLImageElement>;
// input: preact.AccessibleInputHTMLAttributes<HTMLInputElement>;
// ins: preact.InsHTMLAttributes<HTMLModElement>;
// kbd: preact.HTMLAttributes<HTMLElement>;
// keygen: preact.KeygenHTMLAttributes<HTMLUnknownElement>;
// label: preact.LabelHTMLAttributes<HTMLLabelElement>;
// legend: preact.LegendHTMLAttributes<HTMLLegendElement>;
// li: preact.LiHTMLAttributes<HTMLLIElement>;
// link: preact.LinkHTMLAttributes<HTMLLinkElement>;
// main: preact.MainHTMLAttributes<HTMLElement>;
// map: preact.MapHTMLAttributes<HTMLMapElement>;
// mark: preact.HTMLAttributes<HTMLElement>;
// marquee: preact.MarqueeHTMLAttributes<HTMLMarqueeElement>;
// menu: preact.MenuHTMLAttributes<HTMLMenuElement>;
// menuitem: preact.HTMLAttributes<HTMLUnknownElement>;
// meta: preact.MetaHTMLAttributes<HTMLMetaElement>;
// meter: preact.MeterHTMLAttributes<HTMLMeterElement>;
// nav: preact.NavHTMLAttributes<HTMLElement>;
// noscript: preact.NoScriptHTMLAttributes<HTMLElement>;
// object: preact.ObjectHTMLAttributes<HTMLObjectElement>;
// ol: preact.OlHTMLAttributes<HTMLOListElement>;
// optgroup: preact.OptgroupHTMLAttributes<HTMLOptGroupElement>;
// option: preact.OptionHTMLAttributes<HTMLOptionElement>;
// output: preact.OutputHTMLAttributes<HTMLOutputElement>;
// p: preact.HTMLAttributes<HTMLParagraphElement>;
// param: preact.ParamHTMLAttributes<HTMLParamElement>;
// picture: preact.PictureHTMLAttributes<HTMLPictureElement>;
// pre: preact.HTMLAttributes<HTMLPreElement>;
// progress: preact.ProgressHTMLAttributes<HTMLProgressElement>;
// q: preact.QuoteHTMLAttributes<HTMLQuoteElement>;
// rp: preact.HTMLAttributes<HTMLElement>;
// rt: preact.HTMLAttributes<HTMLElement>;
// ruby: preact.HTMLAttributes<HTMLElement>;
// s: preact.HTMLAttributes<HTMLElement>;
// samp: preact.HTMLAttributes<HTMLElement>;
// script: preact.ScriptHTMLAttributes<HTMLScriptElement>;
// search: preact.SearchHTMLAttributes<HTMLElement>;
// section: preact.HTMLAttributes<HTMLElement>;
// select: preact.AccessibleSelectHTMLAttributes<HTMLSelectElement>;
// slot: preact.SlotHTMLAttributes<HTMLSlotElement>;
// small: preact.HTMLAttributes<HTMLElement>;
// source: preact.SourceHTMLAttributes<HTMLSourceElement>;
// span: preact.HTMLAttributes<HTMLSpanElement>;
// strong: preact.HTMLAttributes<HTMLElement>;
// style: preact.StyleHTMLAttributes<HTMLStyleElement>;
// sub: preact.HTMLAttributes<HTMLElement>;
// summary: preact.HTMLAttributes<HTMLElement>;
// sup: preact.HTMLAttributes<HTMLElement>;
// table: preact.TableHTMLAttributes<HTMLTableElement>;
// tbody: preact.HTMLAttributes<HTMLTableSectionElement>;
// td: preact.TdHTMLAttributes<HTMLTableCellElement>;
// template: preact.TemplateHTMLAttributes<HTMLTemplateElement>;
// textarea: preact.TextareaHTMLAttributes<HTMLTextAreaElement>;
// tfoot: preact.HTMLAttributes<HTMLTableSectionElement>;
// th: preact.ThHTMLAttributes<HTMLTableCellElement>;
// thead: preact.HTMLAttributes<HTMLTableSectionElement>;
// time: preact.TimeHTMLAttributes<HTMLTimeElement>;
// title: preact.TitleHTMLAttributes<HTMLTitleElement>;
// tr: preact.HTMLAttributes<HTMLTableRowElement>;
// track: preact.TrackHTMLAttributes<HTMLTrackElement>;
// u: preact.UlHTMLAttributes<HTMLElement>;
// ul: preact.HTMLAttributes<HTMLUListElement>;
// var: preact.HTMLAttributes<HTMLElement>;
// video: preact.VideoHTMLAttributes<HTMLVideoElement>;
// wbr: preact.WbrHTMLAttributes<HTMLElement>;
// }
// export interface IntrinsicElements
// extends IntrinsicSVGElements,
// IntrinsicMathMLElements,
// IntrinsicHTMLElements {}
// }

View File

@@ -0,0 +1,12 @@
/// <reference types="vitest/config" />
import { defineConfig } from "vite";
export default defineConfig({
test: {
name: "unit",
environment: "node",
dir: "./test",
include: ["./**/*.test.{ts,tsx}"],
},
});

View File

@@ -17,7 +17,7 @@ COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,6 +1,6 @@
[project]
name = "authentik"
version = "2025.8.1"
version = "2025.10.0-rc1"
description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.13.*"

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2025.8.1
version: 2025.10.0-rc1
description: Making authentication simple.
contact:
email: hello@goauthentik.io

View File

@@ -10,6 +10,7 @@ from docker.types.healthcheck import Healthcheck
from authentik.core.tests.utils import create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.lib.config import CONFIG
from authentik.outposts.models import (
DockerServiceConnection,
Outpost,
@@ -88,6 +89,7 @@ class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase):
pass
@pytest.mark.timeout(120)
@CONFIG.patch("outposts.container_image_base", "ghcr.io/goauthentik/dev-proxy:gh-main")
def test_docker_controller(self):
"""test that deployment requires update"""
controller = DockerController(self.outpost, self.service_connection)

8
uv.lock generated
View File

@@ -159,7 +159,7 @@ wheels = [
[[package]]
name = "authentik"
version = "2025.8.1"
version = "2025.10.0rc1"
source = { editable = "." }
dependencies = [
{ name = "argon2-cffi" },
@@ -2736,11 +2736,11 @@ wheels = [
[[package]]
name = "redis"
version = "6.2.0"
version = "6.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ea/9a/0551e01ba52b944f97480721656578c8a7c46b51b99d66814f85fe3a4f3e/redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977", size = 4639129, upload-time = "2025-05-28T05:01:18.91Z" }
sdist = { url = "https://files.pythonhosted.org/packages/21/cd/030274634a1a052b708756016283ea3d84e91ae45f74d7f5dcf55d753a0f/redis-6.3.0.tar.gz", hash = "sha256:3000dbe532babfb0999cdab7b3e5744bcb23e51923febcfaeb52c8cfb29632ef", size = 4647275, upload-time = "2025-08-05T08:12:31.648Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/13/67/e60968d3b0e077495a8fee89cf3f2373db98e528288a48f1ee44967f6e8c/redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e", size = 278659, upload-time = "2025-05-28T05:01:16.955Z" },
{ url = "https://files.pythonhosted.org/packages/df/a7/2fe45801534a187543fc45d28b3844d84559c1589255bc2ece30d92dc205/redis-6.3.0-py3-none-any.whl", hash = "sha256:92f079d656ded871535e099080f70fab8e75273c0236797126ac60242d638e9b", size = 280018, upload-time = "2025-08-05T08:12:30.093Z" },
]
[[package]]

1560
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/web",
"version": "2025.8.1",
"version": "2025.10.0-rc1",
"license": "MIT",
"private": true,
"scripts": {
@@ -98,6 +98,7 @@
"@goauthentik/core": "^1.0.0",
"@goauthentik/esbuild-plugin-live-reload": "^1.1.0",
"@goauthentik/eslint-config": "^1.0.5",
"@goauthentik/lit-jsx": "^1.0.0",
"@goauthentik/prettier-config": "^3.1.0",
"@goauthentik/tsconfig": "^1.0.4",
"@hcaptcha/types": "^1.0.4",
@@ -113,18 +114,18 @@
"@openlayers-elements/maps": "^0.4.0",
"@patternfly/elements": "^4.1.0",
"@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^10.3.0",
"@sentry/browser": "^10.5.0",
"@spotlightjs/spotlight": "^3.0.2",
"@storybook/addon-docs": "^9.1.1",
"@storybook/addon-links": "^9.1.1",
"@storybook/web-components": "^9.1.1",
"@storybook/web-components-vite": "^9.1.1",
"@storybook/addon-docs": "^9.1.2",
"@storybook/addon-links": "^9.1.2",
"@storybook/web-components": "^9.1.2",
"@storybook/web-components-vite": "^9.1.2",
"@types/codemirror": "^5.60.16",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.3",
"@types/mocha": "^10.0.10",
"@types/node": "^24.2.1",
"@types/react": "^19.1.8",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
@@ -135,12 +136,12 @@
"chartjs-adapter-date-fns": "^3.0.0",
"codemirror": "^6.0.2",
"construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.44.0",
"core-js": "^3.45.0",
"country-flag-icons": "^1.5.19",
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.2.6",
"esbuild": "^0.25.8",
"esbuild": "^0.25.9",
"esbuild-plugin-copy": "^2.1.1",
"esbuild-plugin-polyfill-node": "^0.3.0",
"esbuild-plugins-node-modules-polyfill": "^1.7.1",
@@ -177,7 +178,7 @@
"ts-pattern": "^5.8.0",
"turnstile-types": "^1.2.3",
"typescript": "^5.8.3",
"typescript-eslint": "^8.39.0",
"typescript-eslint": "^8.39.1",
"unist-util-visit": "^5.0.0",
"webcomponent-qr-code": "^1.3.0",
"wireit": "^0.14.12",
@@ -190,11 +191,11 @@
"@rollup/rollup-darwin-arm64": "^4.46.2",
"@rollup/rollup-linux-arm64-gnu": "^4.46.2",
"@rollup/rollup-linux-x64-gnu": "^4.46.2",
"@wdio/browser-runner": "^9.19.0",
"@wdio/cli": "^9.19.0",
"@wdio/spec-reporter": "^9.19.0",
"@wdio/browser-runner": "^9.19.1",
"@wdio/cli": "^9.19.1",
"@wdio/spec-reporter": "^9.19.1",
"@web/test-runner": "^0.20.2",
"chromedriver": "^139.0.0"
"chromedriver": "^139.0.1"
},
"wireit": {
"build": {

View File

@@ -66,7 +66,7 @@ export class AdminOverviewPage extends AdminOverviewBase {
quickActions: QuickAction[] = [
[msg("Create a new application"), paramURL("/core/applications", { createWizard: true })],
[msg("Check the logs"), paramURL("/events/log")],
[msg("Explore integrations"), "https://integrations.goauthentik.io/", true],
[msg("Explore integrations"), "https://goauthentik.io/integrations/", true],
[msg("Manage users"), paramURL("/identity/users")],
[
msg("Check the release notes"),

View File

@@ -51,8 +51,6 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
}
async send(settingsRequest: SettingsRequest): Promise<Settings> {
settingsRequest.flags ??= this.settings.flags;
const result = await new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({
settingsRequest,
});
@@ -254,7 +252,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
<ak-form-element-horizontal label=${msg("Flags")} name="flags" required>
<ak-codemirror
mode=${CodeMirrorMode.YAML}
value="${YAML.stringify(settings.flags ?? {})}"
value="${YAML.stringify(settings?.flags ?? {})}"
>
</ak-codemirror>
<p class="pf-c-form__helper-text">

View File

@@ -530,8 +530,9 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
</ak-form-element-horizontal>
</div>
</ak-form-group>
<ak-form-group label=${msg("Advanced settings")}>
<div class="pf-c-form">
<ak-form-group>
<span slot="header"> ${msg("Advanced settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Policy engine mode")}
required

View File

@@ -414,8 +414,9 @@ export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm<PlexSo
</ak-form-element-horizontal>
</div>
</ak-form-group>
<ak-form-group label=${msg("Advanced settings")}>
<div class="pf-c-form">
<ak-form-group>
<span slot="header"> ${msg("Advanced settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Policy engine mode")}
required

View File

@@ -574,8 +574,9 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
</ak-form-element-horizontal>
</div>
</ak-form-group>
<ak-form-group label=${msg("Advanced settings")}>
<div class="pf-c-form">
<ak-form-group>
<span slot="header"> ${msg("Advanced settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Policy engine mode")}
required

View File

@@ -20,22 +20,6 @@ export const EscapeTrustPolicy = trustedTypes.createPolicy("authentik-escape", {
},
});
/**
* Trusted types policy that removes all HTML content.
*
*
* @returns {TrustedHTML} All remaining text content.
*/
export const StripHTMLTrustPolicy = trustedTypes.createPolicy("authentik-strip-html", {
createHTML: (untrustedHTML: string) => {
return DOMPurify.sanitize(untrustedHTML, {
RETURN_TRUSTED_TYPE: false,
ALLOWED_TAGS: [],
ALLOWED_ATTR: [],
});
},
});
/**
* Trusted types policy, stripping all HTML content.
*
@@ -121,9 +105,7 @@ export function renderStaticHTMLUnsafe(untrustedHTML: unknown): string {
render(untrustedHTML, container);
const result = container.innerHTML
// Remove all comments as they can interfere with the styles.
.replaceAll("<!---->", "")
.replaceAll(/<!--\?lit\$\d+\$-->/g, "");
const result = container.innerHTML;
return result;
}

View File

@@ -1,18 +1,14 @@
import "#elements/buttons/Dropdown";
import { StripHTMLTrustPolicy } from "#common/purify";
import { rootInterface } from "#common/theme";
import { FormAssociated, FormAssociatedElement } from "#elements/forms/form-associated-element";
import { AKElement } from "#elements/Base";
import { PaginatedResponse } from "#elements/table/Table";
import DjangoQL, { Introspections } from "@mrmarble/djangoql-completion";
import { msg } from "@lit/localize";
import { css, CSSResult, html, LitElement, nothing, PropertyValues, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { createRef, ref, Ref } from "lit/directives/ref.js";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import PFSearchInput from "@patternfly/patternfly/components/SearchInput/search-input.css";
@@ -29,26 +25,40 @@ export class QL extends DjangoQL {
textareaResize() {}
}
/**
* Given an array or length, return logical index of the element at the given delta.
* This is effectively a modulo loop, allowing for positive and negative deltas.
*/
function torusIndex(lengthLike: number | ArrayLike<number>, delta: number): number {
const length = typeof lengthLike === "number" ? lengthLike : lengthLike.length;
@customElement("ak-search-ql")
export class QLSearch extends AKElement {
@property()
value?: string;
if (delta < 0) {
return (length + delta) % length;
@query("[name=search]")
searchElement?: HTMLTextAreaElement;
@state()
menuOpen = false;
@property()
onSearch?: (value: string) => void;
@state()
selected?: number;
@state()
cursorX: number = 0;
@state()
cursorY: number = 0;
ql?: QL;
canvas?: CanvasRenderingContext2D;
set apiResponse(value: PaginatedResponse<unknown> | undefined) {
if (!value || !value.autocomplete || !this.ql) {
return;
}
this.ql.loadIntrospections(value.autocomplete as unknown as Introspections);
}
return ((delta % length) + length) % length;
}
@customElement("ak-search-ql")
export class QLSearch extends FormAssociatedElement<string> implements FormAssociated {
declare anchorRef: Ref<HTMLTextAreaElement>;
declare anchor: HTMLTextAreaElement | null;
public static styles: CSSResult[] = [
static styles: CSSResult[] = [
PFBase,
PFFormControl,
PFSearchInput,
@@ -56,409 +66,207 @@ export class QLSearch extends FormAssociatedElement<string> implements FormAssoc
::-webkit-search-cancel-button {
display: none;
}
.ql.pf-c-form-control {
--input-height: 2.25em;
height: var(--input-height);
min-height: var(--input-height);
max-height: calc(var(--input-height) * 6);
font-family: monospace;
resize: vertical;
height: 2.25em;
}
.selected {
background-color: var(--pf-c-search-input__menu-item--hover--BackgroundColor);
}
:host([theme="dark"]) {
.pf-c-search-input__menu {
--pf-c-search-input__menu--BackgroundColor: var(--ak-dark-background-light-ish);
color: var(--ak-dark-foreground);
}
.pf-c-search-input__menu-item {
--pf-c-search-input__menu-item--Color: var(--ak-dark-foreground);
}
.pf-c-search-input__menu-item:hover {
--pf-c-search-input__menu-item--BackgroundColor: var(
--ak-dark-background-lighter
);
}
.pf-c-search-input__menu-list-item.selected {
--pf-c-search-input__menu-item--hover--BackgroundColor: var(
--ak-dark-background-light
);
}
.pf-c-search-input__text::before {
border: 0;
}
:host([theme="dark"]) .pf-c-search-input__menu {
--pf-c-search-input__menu--BackgroundColor: var(--ak-dark-background-light-ish);
color: var(--ak-dark-foreground);
}
:host([theme="dark"]) .pf-c-search-input__menu-item {
--pf-c-search-input__menu-item--Color: var(--ak-dark-foreground);
}
:host([theme="dark"]) .pf-c-search-input__menu-item:hover {
--pf-c-search-input__menu-item--BackgroundColor: var(--ak-dark-background-lighter);
}
:host([theme="dark"]) .pf-c-search-input__menu-list-item.selected {
--pf-c-search-input__menu-item--hover--BackgroundColor: var(
--ak-dark-background-light
);
}
:host([theme="dark"]) .pf-c-search-input__text::before {
border: 0;
}
.pf-c-search-input__menu {
position: fixed;
min-width: 0;
overflow-y: auto;
max-height: 50vh;
}
`,
];
//#region Properties
@property({ type: Boolean })
public open = false;
@property({ type: Number, attribute: false })
public selectionIndex = -1;
#value = "";
@property({ type: String })
public get value(): string {
return this.#value;
}
public set value(value: unknown) {
const parsed = typeof value === "string" ? value : "";
this.setFormValue(parsed.trim(), parsed);
this.#value = parsed;
if (this.anchor) {
this.anchor.value = this.#value;
}
}
//#endregion
//#region State
#menuRef = createRef<HTMLDivElement>();
#ql: QL | null = null;
#ctx: OffscreenCanvasRenderingContext2D | null = null;
#letterWidth = -1;
#scrollContainer: HTMLElement | null = null;
public set apiResponse(value: PaginatedResponse<unknown> | undefined) {
if (!value?.autocomplete || !this.#ql) {
firstUpdated() {
if (!this.searchElement) {
return;
}
this.#ql.loadIntrospections(value.autocomplete as unknown as Introspections);
}
//#endregion
//#region Lifecycle
public override connectedCallback() {
super.connectedCallback();
this.internals.ariaAutoComplete = "list";
this.internals.role = "combobox";
this.internals.ariaHasPopup = "listbox";
this.#scrollContainer =
rootInterface<LitElement>().renderRoot.querySelector("#main-content");
this.#scrollContainer?.addEventListener("scroll", this.#updateDropdownPosition, {
passive: true,
});
this.tabIndex = 0;
}
public override disconnectedCallback() {
super.disconnectedCallback();
this.#scrollContainer?.removeEventListener("scroll", this.#updateDropdownPosition);
}
public formStateRestoreCallback(state: string) {
this.value = state;
}
public formResetCallback() {
this.value = "";
}
public toJSON() {
return this.value;
}
public override updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("open")) {
this.internals.ariaExpanded = this.open ? "true" : "false";
}
if (changedProperties.has("selectionIndex")) {
const id = `suggestion-${this.selectionIndex}`;
this.setAttribute("aria-activedescendant", this.selectionIndex === -1 ? "" : id);
this.renderRoot.querySelector(`#${id}`)?.scrollIntoView({
behavior: "auto",
block: "nearest",
});
}
}
public override firstUpdated() {
const textarea = this.anchorRef.value;
if (!textarea) return;
this.#ql = new QL({
this.ql = new QL({
completionEnabled: true,
introspections: {
current_model: "",
models: {},
},
selector: textarea,
selector: this.searchElement,
autoResize: false,
});
const canvas = new OffscreenCanvas(300, 150);
this.#ctx = canvas.getContext("2d");
if (!this.#ctx) {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
if (!context) {
console.error("authentik/ql: failed to get canvas context");
return;
}
this.#ctx.font = window.getComputedStyle(textarea).font;
// We need the width of a letter to measure x; we use a monospaced font but still
// check the length for `m` as its the widest ASCII char
const metrics = this.#ctx?.measureText("m");
this.#letterWidth = Math.ceil(metrics?.width || 0) + 1;
context.font = window.getComputedStyle(this.searchElement).font;
this.canvas = context;
}
//#endregion
//#region Completions
#refreshCompletions = () => {
if (this.anchor) {
this.value = this.anchor.value;
}
if (!this.#ql) {
refreshCompletions() {
this.value = this.searchElement?.value;
if (!this.ql) {
return;
}
this.#ql.generateSuggestions();
if (this.#ql.suggestions.length < 1 || this.#ql.loading) {
this.open = false;
this.ql.generateSuggestions();
if (this.ql.suggestions.length < 1 || this.ql.loading) {
this.menuOpen = false;
return;
}
this.open = true;
this.menuOpen = true;
this.updateDropdownPosition();
this.requestUpdate();
}
requestAnimationFrame(this.#updateDropdownPosition);
};
#updateDropdownPosition = () => {
const anchor = this.anchorRef.value;
const menu = this.#menuRef.value;
if (!anchor || !menu) return;
updateDropdownPosition() {
if (!this.searchElement) {
return;
}
const bcr = this.getBoundingClientRect();
const style = window.getComputedStyle(anchor);
// We need the width of a letter to measure x; we use a monospaced font but still
// check the length for `m` as its the widest ASCII char
const metrics = this.canvas?.measureText("m");
const letterWidth = Math.ceil(metrics?.width || 0) + 1;
// Mostly static variables for padding, font line-height and how many
const lineHeight = parseInt(style.lineHeight, 10);
const paddingTop = parseInt(style.paddingTop, 10);
const paddingLeft = parseInt(style.paddingLeft, 10);
const paddingRight = parseInt(style.paddingRight, 10);
const lineHeight = parseInt(window.getComputedStyle(this.searchElement).lineHeight, 10);
const paddingTop = parseInt(window.getComputedStyle(this.searchElement).paddingTop, 10);
const paddingLeft = parseInt(window.getComputedStyle(this.searchElement).paddingLeft, 10);
const paddingRight = parseInt(window.getComputedStyle(this.searchElement).paddingRight, 10);
const actualInnerWidth = bcr.width - paddingLeft - paddingRight;
let relX = 0;
let relY = 1;
let letterIndex = 0;
for (const word of anchor.value.split(" ")) {
this.searchElement.value.split(" ").some((word, idx) => {
letterIndex += word.length;
const newRelX = relX + word.length * this.#letterWidth;
const newRelX = relX + word.length * letterWidth;
if (newRelX > actualInnerWidth) {
relY += 1;
if (letterIndex > anchor.selectionStart) {
if (letterIndex > this.searchElement!.selectionStart) {
relX =
this.#letterWidth * word.length -
(letterIndex - anchor.selectionStart) * this.#letterWidth;
break;
letterWidth * word.length -
(letterIndex - this.searchElement!.selectionStart) * letterWidth;
return true;
}
relX = word.length * this.#letterWidth;
relX = word.length * letterWidth;
} else {
relX = newRelX + 1;
}
}
});
const x = bcr.x + paddingLeft + relX;
const y = bcr.y + paddingTop + relY * lineHeight;
Object.assign(menu.style, {
left: `${x}px`,
top: `${y}px`,
} satisfies Partial<CSSStyleDeclaration>);
};
//#endregion
//#region Event Listeners
#keydownListener = (event: KeyboardEvent) => {
this.#updateDropdownPosition();
const suggestionsLength = this.#ql?.suggestions.length;
if (event.key === "Enter" && !this.open && this.form) {
const submitEvent = new SubmitEvent("submit", {
submitter: this,
bubbles: true,
composed: true,
cancelable: true,
});
this.form.dispatchEvent(submitEvent);
this.cursorX = bcr.x + paddingLeft + relX;
this.cursorY = bcr.y + paddingTop + relY * lineHeight;
}
onKeyDown(ev: KeyboardEvent) {
this.updateDropdownPosition();
if (ev.key === "Enter" && ev.metaKey && this.onSearch && this.searchElement) {
this.onSearch(this.searchElement?.value);
return;
}
if (event.key === "ArrowDown") {
event.preventDefault();
if (this.open && suggestionsLength) {
if (this.selectionIndex === -1) {
this.selectionIndex = 0;
} else {
this.selectionIndex = torusIndex(suggestionsLength, this.selectionIndex + 1);
}
this.#refreshCompletions();
return;
}
this.selectionIndex = 0;
this.#refreshCompletions();
return;
}
if (!this.open) return;
switch (event.key) {
if (!this.menuOpen) return;
switch (ev.key) {
case "ArrowUp":
if (suggestionsLength) {
if (this.selectionIndex === -1) {
this.selectionIndex = suggestionsLength - 1;
if (this.ql?.suggestions.length) {
if (this.selected === undefined) {
this.selected = this.ql?.suggestions.length - 1;
} else if (this.selected === 0) {
this.selected = undefined;
} else {
this.selectionIndex = torusIndex(
suggestionsLength,
this.selectionIndex - 1,
);
this.selected -= 1;
}
this.#refreshCompletions();
event.preventDefault();
this.refreshCompletions();
ev.preventDefault();
}
return;
break;
case "ArrowDown":
if (this.ql?.suggestions.length) {
if (this.selected === undefined) {
this.selected = 0;
} else if (this.selected < this.ql?.suggestions.length - 1) {
this.selected += 1;
} else {
this.selected = undefined;
}
this.refreshCompletions();
ev.preventDefault();
}
break;
case "Tab":
if (this.selectionIndex) {
this.#ql?.selectCompletion(this.selectionIndex);
event.preventDefault();
if (this.selected) {
this.ql?.selectCompletion(this.selected);
ev.preventDefault();
}
return;
break;
case "Enter":
// Technically this is a textarea, due to automatic multi-line feature,
// but other than that it should look and behave like a normal input.
// So expected behavior when pressing Enter is to submit the form,
// not to add a new line.
if (this.selectionIndex !== -1) {
this.#ql?.selectCompletion(this.selectionIndex);
this.selectionIndex = 0;
if (this.selected !== undefined) {
this.ql?.selectCompletion(this.selected);
}
event.preventDefault();
return;
ev.preventDefault();
break;
case "Escape":
this.open = false;
return;
this.menuOpen = false;
break;
case "Shift": // Shift
case "Control": // Ctrl
case "Alt": // Alt
case "Meta": // Windows Key or Cmd on Mac
// Control keys shouldn't trigger completion popup
break;
}
};
}
#blurListener = ({ relatedTarget }: FocusEvent) => {
if (relatedTarget instanceof Node && this.renderRoot.contains(relatedTarget)) {
return;
}
this.open = false;
};
#focusListener = () => {
this.selectionIndex = this.selectionIndex === -1 ? 0 : this.selectionIndex;
this.#refreshCompletions();
};
//#endregion
//#region Render
protected renderMenu() {
if (!this.open || !this.#ql) {
renderMenu() {
if (!this.menuOpen || !this.ql) {
return nothing;
}
return html`
<div ${ref(this.#menuRef)} class="pf-c-search-input__menu">
<ul
class="pf-c-search-input__menu-list"
role="listbox"
id="ql-suggestions"
aria-label=${msg("Query suggestions")}
>
${this.#ql.suggestions.map((suggestion, idx) => {
// Cast to string to sooth Lit Analyzer's primitive type rule.
const label = `${StripHTMLTrustPolicy.createHTML(suggestion.suggestionText)}`;
<div
class="pf-c-search-input__menu"
style="left: ${this.cursorX}px; top: ${this.cursorY}px;"
>
<ul class="pf-c-search-input__menu-list">
${this.ql.suggestions.map((suggestion, idx) => {
return html`<li
role="option"
id="suggestion-${idx}"
aria-selected=${this.selectionIndex === idx ? "true" : "false"}
class="pf-c-search-input__menu-list-item ${this.selectionIndex === idx
class="pf-c-search-input__menu-list-item ${this.selected === idx
? "selected"
: ""}"
>
<button
class="pf-c-search-input__menu-item"
type="button"
aria-label=${label}
@click=${() => {
this.#ql?.selectCompletion(idx);
this.#refreshCompletions();
this.ql?.selectCompletion(idx);
this.refreshCompletions();
}}
>
<span class="pf-c-search-input__menu-item-text pf-m-monospace">
${suggestion.text}</span
<span class="pf-c-search-input__menu-item-text"
>${suggestion.text}</span
>
</button>
</li>`;
@@ -468,33 +276,25 @@ export class QLSearch extends FormAssociatedElement<string> implements FormAssoc
`;
}
public override render(): TemplateResult {
render(): TemplateResult {
return html`<div class="pf-c-search-input">
<div class="pf-c-search-input__bar">
<span class="pf-c-search-input__text">
<textarea
${ref(this.anchorRef)}
class="pf-c-form-control pf-m-monospace ql"
class="pf-c-form-control ql"
name="search"
autocomplete="off"
aria-controls="ql-suggestions"
?required=${this.required}
placeholder=${msg("Search...")}
spellcheck="false"
@input=${this.#refreshCompletions}
@focus=${this.#focusListener}
@blur=${this.#blurListener}
@keydown=${this.#keydownListener}
@input=${(ev: InputEvent) => this.refreshCompletions()}
@keydown=${this.onKeyDown}
>
${ifDefined(this.#value)}</textarea
${ifDefined(this.value)}</textarea
>
</span>
</div>
${this.renderMenu()}
</div>`;
}
//#endregion
}
declare global {

View File

@@ -21,6 +21,7 @@ import diffGrammar from "highlight.js/lib/languages/diff";
import confGrammar from "highlight.js/lib/languages/ini";
import nginxGrammar from "highlight.js/lib/languages/nginx";
import { common } from "lowlight";
import React from "react";
import { createRoot, Root } from "react-dom/client";
import * as runtime from "react/jsx-runtime";
import rehypeHighlight, { Options as HighlightOptions } from "rehype-highlight";
@@ -225,15 +226,17 @@ export class AKMDX extends AKElement {
const { frontmatter = {} } = mdxExports;
this.#reactRoot.render(
<MDXModuleContext.Provider value={mdxModule}>
<Content
frontmatter={frontmatter}
components={{
React.createElement(
MDXModuleContext.Provider,
{ value: mdxModule },
React.createElement(Content, {
frontmatter,
components: {
wrapper: MDXWrapper,
a: MDXAnchor,
}}
/>
</MDXModuleContext.Provider>,
},
}),
),
);
}
}

View File

@@ -49,15 +49,15 @@ export const MDXAnchor = ({
});
};
return (
<a
href={href}
onClick={interceptHeadingLinks}
rel="noopener noreferrer"
target="_blank"
{...props}
>
{children}
</a>
return React.createElement(
"a",
{
href,
onClick: interceptHeadingLinks,
rel: "noopener noreferrer",
target: "_blank",
...props,
},
children,
);
};

View File

@@ -13,8 +13,8 @@ export const MDXWrapper = ({ children, frontmatter }: MDXWrapperProps) => {
const nextChildren = React.Children.toArray(children);
if (title) {
nextChildren.unshift(<h1 key="header-title">{title}</h1>);
nextChildren.unshift(React.createElement("h1", { key: "header-title" }, title));
}
return <div className="pf-c-content">{nextChildren}</div>;
return React.createElement("div", { className: "pf-c-content" }, nextChildren);
};

View File

@@ -9,7 +9,7 @@ import { html } from "lit";
const ACTIONS: QuickAction[] = [
["Create a new application", "/core/applications"],
["Check the logs", "/events/log"],
["Explore integrations", "https://integrations.goauthentik.io/", true],
["Explore integrations", "https://goauthentik.io/integrations/", true],
["Manage users", "/identity/users"],
["Check the release notes", "https://goauthentik.io/docs/releases/", true],
];

View File

@@ -11,7 +11,7 @@ import { html } from "lit";
const ACTIONS: QuickAction[] = [
["Create a new application", "/core/applications"],
["Check the logs", "/events/log"],
["Explore integrations", "https://integrations.goauthentik.io/", true],
["Explore integrations", "https://goauthentik.io/integrations/", true],
["Manage users", "/identity/users"],
["Check the release notes", "https://goauthentik.io/docs/releases/", true],
];

View File

@@ -1,216 +0,0 @@
import { AKElement } from "#elements/Base";
import { Jsonifiable } from "type-fest";
import { msg } from "@lit/localize";
import { LitElement } from "lit";
import { property } from "lit/decorators.js";
import { createRef, Ref } from "lit/directives/ref.js";
/**
* A subset of form associated {@linkcode ElementInternals} properties.
*
* @see {@linkcode FormAssociatedElement} for usage.
*/
export interface FormAssociated
extends Pick<
ElementInternals,
| "form"
| "validity"
| "validationMessage"
| "willValidate"
| "labels"
| "checkValidity"
| "reportValidity"
> {
/**
* The name of the input, provided to the form.
*/
readonly name: string | null;
/**
* The type of the input, provided to the form.
*/
readonly type: string;
/**
* Whether or not the input is required.
*/
required?: boolean;
/**
* Whether or not the input is read-only.
*/
readonly?: boolean;
/**
* A JSON representation of the value.
*/
toJSON(): Jsonifiable;
}
export type FormValue = File | string | FormData | null;
/**
* A base element which provides reactive properties and methods for interacting with a parent form.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals | MDN}
*/
export abstract class FormAssociatedElement<
V extends FormValue = string,
T extends Jsonifiable = V extends string ? V : Jsonifiable,
S extends FormValue = V,
>
extends AKElement
implements FormAssociated
{
static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
public static readonly formAssociated = true;
/**
* The internals of the element.
*
* @protected
* @see {@linkcode FormAssociated}
*/
protected internals = this.attachInternals();
//#region Reactive Properties
@property({ type: Boolean })
public get required() {
return this.internals.ariaRequired === "true";
}
public set required(value: boolean) {
this.internals.ariaRequired = value ? "true" : "false";
}
@property({ type: Boolean, attribute: "readonly" })
public get readOnly() {
return this.internals.ariaReadOnly === "true";
}
public set readOnly(value: boolean) {
this.internals.ariaReadOnly = value ? "true" : "false";
}
@property({ type: Boolean })
public get disabled() {
return this.internals.ariaDisabled === "true";
}
public set disabled(value: boolean) {
this.internals.ariaDisabled = value ? "true" : "false";
}
//#endregion
//#region Aliased Properties
public get form(): HTMLFormElement | null {
return this.internals.form;
}
public get name() {
return this.getAttribute("name");
}
public get type() {
return this.localName;
}
public get validity() {
return this.internals.validity;
}
public get validationMessage() {
return this.internals.validationMessage;
}
public get willValidate() {
return this.internals.willValidate;
}
public get labels() {
return this.internals.labels;
}
//#endregion
//#region Values
/**
* A reference to an element that is focusable when validation fails.
*/
protected anchorRef: Ref<HTMLElement>;
/**
* The element that is focusable when validation fails.
*/
declare protected anchor: HTMLElement | null;
/**
* Set the value of the form.
*
* @param value The value visible to the form during submission.
* @param state The value as provided by the user.
*/
protected setFormValue(value: V, state?: S) {
this.internals.setFormValue(value, state);
if (this.required) {
if (value) {
this.internals.setValidity({});
} else {
this.internals.setValidity(
{
valueMissing: true,
},
msg("This field is required."),
this.anchorRef.value,
);
}
}
}
public abstract toJSON(): T;
//#endregion
//#region Validation
public checkValidity = this.internals.checkValidity.bind(this.internals);
public reportValidity = this.internals.reportValidity.bind(this.internals);
//#endregion
/**
* Set the validity state of the form.
*
* @param flags The validity state flags.
* @param message The validation message.
* @param element The element to set the validity state on.
*/
protected setValidity(flags: ValidityStateFlags = {}, message?: string, element?: HTMLElement) {
this.internals.setValidity(flags, message, element ?? this.anchorRef.value);
}
//#endregion
public constructor() {
super();
this.anchorRef = createRef<HTMLElement>();
// We define the getter here to allow the base type to be extended,
// letting the subclasses define a more accurate HTMLElement type.
Object.defineProperty(this, "anchor", {
get() {
return this.anchorRef.value || null;
},
enumerable: true,
configurable: true,
});
}
}

View File

@@ -1,41 +0,0 @@
/**
* @file Type definitions for form-associated elements.
*
* While these types are part of the HTML standard, they're not yet defined
* in the TypeScript standard library, so we define them here.
*
* @expires 2026-01-01
*/
/**
* Callbacks for form-associated elements.
*/
interface HTMLElement {
/**
* A callback invoked when the browser autofilling sets a value.
*/
formStateRestoreCallback?(state: FormValue, mode: "autocomplete"): void;
/**
* A callback invoked when the browser restores a value from a previous session.
*/
formStateRestoreCallback?(state: FormValue, mode: "restore"): void;
/**
* A callback invoked when the browser restores a value from a previous session.
*/
formStateRestoreCallback?(state: FormValue, mode: "restore" | "autocomplete"): void;
/**
* A callback that is invoked when the form is reset.
*/
formResetCallback?(): void;
/**
* A callback that is invoked when the element's disabled state changes.
*/
formDisabledCallback?(disabled: boolean): void;
/**
* A callback that is invoked when the element is associated with a form.
*/
formAssociatedCallback?(form: HTMLFormElement): void;
}

View File

@@ -124,8 +124,8 @@ export abstract class Table<T extends object>
@property({ type: String })
public order?: string;
@property({ type: String, attribute: false })
public search?: string;
@property({ type: String })
public search: string = "";
@property({ type: Boolean })
public checkbox = false;
@@ -547,11 +547,11 @@ export abstract class Table<T extends object>
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" : ""}"
.defaultValue=${this.search}
value=${ifDefined(this.search)}
label=${ifDefined(this.searchLabel)}
placeholder=${ifDefined(this.searchPlaceholder)}
.onSearch=${this.#searchListener}
.supportsQL=${this.supportsQL}
?supportsQL=${this.supportsQL}
.apiResponse=${this.data}
>
</ak-table-search>

View File

@@ -8,7 +8,6 @@ import { msg } from "@lit/localize";
import { css, CSSResult, html, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { createRef, ref } from "lit/directives/ref.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
@@ -17,23 +16,17 @@ import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@customElement("ak-table-search")
export class TableSearchForm extends WithLicenseSummary(AKElement) {
@property({ type: String, reflect: false })
public defaultValue?: string;
export class TableSearch extends WithLicenseSummary(AKElement) {
@property()
public value?: string;
@property({ type: String })
public label = msg("Table Search");
@property({ type: String })
public placeholder = msg("Search...");
@property({ attribute: false })
@property({ type: Boolean })
public supportsQL: boolean = false;
@property({ attribute: false })
public apiResponse?: PaginatedResponse<unknown>;
@property({ attribute: false })
@property()
public onSearch?: (value: string) => void;
static styles: CSSResult[] = [
@@ -52,26 +45,25 @@ export class TableSearchForm extends WithLicenseSummary(AKElement) {
`,
];
#formRef = createRef<HTMLFormElement>();
public reset = (): void => {
this.#formRef.value?.reset();
this.onSearch?.("");
public reset = () => {
if (!this.onSearch) return;
this.value = "";
this.onSearch("");
};
#submitListener = (event: SubmitEvent) => {
event.preventDefault();
const form = this.#formRef.value;
if (!form || !this.onSearch) return;
form.reportValidity();
if (!this.onSearch) return;
const form = event.target as HTMLFormElement;
const data = new FormData(form);
const value = data.get("search")?.toString() ?? "";
const value = data.get("search")?.toString().trim();
if (!value) {
return;
}
this.onSearch(value);
};
@@ -79,31 +71,27 @@ export class TableSearchForm extends WithLicenseSummary(AKElement) {
renderInput(): TemplateResult {
if (this.supportsQL && this.hasEnterpriseLicense) {
return html`<ak-search-ql
aria-label=${ifDefined(this.label)}
name="search"
required
placeholder=${ifDefined(this.placeholder)}
value=${ifDefined(this.defaultValue)}
.apiResponse=${this.apiResponse}
.value=${this.value}
.onSearch=${(value: string) => {
if (!this.onSearch) return;
this.onSearch(value);
}}
name="search"
></ak-search-ql>`;
}
return html`<input
aria-label=${ifDefined(this.label)}
name="search"
required
placeholder=${ifDefined(this.placeholder)}
value=${ifDefined(this.defaultValue)}
class="pf-c-form-control"
name="search"
type="search"
placeholder=${msg("Search...")}
value="${ifDefined(this.value)}"
/>`;
}
render(): TemplateResult {
return html`<form
${ref(this.#formRef)}
class="pf-c-input-group"
@submit=${this.#submitListener}
>
return html`<form class="pf-c-input-group" method="get" @submit=${this.#submitListener}>
${this.renderInput()}
<button
aria-label=${msg("Clear search")}
@@ -122,6 +110,6 @@ export class TableSearchForm extends WithLicenseSummary(AKElement) {
declare global {
interface HTMLElementTagNameMap {
"ak-table-search": TableSearchForm;
"ak-table-search": TableSearch;
}
}

View File

@@ -54,19 +54,6 @@ export type LitPropertyRecord<T extends object> = {
*/
export type LitPropertyKey<K> = K extends string ? `.${K}` | `?${K}` | K : K;
/**
* A React-like functional component. Used to render a component in a template.
*
* @template P The type of the props object.
* @param props The props object.
* @param children The children to render.
* @returns The rendered template.
*/
export type LitFC<P> = (
props: P,
children?: SlottedTemplateResult,
) => SlottedTemplateResult | SlottedTemplateResult[];
//#endregion
//#region Host/Controller

View File

@@ -3,6 +3,7 @@ import { AKElement } from "#elements/Base";
import { msg } from "@lit/localize";
import { css, CSSResult, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
@@ -17,44 +18,24 @@ export class FormStatic extends AKElement {
static styles: CSSResult[] = [
PFAvatar,
css`
/* Form with user */
.form-control-static {
margin-block-start: var(--pf-global--spacer--sm);
margin-top: var(--pf-global--spacer--sm);
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--pf-global--spacer--sm);
.pf-c-avatar {
flex: 0 0 auto;
}
.primary-content {
display: flex;
align-items: center;
flex: 1 1 auto;
gap: 1rem;
}
.username {
flex: 1 1 auto;
text-align: left;
max-width: 20rem;
text-overflow: ellipsis;
overflow-wrap: break-word;
display: box;
display: -webkit-box;
line-clamp: 3;
-webkit-line-clamp: 3;
box-orient: vertical;
-webkit-box-orient: vertical;
overflow: hidden;
}
.links {
flex: 0 0 auto;
text-align: right;
}
}
.form-control-static .avatar {
display: flex;
align-items: center;
}
.form-control-static img {
margin-right: var(--pf-global--spacer--xs);
}
.form-control-static a {
padding-top: var(--pf-global--spacer--xs);
padding-bottom: var(--pf-global--spacer--xs);
line-height: var(--pf-global--spacer--xl);
}
`,
];
@@ -63,22 +44,17 @@ export class FormStatic extends AKElement {
if (!this.user) {
return nothing;
}
return html`
<div class="form-control-static">
<div class="primary-content">
${this.userAvatar
? html`<img
class="pf-c-avatar"
src=${this.userAvatar}
alt=${msg("User's avatar")}
/>`
: nothing}
<div class="username" aria-label=${msg("Username")}>${this.user}</div>
</div>
<div class="links">
<slot name="link"></slot>
<div class="avatar">
<img
class="pf-c-avatar"
src="${ifDefined(this.userAvatar)}"
alt="${msg("User's avatar")}"
/>
${this.user}
</div>
<slot name="link"></slot>
</div>
`;
}

View File

@@ -33,10 +33,13 @@ export class FlowCard extends AKElement {
PFLogin,
PFTitle,
css`
.pf-c-login__main-footer {
display: block;
slot[name="footer"],
slot[name="footer-band"] {
display: flex;
flex-wrap: wrap;
justify-content: center;
flex-basis: 100%;
}
slot[name="footer-band"] {
text-align: center;
background-color: var(--pf-c-login__main-footer-band--BackgroundColor);

View File

@@ -24,7 +24,6 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
css`
.icon-description {
display: flex;
align-items: center;
}
.icon-description i {
font-size: 2em;

View File

@@ -34,7 +34,7 @@ export class BaseDeviceStage<
css`
.pf-c-form__group.pf-m-action {
display: flex;
gap: 1rem;
gap: 16px;
margin-top: 0;
margin-bottom: calc(var(--pf-c-form__group--m-action--MarginTop) / 2);
flex-direction: column;

View File

@@ -9,7 +9,7 @@ import { ListenerController } from "#elements/utils/listenerController";
import { randomId } from "#elements/utils/randomId";
import { BaseStage } from "#flow/stages/base";
import { CaptchaHandler, CaptchaProvider, iframeTemplate } from "#flow/stages/captcha/shared";
import { CaptchaHandler, iframeTemplate } from "#flow/stages/captcha/shared";
import { CaptchaChallenge, CaptchaChallengeResponseRequest } from "@goauthentik/api";
@@ -109,7 +109,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
//#region State
@state()
protected activeHandler: CaptchaProvider | null = null;
protected activeHandler: CaptchaHandler | null = null;
@state()
protected error: string | null = null;
@@ -265,12 +265,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
//#endregion
/**
* Mapping of captcha provider names to their respective JS API global.
*
* Note that this is a `Map` to ensure the preferred order of discovering provider globals.
*/
#handlers = new Map<CaptchaProvider, CaptchaHandler>([
#handlers = new Map<string, CaptchaHandler>([
[
"grecaptcha",
{
@@ -420,7 +415,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
//#endregion
//#region Resizing
//#region Listeners
#loadListener = () => {
const iframe = this.#iframeRef.value;
@@ -428,73 +423,17 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
if (!iframe || !contentDocument) return;
let synchronizeHeight: () => void;
const resizeListener: ResizeObserverCallback = () => {
if (!this.#iframeRef) return;
if (this.activeHandler === CaptchaProvider.reCAPTCHA) {
// reCAPTCHA's use of nested iframes prevents their internal resize observer from
// reporting the correct height back to our iframe, so we have to do it ourselves.
const target = contentDocument.getElementById("ak-container");
synchronizeHeight = () => {
if (!this.#iframeRef) return;
if (!target) return;
const target = contentDocument.getElementById("ak-container");
this.iframeHeight = Math.round(target.clientHeight);
};
if (!target) return;
const innerIFrame = contentDocument.querySelector<HTMLIFrameElement>(
'iframe[style~="height:"]',
);
const innerBottom = innerIFrame?.getBoundingClientRect().bottom ?? 0;
const actualHeight = Math.max(innerBottom, target.clientHeight);
this.iframeHeight = Math.round(actualHeight * 1.1);
if (innerIFrame?.parentElement) {
innerIFrame.parentElement.style.height = `${actualHeight}px`;
}
};
// We watch for any newly inserted iframes, as they may alter the height
// of the parent iframe...
const mutationObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type !== "childList") continue;
for (const node of mutation.addedNodes as NodeListOf<HTMLElement>) {
if (node.tagName !== "IFRAME") continue;
// And then resize the iframe to match the new size.
//
// This doesn't fix the issue entirely since the challenge frame
// doesn't yet know the correct height, but at least the user can
// try to load the challenge again with the correct height.
resizeObserver.observe(node as HTMLIFrameElement);
requestAnimationFrame(synchronizeHeight);
}
}
});
mutationObserver.observe(contentDocument.body, {
childList: true,
subtree: true,
});
} else {
synchronizeHeight = () => {
if (!this.#iframeRef) return;
const target = contentDocument.getElementById("ak-container");
if (!target) return;
this.iframeHeight = Math.round(target.clientHeight);
};
}
const resizeObserver = new ResizeObserver(synchronizeHeight);
const resizeObserver = new ResizeObserver(resizeListener);
requestAnimationFrame(() => {
resizeObserver.observe(contentDocument.body);
@@ -503,26 +442,22 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
});
};
//#endregion
//#region Loading
#scriptLoadListener = async (): Promise<void> => {
console.debug("authentik/stages/captcha: script loaded");
this.error = null;
this.#iframeLoaded = false;
for (const name of this.#handlers.keys()) {
for (const [name, handler] of this.#handlers) {
if (!Object.hasOwn(window, name)) {
continue;
}
try {
await this.#run(name);
await this.#run(handler);
console.debug(`authentik/stages/captcha[${name}]: handler succeeded`);
this.activeHandler = name;
this.activeHandler = handler;
return;
} catch (error) {
@@ -534,9 +469,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
}
};
async #run(captchaProvider: CaptchaProvider) {
const handler = this.#handlers.get(captchaProvider)!;
async #run(handler: CaptchaHandler) {
if (this.challenge.interactive) {
const iframe = this.#iframeRef.value;
@@ -545,44 +478,18 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
return;
}
const { contentDocument } = iframe;
if (!contentDocument) {
console.debug(
`authentik/stages/captcha: No iframe content window found, skipping.`,
);
return;
}
console.debug(`authentik/stages/captcha: Rendering interactive.`);
const captchaElement = handler.interactive();
const template = iframeTemplate(captchaElement, {
challengeURL: this.challenge.jsUrl,
theme: this.activeTheme,
});
const template = iframeTemplate(captchaElement, this.challenge.jsUrl);
if (captchaProvider === CaptchaProvider.reCAPTCHA) {
// reCAPTCHA's domain verification can't seem to penetrate the true origin
// of the page when loaded from a blob URL, likely due to their double-nested
// iframe structure.
// We fallback to the deprecated `document.write` to get around this.
this.#iframeSource = "about:blank";
contentDocument.open();
contentDocument.write(template);
contentDocument.close();
URL.revokeObjectURL(this.#iframeSource);
// this.#loadListener();
} else {
URL.revokeObjectURL(this.#iframeSource);
const url = URL.createObjectURL(new Blob([template], { type: "text/html" }));
const url = URL.createObjectURL(new Blob([template], { type: "text/html" }));
this.#iframeSource = url;
this.#iframeSource = url;
iframe.src = url;
}
iframe.src = url;
return;
}

View File

@@ -1,20 +1,7 @@
import type { ResolvedUITheme } from "#common/theme";
import { createDocumentTemplate } from "#elements/utils/iframe";
import { html, TemplateResult } from "lit";
/**
* Mapping of captcha provider names to their respective JS API global.
*/
export const CaptchaProvider = {
reCAPTCHA: "grecaptcha",
hCaptcha: "hcaptcha",
Turnstile: "turnstile",
} as const satisfies Record<string, string>;
export type CaptchaProvider = (typeof CaptchaProvider)[keyof typeof CaptchaProvider];
export interface CaptchaHandler {
interactive(): TemplateResult;
execute(): Promise<void>;
@@ -22,29 +9,6 @@ export interface CaptchaHandler {
refresh(): Promise<void>;
}
const ThemeColor = {
dark: "#18191a",
light: "#ffffff",
} as const satisfies Record<ResolvedUITheme, string>;
export function themeMeta(theme: ResolvedUITheme) {
switch (theme) {
case "dark":
return html`
<meta name="color-scheme" content="dark" />
<meta name="theme-color" content=${ThemeColor.dark} />
`;
case "light":
return html` <meta name="color-scheme" content="light" />
<meta name="theme-color" content=${ThemeColor.light} />`;
}
}
export interface IFrameTemplateInit {
challengeURL: string;
theme: ResolvedUITheme;
}
/**
* A container iframe for a hosted Captcha, with an event emitter to monitor
* when the Captcha forces a resize.
@@ -53,17 +17,10 @@ export interface IFrameTemplateInit {
* margin, adding 2rem of height to our container adds padding and prevents scrollbars
* or hidden rendering.
*/
export function iframeTemplate(
children: TemplateResult,
{ challengeURL, theme }: IFrameTemplateInit,
) {
export function iframeTemplate(children: TemplateResult, challengeURL: string): string {
return createDocumentTemplate({
head: html`
<meta charset="UTF-8" />
head: html`<meta charset="UTF-8" />
${themeMeta(theme)}
`,
body: html`
<script>
"use strict";
@@ -86,11 +43,6 @@ export function iframeTemplate(
</script>
<style>
html,
body {
background: ${ThemeColor[theme]};
}
body {
margin: 0;
padding: 0;
@@ -106,9 +58,8 @@ export function iframeTemplate(
align-items: center;
justify-content: center;
}
</style>
${children}
<script onload="loadListener()" src="${challengeURL}"></script>
`,
</style>`,
body: html`${children}
<script onload="loadListener()" src="${challengeURL}"></script> `,
});
}

View File

@@ -58,12 +58,6 @@ export class IdentificationStage extends BaseStage<
PFButton,
...AkRememberMeController.styles,
css`
.pf-c-form__group.pf-m-action {
display: flex;
gap: 1rem;
flex-direction: column;
}
/* login page's icons */
.pf-c-login__main-footer-links-item button {
background-color: transparent;

View File

@@ -14,6 +14,8 @@
"module": "esnext",
"moduleResolution": "bundler",
"baseUrl": ".",
"jsx": "react-jsx",
"jsxImportSource": "@goauthentik/lit-jsx",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
// TODO: We should enable this when when we're ready to enforce it.
"noUncheckedIndexedAccess": false,

View File

@@ -1466,11 +1466,6 @@
<source>No form found</source>
<target>Kein Formular gefunden</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Das Formular hat keinen Wert zum Absenden zurückgegeben</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -10042,6 +10037,12 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1177,10 +1177,6 @@
<source>No form found</source>
<target>No form found</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Form didn't return a promise for submitting</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
<target>Select type</target>
@@ -7921,6 +7917,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1466,11 +1466,6 @@
<source>No form found</source>
<target>No se encontró ningún formulario</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>El formulario no devolvió una promesa para enviarla</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -10090,6 +10085,12 @@ El valor de este campo se compara con el atributo de pertenencia del usuario.</t
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1466,11 +1466,6 @@
<source>No form found</source>
<target>Aucun formulaire trouvé</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Le formulaire n'a pas retourné de promesse de soumission</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -10113,6 +10108,12 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
<target>None</target>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1466,11 +1466,6 @@
<source>No form found</source>
<target>Nessun modulo trovato</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Il modulo non ha restituito una promise di invio</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -10046,6 +10041,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1449,11 +1449,6 @@
<source>No form found</source>
<target>찾을 수 없음</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>양식에서 제출하기로 한 것을 반환하지 않음</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -9368,6 +9363,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1459,11 +1459,6 @@
<source>No form found</source>
<target>Geen formulier gevonden</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Formulier heeft geen promise geretourneerd voor indienen</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -9274,6 +9269,12 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1466,11 +1466,6 @@
<source>No form found</source>
<target>Nie znaleziono formularza</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Formularz nie zwrócił obietnicy do przesłania</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -9689,6 +9684,12 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1456,11 +1456,6 @@
<source>No form found</source>
<target>Ńō ƒōŕḿ ƒōũńď</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Ƒōŕḿ ďĩďń'ţ ŕēţũŕń à ƥŕōḿĩśē ƒōŕ śũƀḿĩţţĩńĝ</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -9698,4 +9693,10 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body></file></xliff>

View File

@@ -1466,11 +1466,6 @@
<source>No form found</source>
<target>Форма не найдена</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Форма не вернула обещание отправить</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -9780,6 +9775,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

View File

@@ -1455,11 +1455,6 @@
<source>No form found</source>
<target>Form bulunamadı</target>
</trans-unit>
<trans-unit id="s45935843b1b5b496">
<source>Form didn't return a promise for submitting</source>
<target>Form göndermek için bir söz vermedi</target>
</trans-unit>
<trans-unit id="s74475586afc1fb0f">
<source>Select type</source>
@@ -9753,6 +9748,12 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit>
<trans-unit id="s669b18c6d2d9c95b">
<source>None</source>
</trans-unit>
<trans-unit id="s2de7890b100c4f4c">
<source>Flags</source>
</trans-unit>
<trans-unit id="sf0b34d32a602aee8">
<source>Modify flags to opt into new authentik behaviours early.</source>
</trans-unit>
</body>
</file>

Some files were not shown because too many files have changed in this diff Show More