tempest-react-sdk 0.64.0 → 0.66.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 (121) hide show
  1. package/README.md +4 -4
  2. package/dist/auth/create-tempest-auth.cjs +1 -1
  3. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  4. package/dist/auth/create-tempest-auth.js +31 -36
  5. package/dist/auth/create-tempest-auth.js.map +1 -1
  6. package/dist/components/Command/Command.module.cjs.map +1 -1
  7. package/dist/components/Command/Command.module.js.map +1 -1
  8. package/dist/components/ContextMenu/ContextMenu.module.cjs.map +1 -1
  9. package/dist/components/ContextMenu/ContextMenu.module.js.map +1 -1
  10. package/dist/components/DataTable/DataTable.cjs +1 -1
  11. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  12. package/dist/components/DataTable/DataTable.js +1 -0
  13. package/dist/components/DataTable/DataTable.js.map +1 -1
  14. package/dist/components/DataTable/use-dev-warnings.cjs +1 -1
  15. package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
  16. package/dist/components/DataTable/use-dev-warnings.js +4 -3
  17. package/dist/components/DataTable/use-dev-warnings.js.map +1 -1
  18. package/dist/components/DropdownMenu/DropdownMenu.module.cjs.map +1 -1
  19. package/dist/components/DropdownMenu/DropdownMenu.module.js.map +1 -1
  20. package/dist/components/InstallBanner/InstallBanner.cjs +1 -1
  21. package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
  22. package/dist/components/InstallBanner/InstallBanner.js +62 -37
  23. package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
  24. package/dist/components/InstallBanner/InstallBanner.module.cjs +1 -1
  25. package/dist/components/InstallBanner/InstallBanner.module.cjs.map +1 -1
  26. package/dist/components/InstallBanner/InstallBanner.module.js +4 -3
  27. package/dist/components/InstallBanner/InstallBanner.module.js.map +1 -1
  28. package/dist/components/InstallButton/InstallButton.cjs +1 -1
  29. package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
  30. package/dist/components/InstallButton/InstallButton.js +34 -10
  31. package/dist/components/InstallButton/InstallButton.js.map +1 -1
  32. package/dist/components/InstallButton/InstallButton.module.cjs +3 -0
  33. package/dist/components/InstallButton/InstallButton.module.cjs.map +1 -0
  34. package/dist/components/InstallButton/InstallButton.module.js +10 -0
  35. package/dist/components/InstallButton/InstallButton.module.js.map +1 -0
  36. package/dist/components/InstallButton/install-hints.cjs +2 -0
  37. package/dist/components/InstallButton/install-hints.cjs.map +1 -0
  38. package/dist/components/InstallButton/install-hints.js +32 -0
  39. package/dist/components/InstallButton/install-hints.js.map +1 -0
  40. package/dist/components/SegmentedControl/SegmentedControl.module.cjs.map +1 -1
  41. package/dist/components/SegmentedControl/SegmentedControl.module.js.map +1 -1
  42. package/dist/components/Select/Select.cjs +1 -1
  43. package/dist/components/Select/Select.cjs.map +1 -1
  44. package/dist/components/Select/Select.js +43 -40
  45. package/dist/components/Select/Select.js.map +1 -1
  46. package/dist/components/Select/Select.module.cjs +1 -1
  47. package/dist/components/Select/Select.module.cjs.map +1 -1
  48. package/dist/components/Select/Select.module.js +5 -3
  49. package/dist/components/Select/Select.module.js.map +1 -1
  50. package/dist/hooks/use-install-prompt.cjs +1 -1
  51. package/dist/hooks/use-install-prompt.cjs.map +1 -1
  52. package/dist/hooks/use-install-prompt.js +9 -2
  53. package/dist/hooks/use-install-prompt.js.map +1 -1
  54. package/dist/http/api-client.cjs +1 -1
  55. package/dist/http/api-client.cjs.map +1 -1
  56. package/dist/http/api-client.js +93 -85
  57. package/dist/http/api-client.js.map +1 -1
  58. package/dist/http/credential-scope.cjs +2 -0
  59. package/dist/http/credential-scope.cjs.map +1 -0
  60. package/dist/http/credential-scope.js +23 -0
  61. package/dist/http/credential-scope.js.map +1 -0
  62. package/dist/http/decode-response.cjs +2 -0
  63. package/dist/http/decode-response.cjs.map +1 -0
  64. package/dist/http/decode-response.js +28 -0
  65. package/dist/http/decode-response.js.map +1 -0
  66. package/dist/http/resumable-upload.cjs +1 -1
  67. package/dist/http/resumable-upload.cjs.map +1 -1
  68. package/dist/http/resumable-upload.js +121 -120
  69. package/dist/http/resumable-upload.js.map +1 -1
  70. package/dist/http/upload-with-progress.cjs +1 -1
  71. package/dist/http/upload-with-progress.cjs.map +1 -1
  72. package/dist/http/upload-with-progress.js +35 -34
  73. package/dist/http/upload-with-progress.js.map +1 -1
  74. package/dist/styles/InstallButton.css +2 -0
  75. package/dist/styles/actions.css +6 -1
  76. package/dist/styles/advanced.css +2 -2
  77. package/dist/styles/component/Command.css +1 -1
  78. package/dist/styles/component/ContextMenu.css +1 -1
  79. package/dist/styles/component/DropdownMenu.css +1 -1
  80. package/dist/styles/component/InstallBanner.css +2 -0
  81. package/dist/styles/component/InstallButton.css +4 -0
  82. package/dist/styles/component/SegmentedControl.css +1 -1
  83. package/dist/styles/component/Select.css +4 -0
  84. package/dist/styles/core.css +2 -2
  85. package/dist/styles/forms.css +4 -0
  86. package/dist/styles/manifest.json +2 -1
  87. package/dist/styles/navigation.css +1 -1
  88. package/dist/styles/tokens.css +2 -2
  89. package/dist/styles.css +1 -1
  90. package/dist/tempest-react-sdk.cjs +1 -1
  91. package/dist/tempest-react-sdk.d.ts +473 -40
  92. package/dist/tempest-react-sdk.js +321 -318
  93. package/dist/theme/ThemeProvider.cjs +1 -1
  94. package/dist/theme/ThemeProvider.cjs.map +1 -1
  95. package/dist/theme/ThemeProvider.js +40 -45
  96. package/dist/theme/ThemeProvider.js.map +1 -1
  97. package/dist/theme/create-theme.cjs +1 -1
  98. package/dist/theme/create-theme.cjs.map +1 -1
  99. package/dist/theme/create-theme.js +1 -1
  100. package/dist/theme/create-theme.js.map +1 -1
  101. package/dist/theme/initial-theme.cjs +2 -2
  102. package/dist/theme/initial-theme.cjs.map +1 -1
  103. package/dist/theme/initial-theme.js +7 -12
  104. package/dist/theme/initial-theme.js.map +1 -1
  105. package/dist/utils/absent.cjs +2 -0
  106. package/dist/utils/absent.cjs.map +1 -0
  107. package/dist/utils/absent.js +12 -0
  108. package/dist/utils/absent.js.map +1 -0
  109. package/dist/utils/format.cjs +1 -1
  110. package/dist/utils/format.cjs.map +1 -1
  111. package/dist/utils/format.js +42 -35
  112. package/dist/utils/format.js.map +1 -1
  113. package/dist/utils/numbers.cjs +1 -1
  114. package/dist/utils/numbers.cjs.map +1 -1
  115. package/dist/utils/numbers.js +14 -12
  116. package/dist/utils/numbers.js.map +1 -1
  117. package/dist/utils/storage.cjs +1 -1
  118. package/dist/utils/storage.cjs.map +1 -1
  119. package/dist/utils/storage.js +13 -0
  120. package/dist/utils/storage.js.map +1 -1
  121. package/package.json +1 -1
