tempest-react-sdk 0.65.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 (36) hide show
  1. package/dist/auth/create-tempest-auth.cjs +1 -1
  2. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  3. package/dist/auth/create-tempest-auth.js +31 -36
  4. package/dist/auth/create-tempest-auth.js.map +1 -1
  5. package/dist/http/api-client.cjs +1 -1
  6. package/dist/http/api-client.cjs.map +1 -1
  7. package/dist/http/api-client.js +87 -85
  8. package/dist/http/api-client.js.map +1 -1
  9. package/dist/http/credential-scope.cjs +2 -0
  10. package/dist/http/credential-scope.cjs.map +1 -0
  11. package/dist/http/credential-scope.js +23 -0
  12. package/dist/http/credential-scope.js.map +1 -0
  13. package/dist/http/resumable-upload.cjs +1 -1
  14. package/dist/http/resumable-upload.cjs.map +1 -1
  15. package/dist/http/resumable-upload.js +121 -120
  16. package/dist/http/resumable-upload.js.map +1 -1
  17. package/dist/http/upload-with-progress.cjs +1 -1
  18. package/dist/http/upload-with-progress.cjs.map +1 -1
  19. package/dist/http/upload-with-progress.js +35 -34
  20. package/dist/http/upload-with-progress.js.map +1 -1
  21. package/dist/tempest-react-sdk.cjs +1 -1
  22. package/dist/tempest-react-sdk.d.ts +217 -16
  23. package/dist/tempest-react-sdk.js +233 -231
  24. package/dist/utils/absent.cjs +2 -0
  25. package/dist/utils/absent.cjs.map +1 -0
  26. package/dist/utils/absent.js +12 -0
  27. package/dist/utils/absent.js.map +1 -0
  28. package/dist/utils/format.cjs +1 -1
  29. package/dist/utils/format.cjs.map +1 -1
  30. package/dist/utils/format.js +42 -37
  31. package/dist/utils/format.js.map +1 -1
  32. package/dist/utils/numbers.cjs +1 -1
  33. package/dist/utils/numbers.cjs.map +1 -1
  34. package/dist/utils/numbers.js +14 -12
  35. package/dist/utils/numbers.js.map +1 -1
  36. 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
