tempest-react-sdk 0.79.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +35 -4
  2. package/bin/lib/component/generate.test.mjs +13 -1
  3. package/bin/lib/openapi/generate.hostile.test.mjs +58 -5
  4. package/bin/lib/openapi/generate.mjs +145 -34
  5. package/bin/lib/openapi/generate.test.mjs +216 -5
  6. package/bin/lib/openapi/schema-to-zod.mjs +4 -3
  7. package/dist/auth/create-tempest-auth.cjs +1 -1
  8. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  9. package/dist/auth/create-tempest-auth.js +172 -103
  10. package/dist/auth/create-tempest-auth.js.map +1 -1
  11. package/dist/br/boleto.cjs +1 -1
  12. package/dist/br/boleto.cjs.map +1 -1
  13. package/dist/br/boleto.js +2 -1
  14. package/dist/br/boleto.js.map +1 -1
  15. package/dist/br.d.ts +22 -3
  16. package/dist/components/AuthForm/AuthFieldControl.cjs +2 -0
  17. package/dist/components/AuthForm/AuthFieldControl.cjs.map +1 -0
  18. package/dist/components/AuthForm/AuthFieldControl.js +85 -0
  19. package/dist/components/AuthForm/AuthFieldControl.js.map +1 -0
  20. package/dist/components/AuthForm/AuthForm.cjs +2 -0
  21. package/dist/components/AuthForm/AuthForm.cjs.map +1 -0
  22. package/dist/components/AuthForm/AuthForm.js +83 -0
  23. package/dist/components/AuthForm/AuthForm.js.map +1 -0
  24. package/dist/components/AuthForm/AuthForm.module.cjs +3 -0
  25. package/dist/components/AuthForm/AuthForm.module.cjs.map +1 -0
  26. package/dist/components/AuthForm/AuthForm.module.js +14 -0
  27. package/dist/components/AuthForm/AuthForm.module.js.map +1 -0
  28. package/dist/components/AuthForm/AuthLinks.cjs +2 -0
  29. package/dist/components/AuthForm/AuthLinks.cjs.map +1 -0
  30. package/dist/components/AuthForm/AuthLinks.js +29 -0
  31. package/dist/components/AuthForm/AuthLinks.js.map +1 -0
  32. package/dist/components/AuthForm/ForgotPasswordForm.cjs +2 -0
  33. package/dist/components/AuthForm/ForgotPasswordForm.cjs.map +1 -0
  34. package/dist/components/AuthForm/ForgotPasswordForm.js +32 -0
  35. package/dist/components/AuthForm/ForgotPasswordForm.js.map +1 -0
  36. package/dist/components/AuthForm/LoginForm.cjs +2 -0
  37. package/dist/components/AuthForm/LoginForm.cjs.map +1 -0
  38. package/dist/components/AuthForm/LoginForm.js +39 -0
  39. package/dist/components/AuthForm/LoginForm.js.map +1 -0
  40. package/dist/components/AuthForm/ResetPasswordForm.cjs +2 -0
  41. package/dist/components/AuthForm/ResetPasswordForm.cjs.map +1 -0
  42. package/dist/components/AuthForm/ResetPasswordForm.js +39 -0
  43. package/dist/components/AuthForm/ResetPasswordForm.js.map +1 -0
  44. package/dist/components/AuthForm/SignUpForm.cjs +2 -0
  45. package/dist/components/AuthForm/SignUpForm.cjs.map +1 -0
  46. package/dist/components/AuthForm/SignUpForm.js +32 -0
  47. package/dist/components/AuthForm/SignUpForm.js.map +1 -0
  48. package/dist/components/AuthForm/auth-defaults.cjs +2 -0
  49. package/dist/components/AuthForm/auth-defaults.cjs.map +1 -0
  50. package/dist/components/AuthForm/auth-defaults.js +54 -0
  51. package/dist/components/AuthForm/auth-defaults.js.map +1 -0
  52. package/dist/components/AuthForm/auth-schemas.cjs +2 -0
  53. package/dist/components/AuthForm/auth-schemas.cjs.map +1 -0
  54. package/dist/components/AuthForm/auth-schemas.js +26 -0
  55. package/dist/components/AuthForm/auth-schemas.js.map +1 -0
  56. package/dist/components/AuthPage/AuthPage.cjs +2 -0
  57. package/dist/components/AuthPage/AuthPage.cjs.map +1 -0
  58. package/dist/components/AuthPage/AuthPage.js +91 -0
  59. package/dist/components/AuthPage/AuthPage.js.map +1 -0
  60. package/dist/components/AuthPage/AuthPage.module.cjs +3 -0
  61. package/dist/components/AuthPage/AuthPage.module.cjs.map +1 -0
  62. package/dist/components/AuthPage/AuthPage.module.js +24 -0
  63. package/dist/components/AuthPage/AuthPage.module.js.map +1 -0
  64. package/dist/components/FilterBar/filter-apply.cjs +1 -1
  65. package/dist/components/FilterBar/filter-apply.cjs.map +1 -1
  66. package/dist/components/FilterBar/filter-apply.js +7 -7
  67. package/dist/forms/index.js +7 -7
  68. package/dist/forms/masked-inputs.cjs +1 -1
  69. package/dist/forms/masked-inputs.cjs.map +1 -1
  70. package/dist/forms/masked-inputs.js +15 -15
  71. package/dist/http/api-client.cjs +1 -1
  72. package/dist/http/api-client.cjs.map +1 -1
  73. package/dist/http/api-client.js +104 -97
  74. package/dist/http/api-client.js.map +1 -1
  75. package/dist/http/build-url.cjs +1 -1
  76. package/dist/http/build-url.cjs.map +1 -1
  77. package/dist/http/build-url.js +3 -1
  78. package/dist/http/build-url.js.map +1 -1
  79. package/dist/http/presigned-post.cjs +2 -0
  80. package/dist/http/presigned-post.cjs.map +1 -0
  81. package/dist/http/presigned-post.js +73 -0
  82. package/dist/http/presigned-post.js.map +1 -0
  83. package/dist/http/resumable-upload.cjs +1 -1
  84. package/dist/http/resumable-upload.cjs.map +1 -1
  85. package/dist/http/resumable-upload.js +97 -97
  86. package/dist/http/resumable-upload.js.map +1 -1
  87. package/dist/http/upload-with-progress.cjs +1 -1
  88. package/dist/http/upload-with-progress.cjs.map +1 -1
  89. package/dist/http/upload-with-progress.js +12 -12
  90. package/dist/http/use-describe-api-error.cjs +1 -1
  91. package/dist/http/use-describe-api-error.cjs.map +1 -1
  92. package/dist/http/use-describe-api-error.js +9 -9
  93. package/dist/offline/create-offline-sync.cjs +1 -1
  94. package/dist/offline/create-offline-sync.cjs.map +1 -1
  95. package/dist/offline/create-offline-sync.js +78 -56
  96. package/dist/offline/create-offline-sync.js.map +1 -1
  97. package/dist/sse/create-event-stream.cjs +1 -1
  98. package/dist/sse/create-event-stream.cjs.map +1 -1
  99. package/dist/sse/create-event-stream.js +44 -30
  100. package/dist/sse/create-event-stream.js.map +1 -1
  101. package/dist/sse/ticket-event-stream.cjs +2 -0
  102. package/dist/sse/ticket-event-stream.cjs.map +1 -0
  103. package/dist/sse/ticket-event-stream.js +41 -0
  104. package/dist/sse/ticket-event-stream.js.map +1 -0
  105. package/dist/sse/use-event-stream.cjs +1 -1
  106. package/dist/sse/use-event-stream.cjs.map +1 -1
  107. package/dist/sse/use-event-stream.js +7 -4
  108. package/dist/sse/use-event-stream.js.map +1 -1
  109. package/dist/styles/AuthForm.css +2 -0
  110. package/dist/styles/AuthPage.css +2 -0
  111. package/dist/styles/component/AuthForm.css +9 -0
  112. package/dist/styles/component/AuthPage.css +29 -0
  113. package/dist/styles/forms.css +36 -0
  114. package/dist/styles/manifest.json +63 -0
  115. package/dist/styles.css +1 -1
  116. package/dist/sw/background-sync.cjs +1 -1
  117. package/dist/sw/background-sync.cjs.map +1 -1
  118. package/dist/sw/background-sync.js +124 -106
  119. package/dist/sw/background-sync.js.map +1 -1
  120. package/dist/sw.d.ts +49 -15
  121. package/dist/tempest-react-sdk.cjs +1 -1
  122. package/dist/tempest-react-sdk.d.ts +809 -22
  123. package/dist/tempest-react-sdk.js +373 -363
  124. package/dist/utils/format.cjs +1 -1
  125. package/dist/utils/format.cjs.map +1 -1
  126. package/dist/utils/format.js +25 -25
  127. package/dist/utils/numbers.cjs +1 -1
  128. package/dist/utils/numbers.cjs.map +1 -1
  129. package/dist/utils/numbers.js +7 -7
  130. package/package.json +1 -1
  131. package/template-pwa/src/sw.ts +10 -6
