@ominiflow/sdk-react 0.2.0-rc.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 ADDED
@@ -0,0 +1,99 @@
1
+ # @ominiflow/sdk-react
2
+
3
+ Bindings React oficiais para o SDK TypeScript do OmniFlow. O pacote entrega `OmniFlowProvider`, hooks idiomáticos com TanStack Query e componentes opt-in para inbox rápido.
4
+
5
+ ## Instalação
6
+
7
+ ```bash
8
+ npm install @ominiflow/sdk @ominiflow/sdk-react @tanstack/react-query
9
+ # ou
10
+ bun add @ominiflow/sdk @ominiflow/sdk-react @tanstack/react-query
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```tsx
16
+ import { OmniFlowProvider, MessageList, Composer, useSendMessage } from '@ominiflow/sdk-react';
17
+
18
+ function ConversationPanel() {
19
+ const sendMessage = useSendMessage();
20
+
21
+ return (
22
+ <div style={{ display: 'grid', gap: 12 }}>
23
+ <MessageList conversationId="conv_123" />
24
+ <Composer
25
+ onSend={(text) =>
26
+ sendMessage.mutateAsync({
27
+ to: '+5511999999999',
28
+ channel: 'whatsapp_official',
29
+ content: { type: 'text', text },
30
+ })
31
+ }
32
+ />
33
+ </div>
34
+ );
35
+ }
36
+
37
+ export function App() {
38
+ return (
39
+ <OmniFlowProvider config={{ jwt: 'jwt_emitido_pelo_backend', baseUrl: 'https://api.omniflow.com.br' }}>
40
+ <ConversationPanel />
41
+ </OmniFlowProvider>
42
+ );
43
+ }
44
+ ```
45
+
46
+ ## Provider
47
+
48
+ O `OmniFlowProvider` aceita um `client` já instanciado ou `config` do `@ominiflow/sdk`.
49
+
50
+ ```tsx
51
+ <OmniFlowProvider config={{ jwt: 'jwt_frontend', baseUrl: 'https://api.omniflow.com.br' }}>
52
+ <App />
53
+ </OmniFlowProvider>
54
+
55
+ <OmniFlowProvider config={{ apiKey: 'omf_live_...' }}>
56
+ <App />
57
+ </OmniFlowProvider>
58
+
59
+ <OmniFlowProvider
60
+ config={{
61
+ oauth: { clientId: 'client_123', clientSecret: 'secret_123' },
62
+ baseUrl: 'https://api.omniflow.com.br',
63
+ }}
64
+ >
65
+ <App />
66
+ </OmniFlowProvider>
67
+ ```
68
+
69
+ ## Hooks
70
+
71
+ ### `useOmniFlow()`
72
+
73
+ Retorna a instância do `OmniFlow` provida pelo contexto.
74
+
75
+ ### `useConversations(filters?)`
76
+
77
+ Executa `client.conversations.list(filters)` com query key namespaced.
78
+
79
+ ### `useMessages(conversationId, options?)`
80
+
81
+ Executa paginação cursor de mensagens com `useInfiniteQuery`.
82
+
83
+ ### `useSendMessage()`
84
+
85
+ Envia mensagens de texto/imagem/template com optimistic update quando `conversationId` é informado.
86
+
87
+ ## Componentes
88
+
89
+ ### `<MessageList conversationId={...} />`
90
+
91
+ - renderiza mensagens da conversa
92
+ - busca próxima página ao rolar para o topo
93
+ - placeholder de estado vazio
94
+
95
+ ### `<Composer onSend={...} />`
96
+
97
+ - textarea com envio por Enter
98
+ - emoji picker simples
99
+ - seletor de anexo opcional
@@ -0,0 +1,15 @@
1
+ export type MessageListProps = {
2
+ conversationId: string;
3
+ pageSize?: number;
4
+ emptyState?: string;
5
+ height?: number | string;
6
+ };
7
+ export declare function MessageList({ conversationId, pageSize, emptyState, height, }: MessageListProps): import("react/jsx-runtime").JSX.Element;
8
+ export type ComposerProps = {
9
+ onSend: (text: string) => void | Promise<void>;
10
+ onAttachmentSelect?: (file: File) => void;
11
+ disabled?: boolean;
12
+ placeholder?: string;
13
+ emojiOptions?: string[];
14
+ };
15
+ export declare function Composer({ onSend, onAttachmentSelect, disabled, placeholder, emojiOptions, }: ComposerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import type { ConversationFilters, ListMessagesParams, MessageDto, PaginatedResult, SendMessageParams, SendTemplateParams } from '@omniflow/sdk';
2
+ export type UseSendMessageParams = SendMessageParams | SendTemplateParams;
3
+ export type UseSendMessageOptions = {
4
+ conversationId?: string;
5
+ };
6
+ export type UseMessagesOptions = ListMessagesParams;
7
+ type MutationContext = {
8
+ optimisticMessage?: MessageDto;
9
+ };
10
+ export declare function useOmniFlow(): import("@omniflow/sdk").OmniFlow;
11
+ export declare function useConversations(filters?: ConversationFilters): import("@tanstack/react-query").UseQueryResult<PaginatedResult<import("@omniflow/sdk").ConversationDto>, Error>;
12
+ export declare function useMessages(conversationId: string, options?: UseMessagesOptions): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/react-query").InfiniteData<PaginatedResult<MessageDto>, unknown>, Error>;
13
+ export declare function useSendMessage(options?: UseSendMessageOptions): import("@tanstack/react-query").UseMutationResult<MessageDto, Error, UseSendMessageParams, MutationContext>;
14
+ export {};
@@ -0,0 +1,7 @@
1
+ export { OmniFlowProvider, useOmniFlowContext } from './provider';
2
+ export { Composer, MessageList } from './components';
3
+ export { useConversations, useMessages, useOmniFlow, useSendMessage } from './hooks';
4
+ export { conversationsQueryKey, messagesQueryKey, messagesQueryRoot, OMNIFLOW_CONVERSATIONS_QUERY_ROOT, OMNIFLOW_QUERY_ROOT, } from './query-keys';
5
+ export type { OmniFlowProviderProps } from './provider';
6
+ export type { ComposerProps, MessageListProps } from './components';
7
+ export type { UseMessagesOptions, UseSendMessageOptions, UseSendMessageParams } from './hooks';
package/dist/index.js ADDED
@@ -0,0 +1,471 @@
1
+ // src/provider.tsx
2
+ import { OmniFlow } from "@omniflow/sdk";
3
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
+ import {
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ useRef,
9
+ useState
10
+ } from "react";
11
+
12
+ // src/query-keys.ts
13
+ var OMNIFLOW_QUERY_ROOT = ["omniflow"];
14
+ var OMNIFLOW_CONVERSATIONS_QUERY_ROOT = [...OMNIFLOW_QUERY_ROOT, "conversations"];
15
+ function conversationsQueryKey(filters = {}) {
16
+ return [...OMNIFLOW_CONVERSATIONS_QUERY_ROOT, filters];
17
+ }
18
+ function messagesQueryRoot(conversationId) {
19
+ return [...OMNIFLOW_QUERY_ROOT, "messages", conversationId];
20
+ }
21
+ function messagesQueryKey(conversationId, params = {}) {
22
+ return [...messagesQueryRoot(conversationId), params];
23
+ }
24
+
25
+ // src/provider.tsx
26
+ import { jsxDEV } from "react/jsx-dev-runtime";
27
+ var OmniFlowReactContext = createContext(null);
28
+ function resolveClient(props) {
29
+ if ("client" in props) {
30
+ if (!props.client) {
31
+ throw new Error("client is required");
32
+ }
33
+ return props.client;
34
+ }
35
+ return new OmniFlow(props.config);
36
+ }
37
+ function getRealtimeTarget(eventTarget) {
38
+ if (eventTarget)
39
+ return eventTarget;
40
+ if (typeof window !== "undefined")
41
+ return window;
42
+ return;
43
+ }
44
+ function OmniFlowProvider({
45
+ children,
46
+ queryClient: providedQueryClient,
47
+ eventTarget,
48
+ eventName = "omniflow:event",
49
+ ...props
50
+ }) {
51
+ const clientRef = useRef(null);
52
+ const client = clientRef.current ?? resolveClient(props);
53
+ clientRef.current = client;
54
+ const [queryClient] = useState(() => providedQueryClient ?? new QueryClient({
55
+ defaultOptions: {
56
+ queries: {
57
+ staleTime: 30000
58
+ }
59
+ }
60
+ }));
61
+ useEffect(() => {
62
+ const target = getRealtimeTarget(eventTarget);
63
+ if (!target)
64
+ return;
65
+ const handleRealtimeEvent = (event) => {
66
+ const detail = event instanceof CustomEvent ? event.detail : null;
67
+ if (!detail) {
68
+ return;
69
+ }
70
+ if (detail.type === "message.received") {
71
+ queryClient.invalidateQueries({ queryKey: OMNIFLOW_CONVERSATIONS_QUERY_ROOT });
72
+ if (detail.conversationId) {
73
+ queryClient.invalidateQueries({
74
+ queryKey: messagesQueryRoot(detail.conversationId)
75
+ });
76
+ }
77
+ return;
78
+ }
79
+ if (detail.type === "conversation.created" || detail.type === "conversation.resolved") {
80
+ queryClient.invalidateQueries({ queryKey: OMNIFLOW_CONVERSATIONS_QUERY_ROOT });
81
+ return;
82
+ }
83
+ queryClient.invalidateQueries({ queryKey: OMNIFLOW_QUERY_ROOT });
84
+ };
85
+ target.addEventListener(eventName, handleRealtimeEvent);
86
+ return () => {
87
+ target.removeEventListener(eventName, handleRealtimeEvent);
88
+ };
89
+ }, [eventName, eventTarget, queryClient]);
90
+ return /* @__PURE__ */ jsxDEV(OmniFlowReactContext.Provider, {
91
+ value: { client, queryClient },
92
+ children: /* @__PURE__ */ jsxDEV(QueryClientProvider, {
93
+ client: queryClient,
94
+ children
95
+ }, undefined, false, undefined, this)
96
+ }, undefined, false, undefined, this);
97
+ }
98
+ function useOmniFlowContext() {
99
+ const context = useContext(OmniFlowReactContext);
100
+ if (!context) {
101
+ throw new Error("OmniFlowProvider is required");
102
+ }
103
+ return context;
104
+ }
105
+ // src/components.tsx
106
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
107
+
108
+ // src/hooks.ts
109
+ import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
110
+ function isTemplateSend(params) {
111
+ return "templateId" in params;
112
+ }
113
+ function toMessageContent(params) {
114
+ if (isTemplateSend(params)) {
115
+ return {
116
+ type: "template",
117
+ templateId: params.templateId,
118
+ params: params.params
119
+ };
120
+ }
121
+ return params.content;
122
+ }
123
+ function buildOptimisticMessage(conversationId, params) {
124
+ return {
125
+ id: `optimistic-${Date.now()}`,
126
+ conversationId,
127
+ direction: "OUTBOUND",
128
+ content: toMessageContent(params),
129
+ createdAt: new Date().toISOString()
130
+ };
131
+ }
132
+ function useOmniFlow() {
133
+ return useOmniFlowContext().client;
134
+ }
135
+ function useConversations(filters = {}) {
136
+ const client = useOmniFlow();
137
+ return useQuery({
138
+ queryKey: conversationsQueryKey(filters),
139
+ queryFn: () => client.conversations.list(filters)
140
+ });
141
+ }
142
+ function useMessages(conversationId, options = {}) {
143
+ const client = useOmniFlow();
144
+ return useInfiniteQuery({
145
+ queryKey: messagesQueryKey(conversationId, options),
146
+ enabled: Boolean(conversationId),
147
+ initialPageParam: options.cursor,
148
+ queryFn: ({ pageParam }) => client.messages.list(conversationId, {
149
+ ...options,
150
+ ...pageParam ? { cursor: pageParam } : {}
151
+ }),
152
+ getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined
153
+ });
154
+ }
155
+ function useSendMessage(options = {}) {
156
+ const client = useOmniFlow();
157
+ const queryClient = useQueryClient();
158
+ return useMutation({
159
+ mutationFn: (params) => isTemplateSend(params) ? client.templates.send(params) : client.messages.send(params),
160
+ onMutate: async (params) => {
161
+ if (!options.conversationId) {
162
+ return {};
163
+ }
164
+ const key = messagesQueryRoot(options.conversationId);
165
+ await queryClient.cancelQueries({ queryKey: key });
166
+ const optimisticMessage = buildOptimisticMessage(options.conversationId, params);
167
+ queryClient.setQueriesData({ queryKey: key }, (current) => {
168
+ if (!current) {
169
+ return current;
170
+ }
171
+ return {
172
+ ...current,
173
+ pages: current.pages.map((page, index) => {
174
+ if (index !== 0) {
175
+ return page;
176
+ }
177
+ return {
178
+ ...page,
179
+ data: [optimisticMessage, ...page.data]
180
+ };
181
+ })
182
+ };
183
+ });
184
+ return { optimisticMessage };
185
+ },
186
+ onSuccess: (_message, _params) => {
187
+ queryClient.invalidateQueries({ queryKey: OMNIFLOW_CONVERSATIONS_QUERY_ROOT });
188
+ if (options.conversationId) {
189
+ queryClient.invalidateQueries({ queryKey: messagesQueryRoot(options.conversationId) });
190
+ }
191
+ },
192
+ onError: (_error, _params, context) => {
193
+ if (!options.conversationId || !context?.optimisticMessage) {
194
+ return;
195
+ }
196
+ queryClient.setQueriesData({ queryKey: messagesQueryRoot(options.conversationId) }, (current) => {
197
+ if (!current) {
198
+ return current;
199
+ }
200
+ return {
201
+ ...current,
202
+ pages: current.pages.map((page) => ({
203
+ ...page,
204
+ data: page.data.filter((message) => message.id !== context.optimisticMessage?.id)
205
+ }))
206
+ };
207
+ });
208
+ }
209
+ });
210
+ }
211
+
212
+ // src/components.tsx
213
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
214
+ var DEFAULT_EMOJIS = ["\uD83D\uDE00", "\uD83D\uDC4D", "\uD83C\uDF89", "❤️"];
215
+ function formatMessageContent(message) {
216
+ if (message.content.type === "text") {
217
+ return message.content.text;
218
+ }
219
+ if (message.content.type === "image") {
220
+ return message.content.caption || message.content.url;
221
+ }
222
+ return `Template: ${message.content.templateId}`;
223
+ }
224
+ function formatTime(iso) {
225
+ try {
226
+ return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
227
+ } catch {
228
+ return "";
229
+ }
230
+ }
231
+ function MessageList({
232
+ conversationId,
233
+ pageSize = 20,
234
+ emptyState = "Nenhuma mensagem ainda",
235
+ height = 360
236
+ }) {
237
+ const listRef = useRef2(null);
238
+ const { data, isPending, hasNextPage, fetchNextPage, isFetchingNextPage } = useMessages(conversationId, { limit: pageSize });
239
+ const messages = (data?.pages ?? []).flatMap((page) => page.data);
240
+ const messageCount = messages.length;
241
+ useEffect2(() => {
242
+ const element = listRef.current;
243
+ if (!element || messageCount === 0) {
244
+ return;
245
+ }
246
+ try {
247
+ element.scrollTo?.({ top: element.scrollHeight });
248
+ } catch {}
249
+ }, [messageCount]);
250
+ const handleScroll = () => {
251
+ const element = listRef.current;
252
+ if (!element || !hasNextPage || isFetchingNextPage) {
253
+ return;
254
+ }
255
+ if (element.scrollTop <= 32) {
256
+ fetchNextPage();
257
+ }
258
+ };
259
+ if (!conversationId) {
260
+ return /* @__PURE__ */ jsxDEV2("div", {
261
+ role: "log",
262
+ children: emptyState
263
+ }, undefined, false, undefined, this);
264
+ }
265
+ if (isPending) {
266
+ return /* @__PURE__ */ jsxDEV2("div", {
267
+ role: "log",
268
+ children: "Carregando mensagens..."
269
+ }, undefined, false, undefined, this);
270
+ }
271
+ if (messages.length === 0) {
272
+ return /* @__PURE__ */ jsxDEV2("div", {
273
+ role: "log",
274
+ children: emptyState
275
+ }, undefined, false, undefined, this);
276
+ }
277
+ return /* @__PURE__ */ jsxDEV2("div", {
278
+ ref: listRef,
279
+ role: "log",
280
+ "aria-live": "polite",
281
+ "data-omniflow-message-list": true,
282
+ onScroll: handleScroll,
283
+ style: {
284
+ display: "flex",
285
+ flexDirection: "column",
286
+ gap: "8px",
287
+ overflowY: "auto",
288
+ height,
289
+ padding: "12px",
290
+ border: "1px solid #e5e7eb",
291
+ borderRadius: "12px",
292
+ background: "#ffffff"
293
+ },
294
+ children: [
295
+ hasNextPage ? /* @__PURE__ */ jsxDEV2("div", {
296
+ style: { alignSelf: "center", color: "#6b7280", fontSize: "12px" },
297
+ children: isFetchingNextPage ? "Carregando mais..." : "Role para carregar mais"
298
+ }, undefined, false, undefined, this) : null,
299
+ messages.map((message) => {
300
+ const outbound = message.direction === "OUTBOUND";
301
+ return /* @__PURE__ */ jsxDEV2("div", {
302
+ style: {
303
+ display: "flex",
304
+ flexDirection: "column",
305
+ alignItems: outbound ? "flex-end" : "flex-start"
306
+ },
307
+ children: [
308
+ /* @__PURE__ */ jsxDEV2("div", {
309
+ style: {
310
+ maxWidth: "80%",
311
+ padding: "10px 12px",
312
+ borderRadius: outbound ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
313
+ background: outbound ? "#0f766e" : "#f3f4f6",
314
+ color: outbound ? "#ffffff" : "#111827",
315
+ lineHeight: "1.4",
316
+ wordBreak: "break-word"
317
+ },
318
+ children: formatMessageContent(message)
319
+ }, undefined, false, undefined, this),
320
+ /* @__PURE__ */ jsxDEV2("span", {
321
+ style: { marginTop: "4px", fontSize: "11px", color: "#6b7280" },
322
+ children: formatTime(message.createdAt)
323
+ }, undefined, false, undefined, this)
324
+ ]
325
+ }, message.id, true, undefined, this);
326
+ })
327
+ ]
328
+ }, undefined, true, undefined, this);
329
+ }
330
+ function Composer({
331
+ onSend,
332
+ onAttachmentSelect,
333
+ disabled = false,
334
+ placeholder = "Digite uma mensagem",
335
+ emojiOptions = DEFAULT_EMOJIS
336
+ }) {
337
+ const [text, setText] = useState2("");
338
+ const [emojiPickerOpen, setEmojiPickerOpen] = useState2(false);
339
+ const fileInputRef = useRef2(null);
340
+ const handleSubmit = async () => {
341
+ const trimmed = text.trim();
342
+ if (!trimmed || disabled) {
343
+ return;
344
+ }
345
+ await onSend(trimmed);
346
+ setText("");
347
+ };
348
+ const canSend = text.trim().length > 0 && !disabled;
349
+ return /* @__PURE__ */ jsxDEV2("div", {
350
+ style: {
351
+ display: "flex",
352
+ flexDirection: "column",
353
+ gap: "8px",
354
+ padding: "12px",
355
+ border: "1px solid #e5e7eb",
356
+ borderRadius: "12px",
357
+ background: "#ffffff"
358
+ },
359
+ children: [
360
+ emojiPickerOpen ? /* @__PURE__ */ jsxDEV2("div", {
361
+ style: { display: "flex", gap: "6px", flexWrap: "wrap" },
362
+ children: emojiOptions.map((emoji) => /* @__PURE__ */ jsxDEV2("button", {
363
+ type: "button",
364
+ "data-emoji": emoji,
365
+ onClick: () => {
366
+ setText((current) => `${current}${emoji}`);
367
+ setEmojiPickerOpen(false);
368
+ },
369
+ style: {
370
+ border: "1px solid #d1d5db",
371
+ borderRadius: "8px",
372
+ background: "#fff",
373
+ padding: "6px 8px",
374
+ cursor: "pointer"
375
+ },
376
+ children: emoji
377
+ }, emoji, false, undefined, this))
378
+ }, undefined, false, undefined, this) : null,
379
+ /* @__PURE__ */ jsxDEV2("div", {
380
+ style: { display: "flex", alignItems: "flex-end", gap: "8px" },
381
+ children: [
382
+ /* @__PURE__ */ jsxDEV2("button", {
383
+ type: "button",
384
+ "aria-label": "Adicionar emoji",
385
+ onClick: () => {
386
+ setEmojiPickerOpen((current) => !current);
387
+ },
388
+ children: "\uD83D\uDE42"
389
+ }, undefined, false, undefined, this),
390
+ /* @__PURE__ */ jsxDEV2("button", {
391
+ type: "button",
392
+ "aria-label": "Adicionar anexo",
393
+ onClick: () => {
394
+ fileInputRef.current?.click();
395
+ },
396
+ children: "+"
397
+ }, undefined, false, undefined, this),
398
+ /* @__PURE__ */ jsxDEV2("input", {
399
+ ref: fileInputRef,
400
+ type: "file",
401
+ style: { display: "none" },
402
+ onChange: (event) => {
403
+ const file = event.target.files?.[0];
404
+ if (!file) {
405
+ return;
406
+ }
407
+ onAttachmentSelect?.(file);
408
+ event.target.value = "";
409
+ }
410
+ }, undefined, false, undefined, this),
411
+ /* @__PURE__ */ jsxDEV2("textarea", {
412
+ value: text,
413
+ rows: 1,
414
+ placeholder,
415
+ disabled,
416
+ onInput: (event) => {
417
+ setText(event.target.value);
418
+ },
419
+ onKeyDown: (event) => {
420
+ if (event.key === "Enter" && !event.shiftKey) {
421
+ event.preventDefault();
422
+ handleSubmit();
423
+ }
424
+ },
425
+ style: {
426
+ flex: 1,
427
+ minHeight: "40px",
428
+ maxHeight: "120px",
429
+ resize: "vertical",
430
+ borderRadius: "10px",
431
+ border: "1px solid #d1d5db",
432
+ padding: "10px 12px",
433
+ fontFamily: "inherit"
434
+ }
435
+ }, undefined, false, undefined, this),
436
+ /* @__PURE__ */ jsxDEV2("button", {
437
+ type: "button",
438
+ "aria-label": "Enviar mensagem",
439
+ disabled: !canSend,
440
+ onClick: () => {
441
+ handleSubmit();
442
+ },
443
+ style: {
444
+ border: "none",
445
+ borderRadius: "9999px",
446
+ padding: "10px 14px",
447
+ background: canSend ? "#0f766e" : "#d1d5db",
448
+ color: "#ffffff"
449
+ },
450
+ children: "Enviar"
451
+ }, undefined, false, undefined, this)
452
+ ]
453
+ }, undefined, true, undefined, this)
454
+ ]
455
+ }, undefined, true, undefined, this);
456
+ }
457
+ export {
458
+ useSendMessage,
459
+ useOmniFlowContext,
460
+ useOmniFlow,
461
+ useMessages,
462
+ useConversations,
463
+ messagesQueryRoot,
464
+ messagesQueryKey,
465
+ conversationsQueryKey,
466
+ OmniFlowProvider,
467
+ OMNIFLOW_QUERY_ROOT,
468
+ OMNIFLOW_CONVERSATIONS_QUERY_ROOT,
469
+ MessageList,
470
+ Composer
471
+ };
@@ -0,0 +1,25 @@
1
+ import { OmniFlow, type OmniFlowConfig } from '@omniflow/sdk';
2
+ import { QueryClient } from '@tanstack/react-query';
3
+ import { type PropsWithChildren } from 'react';
4
+ type OmniFlowProviderWithConfigProps = {
5
+ config: OmniFlowConfig;
6
+ client?: never;
7
+ queryClient?: QueryClient;
8
+ eventTarget?: EventTarget;
9
+ eventName?: string;
10
+ };
11
+ type OmniFlowProviderWithClientProps = {
12
+ client: OmniFlow;
13
+ config?: never;
14
+ queryClient?: QueryClient;
15
+ eventTarget?: EventTarget;
16
+ eventName?: string;
17
+ };
18
+ export type OmniFlowProviderProps = PropsWithChildren<OmniFlowProviderWithConfigProps | OmniFlowProviderWithClientProps>;
19
+ type OmniFlowReactContextValue = {
20
+ client: OmniFlow;
21
+ queryClient: QueryClient;
22
+ };
23
+ export declare function OmniFlowProvider({ children, queryClient: providedQueryClient, eventTarget, eventName, ...props }: OmniFlowProviderProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare function useOmniFlowContext(): OmniFlowReactContextValue;
25
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { ConversationFilters, ListMessagesParams } from '@omniflow/sdk';
2
+ export declare const OMNIFLOW_QUERY_ROOT: readonly ["omniflow"];
3
+ export declare const OMNIFLOW_CONVERSATIONS_QUERY_ROOT: readonly ["omniflow", "conversations"];
4
+ export declare function conversationsQueryKey(filters?: ConversationFilters): readonly ["omniflow", "conversations", ConversationFilters];
5
+ export declare function messagesQueryRoot(conversationId: string): readonly ["omniflow", "messages", string];
6
+ export declare function messagesQueryKey(conversationId: string, params?: ListMessagesParams): readonly ["omniflow", "messages", string, ListMessagesParams];
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@ominiflow/sdk-react",
3
+ "version": "0.2.0-rc.0",
4
+ "description": "React bindings for the OmniFlow TypeScript SDK",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "scripts": {
17
+ "pretest": "bun run --cwd ../sdk build",
18
+ "build": "bun build ./src/index.ts --outdir dist --format esm --external react --external react-dom --external react/jsx-runtime --external @tanstack/react-query --external @omniflow/sdk && tsc --emitDeclarationOnly --declaration --declarationMap false --outDir dist",
19
+ "prebuild": "bun run --cwd ../sdk build",
20
+ "prestorybook": "bun run --cwd ../sdk build",
21
+ "storybook": "storybook dev -p 6006",
22
+ "prebuild-storybook": "bun run --cwd ../sdk build",
23
+ "build-storybook": "storybook build --output-dir storybook-static",
24
+ "typecheck": "tsc --noEmit",
25
+ "test": "bun test",
26
+ "lint": "biome check ."
27
+ },
28
+ "peerDependencies": {
29
+ "@tanstack/react-query": "^5.0.0",
30
+ "react": "^18.0.0 || ^19.0.0",
31
+ "react-dom": "^18.0.0 || ^19.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@ominiflow/sdk": "workspace:*"
35
+ },
36
+ "devDependencies": {
37
+ "@happy-dom/global-registrator": "^14.0.0",
38
+ "@storybook/addon-a11y": "^10.3.5",
39
+ "@storybook/addon-docs": "^10.3.5",
40
+ "@storybook/react-vite": "^10.3.5",
41
+ "@tanstack/react-query": "^5.0.0",
42
+ "@types/bun": "latest",
43
+ "@types/react": "^19.0.0",
44
+ "@types/react-dom": "^19.0.0",
45
+ "@vitejs/plugin-react": "^5.0.0",
46
+ "react": "^19.0.0",
47
+ "react-dom": "^19.0.0",
48
+ "storybook": "^10.3.5",
49
+ "vite": "^6.0.0"
50
+ },
51
+ "sideEffects": false
52
+ }