tempest-react-sdk 0.33.2 → 0.34.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 +3 -3
- package/dist/components/AIChat/AIChat.cjs +2 -0
- package/dist/components/AIChat/AIChat.cjs.map +1 -0
- package/dist/components/AIChat/AIChat.js +141 -0
- package/dist/components/AIChat/AIChat.js.map +1 -0
- package/dist/components/AIChat/AIChat.module.cjs +2 -0
- package/dist/components/AIChat/AIChat.module.cjs.map +1 -0
- package/dist/components/AIChat/AIChat.module.js +58 -0
- package/dist/components/AIChat/AIChat.module.js.map +1 -0
- package/dist/components/AIChat/AIChatComposer.cjs +2 -0
- package/dist/components/AIChat/AIChatComposer.cjs.map +1 -0
- package/dist/components/AIChat/AIChatComposer.js +87 -0
- package/dist/components/AIChat/AIChatComposer.js.map +1 -0
- package/dist/components/AIChat/AIChatTurn.cjs +3 -0
- package/dist/components/AIChat/AIChatTurn.cjs.map +1 -0
- package/dist/components/AIChat/AIChatTurn.js +217 -0
- package/dist/components/AIChat/AIChatTurn.js.map +1 -0
- package/dist/components/AIChat/ai-chat-turns.cjs +2 -0
- package/dist/components/AIChat/ai-chat-turns.cjs.map +1 -0
- package/dist/components/AIChat/ai-chat-turns.js +90 -0
- package/dist/components/AIChat/ai-chat-turns.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +415 -9
- package/dist/tempest-react-sdk.js +118 -114
- package/dist/ws/create-web-socket.cjs +1 -1
- package/dist/ws/create-web-socket.cjs.map +1 -1
- package/dist/ws/create-web-socket.js +54 -37
- package/dist/ws/create-web-socket.js.map +1 -1
- package/dist/ws/use-web-socket.cjs +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js +49 -18
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +1 -1
- package/template/src/lib/api.ts +27 -5
- package/template/src/stores/auth.ts +14 -0
|
@@ -1,35 +1,66 @@
|
|
|
1
1
|
import { createWebSocket as e } from "./create-web-socket.js";
|
|
2
2
|
import { useCallback as t, useEffect as n, useRef as r, useState as i } from "react";
|
|
3
3
|
//#region src/ws/use-web-socket.ts
|
|
4
|
-
function a(
|
|
5
|
-
|
|
4
|
+
function a(e) {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(e);
|
|
7
|
+
} catch {
|
|
8
|
+
return e;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function o(o, s = {}) {
|
|
12
|
+
let { enabled: c = !0, protocols: l, maxRetries: u, initialBackoff: d, maxBackoff: f, pingInterval: p, respondToPing: m, queueWhileClosed: h, maxQueuedMessages: g } = s, [_, v] = i("idle"), [y, b] = i(null), x = r(null), S = r(s);
|
|
6
13
|
return n(() => {
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
if (!
|
|
10
|
-
|
|
14
|
+
S.current = s;
|
|
15
|
+
}), n(() => {
|
|
16
|
+
if (!c || !o) {
|
|
17
|
+
v("idle");
|
|
11
18
|
return;
|
|
12
19
|
}
|
|
13
|
-
let t = e(
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
let t = e(o, {
|
|
21
|
+
protocols: S.current.protocols,
|
|
22
|
+
maxRetries: u,
|
|
23
|
+
initialBackoff: d,
|
|
24
|
+
maxBackoff: f,
|
|
25
|
+
pingInterval: p,
|
|
26
|
+
pingPayload: S.current.pingPayload,
|
|
27
|
+
respondToPing: m,
|
|
28
|
+
pongPayload: S.current.pongPayload,
|
|
29
|
+
queueWhileClosed: h,
|
|
30
|
+
maxQueuedMessages: g,
|
|
31
|
+
parser: (e) => (S.current.parser ?? a)(e),
|
|
32
|
+
onStatusChange: v,
|
|
33
|
+
onOpen: (e) => S.current.onOpen?.(e),
|
|
34
|
+
onClose: (e) => S.current.onClose?.(e),
|
|
35
|
+
onError: (e) => S.current.onError?.(e),
|
|
16
36
|
onMessage: (e) => {
|
|
17
|
-
|
|
37
|
+
b(e), S.current.onMessage?.(e);
|
|
18
38
|
}
|
|
19
39
|
});
|
|
20
|
-
return
|
|
21
|
-
t.close(),
|
|
40
|
+
return x.current = t, () => {
|
|
41
|
+
t.close(), x.current = null;
|
|
22
42
|
};
|
|
23
|
-
}, [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
43
|
+
}, [
|
|
44
|
+
o,
|
|
45
|
+
c,
|
|
46
|
+
Array.isArray(l) ? l.join(",") : l ?? "",
|
|
47
|
+
u,
|
|
48
|
+
d,
|
|
49
|
+
f,
|
|
50
|
+
p,
|
|
51
|
+
m,
|
|
52
|
+
h,
|
|
53
|
+
g
|
|
54
|
+
]), {
|
|
55
|
+
status: _,
|
|
56
|
+
lastMessage: y,
|
|
57
|
+
send: t((e) => x.current?.send(e) ?? !1, []),
|
|
27
58
|
reconnect: t(() => {
|
|
28
|
-
|
|
59
|
+
x.current?.reconnect();
|
|
29
60
|
}, [])
|
|
30
61
|
};
|
|
31
62
|
}
|
|
32
63
|
//#endregion
|
|
33
|
-
export {
|
|
64
|
+
export { o as useWebSocket };
|
|
34
65
|
|
|
35
66
|
//# sourceMappingURL=use-web-socket.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-web-socket.js","names":[],"sources":["../../src/ws/use-web-socket.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n createWebSocket,\n type CreateWebSocketOptions,\n type WebSocketController,\n type WebSocketMessage,\n type WebSocketStatus,\n} from \"./create-web-socket\";\n\nexport interface UseWebSocketOptions<T> extends Omit<CreateWebSocketOptions<T>, \"onStatusChange\"> {\n /** When false, the socket is not opened. Default: true. */\n enabled?: boolean;\n}\n\nexport interface UseWebSocketResult<T> {\n status: WebSocketStatus;\n
|
|
1
|
+
{"version":3,"file":"use-web-socket.js","names":[],"sources":["../../src/ws/use-web-socket.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n createWebSocket,\n type CreateWebSocketOptions,\n type WebSocketController,\n type WebSocketMessage,\n type WebSocketStatus,\n} from \"./create-web-socket\";\n\nexport interface UseWebSocketOptions<T> extends Omit<CreateWebSocketOptions<T>, \"onStatusChange\"> {\n /** When false, the socket is not opened. Default: true. */\n enabled?: boolean;\n}\n\nexport interface UseWebSocketResult<T> {\n status: WebSocketStatus;\n /**\n * Last decoded frame received.\n *\n * A snapshot, not a stream: two frames arriving in the same tick collapse\n * into a single render and only the later one is ever visible. One server\n * action often emits several frames in a row, so anything that must see\n * every message has to use `onMessage`, which fires once per frame. Read\n * `lastMessage` for \"what is the current state\" rendering only.\n */\n lastMessage: WebSocketMessage<T> | null;\n /** Send a payload through the active connection. Returns false when not open. */\n send: (payload: string | Blob | BufferSource) => boolean;\n /** Force a reconnect, resetting the retry counter. */\n reconnect: () => void;\n}\n\n/** Mirror of `createWebSocket`'s default parser: JSON, raw string on failure. */\nfunction defaultParse<T>(raw: string): T {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n}\n\n/**\n * React hook around {@link createWebSocket}. Manages the connection lifecycle\n * for the host component and tears it down on unmount.\n *\n * Every callback is read through a ref, so `onOpen` / `onMessage` / `onClose` /\n * `onError` always run the latest closure — an inline arrow function is fine\n * and never reopens the socket. Connection-shaping options (`protocols`,\n * `maxRetries`, `initialBackoff`, `maxBackoff`, `pingInterval`,\n * `queueWhileClosed`) are baked into the connection, so changing one reopens\n * it with the new value rather than being silently ignored.\n *\n * @param url - Full ws:// or wss:// URL.\n * @param options - Connection configuration and callbacks.\n * @returns Status, last frame, and the `send` / `reconnect` controls.\n */\nexport function useWebSocket<T = unknown>(\n url: string,\n options: UseWebSocketOptions<T> = {},\n): UseWebSocketResult<T> {\n const {\n enabled = true,\n protocols,\n maxRetries,\n initialBackoff,\n maxBackoff,\n pingInterval,\n respondToPing,\n queueWhileClosed,\n maxQueuedMessages,\n } = options;\n const [status, setStatus] = useState<WebSocketStatus>(\"idle\");\n const [lastMessage, setLastMessage] = useState<WebSocketMessage<T> | null>(null);\n const controllerRef = useRef<WebSocketController | null>(null);\n\n const optionsRef = useRef(options);\n useEffect(() => {\n optionsRef.current = options;\n });\n\n const protocolsKey = Array.isArray(protocols) ? protocols.join(\",\") : (protocols ?? \"\");\n\n useEffect(() => {\n if (!enabled || !url) {\n setStatus(\"idle\");\n return;\n }\n\n const controller = createWebSocket<T>(url, {\n protocols: optionsRef.current.protocols,\n maxRetries,\n initialBackoff,\n maxBackoff,\n pingInterval,\n pingPayload: optionsRef.current.pingPayload,\n respondToPing,\n pongPayload: optionsRef.current.pongPayload,\n queueWhileClosed,\n maxQueuedMessages,\n parser: (raw) => (optionsRef.current.parser ?? defaultParse<T>)(raw),\n onStatusChange: setStatus,\n onOpen: (event) => optionsRef.current.onOpen?.(event),\n onClose: (event) => optionsRef.current.onClose?.(event),\n onError: (event) => optionsRef.current.onError?.(event),\n onMessage: (message) => {\n setLastMessage(message);\n optionsRef.current.onMessage?.(message);\n },\n });\n controllerRef.current = controller;\n\n return () => {\n controller.close();\n controllerRef.current = null;\n };\n }, [\n url,\n enabled,\n protocolsKey,\n maxRetries,\n initialBackoff,\n maxBackoff,\n pingInterval,\n respondToPing,\n queueWhileClosed,\n maxQueuedMessages,\n ]);\n\n const send = useCallback((payload: string | Blob | BufferSource): boolean => {\n return controllerRef.current?.send(payload) ?? false;\n }, []);\n\n const reconnect = useCallback((): void => {\n controllerRef.current?.reconnect();\n }, []);\n\n return { status, lastMessage, send, reconnect };\n}\n"],"mappings":";;;AAiCA,SAAS,EAAgB,GAAgB;CACrC,IAAI;EACA,OAAO,KAAK,MAAM,CAAG;CACzB,QAAQ;EACJ,OAAO;CACX;AACJ;AAiBA,SAAgB,EACZ,GACA,IAAkC,CAAC,GACd;CACrB,IAAM,EACF,aAAU,IACV,cACA,eACA,mBACA,eACA,iBACA,kBACA,qBACA,yBACA,GACE,CAAC,GAAQ,KAAa,EAA0B,MAAM,GACtD,CAAC,GAAa,KAAkB,EAAqC,IAAI,GACzE,IAAgB,EAAmC,IAAI,GAEvD,IAAa,EAAO,CAAO;CA6DjC,OA5DA,QAAgB;EACZ,EAAW,UAAU;CACzB,CAAC,GAID,QAAgB;EACZ,IAAI,CAAC,KAAW,CAAC,GAAK;GAClB,EAAU,MAAM;GAChB;EACJ;EAEA,IAAM,IAAa,EAAmB,GAAK;GACvC,WAAW,EAAW,QAAQ;GAC9B;GACA;GACA;GACA;GACA,aAAa,EAAW,QAAQ;GAChC;GACA,aAAa,EAAW,QAAQ;GAChC;GACA;GACA,SAAS,OAAS,EAAW,QAAQ,UAAU,EAAA,CAAiB,CAAG;GACnE,gBAAgB;GAChB,SAAS,MAAU,EAAW,QAAQ,SAAS,CAAK;GACpD,UAAU,MAAU,EAAW,QAAQ,UAAU,CAAK;GACtD,UAAU,MAAU,EAAW,QAAQ,UAAU,CAAK;GACtD,YAAY,MAAY;IAEpB,AADA,EAAe,CAAO,GACtB,EAAW,QAAQ,YAAY,CAAO;GAC1C;EACJ,CAAC;EAGD,OAFA,EAAc,UAAU,SAEX;GAET,AADA,EAAW,MAAM,GACjB,EAAc,UAAU;EAC5B;CACJ,GAAG;EACC;EACA;EArCiB,MAAM,QAAQ,CAAS,IAAI,EAAU,KAAK,GAAG,IAAK,KAAa;EAuChF;EACA;EACA;EACA;EACA;EACA;EACA;CACJ,CAAC,GAUM;EAAE;EAAQ;EAAa,MARjB,GAAa,MACf,EAAc,SAAS,KAAK,CAAO,KAAK,IAChD,CAAC,CAM0B;EAAM,WAJlB,QAAwB;GACtC,EAAc,SAAS,UAAU;EACrC,GAAG,CAAC,CAEgC;CAAU;AAClD"}
|
package/package.json
CHANGED
package/template/src/lib/api.ts
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
1
|
import { createApiClient, createQueryKeys } from "tempest-react-sdk";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** Reads the current bearer token, or null when there is no session. */
|
|
4
|
+
export type TokenProvider = () => string | null;
|
|
5
|
+
|
|
6
|
+
let readToken: TokenProvider = () => null;
|
|
7
|
+
let handleUnauthorized: () => void = () => {};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tell the client where the session lives.
|
|
11
|
+
*
|
|
12
|
+
* The client ships knowing nothing about auth so a project without accounts
|
|
13
|
+
* can delete `@/stores/auth` and touch nothing here: with no call to this
|
|
14
|
+
* function every request goes out unauthenticated and a 401 is just an error.
|
|
15
|
+
* `@/stores/auth` calls it at module load to wire the store in.
|
|
16
|
+
*
|
|
17
|
+
* @param options - Where to read the token and what to do on a 401.
|
|
18
|
+
*/
|
|
19
|
+
export function configureApiAuth(options: {
|
|
20
|
+
getToken: TokenProvider;
|
|
21
|
+
onUnauthorized?: () => void;
|
|
22
|
+
}): void {
|
|
23
|
+
readToken = options.getToken;
|
|
24
|
+
handleUnauthorized = options.onUnauthorized ?? (() => {});
|
|
25
|
+
}
|
|
4
26
|
|
|
5
27
|
/**
|
|
6
|
-
* Typed HTTP client
|
|
7
|
-
*
|
|
28
|
+
* Typed HTTP client pointed at `VITE_API_URL`, reading the bearer token
|
|
29
|
+
* through whatever `configureApiAuth` registered.
|
|
8
30
|
*/
|
|
9
31
|
export const api = createApiClient({
|
|
10
32
|
baseURL: import.meta.env.VITE_API_URL ?? "http://127.0.0.1:8000",
|
|
11
|
-
getToken: () =>
|
|
12
|
-
onUnauthorized: () =>
|
|
33
|
+
getToken: () => readToken(),
|
|
34
|
+
onUnauthorized: () => handleUnauthorized(),
|
|
13
35
|
});
|
|
14
36
|
|
|
15
37
|
/** Stable, namespaced query keys for TanStack Query. */
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createAuthStore, createSelectors } from "tempest-react-sdk";
|
|
2
2
|
|
|
3
|
+
import { configureApiAuth } from "@/lib/api";
|
|
4
|
+
|
|
3
5
|
/** The user shape your backend returns. Adjust to match your API. */
|
|
4
6
|
export interface User {
|
|
5
7
|
id: string;
|
|
@@ -13,3 +15,15 @@ export interface User {
|
|
|
13
15
|
* subscribe to a single slice.
|
|
14
16
|
*/
|
|
15
17
|
export const useAuth = createSelectors(createAuthStore<User>({ name: "app-auth" }));
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hand the store to the HTTP client.
|
|
21
|
+
*
|
|
22
|
+
* The dependency points this way on purpose: `@/lib/api` knows nothing about
|
|
23
|
+
* auth, so a project without accounts deletes this file and the client keeps
|
|
24
|
+
* working unauthenticated.
|
|
25
|
+
*/
|
|
26
|
+
configureApiAuth({
|
|
27
|
+
getToken: () => useAuth.getState().token,
|
|
28
|
+
onUnauthorized: () => useAuth.getState().logout(),
|
|
29
|
+
});
|