@@ -15,6 +15,7 @@ import { ComponentType } from 'react';
15
15
  import { CompositionEventHandler } from 'react';
16
16
  import { Control } from 'react-hook-form';
17
17
  import { Controller } from 'react-hook-form';
18
+ import { ControllerFieldState } from 'react-hook-form';
18
19
  import { ControllerRenderProps } from 'react-hook-form';
19
20
  import { createBrowserRouter } from 'react-router';
20
21
  import { createHashRouter } from 'react-router';
@@ -768,6 +769,20 @@ export declare interface ApiClient {
768
769
  */
769
770
  arrayBuffer(path: string, options?: RequestOptions): Promise<ArrayBuffer>;
770
771
  upload<T>(path: string, formData: FormData, method?: "POST" | "PUT" | "PATCH", options?: Omit<RequestOptions, "body" | "method">): Promise<T>;
772
+ /**
773
+ * The absolute URL a request to `path` would hit, without sending one.
774
+ *
775
+ * For the consumers that cannot go through `fetch` but must land on the
776
+ * same host: an `EventSource`, an `<img src>`, a download link. Rebuilding
777
+ * the URL by hand duplicates `baseURL` and `prefix`, and a forgotten prefix
778
+ * is a silent 404. No credential is attached: the URL is a string, and a
779
+ * token placed in it would end up in every proxy log.
780
+ *
781
+ * @param path - Path joined onto the client's base URL and prefix.
782
+ * @param params - Query params to append; `undefined` and `null` are skipped.
783
+ * @returns The absolute URL.
784
+ */
785
+ url(path: string, params?: RequestOptions["params"]): string;
771
786
  }
772
787
 