@@ -99,6 +99,17 @@ import { useWatch } from 'react-hook-form';
99
99
  import { WheelEventHandler } from 'react';
100
100
  import { z } from 'zod';
101
101
 
102
+ /**
103
+ * What every formatter prints when it has nothing to print.
104
+ *
105
+ * An em dash rather than an empty string, because in a table those are two
106
+ * different claims: a blank cell reads as "this column does not apply here",
107
+ * while `—` reads as "this value is missing". `formatDurationMs` in `/perf` has
108
+ * answered this way since it shipped, and `formatPercent` joined it in 0.65.0;
109
+ * this constant is what stops the third answer from appearing.
110
+ */
111
+ export declare const ABSENT_TEXT = "\u2014";
112
+
102
113
  /**
103
114
  * A pluggable access-control strategy. Implementations decide whether a given
104
115
  * action is allowed, returning a plain boolean, a {@link CanResult}, or a
@@ -597,6 +608,33 @@ export declare interface ApiClient {
597
608
  put<T>(path: string, options?: RequestOptions): Promise<T>;
598
609
  patch<T>(path: string, options?: RequestOptions): Promise<T>;
599
610
  delete<T>(path: string, options?: RequestOptions): Promise<T>;
611
+ /**
612
+ * Download a binary body through the full client pipeline.
613
+ *
614
+ * Same base URL, `getToken` header, 401 refresh-and-replay, `onUnauthorized`,
615
+ * logging, timeout and retry policy as {@link ApiClient.request} — only the
616
+ * decoding differs. Without it every download left the client: a hand-rolled
617
+ * `fetch` that re-declares the `Authorization` header and re-implements the
618
+ * error handling, and that has no refresh, which is the client's reason to
619
+ * exist.
620
+ *
621
+ * @param path - Path joined onto the client's base URL.
622
+ * @param options - The same options `request` takes; `method` defaults to GET.
623
+ * @returns The response body as a `Blob`.
624
+ * @throws TempestApiError On any non-2xx response, carrying the status.
625
+ */
626
+ blob(path: string, options?: RequestOptions): Promise<Blob>;
627
+ /**
628
+ * Download a binary body as an `ArrayBuffer`, for the callers a `Blob` does
629
+ * not serve — a decoder, a hash, a typed array fed to WebGL or to
630
+ * `onnxruntime-web`.
631
+ *
632
+ * @param path - Path joined onto the client's base URL.
633
+ * @param options - The same options `request` takes; `method` defaults to GET.
634
+ * @returns The response body as an `ArrayBuffer`.
635
+ * @throws TempestApiError On any non-2xx response, carrying the status.
636
+ */
637
+ arrayBuffer(path: string, options?: RequestOptions): Promise<ArrayBuffer>;
600
638
  upload<T>(path: string, formData: FormData, method?: "POST" | "PUT" | "PATCH", options?: Omit<RequestOptions, "body" | "method">): Promise<T>;
601
639
  }
602
640
 