@@ -652,6 +663,25 @@ export declare interface ApiClientConfig {
652
663
  prefix?: string;
653
664
  /** Returns the current bearer token (or null/undefined). Called per request. */
654
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[];
655
685
  /**
656
686
  * Per-request correlation id sent as the `X-Request-ID` header, matching the
657
687
  * Tempest FastAPI SDK `RequestIDMiddleware`. Defaults to a generated id.
@@ -3266,8 +3296,17 @@ export declare const CPFInput: ForwardRefExoticComponent<Omit<InputProps, "value
3266
3296
  * a development build, because reading `image/jpeg` as UTF-8 destroyed the bytes
3267
3297
  * on the way in.
3268
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
+ *
3269
3306
  * **Retries** are off unless you set `retry`. See {@link ApiClientConfig.retry}
3270
- * 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}.
3271
3310
  *
3272
3311
  * **Logging** is off unless you pass a `logger`. With one, every finished attempt
3273
3312
  * writes a line — `debug` under 400, `warn` from 400 up, plus a `warn` when
@@ -6443,13 +6482,19 @@ export declare interface FormActionsProps extends HTMLAttributes<HTMLDivElement>
6443
6482
  * controls the fractional digits for scaled units (default 1); `0` bytes
6444
6483
  * always renders as `"0 B"`.
6445
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
+ *
6446
6490
  * @example
6447
6491
  * formatBytes(0); // "0 B"
6448
6492
  * formatBytes(1536); // "1.5 KB"
6449
6493
  * formatBytes(1048576); // "1 MB"
6450
6494
  * formatBytes(1536, 2); // "1.50 KB"
6495
+ * formatBytes(null); // "—"
6451
6496
  */
6452
- export declare function formatBytes(bytes: number, decimals?: number): string;
6497
+ export declare function formatBytes(bytes: number | null | undefined, decimals?: number, options?: FormatFallbackOptions): string;
6453
6498
 
6454
6499
  /** Format an 11-digit Brazilian CEP-like value as `00000-000`. */
6455
6500
  export declare function formatCEP(value: string): string;
@@ -6463,12 +6508,17 @@ export declare function formatCNPJ(value: string): string;
6463
6508
  * Wraps `Intl.NumberFormat` with `notation: "compact"`. The `locale` defaults
6464
6509
  * to `"en-US"`.
6465
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
+ *
6466
6515
  * @example
6467
6516
  * formatCompactNumber(1234); // "1.2K"
6468
6517
  * formatCompactNumber(5600000); // "5.6M"
6469
6518
  * formatCompactNumber(1234, "pt-BR"); // "1,2 mil"
6519
+ * formatCompactNumber(null); // "—"
6470
6520
  */
6471
- export declare function formatCompactNumber(value: number, locale?: string): string;
6521
+ export declare function formatCompactNumber(value: number | null | undefined, locale?: string, options?: FormatFallbackOptions): string;
6472
6522
 
6473
6523
  /**
6474
6524
  * Apply the Brazilian CPF mask `XXX.XXX.XXX-XX`.
@@ -6481,18 +6531,50 @@ export declare function formatCPF(value: string): string;
6481
6531
  /**
6482
6532
  * Format a number as Brazilian Real currency.
6483
6533
  *
6484
- * @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.
6485
6549
  * @returns A locale-formatted string, e.g. "R$ 1.234,56".
6486
6550
  */
6487
- export declare function formatCurrency(value: number): string;
6551
+ export declare function formatCurrency(value: number | null | undefined, options?: FormatFallbackOptions): string;
6488
6552
 
6489
6553
  /**
6490
6554
  * Format an ISO date or Date instance as `dd/MM/yyyy`.
6491
6555
  *
6492
- * @param value - ISO string or Date.
6493
- * @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.
6494
6576
  */
6495
- export declare function formatDate(value: string | Date): string;
6577
+ export declare function formatDate(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6496
6578
 
6497
6579
  /**
6498
6580
  * Format an ISO date or Date instance as `yyyy-MM-dd`, the value an
@@ -6512,19 +6594,29 @@ export declare function formatDate(value: string | Date): string;
6512
6594
  * @example
6513
6595
  * <input type="date" defaultValue={formatDateForInput(order.createdAt)} />
6514
6596
  *
6515
- * @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 `""`.
6516
6603
  * @returns The `yyyy-MM-dd` value, or an empty string when the input is invalid —
6517
6604
  * which is what a date input reads as "no value", unlike `"Invalid Date"`.
6518
6605
  */
6519
- export declare function formatDateForInput(value: string | Date): string;
6606
+ export declare function formatDateForInput(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6520
6607
 
6521
6608
  /**
6522
6609
  * Format an ISO date or Date instance as `dd/MM/yyyy HH:mm`.
6523
6610
  *
6524
- * @param value - ISO string or Date.
6525
- * @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.
6526
6618
  */
6527
- export declare function formatDateTime(value: string | Date): string;
6619
+ export declare function formatDateTime(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6528
6620
 
6529
6621
  /**
6530
6622
  * Format an ISO date or Date instance as `yyyy-MM-ddTHH:mm`, the value an
@@ -6556,7 +6648,7 @@ export declare function formatDateTime(value: string | Date): string;
6556
6648
  * invalid — which is what a datetime input reads as "no value", unlike
6557
6649
  * `"Invalid Date"`.
6558
6650
  */
6559
- export declare function formatDateTimeForInput(value: string | Date): string;
6651
+ export declare function formatDateTimeForInput(value: string | Date | null | undefined, options?: FormatFallbackOptions): string;
6560
6652
 
6561
6653
  /**
6562
6654
  * Rendering helpers for the numbers a profiler produces.
@@ -6583,6 +6675,19 @@ export declare function formatDateTimeForInput(value: string | Date): string;
6583
6675
  */
6584
6676
  export declare function formatDurationMs(value: number): string;
6585
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
+
6586
6691
  /**
6587
6692
  * Format a fraction (0-1) as a percentage.
6588
6693
  *
@@ -6601,10 +6706,10 @@ export declare function formatDurationMs(value: number): string;
6601
6706
  * @param options - Rendering options; `decimals` defaults to 1.
6602
6707
  * @returns Formatted percent string, e.g. "12,5%", or `"—"` for a non-finite input.
6603
6708
  */
6604
- export declare function formatPercent(value: number, options?: FormatPercentOptions): string;
6709
+ export declare function formatPercent(value: number | null | undefined, options?: FormatPercentOptions): string;
6605
6710
 
6606
6711
  /** How {@link formatPercent} renders a fraction. */
6607
- export declare interface FormatPercentOptions {
6712
+ export declare interface FormatPercentOptions extends FormatFallbackOptions {
6608
6713
  /**
6609
6714
  * Decimal places to show, fixed (padded as well as truncated).
6610
6715
  *
@@ -7866,6 +7971,42 @@ declare type IssuePathSegment = PropertyKey | {
7866
7971
  readonly key: PropertyKey;
7867
7972
  };
7868
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
+
7869
8010
  /** True when `value` is a finite latitude in `[-90, 90]`. */
7870
8011
  export declare function isValidLatitude(value: number): boolean;
7871
8012
 
@@ -11670,6 +11811,38 @@ export declare interface RequestOptions extends Omit<RequestInit, "body"> {
11670
11811
  * a `queryFn` and the request is cancelled on unmount and on refetch.
11671
11812
  */
11672
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;
11673
11846
  /**
11674
11847
  * Override the client's timeout for this request, in milliseconds. `null`
11675
11848
  * disables it, which is the escape hatch for a stream or a long poll.
@@ -11825,6 +11998,18 @@ export declare interface ResumableUploadOptions {
11825
11998
  headers?: Record<string, string>;
11826
11999
  /** Returns the current bearer token, read before each request. */
11827
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[];
11828
12013
  /** Send cookies. Default `false`. */
11829
12014
  withCredentials?: boolean;
11830
12015
  /**
@@ -14617,6 +14802,22 @@ export declare interface UploadWithProgressOptions {
14617
14802
  headers?: Record<string, string>;
14618
14803
  /** Returns the current bearer token. */
14619
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[];
14620
14821
  /** Send cookies. Defaults to false. */
14621
14822
  withCredentials?: boolean;
14622
14823
  /** Called on every `progress` event from the XHR upload channel. */