tempest-react-sdk 0.79.0 → 0.80.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/bin/lib/openapi/generate.hostile.test.mjs +45 -4
- package/bin/lib/openapi/generate.mjs +145 -34
- package/bin/lib/openapi/generate.test.mjs +203 -4
- package/bin/lib/openapi/schema-to-zod.mjs +4 -3
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +5 -1
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/build-url.cjs +1 -1
- package/dist/http/build-url.cjs.map +1 -1
- package/dist/http/build-url.js +3 -1
- package/dist/http/build-url.js.map +1 -1
- package/dist/http/presigned-post.cjs +2 -0
- package/dist/http/presigned-post.cjs.map +1 -0
- package/dist/http/presigned-post.js +73 -0
- package/dist/http/presigned-post.js.map +1 -0
- package/dist/sse/create-event-stream.cjs +1 -1
- package/dist/sse/create-event-stream.cjs.map +1 -1
- package/dist/sse/create-event-stream.js +44 -30
- package/dist/sse/create-event-stream.js.map +1 -1
- package/dist/sse/ticket-event-stream.cjs +2 -0
- package/dist/sse/ticket-event-stream.cjs.map +1 -0
- package/dist/sse/ticket-event-stream.js +41 -0
- package/dist/sse/ticket-event-stream.js.map +1 -0
- package/dist/sse/use-event-stream.cjs +1 -1
- package/dist/sse/use-event-stream.cjs.map +1 -1
- package/dist/sse/use-event-stream.js +7 -4
- package/dist/sse/use-event-stream.js.map +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +206 -4
- package/dist/tempest-react-sdk.js +122 -120
- package/package.json +1 -1
|
@@ -768,6 +768,20 @@ export declare interface ApiClient {
|
|
|
768
768
|
*/
|
|
769
769
|
arrayBuffer(path: string, options?: RequestOptions): Promise<ArrayBuffer>;
|
|
770
770
|
upload<T>(path: string, formData: FormData, method?: "POST" | "PUT" | "PATCH", options?: Omit<RequestOptions, "body" | "method">): Promise<T>;
|
|
771
|
+
/**
|
|
772
|
+
* The absolute URL a request to `path` would hit, without sending one.
|
|
773
|
+
*
|
|
774
|
+
* For the consumers that cannot go through `fetch` but must land on the
|
|
775
|
+
* same host: an `EventSource`, an `<img src>`, a download link. Rebuilding
|
|
776
|
+
* the URL by hand duplicates `baseURL` and `prefix`, and a forgotten prefix
|
|
777
|
+
* is a silent 404. No credential is attached: the URL is a string, and a
|
|
778
|
+
* token placed in it would end up in every proxy log.
|
|
779
|
+
*
|
|
780
|
+
* @param path - Path joined onto the client's base URL and prefix.
|
|
781
|
+
* @param params - Query params to append; `undefined` and `null` are skipped.
|
|
782
|
+
* @returns The absolute URL.
|
|
783
|
+
*/
|
|
784
|
+
url(path: string, params?: RequestOptions["params"]): string;
|
|
771
785
|
}
|
|
772
786
|
|
|
773
787
|
export declare interface ApiClientConfig {
|
|
@@ -2417,7 +2431,10 @@ export { BrowserRouter }
|
|
|
2417
2431
|
*
|
|
2418
2432
|
* A path that is itself an absolute URL wins over all of this, which is how a
|
|
2419
2433
|
* client reaches a second host (a signed upload endpoint, a CDN) without a
|
|
2420
|
-
* second client.
|
|
2434
|
+
* second client. Without `params` it is returned exactly as given: a signed
|
|
2435
|
+
* URL covers its query, and a round trip through `URL` re-encodes characters
|
|
2436
|
+
* outside the canonical form (`'` became `%27`, measured), which is a rewrite
|
|
2437
|
+
* of a string the server signed.
|
|
2421
2438
|
*
|
|
2422
2439
|
* @example
|
|
2423
2440
|
* buildApiUrl("https://api.example.com", "/auth/login", { prefix: "/api" });
|
|
@@ -4220,11 +4237,15 @@ export declare function createDataProvider(client: ApiClient, options?: DataProv
|
|
|
4220
4237
|
* `onMessage`. Pass `withCredentials: true` when the backend authenticates via
|
|
4221
4238
|
* cookies. Call `close()` from the returned controller to tear down.
|
|
4222
4239
|
*
|
|
4223
|
-
*
|
|
4240
|
+
* When `url` is a function it is called before every connection, so a
|
|
4241
|
+
* reconnect presents a fresh credential instead of the one that expired. A
|
|
4242
|
+
* resolution still pending when `close()` or `reconnect()` runs is discarded.
|
|
4243
|
+
*
|
|
4244
|
+
* @param url - Full SSE endpoint URL, or a function returning it per connection.
|
|
4224
4245
|
* @param options - Stream configuration and callbacks.
|
|
4225
4246
|
* @returns A controller exposing `close`, `reconnect` and the current `status`.
|
|
4226
4247
|
*/
|
|
4227
|
-
export declare function createEventStream<T = unknown>(url:
|
|
4248
|
+
export declare function createEventStream<T = unknown>(url: EventStreamUrl, options?: CreateEventStreamOptions<T>): EventStreamController;
|
|
4228
4249
|
|
|
4229
4250
|
export declare interface CreateEventStreamOptions<T> {
|
|
4230
4251
|
/** Send cookies with the EventSource handshake. Default: false. */
|
|
@@ -4279,6 +4300,21 @@ export declare interface CreateEventStreamOptions<T> {
|
|
|
4279
4300
|
* `isDevBuild()` to be able to answer, and this callback is the app's own.
|
|
4280
4301
|
*/
|
|
4281
4302
|
onValidationError?: (issues: SchemaIssue[], raw: string) => void;
|
|
4303
|
+
/**
|
|
4304
|
+
* The URL function threw or rejected, so no connection was attempted.
|
|
4305
|
+
*
|
|
4306
|
+
* `onError` cannot carry this: it receives the `EventSource` `Event`, and a
|
|
4307
|
+
* URL that never resolved never had an `EventSource`.
|
|
4308
|
+
*/
|
|
4309
|
+
onUrlError?: (error: unknown) => void;
|
|
4310
|
+
/**
|
|
4311
|
+
* Whether a URL-resolution failure is worth another attempt. Default: always.
|
|
4312
|
+
*
|
|
4313
|
+
* Return `false` for failures no backoff can fix — a `401` or a `403` when
|
|
4314
|
+
* fetching a ticket is a session decision, not a network blip — and the stream
|
|
4315
|
+
* stops at `status: "error"` instead of spending `maxRetries` on it.
|
|
4316
|
+
*/
|
|
4317
|
+
shouldRetryUrl?: (error: unknown) => boolean;
|
|
4282
4318
|
onOpen?: () => void;
|
|
4283
4319
|
onMessage?: (message: EventStreamMessage<T>) => void;
|
|
4284
4320
|
onError?: (error: Event) => void;
|
|
@@ -5478,6 +5514,35 @@ export declare interface CreateThemeOptions {
|
|
|
5478
5514
|
inverseSelector?: string;
|
|
5479
5515
|
}
|
|
5480
5516
|
|
|
5517
|
+
/**
|
|
5518
|
+
* Open the ticket-authenticated stream of the `tempest-fastapi-sdk` SSE router.
|
|
5519
|
+
*
|
|
5520
|
+
* `POST <prefix>/ticket` through the authenticated client, then
|
|
5521
|
+
* `GET <prefix>/stream?ticket=…` — and again for every reconnect, because the
|
|
5522
|
+
* ticket only opens streams for `expires_in` seconds and a reconnect after that
|
|
5523
|
+
* with the old one is a `401`. A ticket request refused with a status no
|
|
5524
|
+
* backoff fixes (`401`, `403 ACCOUNT_INACTIVE`) stops the stream at
|
|
5525
|
+
* `status: "error"` and reaches `onUrlError`; pass `shouldRetryUrl` to replace
|
|
5526
|
+
* {@link isRetriableTicketError}.
|
|
5527
|
+
*
|
|
5528
|
+
* The access token never enters the URL: it travels as the client's header (or
|
|
5529
|
+
* cookie) on the ticket request, and the ticket in the URL opens that stream
|
|
5530
|
+
* and nothing else.
|
|
5531
|
+
*
|
|
5532
|
+
* @example
|
|
5533
|
+
* const stream = createTicketEventStream<Notification>({
|
|
5534
|
+
* client: api,
|
|
5535
|
+
* onMessage: ({ data }) => addNotification(data),
|
|
5536
|
+
* onUrlError: (error) => toast.error(describeApiError(error)),
|
|
5537
|
+
* });
|
|
5538
|
+
*
|
|
5539
|
+
* @param options - Ticket source plus every {@link createEventStream} option.
|
|
5540
|
+
* @returns The stream controller.
|
|
5541
|
+
*/
|
|
5542
|
+
export declare function createTicketEventStream<T = unknown>(options: CreateTicketEventStreamOptions<T>): EventStreamController;
|
|
5543
|
+
|
|
5544
|
+
export declare type CreateTicketEventStreamOptions<T> = TicketEventStreamSource & CreateEventStreamOptions<T>;
|
|
5545
|
+
|
|
5481
5546
|
/**
|
|
5482
5547
|
* Record a video stream — a camera, a screen share, or a `canvas.captureStream()`.
|
|
5483
5548
|
*
|
|
@@ -7326,6 +7391,19 @@ export declare interface EventStreamMessage<T> {
|
|
|
7326
7391
|
|
|
7327
7392
|
export declare type EventStreamStatus = "idle" | "connecting" | "open" | "closed" | "error";
|
|
7328
7393
|
|
|
7394
|
+
/**
|
|
7395
|
+
* Where the stream connects: a fixed URL, or a function resolved again before
|
|
7396
|
+
* every connection, the first one and each reconnect.
|
|
7397
|
+
*
|
|
7398
|
+
* The function form exists for short-lived credentials carried in the URL,
|
|
7399
|
+
* such as the `?ticket=` of the `tempest-fastapi-sdk` SSE router. `EventSource`
|
|
7400
|
+
* cannot send headers, so the credential has to ride in the query, and one that
|
|
7401
|
+
* expires makes a fixed URL a trap: the reconnect after the expiry presents the
|
|
7402
|
+
* same dead credential, gets a `401`, and burns every retry on it while the user
|
|
7403
|
+
* is still signed in.
|
|
7404
|
+
*/
|
|
7405
|
+
export declare type EventStreamUrl = string | (() => string | Promise<string>);
|
|
7406
|
+
|
|
7329
7407
|
/**
|
|
7330
7408
|
* Finish a `tempest-fastapi-sdk` social login from the SPA's callback route.
|
|
7331
7409
|
*
|
|
@@ -9478,6 +9556,21 @@ export declare function isRejectionCloseCode(code: number): boolean;
|
|
|
9478
9556
|
*/
|
|
9479
9557
|
export declare function isRetriableStatus(status: number): boolean;
|
|
9480
9558
|
|
|
9559
|
+
/**
|
|
9560
|
+
* Whether a failure to obtain a ticket is worth another attempt.
|
|
9561
|
+
*
|
|
9562
|
+
* Only a failure the backoff can outlast is: no response at all, a timeout, a
|
|
9563
|
+
* rate limit, a `5xx`. A `401` reached the caller after the client already
|
|
9564
|
+
* tried its refresh, and a `403` (`ACCOUNT_INACTIVE`) is the backend saying no;
|
|
9565
|
+
* both are session decisions, and retrying them only delays the error state the
|
|
9566
|
+
* app has to show. Anything that is not an API error is a contract mismatch,
|
|
9567
|
+
* which a retry cannot fix either.
|
|
9568
|
+
*
|
|
9569
|
+
* @param error - What the ticket request threw.
|
|
9570
|
+
* @returns Whether to schedule a reconnect.
|
|
9571
|
+
*/
|
|
9572
|
+
export declare function isRetriableTicketError(error: unknown): boolean;
|
|
9573
|
+
|
|
9481
9574
|
export { isRouteErrorResponse }
|
|
9482
9575
|
|
|
9483
9576
|
/** Whether `getDisplayMedia` is reachable at all. */
|
|
@@ -12926,6 +13019,22 @@ export declare type PreloadableLazy<T extends AnyComponent> = ReturnType<typeof
|
|
|
12926
13019
|
}>;
|
|
12927
13020
|
};
|
|
12928
13021
|
|
|
13022
|
+
/**
|
|
13023
|
+
* A pre-signed `POST` form, as `tempest-fastapi-sdk` >= 0.331.0 hands it out
|
|
13024
|
+
* (`presigned_post_policy` / `SubjectObjectStorage.presign_post`).
|
|
13025
|
+
*
|
|
13026
|
+
* Snake case on purpose: this is the wire shape, so it parses straight from the
|
|
13027
|
+
* response of the app's own route.
|
|
13028
|
+
*/
|
|
13029
|
+
export declare interface PresignedPost {
|
|
13030
|
+
/** Where the form is posted: the bucket on S3/MinIO, or the API itself on the local backend. */
|
|
13031
|
+
url: string;
|
|
13032
|
+
/** Form fields to send before the file, in this order — `key`, `policy`, signature and the rest. */
|
|
13033
|
+
fields: Record<string, string>;
|
|
13034
|
+
/** Largest file the policy accepts, in bytes. */
|
|
13035
|
+
max_bytes: number;
|
|
13036
|
+
}
|
|
13037
|
+
|
|
12929
13038
|
/** A model whose cached size should appear in the report. */
|
|
12930
13039
|
export declare interface ProfiledModel {
|
|
12931
13040
|
/** Label for the report row, e.g. `"detector"`. */
|
|
@@ -15829,6 +15938,14 @@ export declare interface SpinnerProps {
|
|
|
15829
15938
|
|
|
15830
15939
|
export declare type SpinnerSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
15831
15940
|
|
|
15941
|
+
/** Body of `POST <prefix>/ticket` on the `tempest-fastapi-sdk` SSE router. */
|
|
15942
|
+
export declare interface SseTicketResponse {
|
|
15943
|
+
/** Signed stream ticket, presented as a query param when opening the stream. */
|
|
15944
|
+
ticket: string;
|
|
15945
|
+
/** Seconds the ticket is accepted for opening a stream. */
|
|
15946
|
+
expires_in: number;
|
|
15947
|
+
}
|
|
15948
|
+
|
|
15832
15949
|
/** Flex-based vertical or horizontal stack with a numeric `gap`. */
|
|
15833
15950
|
export declare function Stack({ direction, gap, align, justify, wrap, className, style, children, ...props }: StackProps): JSX.Element;
|
|
15834
15951
|
|
|
@@ -16581,6 +16698,20 @@ export declare function throttle<A extends unknown[]>(fn: (...args: A) => void,
|
|
|
16581
16698
|
cancel: () => void;
|
|
16582
16699
|
};
|
|
16583
16700
|
|
|
16701
|
+
/** Where the ticket comes from and how the stream URL is built. */
|
|
16702
|
+
export declare interface TicketEventStreamSource {
|
|
16703
|
+
/**
|
|
16704
|
+
* Authenticated client the ticket is requested through, so it carries the
|
|
16705
|
+
* `Authorization` header or the session cookie, the CSRF header, and the
|
|
16706
|
+
* 401 refresh-and-replay that a hand-rolled `fetch` would lose.
|
|
16707
|
+
*/
|
|
16708
|
+
client: Pick<ApiClient, "post" | "url">;
|
|
16709
|
+
/** Router prefix, relative to the client's base URL. Default: `"/sse"`. */
|
|
16710
|
+
prefix?: string;
|
|
16711
|
+
/** Query param the stream reads the ticket from. Default: `"ticket"`. */
|
|
16712
|
+
ticketParam?: string;
|
|
16713
|
+
}
|
|
16714
|
+
|
|
16584
16715
|
/**
|
|
16585
16716
|
* Vertical event timeline — activity feeds, order trackers, audit logs.
|
|
16586
16717
|
* Each entry has a marker (color or custom icon), title, optional
|
|
@@ -17363,6 +17494,48 @@ export declare interface UpdateVariables {
|
|
|
17363
17494
|
*/
|
|
17364
17495
|
export declare function uploadFingerprint(endpoint: string, file: Blob | File): string;
|
|
17365
17496
|
|
|
17497
|
+
/**
|
|
17498
|
+
* Upload a file straight to storage through a pre-signed `POST` form, with the
|
|
17499
|
+
* size ceiling the policy enforces.
|
|
17500
|
+
*
|
|
17501
|
+
* The form is built the way S3 requires: every `post.fields` entry in order,
|
|
17502
|
+
* the file last in the `file` part — S3 ignores whatever follows the file.
|
|
17503
|
+
*
|
|
17504
|
+
* The request carries **no** credential and no custom header: no
|
|
17505
|
+
* `Authorization`, no CSRF token, no `X-Request-ID`, no cookies. `post.url` is
|
|
17506
|
+
* usually the storage host, outside the API's origin, and the signature in
|
|
17507
|
+
* `fields` is the whole authorization. Without custom headers the request is a
|
|
17508
|
+
* CORS "simple request", so it does not depend on the bucket's CORS rules
|
|
17509
|
+
* allowing a preflight.
|
|
17510
|
+
*
|
|
17511
|
+
* A file larger than `post.max_bytes` is refused before anything is sent, with
|
|
17512
|
+
* the same `413 FILE_TOO_LARGE` the server would answer. Server errors are
|
|
17513
|
+
* normalized into one `TempestApiError` whichever backend answered: the S3 XML
|
|
17514
|
+
* (`EntityTooLarge` → `FILE_TOO_LARGE`, `EntityTooSmall` → `FILE_TOO_SMALL`,
|
|
17515
|
+
* `AccessDenied` → `SIGNED_URL_INVALID`, or `SIGNED_URL_EXPIRED` when the policy
|
|
17516
|
+
* expired) or the local backend's JSON envelope, which already uses those codes.
|
|
17517
|
+
*
|
|
17518
|
+
* @example
|
|
17519
|
+
* const post = await api.post<PresignedPost>("/avatars/upload-form");
|
|
17520
|
+
* await uploadPresignedPost(post, file, { onProgress: (p) => setProgress(p.fraction) });
|
|
17521
|
+
*
|
|
17522
|
+
* @param post - The form the API handed out.
|
|
17523
|
+
* @param file - The file or blob to upload.
|
|
17524
|
+
* @param options - Progress callback, abort signal and file name.
|
|
17525
|
+
* @returns Resolves when storage accepted the file (`204` on S3).
|
|
17526
|
+
* @throws TempestApiError On a refused upload, with the normalized `code`.
|
|
17527
|
+
*/
|
|
17528
|
+
export declare function uploadPresignedPost(post: PresignedPost, file: Blob, options?: UploadPresignedPostOptions): Promise<void>;
|
|
17529
|
+
|
|
17530
|
+
export declare interface UploadPresignedPostOptions {
|
|
17531
|
+
/** Called on every `progress` event of the upload. */
|
|
17532
|
+
onProgress?: (event: UploadProgressEvent) => void;
|
|
17533
|
+
/** Abort the upload; rejects with an `AbortError` `DOMException`. */
|
|
17534
|
+
signal?: AbortSignal;
|
|
17535
|
+
/** File name sent with the `file` part. Defaults to `file.name` for a `File`. */
|
|
17536
|
+
fileName?: string;
|
|
17537
|
+
}
|
|
17538
|
+
|
|
17366
17539
|
export declare interface UploadProgressEvent {
|
|
17367
17540
|
/** Bytes already uploaded. */
|
|
17368
17541
|
loaded: number;
|
|
@@ -18144,6 +18317,11 @@ export declare function useEventListener<K extends keyof HTMLElementEventMap>(ev
|
|
|
18144
18317
|
* tied to the component (and the `url`/`enabled` dependencies); the stream
|
|
18145
18318
|
* closes on unmount.
|
|
18146
18319
|
*
|
|
18320
|
+
* A string `url` reopens the stream when it changes. A function `url` does not:
|
|
18321
|
+
* an inline arrow is a new function every render, and keying on its identity
|
|
18322
|
+
* would reconnect on every render. The latest function is the one called on the
|
|
18323
|
+
* next connection, so call `reconnect()` when what it resolves to must change now.
|
|
18324
|
+
*
|
|
18147
18325
|
* Pass `schema` to have every frame validated before it reaches `onMessage`,
|
|
18148
18326
|
* instead of trusting the type argument: without it `data` is announced as `T`
|
|
18149
18327
|
* on the strength of the generic alone, which is a promise about the server
|
|
@@ -18167,7 +18345,7 @@ export declare function useEventListener<K extends keyof HTMLElementEventMap>(ev
|
|
|
18167
18345
|
* onMessage: ({ data }) => addNotification(data),
|
|
18168
18346
|
* });
|
|
18169
18347
|
*/
|
|
18170
|
-
export declare function useEventStream<T = unknown>(url:
|
|
18348
|
+
export declare function useEventStream<T = unknown>(url: EventStreamUrl, options?: UseEventStreamOptions<T>): UseEventStreamResult<T>;
|
|
18171
18349
|
|
|
18172
18350
|
export declare interface UseEventStreamOptions<T> extends Omit<CreateEventStreamOptions<T>, "onStatusChange"> {
|
|
18173
18351
|
/** When false, the stream is not opened. Useful for "wait for auth". Default: true. */
|
|
@@ -20186,6 +20364,30 @@ export declare function useTheme(): ThemeContextValue;
|
|
|
20186
20364
|
*/
|
|
20187
20365
|
export declare function useThrottle<T>(value: T, delay: number): T;
|
|
20188
20366
|
|
|
20367
|
+
/**
|
|
20368
|
+
* React form of {@link createTicketEventStream}: same ticket per connection,
|
|
20369
|
+
* lifecycle tied to the component like {@link useEventStream}.
|
|
20370
|
+
*
|
|
20371
|
+
* Changing `client`, `prefix` or `ticketParam` does not drop an open stream:
|
|
20372
|
+
* the next connection uses the new values, and `reconnect()` applies them now.
|
|
20373
|
+
* Reopening on identity would reconnect on every render for a client built
|
|
20374
|
+
* inline.
|
|
20375
|
+
*
|
|
20376
|
+
* @example
|
|
20377
|
+
* const { status } = useTicketEventStream<Notification>({
|
|
20378
|
+
* client: api,
|
|
20379
|
+
* enabled: isAuthenticated,
|
|
20380
|
+
* schema: notificationSchema,
|
|
20381
|
+
* onMessage: ({ data }) => addNotification(data),
|
|
20382
|
+
* });
|
|
20383
|
+
*
|
|
20384
|
+
* @param options - Ticket source plus every {@link useEventStream} option.
|
|
20385
|
+
* @returns Status, last message and `reconnect`.
|
|
20386
|
+
*/
|
|
20387
|
+
export declare function useTicketEventStream<T = unknown>(options: UseTicketEventStreamOptions<T>): UseEventStreamResult<T>;
|
|
20388
|
+
|
|
20389
|
+
export declare type UseTicketEventStreamOptions<T> = TicketEventStreamSource & UseEventStreamOptions<T>;
|
|
20390
|
+
|
|
20189
20391
|
/**
|
|
20190
20392
|
* Run a callback after `delay` ms. Pass `null` to disable. Resets when
|
|
20191
20393
|
* `delay` changes; the callback ref always points at the latest closure.
|