@blinkdotnew/react 1.0.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.
@@ -0,0 +1,186 @@
1
+ import { BlinkClient, Agent, UIMessage, AgentResponse } from '@blinkdotnew/sdk';
2
+ export * from '@blinkdotnew/sdk';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ReactNode } from 'react';
5
+
6
+ interface BlinkContextValue {
7
+ client: BlinkClient;
8
+ projectId: string;
9
+ }
10
+ interface BlinkProviderProps {
11
+ /** Your Blink project ID */
12
+ projectId: string;
13
+ /** Publishable key for client-side usage */
14
+ publishableKey?: string;
15
+ /** Secret key for server-side usage (do not expose in browser!) */
16
+ secretKey?: string;
17
+ /** Children components */
18
+ children: ReactNode;
19
+ }
20
+ /**
21
+ * Provides Blink client to all child components
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * import { BlinkProvider } from '@blinkdotnew/react'
26
+ *
27
+ * function App() {
28
+ * return (
29
+ * <BlinkProvider
30
+ * projectId="proj_abc123"
31
+ * publishableKey="blnk_pk_..."
32
+ * >
33
+ * <MyApp />
34
+ * </BlinkProvider>
35
+ * )
36
+ * }
37
+ * ```
38
+ */
39
+ declare function BlinkProvider({ projectId, publishableKey, secretKey, children, }: BlinkProviderProps): react_jsx_runtime.JSX.Element;
40
+ /**
41
+ * Get the Blink client from context
42
+ *
43
+ * @throws If used outside BlinkProvider
44
+ */
45
+ declare function useBlink(): BlinkContextValue;
46
+ /**
47
+ * Get the Blink client instance directly
48
+ *
49
+ * @throws If used outside BlinkProvider
50
+ */
51
+ declare function useBlinkClient(): BlinkClient;
52
+ interface BlinkAuthContextValue {
53
+ isAuthenticated: boolean;
54
+ isLoading: boolean;
55
+ user: any | null;
56
+ signIn: (email: string, password: string) => Promise<void>;
57
+ signUp: (email: string, password: string) => Promise<void>;
58
+ signOut: () => Promise<void>;
59
+ }
60
+ interface BlinkAuthProviderProps {
61
+ children: ReactNode;
62
+ }
63
+ /**
64
+ * Provides authentication state to all child components
65
+ *
66
+ * Must be nested inside BlinkProvider
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * import { BlinkProvider, BlinkAuthProvider } from '@blinkdotnew/react'
71
+ *
72
+ * function App() {
73
+ * return (
74
+ * <BlinkProvider projectId="proj_abc123" publishableKey="blnk_pk_...">
75
+ * <BlinkAuthProvider>
76
+ * <MyApp />
77
+ * </BlinkAuthProvider>
78
+ * </BlinkProvider>
79
+ * )
80
+ * }
81
+ * ```
82
+ */
83
+ declare function BlinkAuthProvider({ children }: BlinkAuthProviderProps): react_jsx_runtime.JSX.Element;
84
+ /**
85
+ * Get authentication state and methods
86
+ *
87
+ * @throws If used outside BlinkAuthProvider
88
+ */
89
+ declare function useBlinkAuth(): BlinkAuthContextValue;
90
+
91
+ interface UseAgentOptions {
92
+ /**
93
+ * Agent instance created with `new Agent()`
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * const myAgent = new Agent({
98
+ * model: 'openai/gpt-4o',
99
+ * tools: [webSearch],
100
+ * })
101
+ *
102
+ * const chat = useAgent({ agent: myAgent })
103
+ * ```
104
+ */
105
+ agent: Agent;
106
+ /** Initial messages */
107
+ initialMessages?: UIMessage[];
108
+ /** Callback when response is received */
109
+ onFinish?: (response: AgentResponse) => void;
110
+ /** Callback on error */
111
+ onError?: (error: Error) => void;
112
+ }
113
+ /**
114
+ * Options for addToolOutput
115
+ */
116
+ interface AddToolOutputOptions {
117
+ toolCallId: string;
118
+ tool: string;
119
+ output: any;
120
+ }
121
+ interface UseAgentReturn {
122
+ /** Current messages */
123
+ messages: UIMessage[];
124
+ /** Current input value */
125
+ input: string;
126
+ /** Set input value */
127
+ setInput: (input: string) => void;
128
+ /** Whether the agent is processing */
129
+ isLoading: boolean;
130
+ /** Any error that occurred */
131
+ error: Error | null;
132
+ /** Send a message */
133
+ sendMessage: (content?: string) => Promise<void>;
134
+ /** Append a message without sending */
135
+ append: (message: UIMessage) => void;
136
+ /** Handle input change (for controlled inputs) */
137
+ handleInputChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
138
+ /** Handle form submit */
139
+ handleSubmit: (e?: React.FormEvent) => void;
140
+ /** Add tool output for HITL (Human-in-the-Loop) */
141
+ addToolOutput: (options: AddToolOutputOptions) => void;
142
+ /** Clear messages */
143
+ clearMessages: () => void;
144
+ /** Stop generation */
145
+ stop: () => void;
146
+ /** Reload last message */
147
+ reload: () => Promise<void>;
148
+ }
149
+ /**
150
+ * React hook for AI agent chat functionality
151
+ *
152
+ * Compatible with AI SDK's useChat pattern but powered by Blink.
153
+ *
154
+ * **Official usage pattern:**
155
+ * 1. Create agent: `const myAgent = new Agent({...})`
156
+ * 2. Use in hook: `useAgent({ agent: myAgent })`
157
+ *
158
+ * @example
159
+ * ```tsx
160
+ * import { Agent, useAgent, webSearch, dbTools } from '@blinkdotnew/react'
161
+ *
162
+ * // 1. Define agent (can be shared across components)
163
+ * const myAgent = new Agent({
164
+ * model: 'openai/gpt-4o',
165
+ * system: 'You are a helpful assistant.',
166
+ * tools: [webSearch, ...dbTools],
167
+ * })
168
+ *
169
+ * // 2. Use in component
170
+ * function Chat() {
171
+ * const { messages, input, handleInputChange, handleSubmit, isLoading } = useAgent({
172
+ * agent: myAgent,
173
+ * })
174
+ *
175
+ * return (
176
+ * <form onSubmit={handleSubmit}>
177
+ * <input value={input} onChange={handleInputChange} />
178
+ * <button type="submit" disabled={isLoading}>Send</button>
179
+ * </form>
180
+ * )
181
+ * }
182
+ * ```
183
+ */
184
+ declare function useAgent(options: UseAgentOptions): UseAgentReturn;
185
+
186
+ export { type AddToolOutputOptions, BlinkAuthProvider, type BlinkAuthProviderProps, BlinkProvider, type BlinkProviderProps, type UseAgentOptions, type UseAgentReturn, useAgent, useBlink, useBlinkAuth, useBlinkClient };
@@ -0,0 +1,186 @@
1
+ import { BlinkClient, Agent, UIMessage, AgentResponse } from '@blinkdotnew/sdk';
2
+ export * from '@blinkdotnew/sdk';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ReactNode } from 'react';
5
+
6
+ interface BlinkContextValue {
7
+ client: BlinkClient;
8
+ projectId: string;
9
+ }
10
+ interface BlinkProviderProps {
11
+ /** Your Blink project ID */
12
+ projectId: string;
13
+ /** Publishable key for client-side usage */
14
+ publishableKey?: string;
15
+ /** Secret key for server-side usage (do not expose in browser!) */
16
+ secretKey?: string;
17
+ /** Children components */
18
+ children: ReactNode;
19
+ }
20
+ /**
21
+ * Provides Blink client to all child components
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * import { BlinkProvider } from '@blinkdotnew/react'
26
+ *
27
+ * function App() {
28
+ * return (
29
+ * <BlinkProvider
30
+ * projectId="proj_abc123"
31
+ * publishableKey="blnk_pk_..."
32
+ * >
33
+ * <MyApp />
34
+ * </BlinkProvider>
35
+ * )
36
+ * }
37
+ * ```
38
+ */
39
+ declare function BlinkProvider({ projectId, publishableKey, secretKey, children, }: BlinkProviderProps): react_jsx_runtime.JSX.Element;
40
+ /**
41
+ * Get the Blink client from context
42
+ *
43
+ * @throws If used outside BlinkProvider
44
+ */
45
+ declare function useBlink(): BlinkContextValue;
46
+ /**
47
+ * Get the Blink client instance directly
48
+ *
49
+ * @throws If used outside BlinkProvider
50
+ */
51
+ declare function useBlinkClient(): BlinkClient;
52
+ interface BlinkAuthContextValue {
53
+ isAuthenticated: boolean;
54
+ isLoading: boolean;
55
+ user: any | null;
56
+ signIn: (email: string, password: string) => Promise<void>;
57
+ signUp: (email: string, password: string) => Promise<void>;
58
+ signOut: () => Promise<void>;
59
+ }
60
+ interface BlinkAuthProviderProps {
61
+ children: ReactNode;
62
+ }
63
+ /**
64
+ * Provides authentication state to all child components
65
+ *
66
+ * Must be nested inside BlinkProvider
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * import { BlinkProvider, BlinkAuthProvider } from '@blinkdotnew/react'
71
+ *
72
+ * function App() {
73
+ * return (
74
+ * <BlinkProvider projectId="proj_abc123" publishableKey="blnk_pk_...">
75
+ * <BlinkAuthProvider>
76
+ * <MyApp />
77
+ * </BlinkAuthProvider>
78
+ * </BlinkProvider>
79
+ * )
80
+ * }
81
+ * ```
82
+ */
83
+ declare function BlinkAuthProvider({ children }: BlinkAuthProviderProps): react_jsx_runtime.JSX.Element;
84
+ /**
85
+ * Get authentication state and methods
86
+ *
87
+ * @throws If used outside BlinkAuthProvider
88
+ */
89
+ declare function useBlinkAuth(): BlinkAuthContextValue;
90
+
91
+ interface UseAgentOptions {
92
+ /**
93
+ * Agent instance created with `new Agent()`
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * const myAgent = new Agent({
98
+ * model: 'openai/gpt-4o',
99
+ * tools: [webSearch],
100
+ * })
101
+ *
102
+ * const chat = useAgent({ agent: myAgent })
103
+ * ```
104
+ */
105
+ agent: Agent;
106
+ /** Initial messages */
107
+ initialMessages?: UIMessage[];
108
+ /** Callback when response is received */
109
+ onFinish?: (response: AgentResponse) => void;
110
+ /** Callback on error */
111
+ onError?: (error: Error) => void;
112
+ }
113
+ /**
114
+ * Options for addToolOutput
115
+ */
116
+ interface AddToolOutputOptions {
117
+ toolCallId: string;
118
+ tool: string;
119
+ output: any;
120
+ }
121
+ interface UseAgentReturn {
122
+ /** Current messages */
123
+ messages: UIMessage[];
124
+ /** Current input value */
125
+ input: string;
126
+ /** Set input value */
127
+ setInput: (input: string) => void;
128
+ /** Whether the agent is processing */
129
+ isLoading: boolean;
130
+ /** Any error that occurred */
131
+ error: Error | null;
132
+ /** Send a message */
133
+ sendMessage: (content?: string) => Promise<void>;
134
+ /** Append a message without sending */
135
+ append: (message: UIMessage) => void;
136
+ /** Handle input change (for controlled inputs) */
137
+ handleInputChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
138
+ /** Handle form submit */
139
+ handleSubmit: (e?: React.FormEvent) => void;
140
+ /** Add tool output for HITL (Human-in-the-Loop) */
141
+ addToolOutput: (options: AddToolOutputOptions) => void;
142
+ /** Clear messages */
143
+ clearMessages: () => void;
144
+ /** Stop generation */
145
+ stop: () => void;
146
+ /** Reload last message */
147
+ reload: () => Promise<void>;
148
+ }
149
+ /**
150
+ * React hook for AI agent chat functionality
151
+ *
152
+ * Compatible with AI SDK's useChat pattern but powered by Blink.
153
+ *
154
+ * **Official usage pattern:**
155
+ * 1. Create agent: `const myAgent = new Agent({...})`
156
+ * 2. Use in hook: `useAgent({ agent: myAgent })`
157
+ *
158
+ * @example
159
+ * ```tsx
160
+ * import { Agent, useAgent, webSearch, dbTools } from '@blinkdotnew/react'
161
+ *
162
+ * // 1. Define agent (can be shared across components)
163
+ * const myAgent = new Agent({
164
+ * model: 'openai/gpt-4o',
165
+ * system: 'You are a helpful assistant.',
166
+ * tools: [webSearch, ...dbTools],
167
+ * })
168
+ *
169
+ * // 2. Use in component
170
+ * function Chat() {
171
+ * const { messages, input, handleInputChange, handleSubmit, isLoading } = useAgent({
172
+ * agent: myAgent,
173
+ * })
174
+ *
175
+ * return (
176
+ * <form onSubmit={handleSubmit}>
177
+ * <input value={input} onChange={handleInputChange} />
178
+ * <button type="submit" disabled={isLoading}>Send</button>
179
+ * </form>
180
+ * )
181
+ * }
182
+ * ```
183
+ */
184
+ declare function useAgent(options: UseAgentOptions): UseAgentReturn;
185
+
186
+ export { type AddToolOutputOptions, BlinkAuthProvider, type BlinkAuthProviderProps, BlinkProvider, type BlinkProviderProps, type UseAgentOptions, type UseAgentReturn, useAgent, useBlink, useBlinkAuth, useBlinkClient };
package/dist/index.js ADDED
@@ -0,0 +1,400 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/index.ts
32
+ var index_exports = {};
33
+ __export(index_exports, {
34
+ BlinkAuthProvider: () => BlinkAuthProvider,
35
+ BlinkProvider: () => BlinkProvider,
36
+ useAgent: () => useAgent,
37
+ useBlink: () => useBlink,
38
+ useBlinkAuth: () => useBlinkAuth,
39
+ useBlinkClient: () => useBlinkClient
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+ __reExport(index_exports, require("@blinkdotnew/sdk"), module.exports);
43
+
44
+ // src/context.tsx
45
+ var import_react = __toESM(require("react"));
46
+ var import_sdk = require("@blinkdotnew/sdk");
47
+ var import_jsx_runtime = require("react/jsx-runtime");
48
+ var BlinkContext = (0, import_react.createContext)(null);
49
+ function BlinkProvider({
50
+ projectId,
51
+ publishableKey,
52
+ secretKey,
53
+ children
54
+ }) {
55
+ const client = (0, import_react.useMemo)(
56
+ () => (0, import_sdk.createClient)({
57
+ projectId,
58
+ publishableKey,
59
+ secretKey
60
+ }),
61
+ [projectId, publishableKey, secretKey]
62
+ );
63
+ const value = (0, import_react.useMemo)(
64
+ () => ({
65
+ client,
66
+ projectId
67
+ }),
68
+ [client, projectId]
69
+ );
70
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlinkContext.Provider, { value, children });
71
+ }
72
+ function useBlink() {
73
+ const context = (0, import_react.useContext)(BlinkContext);
74
+ if (!context) {
75
+ throw new Error("useBlink must be used within a BlinkProvider");
76
+ }
77
+ return context;
78
+ }
79
+ function useBlinkClient() {
80
+ return useBlink().client;
81
+ }
82
+ var BlinkAuthContext = (0, import_react.createContext)(null);
83
+ function BlinkAuthProvider({ children }) {
84
+ const { client } = useBlink();
85
+ const [isLoading, setIsLoading] = import_react.default.useState(true);
86
+ const [user, setUser] = import_react.default.useState(null);
87
+ import_react.default.useEffect(() => {
88
+ let mounted = true;
89
+ const initAuth = async () => {
90
+ try {
91
+ const currentUser = await client.auth.me();
92
+ if (mounted) {
93
+ setUser(currentUser);
94
+ }
95
+ } catch {
96
+ if (mounted) {
97
+ setUser(null);
98
+ }
99
+ } finally {
100
+ if (mounted) {
101
+ setIsLoading(false);
102
+ }
103
+ }
104
+ };
105
+ initAuth();
106
+ const unsubscribe = client.auth.onAuthStateChanged((state) => {
107
+ setUser(state.user);
108
+ });
109
+ return () => {
110
+ mounted = false;
111
+ unsubscribe();
112
+ };
113
+ }, [client]);
114
+ const signIn = import_react.default.useCallback(
115
+ async (email, password) => {
116
+ const result = await client.auth.signInWithEmail(email, password);
117
+ setUser(result);
118
+ },
119
+ [client]
120
+ );
121
+ const signUp = import_react.default.useCallback(
122
+ async (email, password) => {
123
+ const result = await client.auth.signUp({ email, password });
124
+ setUser(result);
125
+ },
126
+ [client]
127
+ );
128
+ const signOut = import_react.default.useCallback(async () => {
129
+ await client.auth.signOut();
130
+ setUser(null);
131
+ }, [client]);
132
+ const value = (0, import_react.useMemo)(
133
+ () => ({
134
+ isAuthenticated: !!user,
135
+ isLoading,
136
+ user,
137
+ signIn,
138
+ signUp,
139
+ signOut
140
+ }),
141
+ [isLoading, user, signIn, signUp, signOut]
142
+ );
143
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlinkAuthContext.Provider, { value, children });
144
+ }
145
+ function useBlinkAuth() {
146
+ const context = (0, import_react.useContext)(BlinkAuthContext);
147
+ if (!context) {
148
+ throw new Error("useBlinkAuth must be used within a BlinkAuthProvider");
149
+ }
150
+ return context;
151
+ }
152
+
153
+ // src/use-agent.ts
154
+ var import_react2 = require("react");
155
+ function useAgent(options) {
156
+ const client = useBlinkClient();
157
+ const { agent: agentInput, initialMessages = [], onFinish, onError } = options;
158
+ const [messages, setMessages] = (0, import_react2.useState)(initialMessages);
159
+ const [input, setInput] = (0, import_react2.useState)("");
160
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
161
+ const [error, setError] = (0, import_react2.useState)(null);
162
+ const abortControllerRef = (0, import_react2.useRef)(null);
163
+ const agent = (0, import_react2.useMemo)(() => {
164
+ return client.ai.bindAgent(agentInput);
165
+ }, [agentInput, client]);
166
+ const agentModel = agent.model;
167
+ const generateId = (0, import_react2.useCallback)(() => {
168
+ return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
169
+ }, []);
170
+ const parseStream = (0, import_react2.useCallback)(
171
+ async (response, assistantMessageId) => {
172
+ if (!response.body) {
173
+ throw new Error("No response body");
174
+ }
175
+ const reader = response.body.getReader();
176
+ const decoder = new TextDecoder();
177
+ let buffer = "";
178
+ let currentText = "";
179
+ let currentParts = [];
180
+ let finishData = null;
181
+ try {
182
+ while (true) {
183
+ const { done, value } = await reader.read();
184
+ if (done) break;
185
+ buffer += decoder.decode(value, { stream: true });
186
+ const lines = buffer.split("\n");
187
+ buffer = lines.pop() || "";
188
+ for (const line of lines) {
189
+ const trimmedLine = line.trim();
190
+ if (!trimmedLine || !trimmedLine.startsWith("data:")) continue;
191
+ if (trimmedLine === "data: [DONE]" || trimmedLine === "data:[DONE]") continue;
192
+ try {
193
+ const jsonStr = trimmedLine.startsWith("data: ") ? trimmedLine.slice(6) : trimmedLine.slice(5);
194
+ if (!jsonStr.trim()) continue;
195
+ const json = JSON.parse(jsonStr);
196
+ switch (json.type) {
197
+ // Handle text events (AI SDK v5 uses 'text-delta' with 'delta' property)
198
+ case "text-delta":
199
+ case "text": {
200
+ const textChunk = json.delta || json.textDelta || json.value || json.text || "";
201
+ currentText += textChunk;
202
+ setMessages(
203
+ (prev) => prev.map(
204
+ (m) => m.id === assistantMessageId ? { ...m, content: currentText, parts: [...currentParts.filter((p) => p.type !== "text"), { type: "text", text: currentText }] } : m
205
+ )
206
+ );
207
+ break;
208
+ }
209
+ // Handle tool call events
210
+ case "tool-call":
211
+ case "tool-call-start": {
212
+ const existingTool = currentParts.find(
213
+ (p) => p.type === "tool-invocation" && p.toolCallId === json.toolCallId
214
+ );
215
+ if (!existingTool) {
216
+ const toolPart = {
217
+ type: "tool-invocation",
218
+ toolCallId: json.toolCallId,
219
+ toolName: json.toolName,
220
+ state: "pending",
221
+ input: json.args || {}
222
+ };
223
+ currentParts = [...currentParts.filter((p) => p.type !== "text"), toolPart];
224
+ setMessages(
225
+ (prev) => prev.map(
226
+ (m) => m.id === assistantMessageId ? { ...m, parts: [...currentParts, { type: "text", text: currentText }] } : m
227
+ )
228
+ );
229
+ }
230
+ break;
231
+ }
232
+ // Handle tool result events
233
+ case "tool-result": {
234
+ currentParts = currentParts.map(
235
+ (p) => p.type === "tool-invocation" && p.toolCallId === json.toolCallId ? { ...p, state: "result", output: json.result } : p
236
+ );
237
+ setMessages(
238
+ (prev) => prev.map(
239
+ (m) => m.id === assistantMessageId ? { ...m, parts: [...currentParts.filter((p) => p.type !== "text"), { type: "text", text: currentText }] } : m
240
+ )
241
+ );
242
+ break;
243
+ }
244
+ // Handle finish event
245
+ case "finish":
246
+ case "done": {
247
+ finishData = json;
248
+ break;
249
+ }
250
+ // Ignore other event types (start, text-start, text-end, etc.)
251
+ default:
252
+ break;
253
+ }
254
+ } catch {
255
+ }
256
+ }
257
+ }
258
+ } finally {
259
+ reader.releaseLock();
260
+ }
261
+ return { text: currentText, finishData };
262
+ },
263
+ []
264
+ );
265
+ const sendMessage = (0, import_react2.useCallback)(
266
+ async (content) => {
267
+ const messageContent = content ?? input;
268
+ if (!messageContent.trim() && messages.length === 0) return;
269
+ setError(null);
270
+ setIsLoading(true);
271
+ abortControllerRef.current = new AbortController();
272
+ try {
273
+ let newMessages = [...messages];
274
+ if (messageContent.trim()) {
275
+ const userMessage = {
276
+ id: generateId(),
277
+ role: "user",
278
+ content: messageContent.trim()
279
+ };
280
+ newMessages = [...newMessages, userMessage];
281
+ setMessages(newMessages);
282
+ setInput("");
283
+ }
284
+ const assistantMessageId = generateId();
285
+ const assistantMessage = {
286
+ id: assistantMessageId,
287
+ role: "assistant",
288
+ content: "",
289
+ parts: []
290
+ };
291
+ setMessages([...newMessages, assistantMessage]);
292
+ const response = await agent.stream({
293
+ messages: newMessages,
294
+ signal: abortControllerRef.current.signal
295
+ });
296
+ const { text, finishData } = await parseStream(response, assistantMessageId);
297
+ setMessages(
298
+ (prev) => prev.map(
299
+ (m) => m.id === assistantMessageId ? { ...m, content: text } : m
300
+ )
301
+ );
302
+ if (onFinish && finishData) {
303
+ onFinish({
304
+ text,
305
+ finishReason: finishData.finishReason || "stop",
306
+ steps: [],
307
+ usage: finishData.usage || { inputTokens: 0, outputTokens: 0 },
308
+ _billing: finishData._billing || { model: agentModel, creditsCharged: 0, costUSD: 0 }
309
+ });
310
+ }
311
+ } catch (err) {
312
+ const error2 = err instanceof Error ? err : new Error(String(err));
313
+ if (error2.name !== "AbortError") {
314
+ setError(error2);
315
+ onError?.(error2);
316
+ }
317
+ } finally {
318
+ setIsLoading(false);
319
+ abortControllerRef.current = null;
320
+ }
321
+ },
322
+ [agent, agentModel, messages, input, generateId, parseStream, onFinish, onError]
323
+ );
324
+ const append = (0, import_react2.useCallback)((message) => {
325
+ setMessages((prev) => [...prev, { ...message, id: message.id || generateId() }]);
326
+ }, [generateId]);
327
+ const handleInputChange = (0, import_react2.useCallback)(
328
+ (e) => {
329
+ setInput(e.target.value);
330
+ },
331
+ []
332
+ );
333
+ const handleSubmit = (0, import_react2.useCallback)(
334
+ (e) => {
335
+ e?.preventDefault();
336
+ sendMessage();
337
+ },
338
+ [sendMessage]
339
+ );
340
+ const addToolOutput = (0, import_react2.useCallback)(
341
+ ({ toolCallId, tool, output }) => {
342
+ setMessages(
343
+ (prev) => prev.map((m) => {
344
+ if (m.role !== "assistant" || !m.parts) return m;
345
+ const updatedParts = m.parts.map(
346
+ (p) => p.type === "tool-invocation" && p.toolCallId === toolCallId && p.toolName === tool ? { ...p, state: "output-available", output } : p
347
+ );
348
+ return { ...m, parts: updatedParts };
349
+ })
350
+ );
351
+ },
352
+ []
353
+ );
354
+ const clearMessages = (0, import_react2.useCallback)(() => {
355
+ setMessages(initialMessages);
356
+ setError(null);
357
+ }, [initialMessages]);
358
+ const stop = (0, import_react2.useCallback)(() => {
359
+ abortControllerRef.current?.abort();
360
+ }, []);
361
+ const reload = (0, import_react2.useCallback)(async () => {
362
+ let lastUserIndex = -1;
363
+ for (let i = messages.length - 1; i >= 0; i--) {
364
+ if (messages[i].role === "user") {
365
+ lastUserIndex = i;
366
+ break;
367
+ }
368
+ }
369
+ if (lastUserIndex === -1) return;
370
+ const messagesUpToLastUser = messages.slice(0, lastUserIndex + 1);
371
+ setMessages(messagesUpToLastUser);
372
+ await sendMessage();
373
+ }, [messages, sendMessage]);
374
+ return {
375
+ messages,
376
+ input,
377
+ setInput,
378
+ isLoading,
379
+ error,
380
+ sendMessage,
381
+ append,
382
+ handleInputChange,
383
+ handleSubmit,
384
+ addToolOutput,
385
+ clearMessages,
386
+ stop,
387
+ reload
388
+ };
389
+ }
390
+ // Annotate the CommonJS export names for ESM import in node:
391
+ 0 && (module.exports = {
392
+ BlinkAuthProvider,
393
+ BlinkProvider,
394
+ useAgent,
395
+ useBlink,
396
+ useBlinkAuth,
397
+ useBlinkClient,
398
+ ...require("@blinkdotnew/sdk")
399
+ });
400
+ //# sourceMappingURL=index.js.map
package/dist/index.mjs ADDED
@@ -0,0 +1,358 @@
1
+ // src/index.ts
2
+ export * from "@blinkdotnew/sdk";
3
+
4
+ // src/context.tsx
5
+ import React, { createContext, useContext, useMemo } from "react";
6
+ import { createClient } from "@blinkdotnew/sdk";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var BlinkContext = createContext(null);
9
+ function BlinkProvider({
10
+ projectId,
11
+ publishableKey,
12
+ secretKey,
13
+ children
14
+ }) {
15
+ const client = useMemo(
16
+ () => createClient({
17
+ projectId,
18
+ publishableKey,
19
+ secretKey
20
+ }),
21
+ [projectId, publishableKey, secretKey]
22
+ );
23
+ const value = useMemo(
24
+ () => ({
25
+ client,
26
+ projectId
27
+ }),
28
+ [client, projectId]
29
+ );
30
+ return /* @__PURE__ */ jsx(BlinkContext.Provider, { value, children });
31
+ }
32
+ function useBlink() {
33
+ const context = useContext(BlinkContext);
34
+ if (!context) {
35
+ throw new Error("useBlink must be used within a BlinkProvider");
36
+ }
37
+ return context;
38
+ }
39
+ function useBlinkClient() {
40
+ return useBlink().client;
41
+ }
42
+ var BlinkAuthContext = createContext(null);
43
+ function BlinkAuthProvider({ children }) {
44
+ const { client } = useBlink();
45
+ const [isLoading, setIsLoading] = React.useState(true);
46
+ const [user, setUser] = React.useState(null);
47
+ React.useEffect(() => {
48
+ let mounted = true;
49
+ const initAuth = async () => {
50
+ try {
51
+ const currentUser = await client.auth.me();
52
+ if (mounted) {
53
+ setUser(currentUser);
54
+ }
55
+ } catch {
56
+ if (mounted) {
57
+ setUser(null);
58
+ }
59
+ } finally {
60
+ if (mounted) {
61
+ setIsLoading(false);
62
+ }
63
+ }
64
+ };
65
+ initAuth();
66
+ const unsubscribe = client.auth.onAuthStateChanged((state) => {
67
+ setUser(state.user);
68
+ });
69
+ return () => {
70
+ mounted = false;
71
+ unsubscribe();
72
+ };
73
+ }, [client]);
74
+ const signIn = React.useCallback(
75
+ async (email, password) => {
76
+ const result = await client.auth.signInWithEmail(email, password);
77
+ setUser(result);
78
+ },
79
+ [client]
80
+ );
81
+ const signUp = React.useCallback(
82
+ async (email, password) => {
83
+ const result = await client.auth.signUp({ email, password });
84
+ setUser(result);
85
+ },
86
+ [client]
87
+ );
88
+ const signOut = React.useCallback(async () => {
89
+ await client.auth.signOut();
90
+ setUser(null);
91
+ }, [client]);
92
+ const value = useMemo(
93
+ () => ({
94
+ isAuthenticated: !!user,
95
+ isLoading,
96
+ user,
97
+ signIn,
98
+ signUp,
99
+ signOut
100
+ }),
101
+ [isLoading, user, signIn, signUp, signOut]
102
+ );
103
+ return /* @__PURE__ */ jsx(BlinkAuthContext.Provider, { value, children });
104
+ }
105
+ function useBlinkAuth() {
106
+ const context = useContext(BlinkAuthContext);
107
+ if (!context) {
108
+ throw new Error("useBlinkAuth must be used within a BlinkAuthProvider");
109
+ }
110
+ return context;
111
+ }
112
+
113
+ // src/use-agent.ts
114
+ import { useCallback, useRef, useState, useMemo as useMemo2 } from "react";
115
+ function useAgent(options) {
116
+ const client = useBlinkClient();
117
+ const { agent: agentInput, initialMessages = [], onFinish, onError } = options;
118
+ const [messages, setMessages] = useState(initialMessages);
119
+ const [input, setInput] = useState("");
120
+ const [isLoading, setIsLoading] = useState(false);
121
+ const [error, setError] = useState(null);
122
+ const abortControllerRef = useRef(null);
123
+ const agent = useMemo2(() => {
124
+ return client.ai.bindAgent(agentInput);
125
+ }, [agentInput, client]);
126
+ const agentModel = agent.model;
127
+ const generateId = useCallback(() => {
128
+ return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
129
+ }, []);
130
+ const parseStream = useCallback(
131
+ async (response, assistantMessageId) => {
132
+ if (!response.body) {
133
+ throw new Error("No response body");
134
+ }
135
+ const reader = response.body.getReader();
136
+ const decoder = new TextDecoder();
137
+ let buffer = "";
138
+ let currentText = "";
139
+ let currentParts = [];
140
+ let finishData = null;
141
+ try {
142
+ while (true) {
143
+ const { done, value } = await reader.read();
144
+ if (done) break;
145
+ buffer += decoder.decode(value, { stream: true });
146
+ const lines = buffer.split("\n");
147
+ buffer = lines.pop() || "";
148
+ for (const line of lines) {
149
+ const trimmedLine = line.trim();
150
+ if (!trimmedLine || !trimmedLine.startsWith("data:")) continue;
151
+ if (trimmedLine === "data: [DONE]" || trimmedLine === "data:[DONE]") continue;
152
+ try {
153
+ const jsonStr = trimmedLine.startsWith("data: ") ? trimmedLine.slice(6) : trimmedLine.slice(5);
154
+ if (!jsonStr.trim()) continue;
155
+ const json = JSON.parse(jsonStr);
156
+ switch (json.type) {
157
+ // Handle text events (AI SDK v5 uses 'text-delta' with 'delta' property)
158
+ case "text-delta":
159
+ case "text": {
160
+ const textChunk = json.delta || json.textDelta || json.value || json.text || "";
161
+ currentText += textChunk;
162
+ setMessages(
163
+ (prev) => prev.map(
164
+ (m) => m.id === assistantMessageId ? { ...m, content: currentText, parts: [...currentParts.filter((p) => p.type !== "text"), { type: "text", text: currentText }] } : m
165
+ )
166
+ );
167
+ break;
168
+ }
169
+ // Handle tool call events
170
+ case "tool-call":
171
+ case "tool-call-start": {
172
+ const existingTool = currentParts.find(
173
+ (p) => p.type === "tool-invocation" && p.toolCallId === json.toolCallId
174
+ );
175
+ if (!existingTool) {
176
+ const toolPart = {
177
+ type: "tool-invocation",
178
+ toolCallId: json.toolCallId,
179
+ toolName: json.toolName,
180
+ state: "pending",
181
+ input: json.args || {}
182
+ };
183
+ currentParts = [...currentParts.filter((p) => p.type !== "text"), toolPart];
184
+ setMessages(
185
+ (prev) => prev.map(
186
+ (m) => m.id === assistantMessageId ? { ...m, parts: [...currentParts, { type: "text", text: currentText }] } : m
187
+ )
188
+ );
189
+ }
190
+ break;
191
+ }
192
+ // Handle tool result events
193
+ case "tool-result": {
194
+ currentParts = currentParts.map(
195
+ (p) => p.type === "tool-invocation" && p.toolCallId === json.toolCallId ? { ...p, state: "result", output: json.result } : p
196
+ );
197
+ setMessages(
198
+ (prev) => prev.map(
199
+ (m) => m.id === assistantMessageId ? { ...m, parts: [...currentParts.filter((p) => p.type !== "text"), { type: "text", text: currentText }] } : m
200
+ )
201
+ );
202
+ break;
203
+ }
204
+ // Handle finish event
205
+ case "finish":
206
+ case "done": {
207
+ finishData = json;
208
+ break;
209
+ }
210
+ // Ignore other event types (start, text-start, text-end, etc.)
211
+ default:
212
+ break;
213
+ }
214
+ } catch {
215
+ }
216
+ }
217
+ }
218
+ } finally {
219
+ reader.releaseLock();
220
+ }
221
+ return { text: currentText, finishData };
222
+ },
223
+ []
224
+ );
225
+ const sendMessage = useCallback(
226
+ async (content) => {
227
+ const messageContent = content ?? input;
228
+ if (!messageContent.trim() && messages.length === 0) return;
229
+ setError(null);
230
+ setIsLoading(true);
231
+ abortControllerRef.current = new AbortController();
232
+ try {
233
+ let newMessages = [...messages];
234
+ if (messageContent.trim()) {
235
+ const userMessage = {
236
+ id: generateId(),
237
+ role: "user",
238
+ content: messageContent.trim()
239
+ };
240
+ newMessages = [...newMessages, userMessage];
241
+ setMessages(newMessages);
242
+ setInput("");
243
+ }
244
+ const assistantMessageId = generateId();
245
+ const assistantMessage = {
246
+ id: assistantMessageId,
247
+ role: "assistant",
248
+ content: "",
249
+ parts: []
250
+ };
251
+ setMessages([...newMessages, assistantMessage]);
252
+ const response = await agent.stream({
253
+ messages: newMessages,
254
+ signal: abortControllerRef.current.signal
255
+ });
256
+ const { text, finishData } = await parseStream(response, assistantMessageId);
257
+ setMessages(
258
+ (prev) => prev.map(
259
+ (m) => m.id === assistantMessageId ? { ...m, content: text } : m
260
+ )
261
+ );
262
+ if (onFinish && finishData) {
263
+ onFinish({
264
+ text,
265
+ finishReason: finishData.finishReason || "stop",
266
+ steps: [],
267
+ usage: finishData.usage || { inputTokens: 0, outputTokens: 0 },
268
+ _billing: finishData._billing || { model: agentModel, creditsCharged: 0, costUSD: 0 }
269
+ });
270
+ }
271
+ } catch (err) {
272
+ const error2 = err instanceof Error ? err : new Error(String(err));
273
+ if (error2.name !== "AbortError") {
274
+ setError(error2);
275
+ onError?.(error2);
276
+ }
277
+ } finally {
278
+ setIsLoading(false);
279
+ abortControllerRef.current = null;
280
+ }
281
+ },
282
+ [agent, agentModel, messages, input, generateId, parseStream, onFinish, onError]
283
+ );
284
+ const append = useCallback((message) => {
285
+ setMessages((prev) => [...prev, { ...message, id: message.id || generateId() }]);
286
+ }, [generateId]);
287
+ const handleInputChange = useCallback(
288
+ (e) => {
289
+ setInput(e.target.value);
290
+ },
291
+ []
292
+ );
293
+ const handleSubmit = useCallback(
294
+ (e) => {
295
+ e?.preventDefault();
296
+ sendMessage();
297
+ },
298
+ [sendMessage]
299
+ );
300
+ const addToolOutput = useCallback(
301
+ ({ toolCallId, tool, output }) => {
302
+ setMessages(
303
+ (prev) => prev.map((m) => {
304
+ if (m.role !== "assistant" || !m.parts) return m;
305
+ const updatedParts = m.parts.map(
306
+ (p) => p.type === "tool-invocation" && p.toolCallId === toolCallId && p.toolName === tool ? { ...p, state: "output-available", output } : p
307
+ );
308
+ return { ...m, parts: updatedParts };
309
+ })
310
+ );
311
+ },
312
+ []
313
+ );
314
+ const clearMessages = useCallback(() => {
315
+ setMessages(initialMessages);
316
+ setError(null);
317
+ }, [initialMessages]);
318
+ const stop = useCallback(() => {
319
+ abortControllerRef.current?.abort();
320
+ }, []);
321
+ const reload = useCallback(async () => {
322
+ let lastUserIndex = -1;
323
+ for (let i = messages.length - 1; i >= 0; i--) {
324
+ if (messages[i].role === "user") {
325
+ lastUserIndex = i;
326
+ break;
327
+ }
328
+ }
329
+ if (lastUserIndex === -1) return;
330
+ const messagesUpToLastUser = messages.slice(0, lastUserIndex + 1);
331
+ setMessages(messagesUpToLastUser);
332
+ await sendMessage();
333
+ }, [messages, sendMessage]);
334
+ return {
335
+ messages,
336
+ input,
337
+ setInput,
338
+ isLoading,
339
+ error,
340
+ sendMessage,
341
+ append,
342
+ handleInputChange,
343
+ handleSubmit,
344
+ addToolOutput,
345
+ clearMessages,
346
+ stop,
347
+ reload
348
+ };
349
+ }
350
+ export {
351
+ BlinkAuthProvider,
352
+ BlinkProvider,
353
+ useAgent,
354
+ useBlink,
355
+ useBlinkAuth,
356
+ useBlinkClient
357
+ };
358
+ //# sourceMappingURL=index.mjs.map
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@blinkdotnew/react",
3
+ "version": "1.0.0",
4
+ "description": "Blink SDK for React - AI agents, database, storage, and auth for modern SaaS/AI apps",
5
+ "keywords": [
6
+ "blink",
7
+ "react",
8
+ "sdk",
9
+ "ai",
10
+ "agent",
11
+ "chat",
12
+ "typescript",
13
+ "hooks",
14
+ "useChat",
15
+ "useAgent"
16
+ ],
17
+ "homepage": "https://blink.new",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/ShadowWalker2014/blink-sdk.git",
21
+ "directory": "packages/react"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/ShadowWalker2014/blink-sdk/issues"
25
+ },
26
+ "author": "Blink Team",
27
+ "license": "MIT",
28
+ "main": "dist/index.js",
29
+ "module": "dist/index.mjs",
30
+ "types": "dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.mjs",
35
+ "require": "./dist/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist/**/*.js",
40
+ "dist/**/*.mjs",
41
+ "dist/**/*.d.ts",
42
+ "dist/**/*.d.mts"
43
+ ],
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "dev": "tsup --watch",
47
+ "type-check": "tsc --noEmit",
48
+ "clean": "rm -rf dist",
49
+ "prepublishOnly": "npm run build"
50
+ },
51
+ "dependencies": {
52
+ "@blinkdotnew/sdk": "^2.3.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/react": "^18.0.0",
56
+ "react": "^18.0.0",
57
+ "tsup": "^8.0.0",
58
+ "typescript": "^5.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "react": "^18.0.0 || ^19.0.0"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
+ }
67
+
68
+
69
+
70
+