import * as React from 'react'; import { ComponentType, AnchorHTMLAttributes, ReactNode, ErrorInfo, ReactElement } from 'react'; import { FallbackProps } from 'react-error-boundary'; type AuthToken = string | undefined; interface Auth { /** * Which part of the request do we use to send the auth? * * @default 'header' */ in?: 'header' | 'query' | 'cookie'; /** * A unique identifier for the security scheme. * * Defined only when there are multiple security schemes whose `Auth` * shape would otherwise be identical. */ key?: string; /** * Header or query parameter name. * * @default 'Authorization' */ name?: string; scheme?: 'basic' | 'bearer'; type: 'apiKey' | 'http'; } interface SerializerOptions { /** * @default true */ explode: boolean; style: T; } type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; type ObjectStyle = 'form' | 'deepObject'; type QuerySerializer = (query: Record) => string; type BodySerializer = (body: unknown) => unknown; type QuerySerializerOptionsObject = { allowReserved?: boolean; array?: Partial>; object?: Partial>; }; type QuerySerializerOptions = QuerySerializerOptionsObject & { /** * Per-parameter serialization overrides. When provided, these settings * override the global array/object settings for specific parameter names. */ parameters?: Record; }; type HttpMethod = 'connect' | 'delete' | 'get' | 'head' | 'options' | 'patch' | 'post' | 'put' | 'trace'; type Client$1 = { /** * Returns the final request URL. */ buildUrl: BuildUrlFn; getConfig: () => Config; request: RequestFn; setConfig: (config: Config) => Config; } & { [K in HttpMethod]: MethodFn; } & ([SseFn] extends [never] ? { sse?: never; } : { sse: { [K in HttpMethod]: SseFn; }; }); interface Config$1 { /** * Auth token or a function returning auth token. The resolved value will be * added to the request payload as defined by its `security` array. */ auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; /** * A function for serializing request body parameter. By default, * {@link JSON.stringify()} will be used. */ bodySerializer?: BodySerializer | null; /** * An object containing any HTTP headers that you want to pre-populate your * `Headers` object with. * * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} */ headers?: RequestInit['headers'] | Record; /** * The request method. * * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} */ method?: Uppercase; /** * A function for serializing request query parameters. By default, arrays * will be exploded in form style, objects will be exploded in deepObject * style, and reserved characters are percent-encoded. * * This method will have no effect if the native `paramsSerializer()` Axios * API function is used. * * {@link https://swagger.io/docs/specification/serialization/#query View examples} */ querySerializer?: QuerySerializer | QuerySerializerOptions; /** * A function validating request data. This is useful if you want to ensure * the request conforms to the desired shape, so it can be safely sent to * the server. */ requestValidator?: (data: unknown) => Promise; /** * A function transforming response data before it's returned. This is useful * for post-processing data, e.g., converting ISO strings into Date objects. */ responseTransformer?: (data: unknown) => Promise; /** * A function validating response data. This is useful if you want to ensure * the response conforms to the desired shape, so it can be safely passed to * the transformers and returned to the user. */ responseValidator?: (data: unknown) => Promise; } type ServerSentEventsOptions = Omit & Pick & { /** * Fetch API implementation. You can use this option to provide a custom * fetch instance. * * @default globalThis.fetch */ fetch?: typeof fetch; /** * Implementing clients can call request interceptors inside this hook. */ onRequest?: (url: string, init: RequestInit) => Promise; /** * Callback invoked when a network or parsing error occurs during streaming. * * This option applies only if the endpoint returns a stream of events. * * @param error The error that occurred. */ onSseError?: (error: unknown) => void; /** * Callback invoked when an event is streamed from the server. * * This option applies only if the endpoint returns a stream of events. * * @param event Event streamed from the server. * @returns Nothing (void). */ onSseEvent?: (event: StreamEvent) => void; serializedBody?: RequestInit['body']; /** * Default retry delay in milliseconds. * * This option applies only if the endpoint returns a stream of events. * * @default 3000 */ sseDefaultRetryDelay?: number; /** * Maximum number of retry attempts before giving up. */ sseMaxRetryAttempts?: number; /** * Maximum retry delay in milliseconds. * * Applies only when exponential backoff is used. * * This option applies only if the endpoint returns a stream of events. * * @default 30000 */ sseMaxRetryDelay?: number; /** * Optional sleep function for retry backoff. * * Defaults to using `setTimeout`. */ sseSleepFn?: (ms: number) => Promise; url: string; }; interface StreamEvent { data: TData; event?: string; id?: string; retry?: number; } type ServerSentEventsResult = { stream: AsyncGenerator ? TData[keyof TData] : TData, TReturn, TNext>; }; type ErrInterceptor = (error: Err, /** response may be undefined due to a network error where no response object is produced */ response: Res | undefined, /** request may be undefined, because error may be from building the request object itself */ request: Req | undefined, options: Options) => Err | Promise; type ReqInterceptor = (request: Req, options: Options) => Req | Promise; type ResInterceptor = (response: Res, request: Req, options: Options) => Res | Promise; declare class Interceptors { fns: Array; clear(): void; eject(id: number | Interceptor): void; exists(id: number | Interceptor): boolean; getInterceptorIndex(id: number | Interceptor): number; update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false; use(fn: Interceptor): number; } interface Middleware { error: Interceptors>; request: Interceptors>; response: Interceptors>; } type ResponseStyle = 'data' | 'fields'; interface Config extends Omit, Config$1 { /** * Base URL for all requests made by this client. */ baseUrl?: T['baseUrl']; /** * Fetch API implementation. You can use this option to provide a custom * fetch instance. * * @default globalThis.fetch */ fetch?: typeof fetch; /** * Please don't use the Fetch client for Next.js applications. The `next` * options won't have any effect. * * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. */ next?: never; /** * Return the response data parsed in a specified format. By default, `auto` * will infer the appropriate method from the `Content-Type` response header. * You can override this behavior with any of the {@link Body} methods. * Select `stream` if you don't want to parse response data at all. * * @default 'auto' */ parseAs?: 'arrayBuffer' | 'auto' | 'blob' | 'formData' | 'json' | 'stream' | 'text'; /** * Should we return only data or multiple fields (data, error, response, etc.)? * * @default 'fields' */ responseStyle?: ResponseStyle; /** * Throw an error instead of returning it in the response? * * @default false */ throwOnError?: T['throwOnError']; } interface RequestOptions extends Config<{ responseStyle: TResponseStyle; throwOnError: ThrowOnError; }>, Pick, 'onRequest' | 'onSseError' | 'onSseEvent' | 'sseDefaultRetryDelay' | 'sseMaxRetryAttempts' | 'sseMaxRetryDelay'> { /** * Any body that you want to add to your request. * * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} */ body?: unknown; path?: Record; query?: Record; /** * Security mechanism(s) to use for the request. */ security?: ReadonlyArray; url: Url; } interface ResolvedRequestOptions extends RequestOptions { headers: Headers; serializedBody?: string; } type RequestResult = ThrowOnError extends true ? Promise ? TData[keyof TData] : TData : { data: TData extends Record ? TData[keyof TData] : TData; request: Request; response: Response; }> : Promise ? TData[keyof TData] : TData) | undefined : ({ data: TData extends Record ? TData[keyof TData] : TData; error: undefined; } | { data: undefined; error: TError extends Record ? TError[keyof TError] : TError; }) & { /** request may be undefined, because error may be from building the request object itself */ request?: Request; /** response may be undefined, because error may be from building the request object itself or from a network error */ response?: Response; }>; interface ClientOptions$1 { baseUrl?: string; responseStyle?: ResponseStyle; throwOnError?: boolean; } type MethodFn = (options: Omit, 'method'>) => RequestResult; type SseFn = (options: Omit, 'method'>) => Promise>; type RequestFn = (options: Omit, 'method'> & Pick>, 'method'>) => RequestResult; type BuildUrlFn = ; query?: Record; url: string; }>(options: TData & Options) => string; type Client = Client$1 & { interceptors: Middleware; }; interface TDataShape { body?: unknown; headers?: unknown; path?: unknown; query?: unknown; url: string; } type OmitKeys = Pick>; type Options = OmitKeys, 'body' | 'path' | 'query' | 'url'> & ([TData] extends [never] ? unknown : Omit); type ClientOptions = { baseUrl: 'https://api.stage.tesouro.com' | 'https://api.sandbox.stage.tesouro.com' | 'https://api.stage.business-banking.app' | 'https://api.tesouro.com' | 'https://api.sandbox.tesouro.com' | 'https://api.business-banking.app' | (string & {}); }; type CreditCardStatus = 'PENDING_ACTIVATION' | 'ACTIVE' | 'LOCKED' | 'CLOSED'; type CreditCardType = 'DIGITAL' | 'PHYSICAL'; /** * The rolling period a velocity control's amount and usage limits are evaluated over. * `TRANSACTION` applies the limit per-transaction and therefore carries no usage limit. */ type VelocityWindow = 'DAY' | 'LIFETIME' | 'MONTH' | 'TRANSACTION' | 'WEEK'; type EmbeddedClient = Client; type ExtractLiterals = T extends string ? string extends T ? never : T : never; type BaseUrl = ExtractLiterals; type LinkComponentProps = AnchorHTMLAttributes & { children?: ReactNode; }; type LinkComponent = ComponentType; /** * The UI frameworks a widget's UI layer can render with. * * - `shadcn` — the shadcn/Tailwind implementation. This is the default and the * implicit fallback, so existing consumers that select nothing keep rendering * shadcn. * - `tecton` — the Tecton implementation. * * Declared as an `as const` object (not a TypeScript `enum`) per repo * convention. */ declare const UIFramework: { readonly Shadcn: "shadcn"; readonly Tecton: "tecton"; }; type UIFramework = (typeof UIFramework)[keyof typeof UIFramework]; interface PaginationFooterLabels { rowsPerPageLabel: string; previousPageButton: string; nextPageButton: string; } /** * The implementation a widget renders with. * * - `native` — the Tesouro-native implementation. This is the default and the * implicit fallback, so consumers that select nothing keep rendering native. * - `monite` — the Monite SDK implementation. * * Declared as an `as const` object (not a TypeScript `enum`) per repo * convention. */ declare const Implementation: { readonly Native: "native"; readonly Monite: "monite"; }; type Implementation = (typeof Implementation)[keyof typeof Implementation]; /** * The settable fields of the widget config cascade. * * This is the type accepted by {@link setGlobalWidgetConfig} and all provider props. * It intentionally excludes `initResponse`, which is populated automatically by the * provider after a successful fetch and must never be set manually. * * The `null` vs `undefined` distinction on `widgetToken` and `organizationId` is intentional: * - `undefined` — not set at this level; inherit from the nearest ancestor or global store. * - `null` — explicitly cleared; downstream sees "no value" even if an ancestor had one * (e.g. after logout or deliberate de-scoping). * * @see {@link WidgetConfig} for the resolved output type (includes `initResponse`) * @see {@link RootWidgetProvider} * @see {@link WidgetProvider} * @see {@link setGlobalWidgetConfig} */ interface WidgetConfigInput { /** * Base URL of the Tesouro embedded API (e.g. `"https://api.tesouro.com"`). * * When omitted the nearest ancestor's `baseUrl` or the global store value is used. * Changing this recreates the underlying HTTP client so all subsequent requests * go to the new host. */ baseUrl?: BaseUrl; /** * Bearer token used to authenticate widget requests. * * Injected as `Authorization: Bearer ` on every outgoing request via an * interceptor on the scoped HTTP client. Token updates are picked up immediately * without recreating the client. * * - `string` — send this token on all requests from this level downward. * - `null` — explicitly cleared; no auth header is sent and fetching is suppressed. * - `undefined` — not set at this level; inherit from the nearest ancestor or global store. */ widgetToken?: string | null; /** * Organization ID forwarded as the `x-organization-id` request header. * * Passed through {@link EmbedApiProvider} context rather than the auth interceptor, * so individual data-access hooks can opt in per-request. * * - `string` — use this organization for downstream data requests. * - `null` — explicitly cleared; queries that require an org ID will be disabled. * - `undefined` — not set at this level; inherit from the nearest ancestor or global store. * * When `undefined` across the **whole** cascade (no prop, no ancestor, no global * value), the resolved org defaults to the loaded `initResponse.organizationId` * (see {@link WidgetConfig.initResponse}) once the widget-init fetch settles. This * is the lowest-priority fallback — any explicit `string` or `null` at any cascade * level wins, and an explicit `null` is preserved and never falls back. * * Only an **explicit** ancestor org is inherited. An ancestor's *init-derived* * default does not propagate into a descendant that owns its own fetch (its own * `baseUrl`/`widgetToken`); such a descendant defaults to its own * `initResponse.organizationId` instead, so it never sends an ancestor's org with * its own token. */ organizationId?: string | null; /** * Optional post-creation hook for the scoped HTTP client. * * Called once after the provider creates its scoped {@link EmbeddedClient} and * applies the built-in `Authorization: Bearer` interceptor. Receives the * fully-configured client and must return the client to be used for the lifetime of * this provider level — either the same instance (with additional interceptors * attached) or a new client entirely. * * **Order:** The built-in auth interceptor is always applied first. `configClient` * is called on top of it, so any interceptors you add here run after auth is set. * * **Any prop triggers a scoped client.** A {@link WidgetProvider} creates its own * scoped client whenever any prop is set — including `configClient` alone, without * `baseUrl` or `widgetToken`. Only a fully props-free pass-through provider skips * client creation and never calls this function. * * **Stability:** The function reference is included in the client creation memo's * dependency array. Passing an unstable (inline) function recreates the client on * every render. Stabilize with `useCallback` or define the function outside the * component. * * **Cascade:** Inherits from the nearest ancestor when `undefined`. A child * {@link WidgetProvider} that creates its own scoped client will use the resolved * `configClient` from the cascade unless it provides its own override. * * @example * ```tsx * const addLogging = useCallback( * (client: EmbeddedClient) => { * client.interceptors.request.use((req) => { * console.log('[widget]', req.method, req.url); * return req; * }); * return client; * }, * [], * ); * * * * * ``` */ configClient?: (client: EmbeddedClient) => EmbeddedClient; /** * Overrides the widget-gateway routing decision for the scoped HTTP client. * * Any caller reaching the Tesouro API with a widget token must route data * requests through the widget gateway: prefix the path with * `/api/widget-gateway/proxy` and carry the token as `X-Widget-Token`. The * provider applies both automatically per request when the request origin is * a known Tesouro API host (`WIDGET_GATEWAY_HOSTS`, derived from the * generated `ClientOptions['baseUrl']`); `/api/widget-gateway/*` paths (the * init round-trip) always pass through untouched. * * - `undefined` — decide from the request origin, as above. Inherits from * the nearest ancestor or global store like every other config field. * - `true` — always apply the rewrite, even for an unlisted base URL (e.g. a * custom domain in front of the gateway). * - `false` — never apply it. For hosts that route widget requests their own * way, such as a same-origin BFF whose `configClient` retargets every * request. * * Independent of {@link configClient}: a host that only adds a header keeps * the built-in routing, and the built-in interceptor runs before any * `configClient` interceptor. */ gatewayRouting?: boolean; /** * Component the embedded widgets should render in place of plain `` tags. * * Pass e.g. Next.js's `Link` to make in-app navigation use the host router. * Cascades like other config: provider prop > nearest ancestor > global store. * When no value is set anywhere, widgets fall back to a plain `` element. */ linkComponent?: LinkComponent; /** * Which UI framework the widget UI layer should render with. * * Lets a consuming context bind widgets to either the shadcn/Tailwind or the * Tecton implementation behind the same outward-facing API. The selection is * a presentation concern only — it cascades through the provider tree exactly * like {@link linkComponent} and is read by UI libraries via `useUIFramework`; * it never appears in any widget's feature-library or component props. * * - `'shadcn'` — the shadcn/Tailwind implementation. * - `'tecton'` — the Tecton implementation. * - `null` / `undefined` — not set at this level; inherit from the nearest * ancestor or global store, falling back to `shadcn` when unset everywhere. * `shadcn` is the implicit default, so existing consumers need no changes. */ uiFramework?: UIFramework | null; /** * Which implementation a widget renders with. * * Lets a consuming context bind widgets to either the Tesouro-native or the * Monite SDK implementation behind the same outward-facing API. It cascades * through the provider tree exactly like {@link linkComponent} and * {@link uiFramework} — provider prop > nearest ancestor > global store — and * is read via `useImplementation`. * * - `'native'` — the Tesouro-native implementation. * - `'monite'` — the Monite SDK implementation. * - `null` / `undefined` — not set at this level; inherit from the nearest * ancestor or global store, falling back to `native` when unset everywhere. * `native` is the implicit default, so existing consumers need no changes. */ implementation?: Implementation | null; } /** * Props shared by every analytics-owner-capable widget provider. * * Combines the full settable cascade ({@link WidgetConfigInput}) with the * analytics opt-out honored by analytics owners. Both {@link RootWidgetProvider} * and {@link WidgetProvider} build their public props on top of this; the latter * adds error-boundary props of its own. * * @see {@link WidgetConfigInput} for per-field cascade and `null` vs omitted semantics */ interface WidgetProviderBaseProps extends WidgetConfigInput { /** * Opt out of all analytics capture and prevent PostHog from loading. Default `true`. * * Honored only by an analytics **owner** — a {@link RootWidgetProvider} or a * standalone {@link WidgetProvider} with no parent provider. When `false`, * owner-bound `track` calls in this subtree become no-ops and the PostHog * installer is never dynamically imported for this owner's environment. * Setting it on a nested {@link WidgetProvider} is ignored in v1 (a one-time * `console.warn` is emitted to make the no-op discoverable). */ analytics?: boolean; } /** * Copy for the states `WidgetProvider` renders *itself*, in place of the widget: * the error-boundary fallback and the disclosures gate. A host overrides any * subset through the `providerLabels` prop, which cascades like * `disclosuresAcceptance`. * * Not named `Labels` on the public surface: a widget's own `labels` prop is a * different thing, and these two travel together on every widget's props type. */ interface WidgetProviderLabels { /** Default copy for the built-in error boundary fallback. */ errorBoundaryFallback: string; /** Heading when init reports disclosures are required and not yet accepted. */ disclosuresRequiredTitle: string; /** Heading while a token-only refresh leaves init stale for the live token. */ disclosuresRefreshingTitle: string; /** Supporting copy while the accept action is withheld for a stale init. */ disclosuresRefreshingDescription: string; } /** * Props for {@link WidgetProvider}. * * All fields are optional. When **all** are omitted the provider is a transparent * pass-through: no fetch is issued and all resolved values cascade unchanged from * the nearest ancestor. */ interface WidgetProviderProps extends WidgetProviderBaseProps { /** * Fallback rendered when a render-time exception is caught inside this * provider's subtree. Pass either a `ReactNode` (rendered directly) or a * render-prop receiving `{ error, resetErrorBoundary }` from * `react-error-boundary`. Default is a plain `role="alert"` div whose copy * comes from `providerLabels.errorBoundaryFallback`. */ errorFallback?: ReactNode | ((props: FallbackProps) => ReactNode); /** * Called once when the boundary catches an error, before the fallback * renders. Use for telemetry / Sentry / partner logging. Exceptions * thrown from `onError` propagate per `react-error-boundary` semantics. */ onError?: (error: unknown, info: ErrorInfo) => void; /** * Accept surface shown when the caller owes disclosures — an INVITED * teammate (including `NOT_REQUIRED` orgs, who still need Accept to * activate) or an ACTIVE user who owes a new version. Pass * ``. Cascades like `linkComponent`. * WidgetProvider cannot import the widget itself (cycle). */ disclosuresAcceptance?: ReactNode; /** * Overrides for the copy this provider renders in place of the widget: the * built-in error-boundary fallback and the disclosures gate. Any subset; * unlisted keys keep their defaults. Cascades like `linkComponent`. * * Named `providerLabels` rather than `labels` because a widget's own * `labels` prop sits alongside this one on the same props type. */ providerLabels?: Partial; } /** * Which issuing product a card belongs to. A card list shows exactly one * program at a time, and the value selects the endpoint the data-access layer * calls: `credit` → `/embedded-banking/v1/credit-cards`, `debit` → * `/embedded-banking/v1/debit-cards`. * * Defined here rather than in either widget because `CardsWidget` and * `CardDetailsWidget` both publish it as a required public prop. Declared * inline in each, the literal would become public API twice with no single * definition — and `cards-widget/ui` ↔ `card-details-widget/ui` imports are * lint-banned while the reverse feature import would be an Nx cycle, so * `shared/feature` is the only home that serves both without duplication. * * Deliberately **not** the generated `CardTypeEnum` (`credit | debit | prepaid * | unknown`), which greps as an exact match for this concept but belongs to * the unrelated snake_case third-party schema family (its sibling fields are * `card_type`, `last4`, `background_color`) and carries two members neither * widget supports. In this codebase "card type" is the DIGITAL/PHYSICAL form * factor instead — see `CardType` in `./cardPresentation`. * * Const-object-plus-type rather than a bare union, mirroring `UIFramework` in * `shared/ui` — the other two-member literal that ships as a public prop type. */ declare const CardProgram: { readonly Credit: "credit"; readonly Debit: "debit"; }; type CardProgram = (typeof CardProgram)[keyof typeof CardProgram]; /** * A card's lifecycle status, program-agnostic. Aliases the credit union because * one of the two has to be the base; the parity assertions below are what keep * that choice arbitrary. */ type CardStatus = CreditCardStatus; /** * A card's physical form factor — the codebase's meaning of "card type". The * credit/debit axis is `CardProgram`, not this. */ type CardType = CreditCardType; interface BalancesWidgetLabels { /** Card header title (e.g. next to the building icon) */ widgetTitle: string; /** Shown under the total amount */ totalAvailableBalance: string; /** Full explanation shown in the info tooltip */ totalAvailableBalanceTooltip: string; /** Short label for the info control (e.g. `aria-label`) */ totalAvailableBalanceInfoAriaLabel: string; /** CTA to open the full accounts list */ viewAllAccounts: string; /** Shown when there are no balances to list */ noBalancesFound: string; /** Shown when the bank account request failed */ loadErrorTitle: string; loadErrorDescription: string; /** Label for retry after a failed load */ loadErrorRetry: string; } interface BalancesWidgetWithContextProps { /** * Maximum number of account rows to render in-card. Anything beyond this * cap is reachable only via the View-all CTA. Default 5. */ maxAccounts?: number; labels?: Partial; onBalanceRowClick?: (accountId: string) => void; onViewAllAccountsClick?: () => void; } type BalancesWidgetPassthroughProps = Pick; interface BalancesWidgetProps extends WidgetProviderProps, BalancesWidgetPassthroughProps { } interface Labels$2 { bankingTaglinePrefix: string; availableBalanceLabel: string; availableBalanceTooltip: string; accountNumberLabel: string; routingNumberLabel: string; addAccountButton: string; emptyAccountsLabel: string; createDialogTitle: string; createDialogSubtitle: string; createNicknameLabel: string; createNicknameRequiredError: string; /** Use `{agreementLink}` for the deposit-agreement link placeholder. */ createLegalTemplate: string; /** * Rendered instead of {@link Labels.createLegalTemplate} when the host supplies * no `depositAgreementUrl`. Must not reference an agreement — see * `CreateAccountDialog`. */ createLegalTemplateWithoutAgreement: string; createLegalAgreementLink: string; createTeamAccessHeading: string; createDialogCloseAriaLabel: string; createCancelButton: string; createSubmitButton: string; teamMembersLoadingLabel: string; teamMembersErrorTitle: string; teamMembersEmptyLabel: string; teamMemberAccessTooltip: string; toggleAccountNumberVisibility: string; copyAccountNumber: string; copyRoutingNumber: string; copiedLabel: string; openAccountAriaLabel: string; loadingLabel: string; errorTitle: string; } interface Labels$1 { backToAccounts: string; bankingTaglinePrefix: string; availableBalanceLabel: string; availableBalanceTooltip: string; transactionsTab: string; statementsTab: string; detailsTab: string; noTransactionsLabel: string; transactionDateColumn: string; transactionDescriptionColumn: string; transactionAmountColumn: string; transactionBalanceColumn: string; exportButton: string; exportDialogTitle: string; exportDialogDescription: string; exportDialogCloseAriaLabel: string; exportStartDateLabel: string; exportEndDateLabel: string; exportStartDatePlaceholder: string; exportEndDatePlaceholder: string; exportCancelButton: string; exportDownloadButton: string; noStatementsLabel: string; editAccountMenuItem: string; editDialogTitle: string; editNicknameLabel: string; editDialogCloseAriaLabel: string; editCancelButton: string; editSaveButton: string; editErrorTitle: string; detailsSectionTitle: string; receiveMoneySectionTitle: string; domesticWireTitle: string; domesticWireIntro: string; domesticWireNote: string; wireDetailsSectionTitle: string; bankNameLabel: string; bankAddressLabel: string; thingsToKnowHeading: string; thingsToKnowBody: string; supportQuestionsPrefix: string; supportTeamLink: string; routingNumberLabel: string; accountNumberLabel: string; copyLabel: string; copiedLabel: string; toggleAccountNumberVisibility: string; loadingLabel: string; errorTitle: string; } interface FeatureLabels$2 { accountFallback: string; createSuccessToast: string; editSuccessToast: string; copyAccountNumberToast: string; copyRoutingNumberToast: string; /** Account-details export toast — forwarded to AccountDetailsWidget. */ exportLoadingToast: string; /** Account-details export toast — forwarded to AccountDetailsWidget. */ exportSuccessToast: string; /** Account-details export toast — forwarded to AccountDetailsWidget. */ exportErrorToast: string; } interface BankAccountsWidgetProps extends WidgetProviderProps { labels?: Partial; accountDetailsLabels?: Partial; featureLabels?: Partial; isBankingTaglineVisible?: boolean; /** * Bank logo for the tagline row. The widget ships no bank artwork of its own — * an embeddable library must not depend on a Tesouro-hosted asset a host page's * content-security policy can block (PLAT-1179). Omit it and the row renders * the bank name alone. */ bankLogoSrc?: string; bankLogoAlt?: string; /** Deposit-agreement PDF linked from the create-account legal copy when supplied. */ depositAgreementUrl?: string; /** Bank postal address shown in the account-details domestic wire panel. */ bankAddress?: string; /** Support URL linked from the account-details domestic wire copy. */ supportTeamUrl?: string; 'data-testid'?: string; /** * Controlled selection. Pass `null` to clear, a string to select an account. * Leave undefined for uncontrolled behavior (driven by `defaultSelectedAccountId`). */ selectedAccountId?: string | null; /** Initial selected account when uncontrolled. */ defaultSelectedAccountId?: string | null; /** Called when the user opens account details or goes back to the list. */ onSelectedAccountIdChange?: (accountId: string | null) => void; } /** * Every key of `T` optional, recursively, so a consumer can override one nested * label without restating its siblings. Defined locally because the repo has no * shared deep-partial type and does not depend on `type-fest`. */ type PartialDeep$4 = { [K in keyof T]?: T[K] extends object ? PartialDeep$4 : T[K]; }; interface CardsListColumnHeaderLabels { cardName: string; fundingAccount: string; cardholder: string; status: string; limit: string; } /** * Screen-reader-only copy naming the details sheet a row click opens. * * Both strings are invisible by design: the sheet's visible header belongs to * whatever the owner slots into it (`CardDetailsPanel`, via `CardsWidget`), and a * second visible title would duplicate it. They exist because the sheet is a * dialog, and a dialog without an accessible name is announced as nothing. */ interface CardsListDetailsSheetLabels { /** Accessible name for the sheet. */ title: string; /** Accessible description, announced after the name. */ description: string; } /** * Every user-facing string the cards list renders, including screen-reader-only * copy and the decorative separators. * * There is intentionally **no** card-type or card-status map here: the row * arrives with `typeLabel` and `statusLabel` already resolved, so the * `DIGITAL`/`PHYSICAL` and `PENDING_ACTIVATION`/`ACTIVE`/`LOCKED`/`CLOSED` maps * live in `shared/feature` keyed on the generated types (EMBD-4404 amendment; * see `statusBadge.ts`). That is also where the "label keys carry the code term, * values carry the copy" rule applies — `digital`, never `virtual`, so a later * wording change is not a breaking rename. */ interface CardsListLabels { title: string; /** Accessible name for the list region. */ regionLabel: string; columnHeaders: CardsListColumnHeaderLabels; /** Screen-reader name for the unheaded trailing row-actions column. */ rowActionsColumn: string; showMyCardsToggle: string; createCardButton: string; activateButton: string; /** Accessible name for a row's Activate button; the card name is appended. */ activateButtonForCard: string; /** Accessible name for a row's open-details control; card name appended. */ selectCardButton: string; /** Separator between a card's nickname and its last four digits. */ cardNameSeparator: string; /** Stands in for the limit when a card has none set. */ noLimit: string; /** Announced while a page of cards is loading. */ loadingLabel: string; emptyTitle: string; emptyDescription: string; errorTitle: string; errorDescription: string; retryButton: string; pagination: PaginationFooterLabels; /** * Copy for the details sheet a row click opens. Rendered by * `CardsListDetailsSheet` rather than by the table, but part of this surface * because it is one screen and one `labels` prop — the same reason `pagination` * sits here for a footer that lives in `shared/ui`. */ detailsSheet: CardsListDetailsSheetLabels; /** * Screen-reader copy for the same sheet when Create card is slotted in, rather * than a selected card's details. Same chrome, different accessible name. */ createSheet: CardsListDetailsSheetLabels; } /** Accessible copy for the panel header's controls. */ interface CardDetailsHeaderLabels { /** Fallback title when the card has no nickname. */ untitledCard: string; /** Accessible name for the kebab that opens the card actions menu. */ menuTriggerAriaLabel: string; /** Accessible name for the close (X) control. */ closeAriaLabel: string; } /** Accessible copy for the simplified card face. */ interface CardDetailsCardFaceLabels { /** Alt text for the bank logo when the caller supplies no `bankLogoAlt`. */ bankLogoAlt: string; } /** Row headings and per-row copy affordance names for the details table. */ interface CardDetailsFieldLabels { cardholder: string; nickname: string; billingAddress: string; /** Sub-header over the merchant categories / velocity control rows. */ cardControlsHeading: string; allowedCategories: string; spendLimit: string; perTransactionMax: string; /** Stands in for a row whose value the card does not carry. */ emptyValue: string; copyCardholderAriaLabel: string; copyNicknameAriaLabel: string; copyBillingAddressAriaLabel: string; } /** Copy for the controls the owner can switch on alongside the details. */ interface CardDetailsActionLabels { /** Primary control that opens the Activate Card form. */ activateCard: string; /** Secondary control that asks the owner to reveal the card's credentials. */ viewCard: string; /** The same control, once the credentials are on screen. */ hideCard: string; } /** * Copy for the revealed card credentials. The values themselves never have a * default here — the panel is handed them or renders the field blank. */ interface CardDetailsCredentialsLabels { cardNumber: string; expirationDate: string; securityCode: string; copyCardNumberAriaLabel: string; copyExpirationDateAriaLabel: string; copySecurityCodeAriaLabel: string; /** Shown next to the control when the reveal failed. */ errorDescription: string; } /** Copy for the three non-loaded screens. */ interface CardDetailsStatusScreenLabels { /** Accessible name announced while the panel is loading. */ loadingAriaLabel: string; errorTitle: string; errorDescription: string; errorRetry: string; notFoundTitle: string; notFoundDescription: string; } interface CardDetailsPanelLabels { header: CardDetailsHeaderLabels; cardFace: CardDetailsCardFaceLabels; fields: CardDetailsFieldLabels; actions: CardDetailsActionLabels; credentials: CardDetailsCredentialsLabels; status: CardDetailsStatusScreenLabels; } /** Chrome around the form. */ interface ActivateCardHeaderLabels { title: string; /** Accessible name for the close (X) control. */ closeAriaLabel: string; } /** Accessible copy for the card face this panel reuses. */ interface ActivateCardCardFaceLabels { /** Alt text for the bank logo when the caller supplies no `bankLogoAlt`. */ bankLogoAlt: string; } /** Field headings, placeholders and the copy behind each affordance. */ interface ActivateCardFieldLabels { expirationDate: string; expirationDatePlaceholder: string; securityCode: string; securityCodePlaceholder: string; /** Accessible name for the (?) control that reveals the explanation. */ securityCodeInfoAriaLabel: string; /** The explanation itself, shown in the tooltip. */ securityCodeInfo: string; } /** One message per failure the panel can mark. */ interface ActivateCardValidationLabels { impossibleMonth: string; pastExpiration: string; /** * Shown when the server rejects the pair. Names both values because the * verify endpoint withholds which one missed — see `ActivateCardFormErrors`. */ verificationRejected: string; } /** The CTA and the two screens that replace or accompany the form. */ interface ActivateCardStatusLabels { submit: string; /** Accessible name announced while the activation is in flight. */ submittingAriaLabel: string; successTitle: string; successDescription: string; /** Label for the optional post-success control. */ done: string; /** Fallback failure copy when the owner supplies no `errorMessage`. */ errorDescription: string; } interface ActivateCardPanelLabels { header: ActivateCardHeaderLabels; cardFace: ActivateCardCardFaceLabels; fields: ActivateCardFieldLabels; validation: ActivateCardValidationLabels; status: ActivateCardStatusLabels; } /** * Deep partial for label overrides. * * Both label surfaces this widget exposes have nested groups, and a shallow * `Partial` would let a consumer who overrides one nested string drop the rest * of that group. The repo has no shared `PartialDeep` and does not depend on * `type-fest`, so each labels module defines its own — `card-details-widget/ui` * has an identical one for the panel's labels, and this declaration also types * that prop here so the widget carries one such helper rather than two. */ type PartialDeep$3 = { [K in keyof T]?: T[K] extends object ? PartialDeep$3 : T[K]; }; /** * Copy the **feature** layer owns, as opposed to the panel's own * `CardDetailsPanelLabels`. * * The split follows where the string is resolved, not where it is painted. A * status badge word is chosen by mapping a generated `cardStatus` enum member, * which is a data-access-typed decision the presentational layer is not allowed * to make — so it is overridable here even though the panel renders it. The * panel's own labels stay with the panel. */ interface CardDetailsFeatureLabels { /** Confirmation toast after a copy affordance writes to the clipboard. */ copySuccessToast: string; /** * Toast shown when activation is refused because the card is no longer * awaiting it. Resolved here rather than in the panel because it is an * API outcome, and because the panel closes as it fires. */ activationConflictToast: string; /** * Replaces the panel's generic reveal error when the credentials call was * refused (HTTP 403). * * It lives here rather than on the panel because deciding that a failure was a * refusal means reading the error, which is this layer's job — the panel is * handed one already-chosen sentence either way, and its own * `credentials.errorDescription` still covers every other failure. * * The wording deliberately does not tell the reader to ask for a permission. * Reading a debit card and reading its credentials take the same scope, so a * reader looking at this panel already holds it; a 403 here means the call was * refused for this session, which asking an admin for a role will not fix. */ revealNotPermittedDescription: string; /** Program wording on the card face, keyed on the widget's `cardProgram`. */ programLabels: Record; /** Status badge wording, keyed on the generated card status. */ statusLabels: Record; /** Form-factor subtitle wording, keyed on the generated card type. */ formFactorLabels: Record; /** * A velocity control's reset-period wording, keyed on the generated * `velocityWindow`. Used to build the spend limit row, e.g. "$500 / month". * `TRANSACTION` is unused there (it drives the separate per-transaction max * row instead) but is still a real enum member, so it stays in the map. */ velocityWindowLabels: Record; } interface CardDetailsWidgetProps extends WidgetProviderProps { /** Id of the card to show. */ cardId: string; /** * Which issuing product `cardId` belongs to. **Required, with no default** — * it selects the endpoint the card is fetched from, and a wrong guess would * 404 a card that exists. `CardsWidget` publishes the same required prop, so * one value configures a list and its detail panel together. * * A JavaScript consumer, or a host passing a route param straight through, can * still get a value past the type. **There is no catch-all and no default:** * anything that is not explicitly one of the two programs fetches nothing and * renders the panel's error state with no retry, and the reason is named on the * console. That covers an absent prop (`undefined` / `null`) as well as an * unrecognised one (`'prepaid'`, a case-wrong `'Credit'`), because the widget * cannot know which product to ask about in either case. * * A host that resolves the program asynchronously should gate its own render on * it rather than mounting this widget without it — an absent program is not * read as "still loading". */ cardProgram: CardProgram; /** * Called when the header's close control is pressed. Omitting it renders no * close control at all: a standalone card page has nothing to close back to, * while a host rendering the panel in a drawer supplies this and suppresses * its own chrome's close button so the two do not sit side by side. */ onClose?: () => void; /** Overrides for the panel's own copy. */ labels?: PartialDeep$3; /** Overrides for the Activate Card form's copy. */ activateLabels?: PartialDeep$3; /** Overrides for the copy this layer resolves — see `CardDetailsFeatureLabels`. */ featureLabels?: PartialDeep$3; /** * Bank logo for the card face. The widget ships no bank artwork of its own and * resolves none from the init response: an embeddable library must not depend * on a Tesouro-hosted asset a host page's content-security policy can block, * and a bundled bank-name→URL table would put internal CDN links in the * published package (PLAT-1179, embedded ADR 0005). Omit it and the card face * renders without a logo rather than with a placeholder. */ bankLogoSrc?: string; bankLogoAlt?: string; } /** * Reset-period tab labels and notice copy are deliberately absent: the owner * supplies those through `spendLimitPeriodOptions` / `spendLimitPeriodNotices`, * so declaring them here would give consumers override keys that no component * ever reads. */ interface CardControlsLabels { currencySymbol: string; spendLimit: { title: string; description: string; amountPlaceholder: string; amountAriaLabel: string; }; merchantCategories: { title: string; description: string; }; perTransactionMaximum: { title: string; description: string; amountPlaceholder: string; amountAriaLabel: string; }; } type PartialDeep$2 = { [K in keyof T]?: T[K] extends object ? PartialDeep$2 : T[K]; }; interface CreateCardWidgetLabels { header: { title: string; closeAriaLabel: string; }; cardArtAlt: string; setup: { sectionTitle: string; cardholderLabel: string; cardholderPlaceholder: string; cardFormatLabel: string; cardFormatPlaceholder: string; cardFormatVirtual: string; cardFormatPhysical: string; fundingAccountLabel: string; fundingAccountPlaceholder: string; }; mailing: { sectionTitle: string; sectionDescription: string; businessTitle: string; businessDescription: string; customTitle: string; customDescription: string; }; manualAddress: { streetLabel: string; cityLabel: string; stateLabel: string; statePlaceholder: string; zipLabel: string; }; cardControls: CardControlsLabels; preparing: { title: string; description: string; }; success: { title: string; description: string; createAnother: string; viewCard: string; }; footer: { cancel: string; createCard: string; }; cardholderError: { title: string; description: string; retry: string; }; fundingAccountError: { title: string; description: string; retry: string; }; } interface CreateCardFeatureLabels { toast: { creating: string; success: string; error: string; /** * Card issued but a follow-up velocity-control POST failed. The card * already exists; this must not read as a create failure. */ controlsWarning: string; /** * Card issued but granting company admins access to it failed. The card * already exists; this must not read as a create failure. */ companyAdminAccessWarning: string; /** * Card issued, but BOTH the velocity-control POST(s) and the company-admin * access grant failed. Distinct from the two single-failure warnings above * so an issuer who hits both is told about both, not just whichever this * code checks first. */ controlsAndAccessWarning: string; /** * The roster of users to grant access to a new card failed to load. * Shown with a retry action; Create stays disabled until it resolves so * a card is never issued without its required admin grants. */ debitCardIssuersError: string; retry: string; }; untitledFundingAccount: string; /** * API-required `name` on a velocity control. Not shown in this widget; * may surface on a later controls list. */ velocityControlNames: { spendLimit: string; perTransactionMaximum: string; }; /** * Spend-limit reset-period tab labels and notices. Owned here (not UI * labels) because the feature layer decides what each opaque period value * means. */ spendLimit: { periodOneTime: string; periodDaily: string; periodMonthly: string; oneTimeWarningTitle: string; oneTimeWarningDescription: string; dailyResetNotice: string; monthlyResetNotice: string; }; /** * Success-screen copy chosen from the create response's `cardStatus`. * Active/ready wording lives on the UI defaults; these cover statuses that * must not claim the card is already usable. */ success: { pendingActivationTitle: string; pendingActivationDescription: string; createdTitle: string; createdDescription: string; }; } type CreateCardWidgetProps = WidgetProviderProps & { /** Static card plastic image URL (no overlays). Host / white-label supplied. */ cardArtSrc: string; labels?: PartialDeep$2; featureLabels?: Partial<{ toast: Partial; untitledFundingAccount: string; velocityControlNames: Partial; spendLimit: Partial; success: Partial; }>; className?: string; onClose?: () => void; onCancel?: () => void; /** Fired with the new debit card id after a successful create. */ onViewCard?: (cardId: string) => void; }; /** * Every key of `T` optional, recursively, so a consumer can override one nested * label without restating its siblings. Defined locally because the repo has no * shared deep-partial type and does not depend on `type-fest` — same helper the * two card `ui` libs declare. */ type PartialDeep$1 = { [K in keyof T]?: T[K] extends object ? PartialDeep$1 : T[K]; }; /** * Strings this layer produces rather than passes through. * * They are separate from `CardsListLabels` (the `ui` lib's) because the cells * reaching that library are already formatted: the em-dash, the nickname * fallback and the "Viewing" prefix are all written *here*, so the UI lib has * no key to override them with. `bank-accounts-widget`'s `featureLabels` is the * precedent for the split. * * The status and form-factor maps live here for the same reason. EMBD-4503's * amendment put them in `shared/feature` keyed on the generated unions, and the * row arrives with `statusLabel` / `typeLabel` resolved — so this is the only * layer a consumer could reach them through. */ interface CardsFeatureLabels { /** * Stands in for a cell the card program carries no value for — credit has no * funding account, and a debit card's funding account is unresolvable without * the bank-account read scope. */ emptyValue: string; /** Name shown for a card with no nickname set. */ untitledCard: string; /** * Last-resort name for a funding account with neither a nickname nor an * account number to mask. An account with no nickname shows `••••1234` * instead, so that two unnamed accounts stay distinguishable — this is only * for the case where there is nothing at all to show. */ untitledFundingAccount: string; /** Display copy per card lifecycle status; keys are the generated code terms. */ cardStatus: Record; /** Display copy per card form factor; keys are the generated code terms. */ cardType: Record; } /** * What `onPaginationChange` reports. One shared type across both programs — the * shape does not vary by program, so per-program aliases would be published * surface for nothing. Named without the `Widget` segment because no widget * called `CardsWidget` is published any more; `CreditCardsWidget` and * `DebitCardsWidget` both hand back this. */ interface CardsPagination { /** Cursor for the page on screen. Omit for the first page. */ paginationToken?: string; /** Number of rows requested per page. */ pageSize: number; } /** * The wider shape `pagination` / `defaultPagination` accept. Internal: a * `CardsPagination` handed back by `onPaginationChange` satisfies it, which is * the round-trip the docs describe, so the package publishes only that one. * Keeps the `CardsWidget` segment because the internal component keeps the name. */ interface CardsWidgetPaginationInput { paginationToken?: string; pageSize?: number; } /** * The internal props. Not published: `CreditCardsWidgetProps` and * `DebitCardsWidgetProps` are derived from this by omission, which is what makes * `cardProgram` an implementation detail and the debit-only create props * unreachable on a credit list. */ interface CardsWidgetProps extends WidgetProviderProps { /** * Which issuing program the list shows — `'credit'` or `'debit'`. It selects * the endpoint that is read as well as which scopes gate the affordances. * * **Required, with no default**, and supplied by the wrapper rather than by a * consumer: `CreditCardsWidget` and `DebitCardsWidget` pin it. It was a public * prop until those two exports replaced it, and a default here would silently * pick a program, where an integrator on one route only ever wants one. */ cardProgram: CardProgram; /** * Controlled cursor/page-size metadata. Supply with `onPaginationChange` when * a host owns the list position, e.g. URL-backed pagination. */ pagination?: CardsWidgetPaginationInput; /** * Initial cursor/page-size metadata for uncontrolled usage. Omit for first * page with the default page size. Ignored when `pagination` is supplied. */ defaultPagination?: CardsWidgetPaginationInput; /** * Called whenever the widget changes its cursor/page-size pair through pagination * controls or a cursor-invalidating reset such as page-size changes. Hosts that * persist list position should store this whole object, not the token alone. */ onPaginationChange?: (pagination: CardsPagination) => void; /** * Overrides for the list's own copy, merged per nested group. Typed with the * `ui` lib's own deep-partial helper (each labels module declares one — the * repo has no shared version) so this prop follows that library's contract. */ labels?: PartialDeep$4; /** * Overrides for the copy this layer produces rather than passes through — the * status and form-factor vocabulary, the empty-cell placeholder, the nickname * fallbacks, and the pagination range prefix. Separate from `labels` because * the list library receives those values already formatted and so has no key * for them; `BankAccountsWidget.featureLabels` is the same split. */ featureLabels?: PartialDeep$1; /** * The card whose details panel is open. Supply with `onSelectedCardIdChange` * when the host owns that state — a deep-linked `/cards/[id]`, say. `null` * closes the panel; omit the prop for uncontrolled behaviour. * * Pinning it to `null` and navigating from `onSelectedCardIdChange` is how a * host suppresses the built-in panel entirely and owns navigation itself: the * click is still reported, and because the value never comes back non-null, * nothing ever opens. */ selectedCardId?: string | null; /** Initial selection when uncontrolled. Ignored when `selectedCardId` is supplied. */ defaultSelectedCardId?: string | null; /** * Called whenever the open card changes, including on close (`null`). * * Supplying it does **not** change what renders — a host can observe row clicks * for analytics, or mirror the selection into a query param, and still get the * built-in panel. (The removed `onCardSelect` could not: its presence * suppressed the panel, so those two intents were unexpressible together.) */ onSelectedCardIdChange?: (cardId: string | null) => void; /** * Label overrides forwarded into the nested card details panel, mirroring * `BankAccountsWidgetProps.accountDetailsLabels`. * * Derived from the receiving prop rather than restated, so the forwarded and * received types cannot drift. Naming the panel's labels type directly is not * an option anyway: a cross-widget `type:ui` import is banned by * `EMBEDDED_DEP_CONSTRAINTS`, and this indexed access reaches it through the * sanctioned feature → feature edge without re-exporting anything. It already * carries `card-details-widget`'s own deep partial, so no wrapper here. */ cardDetailsLabels?: CardDetailsWidgetProps['labels']; /** * Overrides for the copy the panel's *feature* layer resolves — its status * vocabulary, form-factor wording, program label and copy-success toast. * * Distinct from `featureLabels`, which covers the list's own vocabulary. * Without this prop the panel is unreachable for that copy, so a host that * renames a status in the list would still read the default inside the panel. */ cardDetailsFeatureLabels?: CardDetailsWidgetProps['featureLabels']; /** * Overrides for the Activate Card form the details panel opens. * * Its own prop for the same reason `cardDetailsLabels` is: without it that * screen's copy is unreachable from this composition, so a host that translated * everything else would still meet English on the one form that asks the reader * to type something. */ cardDetailsActivateLabels?: CardDetailsWidgetProps['activateLabels']; /** * Bank logo for the details panel's card face. Forwarded verbatim; the panel * renders no logo without it, so omitting these leaves the built-in sheet * showing a logo-less card rather than a placeholder. * * These stay props because the package ships no bank artwork and resolves none * from the init response (PLAT-1179, embedded ADR 0005). */ bankLogoSrc?: string; bankLogoAlt?: string; /** * Plastic art for the built-in debit create sheet. Forwarded verbatim into * nested `CreateCardWidget`. Required for that sheet: omitting it (with no * host `onCreateCard`) hides Create card rather than opening a panel that * cannot render. Same PLAT-1179 / ADR 0005 rule as `bankLogoSrc` — the package * ships no card artwork. */ cardArtSrc?: string; /** * Label overrides forwarded into the nested create-card panel. Indexed off * `CreateCardWidgetProps` so the forwarded and received types cannot drift, * the same feature → feature edge as `cardDetailsLabels`. */ createCardLabels?: CreateCardWidgetProps['labels']; /** * Overrides for the copy the create panel's *feature* layer resolves — mutation * toasts, the untitled funding-account fallback, and pending-activation success * copy. */ createCardFeatureLabels?: CreateCardWidgetProps['featureLabels']; /** * Called when Create card is clicked. When supplied, the host owns the * gesture: the built-in debit sheet does **not** open. Omit it on a debit list * with `cardArtSrc` to use the built-in `CreateCardWidget` sheet. Credit has * no in-package issuance widget, so credit Create card still requires this * callback — omitting it hides the button rather than offering a control that * does nothing. */ onCreateCard?: () => void; /** * Called with the card's id when a row's Activate button is clicked. The button * renders only on cards in `PENDING_ACTIVATION`, and only when the widget token * also holds the program's activate scope. * * Host-owned because EMBD-4398 designs no activation flow for the *list*. * * The original second reason — that no activate endpoint existed — no longer * holds: both programs have one, and the details panel this widget opens now * runs the whole flow itself (EMBD-5085). So a built-in row flow is now * buildable, and this callback stays host-owned by choice rather than by * necessity. Changing it would be a breaking change to a published prop and * belongs in its own ticket; until then a host that wants the built-in flow * can omit this and let the reader open the card. */ onActivateCard?: (cardId: string) => void; } /** * Derived from the internal props rather than restated, so the two cannot drift. * * Four omissions, not one. `cardProgram` is pinned below. The three create-sheet * props are debit-only: the built-in sheet issues against `POST /credit-cards`'s * sibling and `CreateCardWidget` covers debit alone, so credit's Create card * needs a host `onCreateCard` or the button hides. That was prose in the doc; the * type says it here. */ type CreditCardsWidgetProps = Omit; /** * Derived from the internal props rather than restated, so the two cannot drift. * Only `cardProgram` is omitted — the built-in create sheet is debit's, so this * export keeps `cardArtSrc` and the two create-panel label props. */ type DebitCardsWidgetProps = Omit; interface InsightsWidgetLabels { widgetTitle: string; activeTab: string; dismissedTab: string; noInsightsFound: string; loadErrorTitle: string; loadErrorDescription: string; loadErrorRetry: string; } /** * Presentational model for one insight row — screen-shaped only (no domain ids or types). */ interface InsightRowView { displayText: string; createdDate: string; badgeLabel: string; badgeIcon: ReactElement; showDismissButton: boolean; showToggle: boolean; toggleLabel?: string; /** Required when `onToggle` is set — owner-controlled switch state. */ toggleChecked?: boolean; onToggle?: (checked: boolean) => void; navigationLinkText?: string; onNavigationClick?: () => void; actionText?: string; onActionClick?: () => void; } interface SingleInsightLabels { dismissInsightAriaLabel: string; } interface InsightsFeatureLabels { onboardingBadgeLabel: string; routingOnboardingText: string; enableRoutingToggle: string; createAccountText: string; connectExternalText: string; linkAccountAction: string; verifyExternalAccountText: (nickname: string) => string; } /** Domain model for one generated insight — stays in the feature layer only. */ interface InsightItem { id: string; isDismissed: boolean; showToggle: boolean; canDismiss: boolean; displayText: string; toggleLabel?: string; toggleChecked?: boolean; onToggle?: (checked: boolean) => void; actionText?: string; onActionClick?: () => void; } declare function mapInsightToRowView(insight: InsightItem, labels: InsightsFeatureLabels, createdDate: string): InsightRowView; interface SingleInsightProps { insight: InsightItem; createdDate: string; featureLabels: Parameters[1]; onDismissClick: (id: string) => void; labels?: Partial; } declare const SingleInsight: ({ insight, createdDate, featureLabels, onDismissClick, labels, }: SingleInsightProps) => React.JSX.Element; interface InsightsWidgetInternalProps { labels?: Partial; singleInsightLabels?: Parameters[0]['labels']; } interface InsightsWidgetWithContextProps { labels?: Partial; singleInsightLabels?: InsightsWidgetInternalProps['singleInsightLabels']; featureLabels?: Partial; routingEnabled?: boolean; onRoutingToggleChange?: (enabled: boolean) => void; onLinkExternalAccountClick?: () => void; } type InsightsWidgetPassthroughProps = Pick; interface InsightsWidgetProps extends WidgetProviderProps, InsightsWidgetPassthroughProps { } interface Labels { description?: string; } interface SettingsWidgetSection { id: string; label: string; content: ReactNode; } interface FinopsThemeColors { primary?: string; primaryForeground?: string; } interface MoniteWrapperProps { finopsThemeColors?: FinopsThemeColors; } type MoniteRegionProviderProps = WidgetProviderProps & MoniteWrapperProps; interface FeatureLabels$1 { profileSection: string; teamSection: string; invoiceSection: string; billPaySection: string; accountingSection: string; tagsSection: string; expenseSection: string; helpSection: string; expenseApprovalsTab: string; expenseRequirementsTab: string; } /** * The sections this widget renders itself. These ids are the public vocabulary * a host uses to deep-link into a section, so they are part of the package's * API surface — renaming one is a breaking change. */ declare const SettingsSection: { readonly Profile: "profile"; readonly Team: "team"; readonly Invoice: "invoice"; readonly Expense: "expense"; readonly BillPay: "bill-pay"; readonly Accounting: "accounting"; readonly Tags: "tags"; readonly Help: "help"; }; type SettingsSection = (typeof SettingsSection)[keyof typeof SettingsSection]; /** * A selectable section id: one of the built-ins, or the id of a host-supplied * {@link SettingsWidgetAdditionalSection}. The `string & {}` arm keeps the * built-in literals visible to editor autocomplete while still accepting a * host's own id. */ type SettingsWidgetSectionId = SettingsSection | (string & {}); /** * A section contributed by the host, rendered alongside the built-in ones. * * This exists for surfaces the published package cannot own — the white-label * dashboard's password management, for instance, which is an app-auth concern * rather than an embedded one. Prefer the `*Content` props when replacing the * content of a section that already exists. */ interface SettingsWidgetAdditionalSection extends SettingsWidgetSection { /** * Place this section immediately after the named built-in one. Sections with * no `after` — and those whose anchor is hidden by scope gating — are * appended to the end of the list. */ after?: SettingsSection; } interface SettingsWidgetProps extends MoniteRegionProviderProps { labels?: Partial; featureLabels?: Partial; /** Forwarded to the embedded Team section's {@link TeamWidget} — the * allowlisted URL invite emails link to. Defaults to * `${window.location.origin}/accept-invite`; set it when the host app's * registered landing route differs from that default, or the origin is * not on the OIDC redirect-URI allowlist (preview/localhost). */ acceptInviteRedirectUri?: string; /** Forwarded to the embedded Help section's {@link HelpWidget} — where its * "Contact Us" line links (a support page URL or a `mailto:`). The line is * hidden entirely when unset, since the package has no tenant-agnostic * support address to fall back on. The bank name in that line comes from * widget init, so this is the only value a host needs to supply. */ helpContactUrl?: string; /** Forwarded to the embedded Profile section's {@link ProfileWidget} — where * its "to change this information, contact your bank" line links. The line * falls back to a plain, unlinked sentence when unset, for the same reason * {@link SettingsWidgetProps.helpContactUrl} does. */ profileContactUrl?: string; /** The section to show. Leave unset to let the widget own the selection; * supply it (with {@link SettingsWidgetProps.onSelectedSectionChange}) to * drive navigation from a route, a search param, or host state. A section * hidden by the user's scopes falls back to the first visible one. */ selectedSection?: SettingsWidgetSectionId; /** The section to open on, for a host that wants a deep link without taking * ownership of the selection. Ignored when * {@link SettingsWidgetProps.selectedSection} is supplied; defaults to the * first visible section. Scopes arrive with the init response, so a section * this names opens as soon as it becomes visible, and one the user's scopes * never grant leaves the first visible section on screen. */ defaultSelectedSection?: SettingsWidgetSectionId; /** Called with the id of the section the user selected. Fires whether or not * {@link SettingsWidgetProps.selectedSection} is supplied, so a host can mirror * the selection into its URL without taking ownership of it. */ onSelectedSectionChange?: (section: SettingsWidgetSectionId) => void; /** Host-owned sections rendered alongside the built-in ones. */ additionalSections?: SettingsWidgetAdditionalSection[]; profileContent?: ReactNode; teamContent?: ReactNode; billPayContent?: ReactNode; helpContent?: ReactNode; expenseContent?: ReactNode; accountingContent?: ReactNode; tagsContent?: ReactNode; invoiceContent?: ReactNode; } interface TransfersWidgetLabels { widgetTitle: string; transferMoneyCta: string; /** Tooltip when the transfer CTA is disabled for insufficient eligible accounts. */ transferMoneyCtaDisabledTooltip: string; /** Fallback when an account has no nickname in lists or options. */ accountFallbackName: string; /** Mask prefix before last four digits on account options. */ embeddedAccountMaskPrefix: string; /** Prefix before full account number on review cards (e.g. "Account #:"). */ modalReviewAccountNumberPrefix: string; /** Separator between from and to account names in the route line. */ listAccountsSeparator: string; loadErrorTitle: string; loadErrorDescription: string; loadErrorRetry: string; emptyTitle: string; emptyDescription: string; modalTitle: string; /** Shown under the title on the accounts step (e.g. ACH timing). */ modalAccountsSubtitle: string; /** Placeholder inside the From account select on the accounts step. */ modalFromAccountPlaceholder: string; /** Placeholder inside the To account select on the accounts step. */ modalToAccountPlaceholder: string; /** Primary action on the accounts step (legacy: full-width Confirm). */ modalAccountsConfirm: string; modalStepAccounts: string; modalStepAmount: string; modalStepReview: string; modalStepResult: string; modalFromLabel: string; modalToLabel: string; modalAmountLabel: string; /** Placeholder for the amount input (e.g. "0.00"). */ modalAmountPlaceholder: string; modalContinue: string; modalBack: string; modalSubmit: string; modalClose: string; modalReviewHeading: string; modalReviewFromAccountLabel: string; modalReviewToAccountLabel: string; modalAvailableBalanceCaption: string; modalConfirmTransfer: string; modalGoBack: string; modalSuccessTitle: string; modalSuccessDescription: string; modalResultSuccessTitle: string; modalResultErrorTitle: string; modalResultTransferMessagePrefix: string; modalResultSuccessMessageSuffix: string; modalResultErrorMessageSuffix: string; modalBackToTransfers: string; modalSelectPlaceholder: string; rowStatusCompleted: string; rowStatusPending: string; rowStatusFailed: string; rowTypeInternal: string; rowTypeToExternal: string; rowTypeFromExternal: string; modalAmountInvalidError: string; /** Shown under the amount field when the from (embedded) balance is too low. */ modalInsufficientFundsError: string; /** Resets from, to, and amount on the accounts step. */ modalClearSelections: string; modalUnsupportedPairError: string; modalSubmitFailedError: string; } interface FeatureLabels { unavailableLabel: string; routeSeparator: string; defaultCurrencyCode: string; defaultSecCode: string; } interface TransfersWidgetPassthroughProps { labels?: Partial; featureLabels?: Partial; /** * Whether the transfer money modal is showing. Supplying it makes the modal * controlled: the widget reports every open and close through * `onTransferMoneyOpenChange` and changes nothing on screen until the host * supplies a new value. Omit to let the widget own it. */ isTransferMoneyOpen?: boolean; /** The modal's state on first render, for a host that only wants to seed it. */ defaultIsTransferMoneyOpen?: boolean; /** * Called on every open and close — the CTA, the modal's own dismissals, and * the close that follows a completed transfer — whether or not the modal is * controlled. */ onTransferMoneyOpenChange?: (open: boolean) => void; } interface TransfersWidgetProps extends WidgetProviderProps, TransfersWidgetPassthroughProps { } /** * Copy the shell owns. The section names are deliberately absent: they arrive on * each `SuiteSection`, because whoever decides which sections exist is also the * only layer that can name them. */ interface WidgetSuiteShellLabels { /** Names the section menu for assistive technology. */ navAriaLabel: string; /** * Announces the placeholder shell while the caller resolves which sections * exist. Read out in place of the menu, which has no items yet to name. */ loadingAriaLabel: string; /** Shown in place of the menu when the caller supplies no sections. */ emptyState: { title: string; body: string; }; } /** * `Labels` has a nested group, so a shallow `{ ...defaults, ...overrides }` * would let a consumer overriding one `emptyState` string drop the other. The * repo has no shared deep-partial type, so each labels module declares its own. */ type PartialDeep = { [K in keyof T]?: T[K] extends object ? PartialDeep : T[K]; }; /** * The section id vocabulary, and the shape of a registry entry. * * A leaf module on purpose: it imports no widget feature library and never * reaches the provider, so `labels.ts` and `WidgetSuiteProvider.tsx` can take * the ids from here without sitting downstream of the eight widgets the registry * pulls in. */ /** * The sections the suite can offer, declared in the default menu order. Order * here is documentation only — the suite renders whatever order its `sections` * prop lists. */ declare const WidgetSuiteSectionId: { readonly Dashboard: "dashboard"; readonly Accounts: "accounts"; readonly Cards: "cards"; readonly Transfers: "transfers"; readonly BillPay: "bill-pay"; readonly Invoicing: "invoicing"; readonly Expenses: "expenses"; readonly Settings: "settings"; }; type WidgetSuiteSectionId = (typeof WidgetSuiteSectionId)[keyof typeof WidgetSuiteSectionId]; /** * The whole surface in the shipped white-label header order * (`useDashboardLayout.tsx`): its banking group, then its finops group, then * Settings, which white label keeps in the avatar menu rather than the header. * * This is what `sections` defaults to, and it is exported as well so a host can * spread it and edit the list — dropping a section, or reordering one — without * writing the whole thing out. */ declare const WIDGET_SUITE_DEFAULT_SECTIONS: readonly ["dashboard", "accounts", "cards", "transfers", "bill-pay", "invoicing", "expenses", "settings"]; /** * The section names in the suite menu. * * Keyed by section id rather than hand-listed, so adding a section to the * registry fails the build here until it is named — the menu can never render a * section with no label. The ids come from the leaf `sectionIds` module, so this * reaches neither the registry nor the widgets behind it. */ type WidgetSuiteMenuLabels = Record; /** Copy for the heading above a section, as opposed to its menu item. */ interface WidgetSuiteSectionTitleLabels { /** * The bank attribution beneath the heading. `{bankName}` is replaced with the * bank `/init` names, so a translation can put it anywhere in the sentence * rather than after a fixed lead-in. An override that drops the placeholder * shows no name. */ bankTagline: string; } /** * Copy for the states where the suite cannot prove it may show normal content. * * Both are places the gate cannot proceed, and neither is reachable by a correct * integration against a healthy API. */ interface WidgetSuiteDisclosureLabels { /** * The first init fetch failed before the disclosure flags were known. The suite * must fail closed here, because an unknown requirement is not permission to * show ungated sections. */ initFailedState: { title: string; body: string; retryButton: string; }; /** * The acceptance was recorded and the init refresh that clears the gate * failed, so only our view of it is stale. * * The failure is the title and the reassurance is the body, in that order on * purpose: `ErrorState` puts a destructive-toned glyph above the title, so * leading with "accepted" reads at a glance as the acceptance having failed — * the one thing this state must not imply, since a user who believes that will * try to accept again. */ refreshFailedState: { title: string; body: string; retryButton: string; }; } /** * A widget's whole copy surface: every label prop it accepts, spread onto it so * the props keep the names that widget's own doc gives them. * * Derived rather than hand-listed: the debit card list alone has six, so a * written-out list goes stale the next time one grows a panel. `providerLabels` * is excluded because the suite addresses the provider once, at the top level. */ type WidgetLabelProps = Omit>, 'providerLabels'>; type WidgetSuiteBalancesLabels = WidgetLabelProps; type WidgetSuiteInsightsLabels = WidgetLabelProps; type WidgetSuiteAccountsLabels = WidgetLabelProps; type WidgetSuiteCreditCardsLabels = WidgetLabelProps; type WidgetSuiteDebitCardsLabels = WidgetLabelProps; type WidgetSuiteTransfersLabels = WidgetLabelProps; type WidgetSuiteSettingsLabels = WidgetLabelProps; /** The two widgets the Dashboard section pairs, each taking its own copy. */ interface WidgetSuiteDashboardLabels { balances?: WidgetSuiteBalancesLabels; insights?: WidgetSuiteInsightsLabels; } /** * The two card lists. * * `labels.title` is the one leaf the suite defaults anywhere under `sections`: * `CardsList` titles itself "Cards", and two instances stack in this section, so * each is retitled by program. */ interface WidgetSuiteCardsLabels { creditCards: WidgetSuiteCreditCardsLabels; debitCards: WidgetSuiteDebitCardsLabels; } /** Expenses is only a placeholder until the suite wires a read/self surface. */ interface WidgetSuiteExpensesLabels { expensesUnavailable: string; } /** * Copy the suite puts *inside* a section's panel, one group per section. * * A group is shaped as the override its widget accepts, so it is handed down by * reference rather than remapped key by key. * * Two sections have no group: Bill Pay and Invoicing both mount a Monite * surface, which carries its own copy and takes no label props at all. They get * one when the native builds replace them, and adding a group to an * already-optional tree breaks nobody. */ interface WidgetSuiteSectionContentLabels { dashboard: WidgetSuiteDashboardLabels; accounts?: WidgetSuiteAccountsLabels; cards: WidgetSuiteCardsLabels; transfers?: WidgetSuiteTransfersLabels; expenses: WidgetSuiteExpensesLabels; settings?: WidgetSuiteSettingsLabels; } /** * Every string the suite puts on screen, including the copy inside the widgets * it mounts. Two kinds of group, differing in who holds the default: * * - **Suite-owned** — `shell`, `menu`, `sectionTitle`, `disclosure`, * `sections.expenses`, and the two card-list titles. Defaulted in * {@link WIDGET_SUITE_LABELS_EN}, with an override merged over it. * - **Forwarded** — every other key under `sections`. Handed down untouched for * the widget to merge over its own defaults, so the suite carries no copy of a * widget's strings and cannot go stale against one. * * `WidgetProvider`'s copy is deliberately absent: it is already reachable * through the inherited `providerLabels` prop, and two routes to one string * could disagree. */ interface WidgetSuiteLabels { /** The shell's own copy — the nav landmark name, the empty state. */ shell: WidgetSuiteShellLabels; menu: WidgetSuiteMenuLabels; sectionTitle: WidgetSuiteSectionTitleLabels; disclosure: WidgetSuiteDisclosureLabels; sections: WidgetSuiteSectionContentLabels; } /** * The `labels` prop: a deep partial of {@link WidgetSuiteLabels}. * * `sections` is exempted from `PartialDeep` on purpose — every group under it is * already the exact override its widget accepts, so deepening it again would * admit a shape the suite cannot forward and would flatten a non-plain member * (`PartialDeep` maps a function-valued label to `{}`). The two suite-owned * groups under it are made partial one level for the same reason. */ type WidgetSuiteLabelOverrides = PartialDeep> & { sections?: Partial> & { cards?: Partial; expenses?: Partial; }; }; declare const WIDGET_SUITE_LABELS_EN: WidgetSuiteLabels; interface WidgetSuiteProps extends WidgetProviderProps { /** * Which sections the suite offers, in menu order. * * Defaults to {@link WIDGET_SUITE_DEFAULT_SECTIONS}, the whole surface in the * shipped white-label header order, so mounting the suite with auth props * alone gives a complete banking page. Name the list to compose a narrower * page, or to order it differently. * * A section listed here is still hidden when the token does not earn it, so * this is the ceiling rather than a promise of what renders — which is also * why the default is safe: a section added to the registry reaches a host that * took the default, and a user who cannot see it still does not. */ sections?: readonly WidgetSuiteSectionId[]; /** * Which section is showing, for a host that owns navigation — a URL, a router, * its own state. * * Puts the menu in controlled mode for the lifetime of the mount, so supply it * with `onSectionChange` or the surface will not move when the user clicks. * * The suite is addressable at exactly this depth and no deeper: what a section * has open — an account, a card, a settings tab — belongs to the widget behind * it, so a host that needs to deep-link one of those mounts that widget itself. */ section?: WidgetSuiteSectionId; /** * Which section the suite opens on, for a host that wants a deep link but not * ownership. Read once per mount, and ignored when `section` is supplied. * * Defaults to the first entry in `sections`. */ defaultSection?: WidgetSuiteSectionId; /** * The section a person moved to, from a menu click or a dashboard affordance. * * Never fired for the entitlement fallback, and never fired on mount, so a * host mirroring this into its URL cannot overwrite the deep link it was just * handed. */ onSectionChange?: (section: WidgetSuiteSectionId) => void; /** * Every string the suite puts on screen, in one tree: the shell's own copy, * the menu names, the section heading, the disclosure states, and a group per * section carrying the copy of the widget that section mounts. * * Override any leaf and its siblings keep their defaults, at every depth. See * {@link WidgetSuiteLabels} for which groups the suite defaults and which it * forwards to a widget untouched. * * `WidgetProvider`'s own copy is not in here — it has its own `providerLabels` * prop, inherited from `WidgetProviderProps`. */ labels?: WidgetSuiteLabelOverrides; /** * Shows a heading above each section, named as its menu item is. * * Defaults to `true`: the suite owns its page, and a page with a menu but no * heading reads as a fragment of someone else's. Set `false` when the host * already renders a heading of its own above the suite. * * Every section gets one: no widget heads itself any more (embedded ADR * 0010), so the suite is the only thing that can name them. */ showSectionTitle?: boolean; } /** * A suite of widgets bound together by navigation. * * Unlike a widget — a content area an integrator drops onto a page they own — a * suite owns the page it is placed on, exists once per page, and decides which * widgets appear on it. * * One `/init` serves the whole surface: this provider owns the fetch, and every * composed widget is handed no auth props, so its own provider runs in * pass-through mode. See `sections/sectionIds.ts` for the two composition rules * that arrangement depends on. * * That one response also decides which state the suite is in before the shell * renders: placeholders while init has not settled, a closed retry state when * init fails before answering the disclosure flags, the acceptance surface * alone when the session owes the bank's disclosures, or the nav over its * sections. States rather than an overlay over a mounted shell — see * `WidgetSuiteSurface` for why each boundary sits where it does. * * Navigation is one value: which section is showing, published to the sections * through `WidgetSuiteProvider` and drivable by a host through `section` / * `defaultSection` / `onSectionChange`. Nothing deeper is addressable. Only the * open panel is mounted and every widget owns its own state, so leaving a * section and coming back shows it as it opens: the account list, the card * lists, the widget's own opening tab. A host that needs to deep-link into one * of those mounts that widget itself rather than reaching through the suite. */ declare function WidgetSuite({ baseUrl, widgetToken, organizationId, configClient, gatewayRouting, linkComponent, implementation, uiFramework, errorFallback, onError, analytics, disclosuresAcceptance, providerLabels, ...innerProps }: WidgetSuiteProps): React.JSX.Element; export { WIDGET_SUITE_DEFAULT_SECTIONS, WIDGET_SUITE_LABELS_EN, WidgetSuite, WidgetSuiteSectionId }; export type { WidgetSuiteCardsLabels, WidgetSuiteDashboardLabels, WidgetSuiteDisclosureLabels, WidgetSuiteExpensesLabels, WidgetSuiteLabelOverrides, WidgetSuiteLabels, WidgetSuiteMenuLabels, WidgetSuiteProps, WidgetSuiteSectionContentLabels, WidgetSuiteSectionTitleLabels };