@authup/server-core 1.0.0-beta.58 → 1.0.0-beta.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/http/controllers/index.mjs +2 -1
- package/dist/adapters/http/controllers/workflows/account/index.d.ts +2 -0
- package/dist/adapters/http/controllers/workflows/account/index.d.ts.map +1 -0
- package/dist/adapters/http/controllers/workflows/account/index.mjs +5 -0
- package/dist/adapters/http/controllers/workflows/account/module.d.ts +24 -0
- package/dist/adapters/http/controllers/workflows/account/module.d.ts.map +1 -0
- package/dist/adapters/http/controllers/workflows/account/module.mjs +63 -0
- package/dist/adapters/http/controllers/workflows/account/module.mjs.map +1 -0
- package/dist/adapters/http/controllers/workflows/activate/module.mjs +1 -1
- package/dist/adapters/http/controllers/workflows/authorize/module.mjs +2 -2
- package/dist/adapters/http/controllers/workflows/authorize/module.mjs.map +1 -1
- package/dist/adapters/http/controllers/workflows/index.d.ts +1 -0
- package/dist/adapters/http/controllers/workflows/index.d.ts.map +1 -1
- package/dist/adapters/http/controllers/workflows/index.mjs +3 -1
- package/dist/adapters/http/controllers/workflows/logout/module.mjs +2 -2
- package/dist/adapters/http/controllers/workflows/logout/module.mjs.map +1 -1
- package/dist/adapters/http/controllers/workflows/password-forgot/module.mjs +1 -1
- package/dist/adapters/http/controllers/workflows/password-reset/module.mjs +1 -1
- package/dist/adapters/http/controllers/workflows/register/module.mjs +1 -1
- package/dist/adapters/http/controllers/workflows/userinfo/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/workflows/userinfo/module.mjs +5 -1
- package/dist/adapters/http/controllers/workflows/userinfo/module.mjs.map +1 -1
- package/dist/adapters/http/index.mjs +3 -1
- package/dist/adapters/http/middleware/built-in/assets.d.ts +8 -1
- package/dist/adapters/http/middleware/built-in/assets.d.ts.map +1 -1
- package/dist/adapters/http/middleware/built-in/assets.mjs +48 -4
- package/dist/adapters/http/middleware/built-in/assets.mjs.map +1 -1
- package/dist/adapters/http/middleware/built-in/index.d.ts +2 -0
- package/dist/adapters/http/middleware/built-in/index.d.ts.map +1 -1
- package/dist/adapters/http/middleware/built-in/index.mjs +2 -1
- package/dist/adapters/http/middleware/built-in/theme.d.ts +15 -0
- package/dist/adapters/http/middleware/built-in/theme.d.ts.map +1 -0
- package/dist/adapters/http/middleware/built-in/theme.mjs +27 -0
- package/dist/adapters/http/middleware/built-in/theme.mjs.map +1 -0
- package/dist/adapters/http/middleware/built-in/types.d.ts +14 -0
- package/dist/adapters/http/middleware/built-in/types.d.ts.map +1 -0
- package/dist/adapters/http/middleware/built-in/ui-http-client.d.ts +1 -1
- package/dist/adapters/http/middleware/built-in/ui-http-client.mjs +1 -1
- package/dist/adapters/http/middleware/built-in/ui-http-client.mjs.map +1 -1
- package/dist/adapters/http/middleware/index.mjs +2 -1
- package/dist/adapters/http/ui/account-console/index.d.ts +4 -0
- package/dist/adapters/http/ui/account-console/index.d.ts.map +1 -0
- package/dist/adapters/http/ui/account-console/index.mjs +6 -0
- package/dist/adapters/http/ui/account-console/module.d.ts +28 -0
- package/dist/adapters/http/ui/account-console/module.d.ts.map +1 -0
- package/dist/adapters/http/ui/account-console/module.mjs +80 -0
- package/dist/adapters/http/ui/account-console/module.mjs.map +1 -0
- package/dist/adapters/http/ui/account-console/ref.d.ts +30 -0
- package/dist/adapters/http/ui/account-console/ref.d.ts.map +1 -0
- package/dist/adapters/http/ui/account-console/ref.mjs +50 -0
- package/dist/adapters/http/ui/account-console/ref.mjs.map +1 -0
- package/dist/adapters/http/ui/account-console/types.d.ts +11 -0
- package/dist/adapters/http/ui/account-console/types.d.ts.map +1 -0
- package/dist/adapters/http/ui/account-console/types.mjs +4 -0
- package/dist/adapters/http/ui/{internal-http-client.d.ts → auth-console/http-client.d.ts} +1 -1
- package/dist/adapters/http/ui/auth-console/http-client.d.ts.map +1 -0
- package/dist/adapters/http/ui/{internal-http-client.mjs → auth-console/http-client.mjs} +2 -2
- package/dist/adapters/http/ui/auth-console/http-client.mjs.map +1 -0
- package/dist/adapters/http/ui/auth-console/index.d.ts +6 -0
- package/dist/adapters/http/ui/auth-console/index.d.ts.map +1 -0
- package/dist/adapters/http/ui/auth-console/index.mjs +8 -0
- package/dist/adapters/http/ui/auth-console/module.d.ts +4 -0
- package/dist/adapters/http/ui/auth-console/module.d.ts.map +1 -0
- package/dist/adapters/http/ui/auth-console/module.mjs +73 -0
- package/dist/adapters/http/ui/auth-console/module.mjs.map +1 -0
- package/dist/adapters/http/ui/auth-console/resolve.d.ts +21 -0
- package/dist/adapters/http/ui/auth-console/resolve.d.ts.map +1 -0
- package/dist/adapters/http/ui/auth-console/resolve.mjs +53 -0
- package/dist/adapters/http/ui/auth-console/resolve.mjs.map +1 -0
- package/dist/adapters/http/ui/{serve.d.ts → auth-console/serve.d.ts} +1 -8
- package/dist/adapters/http/ui/auth-console/serve.d.ts.map +1 -0
- package/dist/adapters/http/ui/{serve.mjs → auth-console/serve.mjs} +5 -5
- package/dist/adapters/http/ui/auth-console/serve.mjs.map +1 -0
- package/dist/adapters/http/ui/auth-console/types.d.ts +26 -0
- package/dist/adapters/http/ui/auth-console/types.d.ts.map +1 -0
- package/dist/adapters/http/ui/auth-console/types.mjs +4 -0
- package/dist/adapters/http/ui/console-packages/index.d.ts +3 -0
- package/dist/adapters/http/ui/console-packages/index.d.ts.map +1 -0
- package/dist/adapters/http/ui/console-packages/index.mjs +5 -0
- package/dist/adapters/http/ui/console-packages/module.d.ts +33 -0
- package/dist/adapters/http/ui/console-packages/module.d.ts.map +1 -0
- package/dist/adapters/http/ui/console-packages/module.mjs +70 -0
- package/dist/adapters/http/ui/console-packages/module.mjs.map +1 -0
- package/dist/adapters/http/ui/console-packages/types.d.ts +12 -0
- package/dist/adapters/http/ui/console-packages/types.d.ts.map +1 -0
- package/dist/adapters/http/ui/console-packages/types.mjs +4 -0
- package/dist/adapters/http/ui/index.d.ts +5 -5
- package/dist/adapters/http/ui/index.d.ts.map +1 -1
- package/dist/adapters/http/ui/index.mjs +22 -5
- package/dist/adapters/http/ui/shared/html.d.ts +76 -0
- package/dist/adapters/http/ui/shared/html.d.ts.map +1 -0
- package/dist/adapters/http/ui/shared/html.mjs +103 -0
- package/dist/adapters/http/ui/shared/html.mjs.map +1 -0
- package/dist/adapters/http/ui/shared/index.d.ts +2 -0
- package/dist/adapters/http/ui/shared/index.d.ts.map +1 -0
- package/dist/adapters/http/ui/shared/index.mjs +5 -0
- package/dist/adapters/http/ui/theme/apply.d.ts +12 -0
- package/dist/adapters/http/ui/theme/apply.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/apply.mjs +29 -0
- package/dist/adapters/http/ui/theme/apply.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/assets.d.ts +20 -0
- package/dist/adapters/http/ui/theme/assets.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/assets.mjs +86 -0
- package/dist/adapters/http/ui/theme/assets.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/constants.d.ts +16 -0
- package/dist/adapters/http/ui/theme/constants.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/constants.mjs +20 -0
- package/dist/adapters/http/ui/theme/constants.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/contract/constants.d.ts +120 -0
- package/dist/adapters/http/ui/theme/contract/constants.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/contract/constants.mjs +136 -0
- package/dist/adapters/http/ui/theme/contract/constants.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/contract/head.d.ts +12 -0
- package/dist/adapters/http/ui/theme/contract/head.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/contract/head.mjs +61 -0
- package/dist/adapters/http/ui/theme/contract/head.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/contract/index.d.ts +6 -0
- package/dist/adapters/http/ui/theme/contract/index.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/contract/index.mjs +8 -0
- package/dist/adapters/http/ui/theme/contract/manifest.d.ts +25 -0
- package/dist/adapters/http/ui/theme/contract/manifest.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/contract/manifest.mjs +77 -0
- package/dist/adapters/http/ui/theme/contract/manifest.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/contract/types.d.ts +26 -0
- package/dist/adapters/http/ui/theme/contract/types.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/contract/types.mjs +4 -0
- package/dist/adapters/http/ui/theme/contract/utils.d.ts +11 -0
- package/dist/adapters/http/ui/theme/contract/utils.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/contract/utils.mjs +22 -0
- package/dist/adapters/http/ui/theme/contract/utils.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/index.d.ts +7 -0
- package/dist/adapters/http/ui/theme/index.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/index.mjs +13 -0
- package/dist/adapters/http/ui/theme/module.d.ts +79 -0
- package/dist/adapters/http/ui/theme/module.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/module.mjs +206 -0
- package/dist/adapters/http/ui/theme/module.mjs.map +1 -0
- package/dist/adapters/http/ui/theme/types.d.ts +42 -0
- package/dist/adapters/http/ui/theme/types.d.ts.map +1 -0
- package/dist/adapters/http/ui/theme/types.mjs +4 -0
- package/dist/app/index.mjs +3 -1
- package/dist/app/modules/config/constants.d.ts +5 -0
- package/dist/app/modules/config/constants.d.ts.map +1 -1
- package/dist/app/modules/config/constants.mjs +5 -0
- package/dist/app/modules/config/constants.mjs.map +1 -1
- package/dist/app/modules/config/normalize.d.ts.map +1 -1
- package/dist/app/modules/config/normalize.mjs +8 -0
- package/dist/app/modules/config/normalize.mjs.map +1 -1
- package/dist/app/modules/config/origins.d.ts.map +1 -1
- package/dist/app/modules/config/origins.mjs +2 -0
- package/dist/app/modules/config/origins.mjs.map +1 -1
- package/dist/app/modules/config/read/env.d.ts.map +1 -1
- package/dist/app/modules/config/read/env.mjs +10 -0
- package/dist/app/modules/config/read/env.mjs.map +1 -1
- package/dist/app/modules/config/types.d.ts +63 -3
- package/dist/app/modules/config/types.d.ts.map +1 -1
- package/dist/app/modules/config/validator.d.ts.map +1 -1
- package/dist/app/modules/config/validator.mjs +5 -0
- package/dist/app/modules/config/validator.mjs.map +1 -1
- package/dist/app/modules/http/module.d.ts.map +1 -1
- package/dist/app/modules/http/module.mjs +7 -1
- package/dist/app/modules/http/module.mjs.map +1 -1
- package/dist/app/modules/http/modules/controller.d.ts +2 -1
- package/dist/app/modules/http/modules/controller.d.ts.map +1 -1
- package/dist/app/modules/http/modules/controller.mjs +20 -5
- package/dist/app/modules/http/modules/controller.mjs.map +1 -1
- package/dist/app/modules/http/modules/middleware.d.ts +9 -1
- package/dist/app/modules/http/modules/middleware.d.ts.map +1 -1
- package/dist/app/modules/http/modules/middleware.mjs +18 -3
- package/dist/app/modules/http/modules/middleware.mjs.map +1 -1
- package/dist/app/modules/index.mjs +3 -1
- package/dist/app/modules/provisioning/constants.d.ts +12 -0
- package/dist/app/modules/provisioning/constants.d.ts.map +1 -0
- package/dist/app/modules/provisioning/constants.mjs +16 -0
- package/dist/app/modules/provisioning/constants.mjs.map +1 -0
- package/dist/app/modules/provisioning/index.d.ts +2 -0
- package/dist/app/modules/provisioning/index.d.ts.map +1 -1
- package/dist/app/modules/provisioning/index.mjs +3 -1
- package/dist/app/modules/provisioning/lazy-wildcard.d.ts +15 -0
- package/dist/app/modules/provisioning/lazy-wildcard.d.ts.map +1 -0
- package/dist/app/modules/provisioning/lazy-wildcard.mjs +23 -0
- package/dist/app/modules/provisioning/lazy-wildcard.mjs.map +1 -0
- package/dist/app/modules/provisioning/module.d.ts.map +1 -1
- package/dist/app/modules/provisioning/module.mjs +21 -3
- package/dist/app/modules/provisioning/module.mjs.map +1 -1
- package/dist/app/modules/provisioning/sources/composite/module.d.ts +0 -19
- package/dist/app/modules/provisioning/sources/composite/module.d.ts.map +1 -1
- package/dist/app/modules/provisioning/sources/composite/module.mjs +7 -77
- package/dist/app/modules/provisioning/sources/composite/module.mjs.map +1 -1
- package/dist/core/entities/client/index.d.ts +1 -1
- package/dist/core/entities/client/index.d.ts.map +1 -1
- package/dist/core/entities/client/index.mjs +2 -2
- package/dist/core/entities/client/service.mjs.map +1 -1
- package/dist/core/entities/client/system-clients.d.ts +95 -0
- package/dist/core/entities/client/system-clients.d.ts.map +1 -0
- package/dist/core/entities/client/system-clients.mjs +158 -0
- package/dist/core/entities/client/system-clients.mjs.map +1 -0
- package/dist/core/entities/client/types.d.ts +17 -4
- package/dist/core/entities/client/types.d.ts.map +1 -1
- package/dist/core/entities/index.mjs +2 -2
- package/dist/core/entities/realm/service.d.ts +8 -6
- package/dist/core/entities/realm/service.d.ts.map +1 -1
- package/dist/core/entities/realm/service.mjs +5 -8
- package/dist/core/entities/realm/service.mjs.map +1 -1
- package/dist/core/index.mjs +6 -2
- package/dist/core/key/types.d.ts +2 -1
- package/dist/core/key/types.d.ts.map +1 -1
- package/dist/core/oauth2/authorization/code-request/verifier/module.d.ts.map +1 -1
- package/dist/core/oauth2/authorization/code-request/verifier/module.mjs +2 -2
- package/dist/core/oauth2/authorization/code-request/verifier/module.mjs.map +1 -1
- package/dist/core/oauth2/end-session/service.d.ts.map +1 -1
- package/dist/core/oauth2/end-session/service.mjs +2 -2
- package/dist/core/oauth2/end-session/service.mjs.map +1 -1
- package/dist/core/provisioning/constants.d.ts +8 -0
- package/dist/core/provisioning/constants.d.ts.map +1 -0
- package/dist/core/provisioning/constants.mjs +14 -0
- package/dist/core/provisioning/constants.mjs.map +1 -0
- package/dist/core/provisioning/entities/index.mjs +2 -1
- package/dist/core/provisioning/entities/realm/index.d.ts +1 -0
- package/dist/core/provisioning/entities/realm/index.d.ts.map +1 -1
- package/dist/core/provisioning/entities/realm/index.mjs +2 -1
- package/dist/core/provisioning/entities/realm/validator.d.ts +11 -0
- package/dist/core/provisioning/entities/realm/validator.d.ts.map +1 -1
- package/dist/core/provisioning/entities/realm/validator.mjs +14 -0
- package/dist/core/provisioning/entities/realm/validator.mjs.map +1 -1
- package/dist/core/provisioning/entities/realm/wildcard-validator.d.ts +19 -0
- package/dist/core/provisioning/entities/realm/wildcard-validator.d.ts.map +1 -0
- package/dist/core/provisioning/entities/realm/wildcard-validator.mjs +65 -0
- package/dist/core/provisioning/entities/realm/wildcard-validator.mjs.map +1 -0
- package/dist/core/provisioning/index.d.ts +3 -0
- package/dist/core/provisioning/index.d.ts.map +1 -1
- package/dist/core/provisioning/index.mjs +7 -1
- package/dist/core/provisioning/merge/index.d.ts +3 -0
- package/dist/core/provisioning/merge/index.d.ts.map +1 -0
- package/dist/core/provisioning/merge/index.mjs +5 -0
- package/dist/core/provisioning/merge/module.d.ts +18 -0
- package/dist/core/provisioning/merge/module.d.ts.map +1 -0
- package/dist/core/provisioning/merge/module.mjs +82 -0
- package/dist/core/provisioning/merge/module.mjs.map +1 -0
- package/dist/core/provisioning/merge/types.d.ts +34 -0
- package/dist/core/provisioning/merge/types.d.ts.map +1 -0
- package/dist/core/provisioning/merge/types.mjs +4 -0
- package/dist/core/provisioning/types.d.ts +10 -0
- package/dist/core/provisioning/types.d.ts.map +1 -1
- package/dist/core/provisioning/wildcard/index.d.ts +3 -0
- package/dist/core/provisioning/wildcard/index.d.ts.map +1 -0
- package/dist/core/provisioning/wildcard/index.mjs +5 -0
- package/dist/core/provisioning/wildcard/module.d.ts +53 -0
- package/dist/core/provisioning/wildcard/module.d.ts.map +1 -0
- package/dist/core/provisioning/wildcard/module.mjs +91 -0
- package/dist/core/provisioning/wildcard/module.mjs.map +1 -0
- package/dist/core/provisioning/wildcard/types.d.ts +19 -0
- package/dist/core/provisioning/wildcard/types.d.ts.map +1 -0
- package/dist/core/provisioning/wildcard/types.mjs +4 -0
- package/dist/index.mjs +5 -1
- package/dist/path.d.ts +0 -2
- package/dist/path.d.ts.map +1 -1
- package/dist/path.mjs +1 -3
- package/dist/path.mjs.map +1 -1
- package/dist/swagger.json +50 -6
- package/package.json +19 -57
- package/dist/adapters/http/ui/base-path.d.ts +0 -9
- package/dist/adapters/http/ui/base-path.d.ts.map +0 -1
- package/dist/adapters/http/ui/base-path.mjs +0 -18
- package/dist/adapters/http/ui/base-path.mjs.map +0 -1
- package/dist/adapters/http/ui/internal-http-client.d.ts.map +0 -1
- package/dist/adapters/http/ui/internal-http-client.mjs.map +0 -1
- package/dist/adapters/http/ui/render.d.ts +0 -4
- package/dist/adapters/http/ui/render.d.ts.map +0 -1
- package/dist/adapters/http/ui/render.mjs +0 -63
- package/dist/adapters/http/ui/render.mjs.map +0 -1
- package/dist/adapters/http/ui/serve.d.ts.map +0 -1
- package/dist/adapters/http/ui/serve.mjs.map +0 -1
- package/dist/adapters/http/ui/types.d.ts +0 -20
- package/dist/adapters/http/ui/types.d.ts.map +0 -1
- package/dist/core/entities/client/web-client.d.ts +0 -72
- package/dist/core/entities/client/web-client.d.ts.map +0 -1
- package/dist/core/entities/client/web-client.mjs +0 -123
- package/dist/core/entities/client/web-client.mjs.map +0 -1
- package/dist/ui/client/.vite/ssr-manifest.json +0 -54974
- package/dist/ui/client/assets/asap-latin-400-normal-Dc37g3Il.woff +0 -0
- package/dist/ui/client/assets/asap-latin-400-normal-DnZ1MOsG.woff2 +0 -0
- package/dist/ui/client/assets/asap-latin-700-normal-Dqcopq14.woff +0 -0
- package/dist/ui/client/assets/asap-latin-700-normal-l2Y8pi-F.woff2 +0 -0
- package/dist/ui/client/assets/asap-latin-ext-400-normal-C-fcRdtE.woff +0 -0
- package/dist/ui/client/assets/asap-latin-ext-400-normal-DwYtpHbV.woff2 +0 -0
- package/dist/ui/client/assets/asap-latin-ext-700-normal-D00OESsZ.woff +0 -0
- package/dist/ui/client/assets/asap-latin-ext-700-normal-y8bo5aW5.woff2 +0 -0
- package/dist/ui/client/assets/asap-vietnamese-400-normal-DGyJlJLT.woff2 +0 -0
- package/dist/ui/client/assets/asap-vietnamese-400-normal-lfLaoF2x.woff +0 -0
- package/dist/ui/client/assets/asap-vietnamese-700-normal-B-e19ug5.woff +0 -0
- package/dist/ui/client/assets/asap-vietnamese-700-normal-D-SxY94i.woff2 +0 -0
- package/dist/ui/client/assets/index-BTK3GPZZ.js +0 -106
- package/dist/ui/client/assets/index-Cx_pI7BS.css +0 -2
- package/dist/ui/client/assets/nunito-cyrillic-300-normal-BPQV259B.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-300-normal-g5qSsvc0.woff +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-400-normal-D1j0u8EH.woff +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-400-normal-xAOo5cBP.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-600-normal-BUjmtIuu.woff +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-600-normal-DJGQ2h05.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-700-normal-DP36NgGt.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-700-normal-DfHRUDv-.woff +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-300-normal-BDHtqmCR.woff +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-300-normal-ChMQLIrm.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-400-normal-CuPM9foi.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-400-normal-xaE7D4Sw.woff +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-600-normal-CWPPjBOD.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-600-normal-vnfu4DTE.woff +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-700-normal-BuR0mlCG.woff2 +0 -0
- package/dist/ui/client/assets/nunito-cyrillic-ext-700-normal-DfoqN4Gs.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-300-normal-C3ZIKvzv.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-300-normal-COoPE5VN.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-400-normal-DKg4f3fz.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-400-normal-r8SDr6Up.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-600-normal-Br8yIETf.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-600-normal-Cd0eNu1l.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-700-normal-Dort48En.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-700-normal-OcDqTBcA.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-300-normal-D1SyVuPt.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-300-normal-DfqbmdMl.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-400-normal-CjMJVfGn.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-400-normal-i-8OOpdj.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-600-normal-BKUpuB78.woff +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-600-normal-Dd9Zuxh1.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-700-normal-BWeMsAzO.woff2 +0 -0
- package/dist/ui/client/assets/nunito-latin-ext-700-normal-D4woHhbd.woff +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-300-normal-DATld5Vw.woff +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-300-normal-mLneKULB.woff2 +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-400-normal-BHkVbP3T.woff2 +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-400-normal-DtOtW02z.woff +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-600-normal-BY8O6Cug.woff2 +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-600-normal-DO1RqFw5.woff +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-700-normal-C9SQsXvj.woff +0 -0
- package/dist/ui/client/assets/nunito-vietnamese-700-normal-Ch8EUCfz.woff2 +0 -0
- package/dist/ui/client/index.html +0 -22
- package/dist/ui/server/assets/multipart-parser-BCMy8Xwv.js +0 -181
- package/dist/ui/server/assets/multipart-parser-BVY8pIV2.js +0 -176
- package/dist/ui/server/server.js +0 -155269
- /package/dist/adapters/http/{ui → middleware/built-in}/types.mjs +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Container } from 'validup';
|
|
2
|
+
import type { ThemeManifest } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Validator for the operator-authored `theme.json`.
|
|
5
|
+
*
|
|
6
|
+
* A validup Container over zod mounts, matching ConfigValidator: this file
|
|
7
|
+
* is config, not an API payload, and the config-shaped precedent in this
|
|
8
|
+
* codebase is validup. It also means a second consumer (a per-realm token
|
|
9
|
+
* map, an admin-console form, a CLI `theme validate`) reuses these rules
|
|
10
|
+
* instead of restating them.
|
|
11
|
+
*
|
|
12
|
+
* No validator groups: there is one source and one shape. A per-realm
|
|
13
|
+
* surface that accepts a narrower manifest than a file does would add one.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ThemeManifestValidator extends Container<ThemeManifest> {
|
|
16
|
+
protected initialize(): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate a parsed `theme.json`. On failure the thrown error names the
|
|
20
|
+
* file and renders every issue as `<path>: <message>`, matching
|
|
21
|
+
* FileProvisioningSource: a bad entry must never abort a boot with nothing
|
|
22
|
+
* to act on.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseThemeManifest(input: unknown, filePath: string): Promise<ThemeManifest>;
|
|
25
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/ui/theme/contract/manifest.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,SAAS,EAAiC,MAAM,SAAS,CAAC;AAWnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwChD;;;;;;;;;;;GAWG;AACH,qBAAa,sBAAuB,SAAQ,SAAS,CAAC,aAAa,CAAC;cAC7C,UAAU;CAYhC;AAqBD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAI,OAAO,CAAC,aAAa,CAAC,CA2BlG"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { THEME_ASSETS_DIRECTORY_NAME, THEME_ASSET_EXTENSIONS, THEME_IMAGE_EXTENSIONS, THEME_TOKEN_NAME_PATTERN, THEME_TOKEN_VALUE_FORBIDDEN_PATTERN } from "./constants.mjs";
|
|
5
|
+
import { themeAssetExtension } from "./utils.mjs";
|
|
6
|
+
import { AuthupError } from "@authup/errors";
|
|
7
|
+
import { Container, isValidupError, stringifyPath } from "validup";
|
|
8
|
+
import { createValidator } from "@validup/zod";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
//#region src/adapters/http/ui/theme/contract/manifest.ts
|
|
11
|
+
const assetPathSchema = z.string().regex(new RegExp(`^${THEME_ASSETS_DIRECTORY_NAME}/[a-zA-Z0-9][a-zA-Z0-9._/-]*$`), `must be a path inside '${THEME_ASSETS_DIRECTORY_NAME}/'`).refine((value) => !value.split("/").includes(".."), "must not contain a \"..\" segment").refine((value) => THEME_ASSET_EXTENSIONS.includes(themeAssetExtension(value)), `must end in one of: ${THEME_ASSET_EXTENSIONS.join(", ")}`);
|
|
12
|
+
/**
|
|
13
|
+
* A logo must be an image. The general asset allowlist also covers CSS and
|
|
14
|
+
* fonts, which would silently render as nothing here.
|
|
15
|
+
*/ const logoPathSchema = assetPathSchema.refine((value) => THEME_IMAGE_EXTENSIONS.includes(themeAssetExtension(value)), `must be an image (${THEME_IMAGE_EXTENSIONS.join(", ")})`);
|
|
16
|
+
const tokenMapSchema = z.record(z.string().regex(THEME_TOKEN_NAME_PATTERN, "must be a lowercase CSS custom property name (e.g. --authup-auth-accent)"), z.string().min(1).max(256).refine((value) => !THEME_TOKEN_VALUE_FORBIDDEN_PATTERN.test(value), "must not contain }, <, >, ;, @, \\, /*, url( or expression("));
|
|
17
|
+
/**
|
|
18
|
+
* Validator for the operator-authored `theme.json`.
|
|
19
|
+
*
|
|
20
|
+
* A validup Container over zod mounts, matching ConfigValidator: this file
|
|
21
|
+
* is config, not an API payload, and the config-shaped precedent in this
|
|
22
|
+
* codebase is validup. It also means a second consumer (a per-realm token
|
|
23
|
+
* map, an admin-console form, a CLI `theme validate`) reuses these rules
|
|
24
|
+
* instead of restating them.
|
|
25
|
+
*
|
|
26
|
+
* No validator groups: there is one source and one shape. A per-realm
|
|
27
|
+
* surface that accepts a narrower manifest than a file does would add one.
|
|
28
|
+
*/ var ThemeManifestValidator = class extends Container {
|
|
29
|
+
initialize() {
|
|
30
|
+
super.initialize();
|
|
31
|
+
this.mount("version", createValidator(z.literal(1)));
|
|
32
|
+
this.mount("title", { optional: true }, createValidator(z.string().min(1).max(200)));
|
|
33
|
+
this.mount("favicon", { optional: true }, createValidator(assetPathSchema));
|
|
34
|
+
this.mount("logo", { optional: true }, createValidator(logoPathSchema));
|
|
35
|
+
this.mount("logoDark", { optional: true }, createValidator(logoPathSchema));
|
|
36
|
+
this.mount("stylesheet", { optional: true }, createValidator(assetPathSchema));
|
|
37
|
+
this.mount("tokens", { optional: true }, createValidator(tokenMapSchema));
|
|
38
|
+
this.mount("tokensDark", { optional: true }, createValidator(tokenMapSchema));
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const validator = new ThemeManifestValidator();
|
|
42
|
+
/**
|
|
43
|
+
* Every key the validator mounts. Validup STRIPS an unmounted key rather
|
|
44
|
+
* than rejecting it, and a silently-ignored `stylesheets` typo is the exact
|
|
45
|
+
* failure mode this feature must not have, so unknown keys are rejected
|
|
46
|
+
* explicitly before the run.
|
|
47
|
+
*/ const KNOWN_KEYS = [
|
|
48
|
+
"version",
|
|
49
|
+
"title",
|
|
50
|
+
"favicon",
|
|
51
|
+
"logo",
|
|
52
|
+
"logoDark",
|
|
53
|
+
"stylesheet",
|
|
54
|
+
"tokens",
|
|
55
|
+
"tokensDark"
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* Validate a parsed `theme.json`. On failure the thrown error names the
|
|
59
|
+
* file and renders every issue as `<path>: <message>`, matching
|
|
60
|
+
* FileProvisioningSource: a bad entry must never abort a boot with nothing
|
|
61
|
+
* to act on.
|
|
62
|
+
*/ async function parseThemeManifest(input, filePath) {
|
|
63
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new AuthupError(`The theme manifest "${filePath}" must contain an object at its root.`);
|
|
64
|
+
const unknown = Object.keys(input).filter((key) => !KNOWN_KEYS.includes(key));
|
|
65
|
+
if (unknown.length > 0) throw new AuthupError(`The theme manifest "${filePath}" is invalid.\n${unknown.map((key) => ` ${key}: unknown property`).join("\n")}`);
|
|
66
|
+
try {
|
|
67
|
+
return await validator.run(input);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
if (!isValidupError(e)) throw e;
|
|
70
|
+
const issues = e.issues.map((issue) => ` ${stringifyPath(issue.path)}: ${issue.message}`).join("\n");
|
|
71
|
+
throw new AuthupError(`The theme manifest "${filePath}" is invalid.\n${issues}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { ThemeManifestValidator, parseThemeManifest };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=manifest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.mjs","names":["AuthupError","createValidator","Container","isValidupError","stringifyPath","z","THEME_ASSETS_DIRECTORY_NAME","THEME_ASSET_EXTENSIONS","THEME_IMAGE_EXTENSIONS","THEME_MANIFEST_VERSION","THEME_TOKEN_NAME_PATTERN","THEME_TOKEN_VALUE_FORBIDDEN_PATTERN","THEME_TOKEN_VALUE_MAX_LENGTH","themeAssetExtension","assetPathSchema","string","regex","RegExp","refine","value","split","includes","join","logoPathSchema","tokenMapSchema","record","min","max","test","ThemeManifestValidator","initialize","mount","literal","optional","validator","KNOWN_KEYS","parseThemeManifest","input","filePath","Array","isArray","unknown","Object","keys","filter","key","length","map","run","e","issues","issue","path","message"],"sources":["../../../../../../src/adapters/http/ui/theme/contract/manifest.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { AuthupError } from '@authup/errors';\nimport { createValidator } from '@validup/zod';\nimport { Container, isValidupError, stringifyPath } from 'validup';\nimport { z } from 'zod';\nimport {\n THEME_ASSETS_DIRECTORY_NAME,\n THEME_ASSET_EXTENSIONS,\n THEME_IMAGE_EXTENSIONS,\n THEME_MANIFEST_VERSION,\n THEME_TOKEN_NAME_PATTERN,\n THEME_TOKEN_VALUE_FORBIDDEN_PATTERN,\n THEME_TOKEN_VALUE_MAX_LENGTH,\n} from './constants.ts';\nimport type { ThemeManifest } from './types.ts';\nimport { themeAssetExtension } from './utils.ts';\n\nconst assetPathSchema = z.string()\n .regex(\n new RegExp(`^${THEME_ASSETS_DIRECTORY_NAME}/[a-zA-Z0-9][a-zA-Z0-9._/-]*$`),\n `must be a path inside '${THEME_ASSETS_DIRECTORY_NAME}/'`,\n )\n .refine(\n (value) => !value.split('/').includes('..'),\n 'must not contain a \"..\" segment',\n )\n .refine(\n (value) => THEME_ASSET_EXTENSIONS.includes(themeAssetExtension(value)),\n `must end in one of: ${THEME_ASSET_EXTENSIONS.join(', ')}`,\n );\n\n/**\n * A logo must be an image. The general asset allowlist also covers CSS and\n * fonts, which would silently render as nothing here.\n */\nconst logoPathSchema = assetPathSchema.refine(\n (value) => THEME_IMAGE_EXTENSIONS.includes(themeAssetExtension(value)),\n `must be an image (${THEME_IMAGE_EXTENSIONS.join(', ')})`,\n);\n\nconst tokenMapSchema = z.record(\n z.string().regex(\n THEME_TOKEN_NAME_PATTERN,\n 'must be a lowercase CSS custom property name (e.g. --authup-auth-accent)',\n ),\n z.string()\n .min(1)\n .max(THEME_TOKEN_VALUE_MAX_LENGTH)\n .refine(\n (value) => !THEME_TOKEN_VALUE_FORBIDDEN_PATTERN.test(value),\n 'must not contain }, <, >, ;, @, \\\\, /*, url( or expression(',\n ),\n);\n\n/**\n * Validator for the operator-authored `theme.json`.\n *\n * A validup Container over zod mounts, matching ConfigValidator: this file\n * is config, not an API payload, and the config-shaped precedent in this\n * codebase is validup. It also means a second consumer (a per-realm token\n * map, an admin-console form, a CLI `theme validate`) reuses these rules\n * instead of restating them.\n *\n * No validator groups: there is one source and one shape. A per-realm\n * surface that accepts a narrower manifest than a file does would add one.\n */\nexport class ThemeManifestValidator extends Container<ThemeManifest> {\n protected override initialize() {\n super.initialize();\n\n this.mount('version', createValidator(z.literal(THEME_MANIFEST_VERSION)));\n this.mount('title', { optional: true }, createValidator(z.string().min(1).max(200)));\n this.mount('favicon', { optional: true }, createValidator(assetPathSchema));\n this.mount('logo', { optional: true }, createValidator(logoPathSchema));\n this.mount('logoDark', { optional: true }, createValidator(logoPathSchema));\n this.mount('stylesheet', { optional: true }, createValidator(assetPathSchema));\n this.mount('tokens', { optional: true }, createValidator(tokenMapSchema));\n this.mount('tokensDark', { optional: true }, createValidator(tokenMapSchema));\n }\n}\n\nconst validator = new ThemeManifestValidator();\n\n/**\n * Every key the validator mounts. Validup STRIPS an unmounted key rather\n * than rejecting it, and a silently-ignored `stylesheets` typo is the exact\n * failure mode this feature must not have, so unknown keys are rejected\n * explicitly before the run.\n */\nconst KNOWN_KEYS = [\n 'version',\n 'title',\n 'favicon',\n 'logo',\n 'logoDark',\n 'stylesheet',\n 'tokens',\n 'tokensDark',\n];\n\n/**\n * Validate a parsed `theme.json`. On failure the thrown error names the\n * file and renders every issue as `<path>: <message>`, matching\n * FileProvisioningSource: a bad entry must never abort a boot with nothing\n * to act on.\n */\nexport async function parseThemeManifest(input: unknown, filePath: string) : Promise<ThemeManifest> {\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n throw new AuthupError(`The theme manifest \"${filePath}\" must contain an object at its root.`);\n }\n\n const unknown = Object.keys(input).filter((key) => !KNOWN_KEYS.includes(key));\n if (unknown.length > 0) {\n throw new AuthupError(\n `The theme manifest \"${filePath}\" is invalid.\\n${unknown\n .map((key) => ` ${key}: unknown property`)\n .join('\\n')}`,\n );\n }\n\n try {\n return await validator.run(input as Record<string, any>) as ThemeManifest;\n } catch (e) {\n if (!isValidupError(e)) {\n throw e;\n }\n\n const issues = e.issues\n .map((issue) => ` ${stringifyPath(issue.path)}: ${issue.message}`)\n .join('\\n');\n\n throw new AuthupError(`The theme manifest \"${filePath}\" is invalid.\\n${issues}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The operator-authored `theme.json`, after validation.
|
|
3
|
+
*
|
|
4
|
+
* `favicon` / `logo` / `stylesheet` are theme-relative paths inside
|
|
5
|
+
* `assets/`; they are emitted as `<basePath>/theme/<name>` hrefs.
|
|
6
|
+
*/
|
|
7
|
+
export type ThemeManifest = {
|
|
8
|
+
version: number;
|
|
9
|
+
title?: string;
|
|
10
|
+
favicon?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Replaces the built-in logo mark on both consoles. Maps onto the
|
|
13
|
+
* `--authup-{auth,account}-logo-*` token pairs.
|
|
14
|
+
*/
|
|
15
|
+
logo?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Dark-mode variant of `logo`. Without it dark mode inherits `logo`,
|
|
18
|
+
* which is wrong for a mark that is dark-on-light. Usable on its own to
|
|
19
|
+
* override only dark mode.
|
|
20
|
+
*/
|
|
21
|
+
logoDark?: string;
|
|
22
|
+
stylesheet?: string;
|
|
23
|
+
tokens?: Record<string, string>;
|
|
24
|
+
tokensDark?: Record<string, string>;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/ui/theme/contract/types.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lowercased file extension of a theme-relative path, including the dot
|
|
3
|
+
* (`assets/logo.SVG` -> `.svg`); empty when there is none.
|
|
4
|
+
*
|
|
5
|
+
* Hand-rolled rather than `node:path`'s extname so this folder stays free
|
|
6
|
+
* of node APIs and can be consumed by a browser-side theme editor.
|
|
7
|
+
* Manifest paths are always POSIX and already grammar-checked, so the
|
|
8
|
+
* separator handling extname does is not needed here.
|
|
9
|
+
*/
|
|
10
|
+
export declare function themeAssetExtension(value: string): string;
|
|
11
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/ui/theme/contract/utils.ts"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAI,MAAM,CAS1D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/adapters/http/ui/theme/contract/utils.ts
|
|
5
|
+
/**
|
|
6
|
+
* Lowercased file extension of a theme-relative path, including the dot
|
|
7
|
+
* (`assets/logo.SVG` -> `.svg`); empty when there is none.
|
|
8
|
+
*
|
|
9
|
+
* Hand-rolled rather than `node:path`'s extname so this folder stays free
|
|
10
|
+
* of node APIs and can be consumed by a browser-side theme editor.
|
|
11
|
+
* Manifest paths are always POSIX and already grammar-checked, so the
|
|
12
|
+
* separator handling extname does is not needed here.
|
|
13
|
+
*/ function themeAssetExtension(value) {
|
|
14
|
+
const name = value.slice(value.lastIndexOf("/") + 1);
|
|
15
|
+
const index = name.lastIndexOf(".");
|
|
16
|
+
if (index <= 0) return "";
|
|
17
|
+
return name.slice(index).toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { themeAssetExtension };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":["themeAssetExtension","value","name","slice","lastIndexOf","index","toLowerCase"],"sources":["../../../../../../src/adapters/http/ui/theme/contract/utils.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\n/**\n * Lowercased file extension of a theme-relative path, including the dot\n * (`assets/logo.SVG` -> `.svg`); empty when there is none.\n *\n * Hand-rolled rather than `node:path`'s extname so this folder stays free\n * of node APIs and can be consumed by a browser-side theme editor.\n * Manifest paths are always POSIX and already grammar-checked, so the\n * separator handling extname does is not needed here.\n */\nexport function themeAssetExtension(value: string) : string {\n const name = value.slice(value.lastIndexOf('/') + 1);\n const index = name.lastIndexOf('.');\n\n if (index <= 0) {\n return '';\n }\n\n return name.slice(index).toLowerCase();\n}\n"],"mappings":";;;;;;;;;;;;GAgBA,SAAgBA,oBAAoBC,OAAa;CAC7C,MAAMC,OAAOD,MAAME,MAAMF,MAAMG,YAAY,GAAA,IAAO,CAAA;CAClD,MAAMC,QAAQH,KAAKE,YAAY,GAAA;CAE/B,IAAIC,SAAS,GACT,OAAO;CAGX,OAAOH,KAAKC,MAAME,KAAAA,CAAAA,CAAOC,YAAW;AACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/adapters/http/ui/theme/index.ts"],"names":[],"mappings":"AAOA,cAAc,qBAAqB,CAAC;AAEpC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { THEME_ASSETS_DIRECTORY_NAME, THEME_ASSET_CONTENT_TYPES, THEME_ASSET_EXTENSIONS, THEME_ASSET_MOUNT_PATH, THEME_CSS_LAYER_NAME, THEME_FRAGMENTS_DIRECTORY_NAME, THEME_HEAD_FRAGMENT_FILE_NAME, THEME_HEAD_FRAGMENT_MAX_LENGTH, THEME_IMAGE_EXTENSIONS, THEME_LOGO_TOKENS, THEME_MANIFEST_FILE_NAME, THEME_MANIFEST_VERSION, THEME_TOKEN_NAME_PATTERN, THEME_TOKEN_VALUE_FORBIDDEN_PATTERN, THEME_TOKEN_VALUE_MAX_LENGTH } from "./contract/constants.mjs";
|
|
5
|
+
import { buildThemeHead, escapeHtml } from "./contract/head.mjs";
|
|
6
|
+
import { themeAssetExtension } from "./contract/utils.mjs";
|
|
7
|
+
import { ThemeManifestValidator, parseThemeManifest } from "./contract/manifest.mjs";
|
|
8
|
+
import "./contract/index.mjs";
|
|
9
|
+
import { applyTheme } from "./apply.mjs";
|
|
10
|
+
import { THEME_ASSET_CSP, THEME_MANIFEST_REVALIDATE_INTERVAL } from "./constants.mjs";
|
|
11
|
+
import { createThemeAssetsHandler } from "./assets.mjs";
|
|
12
|
+
import { ThemeProvider } from "./module.mjs";
|
|
13
|
+
export { THEME_ASSETS_DIRECTORY_NAME, THEME_ASSET_CONTENT_TYPES, THEME_ASSET_CSP, THEME_ASSET_EXTENSIONS, THEME_ASSET_MOUNT_PATH, THEME_CSS_LAYER_NAME, THEME_FRAGMENTS_DIRECTORY_NAME, THEME_HEAD_FRAGMENT_FILE_NAME, THEME_HEAD_FRAGMENT_MAX_LENGTH, THEME_IMAGE_EXTENSIONS, THEME_LOGO_TOKENS, THEME_MANIFEST_FILE_NAME, THEME_MANIFEST_REVALIDATE_INTERVAL, THEME_MANIFEST_VERSION, THEME_TOKEN_NAME_PATTERN, THEME_TOKEN_VALUE_FORBIDDEN_PATTERN, THEME_TOKEN_VALUE_MAX_LENGTH, ThemeManifestValidator, ThemeProvider, applyTheme, buildThemeHead, createThemeAssetsHandler, escapeHtml, parseThemeManifest, themeAssetExtension };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Logger } from '@authup/server-kit';
|
|
2
|
+
import type { ThemeManifest } from './contract/index.ts';
|
|
3
|
+
import type { IThemeProvider, ThemeProviderContext } from './types.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Owns the operator theme directory: manifest loading + validation, the
|
|
6
|
+
* realpathed servable asset root, and the memoized head markup.
|
|
7
|
+
*
|
|
8
|
+
* Failure posture is deliberately asymmetric, and inverts the
|
|
9
|
+
* FileProvisioningSource precedent for the request path:
|
|
10
|
+
* - at BOOT an invalid manifest throws, so a typo is reported once, loudly,
|
|
11
|
+
* with the file path and every issue.
|
|
12
|
+
* - AFTER boot a manifest that becomes invalid keeps the last good value and
|
|
13
|
+
* logs. Provisioning seeds authorization data; a theme is decoration, and
|
|
14
|
+
* a broken logo must never take down the login page of an identity
|
|
15
|
+
* provider.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ThemeProvider implements IThemeProvider {
|
|
18
|
+
protected readonly directoryPath: string;
|
|
19
|
+
protected readonly manifestPath: string;
|
|
20
|
+
protected readonly fragmentPath: string;
|
|
21
|
+
protected readonly fragmentsEnabled: boolean;
|
|
22
|
+
protected readonly logger: Logger | undefined;
|
|
23
|
+
protected manifest: ThemeManifest | undefined;
|
|
24
|
+
protected headFragment: string | undefined;
|
|
25
|
+
protected assetsPath: string | undefined;
|
|
26
|
+
protected manifestSignature: string | undefined;
|
|
27
|
+
protected fragmentSignature: string | undefined;
|
|
28
|
+
protected revalidatedAt: number;
|
|
29
|
+
protected headCache: Map<string, string>;
|
|
30
|
+
constructor(ctx: ThemeProviderContext);
|
|
31
|
+
load(): Promise<void>;
|
|
32
|
+
getManifest(): Promise<ThemeManifest | undefined>;
|
|
33
|
+
getAssetsPath(): Promise<string | undefined>;
|
|
34
|
+
getHead(basePath: string): Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* The mount root is `<root>/assets`, never the theme root, so the
|
|
37
|
+
* manifest is unreachable over HTTP by construction.
|
|
38
|
+
*
|
|
39
|
+
* Returns the LOGICAL path, deliberately not a realpath. A Kubernetes
|
|
40
|
+
* ConfigMap volume is a symlink farm whose update swaps `..data` to a
|
|
41
|
+
* NEW timestamped directory and deletes the old one, so a realpath
|
|
42
|
+
* captured here would dangle after the first update and every asset
|
|
43
|
+
* would 404 until the pod restarts. The asset handler resolves and
|
|
44
|
+
* re-asserts containment per request instead.
|
|
45
|
+
*/
|
|
46
|
+
protected resolveAssetsPath(): Promise<string | undefined>;
|
|
47
|
+
/**
|
|
48
|
+
* Read + decode the manifest through locter, the same loader
|
|
49
|
+
* FileProvisioningSource uses for its operator files. Its JSON reader
|
|
50
|
+
* is a plain read-and-parse with no content cache, so the live reload
|
|
51
|
+
* below still sees an edited file.
|
|
52
|
+
*/
|
|
53
|
+
protected readManifest(): Promise<unknown>;
|
|
54
|
+
protected readSignature(filePath: string): string | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Read `fragments/head.html`. Never called when the operator has not
|
|
57
|
+
* opted in, so dropping the file into the directory does nothing on
|
|
58
|
+
* its own.
|
|
59
|
+
*/
|
|
60
|
+
protected readFragment(): string | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Re-read the manifest when it changed on disk, at most once per
|
|
63
|
+
* interval. One stat per console render is negligible next to the SSR
|
|
64
|
+
* pass it precedes, and it is what makes an edit visible without a
|
|
65
|
+
* restart (the property every operator asks about first).
|
|
66
|
+
*/
|
|
67
|
+
protected revalidate(): Promise<void>;
|
|
68
|
+
protected revalidateManifest(): Promise<void>;
|
|
69
|
+
protected revalidateFragment(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Always logged. The dominant failure mode of this feature is silence:
|
|
72
|
+
* mount the wrong path and the page looks exactly like an un-themed
|
|
73
|
+
* page. An enumerable boot summary is both the cheapest debugging
|
|
74
|
+
* affordance and the cheapest way to notice a file that should not be
|
|
75
|
+
* in the volume.
|
|
76
|
+
*/
|
|
77
|
+
protected logInventory(): void;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../src/adapters/http/ui/theme/module.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAejD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,YAAW,cAAc;IAChD,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAG,MAAM,CAAC;IAE1C,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAG,MAAM,CAAC;IAEzC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAG,MAAM,CAAC;IAEzC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAG,OAAO,CAAC;IAE9C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAG,MAAM,GAAG,SAAS,CAAC;IAE/C,SAAS,CAAC,QAAQ,EAAG,aAAa,GAAG,SAAS,CAAC;IAE/C,SAAS,CAAC,YAAY,EAAG,MAAM,GAAG,SAAS,CAAC;IAE5C,SAAS,CAAC,UAAU,EAAG,MAAM,GAAG,SAAS,CAAC;IAE1C,SAAS,CAAC,iBAAiB,EAAG,MAAM,GAAG,SAAS,CAAC;IAEjD,SAAS,CAAC,iBAAiB,EAAG,MAAM,GAAG,SAAS,CAAC;IAEjD,SAAS,CAAC,aAAa,SAAK;IAE5B,SAAS,CAAC,SAAS,sBAA6B;gBAEpC,GAAG,EAAE,oBAAoB;IAY/B,IAAI,IAAK,OAAO,CAAC,IAAI,CAAC;IAsBtB,WAAW,IAAK,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAMlD,aAAa,IAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAM7C,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAI,OAAO,CAAC,MAAM,CAAC;IAqBjD;;;;;;;;;;OAUG;cACa,iBAAiB,IAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAajE;;;;;OAKG;cACa,YAAY,IAAK,OAAO,CAAC,OAAO,CAAC;IAYjD,SAAS,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAI,MAAM,GAAG,SAAS;IAU9D;;;;OAIG;IACH,SAAS,CAAC,YAAY,IAAK,MAAM,GAAG,SAAS;IAqB7C;;;;;OAKG;cACa,UAAU,IAAK,OAAO,CAAC,IAAI,CAAC;cAkB5B,kBAAkB,IAAK,OAAO,CAAC,IAAI,CAAC;IA2BpD,SAAS,CAAC,kBAAkB,IAAK,IAAI;IAiBrC;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,IAAK,IAAI;CAoDlC"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { THEME_ASSETS_DIRECTORY_NAME, THEME_FRAGMENTS_DIRECTORY_NAME, THEME_HEAD_FRAGMENT_FILE_NAME, THEME_HEAD_FRAGMENT_MAX_LENGTH, THEME_MANIFEST_FILE_NAME } from "./contract/constants.mjs";
|
|
5
|
+
import { buildThemeHead } from "./contract/head.mjs";
|
|
6
|
+
import { parseThemeManifest } from "./contract/manifest.mjs";
|
|
7
|
+
import "./contract/index.mjs";
|
|
8
|
+
import "./constants.mjs";
|
|
9
|
+
import { AuthupError, normalizeError } from "@authup/errors";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { read } from "locter";
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
//#region src/adapters/http/ui/theme/module.ts
|
|
14
|
+
/**
|
|
15
|
+
* Owns the operator theme directory: manifest loading + validation, the
|
|
16
|
+
* realpathed servable asset root, and the memoized head markup.
|
|
17
|
+
*
|
|
18
|
+
* Failure posture is deliberately asymmetric, and inverts the
|
|
19
|
+
* FileProvisioningSource precedent for the request path:
|
|
20
|
+
* - at BOOT an invalid manifest throws, so a typo is reported once, loudly,
|
|
21
|
+
* with the file path and every issue.
|
|
22
|
+
* - AFTER boot a manifest that becomes invalid keeps the last good value and
|
|
23
|
+
* logs. Provisioning seeds authorization data; a theme is decoration, and
|
|
24
|
+
* a broken logo must never take down the login page of an identity
|
|
25
|
+
* provider.
|
|
26
|
+
*/ var ThemeProvider = class {
|
|
27
|
+
directoryPath;
|
|
28
|
+
manifestPath;
|
|
29
|
+
fragmentPath;
|
|
30
|
+
fragmentsEnabled;
|
|
31
|
+
logger;
|
|
32
|
+
manifest;
|
|
33
|
+
headFragment;
|
|
34
|
+
assetsPath;
|
|
35
|
+
manifestSignature;
|
|
36
|
+
fragmentSignature;
|
|
37
|
+
revalidatedAt = 0;
|
|
38
|
+
headCache = /* @__PURE__ */ new Map();
|
|
39
|
+
constructor(ctx) {
|
|
40
|
+
this.directoryPath = ctx.directoryPath;
|
|
41
|
+
this.fragmentsEnabled = ctx.fragmentsEnabled ?? false;
|
|
42
|
+
this.logger = ctx.logger;
|
|
43
|
+
this.manifestPath = path.join(ctx.directoryPath, THEME_MANIFEST_FILE_NAME);
|
|
44
|
+
this.fragmentPath = path.join(ctx.directoryPath, THEME_FRAGMENTS_DIRECTORY_NAME, THEME_HEAD_FRAGMENT_FILE_NAME);
|
|
45
|
+
}
|
|
46
|
+
async load() {
|
|
47
|
+
this.assetsPath = await this.resolveAssetsPath();
|
|
48
|
+
this.manifestSignature = this.readSignature(this.manifestPath);
|
|
49
|
+
if (this.manifestSignature) this.manifest = await parseThemeManifest(await this.readManifest(), this.manifestPath);
|
|
50
|
+
if (this.fragmentsEnabled) {
|
|
51
|
+
this.headFragment = this.readFragment();
|
|
52
|
+
this.fragmentSignature = this.readSignature(this.fragmentPath);
|
|
53
|
+
}
|
|
54
|
+
this.revalidatedAt = Date.now();
|
|
55
|
+
this.headCache.clear();
|
|
56
|
+
this.logInventory();
|
|
57
|
+
}
|
|
58
|
+
async getManifest() {
|
|
59
|
+
await this.revalidate();
|
|
60
|
+
return this.manifest;
|
|
61
|
+
}
|
|
62
|
+
async getAssetsPath() {
|
|
63
|
+
await this.revalidate();
|
|
64
|
+
return this.assetsPath;
|
|
65
|
+
}
|
|
66
|
+
async getHead(basePath) {
|
|
67
|
+
const manifest = await this.getManifest();
|
|
68
|
+
const cached = this.headCache.get(basePath);
|
|
69
|
+
if (typeof cached === "string") return cached;
|
|
70
|
+
const head = buildThemeHead(manifest ?? { version: 1 }, basePath, this.headFragment);
|
|
71
|
+
this.headCache.set(basePath, head);
|
|
72
|
+
return head;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The mount root is `<root>/assets`, never the theme root, so the
|
|
76
|
+
* manifest is unreachable over HTTP by construction.
|
|
77
|
+
*
|
|
78
|
+
* Returns the LOGICAL path, deliberately not a realpath. A Kubernetes
|
|
79
|
+
* ConfigMap volume is a symlink farm whose update swaps `..data` to a
|
|
80
|
+
* NEW timestamped directory and deletes the old one, so a realpath
|
|
81
|
+
* captured here would dangle after the first update and every asset
|
|
82
|
+
* would 404 until the pod restarts. The asset handler resolves and
|
|
83
|
+
* re-asserts containment per request instead.
|
|
84
|
+
*/ async resolveAssetsPath() {
|
|
85
|
+
const assetsPath = path.join(this.directoryPath, THEME_ASSETS_DIRECTORY_NAME);
|
|
86
|
+
try {
|
|
87
|
+
return (await fs.promises.stat(assetsPath)).isDirectory() ? assetsPath : void 0;
|
|
88
|
+
} catch {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Read + decode the manifest through locter, the same loader
|
|
94
|
+
* FileProvisioningSource uses for its operator files. Its JSON reader
|
|
95
|
+
* is a plain read-and-parse with no content cache, so the live reload
|
|
96
|
+
* below still sees an edited file.
|
|
97
|
+
*/ async readManifest() {
|
|
98
|
+
try {
|
|
99
|
+
return await read(this.manifestPath);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
const reason = normalizeError(e).message;
|
|
102
|
+
throw new AuthupError(`The theme manifest "${this.manifestPath}" could not be read.\n ${reason}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
readSignature(filePath) {
|
|
106
|
+
try {
|
|
107
|
+
const stats = fs.statSync(filePath);
|
|
108
|
+
return `${stats.size}-${stats.mtimeMs}`;
|
|
109
|
+
} catch {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Read `fragments/head.html`. Never called when the operator has not
|
|
115
|
+
* opted in, so dropping the file into the directory does nothing on
|
|
116
|
+
* its own.
|
|
117
|
+
*/ readFragment() {
|
|
118
|
+
let raw;
|
|
119
|
+
try {
|
|
120
|
+
raw = fs.readFileSync(this.fragmentPath, "utf-8");
|
|
121
|
+
} catch {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (raw.length > 65536) {
|
|
125
|
+
this.logger?.warn(`The head fragment "${this.fragmentPath}" exceeds ${THEME_HEAD_FRAGMENT_MAX_LENGTH} bytes and is ignored.`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const trimmed = raw.trim();
|
|
129
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Re-read the manifest when it changed on disk, at most once per
|
|
133
|
+
* interval. One stat per console render is negligible next to the SSR
|
|
134
|
+
* pass it precedes, and it is what makes an edit visible without a
|
|
135
|
+
* restart (the property every operator asks about first).
|
|
136
|
+
*/ async revalidate() {
|
|
137
|
+
const now = Date.now();
|
|
138
|
+
if (now - this.revalidatedAt < 1e3) return;
|
|
139
|
+
this.revalidatedAt = now;
|
|
140
|
+
this.assetsPath = await this.resolveAssetsPath();
|
|
141
|
+
await this.revalidateManifest();
|
|
142
|
+
this.revalidateFragment();
|
|
143
|
+
}
|
|
144
|
+
async revalidateManifest() {
|
|
145
|
+
const signature = this.readSignature(this.manifestPath);
|
|
146
|
+
if (signature === this.manifestSignature) return;
|
|
147
|
+
if (typeof signature === "undefined") {
|
|
148
|
+
this.manifestSignature = void 0;
|
|
149
|
+
this.manifest = void 0;
|
|
150
|
+
this.headCache.clear();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.manifestSignature = signature;
|
|
154
|
+
try {
|
|
155
|
+
this.manifest = await parseThemeManifest(await this.readManifest(), this.manifestPath);
|
|
156
|
+
this.headCache.clear();
|
|
157
|
+
} catch (e) {
|
|
158
|
+
const reason = normalizeError(e).message;
|
|
159
|
+
this.logger?.warn(`The theme manifest changed but is invalid, keeping the previous one.\n${reason}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
revalidateFragment() {
|
|
163
|
+
if (!this.fragmentsEnabled) return;
|
|
164
|
+
const signature = this.readSignature(this.fragmentPath);
|
|
165
|
+
if (signature === this.fragmentSignature) return;
|
|
166
|
+
this.fragmentSignature = signature;
|
|
167
|
+
this.headFragment = typeof signature === "undefined" ? void 0 : this.readFragment();
|
|
168
|
+
this.headCache.clear();
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Always logged. The dominant failure mode of this feature is silence:
|
|
172
|
+
* mount the wrong path and the page looks exactly like an un-themed
|
|
173
|
+
* page. An enumerable boot summary is both the cheapest debugging
|
|
174
|
+
* affordance and the cheapest way to notice a file that should not be
|
|
175
|
+
* in the volume.
|
|
176
|
+
*/ logInventory() {
|
|
177
|
+
if (!this.logger) return;
|
|
178
|
+
this.logger.info(`Theme directory: ${this.directoryPath} (theming is EXPERIMENTAL and may change in a minor release)`);
|
|
179
|
+
if (this.manifest) {
|
|
180
|
+
const tokens = Object.keys(this.manifest.tokens ?? {}).length;
|
|
181
|
+
const tokensDark = Object.keys(this.manifest.tokensDark ?? {}).length;
|
|
182
|
+
this.logger.info(`Theme manifest: loaded (${tokens} token(s), ${tokensDark} dark token(s))`);
|
|
183
|
+
} else this.logger.info(`Theme manifest: none (no ${THEME_MANIFEST_FILE_NAME})`);
|
|
184
|
+
if (!this.fragmentsEnabled) this.logger.info("Theme head fragment: disabled (themeFragmentsEnabled is off)");
|
|
185
|
+
else if (this.headFragment) this.logger.info(`Theme head fragment: loaded (${this.headFragment.length} bytes from ${THEME_FRAGMENTS_DIRECTORY_NAME}/${THEME_HEAD_FRAGMENT_FILE_NAME})`);
|
|
186
|
+
else this.logger.info(`Theme head fragment: none (no ${THEME_FRAGMENTS_DIRECTORY_NAME}/${THEME_HEAD_FRAGMENT_FILE_NAME})`);
|
|
187
|
+
if (!this.assetsPath) {
|
|
188
|
+
this.logger.info(`Theme assets: none (no ${THEME_ASSETS_DIRECTORY_NAME}/ directory)`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
let files;
|
|
192
|
+
try {
|
|
193
|
+
files = fs.readdirSync(this.assetsPath, {
|
|
194
|
+
recursive: true,
|
|
195
|
+
withFileTypes: true
|
|
196
|
+
}).filter((entry) => entry.isFile()).map((entry) => path.relative(this.assetsPath, path.join(entry.parentPath, entry.name)).split(path.sep).join("/")).sort();
|
|
197
|
+
} catch {
|
|
198
|
+
files = [];
|
|
199
|
+
}
|
|
200
|
+
this.logger.info(`Theme assets (${files.length}): ${files.join(", ") || "-"}`);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
export { ThemeProvider };
|
|
205
|
+
|
|
206
|
+
//# sourceMappingURL=module.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["AuthupError","normalizeError","read","fs","path","THEME_MANIFEST_REVALIDATE_INTERVAL","THEME_ASSETS_DIRECTORY_NAME","THEME_FRAGMENTS_DIRECTORY_NAME","THEME_HEAD_FRAGMENT_FILE_NAME","THEME_HEAD_FRAGMENT_MAX_LENGTH","THEME_MANIFEST_FILE_NAME","THEME_MANIFEST_VERSION","buildThemeHead","parseThemeManifest","ThemeProvider","directoryPath","manifestPath","fragmentPath","fragmentsEnabled","logger","manifest","headFragment","assetsPath","manifestSignature","fragmentSignature","revalidatedAt","headCache","Map","ctx","join","load","resolveAssetsPath","readSignature","readManifest","readFragment","Date","now","clear","logInventory","getManifest","revalidate","getAssetsPath","getHead","basePath","cached","get","head","version","set","stats","promises","stat","isDirectory","undefined","e","reason","message","filePath","statSync","size","mtimeMs","raw","readFileSync","length","warn","trimmed","trim","revalidateManifest","revalidateFragment","signature","info","tokens","Object","keys","tokensDark","files","readdirSync","recursive","withFileTypes","filter","entry","isFile","map","relative","parentPath","name","split","sep","sort"],"sources":["../../../../../src/adapters/http/ui/theme/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { AuthupError, normalizeError } from '@authup/errors';\nimport type { Logger } from '@authup/server-kit';\nimport { read } from 'locter';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { THEME_MANIFEST_REVALIDATE_INTERVAL } from './constants.ts';\nimport {\n THEME_ASSETS_DIRECTORY_NAME,\n THEME_FRAGMENTS_DIRECTORY_NAME,\n THEME_HEAD_FRAGMENT_FILE_NAME,\n THEME_HEAD_FRAGMENT_MAX_LENGTH,\n THEME_MANIFEST_FILE_NAME,\n THEME_MANIFEST_VERSION,\n buildThemeHead,\n parseThemeManifest,\n} from './contract/index.ts';\nimport type { ThemeManifest } from './contract/index.ts';\nimport type { IThemeProvider, ThemeProviderContext } from './types.ts';\n\n/**\n * Owns the operator theme directory: manifest loading + validation, the\n * realpathed servable asset root, and the memoized head markup.\n *\n * Failure posture is deliberately asymmetric, and inverts the\n * FileProvisioningSource precedent for the request path:\n * - at BOOT an invalid manifest throws, so a typo is reported once, loudly,\n * with the file path and every issue.\n * - AFTER boot a manifest that becomes invalid keeps the last good value and\n * logs. Provisioning seeds authorization data; a theme is decoration, and\n * a broken logo must never take down the login page of an identity\n * provider.\n */\nexport class ThemeProvider implements IThemeProvider {\n protected readonly directoryPath : string;\n\n protected readonly manifestPath : string;\n\n protected readonly fragmentPath : string;\n\n protected readonly fragmentsEnabled : boolean;\n\n protected readonly logger : Logger | undefined;\n\n protected manifest : ThemeManifest | undefined;\n\n protected headFragment : string | undefined;\n\n protected assetsPath : string | undefined;\n\n protected manifestSignature : string | undefined;\n\n protected fragmentSignature : string | undefined;\n\n protected revalidatedAt = 0;\n\n protected headCache = new Map<string, string>();\n\n constructor(ctx: ThemeProviderContext) {\n this.directoryPath = ctx.directoryPath;\n this.fragmentsEnabled = ctx.fragmentsEnabled ?? false;\n this.logger = ctx.logger;\n this.manifestPath = path.join(ctx.directoryPath, THEME_MANIFEST_FILE_NAME);\n this.fragmentPath = path.join(\n ctx.directoryPath,\n THEME_FRAGMENTS_DIRECTORY_NAME,\n THEME_HEAD_FRAGMENT_FILE_NAME,\n );\n }\n\n async load() : Promise<void> {\n this.assetsPath = await this.resolveAssetsPath();\n\n // Stat first: it doubles as the existence probe (no manifest is a\n // valid theme — the operator may ship only a stylesheet) and as the\n // revalidation signature.\n this.manifestSignature = this.readSignature(this.manifestPath);\n if (this.manifestSignature) {\n this.manifest = await parseThemeManifest(await this.readManifest(), this.manifestPath);\n }\n\n if (this.fragmentsEnabled) {\n this.headFragment = this.readFragment();\n this.fragmentSignature = this.readSignature(this.fragmentPath);\n }\n\n this.revalidatedAt = Date.now();\n this.headCache.clear();\n\n this.logInventory();\n }\n\n async getManifest() : Promise<ThemeManifest | undefined> {\n await this.revalidate();\n\n return this.manifest;\n }\n\n async getAssetsPath() : Promise<string | undefined> {\n await this.revalidate();\n\n return this.assetsPath;\n }\n\n async getHead(basePath: string) : Promise<string> {\n // Drives the revalidation for both files.\n const manifest = await this.getManifest();\n\n const cached = this.headCache.get(basePath);\n if (typeof cached === 'string') {\n return cached;\n }\n\n const head = buildThemeHead(\n manifest ?? { version: THEME_MANIFEST_VERSION },\n basePath,\n this.headFragment,\n );\n this.headCache.set(basePath, head);\n\n return head;\n }\n\n // ----------------------------------------------------\n\n /**\n * The mount root is `<root>/assets`, never the theme root, so the\n * manifest is unreachable over HTTP by construction.\n *\n * Returns the LOGICAL path, deliberately not a realpath. A Kubernetes\n * ConfigMap volume is a symlink farm whose update swaps `..data` to a\n * NEW timestamped directory and deletes the old one, so a realpath\n * captured here would dangle after the first update and every asset\n * would 404 until the pod restarts. The asset handler resolves and\n * re-asserts containment per request instead.\n */\n protected async resolveAssetsPath() : Promise<string | undefined> {\n const assetsPath = path.join(this.directoryPath, THEME_ASSETS_DIRECTORY_NAME);\n\n try {\n // stat follows the symlink, so this still rejects a non-directory.\n const stats = await fs.promises.stat(assetsPath);\n\n return stats.isDirectory() ? assetsPath : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Read + decode the manifest through locter, the same loader\n * FileProvisioningSource uses for its operator files. Its JSON reader\n * is a plain read-and-parse with no content cache, so the live reload\n * below still sees an edited file.\n */\n protected async readManifest() : Promise<unknown> {\n try {\n return await read(this.manifestPath);\n } catch (e) {\n const reason = normalizeError(e).message;\n\n throw new AuthupError(\n `The theme manifest \"${this.manifestPath}\" could not be read.\\n ${reason}`,\n );\n }\n }\n\n protected readSignature(filePath: string) : string | undefined {\n try {\n const stats = fs.statSync(filePath);\n\n return `${stats.size}-${stats.mtimeMs}`;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Read `fragments/head.html`. Never called when the operator has not\n * opted in, so dropping the file into the directory does nothing on\n * its own.\n */\n protected readFragment() : string | undefined {\n let raw : string;\n try {\n raw = fs.readFileSync(this.fragmentPath, 'utf-8');\n } catch {\n return undefined;\n }\n\n if (raw.length > THEME_HEAD_FRAGMENT_MAX_LENGTH) {\n this.logger?.warn(\n `The head fragment \"${this.fragmentPath}\" exceeds ${THEME_HEAD_FRAGMENT_MAX_LENGTH} bytes and is ignored.`,\n );\n\n return undefined;\n }\n\n const trimmed = raw.trim();\n\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n /**\n * Re-read the manifest when it changed on disk, at most once per\n * interval. One stat per console render is negligible next to the SSR\n * pass it precedes, and it is what makes an edit visible without a\n * restart (the property every operator asks about first).\n */\n protected async revalidate() : Promise<void> {\n const now = Date.now();\n if (now - this.revalidatedAt < THEME_MANIFEST_REVALIDATE_INTERVAL) {\n return;\n }\n this.revalidatedAt = now;\n\n // Re-resolved with the manifest, not captured at boot: a directory\n // that carried no `assets/` when the process started would otherwise\n // 404 every asset forever, even though the manifest hot-reloads and\n // can start referencing them. One stat, on the interval that already\n // pays for two.\n this.assetsPath = await this.resolveAssetsPath();\n\n await this.revalidateManifest();\n this.revalidateFragment();\n }\n\n protected async revalidateManifest() : Promise<void> {\n const signature = this.readSignature(this.manifestPath);\n if (signature === this.manifestSignature) {\n return;\n }\n\n if (typeof signature === 'undefined') {\n this.manifestSignature = undefined;\n this.manifest = undefined;\n this.headCache.clear();\n\n return;\n }\n\n // Stamped before the parse attempt so a persistently broken file is\n // re-parsed once per edit, not once per render.\n this.manifestSignature = signature;\n\n try {\n this.manifest = await parseThemeManifest(await this.readManifest(), this.manifestPath);\n this.headCache.clear();\n } catch (e) {\n const reason = normalizeError(e).message;\n this.logger?.warn(`The theme manifest changed but is invalid, keeping the previous one.\\n${reason}`);\n }\n }\n\n protected revalidateFragment() : void {\n if (!this.fragmentsEnabled) {\n return;\n }\n\n const signature = this.readSignature(this.fragmentPath);\n if (signature === this.fragmentSignature) {\n return;\n }\n\n this.fragmentSignature = signature;\n this.headFragment = typeof signature === 'undefined' ?\n undefined :\n this.readFragment();\n this.headCache.clear();\n }\n\n /**\n * Always logged. The dominant failure mode of this feature is silence:\n * mount the wrong path and the page looks exactly like an un-themed\n * page. An enumerable boot summary is both the cheapest debugging\n * affordance and the cheapest way to notice a file that should not be\n * in the volume.\n */\n protected logInventory() : void {\n if (!this.logger) {\n return;\n }\n\n this.logger.info(\n `Theme directory: ${this.directoryPath} (theming is EXPERIMENTAL and may change in a minor release)`,\n );\n\n if (this.manifest) {\n const tokens = Object.keys(this.manifest.tokens ?? {}).length;\n const tokensDark = Object.keys(this.manifest.tokensDark ?? {}).length;\n this.logger.info(\n `Theme manifest: loaded (${tokens} token(s), ${tokensDark} dark token(s))`,\n );\n } else {\n this.logger.info(`Theme manifest: none (no ${THEME_MANIFEST_FILE_NAME})`);\n }\n\n if (!this.fragmentsEnabled) {\n this.logger.info('Theme head fragment: disabled (themeFragmentsEnabled is off)');\n } else if (this.headFragment) {\n this.logger.info(\n `Theme head fragment: loaded (${this.headFragment.length} bytes from ${THEME_FRAGMENTS_DIRECTORY_NAME}/${THEME_HEAD_FRAGMENT_FILE_NAME})`,\n );\n } else {\n this.logger.info(\n `Theme head fragment: none (no ${THEME_FRAGMENTS_DIRECTORY_NAME}/${THEME_HEAD_FRAGMENT_FILE_NAME})`,\n );\n }\n\n if (!this.assetsPath) {\n this.logger.info(`Theme assets: none (no ${THEME_ASSETS_DIRECTORY_NAME}/ directory)`);\n\n return;\n }\n\n let files : string[];\n try {\n files = fs.readdirSync(this.assetsPath, { recursive: true, withFileTypes: true })\n .filter((entry) => entry.isFile())\n .map((entry) => path\n .relative(this.assetsPath as string, path.join(entry.parentPath, entry.name))\n .split(path.sep)\n .join('/'))\n .sort();\n } catch {\n files = [];\n }\n\n this.logger.info(`Theme assets (${files.length}): ${files.join(', ') || '-'}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Logger } from '@authup/server-kit';
|
|
2
|
+
import type { ThemeManifest } from './contract/index.ts';
|
|
3
|
+
export type ThemeProviderContext = {
|
|
4
|
+
/**
|
|
5
|
+
* Absolute path of the theme root (normalizeConfig resolves a relative
|
|
6
|
+
* value against the rootPath).
|
|
7
|
+
*/
|
|
8
|
+
directoryPath: string;
|
|
9
|
+
/**
|
|
10
|
+
* Opt in to reading `fragments/head.html`. Default off: the fragment
|
|
11
|
+
* is unsanitized markup served on the IdP origin.
|
|
12
|
+
*/
|
|
13
|
+
fragmentsEnabled?: boolean;
|
|
14
|
+
logger?: Logger;
|
|
15
|
+
};
|
|
16
|
+
export interface IThemeProvider {
|
|
17
|
+
/**
|
|
18
|
+
* Read + validate the manifest and resolve the servable asset root.
|
|
19
|
+
* Throws on an invalid manifest, so a typo fails the boot rather than
|
|
20
|
+
* silently rendering an un-themed page.
|
|
21
|
+
*/
|
|
22
|
+
load(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* The current manifest, re-read when the file changed on disk. Returns
|
|
25
|
+
* undefined when the directory carries no manifest (Layer 2 only).
|
|
26
|
+
*
|
|
27
|
+
* Async because validation runs through validup. The re-read is
|
|
28
|
+
* debounced and the head is memoized, so a steady-state render awaits
|
|
29
|
+
* an already-resolved value.
|
|
30
|
+
*/
|
|
31
|
+
getManifest(): Promise<ThemeManifest | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* The realpathed `<root>/assets` directory, or undefined when absent.
|
|
34
|
+
* Every served path is re-checked against this value.
|
|
35
|
+
*/
|
|
36
|
+
getAssetsPath(): Promise<string | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* The markup injected before `</head>`, memoized per base path.
|
|
39
|
+
*/
|
|
40
|
+
getHead(basePath: string): Promise<string>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/adapters/http/ui/theme/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,MAAM,oBAAoB,GAAG;IAC/B;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,WAAW,cAAc;IAC3B;;;;OAIG;IACH,IAAI,IAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;;;;OAOG;IACH,WAAW,IAAK,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAEnD;;;OAGG;IACH,aAAa,IAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAE9C;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/C"}
|