@@ -625,6 +663,25 @@ export declare interface ApiClientConfig {
625
663
  prefix?: string;
626
664
  /** Returns the current bearer token (or null/undefined). Called per request. */
627
665
  getToken?: () => string | null | undefined;
666
+ /**
667
+ * Origins besides `baseURL`'s that may receive the `getToken` credential.
668
+ *
669
+ * Since 0.66.0 the bearer token is scoped to the origin of `baseURL`. A
670
+ * request to any other origin still goes out — it just goes without the
671
+ * header. That matters because a path may be an absolute URL, in which case
672
+ * it overrides `baseURL` completely, so the destination of a credentialed
673
+ * request could be decided by a value the app read off the network: a
674
+ * pagination link, a `download_url`, a `Location`.
675
+ *
676
+ * List the second host here when it genuinely needs the API's token — a CDN
677
+ * behind the same identity, a sibling service. An origin compares as an
678
+ * origin (`"https://cdn.acme.com"`), so path and trailing slash are
679
+ * ignored.
680
+ *
681
+ * A relative `baseURL` needs nothing here: a relative target resolves
682
+ * against the document and cannot cross an origin.
683
+ */
684
+ trustedOrigins?: readonly string[];
628
685
  /**
629
686
  * Per-request correlation id sent as the `X-Request-ID` header, matching the
630
687
  * Tempest FastAPI SDK `RequestIDMiddleware`. Defaults to a generated id.
@@ -3233,8 +3290,23 @@ export declare const CPFInput: ForwardRefExoticComponent<Omit<InputProps, "value
3233
3290
  * is the signal to clear the session. Without `refresh`, the first `401` calls
3234
3291
  * it directly.
3235
3292
  *
3293
+ * **Binary bodies** have their own methods — `blob()` and `arrayBuffer()` — which
3294
+ * run the same pipeline and skip the parse. `request()` no longer decodes an
3295
+ * unknown `Content-Type` as text either: it returns a `Blob` and says so once in
3296
+ * a development build, because reading `image/jpeg` as UTF-8 destroyed the bytes
3297
+ * on the way in.
3298
+ *
3299
+ * **The bearer token is scoped to `baseURL`'s origin** since 0.66.0. A path may
3300
+ * be an absolute URL, which overrides the base entirely, so without a scope the
3301
+ * destination of a credentialed request could come from a value read off the
3302
+ * network. A request to another origin still goes out; it goes without the
3303
+ * header, and a development build says so once. Declare the exceptions in
3304
+ * {@link ApiClientConfig.trustedOrigins}.
3305
+ *
3236
3306
  * **Retries** are off unless you set `retry`. See {@link ApiClientConfig.retry}
3237
- * for the built-in policy; it never replays a write.
3307
+ * for the built-in policy; it never replays a write. A single call overrides it
3308
+ * with {@link RequestOptions.retry}, and opts out of auth entirely with
3309
+ * {@link RequestOptions.skipAuth}.
3238
3310
  *
3239
3311
  * **Logging** is off unless you pass a `logger`. With one, every finished attempt
3240
3312
  * writes a line — `debug` under 400, `warn` from 400 up, plus a `warn` when
@@ -3253,7 +3325,8 @@ export declare const CPFInput: ForwardRefExoticComponent<Omit<InputProps, "value
3253
3325
  * });
3254
3326
  *
3255
3327
  * @param config - Base URL plus the optional auth, retry and fetch hooks.
3256
- * @returns A client with `request`/`get`/`post`/`put`/`patch`/`delete`/`upload`.
3328
+ * @returns A client with `request`/`get`/`post`/`put`/`patch`/`delete`/`blob`/
3329
+ * `arrayBuffer`/`upload`.
3257
3330
  */
3258
3331
  export declare function createApiClient(config: ApiClientConfig): ApiClient;
3259
3332
 
@@ -3595,7 +3668,7 @@ export declare function createInMemoryFlags(options?: InMemoryFlagsOptions): Fea
3595
3668
  * const packed = createJsonStorage(compressedStorageCodec);
3596
3669
  *
3597
3670
  * @param codec - Encoder pair. Defaults to plain JSON.
3598
- * @returns A store with `get` / `set` / `remove`.
3671
+ * @returns A store with `get` / `set` / `getRaw` / `setRaw` / `remove`.
3599
3672
  *
3600
3673
  * @tempest-limits empty-catch — every method is best-effort by contract:
3601
3674
  * `localStorage` throws on quota exhaustion, in Safari private mode, and when a
@@ -5390,6 +5463,23 @@ export declare const DEFAULT_PUSH_TO_TALK_KEY = "Space";
5390
5463
  */
5391
5464
  export declare const DEFAULT_VOICE_CHAIN: VoiceChainSettings;
5392
5465
 
5466
+ /**
5467
+ * The default instruction for a browser that cannot be prompted.
5468
+ *
5469
+ * Written out rather than left to the app because every app wrote the same two
5470
+ * sentences, and getting them wrong is invisible: an iOS user who is told to
5471
+ * look for an "Install" menu never finds one, because Safari puts it behind the
5472
+ * Share sheet.
5473
+ *
5474
+ * On an Android fork the copy also offers the `intent://` link the SDK already
5475
+ * builds — reopening the page in Chrome is the only path that reaches a real
5476
+ * install on a browser whose menu has no install entry at all.
5477
+ *
5478
+ * @param input - The resolved method and the optional Chrome intent.
5479
+ * @returns The instruction, or `null` when the method needs none.
5480
+ */
5481
+ export declare function defaultInstallHint({ method, openInChromeIntent }: InstallHintInput): ReactNode;
5482
+
5393
5483
  /**
5394
5484
  * Identity helper that types a declarative route tree. Use it so editors give
5395
5485
  * autocomplete and type-checking on every node; the array is returned as-is.
@@ -6392,13 +6482,19 @@ export declare interface FormActionsProps extends HTMLAttributes<HTMLDivElement>
6392
6482
  * controls the fractional digits for scaled units (default 1); `0` bytes
6393
6483
  * always renders as `"0 B"`.
6394
6484
  *
6485
+ * An absent or unrepresentable count renders as `"—"`. It used to render as
6486
+ * **`"NaN undefined"`**: `Math.log(NaN)` walks the exponent off the end of the
6487
+ * unit table, so the unit itself came out `undefined` — the size was not merely
6488
+ * unreadable, it claimed a unit that does not exist.
6489
+ *
6395
6490
  * @example
6396
6491
  * formatBytes(0); // "0 B"
6397
6492
  * formatBytes(1536); // "1.5 KB"
6398
6493
  * formatBytes(1048576); // "1 MB"
6399
6494
  * formatBytes(1536, 2); // "1.50 KB"
6495
+ * formatBytes(null); // "—"
6400
6496
  */
6401
- export declare function formatBytes(bytes: number, decimals?: number): string;
6497
+ export declare function formatBytes(bytes: number | null | undefined, decimals?: number, options?: FormatFallbackOptions): string;
6402
6498
 
6403
6499
  /** Format an 11-digit Brazilian CEP-like value as `00000-000`. */
6404
6500
  export declare function formatCEP(value: string): string;
@@ -6412,12 +6508,17 @@ export declare function formatCNPJ(value: string): string;
6412
6508
  * Wraps `Intl.NumberFormat` with `notation: "compact"`. The `locale` defaults
6413
6509
  * to `"en-US"`.
6414
6510
  *
6511
+ * An absent or unrepresentable value renders as `"—"`. The `null` case is the
6512
+ * one that matters: `Intl` coerces it to zero, so a count the backend left out
6513
+ * used to read as **`"0"`** — a number the reader has no reason to doubt.
6514
+ *
6415
6515
  * @example
6416
6516
  * formatCompactNumber(1234); // "1.2K"
6417
6517
  * formatCompactNumber(5600000); // "5.6M"
6418
6518
  * formatCompactNumber(1234, "pt-BR"); // "1,2 mil"
6519
+ * formatCompactNumber(null); // "—"
6419
6520
  */
6420
- export declare function formatCompactNumber(value: number, locale?: string): string;
6521
+ export declare function formatCompactNumber(value: number | null | undefined, locale?: string, options?: FormatFallbackOptions): string;
6421
6522
 
6422
6523
  /**
6423
6524
  * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.
@@ -6430,18 +6531,50 @@ export declare function formatCPF(value: string): string;
6430
6531
  /**
6431
6532
  * Format a number as Brazilian Real currency.
6432
6533
  *
6433
- * @param value - The amount in BRL.
6534
+ * An absent or unrepresentable amount renders as `"—"` rather than as a number.
6535
+ * That branch is a data-correctness fix, not cosmetics: `Intl` coerces `null` to
6536
+ * zero, so an amount the backend left out used to reach the screen as
6537
+ * **`"R$ 0,00"`** — a plausible figure a reader has no way to question — and
6538
+ * `undefined` as `"R$ NaN"`.
6539
+ *
6540
+ * @example
6541
+ * ```typescript
6542
+ * formatCurrency(1234.56); // "R$ 1.234,56"
6543
+ * formatCurrency(null); // "—"
6544
+ * formatCurrency(null, { fallback: "sem valor" }); // "sem valor"
6545
+ * ```
6546
+ *
6547
+ * @param value - The amount in BRL, or `null`/`undefined` when there is none.
6548
+ * @param options - Text to render when there is nothing to format.
6434
6549
  * @returns A locale-formatted string, e.g. "R$ 1.234,56".
6435
6550
  */
6436
- export declare function formatCurrency(value: number): string;
6551
+ export declare function formatCurrency(value: number | null | undefined, options?: FormatFallbackOptions): string;
6437
6552
 
6438
6553
  /**
6439
6554
  * Format an ISO date or Date instance as `dd/MM/yyyy`.
6440
6555
  *
6441
- * @param value - ISO string or Date.
6442
- * @returns Formatted date string, or empty string when input is invalid.
6556
+ * Absent input answers `"—"`. It used to **throw**: `new Date(null)` is epoch and
6557
+ * `null.getTime` is a `TypeError`, so a nullable column — which is every
6558
+ * `deleted_at`, `expires_at` and half the `updated_at` a FastAPI backend sends —
6559
+ * took the screen down instead of rendering a gap.
6560
+ *
6561
+ * A value that is present and unparseable still answers `""`, deliberately: on a
6562
+ * screen that reads a log, "nobody filled this in" and "what they filled in is
6563
+ * broken" are different findings, and the second is the one worth chasing.
6564
+ *
6565
+ * @example
6566
+ * ```typescript
6567
+ * formatDate("2026-05-16T12:00:00Z"); // "16/05/2026"
6568
+ * formatDate(null); // "—"
6569
+ * formatDate("banana"); // ""
6570
+ * ```
6571
+ *
6572
+ * @param value - ISO string, Date, or `null`/`undefined`.
6573
+ * @param options - Text to render when the value is absent.
6574
+ * @returns Formatted date string, the fallback when absent, or empty string when
6575
+ * the value is present and invalid.
6443
6576
  */
6444
- export declare function formatDate(value: string | Date): string;
6577
+ export declare function formatDate(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6445
6578
 
6446
6579
  /**
6447
6580
  * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an
@@ -6461,19 +6594,29 @@ export declare function formatDate(value: string | Date): string;
6461
6594
  * @example
6462
6595
  * <input type="date" defaultValue={formatDateForInput(order.createdAt)} />
6463
6596
  *
6464
- * @param value - ISO string or Date.
6597
+ * Absent input answers `""` here — **not** the em dash the reading formatters
6598
+ * use — because that is what an input reads as "no value"; an em dash would
6599
+ * arrive as content the user has to delete before typing a date.
6600
+ *
6601
+ * @param value - ISO string, Date, or `null`/`undefined`.
6602
+ * @param options - Text for an absent value; defaults to `""`.
6465
6603
  * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —
6466
6604
  * which is what a date input reads as "no value", unlike `"Invalid Date"`.
6467
6605
  */
6468
- export declare function formatDateForInput(value: string | Date): string;
6606
+ export declare function formatDateForInput(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6469
6607
 
6470
6608
  /**
6471
6609
  * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.
6472
6610
  *
6473
- * @param value - ISO string or Date.
6474
- * @returns Formatted datetime string, or empty string when input is invalid.
6611
+ * Same contract as {@link formatDate}: absent answers the fallback (`"—"` by
6612
+ * default) instead of throwing, and present-but-invalid answers `""`.
6613
+ *
6614
+ * @param value - ISO string, Date, or `null`/`undefined`.
6615
+ * @param options - Text to render when the value is absent.
6616
+ * @returns Formatted datetime string, the fallback when absent, or empty string
6617
+ * when the value is present and invalid.
6475
6618
  */
6476
- export declare function formatDateTime(value: string | Date): string;
6619
+ export declare function formatDateTime(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6477
6620
 
6478
6621
  /**
6479
6622
  * Format an ISO date or Date instance as `yyyy-MM-ddTHH:mm`, the value an
@@ -6505,7 +6648,7 @@ export declare function formatDateTime(value: string | Date): string;
6505
6648
  * invalid — which is what a datetime input reads as "no value", unlike
6506
6649
  * `"Invalid Date"`.
6507
6650
  */
6508
- export declare function formatDateTimeForInput(value: string | Date): string;
6651
+ export declare function formatDateTimeForInput(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6509
6652
 
6510
6653
  /**
6511
6654
  * Rendering helpers for the numbers a profiler produces.
@@ -6532,13 +6675,51 @@ export declare function formatDateTimeForInput(value: string | Date): string;
6532
6675
  */
6533
6676
  export declare function formatDurationMs(value: number): string;
6534
6677
 
6678
+ /** The text a formatter falls back to when the value cannot be rendered. */
6679
+ export declare interface FormatFallbackOptions {
6680
+ /**
6681
+ * Text for a value that is absent (`null`/`undefined`) or unrepresentable
6682
+ * (`NaN`, `Infinity`). Defaults to {@link ABSENT_TEXT}.
6683
+ *
6684
+ * The formatters that feed an `<input>` default to `""` instead, because an
6685
+ * input reads an empty string as "no value" and would render an em dash as
6686
+ * content the user has to delete.
6687
+ */
6688
+ fallback?: string;
6689
+ }
6690
+
6535
6691
  /**
6536
- * Format a fraction (0-1) as a percentage with one decimal.
6692
+ * Format a fraction (0-1) as a percentage.
6693
+ *
6694
+ * A non-finite input renders as `"—"` rather than `"NaN%"` or `"∞%"`, which is
6695
+ * what `Intl.NumberFormat` produces and what a division by zero upstream puts on
6696
+ * the screen. It matches `formatDurationMs`, which already answers that way.
6697
+ *
6698
+ * @example
6699
+ * ```typescript
6700
+ * formatPercent(0.9874); // "98,7%"
6701
+ * formatPercent(0.9874, { decimals: 2 }); // "98,74%"
6702
+ * formatPercent(0 / 0); // "—"
6703
+ * ```
6537
6704
  *
6538
6705
  * @param value - Fraction between 0 and 1.
6539
- * @returns Formatted percent string, e.g. "12,5%".
6706
+ * @param options - Rendering options; `decimals` defaults to 1.
6707
+ * @returns Formatted percent string, e.g. "12,5%", or `"—"` for a non-finite input.
6540
6708
  */
6541
- export declare function formatPercent(value: number): string;
6709
+ export declare function formatPercent(value: number | null | undefined, options?: FormatPercentOptions): string;
6710
+
6711
+ /** How {@link formatPercent} renders a fraction. */
6712
+ export declare interface FormatPercentOptions extends FormatFallbackOptions {
6713
+ /**
6714
+ * Decimal places to show, fixed (padded as well as truncated).
6715
+ *
6716
+ * Defaults to `1`, which is what the function always did. Two is the case
6717
+ * that produced this option: a confidence of 98,74% and one of 98,7% are the
6718
+ * same number on a summary card and two different readings on a detail
6719
+ * screen, so the precision belongs to the call site.
6720
+ */
6721
+ decimals?: number;
6722
+ }
6542
6723
 
6543
6724
  /**
6544
6725
  * Apply the Brazilian phone mask `(XX) XXXXX-XXXX` or `(XX) XXXX-XXXX`.
@@ -7277,11 +7458,15 @@ export declare interface InstallBackgroundSyncOptions {
7277
7458
  }
7278
7459
 
7279
7460
  /**
7280
- * Dismissible bottom banner that invites the user to install the PWA. Wired to
7281
- * {@link useBeforeInstallPrompt}: it renders only when the browser captured an
7282
- * install prompt and the app is not already running standalone — so on
7283
- * platforms that never fire `beforeinstallprompt` (e.g. iOS Safari) it stays
7284
- * hidden and you can surface manual instructions elsewhere.
7461
+ * Dismissible bottom banner that invites the user to install the PWA, wired to
7462
+ * {@link useInstallPrompt}.
7463
+ *
7464
+ * Where the browser fires `beforeinstallprompt`, the button installs. Where it
7465
+ * does not — iOS Safari, and the Android Chromium forks that strip the API — the
7466
+ * banner shows that platform's instruction instead of disappearing. It used to
7467
+ * disappear: built on `useBeforeInstallPrompt`, it rendered `null` exactly where
7468
+ * the user has no other way to find the install entry, which is why every app
7469
+ * kept a local copy of this component just to add the two missing paths.
7285
7470
  *
7286
7471
  * @tempest-limits empty-catch — persisting the dismissal is a courtesy, not the
7287
7472
  * feature. When `localStorage` refuses the write (quota, private mode) the banner
@@ -7295,7 +7480,7 @@ export declare interface InstallBackgroundSyncOptions {
7295
7480
  * storageKey="famacha:install-dismissed"
7296
7481
  * />
7297
7482
  */
7298
- export declare function InstallBanner({ title, description, installLabel, dismissLabel, icon, storageKey, onResult, className, }: InstallBannerProps): JSX.Element | null;
7483
+ export declare function InstallBanner({ title, description, installLabel, hintLabel, dismissLabel, icon, storageKey, declineCooldownMs, renderHint, manualFallbackDelayMs, onResult, className, }: InstallBannerProps): JSX.Element | null;
7299
7484
 
7300
7485
  export declare interface InstallBannerProps {
7301
7486
  /** Headline. Default `"Instale o app"`. */
@@ -7313,29 +7498,95 @@ export declare interface InstallBannerProps {
7313
7498
  * make dismissal last only for the current session (component state).
7314
7499
  */
7315
7500
  storageKey?: string;
7501
+ /**
7502
+ * How long, in ms, a dismissal lasts before the banner may return.
7503
+ *
7504
+ * Omitted, a dismissal written to `storageKey` is permanent, which is what
7505
+ * the component always did and what a stored `"1"` keeps meaning. With a
7506
+ * cooldown the dismissal is stored as a timestamp instead, so the banner
7507
+ * comes back after the window — the same shape `useInstallPrompt` uses for
7508
+ * its own decline.
7509
+ */
7510
+ declineCooldownMs?: number;
7511
+ /**
7512
+ * Install button label used when the browser cannot be prompted and the
7513
+ * banner shows the manual instruction. Default `"Como instalar"`.
7514
+ */
7515
+ hintLabel?: string;
7516
+ /**
7517
+ * Replaces the instruction shown for the `"ios"` and `"manual"` methods.
7518
+ *
7519
+ * The default copy is in PT-BR and names the real menu entries.
7520
+ */
7521
+ renderHint?: RenderInstallHint;
7522
+ /**
7523
+ * How long, in ms, to wait for `beforeinstallprompt` before falling back to
7524
+ * the manual instruction. Forwarded to {@link useInstallPrompt}; defaults to
7525
+ * 3000 there.
7526
+ */
7527
+ manualFallbackDelayMs?: number;
7316
7528
  /** Called with the user's choice after the install prompt resolves. */
7317
7529
  onResult?: (outcome: InstallOutcome) => void;
7318
7530
  className?: string;
7319
7531
  }
7320
7532
 
7321
7533
  /**
7322
- * Button wired to the PWA install prompt ({@link useBeforeInstallPrompt}).
7323
- * Renders nothing when the app can't be installed — no prompt captured yet,
7324
- * already installed, or running standalone — so you can drop it anywhere
7325
- * without guarding visibility yourself.
7534
+ * Button wired to PWA installation, through {@link useInstallPrompt}.
7535
+ *
7536
+ * Where the browser fires `beforeinstallprompt`, the click installs. Where it
7537
+ * does not — iOS Safari, and the Android Chromium forks (Mi Browser, UC, Opera
7538
+ * Mini, Huawei) that strip the API — the click reveals the instruction for that
7539
+ * platform instead of the button disappearing.
7540
+ *
7541
+ * That disappearance was the defect: the component was built on
7542
+ * `useBeforeInstallPrompt`, so it rendered `null` exactly where the user needs
7543
+ * the most help, and every app re-implemented the iOS and manual paths locally.
7544
+ * Nothing renders only when there is genuinely nothing to offer — already
7545
+ * installed, running standalone, or inside the decline cooldown.
7326
7546
  *
7327
7547
  * Inherits every {@link Button} prop (`variant`, `size`, `leftIcon`, …).
7328
7548
  *
7329
7549
  * @example
7330
7550
  * <InstallButton variant="primary" leftIcon={<Download />} />
7551
+ *
7552
+ * @example
7553
+ * <InstallButton renderHint={({ method }) => (method === "ios" ? <IOSSteps /> : <MenuSteps />)} />
7331
7554
  */
7332
- export declare function InstallButton({ label, onResult, ...props }: InstallButtonProps): JSX.Element | null;
7555
+ export declare function InstallButton({ label, hintLabel, onResult, renderHint, manualFallbackDelayMs, wrapperClassName, ...props }: InstallButtonProps): JSX.Element | null;
7333
7556
 
7334
7557
  export declare interface InstallButtonProps extends Omit<ButtonProps, "onClick" | "children"> {
7335
7558
  /** Button label. Default `"Instalar app"`. */
7336
7559
  label?: ReactNode;
7560
+ /**
7561
+ * Label used when the browser cannot be prompted and the button reveals the
7562
+ * manual instruction instead. Default `"Como instalar"`.
7563
+ */
7564
+ hintLabel?: ReactNode;
7337
7565
  /** Called with the user's choice after the install prompt resolves. */
7338
7566
  onResult?: (outcome: InstallOutcome) => void;
7567
+ /**
7568
+ * Replaces the instruction shown for the `"ios"` and `"manual"` methods.
7569
+ *
7570
+ * The default copy is in PT-BR and names the real menu entries. Pass this to
7571
+ * translate it, to shorten it, or to point at a screenshot of your own.
7572
+ */
7573
+ renderHint?: RenderInstallHint;
7574
+ /**
7575
+ * How long, in ms, to wait for `beforeinstallprompt` before falling back to
7576
+ * the manual instruction. Forwarded to {@link useInstallPrompt}; defaults to
7577
+ * 3000 there.
7578
+ */
7579
+ manualFallbackDelayMs?: number;
7580
+ /** Class applied to the wrapper that holds the button and the instruction. */
7581
+ wrapperClassName?: string;
7582
+ }
7583
+
7584
+ /** What an install affordance needs to render the instruction for a method. */
7585
+ export declare interface InstallHintInput {
7586
+ /** The strategy `useInstallPrompt` resolved. */
7587
+ method: InstallMethod;
7588
+ /** An `intent://` URL that re-opens the page in Chrome on Android, if any. */
7589
+ openInChromeIntent: string | null;
7339
7590
  }
7340
7591
 
7341
7592
  /**
@@ -7720,6 +7971,42 @@ declare type IssuePathSegment = PropertyKey | {
7720
7971
  readonly key: PropertyKey;
7721
7972
  };
7722
7973
 
7974
+ /**
7975
+ * Whether a request to `target` may carry the caller's credential.
7976
+ *
7977
+ * The SDK writes `Authorization: Bearer` in three places, and until 0.66.0 none
7978
+ * of them looked at where the request was going. That is only safe while every
7979
+ * target URL is written by the app: a resumable upload takes its URL from the
7980
+ * server's `Location` header, and `createApiClient` lets an absolute path
7981
+ * override `baseURL` entirely, so a URL arriving over the network could decide
7982
+ * which origin received the token — and the file bytes with it.
7983
+ *
7984
+ * Handing an upload off to object storage on another host is ordinary `tus`
7985
+ * deployment, not an attack, which is exactly why the credential must be scoped
7986
+ * rather than the request blocked: the upload still goes through, it just goes
7987
+ * without the API's bearer token.
7988
+ *
7989
+ * Both sides are resolved against the document first, so a relative `baseURL`
7990
+ * or `endpoint` — the shape behind a dev-server or reverse proxy — compares
7991
+ * equal to the absolute URL the request actually goes to. Outside a browsing
7992
+ * context a relative target resolves to nothing and is allowed: there is no
7993
+ * origin for it to cross.
7994
+ *
7995
+ * @param target - The URL the request is about to go to.
7996
+ * @param reference - The origin the credential belongs to, normally the
7997
+ * client's `baseURL` or the upload `endpoint`.
7998
+ * @param trustedOrigins - Extra origins the app declared, for the legitimate
7999
+ * second host: a CDN, a signed upload endpoint.
8000
+ * @returns Whether the credential may be attached.
8001
+ *
8002
+ * @example
8003
+ * isTrustedCredentialTarget("https://api.acme.com/me", "https://api.acme.com");
8004
+ * // true
8005
+ * isTrustedCredentialTarget("https://cdn.other/u/1", "https://api.acme.com");
8006
+ * // false
8007
+ */
8008
+ export declare function isTrustedCredentialTarget(target: string, reference: string, trustedOrigins?: readonly string[]): boolean;
8009
+
7723
8010
  /** True when `value` is a finite latitude in `[-90, 90]`. */
7724
8011
  export declare function isValidLatitude(value: number): boolean;
7725
8012
 
@@ -7748,6 +8035,10 @@ export declare interface JsonStorage {
7748
8035
  /**
7749
8036
  * Read and decode a key.
7750
8037
  *
8038
+ * The value is decoded by the store's codec — JSON for {@link storage} —
8039
+ * which is what makes a key written as a bare string unreadable here: it is
8040
+ * not the shape this method writes. {@link JsonStorage.getRaw} reads those.
8041
+ *
7751
8042
  * @typeParam T - The expected value shape.
7752
8043
  * @param key - Storage key.
7753
8044
  * @param fallback - Returned when the key is absent, unreadable, or corrupt.
@@ -7757,11 +8048,42 @@ export declare interface JsonStorage {
7757
8048
  /**
7758
8049
  * Encode and write a key.
7759
8050
  *
8051
+ * The value is **encoded** — `set("theme", "dark")` stores `"dark"` with the
8052
+ * quotes, not `dark`. That matters when an existing key is being adopted:
8053
+ * whatever read it before will no longer recognise what this writes, and the
8054
+ * old value will no longer parse here. {@link JsonStorage.setRaw} is the
8055
+ * passthrough.
8056
+ *
7760
8057
  * @typeParam T - The value being stored.
7761
8058
  * @param key - Storage key.
7762
8059
  * @param value - Any value the codec can represent.
7763
8060
  */
7764
8061
  set<T>(key: string, value: T): void;
8062
+ /**
8063
+ * Read a key exactly as `localStorage` holds it, with no decoding.
8064
+ *
8065
+ * The escape hatch {@link JsonStorage.get} cannot be: `get` runs the codec,
8066
+ * so a key holding a bare `dark` — written by another library, by the app
8067
+ * before it adopted this wrapper, or by the SDK's own `ThemeProvider` —
8068
+ * fails to parse and comes back as the fallback. Reading that key is a
8069
+ * different operation, not a variant of the same one.
8070
+ *
8071
+ * @param key - Storage key.
8072
+ * @returns The stored string, or `null` when the key is absent or storage is
8073
+ * unavailable.
8074
+ */
8075
+ getRaw(key: string): string | null;
8076
+ /**
8077
+ * Write a string exactly as given, with no encoding.
8078
+ *
8079
+ * The counterpart of {@link JsonStorage.getRaw}, and what keeps adoption from
8080
+ * being a silent migration: `set("theme", "dark")` stores `"dark"` **with the
8081
+ * quotes**, which the app's previous reader no longer recognises.
8082
+ *
8083
+ * @param key - Storage key.
8084
+ * @param value - The exact string to store.
8085
+ */
8086
+ setRaw(key: string, value: string): void;
7765
8087
  /**
7766
8088
  * Delete a key.
7767
8089
  *
@@ -11469,6 +11791,9 @@ export declare interface RelativeTimeProps extends HTMLAttributes<HTMLTimeElemen
11469
11791
  */
11470
11792
  export declare function removeById<T, V extends Partial<Record<keyof T, unknown>> = T>(idField?: keyof T): (current: T[] | undefined, item: V) => T[];
11471
11793
 
11794
+ /** Renders the instruction shown when the browser has no install prompt. */
11795
+ export declare type RenderInstallHint = (input: InstallHintInput) => ReactNode;
11796
+
11472
11797
  export declare interface ReorderPayload {
11473
11798
  from: number;
11474
11799
  to: number;
@@ -11486,6 +11811,38 @@ export declare interface RequestOptions extends Omit<RequestInit, "body"> {
11486
11811
  * a `queryFn` and the request is cancelled on unmount and on refetch.
11487
11812
  */
11488
11813
  signal?: AbortSignal | null;
11814
+ /**
11815
+ * Send this request with no `Authorization` header, and never enter the
11816
+ * refresh-and-replay cycle on a `401`.
11817
+ *
11818
+ * This is what login and refresh need: a route that must not carry the
11819
+ * token it is about to obtain, and whose own `401` must surface rather than
11820
+ * call `refresh()` again — which would recurse. Before 0.66.0 the only way
11821
+ * to get it was a second client, which is why `createTempestAuth` used to
11822
+ * build three.
11823
+ */
11824
+ skipAuth?: boolean;
11825
+ /**
11826
+ * Send the `Authorization` header, but let a `401` surface instead of
11827
+ * refreshing and replaying.
11828
+ *
11829
+ * The narrower half of {@link skipAuth}, for a call that is authenticated
11830
+ * but whose failure the caller wants to see — a token probe, a background
11831
+ * poll that should not fight for the refresh lock.
11832
+ */
11833
+ skipAuthRetry?: boolean;
11834
+ /**
11835
+ * Retry policy for this request, overriding the client's.
11836
+ *
11837
+ * Replaces `config.retry` entirely rather than merging into it, the same
11838
+ * way `config.retry` is read today: a merged `shouldRetry` would silently
11839
+ * combine two policies that were each written to be complete.
11840
+ *
11841
+ * `false` turns retrying off for one call on a client that has it on;
11842
+ * `true` or {@link RetryOptions} turns it on for one call on a client that
11843
+ * does not.
11844
+ */
11845
+ retry?: boolean | RetryOptions;
11489
11846
  /**
11490
11847
  * Override the client's timeout for this request, in milliseconds. `null`
11491
11848
  * disables it, which is the escape hatch for a stream or a long poll.
@@ -11641,6 +11998,18 @@ export declare interface ResumableUploadOptions {
11641
11998
  headers?: Record<string, string>;
11642
11999
  /** Returns the current bearer token, read before each request. */
11643
12000
  getToken?: () => string | null | undefined;
12001
+ /**
12002
+ * Origins besides `endpoint`'s that may receive the `getToken` credential.
12003
+ *
12004
+ * Since 0.66.0 the bearer token is scoped to the origin of `endpoint`, and
12005
+ * the creation response's `Location` no longer decides where it goes. A
12006
+ * server that hands the upload to object storage on another host still
12007
+ * works — the `PATCH` requests simply carry no `Authorization`, which is
12008
+ * what a presigned storage URL expects anyway.
12009
+ *
12010
+ * List the storage origin here when it does need the API's token.
12011
+ */
12012
+ trustedOrigins?: readonly string[];
11644
12013
  /** Send cookies. Default `false`. */
11645
12014
  withCredentials?: boolean;
11646
12015
  /**
@@ -12163,26 +12532,63 @@ export declare interface SegmentedControlProps<TValue extends string = string> e
12163
12532
  }
12164
12533
 
12165
12534
  /**
12166
- * Native `<select>` wrapper with label/helper/error slots. Either provide
12167
- * `options` for a quick render, or pass `<option>` children directly.
12535
+ * Native `<select>` wrapper. Either provide `options` for a quick render, or
12536
+ * pass `<option>` children directly.
12537
+ *
12538
+ * Two variants:
12539
+ *
12540
+ * - `"field"` (default) — label, control, helper text and error slot, for a form.
12541
+ * - `"chip"` — the control alone, sized to its content, for a settings row where
12542
+ * the icon and the label are already on the left and the control belongs on the
12543
+ * right. Rendering the full field there stacks two labelled fields inside one
12544
+ * list item, which is why every app ended up hand-rolling a native `<select>`
12545
+ * with `appearance: none` — and hand-rolling it loses the focus ring, the
12546
+ * disabled styling and the caret this component already solves.
12547
+ *
12548
+ * @example
12549
+ * <Select label="Estado" options={states} placeholder="Selecione" />
12550
+ *
12551
+ * @example
12552
+ * <ListTile title="Idioma" trailing={<Select variant="chip" aria-label="Idioma" options={langs} />} />
12168
12553
  */
12169
12554
  export declare const Select: ForwardRefExoticComponent<SelectProps & RefAttributes<HTMLSelectElement>>;
12170
12555
 
12171
- export declare interface SelectOption {
12172
- value: string | number;
12173
- label: string;
12174
- disabled?: boolean;
12556
+ /** What both variants of {@link Select} accept. */
12557
+ declare interface SelectBaseProps extends SelectHTMLAttributes<HTMLSelectElement> {
12558
+ options?: SelectOption[];
12559
+ placeholder?: string;
12560
+ wrapperClassName?: string;
12561
+ }
12562
+
12563
+ /**
12564
+ * The bare control, sized to its content.
12565
+ *
12566
+ * `aria-label` is required rather than optional, because the variant removes the
12567
+ * only visible label the component had. A settings row names the control in the
12568
+ * row's own text, which the `<select>` has no relationship to — so without this
12569
+ * the screen reader announces a combobox with no name at all.
12570
+ */
12571
+ declare interface SelectChipProps extends SelectBaseProps {
12572
+ variant: "chip";
12573
+ "aria-label": string;
12175
12574
  }
12176
12575
 
12177
- export declare interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
12576
+ /** The full form field: label, control, helper text and error slot. */
12577
+ declare interface SelectFieldProps extends SelectBaseProps {
12578
+ variant?: "field";
12178
12579
  label?: string;
12179
12580
  helperText?: string;
12180
12581
  error?: string;
12181
- options?: SelectOption[];
12182
- placeholder?: string;
12183
- wrapperClassName?: string;
12184
12582
  }
12185
12583
 
12584
+ export declare interface SelectOption {
12585
+ value: string | number;
12586
+ label: string;
12587
+ disabled?: boolean;
12588
+ }
12589
+
12590
+ export declare type SelectProps = SelectFieldProps | SelectChipProps;
12591
+
12186
12592
  /**
12187
12593
  * Minimal subset of `@sentry/browser` used by the adapter. Lets you pass
12188
12594
  * either the real SDK module (`import * as Sentry from "@sentry/browser"`)
@@ -13098,6 +13504,11 @@ export declare function stopAudio(): void;
13098
13504
  /**
13099
13505
  * Typed wrapper around `localStorage` that JSON-encodes values and silently
13100
13506
  * handles environments where storage is unavailable (SSR, private mode).
13507
+ *
13508
+ * `get`/`set` encode; `getRaw`/`setRaw` do not. Reach for the raw pair when the
13509
+ * key is shared with something that does not speak JSON — an existing key in an
13510
+ * app adopting this wrapper, a key another library owns, or the theme
13511
+ * preference the SDK's own `ThemeProvider` stores as a bare string.
13101
13512
  */
13102
13513
  export declare const storage: JsonStorage;
13103
13514
 
@@ -14391,6 +14802,22 @@ export declare interface UploadWithProgressOptions {
14391
14802
  headers?: Record<string, string>;
14392
14803
  /** Returns the current bearer token. */
14393
14804
  getToken?: () => string | null | undefined;
14805
+ /**
14806
+ * Origin the `getToken` credential belongs to, normally the API's.
14807
+ *
14808
+ * Unlike `createApiClient`, this helper has no base URL to infer a scope
14809
+ * from — the caller names the destination on every call. Set this when the
14810
+ * `url` can come from somewhere other than your own code (a signed URL the
14811
+ * API handed back, a value from a response body) and the token must not
14812
+ * follow it off-origin. Left unset, the header goes wherever `url` points,
14813
+ * which is the behaviour every version before 0.66.0 had.
14814
+ */
14815
+ credentialOrigin?: string;
14816
+ /**
14817
+ * Extra origins allowed to receive the credential, on top of
14818
+ * {@link credentialOrigin}. Ignored when that one is unset.
14819
+ */
14820
+ trustedOrigins?: readonly string[];
14394
14821
  /** Send cookies. Defaults to false. */
14395
14822
  withCredentials?: boolean;
14396
14823
  /** Called on every `progress` event from the XHR upload channel. */
@@ -15229,6 +15656,12 @@ export declare function useIdle(timeout?: number): boolean;
15229
15656
  * mode, and applies a decline cooldown persisted in `localStorage`. The
15230
15657
  * resulting `method` tells the UI which install affordance to render.
15231
15658
  *
15659
+ * Both sides of that persistence are best-effort: `localStorage` throws in
15660
+ * Safari private mode and in a cross-origin frame with storage blocked, and an
15661
+ * exception raised while resolving an install CTA would take the page down with
15662
+ * it. A refused read means "no decline recorded"; a refused write means the CTA
15663
+ * may come back next visit.
15664
+ *
15232
15665
  * The decline persistence is pluggable through
15233
15666
  * {@link UseInstallPromptOptions.declineStorageKey} and
15234
15667
  * {@link UseInstallPromptOptions.declineCooldownMs} — no app-specific storage