tempest-react-sdk 0.81.0 → 0.82.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 (179) hide show
  1. package/README.md +5 -6
  2. package/bin/create-tempest-app.e2e.test.mjs +31 -0
  3. package/bin/create-tempest-app.mjs +11 -1
  4. package/bin/lib/doctor/doctor.e2e.test.mjs +29 -0
  5. package/bin/lib/doctor/vitest.mjs +120 -0
  6. package/bin/lib/doctor/vitest.test.mjs +139 -0
  7. package/bin/lib/fs/contain.mjs +7 -0
  8. package/bin/lib/openapi/escape.mjs +68 -4
  9. package/bin/lib/openapi/generate.hostile.test.mjs +137 -0
  10. package/bin/lib/openapi/generate.mjs +78 -13
  11. package/bin/lib/openapi/pattern-risk.mjs +134 -0
  12. package/bin/lib/openapi/pattern-risk.test.mjs +45 -0
  13. package/bin/tempest-gen.test.mjs +100 -0
  14. package/bin/tempest.mjs +24 -8
  15. package/dist/access/use-can.cjs +1 -1
  16. package/dist/access/use-can.cjs.map +1 -1
  17. package/dist/access/use-can.js +25 -28
  18. package/dist/access/use-can.js.map +1 -1
  19. package/dist/auth/create-tempest-auth.cjs +1 -1
  20. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  21. package/dist/auth/create-tempest-auth.js +192 -149
  22. package/dist/auth/create-tempest-auth.js.map +1 -1
  23. package/dist/auth/session-identity.cjs +2 -0
  24. package/dist/auth/session-identity.cjs.map +1 -0
  25. package/dist/auth/session-identity.js +12 -0
  26. package/dist/auth/session-identity.js.map +1 -0
  27. package/dist/br/boleto.cjs.map +1 -1
  28. package/dist/br/boleto.js.map +1 -1
  29. package/dist/components/AuthForm/AuthLinks.cjs +1 -1
  30. package/dist/components/AuthForm/AuthLinks.cjs.map +1 -1
  31. package/dist/components/AuthForm/AuthLinks.js +18 -15
  32. package/dist/components/AuthForm/AuthLinks.js.map +1 -1
  33. package/dist/components/AuthPage/AuthPage.cjs +1 -1
  34. package/dist/components/AuthPage/AuthPage.cjs.map +1 -1
  35. package/dist/components/AuthPage/AuthPage.js +58 -52
  36. package/dist/components/AuthPage/AuthPage.js.map +1 -1
  37. package/dist/components/Breadcrumbs/Breadcrumbs.cjs +1 -1
  38. package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
  39. package/dist/components/Breadcrumbs/Breadcrumbs.js +22 -21
  40. package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  41. package/dist/components/Button/Button.cjs +1 -1
  42. package/dist/components/Button/Button.cjs.map +1 -1
  43. package/dist/components/Button/Button.js +41 -40
  44. package/dist/components/Button/Button.js.map +1 -1
  45. package/dist/components/Chat/ChatAttachments.cjs +1 -1
  46. package/dist/components/Chat/ChatAttachments.cjs.map +1 -1
  47. package/dist/components/Chat/ChatAttachments.js +38 -38
  48. package/dist/components/Form/Form.cjs +1 -1
  49. package/dist/components/Form/Form.cjs.map +1 -1
  50. package/dist/components/Form/Form.js +30 -32
  51. package/dist/components/Form/Form.js.map +1 -1
  52. package/dist/components/Layout/Layout.cjs +1 -1
  53. package/dist/components/Layout/Layout.cjs.map +1 -1
  54. package/dist/components/Layout/Layout.js +25 -24
  55. package/dist/components/Layout/Layout.js.map +1 -1
  56. package/dist/components/Layout/gap.cjs +2 -0
  57. package/dist/components/Layout/gap.cjs.map +1 -0
  58. package/dist/components/Layout/gap.js +15 -0
  59. package/dist/components/Layout/gap.js.map +1 -0
  60. package/dist/components/NavigationMenu/NavigationMenu.cjs +1 -1
  61. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  62. package/dist/components/NavigationMenu/NavigationMenu.js +71 -70
  63. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  64. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  65. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  66. package/dist/components/Sidebar/SidebarEntry.cjs +1 -1
  67. package/dist/components/Sidebar/SidebarEntry.cjs.map +1 -1
  68. package/dist/components/Sidebar/SidebarEntry.js +30 -29
  69. package/dist/components/Sidebar/SidebarEntry.js.map +1 -1
  70. package/dist/data/create-data-provider.cjs +1 -1
  71. package/dist/data/create-data-provider.cjs.map +1 -1
  72. package/dist/data/create-data-provider.js +1 -2
  73. package/dist/data/create-data-provider.js.map +1 -1
  74. package/dist/forms/validate-form.cjs +1 -1
  75. package/dist/forms/validate-form.cjs.map +1 -1
  76. package/dist/forms/validate-form.js +6 -1
  77. package/dist/forms/validate-form.js.map +1 -1
  78. package/dist/forms/zod-resolver.cjs +1 -1
  79. package/dist/forms/zod-resolver.cjs.map +1 -1
  80. package/dist/forms/zod-resolver.js +8 -3
  81. package/dist/forms/zod-resolver.js.map +1 -1
  82. package/dist/http/api-client.cjs +1 -1
  83. package/dist/http/api-client.cjs.map +1 -1
  84. package/dist/http/api-client.js +90 -72
  85. package/dist/http/api-client.js.map +1 -1
  86. package/dist/http/credential-scope.cjs +1 -1
  87. package/dist/http/credential-scope.cjs.map +1 -1
  88. package/dist/http/credential-scope.js +33 -9
  89. package/dist/http/credential-scope.js.map +1 -1
  90. package/dist/http/presigned-post.cjs.map +1 -1
  91. package/dist/http/presigned-post.js.map +1 -1
  92. package/dist/http/resumable-upload.cjs +1 -1
  93. package/dist/http/resumable-upload.cjs.map +1 -1
  94. package/dist/http/resumable-upload.js +90 -84
  95. package/dist/http/resumable-upload.js.map +1 -1
  96. package/dist/http/upload-with-progress.cjs +1 -1
  97. package/dist/http/upload-with-progress.cjs.map +1 -1
  98. package/dist/http/upload-with-progress.js +8 -8
  99. package/dist/http/upload-with-progress.js.map +1 -1
  100. package/dist/oauth/GoogleSignIn.cjs +1 -1
  101. package/dist/oauth/GoogleSignIn.cjs.map +1 -1
  102. package/dist/oauth/GoogleSignIn.js +5 -4
  103. package/dist/oauth/GoogleSignIn.js.map +1 -1
  104. package/dist/offline/create-offline-sync.cjs +1 -1
  105. package/dist/offline/create-offline-sync.cjs.map +1 -1
  106. package/dist/offline/create-offline-sync.js +87 -69
  107. package/dist/offline/create-offline-sync.js.map +1 -1
  108. package/dist/query/offline-persistence.cjs +1 -1
  109. package/dist/query/offline-persistence.cjs.map +1 -1
  110. package/dist/query/offline-persistence.js +54 -44
  111. package/dist/query/offline-persistence.js.map +1 -1
  112. package/dist/sse/create-event-stream.cjs +1 -1
  113. package/dist/sse/create-event-stream.cjs.map +1 -1
  114. package/dist/sse/create-event-stream.js +48 -41
  115. package/dist/sse/create-event-stream.js.map +1 -1
  116. package/dist/sse/ticket-event-stream.cjs +1 -1
  117. package/dist/sse/ticket-event-stream.cjs.map +1 -1
  118. package/dist/sse/ticket-event-stream.js +48 -19
  119. package/dist/sse/ticket-event-stream.js.map +1 -1
  120. package/dist/sse/use-event-stream.cjs +1 -1
  121. package/dist/sse/use-event-stream.cjs.map +1 -1
  122. package/dist/sse/use-event-stream.js +19 -14
  123. package/dist/sse/use-event-stream.js.map +1 -1
  124. package/dist/sw/background-sync.cjs +1 -1
  125. package/dist/sw/background-sync.cjs.map +1 -1
  126. package/dist/sw/background-sync.js +10 -8
  127. package/dist/sw/background-sync.js.map +1 -1
  128. package/dist/sw/cache.cjs +1 -1
  129. package/dist/sw/cache.cjs.map +1 -1
  130. package/dist/sw/cache.js +36 -30
  131. package/dist/sw/cache.js.map +1 -1
  132. package/dist/sw/create-push-handler.cjs +1 -1
  133. package/dist/sw/create-push-handler.cjs.map +1 -1
  134. package/dist/sw/create-push-handler.js +20 -10
  135. package/dist/sw/create-push-handler.js.map +1 -1
  136. package/dist/sw.d.ts +44 -13
  137. package/dist/tabular/compact.cjs +1 -1
  138. package/dist/tabular/compact.cjs.map +1 -1
  139. package/dist/tabular/compact.js +35 -16
  140. package/dist/tabular/compact.js.map +1 -1
  141. package/dist/tabular/manifest.cjs +1 -1
  142. package/dist/tabular/manifest.cjs.map +1 -1
  143. package/dist/tabular/manifest.js +33 -15
  144. package/dist/tabular/manifest.js.map +1 -1
  145. package/dist/tabular.d.ts +11 -1
  146. package/dist/tempest-react-sdk.cjs +1 -1
  147. package/dist/tempest-react-sdk.d.ts +457 -68
  148. package/dist/tempest-react-sdk.js +224 -224
  149. package/dist/theme/initial-theme.cjs +4 -4
  150. package/dist/theme/initial-theme.cjs.map +1 -1
  151. package/dist/theme/initial-theme.js +8 -5
  152. package/dist/theme/initial-theme.js.map +1 -1
  153. package/dist/utils/compressed-storage.cjs +1 -1
  154. package/dist/utils/compressed-storage.cjs.map +1 -1
  155. package/dist/utils/compressed-storage.js +13 -7
  156. package/dist/utils/compressed-storage.js.map +1 -1
  157. package/dist/utils/objects.cjs +1 -1
  158. package/dist/utils/objects.cjs.map +1 -1
  159. package/dist/utils/objects.js +13 -7
  160. package/dist/utils/objects.js.map +1 -1
  161. package/dist/utils/safe-url.cjs +1 -1
  162. package/dist/utils/safe-url.cjs.map +1 -1
  163. package/dist/utils/safe-url.js +4 -1
  164. package/dist/utils/safe-url.js.map +1 -1
  165. package/dist/webrtc/peer-mesh.cjs +1 -1
  166. package/dist/webrtc/peer-mesh.cjs.map +1 -1
  167. package/dist/webrtc/peer-mesh.js +7 -1
  168. package/dist/webrtc/peer-mesh.js.map +1 -1
  169. package/dist/ws/create-web-socket.cjs +1 -1
  170. package/dist/ws/create-web-socket.cjs.map +1 -1
  171. package/dist/ws/create-web-socket.js +77 -61
  172. package/dist/ws/create-web-socket.js.map +1 -1
  173. package/dist/ws/use-web-socket.cjs +1 -1
  174. package/dist/ws/use-web-socket.cjs.map +1 -1
  175. package/dist/ws/use-web-socket.js +4 -3
  176. package/dist/ws/use-web-socket.js.map +1 -1
  177. package/package.json +1 -1
  178. package/template-pwa/src/pwa.tsx +42 -0
  179. package/template-pwa/src/sw.ts +5 -4
