diff --git a/web/.prettierrc.json b/web/.prettierrc.json
deleted file mode 100644
index d1897435dc..0000000000
--- a/web/.prettierrc.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "arrowParens": "always",
- "bracketSpacing": true,
- "embeddedLanguageFormatting": "auto",
- "htmlWhitespaceSensitivity": "css",
- "insertPragma": false,
- "jsxSingleQuote": false,
- "printWidth": 100,
- "proseWrap": "preserve",
- "quoteProps": "consistent",
- "requirePragma": false,
- "semi": true,
- "singleQuote": false,
- "tabWidth": 4,
- "trailingComma": "all",
- "useTabs": false,
- "vueIndentScriptAndStyle": false,
- "plugins": ["@trivago/prettier-plugin-sort-imports"],
- "importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
- "importOrderSeparation": true,
- "importOrderSortSpecifiers": true,
- "importOrderParserPlugins": ["typescript", "jsx", "classProperties", "decorators-legacy"]
-}
diff --git a/web/package-lock.json b/web/package-lock.json
index acbb6602ca..af3baaa947 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -73,6 +73,9 @@
},
"devDependencies": {
"@eslint/js": "^9.11.1",
+ "@goauthentik/esbuild-plugin-live-reload": "^1.0.4",
+ "@goauthentik/prettier-config": "^1.0.4",
+ "@goauthentik/tsconfig": "^1.0.4",
"@hcaptcha/types": "^1.0.4",
"@lit/localize-tools": "^0.8.0",
"@rollup/plugin-replace": "^6.0.1",
@@ -84,7 +87,7 @@
"@storybook/manager-api": "^8.3.4",
"@storybook/web-components": "^8.3.4",
"@storybook/web-components-vite": "^8.3.4",
- "@trivago/prettier-plugin-sort-imports": "^4.3.0",
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/chart.js": "^2.9.41",
"@types/codemirror": "^5.60.15",
"@types/dompurify": "^3.0.5",
@@ -107,7 +110,6 @@
"eslint": "^9.11.1",
"eslint-plugin-lit": "^1.15.0",
"eslint-plugin-wc": "^2.1.1",
- "find-free-ports": "^3.1.1",
"github-slugger": "^2.0.0",
"glob": "^11.0.0",
"globals": "^15.10.0",
@@ -234,40 +236,6 @@
"url": "https://opencollective.com/babel"
}
},
- "node_modules/@babel/core/node_modules/@babel/generator": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
- "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.27.0",
- "@babel/types": "^7.27.0",
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25",
- "jsesc": "^3.0.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/core/node_modules/@babel/traverse": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
- "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.26.2",
- "@babel/generator": "^7.27.0",
- "@babel/parser": "^7.27.0",
- "@babel/template": "^7.27.0",
- "@babel/types": "^7.27.0",
- "debug": "^4.3.1",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/core/node_modules/@babel/types": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
@@ -287,27 +255,6 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true
},
- "node_modules/@babel/core/node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/core/node_modules/jsesc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -318,26 +265,34 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.17.7",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
- "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
+ "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/types": "^7.17.0",
- "jsesc": "^2.5.1",
- "source-map": "^0.5.0"
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/@babel/generator/node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "node_modules/@babel/generator/node_modules/@babel/types": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
"engines": {
- "node": ">=0.10.0"
+ "node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets": {
@@ -379,6 +334,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
"integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.24.7"
},
@@ -391,6 +347,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
@@ -404,6 +361,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
"integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/template": "^7.24.7",
"@babel/types": "^7.24.7"
@@ -417,6 +375,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
@@ -430,6 +389,7 @@
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.24.7"
},
@@ -442,6 +402,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
@@ -463,40 +424,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-module-imports/node_modules/@babel/generator": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
- "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.27.0",
- "@babel/types": "^7.27.0",
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25",
- "jsesc": "^3.0.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-imports/node_modules/@babel/traverse": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
- "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.26.2",
- "@babel/generator": "^7.27.0",
- "@babel/parser": "^7.27.0",
- "@babel/template": "^7.27.0",
- "@babel/types": "^7.27.0",
- "debug": "^4.3.1",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-module-imports/node_modules/@babel/types": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
@@ -510,27 +437,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-module-imports/node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/helper-module-imports/node_modules/jsesc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@babel/helper-module-transforms": {
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
@@ -548,79 +454,12 @@
"@babel/core": "^7.0.0"
}
},
- "node_modules/@babel/helper-module-transforms/node_modules/@babel/generator": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
- "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.27.0",
- "@babel/types": "^7.27.0",
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25",
- "jsesc": "^3.0.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
- "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.26.2",
- "@babel/generator": "^7.27.0",
- "@babel/parser": "^7.27.0",
- "@babel/template": "^7.27.0",
- "@babel/types": "^7.27.0",
- "debug": "^4.3.1",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms/node_modules/@babel/types": {
- "version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
- "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-string-parser": "^7.25.9",
- "@babel/helper-validator-identifier": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms/node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/helper-module-transforms/node_modules/jsesc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@babel/helper-split-export-declaration": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
"integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/types": "^7.24.7"
},
@@ -633,6 +472,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
@@ -774,37 +614,19 @@
}
},
"node_modules/@babel/traverse": {
- "version": "7.23.2",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
- "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.22.13",
- "@babel/generator": "^7.23.0",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-function-name": "^7.23.0",
- "@babel/helper-hoist-variables": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.23.0",
- "@babel/types": "^7.23.0",
- "debug": "^4.1.0",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse/node_modules/@babel/generator": {
"version": "7.27.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
- "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
+ "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.27.0",
"@babel/parser": "^7.27.0",
+ "@babel/template": "^7.27.0",
"@babel/types": "^7.27.0",
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25",
- "jsesc": "^3.0.2"
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
},
"engines": {
"node": ">=6.9.0"
@@ -815,6 +637,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
@@ -828,22 +651,11 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=4"
}
},
- "node_modules/@babel/traverse/node_modules/jsesc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
- "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@babel/types": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
@@ -1731,6 +1543,36 @@
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.2.4-1744886287.tgz",
"integrity": "sha512-Wx1/tS3/8fxI/rd8m8WOTuzDOKcUASWdOsoPsds2CxswMi3pt4KaLlh/Hcl8gAabsJnIUNZu4Ajj1p1fMJvwxw=="
},
+ "node_modules/@goauthentik/esbuild-plugin-live-reload": {
+ "resolved": "packages/esbuild-plugin-live-reload",
+ "link": true
+ },
+ "node_modules/@goauthentik/prettier-config": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.4.tgz",
+ "integrity": "sha512-CgUVAThlJHif7ZRXUPMbR/7/YLGzkJw7YbqEcleUjjKkvID0aykrypXx04td6cG76zigspTCgJKoXimKT41E7g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.11"
+ },
+ "peerDependencies": {
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
+ "prettier": "^3.5.3",
+ "prettier-plugin-organize-imports": "^4.1.0",
+ "prettier-plugin-packagejson": "^2.5.10"
+ }
+ },
+ "node_modules/@goauthentik/tsconfig": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@goauthentik/tsconfig/-/tsconfig-1.0.4.tgz",
+ "integrity": "sha512-BTGVpGh8SbCRHTULBf+2WTcw6OHJ8Ws9VtVfAMUUgcq8whbH/A7Q/n8WbkDaEeihzHUFkLk3JBenHKzEKAZWlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.11"
+ }
+ },
"node_modules/@goauthentik/web": {
"resolved": "",
"link": true
@@ -3761,6 +3603,20 @@
"node": ">=14"
}
},
+ "node_modules/@pkgr/core": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz",
+ "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
"node_modules/@prisma/instrumentation": {
"version": "5.22.0",
"resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-5.22.0.tgz",
@@ -5989,28 +5845,54 @@
"dev": true
},
"node_modules/@trivago/prettier-plugin-sort-imports": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz",
- "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==",
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz",
+ "integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "@babel/generator": "7.17.7",
- "@babel/parser": "^7.20.5",
- "@babel/traverse": "7.23.2",
- "@babel/types": "7.17.0",
- "javascript-natural-sort": "0.7.1",
+ "@babel/generator": "^7.26.5",
+ "@babel/parser": "^7.26.7",
+ "@babel/traverse": "^7.26.7",
+ "@babel/types": "^7.26.7",
+ "javascript-natural-sort": "^0.7.1",
"lodash": "^4.17.21"
},
+ "engines": {
+ "node": ">18.12"
+ },
"peerDependencies": {
"@vue/compiler-sfc": "3.x",
- "prettier": "2.x - 3.x"
+ "prettier": "2.x - 3.x",
+ "prettier-plugin-svelte": "3.x",
+ "svelte": "4.x || 5.x"
},
"peerDependenciesMeta": {
"@vue/compiler-sfc": {
"optional": true
+ },
+ "prettier-plugin-svelte": {
+ "optional": true
+ },
+ "svelte": {
+ "optional": true
}
}
},
+ "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/types": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -11121,6 +11003,31 @@
"dev": true,
"optional": true
},
+ "node_modules/detect-indent": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz",
+ "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "node_modules/detect-newline": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz",
+ "integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/devlop": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
@@ -13284,8 +13191,7 @@
"node_modules/find-free-ports": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/find-free-ports/-/find-free-ports-3.1.1.tgz",
- "integrity": "sha512-hQebewth9i5qkf0a0u06iFaxQssk5ZnPBBggsa1vk8zCYaZoz9IZXpoRLTbEOrYdqfrjvcxU00gYoCPgmXugKA==",
- "dev": true
+ "integrity": "sha512-hQebewth9i5qkf0a0u06iFaxQssk5ZnPBBggsa1vk8zCYaZoz9IZXpoRLTbEOrYdqfrjvcxU00gYoCPgmXugKA=="
},
"node_modules/find-up": {
"version": "5.0.0",
@@ -13622,6 +13528,20 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-stdin": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
+ "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/get-stream": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
@@ -13708,6 +13628,17 @@
"giget": "dist/cli.mjs"
}
},
+ "node_modules/git-hooks-list": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz",
+ "integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/fisker/git-hooks-list?sponsor=1"
+ }
+ },
"node_modules/github-slugger": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
@@ -15648,15 +15579,16 @@
}
},
"node_modules/jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
+ "license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
- "node": ">=4"
+ "node": ">=6"
}
},
"node_modules/json-buffer": {
@@ -19551,6 +19483,44 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
+ "node_modules/prettier-plugin-organize-imports": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz",
+ "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "prettier": ">=2.0",
+ "typescript": ">=2.9",
+ "vue-tsc": "^2.1.0"
+ },
+ "peerDependenciesMeta": {
+ "vue-tsc": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/prettier-plugin-packagejson": {
+ "version": "2.5.10",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz",
+ "integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "sort-package-json": "2.15.1",
+ "synckit": "0.9.2"
+ },
+ "peerDependencies": {
+ "prettier": ">= 1.16.0"
+ },
+ "peerDependenciesMeta": {
+ "prettier": {
+ "optional": true
+ }
+ }
+ },
"node_modules/pretty-format": {
"version": "30.0.0-alpha.6",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.0-alpha.6.tgz",
@@ -21697,6 +21667,35 @@
"node": ">=0.10.0"
}
},
+ "node_modules/sort-object-keys": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz",
+ "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/sort-package-json": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.15.1.tgz",
+ "integrity": "sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "detect-indent": "^7.0.1",
+ "detect-newline": "^4.0.0",
+ "get-stdin": "^9.0.0",
+ "git-hooks-list": "^3.0.0",
+ "is-plain-obj": "^4.1.0",
+ "semver": "^7.6.0",
+ "sort-object-keys": "^1.1.3",
+ "tinyglobby": "^0.2.9"
+ },
+ "bin": {
+ "sort-package-json": "cli.js"
+ }
+ },
"node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@@ -22302,6 +22301,24 @@
"ramda-adjunct": "^5.1.0"
}
},
+ "node_modules/synckit": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
+ "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
"node_modules/syncpack": {
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/syncpack/-/syncpack-13.0.3.tgz",
@@ -24634,6 +24651,153 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "packages/esbuild-plugin-live-reload": {
+ "name": "@goauthentik/esbuild-plugin-live-reload",
+ "version": "1.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "find-free-ports": "^3.1.1"
+ },
+ "devDependencies": {
+ "@goauthentik/prettier-config": "^1.0.4",
+ "@goauthentik/tsconfig": "^1.0.4",
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
+ "@types/node": "^22.14.1",
+ "esbuild": "^0.25.0",
+ "prettier": "^3.3.3",
+ "typescript": "^5.6.2"
+ },
+ "engines": {
+ "node": ">=20.11"
+ },
+ "peerDependencies": {
+ "esbuild": "^0.25.0"
+ }
+ },
+ "packages/esbuild-plugin-live-reload/node_modules/@babel/generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
+ "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "packages/esbuild-plugin-live-reload/node_modules/@babel/traverse": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
+ "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.27.0",
+ "@babel/parser": "^7.27.0",
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "packages/esbuild-plugin-live-reload/node_modules/@babel/types": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "packages/esbuild-plugin-live-reload/node_modules/@goauthentik/prettier-config": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@goauthentik/prettier-config/-/prettier-config-1.0.4.tgz",
+ "integrity": "sha512-CgUVAThlJHif7ZRXUPMbR/7/YLGzkJw7YbqEcleUjjKkvID0aykrypXx04td6cG76zigspTCgJKoXimKT41E7g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.11"
+ },
+ "peerDependencies": {
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
+ "prettier": "^3.5.3",
+ "prettier-plugin-organize-imports": "^4.1.0",
+ "prettier-plugin-packagejson": "^2.5.10"
+ }
+ },
+ "packages/esbuild-plugin-live-reload/node_modules/@trivago/prettier-plugin-sort-imports": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz",
+ "integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/generator": "^7.26.5",
+ "@babel/parser": "^7.26.7",
+ "@babel/traverse": "^7.26.7",
+ "@babel/types": "^7.26.7",
+ "javascript-natural-sort": "^0.7.1",
+ "lodash": "^4.17.21"
+ },
+ "engines": {
+ "node": ">18.12"
+ },
+ "peerDependencies": {
+ "@vue/compiler-sfc": "3.x",
+ "prettier": "2.x - 3.x",
+ "prettier-plugin-svelte": "3.x",
+ "svelte": "4.x || 5.x"
+ },
+ "peerDependenciesMeta": {
+ "@vue/compiler-sfc": {
+ "optional": true
+ },
+ "prettier-plugin-svelte": {
+ "optional": true
+ },
+ "svelte": {
+ "optional": true
+ }
+ }
+ },
+ "packages/esbuild-plugin-live-reload/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "packages/esbuild-plugin-live-reload/node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"packages/sfe": {
"name": "@goauthentik/web-sfe",
"version": "0.0.0",
@@ -24674,10 +24838,148 @@
"@swc/core-win32-x64-msvc": "^1.6.13"
}
},
+ "packages/sfe/node_modules/@babel/generator": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
+ "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.17.0",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "packages/sfe/node_modules/@babel/traverse": {
+ "version": "7.23.2",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
+ "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/generator": "^7.23.0",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.23.0",
+ "@babel/types": "^7.23.0",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "packages/sfe/node_modules/@babel/traverse/node_modules/@babel/generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
+ "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "packages/sfe/node_modules/@babel/traverse/node_modules/@babel/types": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "packages/sfe/node_modules/@babel/traverse/node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"packages/sfe/node_modules/@goauthentik/api": {
"version": "2024.6.0-1720200294",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.6.0-1720200294.tgz",
"integrity": "sha512-qGpI+0BpsHWlO8waj89q+6SWjVVuRtYqdmpSIrKFsZt9GLNXCvIAvgS5JI1Sq2z1uWK/8kLNZKDocI/XagqMPQ=="
+ },
+ "packages/sfe/node_modules/@trivago/prettier-plugin-sort-imports": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz",
+ "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/generator": "7.17.7",
+ "@babel/parser": "^7.20.5",
+ "@babel/traverse": "7.23.2",
+ "@babel/types": "7.17.0",
+ "javascript-natural-sort": "0.7.1",
+ "lodash": "^4.17.21"
+ },
+ "peerDependencies": {
+ "@vue/compiler-sfc": "3.x",
+ "prettier": "2.x - 3.x"
+ },
+ "peerDependenciesMeta": {
+ "@vue/compiler-sfc": {
+ "optional": true
+ }
+ }
+ },
+ "packages/sfe/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "packages/sfe/node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "packages/sfe/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
}
}
}
diff --git a/web/package.json b/web/package.json
index 6750441db2..4a9e24dc97 100644
--- a/web/package.json
+++ b/web/package.json
@@ -61,6 +61,9 @@
},
"devDependencies": {
"@eslint/js": "^9.11.1",
+ "@goauthentik/esbuild-plugin-live-reload": "^1.0.4",
+ "@goauthentik/prettier-config": "^1.0.4",
+ "@goauthentik/tsconfig": "^1.0.4",
"@hcaptcha/types": "^1.0.4",
"@lit/localize-tools": "^0.8.0",
"@rollup/plugin-replace": "^6.0.1",
@@ -72,7 +75,7 @@
"@storybook/manager-api": "^8.3.4",
"@storybook/web-components": "^8.3.4",
"@storybook/web-components-vite": "^8.3.4",
- "@trivago/prettier-plugin-sort-imports": "^4.3.0",
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/chart.js": "^2.9.41",
"@types/codemirror": "^5.60.15",
"@types/dompurify": "^3.0.5",
@@ -95,7 +98,6 @@
"eslint": "^9.11.1",
"eslint-plugin-lit": "^1.15.0",
"eslint-plugin-wc": "^2.1.1",
- "find-free-ports": "^3.1.1",
"github-slugger": "^2.0.0",
"glob": "^11.0.0",
"globals": "^15.10.0",
@@ -136,6 +138,7 @@
"axios": "^1.8.4"
}
},
+ "prettier": "@goauthentik/prettier-config",
"private": true,
"scripts": {
"build": "wireit",
@@ -271,7 +274,7 @@
"command": "tsc --noEmit -p ./tests"
},
"lint:types": {
- "command": "tsc --noEmit -p .",
+ "command": "NODE_OPTIONS=\"--max-old-space-size=3000\" tsc -b .",
"dependencies": [
"build-locales",
"lint:types:tests"
diff --git a/web/src/common/client.ts b/web/packages/esbuild-plugin-live-reload/client/ESBuildObserver.js
similarity index 68%
rename from web/src/common/client.ts
rename to web/packages/esbuild-plugin-live-reload/client/ESBuildObserver.js
index 1869392740..b4f90b54a8 100644
--- a/web/src/common/client.ts
+++ b/web/packages/esbuild-plugin-live-reload/client/ESBuildObserver.js
@@ -1,13 +1,18 @@
+///
+
/**
- * @file
- * Client-side observer for ESBuild events.
+ * @file Client-side observer for ESBuild events.
+ *
+ * @import { Message as ESBuildMessage } from "esbuild";
*/
-import type { Message as ESBuildMessage } from "esbuild";
const logPrefix = "👷 [ESBuild]";
const log = console.debug.bind(console, logPrefix);
-type BuildEventListener = (event: MessageEvent) => void;
+/**
+ * @template {unknown} [Data=unknown]
+ * @typedef {(event: MessageEvent) => void} BuildEventListener
+ */
/**
* A client-side watcher for ESBuild.
@@ -16,14 +21,13 @@ type BuildEventListener = (event: MessageEvent) => void;
* ESBuild may tree-shake it out of production builds.
*
* ```ts
- * if (process.env.NODE_ENV === "development" && process.env.WATCHER_URL) {
- * const { ESBuildObserver } = await import("@goauthentik/common/client");
- *
- * new ESBuildObserver(process.env.WATCHER_URL);
+ * if (process.env.NODE_ENV === "development") {
+ * await import("@goauthentik/esbuild-plugin-live-reload/client")
+ * .catch(() => console.warn("Failed to import watcher"))
* }
* ```
-}
-
+ *
+ * @implements {Disposable}
*/
export class ESBuildObserver extends EventSource {
/**
@@ -58,15 +62,19 @@ export class ESBuildObserver extends EventSource {
/**
* The interval for the keep-alive check.
+ * @type {ReturnType | undefined}
*/
- #keepAliveInterval: ReturnType | undefined;
+ #keepAliveInterval;
#trackActivity = () => {
this.lastUpdatedAt = Date.now();
this.alive = true;
};
- #startListener: BuildEventListener = () => {
+ /**
+ * @type {BuildEventListener}
+ */
+ #startListener = () => {
this.#trackActivity();
log("⏰ Build started...");
};
@@ -82,13 +90,18 @@ export class ESBuildObserver extends EventSource {
}
};
- #errorListener: BuildEventListener = (event) => {
+ /**
+ * @type {BuildEventListener}
+ */
+ #errorListener = (event) => {
this.#trackActivity();
- // eslint-disable-next-line no-console
console.group(logPrefix, "⛔️⛔️⛔️ Build error...");
- const esbuildErrorMessages: ESBuildMessage[] = JSON.parse(event.data);
+ /**
+ * @type {ESBuildMessage[]}
+ */
+ const esbuildErrorMessages = JSON.parse(event.data);
for (const error of esbuildErrorMessages) {
console.warn(error.text);
@@ -101,11 +114,13 @@ export class ESBuildObserver extends EventSource {
}
}
- // eslint-disable-next-line no-console
console.groupEnd();
};
- #endListener: BuildEventListener = () => {
+ /**
+ * @type {BuildEventListener}
+ */
+ #endListener = () => {
cancelAnimationFrame(this.#reloadFrameID);
this.#trackActivity();
@@ -126,12 +141,36 @@ export class ESBuildObserver extends EventSource {
});
};
- #keepAliveListener: BuildEventListener = () => {
+ /**
+ * @type {BuildEventListener}
+ */
+ #keepAliveListener = () => {
this.#trackActivity();
log("🏓 Keep-alive");
};
- constructor(url: string | URL) {
+ /**
+ * Initialize the ESBuild observer.
+ * This should be called once in your application.
+ *
+ * @param {string | URL} [url]
+ * @returns {ESBuildObserver}
+ */
+ static initialize = (url) => {
+ const esbuildObserver = new ESBuildObserver(url);
+
+ return esbuildObserver;
+ };
+
+ /**
+ *
+ * @param {string | URL} [url]
+ */
+ constructor(url) {
+ if (!url) {
+ throw new TypeError("ESBuildObserver: Cannot construct without a URL");
+ }
+
super(url);
this.addEventListener("esbuild:start", this.#startListener);
@@ -167,4 +206,14 @@ export class ESBuildObserver extends EventSource {
log("👋 Waiting for build to start...");
}, 15_000);
}
+
+ [Symbol.dispose]() {
+ return this.close();
+ }
+
+ dispose() {
+ return this[Symbol.dispose]();
+ }
}
+
+export default ESBuildObserver;
diff --git a/web/packages/esbuild-plugin-live-reload/client/index.js b/web/packages/esbuild-plugin-live-reload/client/index.js
new file mode 100644
index 0000000000..41b79aeb8d
--- /dev/null
+++ b/web/packages/esbuild-plugin-live-reload/client/index.js
@@ -0,0 +1,13 @@
+///
+/**
+ * @file Entry point for the ESBuild client-side observer.
+ */
+import { ESBuildObserver } from "./ESBuildObserver.js";
+
+if (import.meta.env?.ESBUILD_WATCHER_URL) {
+ const buildObserver = new ESBuildObserver(import.meta.env.ESBUILD_WATCHER_URL);
+
+ window.addEventListener("beforeunload", () => {
+ buildObserver.dispose();
+ });
+}
diff --git a/web/packages/esbuild-plugin-live-reload/client/types.d.ts b/web/packages/esbuild-plugin-live-reload/client/types.d.ts
new file mode 100644
index 0000000000..c446181716
--- /dev/null
+++ b/web/packages/esbuild-plugin-live-reload/client/types.d.ts
@@ -0,0 +1,18 @@
+/**
+ * @file Import meta environment variables available via ESBuild.
+ */
+
+export {};
+declare global {
+ interface ImportMeta {
+ readonly env: {
+ /**
+ * The injected watcher URL for ESBuild.
+ * This is used for live reloading in development mode.
+ *
+ * @format url
+ */
+ ESBUILD_WATCHER_URL: string;
+ };
+ }
+}
diff --git a/web/packages/esbuild-plugin-live-reload/index.js b/web/packages/esbuild-plugin-live-reload/index.js
new file mode 100644
index 0000000000..9aef9c75db
--- /dev/null
+++ b/web/packages/esbuild-plugin-live-reload/index.js
@@ -0,0 +1,2 @@
+export * from "./client/index.js";
+export * from "./plugin/index.js";
diff --git a/web/packages/esbuild-plugin-live-reload/package.json b/web/packages/esbuild-plugin-live-reload/package.json
new file mode 100644
index 0000000000..3ddd26f0a4
--- /dev/null
+++ b/web/packages/esbuild-plugin-live-reload/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "@goauthentik/esbuild-plugin-live-reload",
+ "description": "ESBuild plugin to watch for file changes and trigger client-side reloads.",
+ "version": "1.0.4",
+ "dependencies": {
+ "find-free-ports": "^3.1.1"
+ },
+ "devDependencies": {
+ "@goauthentik/prettier-config": "^1.0.4",
+ "@goauthentik/tsconfig": "^1.0.4",
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
+ "@types/node": "^22.14.1",
+ "esbuild": "^0.25.0",
+ "prettier": "^3.3.3",
+ "typescript": "^5.6.2"
+ },
+ "engines": {
+ "node": ">=20.11"
+ },
+ "exports": {
+ "./package.json": "./package.json",
+ ".": {
+ "types": "./out/index.d.ts",
+ "import": "./index.js"
+ },
+ "./client": {
+ "types": "./out/client/index.d.ts",
+ "import": "./client/index.js"
+ },
+ "./plugin": {
+ "types": "./out/plugin/index.d.ts",
+ "import": "./plugin/index.js"
+ }
+ },
+ "files": [
+ "./index.js",
+ "client/**/*",
+ "plugin/**/*",
+ "out/**/*"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "peerDependencies": {
+ "esbuild": "^0.25.0"
+ },
+ "prettier": "@goauthentik/prettier-config",
+ "private": true,
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "types": "./out/index.d.ts"
+}
diff --git a/web/packages/esbuild-plugin-live-reload/plugin/index.js b/web/packages/esbuild-plugin-live-reload/plugin/index.js
new file mode 100644
index 0000000000..d69c75645e
--- /dev/null
+++ b/web/packages/esbuild-plugin-live-reload/plugin/index.js
@@ -0,0 +1,243 @@
+/**
+ * @file Live reload plugin for ESBuild.
+ *
+ * @import { ListenOptions } from "node:net";
+ * @import {Server as HTTPServer} from "node:http";
+ * @import {Server as HTTPSServer} from "node:https";
+ */
+import { findFreePorts } from "find-free-ports";
+import * as http from "node:http";
+import * as path from "node:path";
+
+/**
+ * Serializes a custom event to a text stream.
+ * @param {Event} event
+ * @returns {string}
+ */
+export function serializeCustomEventToStream(event) {
+ // @ts-expect-error - TS doesn't know about the detail property
+ const data = event.detail ?? {};
+
+ const eventContent = [`event: ${event.type}`, `data: ${JSON.stringify(data)}`];
+
+ return eventContent.join("\n") + "\n\n";
+}
+
+const MIN_PORT = 1025;
+const MAX_PORT = 65535;
+
+/**
+ * Find a random port that is not in use, sufficiently far from the default port.
+ * @returns {Promise}
+ */
+async function findDisparatePort() {
+ const startPort = Math.floor(Math.random() * (MAX_PORT - MIN_PORT + 1)) + MIN_PORT;
+
+ const wathcherPorts = await findFreePorts(1, {
+ startPort,
+ });
+
+ const [port] = wathcherPorts;
+
+ if (!port) {
+ throw new Error("No free ports available");
+ }
+
+ return port;
+}
+
+/**
+ * Event server initialization options.
+ *
+ * @typedef {Object} EventServerInit
+ *
+ * @property {string} pathname
+ * @property {EventTarget} dispatcher
+ * @property {string} [logPrefix]
+ */
+
+/**
+ * @typedef {(req: http.IncomingMessage, res: http.ServerResponse) => void} RequestHandler
+ */
+
+/**
+ * Create an event request handler.
+ * @param {EventServerInit} options
+ * @returns {RequestHandler}
+ * @category ESBuild
+ */
+export function createRequestHandler({ pathname, dispatcher, logPrefix = "Build Observer" }) {
+ // eslint-disable-next-line no-console
+ const log = console.log.bind(console, `[${logPrefix}]`);
+
+ /**
+ * @type {RequestHandler}
+ */
+ const requestHandler = (req, res) => {
+ res.setHeader("Access-Control-Allow-Origin", "*");
+ res.setHeader("Access-Control-Allow-Methods", "GET");
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
+
+ if (req.url !== pathname) {
+ log(`🚫 Invalid request to ${req.url}`);
+ res.writeHead(404);
+ res.end();
+ return;
+ }
+
+ log("🔌 Client connected");
+
+ res.writeHead(200, {
+ "Content-Type": "text/event-stream",
+ "Cache-Control": "no-cache",
+ "Connection": "keep-alive",
+ });
+
+ /**
+ * @param {Event} event
+ */
+ const listener = (event) => {
+ const body = serializeCustomEventToStream(event);
+
+ res.write(body);
+ };
+
+ dispatcher.addEventListener("esbuild:start", listener);
+ dispatcher.addEventListener("esbuild:error", listener);
+ dispatcher.addEventListener("esbuild:end", listener);
+
+ req.on("close", () => {
+ log("🔌 Client disconnected");
+
+ clearInterval(keepAliveInterval);
+
+ dispatcher.removeEventListener("esbuild:start", listener);
+ dispatcher.removeEventListener("esbuild:error", listener);
+ dispatcher.removeEventListener("esbuild:end", listener);
+ });
+
+ const keepAliveInterval = setInterval(() => {
+ console.timeStamp("🏓 Keep-alive");
+
+ res.write("event: keep-alive\n\n");
+ res.write(serializeCustomEventToStream(new CustomEvent("esbuild:keep-alive")));
+ }, 15_000);
+ };
+
+ return requestHandler;
+}
+
+/**
+ * Options for the build observer plugin.
+ *
+ * @typedef {object} BuildObserverOptions
+ *
+ * @property {HTTPServer | HTTPSServer} [server]
+ * @property {ListenOptions} [listenOptions]
+ * @property {string | URL} [publicURL]
+ * @property {string} [logPrefix]
+ * @property {string} [relativeRoot]
+ */
+
+/**
+ * Creates a plugin that listens for build events and sends them to a server-sent event stream.
+ *
+ * @param {BuildObserverOptions} [options]
+ * @returns {import('esbuild').Plugin}
+ */
+export function liveReloadPlugin(options = {}) {
+ return {
+ name: "build-watcher",
+ setup: async (build) => {
+ const logPrefix = options.logPrefix || "Build Observer";
+
+ const timerLabel = `[${logPrefix}] 🏁`;
+ const relativeRoot = options.relativeRoot || process.cwd();
+
+ const dispatcher = new EventTarget();
+
+ /**
+ * @type {URL}
+ */
+ let publicURL;
+
+ if (!options.publicURL) {
+ const port = await findDisparatePort();
+
+ publicURL = new URL(`http://localhost:${port}/events`);
+ } else {
+ publicURL =
+ typeof options.publicURL === "string"
+ ? new URL(options.publicURL)
+ : options.publicURL;
+ }
+
+ build.initialOptions.define = {
+ ...build.initialOptions.define,
+ "import.meta.env.ESBUILD_WATCHER_URL": JSON.stringify(publicURL.href),
+ };
+
+ const requestHandler = createRequestHandler({
+ pathname: publicURL.pathname,
+ dispatcher,
+ logPrefix,
+ });
+
+ const server = options.server || http.createServer(requestHandler);
+
+ const listenOptions = options.listenOptions || {
+ port: parseInt(publicURL.port, 10),
+ host: publicURL.hostname,
+ };
+
+ server.listen(listenOptions, () => {
+ // eslint-disable-next-line no-console
+ console.log(`[${logPrefix}] Listening`);
+ });
+
+ build.onDispose(() => {
+ server?.close();
+ });
+
+ build.onStart(() => {
+ console.time(timerLabel);
+
+ dispatcher.dispatchEvent(
+ new CustomEvent("esbuild:start", {
+ detail: new Date().toISOString(),
+ }),
+ );
+ });
+
+ build.onEnd((buildResult) => {
+ console.timeEnd(timerLabel);
+
+ if (!buildResult.errors.length) {
+ dispatcher.dispatchEvent(
+ new CustomEvent("esbuild:end", {
+ detail: new Date().toISOString(),
+ }),
+ );
+
+ return;
+ }
+
+ console.warn(`Build ended with ${buildResult.errors.length} errors`);
+
+ dispatcher.dispatchEvent(
+ new CustomEvent("esbuild:error", {
+ detail: buildResult.errors.map((error) => ({
+ ...error,
+ location: error.location
+ ? {
+ ...error.location,
+ file: path.resolve(relativeRoot, error.location.file),
+ }
+ : null,
+ })),
+ }),
+ );
+ });
+ },
+ };
+}
diff --git a/web/packages/esbuild-plugin-live-reload/tsconfig.json b/web/packages/esbuild-plugin-live-reload/tsconfig.json
new file mode 100644
index 0000000000..a3d5376967
--- /dev/null
+++ b/web/packages/esbuild-plugin-live-reload/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "@goauthentik/tsconfig",
+ "compilerOptions": {
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "checkJs": true,
+ "emitDeclarationOnly": true
+ }
+}
diff --git a/web/scripts/build-web.mjs b/web/scripts/build-web.mjs
index b75e46d30f..07a14e246a 100644
--- a/web/scripts/build-web.mjs
+++ b/web/scripts/build-web.mjs
@@ -1,8 +1,13 @@
+/**
+ * @file ESBuild script for building the authentik web UI.
+ *
+ * @import { BuildOptions } from "esbuild";
+ */
+import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload/plugin";
import { execFileSync } from "child_process";
import { deepmerge } from "deepmerge-ts";
import esbuild from "esbuild";
import { polyfillNode } from "esbuild-plugin-polyfill-node";
-import findFreePorts from "find-free-ports";
import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs";
import { globSync } from "glob";
import * as path from "path";
@@ -11,7 +16,6 @@ import process from "process";
import { fileURLToPath } from "url";
import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs";
-import { buildObserverPlugin } from "./esbuild/build-observer-plugin.mjs";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
let authentikProjectRoot = path.join(__dirname, "..", "..");
@@ -120,7 +124,7 @@ const BASE_ESBUILD_OPTIONS = {
splitting: true,
treeShaking: true,
external: ["*.woff", "*.woff2"],
- tsconfig: "./tsconfig.json",
+ tsconfig: path.resolve(__dirname, "..", "tsconfig.build.json"),
loader: {
".css": "text",
},
@@ -220,26 +224,17 @@ function doHelp() {
async function doWatch() {
console.log("Watching all entry points...");
- const wathcherPorts = await findFreePorts(entryPoints.length);
-
const buildContexts = await Promise.all(
- entryPoints.map((entryPoint, i) => {
- const port = wathcherPorts[i];
- const serverURL = new URL(`http://localhost:${port}/events`);
-
+ entryPoints.map((entryPoint) => {
return esbuild.context(
createEntryPointOptions(entryPoint, {
+ define: definitions,
plugins: [
- buildObserverPlugin({
- serverURL,
- logPrefix: entryPoint[1],
+ liveReloadPlugin({
+ logPrefix: `Build Observer (${entryPoint[1]})`,
relativeRoot: path.join(__dirname, ".."),
}),
],
- define: {
- ...definitions,
- "process.env.WATCHER_URL": JSON.stringify(serverURL.toString()),
- },
}),
);
}),
diff --git a/web/scripts/esbuild/build-observer-plugin.mjs b/web/scripts/esbuild/build-observer-plugin.mjs
deleted file mode 100644
index c7a0c7e317..0000000000
--- a/web/scripts/esbuild/build-observer-plugin.mjs
+++ /dev/null
@@ -1,141 +0,0 @@
-import * as http from "http";
-import path from "path";
-
-/**
- * Serializes a custom event to a text stream.
- * a
- * @param {Event} event
- * @returns {string}
- */
-export function serializeCustomEventToStream(event) {
- // @ts-expect-error - TS doesn't know about the detail property
- const data = event.detail ?? {};
-
- const eventContent = [`event: ${event.type}`, `data: ${JSON.stringify(data)}`];
-
- return eventContent.join("\n") + "\n\n";
-}
-
-/**
- * Options for the build observer plugin.
- *
- * @typedef {Object} BuildObserverOptions
- *
- * @property {URL} serverURL
- * @property {string} logPrefix
- * @property {string} relativeRoot
- */
-
-/**
- * Creates a plugin that listens for build events and sends them to a server-sent event stream.
- *
- * @param {BuildObserverOptions} options
- * @returns {import('esbuild').Plugin}
- */
-export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) {
- const timerLabel = `[${logPrefix}] Build`;
- const endpoint = serverURL.pathname;
- const dispatcher = new EventTarget();
-
- const eventServer = http.createServer((req, res) => {
- res.setHeader("Access-Control-Allow-Origin", "*");
- res.setHeader("Access-Control-Allow-Methods", "GET");
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
-
- if (req.url !== endpoint) {
- console.log(`🚫 Invalid request to ${req.url}`);
- res.writeHead(404);
- res.end();
- return;
- }
-
- console.log("🔌 Client connected");
-
- res.writeHead(200, {
- "Content-Type": "text/event-stream",
- "Cache-Control": "no-cache",
- "Connection": "keep-alive",
- });
-
- /**
- * @param {Event} event
- */
- const listener = (event) => {
- const body = serializeCustomEventToStream(event);
-
- res.write(body);
- };
-
- dispatcher.addEventListener("esbuild:start", listener);
- dispatcher.addEventListener("esbuild:error", listener);
- dispatcher.addEventListener("esbuild:end", listener);
-
- req.on("close", () => {
- console.log("🔌 Client disconnected");
-
- clearInterval(keepAliveInterval);
-
- dispatcher.removeEventListener("esbuild:start", listener);
- dispatcher.removeEventListener("esbuild:error", listener);
- dispatcher.removeEventListener("esbuild:end", listener);
- });
-
- const keepAliveInterval = setInterval(() => {
- console.timeStamp("🏓 Keep-alive");
-
- res.write("event: keep-alive\n\n");
- res.write(serializeCustomEventToStream(new CustomEvent("esbuild:keep-alive")));
- }, 15_000);
- });
-
- return {
- name: "build-watcher",
- setup: (build) => {
- eventServer.listen(parseInt(serverURL.port, 10), serverURL.hostname);
-
- build.onDispose(() => {
- eventServer.close();
- });
-
- build.onStart(() => {
- console.time(timerLabel);
-
- dispatcher.dispatchEvent(
- new CustomEvent("esbuild:start", {
- detail: new Date().toISOString(),
- }),
- );
- });
-
- build.onEnd((buildResult) => {
- console.timeEnd(timerLabel);
-
- if (!buildResult.errors.length) {
- dispatcher.dispatchEvent(
- new CustomEvent("esbuild:end", {
- detail: new Date().toISOString(),
- }),
- );
-
- return;
- }
-
- console.warn(`Build ended with ${buildResult.errors.length} errors`);
-
- dispatcher.dispatchEvent(
- new CustomEvent("esbuild:error", {
- detail: buildResult.errors.map((error) => ({
- ...error,
- location: error.location
- ? {
- ...error.location,
- file: path.resolve(relativeRoot, error.location.file),
- }
- : null,
- })),
- }),
- );
- });
- },
- };
-}
diff --git a/web/src/admin/AdminInterface/AdminInterface.ts b/web/src/admin/AdminInterface/AdminInterface.ts
index 04c633c81a..dda28c5a37 100644
--- a/web/src/admin/AdminInterface/AdminInterface.ts
+++ b/web/src/admin/AdminInterface/AdminInterface.ts
@@ -34,6 +34,10 @@ import { SessionUser, UiThemeEnum } from "@goauthentik/api";
import "./AdminSidebar";
+if (process.env.NODE_ENV === "development") {
+ await import("@goauthentik/esbuild-plugin-live-reload/client");
+}
+
@customElement("ak-interface-admin")
export class AdminInterface extends AuthenticatedInterface {
@property({ type: Boolean })
@@ -119,16 +123,6 @@ export class AdminInterface extends AuthenticatedInterface {
}
}
- async connectedCallback(): Promise {
- super.connectedCallback();
-
- if (process.env.NODE_ENV === "development" && process.env.WATCHER_URL) {
- const { ESBuildObserver } = await import("@goauthentik/common/client");
-
- new ESBuildObserver(process.env.WATCHER_URL);
- }
- }
-
render(): TemplateResult {
const sidebarClasses = {
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
diff --git a/web/src/admin/applications/wizard/steps/SubmitStepOverviewRenderers.ts b/web/src/admin/applications/wizard/steps/SubmitStepOverviewRenderers.ts
index 934d92c9ac..f519034b7a 100644
--- a/web/src/admin/applications/wizard/steps/SubmitStepOverviewRenderers.ts
+++ b/web/src/admin/applications/wizard/steps/SubmitStepOverviewRenderers.ts
@@ -52,7 +52,6 @@ function renderRadiusOverview(rawProvider: OneOfProvider) {
}
function renderRACOverview(rawProvider: OneOfProvider) {
- // @ts-expect-error TS6133
const _provider = rawProvider as RACProvider;
}
diff --git a/web/src/common/purify.ts b/web/src/common/purify.ts
index 38f13f635b..5da9810c8a 100644
--- a/web/src/common/purify.ts
+++ b/web/src/common/purify.ts
@@ -1,3 +1,4 @@
+import type { Config as DOMPurifyConfig } from "dompurify";
import DOMPurify from "dompurify";
import { render } from "@lit-labs/ssr";
@@ -6,9 +7,9 @@ import { TemplateResult, html } from "lit";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { until } from "lit/directives/until.js";
-export const DOM_PURIFY_STRICT: DOMPurify.Config = {
+export const DOM_PURIFY_STRICT = {
ALLOWED_TAGS: ["#text"],
-};
+} as const satisfies DOMPurifyConfig;
export async function renderStatic(input: TemplateResult): Promise {
return await collectResult(render(input));
diff --git a/web/src/elements/Interface/versionProvider.ts b/web/src/elements/Interface/versionProvider.ts
index b8dc5dade9..913b6dd699 100644
--- a/web/src/elements/Interface/versionProvider.ts
+++ b/web/src/elements/Interface/versionProvider.ts
@@ -1,7 +1,7 @@
import { authentikVersionContext } from "@goauthentik/elements/AuthentikContexts";
import { consume } from "@lit/context";
-import { Constructor } from "@lit/reactive-element/decorators/base";
+import { Constructor } from "@lit/reactive-element/decorators/base.js";
import type { LitElement } from "lit";
import type { Version } from "@goauthentik/api";
diff --git a/web/src/elements/ak-checkbox-group/ak-checkbox-group.ts b/web/src/elements/ak-checkbox-group/ak-checkbox-group.ts
index 6b4af8fb5f..ca8712fc73 100644
--- a/web/src/elements/ak-checkbox-group/ak-checkbox-group.ts
+++ b/web/src/elements/ak-checkbox-group/ak-checkbox-group.ts
@@ -2,7 +2,7 @@ import { AkControlElement } from "@goauthentik/elements/AkControlElement";
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
import { msg } from "@lit/localize";
-import { PropertyValues } from "@lit/reactive-element/reactive-element";
+import { PropertyValues } from "@lit/reactive-element";
import { TemplateResult, css, html } from "lit";
import { customElement, property, queryAll, state } from "lit/decorators.js";
import { map } from "lit/directives/map.js";
diff --git a/web/src/flow/FlowInterface.ts b/web/src/flow/FlowInterface.ts
index 994032501e..9f5cfff49c 100644
--- a/web/src/flow/FlowInterface.ts
+++ b/web/src/flow/FlowInterface.ts
@@ -14,8 +14,6 @@ import "@goauthentik/flow/stages/password/PasswordStage";
// end of stage import
-if (process.env.NODE_ENV === "development" && process.env.WATCHER_URL) {
- const { ESBuildObserver } = await import("@goauthentik/common/client");
-
- new ESBuildObserver(process.env.WATCHER_URL);
+if (process.env.NODE_ENV === "development") {
+ await import("@goauthentik/esbuild-plugin-live-reload/client");
}
diff --git a/web/src/user/UserInterface.ts b/web/src/user/UserInterface.ts
index d418a6ace9..c710a0cdcb 100644
--- a/web/src/user/UserInterface.ts
+++ b/web/src/user/UserInterface.ts
@@ -43,6 +43,10 @@ import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import { CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api";
+if (process.env.NODE_ENV === "development") {
+ await import("@goauthentik/esbuild-plugin-live-reload/client");
+}
+
const customStyles = css`
.pf-c-page__main,
.pf-c-drawer__content,
@@ -291,12 +295,6 @@ export class UserInterface extends AuthenticatedInterface {
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, this.toggleNotificationDrawer);
window.addEventListener(EVENT_API_DRAWER_TOGGLE, this.toggleApiDrawer);
window.addEventListener(EVENT_WS_MESSAGE, this.fetchConfigurationDetails);
-
- if (process.env.NODE_ENV === "development" && process.env.WATCHER_URL) {
- const { ESBuildObserver } = await import("@goauthentik/common/client");
-
- new ESBuildObserver(process.env.WATCHER_URL);
- }
}
disconnectedCallback() {
diff --git a/web/tsconfig.base.json b/web/tsconfig.base.json
deleted file mode 100644
index 31d05ab479..0000000000
--- a/web/tsconfig.base.json
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- "compilerOptions": {
- "strict": true,
- "baseUrl": ".",
- "esModuleInterop": true,
- "paths": {
- "@goauthentik/docs/*": ["../website/docs/*"]
- },
- "types": [
- "node",
- "@wdio/mocha-framework",
- "@wdio/types",
- "expect-webdriverio",
- "grecaptcha"
- ],
- "jsx": "react-jsx",
- "skipLibCheck": true,
- "forceConsistentCasingInFileNames": true,
- "experimentalDecorators": true,
- "sourceMap": true,
- "target": "esnext",
- "module": "esnext",
- "moduleResolution": "node",
- "lib": [
- "ES5",
- "ES2015",
- "ES2016",
- "ES2017",
- "ES2018",
- "ES2019",
- "ES2020",
- "ESNext",
- "DOM",
- "DOM.Iterable",
- "WebWorker"
- ],
- "noUnusedLocals": true,
- "noImplicitReturns": true,
- "noFallthroughCasesInSwitch": true,
- "strictBindCallApply": true,
- "strictFunctionTypes": true,
- "strictNullChecks": true,
- "allowUnreachableCode": false,
- "allowUnusedLabels": false,
- "useDefineForClassFields": false,
- "useUnknownInCatchVariables": true,
- "alwaysStrict": true,
- "noImplicitAny": true,
- "plugins": [
- {
- "name": "ts-lit-plugin",
- "strict": true,
- "rules": {
- "no-unknown-tag-name": "off",
- "no-missing-import": "off",
- "no-incompatible-type-binding": "off",
- "no-unknown-property": "off",
- "no-unknown-attribute": "off"
- }
- },
- {
- "name": "@genesiscommunitysuccess/custom-elements-lsp",
- "designSystemPrefix": "ak-",
- "parser": {
- "timeout": 2000
- }
- }
- ]
- },
- "exclude": ["src/**/*.test.ts", "./tests"]
-}
diff --git a/web/tsconfig.build.json b/web/tsconfig.build.json
new file mode 100644
index 0000000000..26422e5959
--- /dev/null
+++ b/web/tsconfig.build.json
@@ -0,0 +1,6 @@
+// @file TSConfig used by the web package during build.
+
+{
+ "extends": "./tsconfig.json",
+ "exclude": ["src/**/*.test.ts", "./tests"]
+}
diff --git a/web/tsconfig.json b/web/tsconfig.json
index 333347a361..3ec01581d7 100644
--- a/web/tsconfig.json
+++ b/web/tsconfig.json
@@ -1,7 +1,19 @@
+// @file TSConfig used by the web package during development.
{
- "extends": "./tsconfig.base.json",
+ "extends": "@goauthentik/tsconfig",
"compilerOptions": {
- "types": ["@wdio/types", "grecaptcha", "node", "@wdio/mocha-framework", "expect-webdriverio"],
+ "allowSyntheticDefaultImports": true,
+ "emitDeclarationOnly": true,
+ "experimentalDecorators": true,
+ // See https://lit.dev/docs/components/properties/
+ "useDefineForClassFields": false,
+ "target": "esnext",
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "baseUrl": ".",
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ // TODO: We should enable this when when we're ready to enforce it.
+ "noUncheckedIndexedAccess": false,
"paths": {
"@goauthentik/admin/*": ["./src/admin/*"],
"@goauthentik/common/*": ["./src/common/*"],
@@ -13,6 +25,41 @@
"@goauthentik/polyfill/*": ["./src/polyfill/*"],
"@goauthentik/standalone/*": ["./src/standalone/*"],
"@goauthentik/user/*": ["./src/user/*"]
- }
+ },
+ "plugins": [
+ {
+ "name": "ts-lit-plugin",
+ "strict": true,
+ "rules": {
+ "no-unknown-tag-name": "off",
+ "no-missing-import": "off",
+ "no-incompatible-type-binding": "off",
+ "no-unknown-property": "off",
+ "no-unknown-attribute": "off"
+ }
+ },
+ {
+ "name": "@genesiscommunitysuccess/custom-elements-lsp",
+ "designSystemPrefix": "ak-",
+ "parser": {
+ "timeout": 2000
+ }
+ }
+ ]
},
+ "exclude": [
+ // ---
+ "./out/**/*",
+ "./dist/**/*",
+ "src/**/*.test.ts",
+ "./tests",
+
+ // TODO: Remove after monorepo cleanup.
+ "src/**/*.comp.ts"
+ ],
+ "references": [
+ {
+ "path": "./packages/esbuild-plugin-live-reload"
+ }
+ ],
}
diff --git a/web/tsconfig.test.json b/web/tsconfig.test.json
index f93c6adf05..8ded1a98d4 100644
--- a/web/tsconfig.test.json
+++ b/web/tsconfig.test.json
@@ -1,3 +1,4 @@
+// @file TSConfig used during tests.
{
"compilerOptions": {
"baseUrl": ".",
diff --git a/web/types/global.d.ts b/web/types/global.d.ts
new file mode 100644
index 0000000000..5edb41ecd5
--- /dev/null
+++ b/web/types/global.d.ts
@@ -0,0 +1,26 @@
+/**
+ * @file Environment variables available via ESBuild.
+ */
+
+declare module "process" {
+ global {
+ namespace NodeJS {
+ interface ProcessEnv {
+ NODE_ENV: "production" | "development";
+ /**
+ *
+ * @todo Determine where this is used and if it is needed,
+ * give it a better name.
+ * @deprecated
+ */
+ CWD: string;
+ /**
+ * @todo Determine where this is used and if it is needed,
+ * give it a better name.
+ * @deprecated
+ */
+ AK_API_BASE_PATH: string;
+ }
+ }
+ }
+}
diff --git a/web/wdio.conf.ts b/web/wdio.conf.ts
index 8767a207f7..8b41fdcd79 100644
--- a/web/wdio.conf.ts
+++ b/web/wdio.conf.ts
@@ -336,6 +336,7 @@ export const config: Options.Testrunner = {
{ error: _error, result: _result, duration: _duration, passed: _passed, retries: _retries },
) {
if (lemmeSee) {
+ // @ts-expect-error TODO
await browser.pause(500);
}
},