773
788
  export declare interface ApiClientConfig {
@@ -856,7 +871,10 @@ export declare interface ApiClientConfig {
856
871
  * Never logs a body, a header, or the query string — a bearer token in
857
872
  * `Authorization`, a password in a login payload and an `access_token` query
858
873
  * param would all end up in whatever the sink writes to. What is logged is
859
- * the method, the path as the call site wrote it, and the numbers.
874
+ * the method, the path and the numbers. The path is cut at the first `?` or
875
+ * `#`, and an absolute URL loses its `user:password@`, so a query written
876
+ * into the path (`"/files?access_token=…"`) or the signature of a presigned
877
+ * URL stays out just like `params` does.
860
878
  */
861
879
  logger?: ApiClientLogger;
862
880
  /**
@@ -1769,6 +1787,152 @@ export declare interface AudioRecording {
1769
1787
  durationMs: number;
1770
1788
  }
1771
1789
 
1790
+ /**
1791
+ * An e-mail field: trimmed and lower-cased *before* it is validated.
1792
+ *
1793
+ * The order is the point. Validating first rejects `" ana@x.com"` pasted with a
1794
+ * trailing space from a password manager, and a backend that compares e-mails
1795
+ * case-sensitively creates a second account for `Ana@x.com`. An empty value gets
1796
+ * its own sentence instead of "e-mail inválido", which reads like an accusation
1797
+ * on a field the user simply has not reached yet.
1798
+ *
1799
+ * @returns A schema producing the normalized address.
1800
+ */
1801
+ export declare function authEmail(): z.ZodPipe<z.ZodString, z.ZodEmail>;
1802
+
1803
+ /**
1804
+ * One entry of an auth form's `fields` list.
1805
+ *
1806
+ * The form is generated from this list, so the app declares *which* fields and
1807
+ * *how they read* — never the markup, the wiring to react-hook-form or the error
1808
+ * slot.
1809
+ *
1810
+ * @typeParam TName - The schema's keys, so a typo in `name` fails to compile.
1811
+ */
1812
+ export declare interface AuthField<TName extends string = string> {
1813
+ /** Key in the schema and in the submitted values. */
1814
+ name: TName;
1815
+ /** Visible label. Rich content works for `checkbox` (a terms link); keep it text for `pin`. */
1816
+ label: ReactNode;
1817
+ /** Control used to render it. Default `"text"`. */
1818
+ type?: AuthFieldType;
1819
+ /** Placeholder shown while the field is empty. */
1820
+ placeholder?: string;
1821
+ /**
1822
+ * `autocomplete` token. Defaults per type: `email` → `"email"`, `password` →
1823
+ * `"current-password"`, `phone` → `"tel"`. Sign-up and reset presets pass
1824
+ * `"new-password"` so password managers offer to generate one.
1825
+ */
1826
+ autoComplete?: string;
1827
+ /** Help text under the field, replaced by the error when there is one. */
1828
+ helperText?: ReactNode;
1829
+ /** Marks the control as required (visual hint + native attribute). */
1830
+ required?: boolean;
1831
+ /** `password` only: show the strength meter under the field. */
1832
+ showStrength?: boolean;
1833
+ /** `pin` only: number of cells. Default `6`. */
1834
+ length?: number;
1835
+ /** Render the control yourself — for a field none of the types covers. */
1836
+ render?: (props: AuthFieldRenderProps) => ReactNode;
1837
+ }
1838
+
1839
+ /** The field names a schema accepts. */
1840
+ export declare type AuthFieldName<TSchema extends z.ZodTypeAny> = Extract<keyof z.infer<TSchema>, string>;
1841
+
1842
+ /** What a field's `render` receives: the react-hook-form binding plus the field's own config. */
1843
+ export declare interface AuthFieldRenderProps {
1844
+ /** `name`, `value`, `onChange`, `onBlur` and `ref` — spread or wire them into the control. */
1845
+ field: ControllerRenderProps<FieldValues, string>;
1846
+ /** The field's validation state, including the server error mapped onto it. */
1847
+ fieldState: ControllerFieldState;
1848
+ /** The current error message, already resolved from `fieldState`. */
1849
+ error?: string;
1850
+ /** The config entry this field was declared with. */
1851
+ config: AuthField;
1852
+ }
1853
+
1854
+ /**
1855
+ * Which SDK control renders a field.
1856
+ *
1857
+ * `text` and `email` → `Input`, `password` → `PasswordInput`, `phone` / `cpf` /
1858
+ * `cnpj` → the masked inputs, `checkbox` → `Checkbox`, `pin` → `PinInput`. The
1859
+ * list is closed on purpose: these are the controls an auth screen uses, and
1860
+ * anything else goes through `render`.
1861
+ */
1862
+ export declare type AuthFieldType = "text" | "email" | "password" | "phone" | "cpf" | "cnpj" | "checkbox" | "pin";
1863
+
1864
+ /**
1865
+ * A complete auth form generated from a list of field configs and a zod schema.
1866
+ *
1867
+ * Every hand-built login and sign-up screen repeated the same wiring: a
1868
+ * `useZodForm`, one labelled input per field with its error slot, a password eye,
1869
+ * a submit button disabled while the request runs, and a `catch` that copied the
1870
+ * 422's field messages back onto the inputs. The app now declares *which* fields
1871
+ * and *what happens on submit*; the rest is here, built on the SDK's own controls
1872
+ * and its forms layer (`useZodForm` + `FormProvider` + `FormField`).
1873
+ *
1874
+ * Errors thrown by `onSubmit` are shown, never rethrown — a rethrow from a submit
1875
+ * handler is an unhandled rejection with nowhere to go. A `TempestApiError` whose
1876
+ * `fields` name fields of this form marks them; anything else, and any field
1877
+ * message that matched no declared field, goes to an `Alert` above the fields,
1878
+ * worded by `describeApiError`. Pass `onError` to log.
1879
+ *
1880
+ * @example
1881
+ * const schema = z.object({ email: z.email("E-mail inválido") });
1882
+ * <AuthForm
1883
+ * schema={schema}
1884
+ * fields={[{ name: "email", type: "email", label: "E-mail" }]}
1885
+ * submitLabel="Continuar"
1886
+ * onSubmit={async (values) => { await api.post("/auth/magic-link", { body: values }); }}
1887
+ * />
1888
+ */
1889
+ export declare function AuthForm<TSchema extends z.ZodTypeAny>({ schema, fields, onSubmit, submitLabel, defaultValues, beforeSubmit, afterSubmit, social, labels, errorOptions, onError, className, }: AuthFormProps<TSchema>): ReactNode;
1890
+
1891
+ /** Fixed sentences of {@link AuthFormProps}, PT-BR by default. */
1892
+ export declare interface AuthFormLabels {
1893
+ /** Text of the divider drawn above the `social` slot. Default `"ou"`. */
1894
+ divider: string;
1895
+ /**
1896
+ * Fallback sentence of the error alert, when the thrown error carries nothing
1897
+ * better. Default `"Não foi possível concluir agora. Tente de novo."`.
1898
+ */
1899
+ error: string;
1900
+ }
1901
+
1902
+ export declare interface AuthFormProps<TSchema extends z.ZodTypeAny> {
1903
+ /** Zod schema validating the values; its messages are what each field shows. */
1904
+ schema: TSchema;
1905
+ /** The fields, in display order. */
1906
+ fields: readonly AuthField<AuthFieldName<TSchema>>[];
1907
+ /**
1908
+ * Called with the validated values. Throw to fail: a `TempestApiError` whose
1909
+ * `fields` name fields of this form marks those fields; any other error shows
1910
+ * an alert above the form.
1911
+ */
1912
+ onSubmit: (values: z.infer<TSchema>) => Promise<void> | void;
1913
+ /** Submit button content. Default `"Enviar"`. */
1914
+ submitLabel?: ReactNode;
1915
+ /** Initial values. Fields left out start as `""` (`false` for checkboxes). */
1916
+ defaultValues?: Partial<z.input<TSchema>>;
1917
+ /** Content between the fields and the submit button — the "forgot password" link. */
1918
+ beforeSubmit?: ReactNode;
1919
+ /** Content after the submit button — the "no account? sign up" line. */
1920
+ afterSubmit?: ReactNode;
1921
+ /** Social sign-in buttons. Rendered under a divider reading `labels.divider`. */
1922
+ social?: ReactNode;
1923
+ /** Overrides for the fixed sentences. */
1924
+ labels?: Partial<AuthFormLabels>;
1925
+ /** Passed to `describeApiError` to word the alert — map error `codes`, `statuses`. */
1926
+ errorOptions?: DescribeApiErrorOptions;
1927
+ /** Called with every error `onSubmit` threw, after it is shown — for logging. */
1928
+ onError?: (error: unknown) => void;
1929
+ /** Class name for the `<form>`. */
1930
+ className?: string;
1931
+ }
1932
+
1933
+ /** The value object a schema produces, as react-hook-form sees it. */
1934
+ export declare type AuthFormValues<TSchema extends z.ZodTypeAny> = z.infer<TSchema> & FieldValues;
1935
+
1772
1936
  /**
1773
1937
  * Router-agnostic auth gate. The caller decides what to render in either
1774
1938
  * branch — typically `<Outlet />` for protected layouts and `<Navigate />` for
@@ -1793,6 +1957,122 @@ export declare interface AuthGuardProps {
1793
1957
  fallback: ReactNode;
1794
1958
  }
1795
1959
 
1960
+ /**
1961
+ * The layout shell of a sign-in, sign-up or password-recovery screen.
1962
+ *
1963
+ * Every app was hand-building the same page around its form: a colored top with
1964
+ * the logo and a back button over a white sheet with rounded top corners on
1965
+ * mobile, a 50/50 brand panel and form on desktop, a "Bem-vindo!" heading. This
1966
+ * component is that page, so the app passes only the brand and the form.
1967
+ *
1968
+ * The switch between the two layouts is a JavaScript media query, not a CSS
1969
+ * `@media` rule, because the breakpoint is a prop: a CSS media query cannot read a
1970
+ * custom property. `useMediaQuery` evaluates synchronously on the first render, so
1971
+ * a client-rendered page does not flash the wrong layout.
1972
+ *
1973
+ * @example
1974
+ * <AuthPage brand={<img src="/logo.svg" alt="Acme" />} back="/" title="Bem-vindo!">
1975
+ * <LoginForm onSubmit={signIn} links={{ forgotPassword: "/recuperar", signUp: "/cadastro" }} />
1976
+ * </AuthPage>
1977
+ */
1978
+ export declare function AuthPage({ brand, title, subtitle, aside, variant, breakpoint, back, footer, headingLevel, as, children, className, style, ...props }: AuthPageProps): ReactNode;
1979
+
1980
+ /** The brand panel of a `split` page — the colored half on desktop, the colored top on mobile. */
1981
+ export declare interface AuthPageAside {
1982
+ /**
1983
+ * Any CSS `background` value: a color, a token, a gradient, an image.
1984
+ * Defaults to `var(--tempest-primary)`, so a themed app gets its brand color
1985
+ * without passing anything.
1986
+ */
1987
+ background?: string;
1988
+ /**
1989
+ * Text color over that background. Defaults to `var(--tempest-text-on-primary)`,
1990
+ * which is only right while the background is the primary color — set it
1991
+ * together with a custom `background` so the back button and the app name
1992
+ * keep their contrast.
1993
+ */
1994
+ color?: string;
1995
+ /** Extra content under the brand on the panel: a tagline, an illustration. */
1996
+ content?: ReactNode;
1997
+ }
1998
+
1999
+ /** The back action, as an object when the label or the behavior is not the default. */
2000
+ export declare interface AuthPageBackAction {
2001
+ /** Route to navigate to, rendered as a router `Link`. */
2002
+ to?: string;
2003
+ /** Visible label (and accessible name). Default `"Voltar"`. */
2004
+ label?: string;
2005
+ /** Click handler — used alone (a `<button>`) or together with `to`. */
2006
+ onClick?: () => void;
2007
+ }
2008
+
2009
+ /** Heading level of the page title. */
2010
+ export declare type AuthPageHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
2011
+
2012
+ export declare interface AuthPageProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
2013
+ /** Logo and/or app name, shown on the brand panel (split) or at the top of the card. */
2014
+ brand?: ReactNode;
2015
+ /** Page title, rendered as a heading — e.g. `"Bem-vindo!"`. */
2016
+ title?: ReactNode;
2017
+ /** Line under the title — e.g. `"Entre com sua conta para continuar"`. */
2018
+ subtitle?: ReactNode;
2019
+ /** Brand panel colors and extra content. */
2020
+ aside?: AuthPageAside;
2021
+ /**
2022
+ * `split` (default): colored top + rounded white sheet below `breakpoint`,
2023
+ * 50/50 brand panel and form at or above it. `card`: one centered card at
2024
+ * every width — the internal-panel login.
2025
+ */
2026
+ variant?: AuthPageVariant;
2027
+ /**
2028
+ * Viewport width, in px, at which `split` turns into two columns. Default
2029
+ * `768`, the width every hand-built auth screen this replaces switched at —
2030
+ * deliberately not the `1024` of `useBreakpoint`, which is about navigation
2031
+ * chrome, not about whether a form fits beside a brand panel.
2032
+ */
2033
+ breakpoint?: number;
2034
+ /** Back action: a route string, or an object with `to`/`label`/`onClick`. Omit to hide it. */
2035
+ back?: string | AuthPageBackAction;
2036
+ /** Content under the form, inside the sheet or card — legal text, a secondary link. */
2037
+ footer?: ReactNode;
2038
+ /** Heading level of `title`. Default `1`, since the auth screen is usually the whole page. */
2039
+ headingLevel?: AuthPageHeadingLevel;
2040
+ /**
2041
+ * Root element. Default `"main"`, the page's main landmark. Use `"div"` when
2042
+ * the page is rendered inside a layout that already owns a `<main>` — two
2043
+ * nested main landmarks are an accessibility violation.
2044
+ */
2045
+ as?: "main" | "div";
2046
+ /** The form — usually an `AuthForm` or one of its presets. */
2047
+ children?: ReactNode;
2048
+ }
2049
+
2050
+ /** How the page is laid out: brand panel beside the form, or one centered card. */
2051
+ export declare type AuthPageVariant = "split" | "card";
2052
+
2053
+ /**
2054
+ * A password field with the minimum length every Tempest backend enforces.
2055
+ *
2056
+ * @param message - Sentence shown below 8 characters.
2057
+ * @returns The password schema.
2058
+ */
2059
+ export declare function authPassword(message?: string): z.ZodString;
2060
+
2061
+ /**
2062
+ * Props every preset (`LoginForm`, `SignUpForm`, …) shares.
2063
+ *
2064
+ * `schema` and `fields` become optional — the preset ships both — and the slots
2065
+ * around the submit button are the preset's to fill, from its `links`.
2066
+ *
2067
+ * @typeParam TSchema - The schema in use; the preset's default when none is passed.
2068
+ */
2069
+ export declare interface AuthPresetProps<TSchema extends z.ZodTypeAny> extends Omit<AuthFormProps<TSchema>, "schema" | "fields" | "labels" | "beforeSubmit" | "afterSubmit"> {
2070
+ /** Replaces the preset's schema. Pair it with `fields` when it adds or renames keys. */
2071
+ schema?: TSchema;
2072
+ /** Replaces the preset's field list. */
2073
+ fields?: readonly AuthField<AuthFieldName<TSchema>>[];
2074
+ }
2075
+
1796
2076
  export declare interface AuthState<TUser> {
1797
2077
  user: TUser | null;
1798
2078
  token: string | null;
@@ -2417,7 +2697,10 @@ export { BrowserRouter }
2417
2697
  *
2418
2698
  * A path that is itself an absolute URL wins over all of this, which is how a
2419
2699
  * client reaches a second host (a signed upload endpoint, a CDN) without a
2420
- * second client.
2700
+ * second client. Without `params` it is returned exactly as given: a signed
2701
+ * URL covers its query, and a round trip through `URL` re-encodes characters
2702
+ * outside the canonical form (`'` became `%27`, measured), which is a rewrite
2703
+ * of a string the server signed.
2421
2704
  *
2422
2705
  * @example
2423
2706
  * buildApiUrl("https://api.example.com", "/auth/login", { prefix: "/api" });
@@ -4220,11 +4503,15 @@ export declare function createDataProvider(client: ApiClient, options?: DataProv
4220
4503
  * `onMessage`. Pass `withCredentials: true` when the backend authenticates via
4221
4504
  * cookies. Call `close()` from the returned controller to tear down.
4222
4505
  *
4223
- * @param url - Full SSE endpoint URL.
4506
+ * When `url` is a function it is called before every connection, so a
4507
+ * reconnect presents a fresh credential instead of the one that expired. A
4508
+ * resolution still pending when `close()` or `reconnect()` runs is discarded.
4509
+ *
4510
+ * @param url - Full SSE endpoint URL, or a function returning it per connection.
4224
4511
  * @param options - Stream configuration and callbacks.
4225
4512
  * @returns A controller exposing `close`, `reconnect` and the current `status`.
4226
4513
  */
4227
- export declare function createEventStream<T = unknown>(url: string, options?: CreateEventStreamOptions<T>): EventStreamController;
4514
+ export declare function createEventStream<T = unknown>(url: EventStreamUrl, options?: CreateEventStreamOptions<T>): EventStreamController;
4228
4515
 
4229
4516
  export declare interface CreateEventStreamOptions<T> {
4230
4517
  /** Send cookies with the EventSource handshake. Default: false. */
@@ -4279,6 +4566,21 @@ export declare interface CreateEventStreamOptions<T> {
4279
4566
  * `isDevBuild()` to be able to answer, and this callback is the app's own.
4280
4567
  */
4281
4568
  onValidationError?: (issues: SchemaIssue[], raw: string) => void;
4569
+ /**
4570
+ * The URL function threw or rejected, so no connection was attempted.
4571
+ *
4572
+ * `onError` cannot carry this: it receives the `EventSource` `Event`, and a
4573
+ * URL that never resolved never had an `EventSource`.
4574
+ */
4575
+ onUrlError?: (error: unknown) => void;
4576
+ /**
4577
+ * Whether a URL-resolution failure is worth another attempt. Default: always.
4578
+ *
4579
+ * Return `false` for failures no backoff can fix — a `401` or a `403` when
4580
+ * fetching a ticket is a session decision, not a network blip — and the stream
4581
+ * stops at `status: "error"` instead of spending `maxRetries` on it.
4582
+ */
4583
+ shouldRetryUrl?: (error: unknown) => boolean;
4282
4584
  onOpen?: () => void;
4283
4585
  onMessage?: (message: EventStreamMessage<T>) => void;
4284
4586
  onError?: (error: Event) => void;
@@ -5216,6 +5518,17 @@ export declare interface CreateStorePersistOptions<T> {
5216
5518
  * without a working revocation, that boot `refresh()` restores the previous
5217
5519
  * account for whoever uses the browser next.
5218
5520
  *
5521
+ * With `storage: "local"` the session belongs to the **origin**, not the tab:
5522
+ * every tab reads and writes the same keys and follows what another tab wrote
5523
+ * — on the `storage` event and, before any refresh, `logout()` or ending of
5524
+ * the session, by comparing the stored session with the one it holds. A
5525
+ * rotation elsewhere is adopted; a logout or another account's login
5526
+ * elsewhere ends the session here too, with `onSessionEnd` told it happened
5527
+ * `elsewhere`. Before 0.81.0 a tab left open after logout kept account A and,
5528
+ * on its next `401`, redeemed account B's refresh token from the shared
5529
+ * storage, persisting `{ user: A, token: B }` (#524). `storage: "session"` is
5530
+ * per tab and behaves as before.
5531
+ *
5219
5532
  * Refreshes are collapsed per rotation, not just per overlap: a `401` whose
5220
5533
  * request left with a token that has since been replaced in the same session
5221
5534
  * replays with the new token and refreshes nothing. Three expired requests take
@@ -5478,6 +5791,35 @@ export declare interface CreateThemeOptions {
5478
5791
  inverseSelector?: string;
5479
5792
  }
5480
5793
 
5794
+ /**
5795
+ * Open the ticket-authenticated stream of the `tempest-fastapi-sdk` SSE router.
5796
+ *
5797
+ * `POST <prefix>/ticket` through the authenticated client, then
5798
+ * `GET <prefix>/stream?ticket=…` — and again for every reconnect, because the
5799
+ * ticket only opens streams for `expires_in` seconds and a reconnect after that
5800
+ * with the old one is a `401`. A ticket request refused with a status no
5801
+ * backoff fixes (`401`, `403 ACCOUNT_INACTIVE`) stops the stream at
5802
+ * `status: "error"` and reaches `onUrlError`; pass `shouldRetryUrl` to replace
5803
+ * {@link isRetriableTicketError}.
5804
+ *
5805
+ * The access token never enters the URL: it travels as the client's header (or
5806
+ * cookie) on the ticket request, and the ticket in the URL opens that stream
5807
+ * and nothing else.
5808
+ *
5809
+ * @example
5810
+ * const stream = createTicketEventStream<Notification>({
5811
+ * client: api,
5812
+ * onMessage: ({ data }) => addNotification(data),
5813
+ * onUrlError: (error) => toast.error(describeApiError(error)),
5814
+ * });
5815
+ *
5816
+ * @param options - Ticket source plus every {@link createEventStream} option.
5817
+ * @returns The stream controller.
5818
+ */
5819
+ export declare function createTicketEventStream<T = unknown>(options: CreateTicketEventStreamOptions<T>): EventStreamController;
5820
+
5821
+ export declare type CreateTicketEventStreamOptions<T> = TicketEventStreamSource & CreateEventStreamOptions<T>;
5822
+
5481
5823
  /**
5482
5824
  * Record a video stream — a camera, a screen share, or a `canvas.captureStream()`.
5483
5825
  *
@@ -6489,6 +6831,9 @@ export declare function deepMerge<T>(target: T, source: Partial<T>): T;
6489
6831
  */
6490
6832
  export declare const DEFAULT_API_ERROR_STRINGS: ApiErrorStrings;
6491
6833
 
6834
+ /** PT-BR defaults of {@link AuthFormLabels}. */
6835
+ export declare const DEFAULT_AUTH_FORM_LABELS: AuthFormLabels;
6836
+
6492
6837
  /**
6493
6838
  * What a scanner looks for when you do not say.
6494
6839
  *
@@ -7326,6 +7671,19 @@ export declare interface EventStreamMessage<T> {
7326
7671
 
7327
7672
  export declare type EventStreamStatus = "idle" | "connecting" | "open" | "closed" | "error";
7328
7673
 
7674
+ /**
7675
+ * Where the stream connects: a fixed URL, or a function resolved again before
7676
+ * every connection, the first one and each reconnect.
7677
+ *
7678
+ * The function form exists for short-lived credentials carried in the URL,
7679
+ * such as the `?ticket=` of the `tempest-fastapi-sdk` SSE router. `EventSource`
7680
+ * cannot send headers, so the credential has to ride in the query, and one that
7681
+ * expires makes a fixed URL a trap: the reconnect after the expiry presents the
7682
+ * same dead credential, gets a `401`, and burns every retry on it while the user
7683
+ * is still signed in.
7684
+ */
7685
+ export declare type EventStreamUrl = string | (() => string | Promise<string>);
7686
+
7329
7687
  /**
7330
7688
  * Finish a `tempest-fastapi-sdk` social login from the SPA's callback route.
7331
7689
  *
@@ -7812,6 +8170,51 @@ declare type FloatingActionButtonVariant = "primary" | "surface";
7812
8170
  */
7813
8171
  export declare function For<T>({ each, children, fallback }: ForProps<T>): ReactNode;
7814
8172
 
8173
+ /** Default fields of `ForgotPasswordForm`: the e-mail, with a line saying what happens next. */
8174
+ export declare const FORGOT_PASSWORD_FIELDS: readonly AuthField<"email">[];
8175
+
8176
+ /**
8177
+ * "Forgot password" form: the e-mail the reset link goes to.
8178
+ *
8179
+ * The confirmation screen after a successful submit is the app's call — most
8180
+ * backends answer the same whether or not the address exists, so the copy
8181
+ * belongs next to the backend's policy, not here.
8182
+ *
8183
+ * @example
8184
+ * const [sent, setSent] = useState(false);
8185
+ * <ForgotPasswordForm
8186
+ * onSubmit={async (values) => { await api.post("/auth/forgot-password", { body: values }); setSent(true); }}
8187
+ * links={{ signIn: "/login" }}
8188
+ * />
8189
+ */
8190
+ export declare function ForgotPasswordForm<TSchema extends z.ZodTypeAny = typeof forgotPasswordSchema>({ schema, fields, links, labels, submitLabel, ...props }: ForgotPasswordFormProps<TSchema>): ReactNode;
8191
+
8192
+ /** Sentences of {@link ForgotPasswordForm}, PT-BR by default. */
8193
+ export declare interface ForgotPasswordFormLabels extends AuthFormLabels {
8194
+ /** Default `"Lembrou a senha?"`. */
8195
+ signInPrompt: string;
8196
+ /** Default `"Entrar"`. */
8197
+ signIn: string;
8198
+ }
8199
+
8200
+ /** Routes the forgot-password form links to. */
8201
+ export declare interface ForgotPasswordFormLinks {
8202
+ /** "Entrar" link, under the form. */
8203
+ signIn?: string;
8204
+ }
8205
+
8206
+ export declare interface ForgotPasswordFormProps<TSchema extends z.ZodTypeAny = typeof forgotPasswordSchema> extends AuthPresetProps<TSchema> {
8207
+ /** Where the links go. */
8208
+ links?: ForgotPasswordFormLinks;
8209
+ /** Overrides for the fixed sentences. */
8210
+ labels?: Partial<ForgotPasswordFormLabels>;
8211
+ }
8212
+
8213
+ /** Default schema of `ForgotPasswordForm`: the e-mail the reset link goes to. */
8214
+ export declare const forgotPasswordSchema: z.ZodObject<{
8215
+ email: z.ZodPipe<z.ZodString, z.ZodEmail>;
8216
+ }, z.core.$strip>;
8217
+
7815
8218
  /**
7816
8219
  * Form wrapper with a built-in layout variant. Replaces ad-hoc `<form>` +
7817
8220
  * `<Stack>` / `<Grid>` boilerplate; pair with [[FormRow]], [[FormSection]],
@@ -8868,6 +9271,43 @@ export declare interface InstallBackgroundSyncOptions {
8868
9271
  * persisting and replaying every header, credentials included.
8869
9272
  */
8870
9273
  credentialHeaders?: readonly string[];
9274
+ /**
9275
+ * Origins besides the worker's own that a replayed request may carry a
9276
+ * credential header to — typically the API host when it is not the app's
9277
+ * origin (`["https://api.acme.com"]`). Default `[]`: only the worker's origin.
9278
+ *
9279
+ * A credential re-applied at replay comes from the page, so it is the app's
9280
+ * credential, whatever the request originally carried under the same header
9281
+ * name. Before 0.81.0 the worker checked only the name, and a mutation to a
9282
+ * third party that failed offline carrying that party's `Authorization` was
9283
+ * replayed to it with the app's bearer (#523). Now, with the rule
9284
+ * `isTrustedCredentialTarget` enforces on the live `ApiClient`:
9285
+ * - an entry that carried a credential header to an origin outside this
9286
+ * scope is dropped before `beforeReplay` runs — re-sending it without the
9287
+ * credential would be a request the app never made, and with the app's
9288
+ * credential, a leak;
9289
+ * - a request returned by `beforeReplay` (custom or default) that carries a
9290
+ * header from `credentialHeaders` to an origin outside this scope is
9291
+ * dropped instead of sent.
9292
+ *
9293
+ * Both drops warn once per origin per replay in development builds. Entries
9294
+ * with no credential header are not affected: the browser attaches that
9295
+ * origin's own cookies, as it did on the original attempt.
9296
+ */
9297
+ trustedOrigins?: readonly string[];
9298
+ /**
9299
+ * How an `"unknown"` owner is handled on an entry with no stripped
9300
+ * credentials (see {@link BackgroundSyncOwnerPolicy}). Default `"strict"`.
9301
+ *
9302
+ * `"lenient"` restores the replay-as-is of 0.77.0 and earlier for an app without sign-in,
9303
+ * where no page ever reports a principal. Do not use it with a cookie
9304
+ * session: the browser attaches on replay the cookie of whoever is signed in
9305
+ * at that moment, so a mutation queued by A whose owner was never stamped
9306
+ * (issuing tab closed, empty `clientId`, stamp lost, legacy entry) goes out
9307
+ * with B's session. Entries with stripped credentials ask the pages either
9308
+ * way. Ignored when a custom `beforeReplay` is given.
9309
+ */
9310
+ ownerPolicy?: BackgroundSyncOwnerPolicy;
8871
9311
  /**
8872
9312
  * Prepare each queued request right before it is replayed: return the
8873
9313
  * request to send (typically with the current credentials applied) or
@@ -8895,20 +9335,11 @@ export declare interface InstallBackgroundSyncOptions {
8895
9335
  * `maxRetentionMinutes` (and warns once per replay in development builds) —
8896
9336
  * so a page that never calls {@link provideBackgroundSyncCredentials} leaves
8897
9337
  * every entry it queued `"unknown"`, and none of them is replayed.
8898
- */
8899
- /**
8900
- * How an `"unknown"` owner is handled on an entry with no stripped
8901
- * credentials (see {@link BackgroundSyncOwnerPolicy}). Default `"strict"`.
8902
9338
  *
8903
- * `"lenient"` restores the replay-as-is of 0.77.0 and earlier for an app without sign-in,
8904
- * where no page ever reports a principal. Do not use it with a cookie
8905
- * session: the browser attaches on replay the cookie of whoever is signed in
8906
- * at that moment, so a mutation queued by A whose owner was never stamped
8907
- * (issuing tab closed, empty `clientId`, stamp lost, legacy entry) goes out
8908
- * with B's session. Entries with stripped credentials ask the pages either
8909
- * way. Ignored when a custom `beforeReplay` is given.
9339
+ * Whatever the hook returns is still held to `trustedOrigins`: a credential
9340
+ * header bound for an origin outside the worker's own and `trustedOrigins` is
9341
+ * never sent.
8910
9342
  */
8911
- ownerPolicy?: BackgroundSyncOwnerPolicy;
8912
9343
  beforeReplay?: (request: Request, context: BackgroundSyncReplayContext) => Request | null | Promise<Request | null>;
8913
9344
  }
8914
9345
 
@@ -9478,6 +9909,21 @@ export declare function isRejectionCloseCode(code: number): boolean;
9478
9909
  */
9479
9910
  export declare function isRetriableStatus(status: number): boolean;
9480
9911
 
9912
+ /**
9913
+ * Whether a failure to obtain a ticket is worth another attempt.
9914
+ *
9915
+ * Only a failure the backoff can outlast is: no response at all, a timeout, a
9916
+ * rate limit, a `5xx`. A `401` reached the caller after the client already
9917
+ * tried its refresh, and a `403` (`ACCOUNT_INACTIVE`) is the backend saying no;
9918
+ * both are session decisions, and retrying them only delays the error state the
9919
+ * app has to show. Anything that is not an API error is a contract mismatch,
9920
+ * which a retry cannot fix either.
9921
+ *
9922
+ * @param error - What the ticket request threw.
9923
+ * @returns Whether to schedule a reconnect.
9924
+ */
9925
+ export declare function isRetriableTicketError(error: unknown): boolean;
9926
+
9481
9927
  export { isRouteErrorResponse }
9482
9928
 
9483
9929
  /** Whether `getDisplayMedia` is reachable at all. */
@@ -10227,6 +10673,64 @@ export declare interface LoggerSink {
10227
10673
  (entry: LogEntry): void;
10228
10674
  }
10229
10675
 
10676
+ /**
10677
+ * Default fields of `LoginForm`: e-mail and password.
10678
+ *
10679
+ * Exported, like the other presets' lists, so an app extends the defaults
10680
+ * (`[...LOGIN_FIELDS, { name: "remember", type: "checkbox", … }]`) instead of
10681
+ * retyping them.
10682
+ */
10683
+ export declare const LOGIN_FIELDS: readonly AuthField<"email" | "password">[];
10684
+
10685
+ /**
10686
+ * Sign-in form: e-mail and password, "forgot password" link, sign-up line and a
10687
+ * slot for social buttons.
10688
+ *
10689
+ * `AuthForm` with the defaults every app wrote by hand: the e-mail trimmed and
10690
+ * lower-cased before validation, `current-password` autocomplete so the saved
10691
+ * password is offered, and the two links in the place users look for them. What
10692
+ * happens on success — storing the session, redirecting — stays in `onSubmit`.
10693
+ *
10694
+ * @example
10695
+ * <LoginForm
10696
+ * onSubmit={async (values) => { await auth.login(values); navigate("/"); }}
10697
+ * links={{ forgotPassword: "/recuperar-senha", signUp: "/cadastro" }}
10698
+ * social={<GoogleSignIn component={GoogleLogin} onSuccess={signInWithGoogle} />}
10699
+ * />
10700
+ */
10701
+ export declare function LoginForm<TSchema extends z.ZodTypeAny = typeof loginSchema>({ schema, fields, links, labels, submitLabel, ...props }: LoginFormProps<TSchema>): ReactNode;
10702
+
10703
+ /** Sentences of {@link LoginForm}, PT-BR by default. */
10704
+ export declare interface LoginFormLabels extends AuthFormLabels {
10705
+ /** Default `"Esqueceu a senha?"`. */
10706
+ forgotPassword: string;
10707
+ /** Default `"Não tem uma conta?"`. */
10708
+ signUpPrompt: string;
10709
+ /** Default `"Cadastre-se"`. */
10710
+ signUp: string;
10711
+ }
10712
+
10713
+ /** Routes the login form links to. Each link is rendered only when its route is set. */
10714
+ export declare interface LoginFormLinks {
10715
+ /** "Esqueceu a senha?" link, above the submit button. */
10716
+ forgotPassword?: string;
10717
+ /** "Cadastre-se" link, under the form. */
10718
+ signUp?: string;
10719
+ }
10720
+
10721
+ export declare interface LoginFormProps<TSchema extends z.ZodTypeAny = typeof loginSchema> extends AuthPresetProps<TSchema> {
10722
+ /** Where the links go. */
10723
+ links?: LoginFormLinks;
10724
+ /** Overrides for the fixed sentences. */
10725
+ labels?: Partial<LoginFormLabels>;
10726
+ }
10727
+
10728
+ /** Default schema of `LoginForm`: normalized e-mail + password of at least 8 characters. */
10729
+ export declare const loginSchema: z.ZodObject<{
10730
+ email: z.ZodPipe<z.ZodString, z.ZodEmail>;
10731
+ password: z.ZodString;
10732
+ }, z.core.$strip>;
10733
+
10230
10734
  export declare type LogLevel = "debug" | "info" | "warn" | "error";
10231
10735
 
10232
10736
  /**
@@ -11610,9 +12114,22 @@ export declare interface OfflineSync<TPayload> {
11610
12114
  pendingCount: () => Promise<number>;
11611
12115
  /** The queued mutations in FIFO order. */
11612
12116
  listPending: () => Promise<OutboxEntry<TPayload>[]>;
11613
- /** Drop every queued mutation (e.g. on logout). */
12117
+ /**
12118
+ * Drop every queued mutation (e.g. on logout).
12119
+ *
12120
+ * Also ends the current session for any `flush` already in flight: that run
12121
+ * delivers no further entry, pulls and applies nothing more and writes no
12122
+ * watermark, and resolves with `interrupted: true`. A `flush` called after this
12123
+ * starts a fresh run instead of joining the abandoned one.
12124
+ */
11614
12125
  clearOutbox: () => Promise<void>;
11615
- /** Reset the pull watermark (e.g. on logout / account switch). */
12126
+ /**
12127
+ * Reset the pull watermark (e.g. on logout / account switch).
12128
+ *
12129
+ * Ends the current session for an in-flight `flush` exactly like
12130
+ * {@link OfflineSync.clearOutbox}, so the run cannot write the ended
12131
+ * session's watermark back after the reset.
12132
+ */
11616
12133
  resetWatermark: () => void;
11617
12134
  /** Read the current reactive {@link SyncState} snapshot synchronously. */
11618
12135
  getState: () => SyncState;
@@ -12926,6 +13443,22 @@ export declare type PreloadableLazy<T extends AnyComponent> = ReturnType<typeof
12926
13443
  }>;
12927
13444
  };
12928
13445
 
13446
+ /**
13447
+ * A pre-signed `POST` form, as `tempest-fastapi-sdk` >= 0.331.0 hands it out
13448
+ * (`presigned_post_policy` / `SubjectObjectStorage.presign_post`).
13449
+ *
13450
+ * Snake case on purpose: this is the wire shape, so it parses straight from the
13451
+ * response of the app's own route.
13452
+ */
13453
+ export declare interface PresignedPost {
13454
+ /** Where the form is posted: the bucket on S3/MinIO, or the API itself on the local backend. */
13455
+ url: string;
13456
+ /** Form fields to send before the file, in this order — `key`, `policy`, signature and the rest. */
13457
+ fields: Record<string, string>;
13458
+ /** Largest file the policy accepts, in bytes. */
13459
+ max_bytes: number;
13460
+ }
13461
+
12929
13462
  /** A model whose cached size should appear in the report. */
12930
13463
  export declare interface ProfiledModel {
12931
13464
  /** Label for the report row, e.g. `"detector"`. */
@@ -12996,9 +13529,15 @@ export declare function projectMercator(coord: Coordinate): MercatorPoint;
12996
13529
  * keeps an entry pending when one it stripped is missing), or `null` when there
12997
13530
  * is no session — the worker then drops the entry instead of replaying it
12998
13531
  * anonymously. A throw from `getHeaders` or `getPrincipal` is answered as an
12999
- * explicit `{ error: true }`. The worker only applies headers whose names the
13000
- * original request carried, so returning `Authorization` for every URL never
13001
- * leaks it to a URL that did not receive it in the first place.
13532
+ * explicit `{ error: true }`.
13533
+ *
13534
+ * The worker applies only the header names the original request carried, and
13535
+ * only to the worker's own origin or the `trustedOrigins` given to
13536
+ * {@link installBackgroundSync}: an entry bound for any other origin is dropped
13537
+ * without asking this page. Matching the name is not enough on its own — a
13538
+ * request to a third party carries *that party's* `Authorization`, and until
13539
+ * 0.81.0 its replay received the app's bearer (#523). `getHeaders` still gets
13540
+ * the URL, for an app whose trusted origins need different credentials.
13002
13541
  *
13003
13542
  * Pass `getPrincipal` to bind each entry to whoever queued it: the worker asks
13004
13543
  * the issuing tab right after queueing, asks every tab again before replaying,
@@ -13848,6 +14387,68 @@ export declare interface RequestOptions extends Omit<RequestInit, "body"> {
13848
14387
  */
13849
14388
  export declare function requestPersistentStorage(): Promise<boolean>;
13850
14389
 
14390
+ /** Default fields of `ResetPasswordForm`: new password (with strength meter) and confirmation. */
14391
+ export declare const RESET_PASSWORD_FIELDS: readonly AuthField<"password" | "confirmPassword">[];
14392
+
14393
+ /**
14394
+ * "Choose a new password" form: password and confirmation, with the mismatch
14395
+ * reported under the confirmation field.
14396
+ *
14397
+ * The reset token stays out of the fields: it comes from the URL, and the app
14398
+ * adds it in `onSubmit` — `{ ...values, token }` — where it also decides what an
14399
+ * expired token looks like.
14400
+ *
14401
+ * @example
14402
+ * const [params] = useSearchParams();
14403
+ * <ResetPasswordForm
14404
+ * hints={["Pelo menos 8 caracteres"]}
14405
+ * onSubmit={async (values) => {
14406
+ * await api.post("/auth/reset-password", { body: { ...values, token: params.get("token") } });
14407
+ * }}
14408
+ * />
14409
+ */
14410
+ export declare function ResetPasswordForm<TSchema extends z.ZodTypeAny = typeof resetPasswordSchema>({ schema, fields, hints, links, labels, submitLabel, ...props }: ResetPasswordFormProps<TSchema>): ReactNode;
14411
+
14412
+ /** Sentences of {@link ResetPasswordForm}, PT-BR by default. */
14413
+ export declare interface ResetPasswordFormLabels extends AuthFormLabels {
14414
+ /** Default `"Lembrou a senha?"`. */
14415
+ signInPrompt: string;
14416
+ /** Default `"Entrar"`. */
14417
+ signIn: string;
14418
+ /** Accessible name of the hints list. Default `"Requisitos da senha"`. */
14419
+ hints: string;
14420
+ }
14421
+
14422
+ /** Routes the reset-password form links to. */
14423
+ export declare interface ResetPasswordFormLinks {
14424
+ /** "Entrar" link, under the form. */
14425
+ signIn?: string;
14426
+ }
14427
+
14428
+ export declare interface ResetPasswordFormProps<TSchema extends z.ZodTypeAny = typeof resetPasswordSchema> extends AuthPresetProps<TSchema> {
14429
+ /**
14430
+ * Password requirements listed above the submit button ("Pelo menos 8
14431
+ * caracteres", "Uma letra maiúscula"). Kept as text the app writes, because
14432
+ * only the backend knows its rules.
14433
+ */
14434
+ hints?: ReactNode[];
14435
+ /** Where the links go. */
14436
+ links?: ResetPasswordFormLinks;
14437
+ /** Overrides for the fixed sentences. */
14438
+ labels?: Partial<ResetPasswordFormLabels>;
14439
+ }
14440
+
14441
+ /**
14442
+ * Default schema of `ResetPasswordForm`: new password and its confirmation.
14443
+ *
14444
+ * The mismatch is reported on `confirmPassword` (`path`), so the message lands
14445
+ * under the field the user has to fix rather than in a form-level alert.
14446
+ */
14447
+ export declare const resetPasswordSchema: z.ZodObject<{
14448
+ password: z.ZodString;
14449
+ confirmPassword: z.ZodString;
14450
+ }, z.core.$strip>;
14451
+
13851
14452
  /**
13852
14453
  * A two-pane split layout with a draggable divider. The first pane is sized via
13853
14454
  * `flex-basis` as a percentage; the second pane fills the rest. Drag the divider
@@ -15484,6 +16085,9 @@ export declare interface SidebarSeparator {
15484
16085
  key: string;
15485
16086
  }
15486
16087
 
16088
+ /** Default fields of `SignUpForm`: name, e-mail and a password with strength meter. */
16089
+ export declare const SIGN_UP_FIELDS: readonly AuthField<"name" | "email" | "password">[];
16090
+
15487
16091
  /**
15488
16092
  * Signature capture on a canvas — the "sign here" field of a delivery receipt,
15489
16093
  * a service order, a term of acceptance.
@@ -15555,6 +16159,59 @@ export declare interface SignaturePadProps {
15555
16159
  className?: string;
15556
16160
  }
15557
16161
 
16162
+ /**
16163
+ * Sign-up form: name, e-mail and password by default, plus the "Já tem uma conta?"
16164
+ * line.
16165
+ *
16166
+ * Apps differ most here — a phone, a CPF, a terms checkbox — so the usual call
16167
+ * passes `fields` and a `schema` built with `signUpSchema.extend({...})`. The
16168
+ * password defaults to `new-password` autocomplete, which is what makes a
16169
+ * password manager offer to generate one.
16170
+ *
16171
+ * @example
16172
+ * <SignUpForm
16173
+ * schema={signUpSchema.extend({ terms: z.literal(true, "Aceite os termos") })}
16174
+ * fields={[...SIGN_UP_FIELDS, { name: "terms", type: "checkbox", label: "Aceito os termos" }]}
16175
+ * onSubmit={async (values) => { await api.post("/auth/register", { body: values }); }}
16176
+ * links={{ signIn: "/login" }}
16177
+ * />
16178
+ */
16179
+ export declare function SignUpForm<TSchema extends z.ZodTypeAny = typeof signUpSchema>({ schema, fields, links, labels, submitLabel, ...props }: SignUpFormProps<TSchema>): ReactNode;
16180
+
16181
+ /** Sentences of {@link SignUpForm}, PT-BR by default. */
16182
+ export declare interface SignUpFormLabels extends AuthFormLabels {
16183
+ /** Default `"Já tem uma conta?"`. */
16184
+ signInPrompt: string;
16185
+ /** Default `"Entrar"`. */
16186
+ signIn: string;
16187
+ }
16188
+
16189
+ /** Routes the sign-up form links to. */
16190
+ export declare interface SignUpFormLinks {
16191
+ /** "Entrar" link, under the form. */
16192
+ signIn?: string;
16193
+ }
16194
+
16195
+ export declare interface SignUpFormProps<TSchema extends z.ZodTypeAny = typeof signUpSchema> extends AuthPresetProps<TSchema> {
16196
+ /** Where the links go. */
16197
+ links?: SignUpFormLinks;
16198
+ /** Overrides for the fixed sentences. */
16199
+ labels?: Partial<SignUpFormLabels>;
16200
+ }
16201
+
16202
+ /**
16203
+ * Default schema of `SignUpForm`: name, e-mail and password.
16204
+ *
16205
+ * Exported so an app adds its own fields with `.extend()` instead of rewriting
16206
+ * the three it shares with every other app — see the phone and terms example in
16207
+ * the docs.
16208
+ */
16209
+ export declare const signUpSchema: z.ZodObject<{
16210
+ name: z.ZodString;
16211
+ email: z.ZodPipe<z.ZodString, z.ZodEmail>;
16212
+ password: z.ZodString;
16213
+ }, z.core.$strip>;
16214
+
15558
16215
  /** Loading placeholder block. Use `variant="text"` for inline lines, `circle` for avatars. */
15559
16216
  export declare function Skeleton({ variant, width, height, className, style }: SkeletonProps): JSX.Element;
15560
16217
 
@@ -15829,6 +16486,14 @@ export declare interface SpinnerProps {
15829
16486
 
15830
16487
  export declare type SpinnerSize = "xs" | "sm" | "md" | "lg" | "xl";
15831
16488
 
16489
+ /** Body of `POST <prefix>/ticket` on the `tempest-fastapi-sdk` SSE router. */
16490
+ export declare interface SseTicketResponse {
16491
+ /** Signed stream ticket, presented as a query param when opening the stream. */
16492
+ ticket: string;
16493
+ /** Seconds the ticket is accepted for opening a stream. */
16494
+ expires_in: number;
16495
+ }
16496
+
15832
16497
  /** Flex-based vertical or horizontal stack with a numeric `gap`. */
15833
16498
  export declare function Stack({ direction, gap, align, justify, wrap, className, style, children, ...props }: StackProps): JSX.Element;
15834
16499
 
@@ -16052,6 +16717,16 @@ export declare interface SyncRunSummary {
16052
16717
  skipped: boolean;
16053
16718
  /** Last delivery error message seen this run, or `null` when none failed. */
16054
16719
  lastError: string | null;
16720
+ /**
16721
+ * `true` when {@link OfflineSync.clearOutbox} or {@link OfflineSync.resetWatermark}
16722
+ * ended the session while this run was in flight, so it stopped early.
16723
+ *
16724
+ * The counts cover only what happened before the stop: a delivery already in
16725
+ * progress at logout is still counted, but no later entry was sent, no further
16726
+ * page was pulled or applied, and the watermark was not written. Absent on a
16727
+ * run that went to completion.
16728
+ */
16729
+ interrupted?: true;
16055
16730
  }
16056
16731
 
16057
16732
  /**
@@ -16345,6 +17020,17 @@ export declare interface TempestAuth<TUser, TCredentials> {
16345
17020
  refresh: () => Promise<void>;
16346
17021
  /** The current access token, or null. */
16347
17022
  getToken: () => string | null;
17023
+ /**
17024
+ * Stop following the other tabs: removes the `storage` listener this
17025
+ * preset adds to `window`. Idempotent.
17026
+ *
17027
+ * An app keeps one preset for the page's lifetime and never needs it; it
17028
+ * exists for tests and hot-module reload, which build several presets in
17029
+ * one window. A disposed preset still compares the shared storage with
17030
+ * its session before every refresh, so it never redeems another tab's
17031
+ * refresh token.
17032
+ */
17033
+ dispose: () => void;
16348
17034
  }
16349
17035
 
16350
17036
  /**
@@ -16441,6 +17127,22 @@ declare interface TempestSessionEnd<TUser> {
16441
17127
  * call to fail and keep the local half of the teardown independent of it.
16442
17128
  */
16443
17129
  api: ApiClient;
17130
+ /**
17131
+ * Whether another tab of the origin ended or replaced the session, and this
17132
+ * tab is only catching up (#524). With `storage: "local"` the session's
17133
+ * keys are shared by every tab, so a `logout()` or a `login()` in one tab
17134
+ * ends the session in all of them: `reason` is `"logout"` when the other
17135
+ * tab left no session behind and `"switch"` when it installed another
17136
+ * account.
17137
+ *
17138
+ * The other tab already ran this hook for the same session. Teardown that
17139
+ * lives in this tab's memory (an in-memory cache, an open socket) still has
17140
+ * to run here; teardown of state the origin shares (IndexedDB, Cache
17141
+ * Storage, a push subscription) was done there, and on `"switch"` redoing
17142
+ * it here would wipe what the new account already wrote — skip it when
17143
+ * this is `true`.
17144
+ */
17145
+ elsewhere: boolean;
16444
17146
  }
16445
17147
 
16446
17148
  /** The token envelope returned by a Tempest FastAPI SDK login/refresh route. */
@@ -16581,6 +17283,20 @@ export declare function throttle<A extends unknown[]>(fn: (...args: A) => void,
16581
17283
  cancel: () => void;
16582
17284
  };
16583
17285
 
17286
+ /** Where the ticket comes from and how the stream URL is built. */
17287
+ export declare interface TicketEventStreamSource {
17288
+ /**
17289
+ * Authenticated client the ticket is requested through, so it carries the
17290
+ * `Authorization` header or the session cookie, the CSRF header, and the
17291
+ * 401 refresh-and-replay that a hand-rolled `fetch` would lose.
17292
+ */
17293
+ client: Pick<ApiClient, "post" | "url">;
17294
+ /** Router prefix, relative to the client's base URL. Default: `"/sse"`. */
17295
+ prefix?: string;
17296
+ /** Query param the stream reads the ticket from. Default: `"ticket"`. */
17297
+ ticketParam?: string;
17298
+ }
17299
+
16584
17300
  /**
16585
17301
  * Vertical event timeline — activity feeds, order trackers, audit logs.
16586
17302
  * Each entry has a marker (color or custom icon), title, optional
@@ -17363,6 +18079,48 @@ export declare interface UpdateVariables {
17363
18079
  */
17364
18080
  export declare function uploadFingerprint(endpoint: string, file: Blob | File): string;
17365
18081
 
18082
+ /**
18083
+ * Upload a file straight to storage through a pre-signed `POST` form, with the
18084
+ * size ceiling the policy enforces.
18085
+ *
18086
+ * The form is built the way S3 requires: every `post.fields` entry in order,
18087
+ * the file last in the `file` part — S3 ignores whatever follows the file.
18088
+ *
18089
+ * The request carries **no** credential and no custom header: no
18090
+ * `Authorization`, no CSRF token, no `X-Request-ID`, no cookies. `post.url` is
18091
+ * usually the storage host, outside the API's origin, and the signature in
18092
+ * `fields` is the whole authorization. Without custom headers the request is a
18093
+ * CORS "simple request", so it does not depend on the bucket's CORS rules
18094
+ * allowing a preflight.
18095
+ *
18096
+ * A file larger than `post.max_bytes` is refused before anything is sent, with
18097
+ * the same `413 FILE_TOO_LARGE` the server would answer. Server errors are
18098
+ * normalized into one `TempestApiError` whichever backend answered: the S3 XML
18099
+ * (`EntityTooLarge` → `FILE_TOO_LARGE`, `EntityTooSmall` → `FILE_TOO_SMALL`,
18100
+ * `AccessDenied` → `SIGNED_URL_INVALID`, or `SIGNED_URL_EXPIRED` when the policy
18101
+ * expired) or the local backend's JSON envelope, which already uses those codes.
18102
+ *
18103
+ * @example
18104
+ * const post = await api.post<PresignedPost>("/avatars/upload-form");
18105
+ * await uploadPresignedPost(post, file, { onProgress: (p) => setProgress(p.fraction) });
18106
+ *
18107
+ * @param post - The form the API handed out.
18108
+ * @param file - The file or blob to upload.
18109
+ * @param options - Progress callback, abort signal and file name.
18110
+ * @returns Resolves when storage accepted the file (`204` on S3).
18111
+ * @throws TempestApiError On a refused upload, with the normalized `code`.
18112
+ */
18113
+ export declare function uploadPresignedPost(post: PresignedPost, file: Blob, options?: UploadPresignedPostOptions): Promise<void>;
18114
+
18115
+ export declare interface UploadPresignedPostOptions {
18116
+ /** Called on every `progress` event of the upload. */
18117
+ onProgress?: (event: UploadProgressEvent) => void;
18118
+ /** Abort the upload; rejects with an `AbortError` `DOMException`. */
18119
+ signal?: AbortSignal;
18120
+ /** File name sent with the `file` part. Defaults to `file.name` for a `File`. */
18121
+ fileName?: string;
18122
+ }
18123
+
17366
18124
  export declare interface UploadProgressEvent {
17367
18125
  /** Bytes already uploaded. */
17368
18126
  loaded: number;
@@ -18144,6 +18902,11 @@ export declare function useEventListener<K extends keyof HTMLElementEventMap>(ev
18144
18902
  * tied to the component (and the `url`/`enabled` dependencies); the stream
18145
18903
  * closes on unmount.
18146
18904
  *
18905
+ * A string `url` reopens the stream when it changes. A function `url` does not:
18906
+ * an inline arrow is a new function every render, and keying on its identity
18907
+ * would reconnect on every render. The latest function is the one called on the
18908
+ * next connection, so call `reconnect()` when what it resolves to must change now.
18909
+ *
18147
18910
  * Pass `schema` to have every frame validated before it reaches `onMessage`,
18148
18911
  * instead of trusting the type argument: without it `data` is announced as `T`
18149
18912
  * on the strength of the generic alone, which is a promise about the server
@@ -18167,7 +18930,7 @@ export declare function useEventListener<K extends keyof HTMLElementEventMap>(ev
18167
18930
  * onMessage: ({ data }) => addNotification(data),
18168
18931
  * });
18169
18932
  */
18170
- export declare function useEventStream<T = unknown>(url: string, options?: UseEventStreamOptions<T>): UseEventStreamResult<T>;
18933
+ export declare function useEventStream<T = unknown>(url: EventStreamUrl, options?: UseEventStreamOptions<T>): UseEventStreamResult<T>;
18171
18934
 
18172
18935
  export declare interface UseEventStreamOptions<T> extends Omit<CreateEventStreamOptions<T>, "onStatusChange"> {
18173
18936
  /** When false, the stream is not opened. Useful for "wait for auth". Default: true. */
@@ -20186,6 +20949,30 @@ export declare function useTheme(): ThemeContextValue;
20186
20949
  */
20187
20950
  export declare function useThrottle<T>(value: T, delay: number): T;
20188
20951
 
20952
+ /**
20953
+ * React form of {@link createTicketEventStream}: same ticket per connection,
20954
+ * lifecycle tied to the component like {@link useEventStream}.
20955
+ *
20956
+ * Changing `client`, `prefix` or `ticketParam` does not drop an open stream:
20957
+ * the next connection uses the new values, and `reconnect()` applies them now.
20958
+ * Reopening on identity would reconnect on every render for a client built
20959
+ * inline.
20960
+ *
20961
+ * @example
20962
+ * const { status } = useTicketEventStream<Notification>({
20963
+ * client: api,
20964
+ * enabled: isAuthenticated,
20965
+ * schema: notificationSchema,
20966
+ * onMessage: ({ data }) => addNotification(data),
20967
+ * });
20968
+ *
20969
+ * @param options - Ticket source plus every {@link useEventStream} option.
20970
+ * @returns Status, last message and `reconnect`.
20971
+ */
20972
+ export declare function useTicketEventStream<T = unknown>(options: UseTicketEventStreamOptions<T>): UseEventStreamResult<T>;
20973
+
20974
+ export declare type UseTicketEventStreamOptions<T> = TicketEventStreamSource & UseEventStreamOptions<T>;
20975
+
20189
20976
  /**
20190
20977
  * Run a callback after `delay` ms. Pass `null` to disable. Resets when
20191
20978
  * `delay` changes; the callback ref always points at the latest closure.