@buildbase/sdk 0.0.88 → 0.0.90
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/AGENTS.md +7 -3
- package/CHANGELOG.md +515 -0
- package/README.md +120 -3
- package/dist/cli.mjs +1 -1
- package/dist/css/styles.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1351 -12
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.d.ts +120 -2
- package/dist/mcp/index.mjs +1 -1
- package/dist/react/chunks/SettingsDialog-BVyV5Zfm.mjs +2 -0
- package/dist/react/chunks/SettingsDialog-Ch8X6WXy.cjs +2 -0
- package/dist/react/chunks/{SubscriptionDialog-B8FTFRtp.cjs → SubscriptionDialog-CGIvMKpT.cjs} +1 -1
- package/dist/react/chunks/SubscriptionDialog-g0LwHhbe.mjs +2 -0
- package/dist/react/chunks/ar-3aqWucom.mjs +2 -0
- package/dist/react/chunks/ar-DQVB89r8.cjs +2 -0
- package/dist/react/chunks/de-DO10bhnU.cjs +2 -0
- package/dist/react/chunks/de-HXQk5D6k.mjs +2 -0
- package/dist/react/chunks/es-BZsmhBN3.mjs +2 -0
- package/dist/react/chunks/es-Cxg-qhUh.cjs +2 -0
- package/dist/react/chunks/fr-COCMy3vW.cjs +2 -0
- package/dist/react/chunks/fr-rdnNHzxy.mjs +2 -0
- package/dist/react/chunks/hi-BB0Z-Dgn.cjs +2 -0
- package/dist/react/chunks/hi-t3RH7ITu.mjs +2 -0
- package/dist/react/chunks/ja-CDyla2g0.mjs +2 -0
- package/dist/react/chunks/ja-D92JgUlk.cjs +2 -0
- package/dist/react/chunks/react-DTq7A_PT.mjs +2 -0
- package/dist/react/chunks/react-DeapSbq6.cjs +2 -0
- package/dist/react/chunks/zh-C5HABZZV.mjs +2 -0
- package/dist/react/chunks/zh-DK2yA5wB.cjs +2 -0
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.d.ts +1517 -14
- package/dist/react/index.mjs +1 -1
- package/docs/ERROR_CODES.md +218 -0
- package/docs/THEMING.md +89 -21
- package/docs/UI-CONFIG.md +23 -4
- package/docs/USER-IMPORT.md +320 -0
- package/package.json +2 -1
- package/dist/react/chunks/SettingsDialog-CKVE1PpH.mjs +0 -2
- package/dist/react/chunks/SettingsDialog-DrDmxFEj.cjs +0 -2
- package/dist/react/chunks/SubscriptionDialog-UN7o9Hqb.mjs +0 -2
- package/dist/react/chunks/ar-B2l90OZJ.cjs +0 -2
- package/dist/react/chunks/ar-DVVNZhXD.mjs +0 -2
- package/dist/react/chunks/de-BspJtvAC.mjs +0 -2
- package/dist/react/chunks/de-dURUqbl0.cjs +0 -2
- package/dist/react/chunks/es-DArjREi5.cjs +0 -2
- package/dist/react/chunks/es-vu4BmI3g.mjs +0 -2
- package/dist/react/chunks/fr-DVm5WCvZ.mjs +0 -2
- package/dist/react/chunks/fr-DhYDaYrX.cjs +0 -2
- package/dist/react/chunks/hi-C_nv_c1T.cjs +0 -2
- package/dist/react/chunks/hi-VW8xCj_K.mjs +0 -2
- package/dist/react/chunks/ja-C-b88dvw.mjs +0 -2
- package/dist/react/chunks/ja-CqtDkLHr.cjs +0 -2
- package/dist/react/chunks/react-CHivaJhA.mjs +0 -2
- package/dist/react/chunks/react-Ch_Suxxy.cjs +0 -2
- package/dist/react/chunks/zh-D1yfhUf0.mjs +0 -2
- package/dist/react/chunks/zh-D_tMpsNt.cjs +0 -2
package/dist/react/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export{
|
|
2
|
+
export{bw as APP_PERMISSION_FALLBACK_GROUP,bx as ApiVersion,av as AuthApi,by as AuthStatus,E as BBAction,aA as BBScreen,F as BBStatus,az as BB_PARAM,bz as BaseApi,bA as BetaForm,aO as BillingIntervals,bB as BuildBaseBadge,bC as BuildBaseProvider,bD as CURRENCY_DISPLAY,bE as CURRENCY_FLAG,bF as ConnectMcpGuide,C as ConnectedAgents,bG as ConnectedAgentsApi,bH as CreditActionsProvider,bI as CreditBalance,bJ as CreditBalanceContextProvider,bK as CreditBucketSource,bL as CreditBucketStatus,bM as CreditStorePage,N as CreditTransactionType,bN as DEFAULT_ROLE_PERMISSIONS,a2 as Devices,bO as DevicesApi,b3 as DunningState,I as EmptyState,bP as EventEmitter,bQ as FALLBACK_WORKSPACE_ROLE,bR as FullScreenLoader,at as GuestUpgrade,bS as INVITATION_ERROR_CODE,bT as ImpersonationBanner,aM as InvoiceStatuses,L as LoadingState,bU as NotificationBell,bV as NotificationInbox,bW as NotificationsApi,bX as PLAN_CURRENCY_CODES,bY as PLAN_CURRENCY_OPTIONS,bZ as PUSH_SERVICE_WORKER_SCRIPT,b_ as PendingInvitations,P as Permission,b$ as PricingPage,c0 as PushApi,c1 as PushNotificationProvider,c2 as QuotaUsageContextProvider,c3 as SDKEvent,br as SETTINGS_SCREENS,c4 as SUPPORTED_LOCALES,bC as SaaSOSProvider,H as SectionHeader,a3 as Sessions,c5 as SessionsApi,c6 as SettingsApi,bk as SettingsScreen,bn as SidebarNavItem,bm as SidebarNavSection,S as StatusBanner,c7 as SubscriptionContextProvider,aP as SubscriptionItemType,aS as SubscriptionStatus,ah as UPLOAD_ACCEPT,c8 as UPLOAD_MAX_BYTES,c9 as UploadsApi,ca as UserApi,cb as WALLET_LINK_STATEMENT,aE as WalletFlowError,cc as WhenAuthenticated,cd as WhenCreditsAvailable,ce as WhenCreditsExhausted,cf as WhenCreditsLow,cg as WhenNoSubscription,ch as WhenNotTrialing,ci as WhenPendingInvitations,cj as WhenPermission,ck as WhenQuotaAvailable,cl as WhenQuotaExhausted,cm as WhenQuotaOverage,cn as WhenQuotaThreshold,co as WhenRoles,cp as WhenSubscription,cq as WhenSubscriptionToPlans,cr as WhenTrialEnding,cs as WhenTrialing,ct as WhenUnauthenticated,cu as WhenUserFeatureDisabled,cv as WhenUserFeatureEnabled,cw as WhenWorkspaceFeatureDisabled,cx as WhenWorkspaceFeatureEnabled,cy as WhenWorkspaceRoles,cz as WorkspaceApi,cA as WorkspaceSwitcher,cB as actionableInvitations,an as appPermissionCatalogue,cC as buildWalletMessage,cD as calculateBillableSeats,cE as calculateSeatOverageCents,cF as calculateTotalSubscriptionCents,cG as cleanBBParams,cH as clearAuthIntent,cI as clearPendingInvite,cJ as coerceBadgeRef,aD as connectWallet,cK as consumeAuthIntent,cL as createBBUrl,b5 as createCheckoutRedirectUrls,K as createCreditPurchaseRedirectUrls,cM as eventEmitter,cN as fillMcpTemplate,cO as formatCents,o as formatMinorAmountIntl,cP as formatOverageRate,cQ as formatOverageRateWithLabel,cR as formatQuotaIncludedOverage,cS as formatQuotaWithPrice,cT as formatUploadLimit,bu as getAvailableBillingIntervals,bt as getAvailableCurrenciesFromPlans,b0 as getBasePriceCents,a$ as getBillingIntervalAndCurrencyFromPriceId,q as getCurrencyDecimals,ad as getCurrencyFlag,n as getCurrencySymbol,cU as getDisplayCurrency,b2 as getPerSeatPriceCents,bs as getPricingVariant,aR as getQuotaDisplayValue,aQ as getQuotaDisplayWithVariant,b9 as getQuotaOverageCents,cV as getQuotaUnitLabelFromName,b1 as getSeatPricing,b4 as getStripePriceIdForInterval,cW as hasPermission,G as invalidateCreditBalance,cX as invalidateQuotaUsage,b6 as invalidateSubscription,cY as inviterNameOf,cZ as isImpersonationSignInLocked,c_ as isInvitationActionable,c$ as isInvitationError,bh as isInvitationLapsed,d0 as isReadOnlySession,d1 as isReauthRequired,d2 as isValidUsername,d3 as isZeroDecimalCurrency,d4 as mcpServerKey,bo as mergeUIConfig,d5 as minorAmountToDisplay,ax as normaliseUsername,d6 as parkPendingInvite,bf as pickDefaultRole,d7 as preflightUpload,d8 as readBBParams,d9 as readPendingInvite,bg as resendCooldownSeconds,da as resolveMaxUsers,db as resolvePermissions,ao as resolveRolePermissions,M as safeRedirect,dc as saveAuthIntent,dd as sortInvitations,de as toChecksumAddress,df as uploadFailureOf,dg as uploadPolicyOf,dh as useAllQuotaUsage,aY as useBillingPortal,aW as useCancelSubscription,di as useConnectedAgents,dj as useConnectedAgentsApi,dk as useConsumeCredits,aV as useCreateCheckoutSession,dl as useCreditBalance,v as useCreditBalanceContext,w as useCreditPackages,y as useCreditTransactions,dm as useDevices,dn as useDevicesApi,x as useExpiringCredits,dp as useFullScreenLoader,dq as useGuestUpgrade,dr as useInvoice,aK as useInvoices,ds as useMcpConnection,dt as useMyInvitations,du as useNotifications,dv as useNotificationsApi,s as usePermissions,dw as usePlanGroup,aZ as usePlanGroupVersions,dx as usePublicCreditPackages,dy as usePublicPlanGroupVersion,dz as usePublicPlans,z as usePurchaseCredits,al as usePushNotifications,b7 as useQuotaUsageContext,dA as useQuotaUsageStatus,dB as useRecordUsage,aX as useResumeSubscription,aB as useSaaSAuth,au as useSaaSOs,Q as useSaaSSettings,O as useSaaSWorkspaces,be as useSeatStatus,dC as useSessions,dD as useSessionsApi,dE as useSubscription,b8 as useSubscriptionContext,aU as useSubscriptionManagement,c as useTranslation,dF as useTrialStatus,d as useUIConfig,t as useUIVisibility,dG as useUpdateSubscription,a4 as useUpload,dH as useUploadsApi,dI as useUsageLogs,dJ as useUserAttributes,dK as useUserFeatures,bc as useWorkspaceInvitations,dL as validateInvite,aF as validateRedirectUrl,dM as workspaceIdOf,dN as workspaceNameOf,A as workspaceSettingsManager}from"./chunks/react-DTq7A_PT.mjs";import"react/jsx-runtime";import"react";import"clsx";import"tailwind-merge";import"lucide-react";import"intl-messageformat";import"@radix-ui/react-slot";import"class-variance-authority";import"@radix-ui/react-dialog";import"@radix-ui/react-icons";import"@hookform/resolvers/zod";import"react-hook-form";import"@radix-ui/react-label";import"zod";import"@radix-ui/react-avatar";import"@radix-ui/react-radio-group";import"@radix-ui/react-scroll-area";import"@radix-ui/react-separator";import"@radix-ui/react-accordion";import"@radix-ui/react-alert-dialog";import"@radix-ui/react-popover";
|
package/docs/ERROR_CODES.md
CHANGED
|
@@ -36,6 +36,224 @@ server answered with.
|
|
|
36
36
|
| ---------------------- | ----------------------------------------------------------------------------------------------- |
|
|
37
37
|
| `INSUFFICIENT_CREDITS` | A spend exceeds the balance. Carries `available` and `requested`, and the server answered `402` |
|
|
38
38
|
|
|
39
|
+
## Two-step sign-in codes
|
|
40
|
+
|
|
41
|
+
From the authenticator-app calls on `WorkspaceApi` (`getMfaStatus`,
|
|
42
|
+
`startTotpEnrollment`, `confirmTotpEnrollment`, `disableTotp`,
|
|
43
|
+
`regenerateBackupCodes`). Lower case and hyphenated (`guest_account` aside), because they are the
|
|
44
|
+
server's `reason` passed through unchanged; the type is `MfaErrorCode`. Each
|
|
45
|
+
error also carries the HTTP `status`, and its message is the server's sentence,
|
|
46
|
+
written to be shown to the user.
|
|
47
|
+
|
|
48
|
+
| Code | Status | When it occurs |
|
|
49
|
+
| ------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
50
|
+
| `mfa-off` | 403 | The app has two-step sign-in turned off, so a setup cannot start |
|
|
51
|
+
| `already-enrolled` | 409 | An authenticator app is already set up; remove it before adding another |
|
|
52
|
+
| `not-enrolling` | 409 | A confirm arrived with no setup pending; start again |
|
|
53
|
+
| `invalid-code` | 400 | The code did not match. On remove and new backup codes it counts toward the lockout |
|
|
54
|
+
| `not-enrolled` | 404 | Remove or new backup codes, with no authenticator app set up |
|
|
55
|
+
| `required-by-org` | 403 | The app requires two-step sign-in, so it cannot be removed |
|
|
56
|
+
| `locked` | 429 | Too many wrong codes; wait for the window to pass |
|
|
57
|
+
| `guest_account` | 409 | A guest creates an account before setting up a second step (also from `startSmsEnrollment`); the Security screen says so in its own words |
|
|
58
|
+
|
|
59
|
+
## Password change codes
|
|
60
|
+
|
|
61
|
+
From `changePassword` on `WorkspaceApi`. Lower case with underscores, because
|
|
62
|
+
they are the server's `reason` passed through unchanged; the type is
|
|
63
|
+
`PasswordChangeErrorCode`. Each error also carries the HTTP `status`, and its
|
|
64
|
+
message is the server's sentence, written to be shown to the user. The
|
|
65
|
+
profile's `passwordSet` says ahead of time whether there is a password to
|
|
66
|
+
change.
|
|
67
|
+
|
|
68
|
+
| Code | Status | When it occurs |
|
|
69
|
+
| ------------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `password_not_set` | 409 | The account has no password the person knows (social sign-up, a wallet, or a code or link). Not counted toward the lockout; set one with Forgot password? |
|
|
71
|
+
| `invalid` | 400 | The current password is wrong (counted toward the sign-in lockout), or the new one appeared in a known data breach |
|
|
72
|
+
| `locked` | 429 | Too many wrong passwords; wait up to 15 minutes for the window to pass, or reset the password, which lifts it |
|
|
73
|
+
|
|
74
|
+
## Change email codes
|
|
75
|
+
|
|
76
|
+
From `requestEmailChange` and `confirmEmailChange` on `WorkspaceApi`. Lower
|
|
77
|
+
case with underscores, because they are the server's `reason` passed through
|
|
78
|
+
unchanged; the type is `EmailChangeErrorCode`. Each error also carries the HTTP
|
|
79
|
+
`status`, and its message is the server's sentence, written to be shown to the
|
|
80
|
+
user.
|
|
81
|
+
|
|
82
|
+
| Code | Status | When it occurs |
|
|
83
|
+
| ----------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
+
| `email_unavailable` | 409 | The new address cannot be used in this app, whether it is taken or invalid; the sentence is the same either way |
|
|
85
|
+
| `linked_social_account` | 409 | Only from an older server: the account had a linked social sign-in, so its email could not change. A current server does not send it |
|
|
86
|
+
| `cooldown` | 429 | A code went out moments ago. Carries `retryAfter`, the seconds until another can be sent |
|
|
87
|
+
| `disposable_email` | 400 | The app blocks disposable addresses and the new one is from such a domain |
|
|
88
|
+
| `invalid_code` | 400 | The code did not match |
|
|
89
|
+
| `code_expired` | 410 | No code is pending, it lapsed, or it used its tries; send a new one. The session is fine, so the SDK does not sign the user out for it |
|
|
90
|
+
|
|
91
|
+
## Connected account codes
|
|
92
|
+
|
|
93
|
+
From `getConnectedAccounts`, `linkAccount` and `unlinkAccount` on
|
|
94
|
+
`WorkspaceApi`. Lower case with underscores, because they are the server's
|
|
95
|
+
`reason` passed through unchanged; the type is `ConnectedAccountErrorCode`.
|
|
96
|
+
Each error also carries the HTTP `status`, and its message is the server's
|
|
97
|
+
sentence, written to be shown to the user. None of them signs the user out;
|
|
98
|
+
only a 401 does.
|
|
99
|
+
|
|
100
|
+
| Code | Status | When it occurs |
|
|
101
|
+
| --------------------- | ------ | ------------------------------------------------------------------------------------------------ |
|
|
102
|
+
| `reauth_required` | 403 | Removing one needs a sign-in or re-confirmation in the last 10 minutes; see Session refusals |
|
|
103
|
+
| `last_sign_in_method` | 409 | It is the only way the user can sign in (`canUnlink` is false); add another sign-in method first |
|
|
104
|
+
| `already_linked` | 409 | The user already has an account from that provider linked |
|
|
105
|
+
| `method_unavailable` | 404 | The method is not a social sign-in on this app |
|
|
106
|
+
|
|
107
|
+
A link that fails at the provider comes back on the page URL instead, as
|
|
108
|
+
`?error=...&reason=...` (`AccountLinkErrorReason`), which the Security screen
|
|
109
|
+
reads and shows once:
|
|
110
|
+
|
|
111
|
+
| Reason | When it occurs |
|
|
112
|
+
| ------------------- | ----------------------------------------------------------------------------------------------- |
|
|
113
|
+
| `link_conflict` | That provider account is already linked to another user of this app |
|
|
114
|
+
| `provider_error` | The provider answered with an error, or the person cancelled there |
|
|
115
|
+
| `provider_failed` | The provider's answer could not be completed into a link |
|
|
116
|
+
| `no_verified_email` | The provider account has no verified email address (GitHub's can have none) |
|
|
117
|
+
| `session_ended` | The session that started the link ended before it came back |
|
|
118
|
+
| `email_unverified` | A guest connected an account with no verified address to create theirs with; nothing was linked |
|
|
119
|
+
| `email_unavailable` | A guest: the provider account's address cannot be used here (it has an account, or is refused) |
|
|
120
|
+
| `disposable_email` | A guest: the provider account's address is a disposable one, which the app blocks |
|
|
121
|
+
| `signup_closed` | A guest: the app has sign-up closed, so the account cannot be created |
|
|
122
|
+
| `signup_restricted` | A guest: sign-up is not available for that address (the app's allowlist or blocklist) |
|
|
123
|
+
| `waitlist_required` | A guest: the app signs people up from its waitlist, and that address is not approved |
|
|
124
|
+
| `not_a_guest` | The guest became an account in another tab before the link came back |
|
|
125
|
+
|
|
126
|
+
The screen shows each in the user's language and takes `error` and `reason`
|
|
127
|
+
off the URL. A `reason` it does not know is left on the URL, since `error` is
|
|
128
|
+
a common name for an app's own parameter.
|
|
129
|
+
|
|
130
|
+
## A call nobody answered
|
|
131
|
+
|
|
132
|
+
When the API never answers (the browser is offline, a proxy or gateway in
|
|
133
|
+
between fails and sends its own page, or the request times out), the error
|
|
134
|
+
carries no `code`. The Security screen, Confirm it's you, the account
|
|
135
|
+
screen's email change and username, deleting the account and the guest
|
|
136
|
+
upgrade say "The server could not be reached. Check your connection and try
|
|
137
|
+
again." in the user's language (`errors.unreachable`) instead of the
|
|
138
|
+
browser's words ("Failed to fetch", "Load failed") or the API layer's
|
|
139
|
+
fallback ("Failed to change password (502: Bad Gateway)"). A gateway's page
|
|
140
|
+
keeps its HTTP `status` on the error. `useGuestUpgrade().error` says
|
|
141
|
+
`unreachable: true` for it.
|
|
142
|
+
|
|
143
|
+
## Phone codes
|
|
144
|
+
|
|
145
|
+
From the phone calls on `WorkspaceApi` (`addPhone`, `confirmPhone`,
|
|
146
|
+
`removePhone`) and the text-message factor for two-step sign-in
|
|
147
|
+
(`startSmsEnrollment`, `confirmSmsEnrollment`, `disableSms`). Lower case with
|
|
148
|
+
underscores, because they are the server's `reason` passed through unchanged;
|
|
149
|
+
the type is `PhoneErrorCode`. Each error also carries the HTTP `status`, and
|
|
150
|
+
its message is the server's sentence, written to be shown to the user. None of
|
|
151
|
+
them signs the user out; only a 401 does.
|
|
152
|
+
|
|
153
|
+
| Code | Status | When it occurs |
|
|
154
|
+
| --------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
155
|
+
| `invalid_phone` | 400 | The number is not a valid phone number |
|
|
156
|
+
| `method_unavailable` | 400 | From `addPhone`: the app does not send codes the way asked for (by text message, when no `channel` is given) |
|
|
157
|
+
| `phone_unavailable` | 409 | The number is on another account |
|
|
158
|
+
| `cooldown` | 429 | A code went to that number moments ago. Carries `retryAfter`, the seconds until another can be sent |
|
|
159
|
+
| `rate_limited` | 429 | Past the number's or the address's hourly or daily limit |
|
|
160
|
+
| `number_refused` | 422 | The app's SMS provider will not send to that number |
|
|
161
|
+
| `sms_unavailable` | 503 | Codes cannot be sent right now: no provider, a provider failure, or the app's daily cap reached |
|
|
162
|
+
| `invalid_code` | 400 | The code did not match |
|
|
163
|
+
| `code_expired` | 410 | No code is pending, it lapsed, or it used its tries; send a new one. The session is fine, so the SDK does not sign out |
|
|
164
|
+
| `reauth_required` | 403 | Removing the phone or turning text message codes off needs a sign-in or re-confirmation in the last 10 minutes; see Session refusals |
|
|
165
|
+
| `last_sign_in_method` | 409 | The phone is the only way the user can sign in; add another sign-in method first |
|
|
166
|
+
| `mfa_sms_enrolled` | 409 | Text message codes for two-step sign-in go to this phone; turn them off before removing it |
|
|
167
|
+
| `phone_required` | 409 | Turning text message codes on needs a verified phone; add one first |
|
|
168
|
+
| `factor_not_allowed` | 403 | The app does not allow text message codes for two-step sign-in |
|
|
169
|
+
|
|
170
|
+
## Session refusals
|
|
171
|
+
|
|
172
|
+
Any session-authenticated call may answer one of these, beside its own codes.
|
|
173
|
+
The type is `SessionRefusalCode`. None of them signs the user out.
|
|
174
|
+
|
|
175
|
+
| Code | Status | When it occurs |
|
|
176
|
+
| ------------------------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
177
|
+
| `reauth_required` | 403 | The change needs a recent sign-in. Carries `maxAgeSeconds` and, when it wants the second step too, `factors: ['mfa']`. Re-confirm with `useSaaSAuth().requireReverification({ factors })`; `isReauthRequired(error)` |
|
|
178
|
+
| `read_only_session` | 403 | An app owner is viewing as the user, read-only, so nothing can change. Show the sentence; `isReadOnlySession(error)` |
|
|
179
|
+
| `impersonation_sign_in_locked` | 403 | An app owner is signed in as the user from the console, read-only or not, and the call changes how the user signs in: email, phone, username, password, second step, passkeys, linked accounts or a guest's upgrade. Those stay the user's. Show the sentence; `isImpersonationSignInLocked(error)` |
|
|
180
|
+
|
|
181
|
+
## Re-confirm codes
|
|
182
|
+
|
|
183
|
+
From `WorkspaceApi.startReverification` (and `requireReverification`). The
|
|
184
|
+
type is `ReverifyErrorCode`.
|
|
185
|
+
|
|
186
|
+
| Code | Status | When it occurs |
|
|
187
|
+
| ---------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
188
|
+
| `reverify_unavailable` | 400 | Nobody signed in to this session (an API-key exchange), an owner holds it as the user, or the user is a guest. The Security screen signs out and in instead, except for a guest and while an owner holds the session: then it says why and stays signed in |
|
|
189
|
+
| `invalid_return_url` | 400 | The return URL is not one of the auth client's redirect URLs. `requireReverification` then comes back through `auth.redirectUrl` |
|
|
190
|
+
|
|
191
|
+
## Account deletion codes
|
|
192
|
+
|
|
193
|
+
From `requestAccountDeletion` and `cancelAccountDeletion` on `WorkspaceApi`.
|
|
194
|
+
The type is `AccountDeletionErrorCode`.
|
|
195
|
+
|
|
196
|
+
| Code | Status | When it occurs |
|
|
197
|
+
| ------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
198
|
+
| `reauth_required` | 403 | Deleting needs a recent sign-in; see Session refusals |
|
|
199
|
+
| `owns_workspaces` | 409 | The user created a workspace that has other members or a subscription. Carries `workspaces: [{ id, name }]`. Nothing hands a workspace over: remove the other members and cancel the subscription, or delete the workspace |
|
|
200
|
+
| `read_only_session` | 403 | An app owner is viewing as the user |
|
|
201
|
+
|
|
202
|
+
## Username codes
|
|
203
|
+
|
|
204
|
+
From `setUsername` and `removeUsername` on `WorkspaceApi`. The type is
|
|
205
|
+
`UsernameErrorCode`.
|
|
206
|
+
|
|
207
|
+
| Code | Status | When it occurs |
|
|
208
|
+
| ------------------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
|
|
209
|
+
| `invalid_username` | 400 | Not 3 to 30 of `a-z 0-9 _ . -` starting and ending with a letter or digit, two dots in a row, or a reserved word |
|
|
210
|
+
| `username_taken` | 409 | Another account here has it |
|
|
211
|
+
| `usernames_off` | 400 | The app has usernames off |
|
|
212
|
+
| `username_required` | 409 | Removing it, while the app requires one |
|
|
213
|
+
|
|
214
|
+
## Guest upgrade codes
|
|
215
|
+
|
|
216
|
+
From `requestGuestUpgrade` and `confirmGuestUpgrade` on `WorkspaceApi` (and
|
|
217
|
+
`useGuestUpgrade`). The type is `GuestUpgradeErrorCode`. None of them signs the
|
|
218
|
+
guest out.
|
|
219
|
+
|
|
220
|
+
| Code | Status | When it occurs |
|
|
221
|
+
| ------------------- | ------ | ---------------------------------------------------------------------------- |
|
|
222
|
+
| `email_unavailable` | 409 | The address cannot be used here: it is taken or invalid |
|
|
223
|
+
| `not_a_guest` | 409 | The user is already a real account |
|
|
224
|
+
| `signup_restricted` | 403 | Sign-up is not available for this address (the app's allowlist or blocklist) |
|
|
225
|
+
| `waitlist_required` | 403 | The app signs people up from its waitlist; join it first |
|
|
226
|
+
| `signup_closed` | 403 | The app has sign-up closed |
|
|
227
|
+
| `disposable_email` | 400 | The app blocks disposable addresses |
|
|
228
|
+
| `breached_password` | 400 | The password appeared in a known data breach |
|
|
229
|
+
| `cooldown` | 429 | A code went out moments ago. Carries `retryAfter` |
|
|
230
|
+
| `invalid_code` | 400 | The code did not match |
|
|
231
|
+
| `code_expired` | 410 | No code is pending, it lapsed, or it used its tries; send a new one |
|
|
232
|
+
|
|
233
|
+
A guest also gets `guest_account` (409) from `requestEmailChange` and from
|
|
234
|
+
`linkWallet`: they create an account first.
|
|
235
|
+
|
|
236
|
+
## Wallet codes
|
|
237
|
+
|
|
238
|
+
From `requestWalletNonce` and `linkWallet` on `WorkspaceApi` (and
|
|
239
|
+
`connectWallet`). The type is `WalletErrorCode`.
|
|
240
|
+
|
|
241
|
+
| Code | Status | When it occurs |
|
|
242
|
+
| -------------------- | ------ | -------------------------------------------------------------------------------------------------------- |
|
|
243
|
+
| `method_unavailable` | 403 | The app offers no wallet sign-in |
|
|
244
|
+
| `chain_not_allowed` | 400 | The app does not take that kind of wallet, or that network |
|
|
245
|
+
| `invalid_message` | 400 | The message was not made for this page (its domain or URI), or its time is off |
|
|
246
|
+
| `invalid_signature` | 400 | The signature is not the wallet's |
|
|
247
|
+
| `nonce_expired` | 401 | The nonce lapsed (5 minutes) or was spent; start again. A refusal, so the SDK does not sign the user out |
|
|
248
|
+
| `link_conflict` | 409 | The wallet is connected to another account here |
|
|
249
|
+
| `guest_account` | 409 | A guest creates an account before connecting a wallet |
|
|
250
|
+
|
|
251
|
+
`connectWallet` throws a `WalletFlowError` instead when the wallet stops it
|
|
252
|
+
before the server answers: `no_wallet` (nothing injected for that chain),
|
|
253
|
+
`rejected` (the person said no), `wallet_busy` (the wallet is already asking
|
|
254
|
+
something), `wrong_network` (on a network the app does not take, and it would
|
|
255
|
+
not switch), `no_account` (no address shared) and `wallet_failed`.
|
|
256
|
+
|
|
39
257
|
## Upload codes
|
|
40
258
|
|
|
41
259
|
From `UploadsApi.upload()` and `useUpload().upload()`, with the HTTP `status`
|
package/docs/THEMING.md
CHANGED
|
@@ -36,31 +36,98 @@ Load your overrides after the SDK stylesheet, against the SDK's own selector:
|
|
|
36
36
|
--muted-foreground: 240 4% 46%; /* secondary text, hints, labels */
|
|
37
37
|
--border: 240 6% 90%;
|
|
38
38
|
|
|
39
|
-
/* Status colors
|
|
40
|
-
--destructive: 0
|
|
41
|
-
--
|
|
42
|
-
--
|
|
43
|
-
--
|
|
39
|
+
/* Status colors: each a fill (tints, bars, buttons) and a text tone */
|
|
40
|
+
--destructive: 0 72% 45%; /* Delete buttons, error tints, overage bars */
|
|
41
|
+
--destructive-text: 0 72% 36%; /* errors, banner text, Sign out */
|
|
42
|
+
--success: 142 72% 26%; /* success tints, paid badges, seat bars */
|
|
43
|
+
--success-text: 142 72% 19%; /* success messages, checkmarks */
|
|
44
|
+
--warning: 26 90% 33%; /* warning tints, near-limit bars */
|
|
45
|
+
--warning-text: 26 90% 24%; /* warnings, expiring credits */
|
|
46
|
+
--info: 224 76% 48%; /* info tints, the current plan, usage bars */
|
|
47
|
+
--info-text: 224 76% 38%; /* info notices, plan highlights */
|
|
44
48
|
}
|
|
45
49
|
```
|
|
46
50
|
|
|
47
51
|
## Full variable reference
|
|
48
52
|
|
|
49
|
-
| Variable | Used for
|
|
50
|
-
| -------------------------------------------- |
|
|
51
|
-
| `--background` / `--foreground` | Page surface and main text
|
|
52
|
-
| `--card` / `--card-foreground` | Card surfaces
|
|
53
|
-
| `--popover` / `--popover-foreground` | Dropdowns, popovers
|
|
54
|
-
| `--primary` / `--primary-foreground` | Buttons, active sidebar item, selected states
|
|
55
|
-
| `--secondary` / `--secondary-foreground` | Secondary buttons
|
|
56
|
-
| `--muted` / `--muted-foreground` | Subtle fills, secondary text, disabled states
|
|
57
|
-
| `--accent` / `--accent-foreground` | Hover highlights
|
|
58
|
-
| `--destructive` / `--destructive-foreground` |
|
|
59
|
-
| `--
|
|
60
|
-
| `--
|
|
61
|
-
| `--
|
|
62
|
-
| `--
|
|
63
|
-
| `--
|
|
53
|
+
| Variable | Used for |
|
|
54
|
+
| -------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
55
|
+
| `--background` / `--foreground` | Page surface and main text |
|
|
56
|
+
| `--card` / `--card-foreground` | Card surfaces |
|
|
57
|
+
| `--popover` / `--popover-foreground` | Dropdowns, popovers |
|
|
58
|
+
| `--primary` / `--primary-foreground` | Buttons, active sidebar item, selected states |
|
|
59
|
+
| `--secondary` / `--secondary-foreground` | Secondary buttons |
|
|
60
|
+
| `--muted` / `--muted-foreground` | Subtle fills, secondary text, disabled states |
|
|
61
|
+
| `--accent` / `--accent-foreground` | Hover highlights |
|
|
62
|
+
| `--destructive` / `--destructive-foreground` | Red fills (Delete buttons, error tints, overage bars) and the text on them |
|
|
63
|
+
| `--destructive-text` | Red text: errors, banner and badge text, Sign out |
|
|
64
|
+
| `--success` / `--success-foreground` | Success fills and tints (banners, paid badges, seat bars) and the text on them |
|
|
65
|
+
| `--success-text` | Success text and checkmarks |
|
|
66
|
+
| `--warning` / `--warning-foreground` | Warning fills and tints (banners, near-limit bars) and the text on them |
|
|
67
|
+
| `--warning-text` | Warning text: permission notices, expiring credits |
|
|
68
|
+
| `--info` / `--info-foreground` | Info fills and tints (notices, the current plan, usage bars) and the text on them |
|
|
69
|
+
| `--info-text` | Info text: notices, plan highlights |
|
|
70
|
+
| `--border` / `--input` / `--ring` | Borders, input outlines, focus rings |
|
|
71
|
+
| `--radius` | Border radius scale (lg/md/sm derive from it) |
|
|
72
|
+
|
|
73
|
+
## Status colours: a fill and a text tone
|
|
74
|
+
|
|
75
|
+
Each status colour (`destructive`, `success`, `warning`, `info`) does several
|
|
76
|
+
jobs, and one value cannot do them all. Behind white text (a Delete button) it
|
|
77
|
+
has to be dark enough for the white to read; as text on a tinted banner it has
|
|
78
|
+
to be darker still in light mode; as a progress bar or focus ring it has to
|
|
79
|
+
stand out from the page. In dark mode the fill behind white text and the text
|
|
80
|
+
on the page pull apart completely. So each status has two tokens:
|
|
81
|
+
|
|
82
|
+
| Token | Utilities | Used for |
|
|
83
|
+
| ----------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
84
|
+
| `--<status>` | `bg-<status>`, `bg-<status>/10`, `border-<status>/20`, `ring-<status>` | Fills and tints: buttons, banner and badge backgrounds, progress bars, borders |
|
|
85
|
+
| `--<status>-text` | `text-<status>`, `text-<status>/80` | Text and icons: messages, banner and badge text, Sign out, checkmarks |
|
|
86
|
+
|
|
87
|
+
The shipped values reach WCAG AA in every use: 4.5:1 for small text, and 3:1
|
|
88
|
+
for a progress bar, a focus ring or a state border against what it touches.
|
|
89
|
+
Light / dark:
|
|
90
|
+
|
|
91
|
+
| Use | Destructive | Success | Warning | Info |
|
|
92
|
+
| ----------------------- | ------------- | -------------- | -------------- | -------------- |
|
|
93
|
+
| Text on the page | 8.0:1 / 7.3:1 | 9.2:1 / 11.5:1 | 9.2:1 / 10.8:1 | 9.0:1 / 10.2:1 |
|
|
94
|
+
| Banner (10% tint) | 6.8:1 / 7.0:1 | 7.9:1 / 9.9:1 | 8.0:1 / 9.6:1 | 7.8:1 / 9.2:1 |
|
|
95
|
+
| Badge (15% tint) | 6.3:1 / 6.8:1 | 7.3:1 / 8.9:1 | 7.4:1 / 8.8:1 | 7.2:1 / 8.5:1 |
|
|
96
|
+
| Hint line (`/80`) | 4.8:1 / 4.8:1 | 4.9:1 / 6.8:1 | 4.9:1 / 6.6:1 | 4.9:1 / 6.3:1 |
|
|
97
|
+
| Label on the fill | 5.5:1 / 5.2:1 | 6.0:1 / 10.2:1 | 6.0:1 / 8.3:1 | 6.7:1 / 6.1:1 |
|
|
98
|
+
| Label, hovered | 4.9:1 / 6.1:1 | 4.9:1 / 8.3:1 | 4.9:1 / 6.8:1 | 5.4:1 / 5.0:1 |
|
|
99
|
+
| Bar on its track | 4.5:1 / 3.4:1 | 4.8:1 / 8.9:1 | 4.8:1 / 7.6:1 | 5.3:1 / 5.7:1 |
|
|
100
|
+
| Bar or ring on the page | 5.8:1 / 3.7:1 | 6.0:1 / 11.5:1 | 6.0:1 / 9.4:1 | 6.7:1 / 6.8:1 |
|
|
101
|
+
|
|
102
|
+
Destructive keeps white text on its fill in dark mode, so its dark fill is a
|
|
103
|
+
mid red, light enough to stand out from the page and dark enough for the white.
|
|
104
|
+
Success, warning and info carry dark text on a light fill in dark mode, as
|
|
105
|
+
before.
|
|
106
|
+
|
|
107
|
+
The SDK's test suite (`src/styles/theme-contrast.test.ts` in the repository)
|
|
108
|
+
measures every use of every status colour on the CSS the build produces,
|
|
109
|
+
light and dark, including the text on sidebar badges, hovered buttons inside
|
|
110
|
+
banners and the current plan column, and fails below the minimum.
|
|
111
|
+
|
|
112
|
+
### If you set only the fill
|
|
113
|
+
|
|
114
|
+
The SDK declares every `--<status>-text` itself, so overriding only
|
|
115
|
+
`--success` (say) changes the fills and leaves success text on the SDK's tone.
|
|
116
|
+
That is on purpose. A fallback such as `var(--success-text, var(--success))`
|
|
117
|
+
would never apply, since the SDK's own declaration is always there, and text
|
|
118
|
+
that followed the fill would bring back the contrast failures this split fixes.
|
|
119
|
+
Instead each text tone is chosen to read on whatever fill you pick: for any
|
|
120
|
+
value within 30 degrees of the shipped hue (saturation 40% or more) that meets
|
|
121
|
+
the fill's own uses (its label at 4.5:1, hovered too, and its bars, rings and
|
|
122
|
+
borders at 3:1), every text use stays at 4.5:1 or better on its 5 to 25% tints,
|
|
123
|
+
in light and dark. The same test checks that sweep.
|
|
124
|
+
|
|
125
|
+
For text in your own hue, set `--<status>-text` as well, in your light rule and
|
|
126
|
+
your dark one, to a value with 4.5:1 on your `--background`, on `--muted`, and
|
|
127
|
+
on a 25% tint of your fill over each. Check it again if you change
|
|
128
|
+
`--background` or `--muted`. To go back to a single value, write
|
|
129
|
+
`--success-text: var(--success);` in both rules, knowing that a fill dark
|
|
130
|
+
enough for white text is usually too dark to read as text in dark mode.
|
|
64
131
|
|
|
65
132
|
## Dark mode
|
|
66
133
|
|
|
@@ -89,10 +156,11 @@ CSS has no media query of its own, so nothing competes with it.
|
|
|
89
156
|
|
|
90
157
|
## Components that work without the stylesheet
|
|
91
158
|
|
|
92
|
-
`FullScreenLoader` and the `ErrorBoundary` fallback use inline styles so they render correctly before/without CSS loading. They still read the variables (with hard-coded fallbacks), so brand overrides apply to them too once your CSS is present.
|
|
159
|
+
`FullScreenLoader` and the `ErrorBoundary` fallback use inline styles so they render correctly before/without CSS loading. They still read the variables (with hard-coded fallbacks), so brand overrides apply to them too once your CSS is present. The `ErrorBoundary` fallback carries the `.saas-os-ui` class, so it gets the SDK's tokens (and yours, written against that selector) even though it renders outside every other SDK root.
|
|
93
160
|
|
|
94
161
|
## Rules for SDK contributors
|
|
95
162
|
|
|
96
163
|
- Never use Tailwind palette classes (`gray-*`, `blue-*`, `red-*`, `green-*`, `amber-*`, `slate-*`, `bg-white`, `text-white`) in components — they bypass theming. Use the semantic tokens above.
|
|
97
164
|
- Soft status fills are alpha tints of the token: `bg-success/10` (background), `border-success/20` (border), `text-success` (text).
|
|
165
|
+
- Status text is `text-<status>`, which `tailwind.config.js` points at `--<status>-text`; fills, tints, bars, rings and borders stay on `--<status>`. Never colour text with `hsl(var(--success))` (or any status fill) in an inline style or an arbitrary value, paint a status background at an alpha other than 5, 10, 15, 80 or 90%, or put a progress bar on a muted track: `src/styles/theme-contrast.test.ts` fails on each.
|
|
98
166
|
- CI-check locally: `grep -rE '(bg|text|border|divide)-[a-z]+-[0-9]{2,3}|bg-white|text-white|#[0-9a-fA-F]{3,6}' src/components src/providers --include='*.tsx'` should return nothing (the pattern catches every Tailwind palette shade and raw hex).
|
package/docs/UI-CONFIG.md
CHANGED
|
@@ -51,6 +51,14 @@ Fine-grained switches inside each settings screen. All default to `true`.
|
|
|
51
51
|
| `settings.profile.country` | Country dropdown |
|
|
52
52
|
| `settings.profile.currency` | Currency dropdown |
|
|
53
53
|
| `settings.profile.timezone` | Timezone dropdown |
|
|
54
|
+
| `settings.profile.changeEmail` | Change-email flow (offered only while the server allows it for the user) |
|
|
55
|
+
| `settings.profile.username` | Username field (shown while usernames are on or the user has one) |
|
|
56
|
+
| `settings.security.password` | Change-password form (hide it for an app without password sign-in) |
|
|
57
|
+
| `settings.security.phone` | Phone number section (hide it for an app that sends no phone codes) |
|
|
58
|
+
| `settings.security.twoStep` | Two-step sign-in section (also hidden while the org has it turned off) |
|
|
59
|
+
| `settings.security.connectedAccounts` | Connected accounts section (also hidden while there is nothing to list) |
|
|
60
|
+
| `settings.security.wallets` | Connect a wallet buttons (connected wallets stay listed) |
|
|
61
|
+
| `settings.security.deleteAccount` | Delete account section, and the notice atop every screen while scheduled |
|
|
54
62
|
| `settings.security.passkeyRename` | Rename action on passkey rows |
|
|
55
63
|
| `settings.security.passkeyDelete` | Delete action on passkey rows |
|
|
56
64
|
| `settings.general.nameEdit` | Workspace name field |
|
|
@@ -80,6 +88,17 @@ ui={{
|
|
|
80
88
|
}}
|
|
81
89
|
```
|
|
82
90
|
|
|
91
|
+
## Impersonation banner
|
|
92
|
+
|
|
93
|
+
While an app owner is viewing the app as the signed-in person (the profile's
|
|
94
|
+
`impersonation`), the provider pins `<ImpersonationBanner />` to the bottom of
|
|
95
|
+
the window. `impersonationBanner: false` hides it, for an app that places
|
|
96
|
+
`<ImpersonationBanner fixed={false} />` in its own layout instead.
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
ui={{ impersonationBanner: false }}
|
|
100
|
+
```
|
|
101
|
+
|
|
83
102
|
## Workspace switcher
|
|
84
103
|
|
|
85
104
|
Client-side switcher control, ANDed with the server settings (`showSwitcher`, `canCreateWorkspace`):
|
|
@@ -93,10 +112,10 @@ Client-side switcher control, ANDed with the server settings (`showSwitcher`, `c
|
|
|
93
112
|
|
|
94
113
|
## Behavior
|
|
95
114
|
|
|
96
|
-
| Config key | Effect
|
|
97
|
-
| ----------------------------- |
|
|
98
|
-
| `behavior.autoOpenPlanDialog` | `false` disables the automatic plan-picker popup when a workspace has no subscription. Explicit `selectPlan` deep links (e.g. from `PricingPage`) still open it. |
|
|
99
|
-
| `behavior.trialEndingDays` | Global default threshold for `<WhenTrialEnding>` (default 3). A `daysThreshold` prop still wins per usage.
|
|
115
|
+
| Config key | Effect |
|
|
116
|
+
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| `behavior.autoOpenPlanDialog` | `false` disables the automatic plan-picker popup when a workspace has no subscription, and stops `createWorkspace()` opening Plan & Billing on the new workspace (which hiding `settings.sections.subscription` also does). Explicit `selectPlan` deep links (e.g. from `PricingPage`) still open it. |
|
|
118
|
+
| `behavior.trialEndingDays` | Global default threshold for `<WhenTrialEnding>` (default 3). A `daysThreshold` prop still wins per usage. |
|
|
100
119
|
|
|
101
120
|
## String overrides (`messages`)
|
|
102
121
|
|