@better-forge/ui 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +13 -3
- package/dist/api/client.d.ts +42 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +130 -0
- package/dist/api/client.js.map +1 -0
- package/dist/components/ConfirmIdentityModal.d.ts +18 -0
- package/dist/components/ConfirmIdentityModal.d.ts.map +1 -0
- package/dist/components/ConfirmIdentityModal.js +30 -0
- package/dist/components/ConfirmIdentityModal.js.map +1 -0
- package/dist/components/ConfirmModal.d.ts +18 -0
- package/dist/components/ConfirmModal.d.ts.map +1 -0
- package/dist/components/ConfirmModal.js +11 -0
- package/dist/components/ConfirmModal.js.map +1 -0
- package/dist/components/CopyButton.d.ts +11 -0
- package/dist/components/CopyButton.d.ts.map +1 -0
- package/dist/components/CopyButton.js +44 -0
- package/dist/components/CopyButton.js.map +1 -0
- package/dist/components/CredentialSection.d.ts +38 -0
- package/dist/components/CredentialSection.d.ts.map +1 -0
- package/dist/components/CredentialSection.js +69 -0
- package/dist/components/CredentialSection.js.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +18 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +25 -0
- package/dist/components/ErrorBoundary.js.map +1 -0
- package/dist/components/ModalShell.d.ts +58 -0
- package/dist/components/ModalShell.d.ts.map +1 -0
- package/dist/components/ModalShell.js +92 -0
- package/dist/components/ModalShell.js.map +1 -0
- package/dist/components/ResetTwoFactorModal.d.ts +14 -0
- package/dist/components/ResetTwoFactorModal.d.ts.map +1 -0
- package/dist/components/ResetTwoFactorModal.js +16 -0
- package/dist/components/ResetTwoFactorModal.js.map +1 -0
- package/dist/components/SidePanel.d.ts +35 -0
- package/dist/components/SidePanel.d.ts.map +1 -0
- package/dist/components/SidePanel.js +19 -0
- package/dist/components/SidePanel.js.map +1 -0
- package/dist/components/SingleSignOnModal.d.ts +4 -0
- package/dist/components/SingleSignOnModal.d.ts.map +1 -0
- package/dist/components/SingleSignOnModal.js +64 -0
- package/dist/components/SingleSignOnModal.js.map +1 -0
- package/dist/components/SsoConfirmation.d.ts +22 -0
- package/dist/components/SsoConfirmation.d.ts.map +1 -0
- package/dist/components/SsoConfirmation.js +87 -0
- package/dist/components/SsoConfirmation.js.map +1 -0
- package/dist/components/VaultTypePicker.d.ts +13 -0
- package/dist/components/VaultTypePicker.d.ts.map +1 -0
- package/dist/components/VaultTypePicker.js +62 -0
- package/dist/components/VaultTypePicker.js.map +1 -0
- package/dist/components/WhatsNew.d.ts +18 -0
- package/dist/components/WhatsNew.d.ts.map +1 -0
- package/dist/components/WhatsNew.js +50 -0
- package/dist/components/WhatsNew.js.map +1 -0
- package/dist/components/formParts.d.ts +40 -0
- package/dist/components/formParts.d.ts.map +1 -0
- package/dist/components/formParts.js +31 -0
- package/dist/components/formParts.js.map +1 -0
- package/dist/components/ui/Alert.d.ts +15 -0
- package/dist/components/ui/Alert.d.ts.map +1 -0
- package/dist/components/ui/Alert.js +14 -0
- package/dist/components/ui/Alert.js.map +1 -0
- package/dist/components/ui/DataTable.d.ts +52 -0
- package/dist/components/ui/DataTable.d.ts.map +1 -0
- package/dist/components/ui/DataTable.js +112 -0
- package/dist/components/ui/DataTable.js.map +1 -0
- package/dist/components/ui/Field.d.ts +27 -0
- package/dist/components/ui/Field.d.ts.map +1 -0
- package/dist/components/ui/Field.js +25 -0
- package/dist/components/ui/Field.js.map +1 -0
- package/dist/components/ui/PageHeader.d.ts +18 -0
- package/dist/components/ui/PageHeader.d.ts.map +1 -0
- package/dist/components/ui/PageHeader.js +10 -0
- package/dist/components/ui/PageHeader.js.map +1 -0
- package/dist/components/ui/Pagination.d.ts +21 -0
- package/dist/components/ui/Pagination.d.ts.map +1 -0
- package/dist/components/ui/Pagination.js +10 -0
- package/dist/components/ui/Pagination.js.map +1 -0
- package/dist/components/ui/QuickStartPanel.d.ts +21 -0
- package/dist/components/ui/QuickStartPanel.d.ts.map +1 -0
- package/dist/components/ui/QuickStartPanel.js +11 -0
- package/dist/components/ui/QuickStartPanel.js.map +1 -0
- package/dist/components/ui/StateViews.d.ts +50 -0
- package/dist/components/ui/StateViews.d.ts.map +1 -0
- package/dist/components/ui/StateViews.js +33 -0
- package/dist/components/ui/StateViews.js.map +1 -0
- package/dist/components/ui/Switch.d.ts +16 -0
- package/dist/components/ui/Switch.d.ts.map +1 -0
- package/dist/components/ui/Switch.js +15 -0
- package/dist/components/ui/Switch.js.map +1 -0
- package/dist/components/ui/Toast.d.ts +20 -0
- package/dist/components/ui/Toast.d.ts.map +1 -0
- package/dist/components/ui/Toast.js +46 -0
- package/dist/components/ui/Toast.js.map +1 -0
- package/dist/components/ui/index.d.ts +10 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/index.js +10 -0
- package/dist/components/ui/index.js.map +1 -0
- package/dist/config.d.ts +46 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/useDarkMode.d.ts +2 -0
- package/dist/hooks/useDarkMode.d.ts.map +1 -0
- package/dist/hooks/useDarkMode.js +37 -0
- package/dist/hooks/useDarkMode.js.map +1 -0
- package/dist/hooks/useDebouncedValue.d.ts +6 -0
- package/dist/hooks/useDebouncedValue.d.ts.map +1 -0
- package/dist/hooks/useDebouncedValue.js +19 -0
- package/dist/hooks/useDebouncedValue.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/changelog.d.ts +32 -0
- package/dist/lib/changelog.d.ts.map +1 -0
- package/dist/lib/changelog.js +71 -0
- package/dist/lib/changelog.js.map +1 -0
- package/dist/lib/dateFormat.d.ts +24 -0
- package/dist/lib/dateFormat.d.ts.map +1 -0
- package/dist/lib/dateFormat.js +67 -0
- package/dist/lib/dateFormat.js.map +1 -0
- package/dist/navigation.d.ts +5 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +7 -0
- package/dist/navigation.js.map +1 -0
- package/dist/pages/ApiTokens.d.ts +9 -0
- package/dist/pages/ApiTokens.d.ts.map +1 -0
- package/dist/pages/ApiTokens.js +116 -0
- package/dist/pages/ApiTokens.js.map +1 -0
- package/dist/pages/AuditLog.d.ts +14 -0
- package/dist/pages/AuditLog.d.ts.map +1 -0
- package/dist/pages/AuditLog.js +104 -0
- package/dist/pages/AuditLog.js.map +1 -0
- package/dist/pages/Backups.d.ts +2 -0
- package/dist/pages/Backups.d.ts.map +1 -0
- package/dist/pages/Backups.js +118 -0
- package/dist/pages/Backups.js.map +1 -0
- package/dist/pages/ChangePassword.d.ts +2 -0
- package/dist/pages/ChangePassword.d.ts.map +1 -0
- package/dist/pages/ChangePassword.js +40 -0
- package/dist/pages/ChangePassword.js.map +1 -0
- package/dist/pages/Login.d.ts +12 -0
- package/dist/pages/Login.d.ts.map +1 -0
- package/dist/pages/Login.js +69 -0
- package/dist/pages/Login.js.map +1 -0
- package/dist/pages/Settings.d.ts +2 -0
- package/dist/pages/Settings.d.ts.map +1 -0
- package/dist/pages/Settings.js +149 -0
- package/dist/pages/Settings.js.map +1 -0
- package/dist/pages/Setup.d.ts +2 -0
- package/dist/pages/Setup.d.ts.map +1 -0
- package/dist/pages/Setup.js +113 -0
- package/dist/pages/Setup.js.map +1 -0
- package/dist/pages/SsoConfirm.d.ts +3 -0
- package/dist/pages/SsoConfirm.d.ts.map +1 -0
- package/dist/pages/SsoConfirm.js +20 -0
- package/dist/pages/SsoConfirm.js.map +1 -0
- package/dist/pages/SsoTestResult.d.ts +2 -0
- package/dist/pages/SsoTestResult.d.ts.map +1 -0
- package/dist/pages/SsoTestResult.js +49 -0
- package/dist/pages/SsoTestResult.js.map +1 -0
- package/dist/pages/Users.d.ts +22 -0
- package/dist/pages/Users.d.ts.map +1 -0
- package/dist/pages/Users.js +124 -0
- package/dist/pages/Users.js.map +1 -0
- package/dist/pages/Vault.d.ts +2 -0
- package/dist/pages/Vault.d.ts.map +1 -0
- package/dist/pages/Vault.js +372 -0
- package/dist/pages/Vault.js.map +1 -0
- package/dist/shell/AdminShell.d.ts +46 -0
- package/dist/shell/AdminShell.d.ts.map +1 -0
- package/dist/shell/AdminShell.js +76 -0
- package/dist/shell/AdminShell.js.map +1 -0
- package/dist/shell/guards.d.ts +28 -0
- package/dist/shell/guards.d.ts.map +1 -0
- package/dist/shell/guards.js +71 -0
- package/dist/shell/guards.js.map +1 -0
- package/icons.json +170 -0
- package/package.json +71 -6
- package/src/api/client.ts +139 -0
- package/src/components/ConfirmIdentityModal.tsx +82 -0
- package/src/components/ConfirmModal.tsx +83 -0
- package/src/components/CopyButton.tsx +71 -0
- package/src/components/CredentialSection.tsx +270 -0
- package/src/components/ErrorBoundary.tsx +47 -0
- package/src/components/ModalShell.tsx +160 -0
- package/src/components/ResetTwoFactorModal.tsx +74 -0
- package/src/components/SidePanel.tsx +96 -0
- package/src/components/SingleSignOnModal.tsx +161 -0
- package/src/components/SsoConfirmation.tsx +96 -0
- package/src/components/VaultTypePicker.tsx +131 -0
- package/src/components/WhatsNew.tsx +84 -0
- package/src/components/formParts.tsx +59 -0
- package/src/components/ui/Alert.tsx +45 -0
- package/src/components/ui/DataTable.tsx +225 -0
- package/src/components/ui/Field.tsx +63 -0
- package/src/components/ui/PageHeader.tsx +36 -0
- package/src/components/ui/Pagination.tsx +37 -0
- package/src/components/ui/QuickStartPanel.tsx +57 -0
- package/src/components/ui/StateViews.tsx +135 -0
- package/src/components/ui/Switch.tsx +65 -0
- package/src/components/ui/Toast.tsx +104 -0
- package/src/components/ui/index.ts +9 -0
- package/src/config.ts +46 -0
- package/src/hooks/useDarkMode.ts +38 -0
- package/src/hooks/useDebouncedValue.ts +22 -0
- package/src/index.ts +42 -0
- package/src/lib/changelog.ts +93 -0
- package/src/lib/dateFormat.ts +90 -0
- package/src/navigation.ts +6 -0
- package/src/pages/ApiTokens.tsx +361 -0
- package/src/pages/AuditLog.tsx +388 -0
- package/src/pages/Backups.tsx +170 -0
- package/src/pages/ChangePassword.tsx +84 -0
- package/src/pages/Login.tsx +275 -0
- package/src/pages/Settings.tsx +264 -0
- package/src/pages/Setup.tsx +438 -0
- package/src/pages/SsoConfirm.tsx +26 -0
- package/src/pages/SsoTestResult.tsx +104 -0
- package/src/pages/Users.tsx +426 -0
- package/src/pages/Vault.tsx +1036 -0
- package/src/shell/AdminShell.tsx +256 -0
- package/src/shell/guards.tsx +88 -0
- package/styles/base.css +521 -0
- package/styles/fonts/inter-latin-1.woff2 +0 -0
- package/styles/fonts/inter-latin-ext.woff2 +0 -0
- package/styles/fonts/jetbrains-mono-latin-3.woff2 +0 -0
- package/styles/fonts/jetbrains-mono-latin-ext-2.woff2 +0 -0
- package/styles/fonts/manrope-latin-5.woff2 +0 -0
- package/styles/fonts/manrope-latin-ext-4.woff2 +0 -0
- package/styles/fonts/material-symbols-outlined-fill.woff2 +0 -0
- package/styles/icons.css +4 -0
- package/tailwind-preset.js +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BetterStatusPage contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @better-forge/ui
|
|
2
|
+
|
|
3
|
+
The interface the Better products share: React components, the API client, hooks and the look.
|
|
4
|
+
|
|
5
|
+
- **Components and helpers**: `Modal`, `ConfirmModal`, `CopyButton`, `SidePanelFrame`, the form and state components (`Alert`, `Field`, `Switch`, `Toast`, ...), `useDarkMode`, `useDebouncedValue`, date formatting and the changelog parser.
|
|
6
|
+
- **API client** (`api`): cookie session, CSRF header, typed errors.
|
|
7
|
+
- **Look**: `@better-forge/ui/styles.css` (the Material 3 colour tokens for light and dark, base styles, buttons, inputs, the navigation, bundled fonts) and `@better-forge/ui/tailwind-preset` (colours backed by those variables, type faces). An application imports the stylesheet first in its own CSS (`@import '@better-forge/ui/styles.css';`), uses the preset in its Tailwind config and adds `packages/ui/src` to Tailwind's `content`.
|
|
8
|
+
- **`configureUi({ storagePrefix, apiBase, csrfCookie, basePath, redirectAfterSignIn })`** tells the interface what application it is part of: what it keeps in the browser, where the API answers and where the sign-in page is. Call it before anything is rendered.
|
|
9
|
+
|
|
10
|
+
- **Pages**: sign-in and first-run setup, change password, settings, users, API tokens, audit log, vault, backups and the SSO test result. What is the product's comes in as props (roles, the API's permissions, audit entity names, the sign-in copy) or as `texts` and `brand` of `configureUi`.
|
|
11
|
+
- **Shell**: `createAdminShell(config)` is the signed-in layout (sidebar and drawer, role-filtered sections, footer links, what's new).
|
|
12
|
+
|
|
13
|
+
Depends on `@better-forge/contracts`; React 19, `react-dom`, `react-router-dom`, `@tanstack/react-query` and `clsx` are peer dependencies.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface AuthUser {
|
|
2
|
+
userId: number;
|
|
3
|
+
email: string;
|
|
4
|
+
role: string;
|
|
5
|
+
mustChangePassword: boolean;
|
|
6
|
+
twoFactorEnabled: boolean;
|
|
7
|
+
authMethod?: 'password' | 'oidc';
|
|
8
|
+
}
|
|
9
|
+
export declare function setSession(user: AuthUser): void;
|
|
10
|
+
export declare function clearSession(): void;
|
|
11
|
+
export declare function getCurrentUser(): AuthUser | null;
|
|
12
|
+
export declare function isAuthenticated(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Where a signed-in user starts. One who does not belong in this interface goes where the application sends them
|
|
15
|
+
* (which may also have them replace a temporary password); everyone else replaces it here first.
|
|
16
|
+
*/
|
|
17
|
+
export declare function enterAfterSignIn(user: AuthUser, navigate: (path: string) => void): void;
|
|
18
|
+
export declare function mustChangePassword(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* A failed API call: `message` is the server's error text, `code` its machine-readable code when it sent one, and
|
|
21
|
+
* `body` the whole JSON answer, for the endpoints that say more than a message (a list of problems, say).
|
|
22
|
+
*/
|
|
23
|
+
export declare class ApiError extends Error {
|
|
24
|
+
readonly status: number;
|
|
25
|
+
readonly code?: string | undefined;
|
|
26
|
+
readonly body?: unknown | undefined;
|
|
27
|
+
constructor(message: string, status: number, code?: string | undefined, body?: unknown | undefined);
|
|
28
|
+
}
|
|
29
|
+
export declare const api: {
|
|
30
|
+
get: <T>(path: string) => Promise<T>;
|
|
31
|
+
/** GET an answer that is text, for example a YAML document. */
|
|
32
|
+
getText: (path: string) => Promise<string>;
|
|
33
|
+
post: <T>(path: string, body?: unknown) => Promise<T>;
|
|
34
|
+
patch: <T>(path: string, body: unknown) => Promise<T>;
|
|
35
|
+
put: <T>(path: string, body: unknown) => Promise<T>;
|
|
36
|
+
delete: (path: string) => Promise<void>;
|
|
37
|
+
upload: <T>(path: string, formData: FormData) => Promise<T>;
|
|
38
|
+
/** POST a text body as it is, for example a YAML file, with the given content type. */
|
|
39
|
+
postText: <T>(path: string, text: string, contentType: string) => Promise<T>;
|
|
40
|
+
download: (path: string, filename: string) => Promise<void>;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,kBAAkB,EAAE,OAAO,CAAA;IAC3B,gBAAgB,EAAE,OAAO,CAAA;IACzB,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,CAAA;CACjC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAG/C;AAED,wBAAgB,YAAY,IAAI,IAAI,CAGnC;AAED,wBAAgB,cAAc,IAAI,QAAQ,GAAG,IAAI,CAKhD;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAKvF;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACJ,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO;gBAAzF,OAAO,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM,EAAW,IAAI,CAAC,EAAE,MAAM,YAAA,EAAW,IAAI,CAAC,EAAE,OAAO,YAAA;CAItG;AAyDD,eAAO,MAAM,GAAG;UACR,CAAC,QAAQ,MAAM;IACrB,+DAA+D;oBAC/C,MAAM;WACf,CAAC,QAAQ,MAAM,SAAQ,OAAO;YAC7B,CAAC,QAAQ,MAAM,QAAQ,OAAO;UAChC,CAAC,QAAQ,MAAM,QAAQ,OAAO;mBACrB,MAAM;aACZ,CAAC,QAAQ,MAAM,YAAY,QAAQ;IAC5C,uFAAuF;eAC5E,CAAC,QAAQ,MAAM,QAAQ,MAAM,eAAe,MAAM;qBACtC,MAAM,YAAY,MAAM;CAUhD,CAAA"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { safeSessionStorage } from '@better-forge/contracts';
|
|
2
|
+
import { storageKey, uiConfig } from '../config';
|
|
3
|
+
import { navigation } from '../navigation';
|
|
4
|
+
const userKey = () => storageKey('auth-user');
|
|
5
|
+
export function setSession(user) {
|
|
6
|
+
safeSessionStorage.set(userKey(), JSON.stringify(user));
|
|
7
|
+
window.dispatchEvent(new CustomEvent(storageKey('auth-change')));
|
|
8
|
+
}
|
|
9
|
+
export function clearSession() {
|
|
10
|
+
safeSessionStorage.remove(userKey());
|
|
11
|
+
window.dispatchEvent(new CustomEvent(storageKey('auth-change')));
|
|
12
|
+
}
|
|
13
|
+
export function getCurrentUser() {
|
|
14
|
+
try {
|
|
15
|
+
const stored = safeSessionStorage.get(userKey());
|
|
16
|
+
return stored ? JSON.parse(stored) : null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function isAuthenticated() {
|
|
23
|
+
return getCurrentUser() !== null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Where a signed-in user starts. One who does not belong in this interface goes where the application sends them
|
|
27
|
+
* (which may also have them replace a temporary password); everyone else replaces it here first.
|
|
28
|
+
*/
|
|
29
|
+
export function enterAfterSignIn(user, navigate) {
|
|
30
|
+
const elsewhere = uiConfig().redirectAfterSignIn?.(user) ?? null;
|
|
31
|
+
if (elsewhere)
|
|
32
|
+
navigation.assign(elsewhere);
|
|
33
|
+
else if (user.mustChangePassword)
|
|
34
|
+
navigate(`${uiConfig().basePath}/change-password`);
|
|
35
|
+
else
|
|
36
|
+
navigate(`${uiConfig().basePath}/`);
|
|
37
|
+
}
|
|
38
|
+
export function mustChangePassword() {
|
|
39
|
+
return !!getCurrentUser()?.mustChangePassword;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A failed API call: `message` is the server's error text, `code` its machine-readable code when it sent one, and
|
|
43
|
+
* `body` the whole JSON answer, for the endpoints that say more than a message (a list of problems, say).
|
|
44
|
+
*/
|
|
45
|
+
export class ApiError extends Error {
|
|
46
|
+
status;
|
|
47
|
+
code;
|
|
48
|
+
body;
|
|
49
|
+
constructor(message, status, code, body) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.status = status;
|
|
52
|
+
this.code = code;
|
|
53
|
+
this.body = body;
|
|
54
|
+
this.name = 'ApiError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function cookie(name) {
|
|
58
|
+
const prefix = `${encodeURIComponent(name)}=`;
|
|
59
|
+
const part = document.cookie.split('; ').find((item) => item.startsWith(prefix));
|
|
60
|
+
return part ? decodeURIComponent(part.slice(prefix.length)) : null;
|
|
61
|
+
}
|
|
62
|
+
async function request(method, path, body, isFormData = false,
|
|
63
|
+
/** Sends `body` (a string) as it is with this content type, instead of as JSON. */
|
|
64
|
+
contentType,
|
|
65
|
+
/** Reads the answer as text instead of JSON. */
|
|
66
|
+
asText = false) {
|
|
67
|
+
const headers = {};
|
|
68
|
+
if (contentType)
|
|
69
|
+
headers['Content-Type'] = contentType;
|
|
70
|
+
else if (!isFormData && body !== undefined)
|
|
71
|
+
headers['Content-Type'] = 'application/json';
|
|
72
|
+
if (!['GET', 'HEAD', 'OPTIONS'].includes(method)) {
|
|
73
|
+
const csrf = cookie(uiConfig().csrfCookie);
|
|
74
|
+
if (csrf)
|
|
75
|
+
headers['X-CSRF-Token'] = csrf;
|
|
76
|
+
}
|
|
77
|
+
const fetchBody = contentType ? body : isFormData ? body : body !== undefined ? JSON.stringify(body) : null;
|
|
78
|
+
const res = await fetch(`${uiConfig().apiBase}${path}`, {
|
|
79
|
+
method,
|
|
80
|
+
headers,
|
|
81
|
+
credentials: 'same-origin',
|
|
82
|
+
...(fetchBody !== null ? { body: fetchBody } : {}),
|
|
83
|
+
});
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
86
|
+
if (res.status === 403 && err.code === 'PASSWORD_CHANGE_REQUIRED') {
|
|
87
|
+
const user = getCurrentUser();
|
|
88
|
+
if (user && !user.mustChangePassword)
|
|
89
|
+
setSession({ ...user, mustChangePassword: true });
|
|
90
|
+
if (!window.location.pathname.includes('/change-password')) {
|
|
91
|
+
window.location.href = `${uiConfig().basePath}/change-password`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (res.status === 401) {
|
|
95
|
+
clearSession();
|
|
96
|
+
const isLoginRequest = path === '/auth/login' || path === '/auth/2fa/verify';
|
|
97
|
+
if (!isLoginRequest && !window.location.pathname.includes('/login')) {
|
|
98
|
+
window.location.href = `${uiConfig().basePath}/login`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
throw new ApiError(err.error ?? res.statusText, res.status, err.code, err);
|
|
102
|
+
}
|
|
103
|
+
if (res.status === 204)
|
|
104
|
+
return undefined;
|
|
105
|
+
return (asText ? res.text() : res.json());
|
|
106
|
+
}
|
|
107
|
+
export const api = {
|
|
108
|
+
get: (path) => request('GET', path),
|
|
109
|
+
/** GET an answer that is text, for example a YAML document. */
|
|
110
|
+
getText: (path) => request('GET', path, undefined, false, undefined, true),
|
|
111
|
+
post: (path, body = {}) => request('POST', path, body),
|
|
112
|
+
patch: (path, body) => request('PATCH', path, body),
|
|
113
|
+
put: (path, body) => request('PUT', path, body),
|
|
114
|
+
delete: (path) => request('DELETE', path),
|
|
115
|
+
upload: (path, formData) => request('POST', path, formData, true),
|
|
116
|
+
/** POST a text body as it is, for example a YAML file, with the given content type. */
|
|
117
|
+
postText: (path, text, contentType) => request('POST', path, text, false, contentType),
|
|
118
|
+
download: async (path, filename) => {
|
|
119
|
+
const res = await fetch(`${uiConfig().apiBase}${path}`, { credentials: 'same-origin' });
|
|
120
|
+
if (!res.ok)
|
|
121
|
+
throw new Error('Download failed');
|
|
122
|
+
const url = URL.createObjectURL(await res.blob());
|
|
123
|
+
const link = document.createElement('a');
|
|
124
|
+
link.href = url;
|
|
125
|
+
link.download = filename;
|
|
126
|
+
link.click();
|
|
127
|
+
URL.revokeObjectURL(url);
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface ConfirmIdentityModalProps {
|
|
2
|
+
title: string;
|
|
3
|
+
message: string;
|
|
4
|
+
confirmLabel: string;
|
|
5
|
+
pendingLabel?: string;
|
|
6
|
+
danger?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Runs the action. `currentPassword` is undefined in an SSO session, which is confirmed at the identity
|
|
9
|
+
* provider instead. A rejection is shown in the dialog, which stays open for another try.
|
|
10
|
+
*/
|
|
11
|
+
action: (currentPassword: string | undefined) => Promise<unknown>;
|
|
12
|
+
/** Called when the action succeeded or the user gave up. */
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
}
|
|
15
|
+
/** Asks the signed-in user to prove it is them (password, or SSO) before a sensitive action goes through. */
|
|
16
|
+
export declare function ConfirmIdentityModal({ title, message, confirmLabel, pendingLabel, danger, action, onClose }: ConfirmIdentityModalProps): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=ConfirmIdentityModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmIdentityModal.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmIdentityModal.tsx"],"names":[],"mappings":"AAKA,UAAU,yBAAyB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,MAAM,EAAE,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACjE,4DAA4D;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,6GAA6G;AAC7G,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,YAAyB,EAAE,MAAc,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,yBAAyB,+BA4D3J"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId, useState } from 'react';
|
|
3
|
+
import { ModalFooter, ModalHeader, ModalShell } from './ModalShell';
|
|
4
|
+
import { SsoConfirmationNote, isSsoSession, useSsoConfirmation } from './SsoConfirmation';
|
|
5
|
+
import { Alert } from './ui';
|
|
6
|
+
/** Asks the signed-in user to prove it is them (password, or SSO) before a sensitive action goes through. */
|
|
7
|
+
export function ConfirmIdentityModal({ title, message, confirmLabel, pendingLabel = 'Working…', danger = false, action, onClose }) {
|
|
8
|
+
const sso = isSsoSession();
|
|
9
|
+
const ssoConfirmation = useSsoConfirmation();
|
|
10
|
+
const [currentPassword, setCurrentPassword] = useState('');
|
|
11
|
+
const [pending, setPending] = useState(false);
|
|
12
|
+
const [error, setError] = useState('');
|
|
13
|
+
const titleId = useId();
|
|
14
|
+
const canConfirm = !pending && (sso || currentPassword.length > 0);
|
|
15
|
+
async function confirm() {
|
|
16
|
+
setPending(true);
|
|
17
|
+
setError('');
|
|
18
|
+
try {
|
|
19
|
+
await ssoConfirmation.run(() => action(sso ? undefined : currentPassword));
|
|
20
|
+
onClose();
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
setError(err instanceof Error && err.message ? err.message : 'Something went wrong. Try again.');
|
|
24
|
+
setPending(false);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return (_jsx(ModalShell, { onClose: pending ? undefined : onClose, children: _jsxs("form", { role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, "aria-busy": pending || undefined, className: "rounded-2xl w-full max-w-md", style: { background: 'var(--m3-surface-container-low)', border: '1px solid var(--m3-outline-variant)' }, onSubmit: (event) => { event.preventDefault(); if (canConfirm)
|
|
28
|
+
void confirm(); }, children: [_jsx(ModalHeader, { icon: "shield_lock", titleId: titleId, title: title, onClose: pending ? () => { } : onClose }), _jsxs("div", { className: "p-6 space-y-4", children: [_jsx("p", { className: "text-sm", style: { color: 'var(--m3-on-surface-variant)' }, children: message }), error && _jsx(Alert, { tone: "error", children: error }), sso ? _jsx(SsoConfirmationNote, { confirmation: ssoConfirmation }) : (_jsxs("label", { className: "block space-y-2 text-sm", style: { color: 'var(--m3-on-surface-variant)' }, children: [_jsx("span", { children: "Your current password" }), _jsx("input", { type: "password", autoComplete: "current-password", autoFocus: true, value: currentPassword, disabled: pending, onChange: (event) => setCurrentPassword(event.target.value), className: "input-sig w-full" })] }))] }), _jsxs(ModalFooter, { children: [_jsx("button", { type: "button", onClick: onClose, disabled: pending, className: "btn btn-secondary", children: "Cancel" }), _jsx("button", { type: "submit", disabled: !canConfirm, className: `btn ${danger ? 'btn-danger' : 'btn-primary'}`, children: pending ? pendingLabel : confirmLabel })] })] }) }));
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=ConfirmIdentityModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmIdentityModal.js","sourceRoot":"","sources":["../../src/components/ConfirmIdentityModal.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface ConfirmModalProps {
|
|
2
|
+
title: string;
|
|
3
|
+
message: string;
|
|
4
|
+
confirmLabel?: string | undefined;
|
|
5
|
+
danger?: boolean | undefined;
|
|
6
|
+
confirmationText?: string | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* While true the modal stays open with both buttons disabled and the confirm button reading
|
|
9
|
+
* `pendingLabel`, so callers can keep it up until their mutation settles and close it in onSuccess.
|
|
10
|
+
*/
|
|
11
|
+
pending?: boolean | undefined;
|
|
12
|
+
pendingLabel?: string | undefined;
|
|
13
|
+
onConfirm: () => void;
|
|
14
|
+
onCancel: () => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function ConfirmModal({ title, message, confirmLabel, danger, confirmationText, pending, pendingLabel, onConfirm, onCancel, }: ConfirmModalProps): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=ConfirmModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmModal.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmModal.tsx"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAO,EACP,YAAuB,EACvB,MAAa,EACb,gBAAgB,EAChB,OAAe,EACf,YAAyB,EACzB,SAAS,EACT,QAAQ,GACT,EAAE,iBAAiB,+BAqDnB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId, useState } from 'react';
|
|
3
|
+
import { ModalFooter, ModalHeader, ModalShell } from './ModalShell';
|
|
4
|
+
export function ConfirmModal({ title, message, confirmLabel = 'Delete', danger = true, confirmationText, pending = false, pendingLabel = 'Working…', onConfirm, onCancel, }) {
|
|
5
|
+
const [typedConfirmation, setTypedConfirmation] = useState('');
|
|
6
|
+
const titleId = useId();
|
|
7
|
+
const messageId = useId();
|
|
8
|
+
const canConfirm = (!confirmationText || typedConfirmation === confirmationText) && !pending;
|
|
9
|
+
return (_jsx(ModalShell, { onClose: pending ? undefined : onCancel, children: _jsxs("div", { role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, "aria-describedby": messageId, "aria-busy": pending || undefined, className: "rounded-2xl w-full max-w-sm", style: { background: 'var(--m3-surface-container-low)', border: '1px solid var(--m3-outline-variant)' }, children: [_jsx(ModalHeader, { icon: danger ? 'warning' : 'help', titleId: titleId, title: title, onClose: pending ? () => { } : onCancel }), _jsxs("div", { className: "p-6 space-y-4", children: [_jsx("p", { id: messageId, className: "text-sm", style: { color: 'var(--m3-on-surface-variant)' }, children: message }), confirmationText && (_jsxs("label", { className: "block space-y-2 text-sm", style: { color: 'var(--m3-on-surface-variant)' }, children: [_jsxs("span", { children: ["Type ", _jsx("strong", { className: "font-mono", children: confirmationText }), " to confirm:"] }), _jsx("input", { autoFocus: true, value: typedConfirmation, disabled: pending, autoComplete: "off", spellCheck: false, onChange: (event) => setTypedConfirmation(event.target.value), className: "input-m3" })] }))] }), _jsxs(ModalFooter, { children: [_jsx("button", { type: "button", onClick: onCancel, disabled: pending, className: "btn btn-secondary", children: "Cancel" }), _jsx("button", { type: "button", onClick: onConfirm, disabled: !canConfirm, className: `btn ${danger ? 'btn-danger' : 'btn-primary'}`, children: pending ? pendingLabel : confirmLabel })] })] }) }));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ConfirmModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmModal.js","sourceRoot":"","sources":["../../src/components/ConfirmModal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAkBnE,MAAM,UAAU,YAAY,CAAC,EAC3B,KAAK,EACL,OAAO,EACP,YAAY,GAAG,QAAQ,EACvB,MAAM,GAAG,IAAI,EACb,gBAAgB,EAChB,OAAO,GAAG,KAAK,EACf,YAAY,GAAG,UAAU,EACzB,SAAS,EACT,QAAQ,GACU;IAClB,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC9D,MAAM,OAAO,GAAG,KAAK,EAAE,CAAA;IACvB,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IACzB,MAAM,UAAU,GAAG,CAAC,CAAC,gBAAgB,IAAI,iBAAiB,KAAK,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAA;IAE5F,OAAO,CACL,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,YACjD,eACE,IAAI,EAAC,QAAQ,gBACF,MAAM,qBACA,OAAO,sBACN,SAAS,eAChB,OAAO,IAAI,SAAS,EAC/B,SAAS,EAAC,6BAA6B,EACvC,KAAK,EAAE,EAAE,UAAU,EAAE,iCAAiC,EAAE,MAAM,EAAE,qCAAqC,EAAE,aAEvG,KAAC,WAAW,IAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAI,EAC1H,eAAK,SAAS,EAAC,eAAe,aAC9B,YAAG,EAAE,EAAE,SAAS,EAAE,SAAS,EAAC,SAAS,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,8BAA8B,EAAE,YACnF,OAAO,GACN,EACH,gBAAgB,IAAI,CACnB,iBAAO,SAAS,EAAC,yBAAyB,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,8BAA8B,EAAE,aACzF,oCAAW,iBAAQ,SAAS,EAAC,WAAW,YAAE,gBAAgB,GAAU,oBAAmB,EACvF,gBACE,SAAS,QACT,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,OAAO,EACjB,YAAY,EAAC,KAAK,EAClB,UAAU,EAAE,KAAK,EACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAC7D,SAAS,EAAC,UAAU,GACpB,IACI,CACT,IACK,EACN,MAAC,WAAW,eACV,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAC,mBAAmB,uBAEhF,EACT,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,OAAO,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,YAExD,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,GAC/B,IACG,IACV,GACK,CACd,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface CopyButtonProps {
|
|
2
|
+
value: string;
|
|
3
|
+
label?: string | undefined;
|
|
4
|
+
copiedLabel?: string | undefined;
|
|
5
|
+
failedLabel?: string | undefined;
|
|
6
|
+
/** Show only the icon; the label still names the button for assistive technology. */
|
|
7
|
+
iconOnly?: boolean | undefined;
|
|
8
|
+
}
|
|
9
|
+
export declare function CopyButton({ value, label, copiedLabel, failedLabel, iconOnly }: CopyButtonProps): import("react").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=CopyButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyButton.d.ts","sourceRoot":"","sources":["../../src/components/CopyButton.tsx"],"names":[],"mappings":"AAEA,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,qFAAqF;IACrF,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAwBD,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,KAAc,EAAE,WAAuB,EAAE,WAA2B,EAAE,QAAgB,EAAE,EAAE,eAAe,+BAqC5I"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
/** Clipboard API first; outside a secure context (plain-HTTP deployments) fall back to a hidden textarea. */
|
|
4
|
+
async function writeClipboard(value) {
|
|
5
|
+
if (navigator.clipboard?.writeText) {
|
|
6
|
+
await navigator.clipboard.writeText(value);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const textarea = document.createElement('textarea');
|
|
10
|
+
textarea.value = value;
|
|
11
|
+
textarea.setAttribute('readonly', '');
|
|
12
|
+
textarea.style.position = 'fixed';
|
|
13
|
+
textarea.style.opacity = '0';
|
|
14
|
+
document.body.appendChild(textarea);
|
|
15
|
+
textarea.select();
|
|
16
|
+
try {
|
|
17
|
+
if (!document.execCommand('copy'))
|
|
18
|
+
throw new Error('Copy command was rejected');
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
textarea.remove();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function CopyButton({ value, label = 'Copy', copiedLabel = 'Copied!', failedLabel = 'Copy failed', iconOnly = false }) {
|
|
25
|
+
const [state, setState] = useState('idle');
|
|
26
|
+
const timer = useRef(undefined);
|
|
27
|
+
useEffect(() => () => clearTimeout(timer.current), []);
|
|
28
|
+
async function copy() {
|
|
29
|
+
let next;
|
|
30
|
+
try {
|
|
31
|
+
await writeClipboard(value);
|
|
32
|
+
next = 'copied';
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
next = 'failed';
|
|
36
|
+
}
|
|
37
|
+
setState(next);
|
|
38
|
+
clearTimeout(timer.current);
|
|
39
|
+
timer.current = setTimeout(() => setState('idle'), 2000);
|
|
40
|
+
}
|
|
41
|
+
const text = state === 'copied' ? copiedLabel : state === 'failed' ? failedLabel : label;
|
|
42
|
+
return (_jsxs("button", { type: "button", onClick: () => void copy(), "aria-label": text, title: iconOnly ? text : undefined, className: "copy-feedback-button flex items-center gap-1.5 text-xs px-3 py-2 rounded-lg font-semibold", "data-copied": state === 'copied' || undefined, "data-failed": state === 'failed' || undefined, children: [_jsx("span", { className: "material-symbols-outlined", "aria-hidden": "true", style: { fontSize: '14px' }, children: state === 'copied' ? 'check' : state === 'failed' ? 'error' : 'content_copy' }), !iconOnly && _jsx("span", { "aria-live": "polite", children: text })] }));
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=CopyButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyButton.js","sourceRoot":"","sources":["../../src/components/CopyButton.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { VaultRef } from '@better-forge/contracts';
|
|
3
|
+
import { type VaultPickerProps } from './formParts';
|
|
4
|
+
interface CredentialSectionProps extends VaultPickerProps {
|
|
5
|
+
vault: VaultRef | undefined;
|
|
6
|
+
onVaultChange: (v: VaultRef | undefined) => void;
|
|
7
|
+
mappingFields: {
|
|
8
|
+
key: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}[];
|
|
11
|
+
/** Optional note shown at the top of the vault/direct section */
|
|
12
|
+
note?: string;
|
|
13
|
+
/** What a `value` secret is used as; defaults to the password / client secret. */
|
|
14
|
+
valueLabel?: string;
|
|
15
|
+
/** Replaces the note shown for a User / Password secret when its username and password are not a username and password. */
|
|
16
|
+
userpassNote?: string;
|
|
17
|
+
/** Restricts the secret dropdown to these types; every type is offered when omitted. */
|
|
18
|
+
secretTypes?: string[];
|
|
19
|
+
/** Direct-input fields — rendered only when not using vault */
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wraps a set of credential fields with a Direct / Vault source toggle.
|
|
24
|
+
* In direct mode: shows a warning banner + the credential inputs (children).
|
|
25
|
+
* In vault mode: shows vault → secret dropdowns + JSON field mapping if needed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function CredentialSection({ vault, onVaultChange, vaults, secretsByVault, onLoadSecrets, mappingFields, note, valueLabel, userpassNote, secretTypes, children, }: CredentialSectionProps): import("react").JSX.Element;
|
|
28
|
+
interface ConnectionStringSectionProps extends VaultPickerProps {
|
|
29
|
+
vault: VaultRef | undefined;
|
|
30
|
+
onVaultChange: (v: VaultRef | undefined) => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Vault-only picker for SQL database connection strings.
|
|
34
|
+
* Direct input is intentionally not available — a connection string contains credentials.
|
|
35
|
+
*/
|
|
36
|
+
export declare function ConnectionStringSection({ vault, onVaultChange, vaults, secretsByVault, onLoadSecrets, }: ConnectionStringSectionProps): import("react").JSX.Element;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=CredentialSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CredentialSection.d.ts","sourceRoot":"","sources":["../../src/components/CredentialSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEhE,UAAU,sBAAuB,SAAQ,gBAAgB;IACvD,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAG,SAAS,KAAK,IAAI,CAAA;IAChD,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/C,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2HAA2H;IAC3H,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,+DAA+D;IAC/D,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,cAAc,EAAE,aAAa,EACrC,aAAa,EAAE,IAAI,EAAE,UAAuC,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,GAClG,EAAE,sBAAsB,+BA8IxB;AAED,UAAU,4BAA6B,SAAQ,gBAAgB;IAC7D,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,aAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAG,SAAS,KAAK,IAAI,CAAA;CACjD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,GAC5D,EAAE,4BAA4B,+BAqF9B"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Field, Note } from './formParts';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a set of credential fields with a Direct / Vault source toggle.
|
|
5
|
+
* In direct mode: shows a warning banner + the credential inputs (children).
|
|
6
|
+
* In vault mode: shows vault → secret dropdowns + JSON field mapping if needed.
|
|
7
|
+
*/
|
|
8
|
+
export function CredentialSection({ vault, onVaultChange, vaults, secretsByVault, onLoadSecrets, mappingFields, note, valueLabel = 'password / client secret', userpassNote, secretTypes, children, }) {
|
|
9
|
+
const isVault = !!vault;
|
|
10
|
+
const selectedVaultId = vault?.vaultId;
|
|
11
|
+
const selectedSecretId = vault?.secretId;
|
|
12
|
+
const fieldMapping = vault?.fieldMapping ?? {};
|
|
13
|
+
const secrets = selectedVaultId ? (secretsByVault[selectedVaultId]?.filter((s) => !secretTypes || secretTypes.includes(s.type)) ?? null) : null;
|
|
14
|
+
const selectedSecret = secrets?.find((s) => s.id === selectedSecretId);
|
|
15
|
+
return (_jsxs("div", { className: "space-y-3", children: [note && (_jsx("p", { className: "text-xs", style: { color: 'var(--m3-secondary)' }, children: note })), _jsx("div", { className: "flex rounded-lg overflow-hidden", style: { border: '1px solid var(--m3-outline-variant)', width: 'fit-content' }, children: ['direct', 'vault'].map((src) => {
|
|
16
|
+
const active = src === 'vault' ? isVault : !isVault;
|
|
17
|
+
return (_jsx("button", { type: "button", onClick: () => {
|
|
18
|
+
if (src === 'direct') {
|
|
19
|
+
onVaultChange(undefined);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const first = vaults[0];
|
|
23
|
+
if (first)
|
|
24
|
+
onLoadSecrets(first.id);
|
|
25
|
+
onVaultChange({ vaultId: first?.id ?? 0, secretId: 0, fieldMapping: {} });
|
|
26
|
+
}
|
|
27
|
+
}, className: `px-4 py-1.5 text-xs font-medium transition-all ${active ? 'selection-active' : ''}`, style: {
|
|
28
|
+
background: active ? 'var(--m3-primary-fixed)' : 'transparent',
|
|
29
|
+
color: active ? 'var(--m3-primary)' : 'var(--m3-secondary)',
|
|
30
|
+
}, children: src === 'direct' ? 'Direct input' : 'From Vault' }, src));
|
|
31
|
+
}) }), !isVault && (_jsxs(_Fragment, { children: [_jsx(Note, { tone: "warning", children: "For security, store credentials in Vault rather than entering them directly here." }), children] })), isVault && (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Field, { label: "Vault", children: _jsxs("select", { className: "input-sig", value: selectedVaultId || '', onChange: (e) => {
|
|
32
|
+
const id = Number(e.target.value);
|
|
33
|
+
onLoadSecrets(id);
|
|
34
|
+
onVaultChange({ vaultId: id, secretId: 0, fieldMapping: {} });
|
|
35
|
+
}, children: [_jsx("option", { value: "", children: "\u2014 select vault \u2014" }), vaults.map((v) => _jsx("option", { value: v.id, children: v.name }, v.id))] }) }), _jsx(Field, { label: "Secret", children: _jsxs("select", { className: "input-sig", value: selectedSecretId || '', disabled: !selectedVaultId, onChange: (e) => {
|
|
36
|
+
onVaultChange({ vaultId: selectedVaultId, secretId: Number(e.target.value), fieldMapping: {} });
|
|
37
|
+
}, children: [_jsx("option", { value: "", children: "\u2014 select secret \u2014" }), (secrets ?? []).map((s) => (_jsxs("option", { value: s.id, children: [s.name, " (", s.type, ")"] }, s.id)))] }) })] }), selectedSecret?.type === 'userpass' && (_jsx("p", { className: "text-xs rounded-lg px-3 py-2", style: { background: 'var(--m3-surface-container)', color: 'var(--m3-secondary)' }, children: userpassNote ?? _jsxs(_Fragment, { children: ["Auto-mapped: ", _jsx("strong", { children: "username" }), " \u2192 username field, ", _jsx("strong", { children: "password" }), " \u2192 password field."] }) })), selectedSecret?.type === 'value' && (_jsxs("p", { className: "text-xs rounded-lg px-3 py-2", style: { background: 'var(--m3-surface-container)', color: 'var(--m3-secondary)' }, children: ["The secret value will be used as the ", _jsx("strong", { children: valueLabel }), "."] })), selectedSecret?.type === 'json' && selectedSecretId && (_jsxs("div", { children: [_jsx("p", { className: "font-mono text-xs uppercase tracking-wider mb-1", style: { color: 'var(--m3-secondary)' }, children: "JSON Field Mapping" }), _jsx("p", { className: "text-xs mb-3", style: { color: 'var(--m3-secondary)' }, children: "For each credential, enter the key name from the JSON secret." }), _jsx("div", { className: "space-y-2", children: mappingFields.map(({ key, label }) => (_jsxs("div", { className: "grid items-center gap-3", style: { gridTemplateColumns: '110px 1fr' }, children: [_jsx("span", { className: "text-xs font-medium truncate", style: { color: 'var(--m3-on-surface-variant)' }, children: label }), _jsx("input", { className: "input-sig text-xs", placeholder: `JSON key (e.g. "${key}")`, value: fieldMapping[key] ?? '', onChange: (e) => {
|
|
38
|
+
onVaultChange({
|
|
39
|
+
vaultId: selectedVaultId,
|
|
40
|
+
secretId: selectedSecretId,
|
|
41
|
+
fieldMapping: { ...fieldMapping, [key]: e.target.value },
|
|
42
|
+
});
|
|
43
|
+
} })] }, key))) })] })), vaults.length === 0 && (_jsx("p", { className: "text-xs", style: { color: 'var(--m3-secondary)' }, children: "No vaults found. Create one in the Vault section first." }))] }))] }));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Vault-only picker for SQL database connection strings.
|
|
47
|
+
* Direct input is intentionally not available — a connection string contains credentials.
|
|
48
|
+
*/
|
|
49
|
+
export function ConnectionStringSection({ vault, onVaultChange, vaults, secretsByVault, onLoadSecrets, }) {
|
|
50
|
+
const selectedVaultId = vault?.vaultId;
|
|
51
|
+
const selectedSecretId = vault?.secretId;
|
|
52
|
+
const fieldMapping = vault?.fieldMapping ?? {};
|
|
53
|
+
const secrets = selectedVaultId ? (secretsByVault[selectedVaultId] ?? null) : null;
|
|
54
|
+
const selectedSecret = secrets?.find((s) => s.id === selectedSecretId);
|
|
55
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx(Note, { tone: "info", children: "Connection strings contain credentials and must always be stored in Vault." }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Field, { label: "Vault", children: _jsxs("select", { className: "input-sig", value: selectedVaultId || '', onChange: (e) => {
|
|
56
|
+
const id = Number(e.target.value);
|
|
57
|
+
onLoadSecrets(id);
|
|
58
|
+
onVaultChange({ vaultId: id, secretId: 0, fieldMapping: {} });
|
|
59
|
+
}, children: [_jsx("option", { value: "", children: "\u2014 select vault \u2014" }), vaults.map((v) => _jsx("option", { value: v.id, children: v.name }, v.id))] }) }), _jsx(Field, { label: "Secret", children: _jsxs("select", { className: "input-sig", value: selectedSecretId || '', disabled: !selectedVaultId, onChange: (e) => {
|
|
60
|
+
onVaultChange({ vaultId: selectedVaultId, secretId: Number(e.target.value), fieldMapping: {} });
|
|
61
|
+
}, children: [_jsx("option", { value: "", children: "\u2014 select secret \u2014" }), (secrets ?? []).map((s) => (_jsxs("option", { value: s.id, children: [s.name, " (", s.type, ")"] }, s.id)))] }) })] }), selectedSecret?.type === 'value' && (_jsx("p", { className: "text-xs rounded-lg px-3 py-2", style: { background: 'var(--m3-surface-container)', color: 'var(--m3-secondary)' }, children: "The secret value will be used as the full connection string." })), selectedSecret?.type === 'userpass' && (_jsxs(Note, { tone: "warning", children: ["A ", _jsx("strong", { children: "userpass" }), " secret cannot be used as a connection string. Use a ", _jsx("strong", { children: "value" }), " or ", _jsx("strong", { children: "json" }), " secret instead."] })), selectedSecret?.type === 'json' && selectedSecretId && (_jsxs("div", { children: [_jsx("p", { className: "font-mono text-xs uppercase tracking-wider mb-1", style: { color: 'var(--m3-secondary)' }, children: "JSON Field Mapping" }), _jsx("p", { className: "text-xs mb-3", style: { color: 'var(--m3-secondary)' }, children: "Enter the JSON key that holds the connection string." }), _jsxs("div", { className: "grid items-center gap-3", style: { gridTemplateColumns: '130px 1fr' }, children: [_jsx("span", { className: "text-xs font-medium", style: { color: 'var(--m3-on-surface-variant)' }, children: "Connection String" }), _jsx("input", { className: "input-sig text-xs", placeholder: 'JSON key (e.g. "connectionString")', value: fieldMapping['connectionString'] ?? '', onChange: (e) => {
|
|
62
|
+
onVaultChange({
|
|
63
|
+
vaultId: selectedVaultId,
|
|
64
|
+
secretId: selectedSecretId,
|
|
65
|
+
fieldMapping: { connectionString: e.target.value },
|
|
66
|
+
});
|
|
67
|
+
} })] })] })), vaults.length === 0 && (_jsx("p", { className: "text-xs", style: { color: 'var(--m3-secondary)' }, children: "No vaults found. Create one in the Vault section first." }))] }));
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=CredentialSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CredentialSection.js","sourceRoot":"","sources":["../../src/components/CredentialSection.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactNode } from 'react';
|
|
2
|
+
/** A lazy chunk that failed to load: after a deploy the old file names are gone, so the page needs a reload. */
|
|
3
|
+
export declare function isChunkLoadError(error: unknown): boolean;
|
|
4
|
+
interface Props {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
interface State {
|
|
8
|
+
error: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** Catches errors thrown while rendering (and by lazy imports) so the screen is not left blank; offers a reload. */
|
|
11
|
+
export declare class ErrorBoundary extends Component<Props, State> {
|
|
12
|
+
state: State;
|
|
13
|
+
static getDerivedStateFromError(error: unknown): State;
|
|
14
|
+
componentDidCatch(error: unknown, info: ErrorInfo): void;
|
|
15
|
+
render(): ReactNode;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=ErrorBoundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/components/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjE,gHAAgH;AAChH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGxD;AAED,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,UAAU,KAAK;IACb,KAAK,EAAE,OAAO,CAAA;CACf;AAED,oHAAoH;AACpH,qBAAa,aAAc,SAAQ,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;IAC/C,KAAK,EAAE,KAAK,CAAkB;IAEvC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK;IAI7C,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAIxD,MAAM,IAAI,SAAS;CAkB7B"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Component } from 'react';
|
|
3
|
+
/** A lazy chunk that failed to load: after a deploy the old file names are gone, so the page needs a reload. */
|
|
4
|
+
export function isChunkLoadError(error) {
|
|
5
|
+
const message = error instanceof Error ? `${error.name} ${error.message}` : String(error);
|
|
6
|
+
return /ChunkLoadError|Failed to fetch dynamically imported module|error loading dynamically imported module|Importing a module script failed/i.test(message);
|
|
7
|
+
}
|
|
8
|
+
/** Catches errors thrown while rendering (and by lazy imports) so the screen is not left blank; offers a reload. */
|
|
9
|
+
export class ErrorBoundary extends Component {
|
|
10
|
+
state = { error: null };
|
|
11
|
+
static getDerivedStateFromError(error) {
|
|
12
|
+
return { error: error ?? new Error('Unknown error') };
|
|
13
|
+
}
|
|
14
|
+
componentDidCatch(error, info) {
|
|
15
|
+
console.error(error, info.componentStack);
|
|
16
|
+
}
|
|
17
|
+
render() {
|
|
18
|
+
const { error } = this.state;
|
|
19
|
+
if (!error)
|
|
20
|
+
return this.props.children;
|
|
21
|
+
const stale = isChunkLoadError(error);
|
|
22
|
+
return (_jsxs("div", { role: "alert", className: "min-h-screen flex flex-col items-center justify-center gap-3 px-6 text-center", style: { background: 'var(--m3-surface)', color: 'var(--m3-on-surface)' }, children: [_jsx("span", { className: "material-symbols-outlined", "aria-hidden": "true", style: { fontSize: '32px', color: 'var(--m3-down)' }, children: "error" }), _jsx("h1", { className: "font-headline text-lg font-bold", children: stale ? 'A new version is available' : 'Something went wrong' }), _jsx("p", { className: "text-sm max-w-md", style: { color: 'var(--m3-secondary)' }, children: stale ? 'This page was updated while it was open. Reload to continue.' : 'The page could not be displayed. Reloading usually fixes it.' }), _jsxs("button", { type: "button", onClick: () => window.location.reload(), className: "btn btn-secondary btn-sm", children: [_jsx("span", { className: "material-symbols-outlined", "aria-hidden": "true", children: "refresh" }), "Reload"] })] }));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=ErrorBoundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../../src/components/ErrorBoundary.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAA;AAEjE,gHAAgH;AAChH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACzF,OAAO,wIAAwI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AAC/J,CAAC;AAUD,oHAAoH;AACpH,MAAM,OAAO,aAAc,SAAQ,SAAuB;IAC/C,KAAK,GAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IAEvC,MAAM,CAAC,wBAAwB,CAAC,KAAc;QAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,KAAK,CAAC,eAAe,CAAC,EAAE,CAAA;IACvD,CAAC;IAEQ,iBAAiB,CAAC,KAAc,EAAE,IAAe;QACxD,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;IAC3C,CAAC;IAEQ,MAAM;QACb,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAC5B,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;QACtC,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;QACrC,OAAO,CACL,eAAK,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,+EAA+E,EAAC,KAAK,EAAE,EAAE,UAAU,EAAE,mBAAmB,EAAE,KAAK,EAAE,sBAAsB,EAAE,aACnL,eAAM,SAAS,EAAC,2BAA2B,iBAAa,MAAM,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,sBAAc,EACjI,aAAI,SAAS,EAAC,iCAAiC,YAAE,KAAK,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,sBAAsB,GAAM,EACpH,YAAG,SAAS,EAAC,kBAAkB,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,YACpE,KAAK,CAAC,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC,8DAA8D,GACtI,EACJ,kBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,SAAS,EAAC,0BAA0B,aACjG,eAAM,SAAS,EAAC,2BAA2B,iBAAa,MAAM,wBAAe,cAEtE,IACL,CACP,CAAA;IACH,CAAC;CACF"}
|