@@ -966,7 +966,20 @@ export declare interface ApiClientConfig {
966
966
  * without it, and is exposed the same way.
967
967
  */
968
968
  csrf?: boolean | CsrfOptions;
969
- /** Default headers merged into every request. */
969
+ /**
970
+ * Default headers merged into every request inside the credential scope.
971
+ *
972
+ * Treated as a credential since 0.82.0 — an `X-Api-Key` set once here is as
973
+ * much a secret as the bearer — so they go only to `baseURL`'s origin and
974
+ * {@link trustedOrigins}. A redirect would move them past that check, and
975
+ * unlike `Authorization` no browser strips them on a cross-origin hop
976
+ * (measured in Chrome 150, #554). So while this is non-empty, an in-scope
977
+ * request goes out with `redirect: "manual"`, and a redirect answering it
978
+ * is thrown as a `TempestApiError` with `code: "REDIRECT_REFUSED"` — the
979
+ * browser hides the `Location`, so the same-origin case cannot be told
980
+ * apart and followed by hand. Call the final URL (FastAPI's trailing-slash
981
+ * `307` is the usual source), or pass `redirect: "follow"` on the call.
982
+ */
970
983
  headers?: Record<string, string>;
971
984
  /** Optional fetch implementation (defaults to globalThis.fetch). */
972
985
  fetcher?: typeof fetch;
@@ -1998,7 +2011,11 @@ export declare interface AuthPageAside {
1998
2011
 
1999
2012
  /** The back action, as an object when the label or the behavior is not the default. */
2000
2013
  export declare interface AuthPageBackAction {
2001
- /** Route to navigate to, rendered as a router `Link`. */
2014
+ /**
2015
+ * Route to navigate to, rendered as a router `Link`. Passes through
2016
+ * `safeLinkUrl`: a destination it rejects is dropped, leaving the button with
2017
+ * `onClick` or, without one, no back action at all.
2018
+ */
2002
2019
  to?: string;
2003
2020
  /** Visible label (and accessible name). Default `"Voltar"`. */
2004
2021
  label?: string;
@@ -2642,6 +2659,12 @@ export declare interface BreadcrumbItem {
2642
2659
  * Hierarchical navigation breadcrumbs. The last item is rendered as plain
2643
2660
  * text with `aria-current="page"`. Items with neither `href` nor `onClick`
2644
2661
  * also render as text (intermediate but non-clickable).
2662
+ *
2663
+ * Each `href` is vetted with `safeLinkUrl`'s allowlist: an item whose URL uses a scheme
2664
+ * outside `http:`/`https:`/`mailto:`/`tel:`/`sms:` loses the `href`, and renders
2665
+ * as text unless it also has an `onClick`. A trail built from API data — the
2666
+ * folder names of a document tree — would otherwise put a clickable
2667
+ * `javascript:` link on the page under React 18, which writes it verbatim.
2645
2668
  */
2646
2669
  export declare function Breadcrumbs({ items, separator, className }: BreadcrumbsProps): JSX.Element;
2647
2670
 
@@ -2789,6 +2812,13 @@ export declare function buildOpenInChromeIntent(): string | null;
2789
2812
  * link gets `aria-disabled="true"`, `role="link"`, `tabIndex={-1}`, no `href`, and
2790
2813
  * its click and middle-click cancelled.
2791
2814
  *
2815
+ * The `href` is vetted first, with `safeLinkUrl`'s allowlist: `http:`, `https:`, `mailto:`,
2816
+ * `tel:`, `sms:` and relative URLs pass, anything else renders as the same inert,
2817
+ * `aria-disabled` link. React 19 blocks a `javascript:` href but the peer range
2818
+ * admits React 18, which writes it verbatim (#447), and a CTA built from API data
2819
+ * should not need the app to remember that. A link to a custom scheme (`whatsapp:`,
2820
+ * an app deep link) goes through {@link ButtonSlot} with the app's own `<a>`.
2821
+ *
2792
2822
  * To style another element — a react-router `Link` — use {@link ButtonSlot}. It is
2793
2823
  * a separate component, not an `asChild` prop, so an app that never uses it does
2794
2824
  * not ship the prop merge and ref composition it needs.
@@ -4177,7 +4207,21 @@ export declare const CPFInput: ForwardRefExoticComponent<Omit<InputProps, "value
4177
4207
  * destination of a credentialed request could come from a value read off the
4178
4208
  * network. A request to another origin still goes out; it goes without the
4179
4209
  * header, and a development build says so once. Declare the exceptions in
4180
- * {@link ApiClientConfig.trustedOrigins}.
4210
+ * {@link ApiClientConfig.trustedOrigins}. Since 0.82.0 the static
4211
+ * {@link ApiClientConfig.headers} follow the same scope, and a `401` from
4212
+ * outside it is thrown without `refresh` or `onUnauthorized` — it answered a
4213
+ * request that never carried this session's credential.
4214
+ *
4215
+ * **Redirects.** The scope is checked against the URL the request starts at;
4216
+ * where a redirect then goes is the transport's call. For `Authorization` that
4217
+ * is safe: browsers strip it on a cross-origin redirect, measured in Chrome 150
4218
+ * for `fetch` and XHR on `301`/`302`/`303`/`307`/`308` (#554). They strip
4219
+ * nothing else, so a request carrying the static {@link ApiClientConfig.headers}
4220
+ * goes out with `redirect: "manual"`, and a redirect answering it is thrown as a
4221
+ * {@link TempestApiError} with `code: "REDIRECT_REFUSED"` instead of being
4222
+ * followed. It cannot be followed by hand: a browser's manual redirect is an
4223
+ * opaque response whose `Location` is unreadable. A `redirect` set on the call
4224
+ * always wins.
4181
4225
  *
4182
4226
  * **CSRF** is off unless you set `csrf`. With it, every unsafe request (anything
4183
4227
  * but `GET`/`HEAD`/`OPTIONS`/`TRACE`) echoes the `csrf_token` cookie in an
@@ -4505,7 +4549,13 @@ export declare function createDataProvider(client: ApiClient, options?: DataProv
4505
4549
  *
4506
4550
  * When `url` is a function it is called before every connection, so a
4507
4551
  * reconnect presents a fresh credential instead of the one that expired. A
4508
- * resolution still pending when `close()` or `reconnect()` runs is discarded.
4552
+ * resolution still pending when `close()` or `reconnect()` runs is discarded, and
4553
+ * one that has not started yet is never made: a stream closed in the same tick
4554
+ * it was created asks for no URL — no ticket request, for the ticket stream.
4555
+ *
4556
+ * Pass `sessionKey` to bind the stream to the session it opened under: once
4557
+ * the key changes, no frame of the previous session reaches `onMessage`, and
4558
+ * the stream reconnects for the new one.
4509
4559
  *
4510
4560
  * @param url - Full SSE endpoint URL, or a function returning it per connection.
4511
4561
  * @param options - Stream configuration and callbacks.
@@ -4573,6 +4623,30 @@ export declare interface CreateEventStreamOptions<T> {
4573
4623
  * URL that never resolved never had an `EventSource`.
4574
4624
  */
4575
4625
  onUrlError?: (error: unknown) => void;
4626
+ /**
4627
+ * Read the current session key; the stream belongs to the session that was
4628
+ * current when it connected.
4629
+ *
4630
+ * The server cannot end the stream for you. The `tempest-fastapi-sdk` SSE
4631
+ * router checks the ticket only when the stream opens, and neither logout
4632
+ * nor refresh-token revocation touches an open stream: it lives until the
4633
+ * client leaves. So a tab that switches from user A to user B without
4634
+ * closing the stream keeps receiving A's events under B.
4635
+ *
4636
+ * With this set, the key is read when each connection starts, again when
4637
+ * its URL resolved, and before every frame is delivered. A frame that
4638
+ * arrives after the key changed is dropped, never delivered, and the stream
4639
+ * reconnects for the session now current — with a fresh ticket, for the
4640
+ * ticket stream. A URL resolved while the key changed is discarded the same
4641
+ * way, since the ticket inside it may belong to either session.
4642
+ *
4643
+ * The check runs when a frame arrives: a stream that stays quiet holds its
4644
+ * connection until the next frame or until `close()`. The hook form closes
4645
+ * it as soon as the key changes.
4646
+ *
4647
+ * Keys are compared with `Object.is`. Leave it out and nothing changes.
4648
+ */
4649
+ sessionKey?: () => SessionKey;
4576
4650
  /**
4577
4651
  * Whether a URL-resolution failure is worth another attempt. Default: always.
4578
4652
  *
@@ -5526,8 +5600,13 @@ export declare interface CreateStorePersistOptions<T> {
5526
5600
  * elsewhere ends the session here too, with `onSessionEnd` told it happened
5527
5601
  * `elsewhere`. Before 0.81.0 a tab left open after logout kept account A and,
5528
5602
  * 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.
5603
+ * storage, persisting `{ user: A, token: B }` (#524). Whether the stored
5604
+ * session is the held one is decided by a **session lineage** that `login()`
5605
+ * mints and every rotation keeps — not by comparing users, which took two
5606
+ * sign-ins whose user had not loaded yet for the same account (#544). Two tabs
5607
+ * never redeem the same refresh token at once: the redemption runs under an
5608
+ * origin-wide Web Lock when the browser has one. `storage: "session"` is per
5609
+ * tab and behaves as before.
5531
5610
  *
5532
5611
  * Refreshes are collapsed per rotation, not just per overlap: a `401` whose
5533
5612
  * request left with a token that has since been replaced in the same session
@@ -5593,9 +5672,14 @@ export declare interface CreateTempestAuthOptions<TUser> {
5593
5672
  * while it is in flight waits for it, because the logout response clears
5594
5673
  * the auth cookies and would otherwise clear the new account's.
5595
5674
  *
5596
- * Only an explicit `logout()` revokes. On `"switch"` the cookie jar already
5597
- * holds the new account's cookie, and on `"expired"` the server has already
5598
- * refused the session.
5675
+ * An explicit `logout()` revokes, and so does a `"switch"` in body mode:
5676
+ * the replaced session's refresh token is still in the client's hands, and
5677
+ * the tempest-fastapi-sdk revokes by rotation family, so the session the
5678
+ * new login opened is untouched even for the same user. Before 0.82.0 a
5679
+ * switch revoked nothing and the replaced token stayed redeemable until it
5680
+ * expired. A `"switch"` in cookie mode does not revoke — the cookie jar
5681
+ * already holds the new account's cookie — and on `"expired"` the server
5682
+ * has already refused the session.
5599
5683
  *
5600
5684
  * Overlapping sign-outs each send their own revocation, and `login()` waits
5601
5685
  * for all of them — at most 10 seconds, so one that
@@ -5806,9 +5890,23 @@ export declare interface CreateThemeOptions {
5806
5890
  * cookie) on the ticket request, and the ticket in the URL opens that stream
5807
5891
  * and nothing else.
5808
5892
  *
5893
+ * With the `api` of `createTempestAuth` as `client` and no `sessionKey`, the
5894
+ * stream follows that preset's session (since 0.82.0): it closes the moment
5895
+ * the session is replaced or ends, reopens with a fresh ticket for the next
5896
+ * one, stays closed while nobody is signed in, and ignores token rotation.
5897
+ * Any other client binds only through `sessionKey`, checked per frame.
5898
+ *
5809
5899
  * @example
5810
5900
  * const stream = createTicketEventStream<Notification>({
5901
+ * client: auth.api,
5902
+ * onMessage: ({ data }) => addNotification(data),
5903
+ * });
5904
+ *
5905
+ * @example
5906
+ * // A client createTempestAuth did not build
5907
+ * const stream = createTicketEventStream<Notification>({
5811
5908
  * client: api,
5909
+ * sessionKey: () => useAuthStore.getState().user?.id ?? null,
5812
5910
  * onMessage: ({ data }) => addNotification(data),
5813
5911
  * onUrlError: (error) => toast.error(describeApiError(error)),
5814
5912
  * });
@@ -5992,6 +6090,24 @@ export declare interface CreateWebSocketOptions<T> {
5992
6090
  queueWhileClosed?: boolean;
5993
6091
  /** Cap on buffered payloads when `queueWhileClosed` is on. Default: 100. */
5994
6092
  maxQueuedMessages?: number;
6093
+ /**
6094
+ * Who is signed in right now — a stable, non-secret id such as the user id;
6095
+ * `null`/`undefined` when nobody is. Read when a payload is buffered by
6096
+ * `queueWhileClosed` and again when the buffer drains on the next open: a
6097
+ * payload goes out only while the same principal is signed in, and is
6098
+ * dropped otherwise.
6099
+ *
6100
+ * The URL and the protocols are fixed for the controller's life, so a socket
6101
+ * that authenticates by cookie re-handshakes as whoever is signed in at
6102
+ * reconnect time. Without this, a frame buffered by one account during the
6103
+ * backoff was sent on the reconnect under the next account's cookie when the
6104
+ * app did not `close()` the socket on logout (#531).
6105
+ *
6106
+ * Read synchronously. A non-string, non-number value (the `Promise` of an
6107
+ * `async` getter) or a getter that throws reads as nobody. Default: no
6108
+ * owner check.
6109
+ */
6110
+ getPrincipal?: () => string | number | null | undefined;
5995
6111
  /** Parse incoming frames. Default: JSON with raw-string fallback. */
5996
6112
  parser?: (raw: string) => T;
5997
6113
  /**
@@ -6794,10 +6910,18 @@ export declare function decodeJWT(token: string): DecodedJWT;
6794
6910
  * compression was enabled — or by a write that fell back after `gzipSync`
6795
6911
  * failed — still read back.
6796
6912
  *
6913
+ * The output size is bounded before anything is inflated. fflate sizes its
6914
+ * output buffer from the gzip trailer (`ISIZE`) and never grows it — measured: a
6915
+ * 1 MB payload whose trailer claims 10 bytes inflates to exactly 10 — so
6916
+ * refusing a trailer above {@link MAX_DECOMPRESSED_BYTES} bounds the allocation,
6917
+ * whatever the trailer says about the real data. A corrupted or hostile value
6918
+ * fails fast instead of taking the tab's memory.
6919
+ *
6797
6920
  * @typeParam T - The expected value shape.
6798
6921
  * @param raw - The stored string.
6799
6922
  * @returns The decoded value.
6800
- * @throws If the payload is neither valid compressed data nor valid JSON.
6923
+ * @throws If the payload is neither valid compressed data nor valid JSON, or
6924
+ * declares a decompressed size above 64 MiB.
6801
6925
  */
6802
6926
  export declare function decompressFromString<T>(raw: string): T;
6803
6927
 
@@ -6808,6 +6932,13 @@ export declare function decompressFromString<T>(raw: string): T;
6808
6932
  * - Arrays and non-plain values (dates, class instances, primitives) replace
6809
6933
  * the target value entirely — they are not merged element by element.
6810
6934
  * - Neither `target` nor `source` is mutated.
6935
+ * - `__proto__`, `constructor` and `prototype` keys in `source` are skipped.
6936
+ * Assigning `result["__proto__"]` does not create a key: it **replaces the
6937
+ * result's prototype**, so a `JSON.parse`d settings blob carrying
6938
+ * `{"__proto__": {"isAdmin": true}}` produced an object whose `isAdmin` read
6939
+ * `true` through inheritance while `Object.keys` showed nothing. No global
6940
+ * prototype is touched either way; the skip removes the surprise, and the
6941
+ * other two names go with it because nothing legitimate merges into them.
6811
6942
  *
6812
6943
  * @example
6813
6944
  * deepMerge(
@@ -8247,7 +8378,7 @@ export declare type FormActionsAlign = "start" | "center" | "end" | "between";
8247
8378
  export declare interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {
8248
8379
  /** Horizontal alignment of buttons. Default `end`. */
8249
8380
  align?: FormActionsAlign;
8250
- /** Gap between buttons. Numbers map to a multiple of the 4px scale (default `2` → 8px). */
8381
+ /** Gap between buttons. Numbers map to a multiple of the 4px scale (default `2` → 8px). A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `"96px"` passes through untouched. */
8251
8382
  gap?: number | string;
8252
8383
  }
8253
8384
 
@@ -8597,7 +8728,7 @@ export { FormProvider }
8597
8728
  export declare function FormRow({ gap, className, style, children, ...props }: FormRowProps): JSX.Element;
8598
8729
 
8599
8730
  export declare interface FormRowProps extends HTMLAttributes<HTMLDivElement> {
8600
- /** Gap between row items. Numbers map to a multiple of the 4px scale (default `3` → 12px). */
8731
+ /** Gap between row items. Numbers map to a multiple of the 4px scale (default `3` → 12px). A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `"96px"` passes through untouched. */
8601
8732
  gap?: number | string;
8602
8733
  }
8603
8734
 
@@ -8720,7 +8851,14 @@ export declare interface GetListParams {
8720
8851
  field: string;
8721
8852
  order?: "asc" | "desc";
8722
8853
  };
8723
- /** Arbitrary filter query params, spread onto the request. */
8854
+ /**
8855
+ * Arbitrary filter query params, spread onto the request **before**
8856
+ * `pagination` and `sort`. A filter whose key equals a pagination or sort
8857
+ * param name (`page`, `size`, `order_by`, `ascending` by default) is
8858
+ * overridden by the structured value when that one is set: until 0.81.0 the
8859
+ * filter won, so `filters: { page: 1 }` from a table's filter state silently
8860
+ * pinned every request to page 1.
8861
+ */
8724
8862
  filters?: DataFilters;
8725
8863
  }
8726
8864
 
@@ -8769,7 +8907,7 @@ export declare function getThemePreset(name: string): CreateThemeOptions | undef
8769
8907
  * />
8770
8908
  * </GoogleOAuthProvider>
8771
8909
  */
8772
- export declare function GoogleSignIn({ component: Component, onSuccess, onError, locale, theme, text, shape, size, disableOneTap, width, className, style, }: GoogleSignInProps): JSX.Element;
8910
+ export declare function GoogleSignIn({ component: Component, onSuccess, onError, locale, theme, text, shape, size, disableOneTap, width, nonce, className, style, }: GoogleSignInProps): JSX.Element;
8773
8911
 
8774
8912
  export declare interface GoogleSignInProps {
8775
8913
  /**
@@ -8796,6 +8934,16 @@ export declare interface GoogleSignInProps {
8796
8934
  disableOneTap?: boolean;
8797
8935
  /** Optional `width` override (px) — Google's button is fixed-width. */
8798
8936
  width?: number;
8937
+ /**
8938
+ * Server-issued nonce Google copies into the ID token's `nonce` claim.
8939
+ *
8940
+ * The backend issues one per sign-in attempt, keeps it, and rejects an
8941
+ * `id_token` whose claim does not match — which is what stops a token
8942
+ * captured elsewhere from being replayed against your API. Until 0.82.0
8943
+ * the wrapper dropped it, so an app could not bind the token through
8944
+ * `<GoogleSignIn>` at all (#530).
8945
+ */
8946
+ nonce?: string;
8799
8947
  /** Optional className applied to the wrapper. */
8800
8948
  className?: string;
8801
8949
  /** Optional inline style applied to the wrapper. */
@@ -8816,7 +8964,7 @@ export declare function Grid({ columns, gap, className, style, children, ...prop
8816
8964
  export declare interface GridProps extends HTMLAttributes<HTMLDivElement> {
8817
8965
  /** Number of columns or a custom `grid-template-columns` value. Accepts responsive object. */
8818
8966
  columns?: ResponsiveValue<number | string>;
8819
- /** Gap as a CSS length. Numbers map to a multiple of the 4px scale. Accepts responsive object. */
8967
+ /** Gap as a CSS length. Numbers map to a multiple of the 4px scale. Accepts responsive object. A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `"96px"` passes through untouched. */
8820
8968
  gap?: ResponsiveValue<number | string>;
8821
8969
  children?: ReactNode;
8822
8970
  }
@@ -9494,7 +9642,9 @@ export declare type InstallMethod = "native" | "ios" | "manual" | "none";
9494
9642
  *
9495
9643
  * A click on an action button opens that action's URL (see
9496
9644
  * {@link PushNotificationAction.url}); a click on the body, or on an action
9497
- * without one, opens the top-level `url`.
9645
+ * without one, opens the top-level `url`. Only a same-origin `http(s)` URL is
9646
+ * opened by default; see {@link InstallNotificationClickHandlerOptions.resolveUrl}
9647
+ * for opting into another origin.
9498
9648
  */
9499
9649
  export declare function installNotificationClickHandler(options?: InstallNotificationClickHandlerOptions): void;
9500
9650
 
@@ -9503,6 +9653,12 @@ export declare interface InstallNotificationClickHandlerOptions {
9503
9653
  * Resolve the destination URL from the notification data and the clicked
9504
9654
  * action id (`""` or `undefined` for a click on the body). Default: the
9505
9655
  * action's entry in `data.actionUrls`, else `data.url`, else `"/"`.
9656
+ *
9657
+ * The default only ever opens a page of the worker's **own origin**: a URL on
9658
+ * another origin, or with a scheme other than `http:`/`https:`, becomes `"/"`.
9659
+ * A resolver of your own is how an app opts into another origin — what it
9660
+ * returns may point anywhere `http:`/`https:`, and any other scheme still
9661
+ * becomes `"/"`.
9506
9662
  */
9507
9663
  resolveUrl?: (data: unknown, action: string | undefined) => string;
9508
9664
  }
@@ -9549,8 +9705,9 @@ export declare interface InstallPrecacheOptions {
9549
9705
  * Every `NotificationOptions` member the payload carries is forwarded —
9550
9706
  * `actions`, `requireInteraction`, `vibrate`, `silent`, `renotify`,
9551
9707
  * `timestamp`, `dir` and `lang` alongside `body`, `icon`, `badge`, `image` and
9552
- * `tag`. `data` receives the top-level `url` (default `"/"`), the payload's
9553
- * own `data`, and `actionUrls` mapping each action id to its `url`.
9708
+ * `tag`. `data` receives the payload's own `data`, then the top-level `url`
9709
+ * (default `"/"`) and `actionUrls` mapping each action id to its `url` — in that
9710
+ * order, so `payload.data` cannot override where a click goes.
9554
9711
  */
9555
9712
  export declare function installPushHandler(options?: InstallPushHandlerOptions): void;
9556
9713
 
@@ -9574,9 +9731,10 @@ export declare interface InstallPushHandlerOptions {
9574
9731
  * `respondWith`), so a later {@link installPrecache} can handle them.
9575
9732
  *
9576
9733
  * A request carrying a credential header (`Authorization`, `X-Api-Key`, … — the
9577
- * route's `credentialHeaders`) is also left untouched unless its route sets
9734
+ * route's `credentialHeaders`), or a CORS `fetch` sent with
9735
+ * `credentials: "include"`, is also left untouched unless its route sets
9578
9736
  * `cacheAuthenticated`, and a response with `Cache-Control: no-store`
9579
- * (or `private`, without that opt-in) is served but never stored. `maxAgeSeconds`
9737
+ * (or `private`, without that opt-in) or `Vary: *` is served but never stored. `maxAgeSeconds`
9580
9738
  * applies to every read, including the offline fallback: an expired entry is a
9581
9739
  * miss, never a last resort. Under `rangeRequests`, the full resource is cached
9582
9740
  * once (the `Range` header stripped) and each range is sliced from it.
@@ -9973,11 +10131,13 @@ declare type IssuePathSegment = PropertyKey | {
9973
10131
  * rather than the request blocked: the upload still goes through, it just goes
9974
10132
  * without the API's bearer token.
9975
10133
  *
9976
- * Both sides are resolved against the document first, so a relative `baseURL`
9977
- * or `endpoint` — the shape behind a dev-server or reverse proxy — compares
9978
- * equal to the absolute URL the request actually goes to. Outside a browsing
9979
- * context a relative target resolves to nothing and is allowed: there is no
9980
- * origin for it to cross.
10134
+ * Both sides are resolved first, so a relative `baseURL` or `endpoint` — the
10135
+ * shape behind a dev-server or reverse proxy — compares equal to the absolute
10136
+ * URL the request actually goes to. The target resolves against the document
10137
+ * base URL, where the transport sends it; the reference against the page's
10138
+ * own address, which a `<base href>` cannot move. Outside a browsing context a
10139
+ * relative target is allowed — there is no origin for it to cross — and a
10140
+ * target that does not parse is refused rather than trusted.
9981
10141
  *
9982
10142
  * @param target - The URL the request is about to go to.
9983
10143
  * @param reference - The origin the credential belongs to, normally the
@@ -10273,7 +10433,7 @@ declare interface LayoutProps {
10273
10433
  layout?: FormLayout;
10274
10434
  /** Number of columns when `layout="grid"`, or a custom `grid-template-columns` value. Default `2`. */
10275
10435
  columns?: number | string;
10276
- /** Gap between fields. Numbers map to a multiple of the 4px scale (default `4` → 16px). */
10436
+ /** Gap between fields. Numbers map to a multiple of the 4px scale (default `4` → 16px). A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `"96px"` passes through untouched. */
10277
10437
  gap?: number | string;
10278
10438
  }
10279
10439
 
@@ -11604,7 +11764,12 @@ export declare function NavigationMenu({ items, portal, className, ...props }: N
11604
11764
  export declare interface NavigationMenuItem {
11605
11765
  /** Visible label for the entry. */
11606
11766
  label: ReactNode;
11607
- /** When set, the entry renders as an anchor. */
11767
+ /**
11768
+ * When set, the entry renders as an anchor. The URL is checked against
11769
+ * `safeLinkUrl`'s allowlist: a scheme outside `http:`/`https:`/`mailto:`/`tel:`/`sms:`
11770
+ * renders the entry as a button instead, so a menu built from API data cannot
11771
+ * put a clickable `javascript:` link on the page under React 18.
11772
+ */
11608
11773
  href?: string;
11609
11774
  /** Invoked when the entry is activated (click / Enter). */
11610
11775
  onSelect?: () => void;
@@ -11976,7 +12141,8 @@ export declare interface OfflineQueryPersistence {
11976
12141
  /**
11977
12142
  * Load the persisted snapshot into the `QueryClient`. Call once on boot,
11978
12143
  * before rendering queries, so cached data is available offline. A no-op
11979
- * once {@link OfflineQueryPersistence.clear} has been called.
12144
+ * once {@link OfflineQueryPersistence.clear} has been called, and — with
12145
+ * `getPrincipal` — for a snapshot written under another principal.
11980
12146
  */
11981
12147
  restore: () => Promise<void>;
11982
12148
  /**
@@ -12036,6 +12202,29 @@ export declare interface OfflineQueryPersistenceOptions {
12036
12202
  * one trailing write per window. Default `1000`.
12037
12203
  */
12038
12204
  throttleMs?: number;
12205
+ /**
12206
+ * Who is signed in right now — a stable, non-secret id such as the user id;
12207
+ * `null`/`undefined` when nobody is. Read on every write, which stamps the
12208
+ * snapshot with a one-way fingerprint of it (never the id itself), and on
12209
+ * `restore()`, which hydrates only a snapshot stamped with the same
12210
+ * fingerprint.
12211
+ *
12212
+ * `clear()` on logout already deletes the snapshot; this covers the session
12213
+ * that ends without it — a token that expired, a crash, a tab closed while
12214
+ * signed in. Without it, the next account to boot on the browser is shown
12215
+ * the previous account's cached responses (#531). A snapshot written before
12216
+ * this option was set carries no stamp and is not restored either; it is a
12217
+ * cache, so the cost is one cold start.
12218
+ *
12219
+ * A foreign snapshot is skipped, not deleted: a cookie app that only learns
12220
+ * the principal after a `/me` round trip would otherwise wipe its own cache
12221
+ * on every cold start. The next write replaces it.
12222
+ *
12223
+ * Read synchronously. A non-string, non-number value (the `Promise` of an
12224
+ * `async` getter) or a getter that throws reads as nobody. Omit it to restore
12225
+ * every snapshot, as before 0.82.0.
12226
+ */
12227
+ getPrincipal?: () => string | number | null | undefined;
12039
12228
  }
12040
12229
 
12041
12230
  /** Maps each table name to the record type it stores. */
@@ -12212,6 +12401,30 @@ export declare interface OfflineSyncConfig<TPayload, TRemote> {
12212
12401
  crossTab?: boolean;
12213
12402
  /** Channel name when `crossTab` is on. Default `tempest-sync:${databaseName}`. */
12214
12403
  broadcastChannelName?: string;
12404
+ /**
12405
+ * Who is signed in right now — a stable, non-secret id such as the user id;
12406
+ * `null`/`undefined` when nobody is. Read synchronously when an entry is
12407
+ * queued, which stamps it with a one-way fingerprint (`entry.owner`, never
12408
+ * the id itself), and again right before each delivery:
12409
+ *
12410
+ * - same principal → delivered;
12411
+ * - nobody signed in now → held: it stays queued and goes out when its owner
12412
+ * signs back in, so a flush fired by the `online` event before login loses
12413
+ * nothing;
12414
+ * - anybody else → deleted unsent and counted in `summary.dropped`, because
12415
+ * delivering it would send the previous account's mutation with the next
12416
+ * account's session.
12417
+ *
12418
+ * `clearOutbox()` on logout already empties the queue; this covers the app
12419
+ * that forgets it, or a session that ends without a logout (#531). Entries
12420
+ * queued before this option was set carry no stamp and are delivered as
12421
+ * before — they are the user's own unsynced edits, and holding them would
12422
+ * strand every queue on upgrade.
12423
+ *
12424
+ * A non-string, non-number value (the `Promise` of an `async` getter) or a
12425
+ * getter that throws reads as nobody. Default: no owner binding.
12426
+ */
12427
+ getPrincipal?: () => string | number | null | undefined;
12215
12428
  }
12216
12429
 
12217
12430
  export declare interface OfflineTableConfig<TItem> {
@@ -12493,6 +12706,13 @@ export declare interface OutboxEntry<TPayload = unknown> {
12493
12706
  lastError?: string;
12494
12707
  /** Record snapshot for `create`/`update`. Omitted for `delete`. */
12495
12708
  payload?: TPayload;
12709
+ /**
12710
+ * One-way fingerprint of whoever was signed in when the entry was queued
12711
+ * (`null` for nobody), stamped only when the engine has
12712
+ * {@link OfflineSyncConfig.getPrincipal}. Absent on entries queued without
12713
+ * it, which are delivered as before.
12714
+ */
12715
+ owner?: string | null;
12496
12716
  }
12497
12717
 
12498
12718
  /**
@@ -13059,6 +13279,23 @@ export declare interface PeerMeshOptions {
13059
13279
  onState?: (state: MeshState, detail?: string) => void;
13060
13280
  /** Called for a failure worth surfacing that is not fatal to the mesh. */
13061
13281
  onNotice?: (reason: string) => void;
13282
+ /**
13283
+ * Decide whether an offer from a peer the mesh has no link to is answered.
13284
+ *
13285
+ * By default the mesh answers any offer that reaches {@link PeerMesh.accept},
13286
+ * opening a link and publishing the local tracks to it — the answering side
13287
+ * usually learns of a peer from its offer, so that is the documented flow.
13288
+ * It also means whether the microphone and camera reach someone rests only
13289
+ * on the signalling server relaying offers to the right people. Pass a gate
13290
+ * — typically "is this user in the room roster I fetched" — to make that
13291
+ * the app's decision too. A refused offer opens nothing, answers nothing and
13292
+ * reports `onNotice("offer_refused")`. Peers added with `addPeer` are not
13293
+ * asked: adding them was the decision.
13294
+ *
13295
+ * @param peerId - The `from` of the offer.
13296
+ * @returns `true` to answer it.
13297
+ */
13298
+ acceptPeer?: (peerId: string) => boolean;
13062
13299
  /** Initial encoder limits. */
13063
13300
  quality?: MeshQuality;
13064
13301
  /** Measure every link, on one timer the mesh owns. See {@link MeshStatsOptions}. */
@@ -13541,7 +13778,13 @@ export declare function projectMercator(coord: Coordinate): MercatorPoint;
13541
13778
  *
13542
13779
  * Pass `getPrincipal` to bind each entry to whoever queued it: the worker asks
13543
13780
  * the issuing tab right after queueing, asks every tab again before replaying,
13544
- * and drops the entry when no signed-in tab is its owner.
13781
+ * and drops the entry when no signed-in tab is its owner. Without it every
13782
+ * entry is `"unbound"` and replays with whichever session answers — the next
13783
+ * account's bearer included, unless `clearSessionData()` ran on logout — so a
13784
+ * development build warns once per call (#531). Treating an unbound
13785
+ * credentialed entry as `"unknown"` instead would change nothing: an
13786
+ * `"unknown"` entry replays when no answering tab reports a principal, which is
13787
+ * exactly the page that omits `getPrincipal`.
13545
13788
  *
13546
13789
  * @param getHeaders - Resolves the headers for one queued request at replay.
13547
13790
  * @param options - `getPrincipal` binds entries to their owner.
@@ -13663,7 +13906,14 @@ export declare interface PushPayload {
13663
13906
  dir?: NotificationDirection;
13664
13907
  /** BCP 47 language tag of the title and body. */
13665
13908
  lang?: string;
13666
- /** Arbitrary extra data forwarded to `event.notification.data`. */
13909
+ /**
13910
+ * Arbitrary extra data forwarded to `event.notification.data`.
13911
+ *
13912
+ * Its `url` and `actionUrls` keys are overwritten by the top-level `url` and
13913
+ * the actions' `url`s: those two keys decide where a click goes, and a
13914
+ * free-form bag — often filled from the record that triggered the push — must
13915
+ * not be able to redirect it.
13916
+ */
13667
13917
  data?: Record<string, unknown>;
13668
13918
  }
13669
13919
 
@@ -14580,7 +14830,11 @@ export declare interface ResumableUploadOptions {
14580
14830
  chunkSize?: number;
14581
14831
  /** Sent as `Upload-Metadata` (base64-encoded values), e.g. `{ filename }`. */
14582
14832
  metadata?: Record<string, string>;
14583
- /** Extra headers on every request. */
14833
+ /**
14834
+ * Extra headers on every request inside the credential scope — the
14835
+ * `endpoint`'s origin plus {@link trustedOrigins}. A `Location` on another
14836
+ * host gets only the tus headers, never these (since 0.82.0).
14837
+ */
14584
14838
  headers?: Record<string, string>;
14585
14839
  /** Returns the current bearer token, read before each request. */
14586
14840
  getToken?: () => string | null | undefined;
@@ -14592,13 +14846,20 @@ export declare interface ResumableUploadOptions {
14592
14846
  * The resume record is stamped with a one-way fingerprint of it, and a
14593
14847
  * record stamped by anyone else — another user, nobody, or no stamp at all
14594
14848
  * — is deleted instead of resumed: its upload URL is not probed, and its
14595
- * `Idempotency-Key` is never replayed. Without it, the record is bound to
14596
- * the file alone (endpoint, name, size, type, mtime), so after user A
14597
- * signed out leaving an upload half-done, user B picking an identical file
14598
- * sent `HEAD`/`PATCH` to A's upload URL with B's token, and on refusal
14599
- * replayed A's `Idempotency-Key` with B's token (#479). Pass it on every
14600
- * app with more than one account per browser, and also clear the storage
14601
- * at session end (`createLocalUploadStorage().clear()` in `onSessionEnd`).
14849
+ * `Idempotency-Key` is never replayed (#479).
14850
+ *
14851
+ * Without it, since 0.82.0 the record is bound to a fingerprint of the
14852
+ * `getToken` token instead, so another account's record is still never
14853
+ * adopted (#528) — but a token rotates: a reload after a refresh that no
14854
+ * chunk landed under starts the upload over. Pass `getPrincipal` to keep
14855
+ * resuming across refreshes, and always with a cookie session, where there
14856
+ * is no token to bind to and the record stays unbound.
14857
+ *
14858
+ * `getToken: auth.getToken` — the getter of `createTempestAuth` itself, not
14859
+ * an arrow around it — binds to that preset's session instead, as if
14860
+ * `getPrincipal: auth.sessionKey` were passed: the record survives token
14861
+ * rotation, a switch of account stops the run, and a new `login()`, even
14862
+ * into the same account, starts over.
14602
14863
  *
14603
14864
  * It is read again before every chunk and every record write: when it
14604
14865
  * names someone else mid-run, the run stops with
@@ -14675,10 +14936,11 @@ export declare interface ResumableUploadRecord {
14675
14936
  /** Epoch ms of the last write, so an app can sweep stale records. */
14676
14937
  updatedAt: number;
14677
14938
  /**
14678
- * One-way fingerprint of the principal that created the upload (`"p:<hex>"`),
14679
- * `null` for an anonymous one. Absent when the upload ran without
14680
- * {@link ResumableUploadOptions.getPrincipal}, and on records written before
14681
- * 0.78.0.
14939
+ * One-way fingerprint of the principal that created the upload (`"p:<hex>"`)
14940
+ * or, without {@link ResumableUploadOptions.getPrincipal}, of the bearer
14941
+ * token of its last write (`"t:<hex>"`, since 0.82.0); `null` for an
14942
+ * anonymous one. Absent when the upload ran with neither getter, and on
14943
+ * records written before 0.78.0.
14682
14944
  */
14683
14945
  owner?: string | null;
14684
14946
  }
@@ -14999,8 +15261,9 @@ export declare interface RuntimeRoute {
14999
15261
  */
15000
15262
  rangeRequests?: boolean;
15001
15263
  /**
15002
- * Opt in to caching requests that carry a credential header (see
15003
- * `credentialHeaders`), and responses marked `Cache-Control: private`.
15264
+ * Opt in to caching credentialed requests — those that carry a credential
15265
+ * header (see `credentialHeaders`) or are a CORS-mode `fetch` sent with
15266
+ * `credentials: "include"` — and responses marked `Cache-Control: private`.
15004
15267
  * Default `false`.
15005
15268
  *
15006
15269
  * Cache Storage is keyed by URL and outlives the session: without this
@@ -15017,11 +15280,18 @@ export declare interface RuntimeRoute {
15017
15280
  * `clearSessionData()` on logout, which removes the entries themselves.
15018
15281
  * `Cache-Control: no-store` is never cached, opt-in or not.
15019
15282
  *
15020
- * A cookie session is invisible here: the worker cannot see `Cookie`, and
15021
- * gating on `request.credentials` would switch off caching for every
15022
- * same-origin image and font. A cookie-authenticated API stays private only
15283
+ * A cookie session is only partly visible here: the worker cannot see
15284
+ * `Cookie`. What it can see is a `fetch` that asked for cookies explicitly —
15285
+ * `mode: "cors"` with `credentials: "include"`, which is what `ApiClient`
15286
+ * sends under `withCredentials` — and since 0.82.0 that request is treated
15287
+ * as credentialed too (#531). `<img>`, `<audio>` and `<video>` also send
15288
+ * `credentials: "include"`, but in `no-cors` mode, so they keep caching. A
15289
+ * plain same-origin `fetch` (`credentials: "same-origin"`, the default)
15290
+ * carries the cookie without saying so; such an API stays private only
15023
15291
  * through `Cache-Control: private`/`no-store` on its responses and
15024
- * `clearSessionData()` on logout.
15292
+ * `clearSessionData()` on logout. Opting a cookie request in caches it by
15293
+ * URL — there is no credential header to vary on — so `clearSessionData()`
15294
+ * is then its only isolation.
15025
15295
  */
15026
15296
  cacheAuthenticated?: boolean;
15027
15297
  /**
@@ -15645,6 +15915,16 @@ export declare interface SequentialScaleOptions {
15645
15915
  ordinal?: boolean;
15646
15916
  }
15647
15917
 
15918
+ /**
15919
+ * Identifies the session a stream belongs to: the signed-in user's id, an
15920
+ * auth epoch, `null` when signed out.
15921
+ *
15922
+ * A primitive on purpose. The hook form lists it in its effect dependencies,
15923
+ * and an object built inline would be a new identity on every render, so the
15924
+ * stream would reopen on every render.
15925
+ */
15926
+ export declare type SessionKey = string | number | boolean | null | undefined;
15927
+
15648
15928
  /**
15649
15929
  * Thrown when the session that started an operation is no longer the current
15650
15930
  * one by the time it settles: a `refresh` hook (rethrown by
@@ -16026,6 +16306,10 @@ export declare interface SidebarItem {
16026
16306
  * A `disabled` entry ignores this and stays a `<button disabled>`: there is no
16027
16307
  * disabled state for an anchor, and dropping the `href` to fake one leaves a
16028
16308
  * link that announces itself as actionable and is not.
16309
+ *
16310
+ * The URL goes through `safeLinkUrl`: a scheme outside
16311
+ * `http:`/`https:`/`mailto:`/`tel:`/`sms:` makes the entry a button, the same
16312
+ * as an entry without `href`.
16029
16313
  */
16030
16314
  href?: string;
16031
16315
  }
@@ -16506,7 +16790,7 @@ export declare type StackJustify = "start" | "center" | "end" | "between";
16506
16790
  export declare interface StackProps extends HTMLAttributes<HTMLDivElement> {
16507
16791
  /** Direction. Accepts responsive object — e.g. `{ mobile: "vertical", desktop: "horizontal" }`. */
16508
16792
  direction?: ResponsiveValue<StackDirection>;
16509
- /** Gap as a CSS length. Numbers map to a multiple of the 4px scale. Accepts responsive object. */
16793
+ /** Gap as a CSS length. Numbers map to a multiple of the 4px scale. Accepts responsive object. A number is a step, not pixels (`6` → 24px): above `12` a development build warns once, and a string such as `"96px"` passes through untouched. */
16510
16794
  gap?: ResponsiveValue<number | string>;
16511
16795
  align?: StackAlign;
16512
16796
  justify?: StackJustify;
@@ -16727,6 +17011,17 @@ export declare interface SyncRunSummary {
16727
17011
  * run that went to completion.
16728
17012
  */
16729
17013
  interrupted?: true;
17014
+ /**
17015
+ * Entries left queued because nobody is signed in and they belong to
17016
+ * somebody (see {@link OfflineSyncConfig.getPrincipal}). Absent when zero.
17017
+ */
17018
+ held?: number;
17019
+ /**
17020
+ * Entries deleted unsent because they were queued under another principal —
17021
+ * or while signed out — and somebody else is signed in now (see
17022
+ * {@link OfflineSyncConfig.getPrincipal}). Absent when zero.
17023
+ */
17024
+ dropped?: number;
16730
17025
  }
16731
17026
 
16732
17027
  /**
@@ -17020,6 +17315,23 @@ export declare interface TempestAuth<TUser, TCredentials> {
17020
17315
  refresh: () => Promise<void>;
17021
17316
  /** The current access token, or null. */
17022
17317
  getToken: () => string | null;
17318
+ /**
17319
+ * The key of the session signed in now, or `null` when nobody is.
17320
+ *
17321
+ * It changes when the session is replaced — `login()`, even into the same
17322
+ * account, a sign-in or sign-out in another tab, `logout()` — and stays
17323
+ * the same while that session's tokens rotate: it is the session lineage
17324
+ * `login()` mints, not the token. That is the identity a per-session
17325
+ * resource needs. `useTicketEventStream`/`createTicketEventStream` read it
17326
+ * on their own from `api`, and `createResumableUpload` from `getToken`;
17327
+ * pass it as `sessionKey` or `getPrincipal` anywhere else.
17328
+ *
17329
+ * A session that has no lineage — persisted before lineages existed, or
17330
+ * restored from a refresh cookie without a `login()` in this storage —
17331
+ * answers `"unlineaged"`. Replacing it still takes a
17332
+ * `login()`, which mints one, so the key still changes on a switch.
17333
+ */
17334
+ sessionKey: () => string | null;
17023
17335
  /**
17024
17336
  * Stop following the other tabs: removes the `storage` listener this
17025
17337
  * preset adds to `window`. Idempotent.
@@ -17194,6 +17506,10 @@ export declare function themeContrast(options: CreateThemeOptions): number | nul
17194
17506
  * hydrates. Inline this in `<head>` to avoid a flash of the wrong theme on
17195
17507
  * first paint.
17196
17508
  *
17509
+ * Both options are embedded through an escaper that keeps them inside the
17510
+ * `<script>` element whatever they contain, so the snippet is safe to inline
17511
+ * even when the storage key comes from configuration.
17512
+ *
17197
17513
  * @example
17198
17514
  * <script dangerouslySetInnerHTML={{ __html: themeInitScript() }} />
17199
17515
  */
@@ -17289,6 +17605,10 @@ export declare interface TicketEventStreamSource {
17289
17605
  * Authenticated client the ticket is requested through, so it carries the
17290
17606
  * `Authorization` header or the session cookie, the CSRF header, and the
17291
17607
  * 401 refresh-and-replay that a hand-rolled `fetch` would lose.
17608
+ *
17609
+ * The `api` of `createTempestAuth` also carries the session it acts for,
17610
+ * and the stream binds to it when `sessionKey` is not passed — see
17611
+ * {@link useTicketEventStream}.
17292
17612
  */
17293
17613
  client: Pick<ApiClient, "post" | "url">;
17294
17614
  /** Router prefix, relative to the client's base URL. Default: `"/sse"`. */
@@ -18094,7 +18414,12 @@ export declare function uploadFingerprint(endpoint: string, file: Blob | File):
18094
18414
  * allowing a preflight.
18095
18415
  *
18096
18416
  * 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
18417
+ * the same `413 FILE_TOO_LARGE` the server would answer. A numeric string
18418
+ * (`"5242880"`) compares as a number; a missing or non-numeric `max_bytes` —
18419
+ * a bare boto3 `generate_presigned_post` has none — skips this precheck, and
18420
+ * the signed policy's `content-length-range` stays the control that refuses
18421
+ * the bytes. The precheck saves a wasted upload; it never was the limit.
18422
+ * Server errors are
18098
18423
  * normalized into one `TempestApiError` whichever backend answered: the S3 XML
18099
18424
  * (`EntityTooLarge` → `FILE_TOO_LARGE`, `EntityTooSmall` → `FILE_TOO_SMALL`,
18100
18425
  * `AccessDenied` → `SIGNED_URL_INVALID`, or `SIGNED_URL_EXPIRED` when the policy
@@ -18151,18 +18476,22 @@ export declare interface UploadWithProgressOptions {
18151
18476
  getToken?: () => string | null | undefined;
18152
18477
  /**
18153
18478
  * Origin the `getToken` credential belongs to, normally the API's.
18479
+ * Defaults to the page's own origin (`location.origin`).
18154
18480
  *
18155
18481
  * Unlike `createApiClient`, this helper has no base URL to infer a scope
18156
- * from — the caller names the destination on every call. Set this when the
18157
- * `url` can come from somewhere other than your own code (a signed URL the
18158
- * API handed back, a value from a response body) and the token must not
18159
- * follow it off-origin. Left unset, the header goes wherever `url` points,
18160
- * which is the behaviour every version before 0.66.0 had.
18482
+ * from — the caller names the destination on every call, and the `url` can
18483
+ * come from somewhere other than your own code (a signed URL the API handed
18484
+ * back, a value from a response body). Until 0.82.0 an unset scope sent the
18485
+ * token wherever `url` pointed; it now starts closed, like the CSRF token
18486
+ * always did, and an API on another origin sets this to receive it (#530).
18487
+ *
18488
+ * Outside a browsing context there is no page origin, and the scope falls
18489
+ * back to `url` itself.
18161
18490
  */
18162
18491
  credentialOrigin?: string;
18163
18492
  /**
18164
18493
  * Extra origins allowed to receive the credential, on top of
18165
- * {@link credentialOrigin}. Ignored when that one is unset.
18494
+ * {@link credentialOrigin} (or the page origin it defaults to).
18166
18495
  */
18167
18496
  trustedOrigins?: readonly string[];
18168
18497
  /** Send cookies. Defaults to false. */
@@ -18171,12 +18500,9 @@ export declare interface UploadWithProgressOptions {
18171
18500
  * Send a CSRF token with the upload, as `createApiClient`'s `csrf` does.
18172
18501
  * Off by default; `true` reads the `csrf_token` cookie into `X-CSRF-Token`.
18173
18502
  *
18174
- * Scoped to {@link credentialOrigin} plus {@link trustedOrigins} — and,
18175
- * unlike the bearer token, scoped even when `credentialOrigin` is unset:
18176
- * then the scope is the page's own origin. The bearer default stays open
18177
- * only for compatibility with every version before 0.66.0; this option is
18178
- * new, so it starts closed. An API on another origin sets
18179
- * `credentialOrigin` to receive the token.
18503
+ * Scoped exactly like the bearer token: to {@link credentialOrigin} (the
18504
+ * page's own origin when unset) plus {@link trustedOrigins}. An API on
18505
+ * another origin sets `credentialOrigin` to receive the token.
18180
18506
  */
18181
18507
  csrf?: boolean | CsrfOptions;
18182
18508
  /** Called on every `progress` event from the XHR upload channel. */
@@ -18557,9 +18883,19 @@ export declare function useBreakpoint(): BreakpointHelpers;
18557
18883
  *
18558
18884
  * Handles sync booleans, sync {@link CanResult}, and promises of either. When
18559
18885
  * no provider is present, access is allowed (see `useAccessControl`). The check
18560
- * re-runs whenever the params change.
18886
+ * re-runs whenever the params change; they are compared by their
18887
+ * `JSON.stringify` form, so an inline object literal does not re-run it on
18888
+ * every render.
18889
+ *
18890
+ * A resolved answer is stored with the control and the params it answers, and
18891
+ * is only returned while both still match. Until 0.81.0 the previous answer was
18892
+ * returned with `isLoading: false` for the render between a params change and
18893
+ * the effect that re-ran the check, so `<Can>` showed the children for the new
18894
+ * resource for one frame on the strength of the old resource's permission. The
18895
+ * first render for new params now reads `{ allowed: false, isLoading: true }`.
18561
18896
  *
18562
18897
  * @param params - The action/resource being checked.
18898
+ * @returns The answer for exactly these params, or the pending state.
18563
18899
  */
18564
18900
  export declare function useCan(params: CanParams): UseCanResult;
18565
18901
 
@@ -18899,8 +19235,12 @@ export declare function useEventListener<K extends keyof HTMLElementEventMap>(ev
18899
19235
 
18900
19236
  /**
18901
19237
  * React hook wrapper around {@link createEventStream}. Connection lifecycle is
18902
- * tied to the component (and the `url`/`enabled` dependencies); the stream
18903
- * closes on unmount.
19238
+ * tied to the component (and the `url`/`enabled`/`sessionKey` dependencies);
19239
+ * the stream closes on unmount.
19240
+ *
19241
+ * Pass `sessionKey` (the signed-in user's id) whenever the stream carries one
19242
+ * user's data: a change of account then closes the previous user's stream and
19243
+ * clears `lastMessage`, even when `enabled` never went `false` in between.
18904
19244
  *
18905
19245
  * A string `url` reopens the stream when it changes. A function `url` does not:
18906
19246
  * an inline arrow is a new function every render, and keying on its identity
@@ -18932,9 +19272,24 @@ export declare function useEventListener<K extends keyof HTMLElementEventMap>(ev
18932
19272
  */
18933
19273
  export declare function useEventStream<T = unknown>(url: EventStreamUrl, options?: UseEventStreamOptions<T>): UseEventStreamResult<T>;
18934
19274
 
18935
- export declare interface UseEventStreamOptions<T> extends Omit<CreateEventStreamOptions<T>, "onStatusChange"> {
19275
+ export declare interface UseEventStreamOptions<T> extends Omit<CreateEventStreamOptions<T>, "onStatusChange" | "sessionKey"> {
18936
19276
  /** When false, the stream is not opened. Useful for "wait for auth". Default: true. */
18937
19277
  enabled?: boolean;
19278
+ /**
19279
+ * The session the stream belongs to — the signed-in user's id, typically.
19280
+ *
19281
+ * A change closes the stream, clears `lastMessage` and, while `enabled`,
19282
+ * opens a new connection for the new session (a fresh ticket, for
19283
+ * {@link useTicketEventStream}). Without it, a tab that switches accounts
19284
+ * without passing through `enabled: false` keeps the previous user's
19285
+ * stream open and delivers its events under the new user: the server does
19286
+ * not end an open stream on logout or revocation, so the client has to.
19287
+ *
19288
+ * A frame that arrives between the render that changed the key and the
19289
+ * effect that closes the stream is dropped as well. Default: `undefined`,
19290
+ * which never changes, so the stream follows `url` and `enabled` alone.
19291
+ */
19292
+ sessionKey?: SessionKey;
18938
19293
  }
18939
19294
 
18940
19295
  export declare interface UseEventStreamResult<T> {
@@ -20958,11 +21313,33 @@ export declare function useThrottle<T>(value: T, delay: number): T;
20958
21313
  * Reopening on identity would reconnect on every render for a client built
20959
21314
  * inline.
20960
21315
  *
21316
+ * The stream is bound to a session: the ticket names the channel of the user
21317
+ * who asked for it, the server never ends an open stream on logout, and a tab
21318
+ * that switches accounts without `enabled` going `false` would otherwise keep
21319
+ * delivering the previous user's events. A change of session closes that
21320
+ * stream and asks a ticket for the new one.
21321
+ *
21322
+ * With the `api` of `createTempestAuth` as `client`, that binding is automatic
21323
+ * (since 0.82.0): the session is read from the client — `auth.sessionKey()`,
21324
+ * the lineage `login()` mints — so a switch closes the stream, a token
21325
+ * rotation of the same session does not, and while nobody is signed in the
21326
+ * stream stays `idle` without asking for a ticket. Any other client keeps the
21327
+ * behaviour it had: pass `sessionKey` (the signed-in user's id). A
21328
+ * `sessionKey` passed explicitly always wins over the client's.
21329
+ *
20961
21330
  * @example
20962
21331
  * const { status } = useTicketEventStream<Notification>({
21332
+ * client: auth.api,
21333
+ * schema: notificationSchema,
21334
+ * onMessage: ({ data }) => addNotification(data),
21335
+ * });
21336
+ *
21337
+ * @example
21338
+ * // A client createTempestAuth did not build
21339
+ * const { status } = useTicketEventStream<Notification>({
20963
21340
  * client: api,
20964
21341
  * enabled: isAuthenticated,
20965
- * schema: notificationSchema,
21342
+ * sessionKey: user?.id ?? null,
20966
21343
  * onMessage: ({ data }) => addNotification(data),
20967
21344
  * });
20968
21345
  *
@@ -21184,7 +21561,7 @@ export { useWatch }
21184
21561
  * for the host component and tears it down on unmount.
21185
21562
  *
21186
21563
  * Every callback is read through a ref, so `onOpen` / `onMessage` / `onClose` /
21187
- * `onError` / `onReconnecting` / `onReconnected` / `onLost` always run the
21564
+ * `onError` / `onReconnecting` / `onReconnected` / `onLost` / `getPrincipal` always run the
21188
21565
  * latest closure — an inline arrow function is fine and never reopens the
21189
21566
  * socket. Connection-shaping options (`protocols`, `maxRetries`,
21190
21567
  * `initialBackoff`, `maxBackoff`, `jitter`, `handshakeTimeout`,
@@ -21275,6 +21652,12 @@ export declare function validateCPF(value: string): boolean;
21275
21652
  * become `"address.city"`, array entries `"items.0.name"`. The first issue
21276
21653
  * per path wins (subsequent ones are dropped to keep error UIs tidy).
21277
21654
  *
21655
+ * The map is a plain object, but every key is written as an **own** property
21656
+ * and looked up with `hasOwnProperty`: with `path in errors` and `errors[path] =`
21657
+ * a field named `constructor` or `toString` read as already failed through
21658
+ * `Object.prototype` and lost its message, and an issue a `superRefine` files
21659
+ * under `__proto__` replaced the map's prototype instead of becoming a key.
21660
+ *
21278
21661
  * @param schema - zod schema describing the form shape.
21279
21662
  * @param values - Raw form values (typed as `unknown`).
21280
21663
  * @returns Either `{ success: true, data, errors: {} }` or `{ success: false, errors }`.
@@ -22381,6 +22764,12 @@ export declare function writeXlsx(headers: readonly string[], rows: readonly (re
22381
22764
  * `ResolverOptions` is accepted and ignored, because react-hook-form always
22382
22765
  * passes the whole object.
22383
22766
  *
22767
+ * Errors are written as **own** properties and checked with `hasOwnProperty`.
22768
+ * A plain `errors[path]` read saw `Object.prototype.constructor` for a field
22769
+ * named `constructor`, so `firstError` mode kept the inherited function instead
22770
+ * of the message, and an issue a `superRefine` files under `__proto__` replaced
22771
+ * the map's prototype instead of adding the field.
22772
+ *
22384
22773
  * @example
22385
22774
  * const form = useForm<LoginForm>({ resolver: zodResolver(loginSchema) });
22386
22775
  */