@tambo-ai/react 0.37.0 → 0.37.1

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.
Files changed (27) hide show
  1. package/dist/providers/hooks/__tests__/use-tambo-session-token.test.d.ts +2 -0
  2. package/dist/providers/hooks/__tests__/use-tambo-session-token.test.d.ts.map +1 -0
  3. package/dist/providers/hooks/__tests__/use-tambo-session-token.test.js +161 -0
  4. package/dist/providers/hooks/__tests__/use-tambo-session-token.test.js.map +1 -0
  5. package/dist/providers/hooks/use-tambo-session-token.d.ts +15 -0
  6. package/dist/providers/hooks/use-tambo-session-token.d.ts.map +1 -0
  7. package/dist/providers/hooks/use-tambo-session-token.js +65 -0
  8. package/dist/providers/hooks/use-tambo-session-token.js.map +1 -0
  9. package/dist/providers/tambo-client-provider.d.ts.map +1 -1
  10. package/dist/providers/tambo-client-provider.js +2 -48
  11. package/dist/providers/tambo-client-provider.js.map +1 -1
  12. package/dist/setupTests.js +3 -0
  13. package/dist/setupTests.js.map +1 -1
  14. package/esm/providers/hooks/__tests__/use-tambo-session-token.test.d.ts +2 -0
  15. package/esm/providers/hooks/__tests__/use-tambo-session-token.test.d.ts.map +1 -0
  16. package/esm/providers/hooks/__tests__/use-tambo-session-token.test.js +159 -0
  17. package/esm/providers/hooks/__tests__/use-tambo-session-token.test.js.map +1 -0
  18. package/esm/providers/hooks/use-tambo-session-token.d.ts +15 -0
  19. package/esm/providers/hooks/use-tambo-session-token.d.ts.map +1 -0
  20. package/esm/providers/hooks/use-tambo-session-token.js +62 -0
  21. package/esm/providers/hooks/use-tambo-session-token.js.map +1 -0
  22. package/esm/providers/tambo-client-provider.d.ts.map +1 -1
  23. package/esm/providers/tambo-client-provider.js +2 -48
  24. package/esm/providers/tambo-client-provider.js.map +1 -1
  25. package/esm/setupTests.js +3 -0
  26. package/esm/setupTests.js.map +1 -1
  27. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-tambo-session-token.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.test.d.ts","sourceRoot":"","sources":["../../../../src/providers/hooks/__tests__/use-tambo-session-token.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_1 = require("@testing-library/react");
4
+ const use_tambo_session_token_1 = require("../use-tambo-session-token");
5
+ describe("useTamboSessionToken", () => {
6
+ const mockTokenResponse = {
7
+ access_token: "test-access-token",
8
+ expires_in: 3600, // 1 hour
9
+ token_type: "Bearer",
10
+ };
11
+ const mockAuthApi = {
12
+ getToken: jest.fn(),
13
+ };
14
+ const mockBeta = {
15
+ auth: mockAuthApi,
16
+ };
17
+ const mockTamboAI = {
18
+ apiKey: "",
19
+ beta: mockBeta,
20
+ bearer: "",
21
+ };
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ jest.clearAllTimers();
25
+ jest.useFakeTimers();
26
+ mockTamboAI.bearer = "";
27
+ });
28
+ afterEach(() => {
29
+ jest.runOnlyPendingTimers();
30
+ jest.useRealTimers();
31
+ });
32
+ it("should return null initially when no userToken is provided", () => {
33
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, undefined));
34
+ expect(result.current).toBeNull();
35
+ expect(mockAuthApi.getToken).not.toHaveBeenCalled();
36
+ });
37
+ it("should fetch and return session token when userToken is provided", async () => {
38
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
39
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, "user-token"));
40
+ await (0, react_1.act)(async () => {
41
+ await jest.runOnlyPendingTimersAsync();
42
+ });
43
+ expect(result.current).toBe("test-access-token");
44
+ expect(mockTamboAI.bearer).toBe("test-access-token");
45
+ // Verify the hook was called with correct parameters
46
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);
47
+ expect(mockAuthApi.getToken).toHaveBeenCalledWith(expect.any(Object));
48
+ });
49
+ it("should call getToken with correct token exchange parameters", async () => {
50
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
51
+ (0, react_1.renderHook)(() => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, "user-token"));
52
+ await (0, react_1.act)(async () => {
53
+ await jest.runOnlyPendingTimersAsync();
54
+ });
55
+ const callArgs = jest.mocked(mockAuthApi.getToken).mock.calls[0][0];
56
+ const tokenRequestString = new TextDecoder().decode(callArgs);
57
+ const tokenRequest = new URLSearchParams(tokenRequestString);
58
+ expect(tokenRequest.get("grant_type")).toBe("urn:ietf:params:oauth:grant-type:token-exchange");
59
+ expect(tokenRequest.get("subject_token")).toBe("user-token");
60
+ expect(tokenRequest.get("subject_token_type")).toBe("urn:ietf:params:oauth:token-type:access_token");
61
+ });
62
+ it("should set bearer token on client", async () => {
63
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
64
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, "user-token"));
65
+ await (0, react_1.act)(async () => {
66
+ await jest.runOnlyPendingTimersAsync();
67
+ });
68
+ expect(result.current).toBe("test-access-token");
69
+ expect(mockTamboAI.bearer).toBe("test-access-token");
70
+ });
71
+ it("should handle different token responses", async () => {
72
+ const customTokenResponse = {
73
+ access_token: "custom-access-token",
74
+ expires_in: 7200, // 2 hours
75
+ token_type: "Bearer",
76
+ };
77
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(customTokenResponse);
78
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, "user-token"));
79
+ await (0, react_1.act)(async () => {
80
+ await jest.runOnlyPendingTimersAsync();
81
+ });
82
+ expect(result.current).toBe("custom-access-token");
83
+ expect(mockTamboAI.bearer).toBe("custom-access-token");
84
+ });
85
+ it("should not fetch token when userToken changes to undefined", async () => {
86
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
87
+ const { result, rerender } = (0, react_1.renderHook)(({ userToken }) => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, userToken), {
88
+ initialProps: { userToken: "user-token" },
89
+ });
90
+ await (0, react_1.act)(async () => {
91
+ await jest.runOnlyPendingTimersAsync();
92
+ });
93
+ expect(result.current).toBe("test-access-token");
94
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);
95
+ // Clear mock and change userToken to undefined
96
+ jest.clearAllMocks();
97
+ (0, react_1.act)(() => {
98
+ rerender({ userToken: undefined });
99
+ });
100
+ expect(mockAuthApi.getToken).not.toHaveBeenCalled();
101
+ });
102
+ it("should refetch token when userToken changes", async () => {
103
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
104
+ const { result, rerender } = (0, react_1.renderHook)(({ userToken }) => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, userToken), {
105
+ initialProps: { userToken: "user-token-1" },
106
+ });
107
+ await (0, react_1.act)(async () => {
108
+ await jest.runOnlyPendingTimersAsync();
109
+ });
110
+ expect(result.current).toBe("test-access-token");
111
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);
112
+ // Mock response for new token
113
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue({
114
+ ...mockTokenResponse,
115
+ access_token: "new-access-token",
116
+ });
117
+ // Change userToken
118
+ (0, react_1.act)(() => {
119
+ rerender({ userToken: "user-token-2" });
120
+ });
121
+ await (0, react_1.act)(async () => {
122
+ await jest.runOnlyPendingTimersAsync();
123
+ });
124
+ expect(result.current).toBe("new-access-token");
125
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(2);
126
+ });
127
+ it("should reset token when userToken becomes null", async () => {
128
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
129
+ const { result, rerender } = (0, react_1.renderHook)(({ userToken }) => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, userToken), {
130
+ initialProps: { userToken: "user-token" },
131
+ });
132
+ await (0, react_1.act)(async () => {
133
+ await jest.runOnlyPendingTimersAsync();
134
+ });
135
+ expect(result.current).toBe("test-access-token");
136
+ // Change userToken to undefined
137
+ (0, react_1.act)(() => {
138
+ rerender({ userToken: undefined });
139
+ });
140
+ // Token should remain the same (hook doesn't reset it to null when userToken is undefined)
141
+ expect(result.current).toBe("test-access-token");
142
+ });
143
+ it("should not update state if component is unmounted during token fetch", async () => {
144
+ let resolvePromise;
145
+ const promise = new Promise((resolve) => {
146
+ resolvePromise = resolve;
147
+ });
148
+ jest.mocked(mockAuthApi.getToken).mockReturnValue(promise);
149
+ const { result, unmount } = (0, react_1.renderHook)(() => (0, use_tambo_session_token_1.useTamboSessionToken)(mockTamboAI, "user-token"));
150
+ expect(result.current).toBeNull();
151
+ // Unmount before the promise resolves
152
+ unmount();
153
+ // Now resolve the promise
154
+ (0, react_1.act)(() => {
155
+ resolvePromise(mockTokenResponse);
156
+ });
157
+ // Token should still be null since component was unmounted
158
+ expect(result.current).toBeNull();
159
+ });
160
+ });
161
+ //# sourceMappingURL=use-tambo-session-token.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.test.js","sourceRoot":"","sources":["../../../../src/providers/hooks/__tests__/use-tambo-session-token.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { act, renderHook } from \"@testing-library/react\";\nimport { DeepPartial } from \"ts-essentials\";\nimport { useTamboSessionToken } from \"../use-tambo-session-token\";\n\ntype PartialTamboAI = DeepPartial<TamboAI>;\n\ndescribe(\"useTamboSessionToken\", () => {\n const mockTokenResponse = {\n access_token: \"test-access-token\",\n expires_in: 3600, // 1 hour\n token_type: \"Bearer\",\n };\n\n const mockAuthApi = {\n getToken: jest.fn(),\n } satisfies DeepPartial<TamboAI[\"beta\"][\"auth\"]>;\n\n const mockBeta = {\n auth: mockAuthApi,\n } satisfies PartialTamboAI[\"beta\"];\n\n const mockTamboAI = {\n apiKey: \"\",\n beta: mockBeta,\n bearer: \"\",\n } satisfies PartialTamboAI as unknown as TamboAI;\n\n beforeEach(() => {\n jest.clearAllMocks();\n jest.clearAllTimers();\n jest.useFakeTimers();\n (mockTamboAI as any).bearer = \"\";\n });\n\n afterEach(() => {\n jest.runOnlyPendingTimers();\n jest.useRealTimers();\n });\n\n it(\"should return null initially when no userToken is provided\", () => {\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, undefined),\n );\n\n expect(result.current).toBeNull();\n expect(mockAuthApi.getToken).not.toHaveBeenCalled();\n });\n\n it(\"should fetch and return session token when userToken is provided\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockTamboAI.bearer).toBe(\"test-access-token\");\n // Verify the hook was called with correct parameters\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);\n expect(mockAuthApi.getToken).toHaveBeenCalledWith(expect.any(Object));\n });\n\n it(\"should call getToken with correct token exchange parameters\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n renderHook(() => useTamboSessionToken(mockTamboAI, \"user-token\"));\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n const callArgs = jest.mocked(mockAuthApi.getToken).mock.calls[0][0];\n const tokenRequestString = new TextDecoder().decode(callArgs);\n const tokenRequest = new URLSearchParams(tokenRequestString);\n\n expect(tokenRequest.get(\"grant_type\")).toBe(\n \"urn:ietf:params:oauth:grant-type:token-exchange\",\n );\n expect(tokenRequest.get(\"subject_token\")).toBe(\"user-token\");\n expect(tokenRequest.get(\"subject_token_type\")).toBe(\n \"urn:ietf:params:oauth:token-type:access_token\",\n );\n });\n\n it(\"should set bearer token on client\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockTamboAI.bearer).toBe(\"test-access-token\");\n });\n\n it(\"should handle different token responses\", async () => {\n const customTokenResponse = {\n access_token: \"custom-access-token\",\n expires_in: 7200, // 2 hours\n token_type: \"Bearer\",\n };\n\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(customTokenResponse);\n\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"custom-access-token\");\n expect(mockTamboAI.bearer).toBe(\"custom-access-token\");\n });\n\n it(\"should not fetch token when userToken changes to undefined\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result, rerender } = renderHook(\n ({ userToken }) => useTamboSessionToken(mockTamboAI, userToken),\n {\n initialProps: { userToken: \"user-token\" as string | undefined },\n },\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);\n\n // Clear mock and change userToken to undefined\n jest.clearAllMocks();\n\n act(() => {\n rerender({ userToken: undefined });\n });\n\n expect(mockAuthApi.getToken).not.toHaveBeenCalled();\n });\n\n it(\"should refetch token when userToken changes\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result, rerender } = renderHook(\n ({ userToken }) => useTamboSessionToken(mockTamboAI, userToken),\n {\n initialProps: { userToken: \"user-token-1\" },\n },\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);\n\n // Mock response for new token\n jest.mocked(mockAuthApi.getToken).mockResolvedValue({\n ...mockTokenResponse,\n access_token: \"new-access-token\",\n });\n\n // Change userToken\n act(() => {\n rerender({ userToken: \"user-token-2\" });\n });\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"new-access-token\");\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(2);\n });\n\n it(\"should reset token when userToken becomes null\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result, rerender } = renderHook(\n ({ userToken }) => useTamboSessionToken(mockTamboAI, userToken),\n {\n initialProps: { userToken: \"user-token\" as string | undefined },\n },\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n\n // Change userToken to undefined\n act(() => {\n rerender({ userToken: undefined });\n });\n\n // Token should remain the same (hook doesn't reset it to null when userToken is undefined)\n expect(result.current).toBe(\"test-access-token\");\n });\n\n it(\"should not update state if component is unmounted during token fetch\", async () => {\n let resolvePromise: (value: any) => void;\n const promise = new Promise((resolve) => {\n resolvePromise = resolve;\n });\n\n jest.mocked(mockAuthApi.getToken).mockReturnValue(promise);\n\n const { result, unmount } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n expect(result.current).toBeNull();\n\n // Unmount before the promise resolves\n unmount();\n\n // Now resolve the promise\n act(() => {\n resolvePromise!(mockTokenResponse);\n });\n\n // Token should still be null since component was unmounted\n expect(result.current).toBeNull();\n });\n});\n"]}
@@ -0,0 +1,15 @@
1
+ import TamboAI from "@tambo-ai/typescript-sdk";
2
+ /**
3
+ * This internal hook is used to get the Tambo session token and keep it
4
+ * refreshed.
5
+ *
6
+ * It will refresh the token when it expires.
7
+ * It will also set the bearer token on the client.
8
+ *
9
+ * This hook is used by the TamboClientProvider.
10
+ * @param client - The Tambo client.
11
+ * @param userToken - The user token.
12
+ * @returns The Tambo session token.
13
+ */
14
+ export declare function useTamboSessionToken(client: TamboAI, userToken: string | undefined): string | null;
15
+ //# sourceMappingURL=use-tambo-session-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.d.ts","sourceRoot":"","sources":["../../../src/providers/hooks/use-tambo-session-token.tsx"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,0BAA0B,CAAC;AAG/C;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAAG,SAAS,iBAiE9B"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ "use client";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useTamboSessionToken = useTamboSessionToken;
5
+ const react_1 = require("react");
6
+ /**
7
+ * This internal hook is used to get the Tambo session token and keep it
8
+ * refreshed.
9
+ *
10
+ * It will refresh the token when it expires.
11
+ * It will also set the bearer token on the client.
12
+ *
13
+ * This hook is used by the TamboClientProvider.
14
+ * @param client - The Tambo client.
15
+ * @param userToken - The user token.
16
+ * @returns The Tambo session token.
17
+ */
18
+ function useTamboSessionToken(client, userToken) {
19
+ const [tamboSessionToken, setTamboSessionToken] = (0, react_1.useState)(null);
20
+ // we need to set this to true when the token is expired, this is effectively
21
+ // like a dirty bit, which will trigger a new useEffect()
22
+ const [isExpired, setIsExpired] = (0, react_1.useState)(true);
23
+ (0, react_1.useEffect)(() => {
24
+ let expireTimer = null;
25
+ async function updateToken(subjectToken, abortController) {
26
+ if (abortController.signal.aborted || !userToken) {
27
+ return;
28
+ }
29
+ const tokenRequest = {
30
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
31
+ subject_token: subjectToken,
32
+ subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
33
+ };
34
+ const tokenRequestFormEncoded = new URLSearchParams(tokenRequest).toString();
35
+ const tokenAsArrayBuffer = new TextEncoder().encode(tokenRequestFormEncoded);
36
+ const tamboToken = await client.beta.auth.getToken(tokenAsArrayBuffer);
37
+ if (abortController.signal.aborted) {
38
+ return;
39
+ }
40
+ setTamboSessionToken(tamboToken.access_token);
41
+ client.bearer = tamboToken.access_token;
42
+ // we need to set a timer to refresh the token when it expires
43
+ const refreshTime = Math.max(tamboToken.expires_in - 60, 0);
44
+ // careful with the assignment here: since this is an async function, this
45
+ // code is executed outside the of the scope of the useEffect() hook, so
46
+ // we need to use a let variable to store the timer
47
+ expireTimer = setTimeout(() => {
48
+ setIsExpired(true);
49
+ }, refreshTime * 1000);
50
+ }
51
+ const abortController = new AbortController();
52
+ if (userToken && isExpired) {
53
+ updateToken(userToken, abortController);
54
+ }
55
+ return () => {
56
+ // This fires when the component unmounts or the userToken changes
57
+ abortController.abort();
58
+ if (expireTimer) {
59
+ clearTimeout(expireTimer);
60
+ }
61
+ };
62
+ }, [client, isExpired, userToken]);
63
+ return tamboSessionToken;
64
+ }
65
+ //# sourceMappingURL=use-tambo-session-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.js","sourceRoot":"","sources":["../../../src/providers/hooks/use-tambo-session-token.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAgBb,oDAmEC;AAjFD,iCAA4C;AAE5C;;;;;;;;;;;GAWG;AACH,SAAgB,oBAAoB,CAClC,MAAe,EACf,SAA6B;IAE7B,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,IAAA,gBAAQ,EACxD,IAAI,CACL,CAAC;IACF,6EAA6E;IAC7E,yDAAyD;IACzD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,CAAC;IACjD,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,WAAW,GAA0B,IAAI,CAAC;QAE9C,KAAK,UAAU,WAAW,CACxB,YAAoB,EACpB,eAAgC;YAEhC,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjD,OAAO;YACT,CAAC;YACD,MAAM,YAAY,GAAG;gBACnB,UAAU,EAAE,iDAAiD;gBAC7D,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,+CAA+C;aACpE,CAAC;YACF,MAAM,uBAAuB,GAAG,IAAI,eAAe,CACjD,YAAY,CACb,CAAC,QAAQ,EAAE,CAAC;YACb,MAAM,kBAAkB,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CACjD,uBAAuB,CACxB,CAAC;YACF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAChD,kBAAyB,CAC1B,CAAC;YAEF,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnC,OAAO;YACT,CAAC;YACD,oBAAoB,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC;YAExC,8DAA8D;YAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;YAE5D,0EAA0E;YAC1E,wEAAwE;YACxE,mDAAmD;YACnD,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,YAAY,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC;QACzB,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;YAC3B,WAAW,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;QAC1C,CAAC;QAED,OAAO,GAAG,EAAE;YACV,kEAAkE;YAClE,eAAe,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,WAAW,EAAE,CAAC;gBAChB,YAAY,CAAC,WAAW,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IAEnC,OAAO,iBAAiB,CAAC;AAC3B,CAAC","sourcesContent":["\"use client\";\nimport TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { useEffect, useState } from \"react\";\n\n/**\n * This internal hook is used to get the Tambo session token and keep it\n * refreshed.\n *\n * It will refresh the token when it expires.\n * It will also set the bearer token on the client.\n *\n * This hook is used by the TamboClientProvider.\n * @param client - The Tambo client.\n * @param userToken - The user token.\n * @returns The Tambo session token.\n */\nexport function useTamboSessionToken(\n client: TamboAI,\n userToken: string | undefined,\n) {\n const [tamboSessionToken, setTamboSessionToken] = useState<string | null>(\n null,\n );\n // we need to set this to true when the token is expired, this is effectively\n // like a dirty bit, which will trigger a new useEffect()\n const [isExpired, setIsExpired] = useState(true);\n useEffect(() => {\n let expireTimer: NodeJS.Timeout | null = null;\n\n async function updateToken(\n subjectToken: string,\n abortController: AbortController,\n ) {\n if (abortController.signal.aborted || !userToken) {\n return;\n }\n const tokenRequest = {\n grant_type: \"urn:ietf:params:oauth:grant-type:token-exchange\",\n subject_token: subjectToken,\n subject_token_type: \"urn:ietf:params:oauth:token-type:access_token\",\n };\n const tokenRequestFormEncoded = new URLSearchParams(\n tokenRequest,\n ).toString();\n const tokenAsArrayBuffer = new TextEncoder().encode(\n tokenRequestFormEncoded,\n );\n const tamboToken = await client.beta.auth.getToken(\n tokenAsArrayBuffer as any,\n );\n\n if (abortController.signal.aborted) {\n return;\n }\n setTamboSessionToken(tamboToken.access_token);\n client.bearer = tamboToken.access_token;\n\n // we need to set a timer to refresh the token when it expires\n const refreshTime = Math.max(tamboToken.expires_in - 60, 0);\n\n // careful with the assignment here: since this is an async function, this\n // code is executed outside the of the scope of the useEffect() hook, so\n // we need to use a let variable to store the timer\n expireTimer = setTimeout(() => {\n setIsExpired(true);\n }, refreshTime * 1000);\n }\n\n const abortController = new AbortController();\n if (userToken && isExpired) {\n updateToken(userToken, abortController);\n }\n\n return () => {\n // This fires when the component unmounts or the userToken changes\n abortController.abort();\n if (expireTimer) {\n clearTimeout(expireTimer);\n }\n };\n }, [client, isExpired, userToken]);\n\n return tamboSessionToken;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-client-provider.d.ts","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":"AACA,OAAO,OAA0B,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,EAEZ,iBAAiB,EAGlB,MAAM,OAAO,CAAC;AAGf,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAEvC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,yBAAyB;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,sCAAsC;IACtC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,eAAO,MAAM,kBAAkB,oDAEnB,CAAC;AAEb;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CACxC,iBAAiB,CAAC,wBAAwB,CAAC,CAyB5C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,eAM1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,mBAQ/B,CAAC"}
1
+ {"version":3,"file":"tambo-client-provider.d.ts","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":"AACA,OAAO,OAA0B,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,EAAiB,iBAAiB,EAAY,MAAM,OAAO,CAAC;AAI1E,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAEvC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,yBAAyB;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,sCAAsC;IACtC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,eAAO,MAAM,kBAAkB,oDAEnB,CAAC;AAEb;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CACxC,iBAAiB,CAAC,wBAAwB,CAAC,CAyB5C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,eAM1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,mBAQ/B,CAAC"}
@@ -42,6 +42,7 @@ const typescript_sdk_1 = __importDefault(require("@tambo-ai/typescript-sdk"));
42
42
  const react_query_1 = require("@tanstack/react-query");
43
43
  const react_1 = __importStar(require("react"));
44
44
  const package_json_1 = __importDefault(require("../../package.json"));
45
+ const use_tambo_session_token_1 = require("./hooks/use-tambo-session-token");
45
46
  exports.TamboClientContext = (0, react_1.createContext)(undefined);
46
47
  /**
47
48
  * The TamboClientProvider is a React provider that provides a TamboAI client
@@ -70,7 +71,7 @@ const TamboClientProvider = ({ children, tamboUrl, apiKey, environment, userToke
70
71
  const [client] = (0, react_1.useState)(() => new typescript_sdk_1.default(tamboConfig));
71
72
  const [queryClient] = (0, react_1.useState)(() => new react_query_1.QueryClient());
72
73
  // Keep the session token updated
73
- useTamboSessionToken(client, userToken);
74
+ (0, use_tambo_session_token_1.useTamboSessionToken)(client, userToken);
74
75
  return (react_1.default.createElement(exports.TamboClientContext.Provider, { value: { client, queryClient } }, children));
75
76
  };
76
77
  exports.TamboClientProvider = TamboClientProvider;
@@ -101,51 +102,4 @@ const useTamboQueryClient = () => {
101
102
  return context.queryClient;
102
103
  };
103
104
  exports.useTamboQueryClient = useTamboQueryClient;
104
- function useTamboSessionToken(client, userToken) {
105
- const [tamboSessionToken, setTamboSessionToken] = (0, react_1.useState)(null);
106
- // we need to set this to true when the token is expired, this is effectively
107
- // like a dirty bit, which will trigger a new useEffect()
108
- const [isExpired, setIsExpired] = (0, react_1.useState)(true);
109
- (0, react_1.useEffect)(() => {
110
- let expireTimer = null;
111
- async function updateToken(subjectToken, abortController) {
112
- if (abortController.signal.aborted || !userToken) {
113
- return;
114
- }
115
- const tokenRequest = {
116
- grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
117
- subject_token: subjectToken,
118
- subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
119
- };
120
- const tokenRequestFormEncoded = new URLSearchParams(tokenRequest).toString();
121
- const tokenAsArrayBuffer = new TextEncoder().encode(tokenRequestFormEncoded);
122
- const tamboToken = await client.beta.auth.getToken(tokenAsArrayBuffer);
123
- if (abortController.signal.aborted) {
124
- return;
125
- }
126
- setTamboSessionToken(tamboToken.access_token);
127
- client.bearer = tamboToken.access_token;
128
- // we need to set a timer to refresh the token when it expires
129
- const refreshTime = Math.max(tamboToken.expires_in - 60, 0);
130
- // careful with the assignment here: since this is an async function, this
131
- // code is executed outside the of the scope of the useEffect() hook, so
132
- // we need to use a let variable to store the timer
133
- expireTimer = setTimeout(() => {
134
- setIsExpired(true);
135
- }, refreshTime * 1000);
136
- }
137
- const abortController = new AbortController();
138
- if (userToken && isExpired) {
139
- updateToken(userToken, abortController);
140
- }
141
- return () => {
142
- // This fires when the component unmounts or the userToken changes
143
- abortController.abort();
144
- if (expireTimer) {
145
- clearTimeout(expireTimer);
146
- }
147
- };
148
- }, [client, isExpired, userToken]);
149
- return tamboSessionToken;
150
- }
151
105
  //# sourceMappingURL=tambo-client-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-client-provider.js","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport TamboAI, { ClientOptions } from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport React, {\n createContext,\n PropsWithChildren,\n useEffect,\n useState,\n} from \"react\";\nimport packageJson from \"../../package.json\";\n\nexport interface TamboClientProviderProps {\n /**\n * The URL of the Tambo API (only used for local development and debugging)\n */\n tamboUrl?: string;\n /**\n * The API key for the Tambo API. This typically comes from a variable like\n * `process.env.NEXT_PUBLIC_TAMBO_API_KEY`\n */\n apiKey: string;\n /**\n * The environment to use for the Tambo API\n */\n environment?: \"production\" | \"staging\";\n\n /**\n * The user token to use to identify the user in the Tambo API. This token is\n * a 3rd party token like a Google or GitHub access token, exchanged with the\n * Tambo API to get a session token. This is used to securely identify the\n * user when calling the Tambo API.\n */\n userToken?: string;\n}\n\nexport interface TamboClientContextProps {\n /** The TamboAI client */\n client: TamboAI;\n /** The tambo-specific query client */\n queryClient: QueryClient;\n}\n\nexport const TamboClientContext = createContext<\n TamboClientContextProps | undefined\n>(undefined);\n\n/**\n * The TamboClientProvider is a React provider that provides a TamboAI client\n * and a query client to the descendants of the provider.\n * @param props - The props for the TamboClientProvider\n * @param props.children - The children to wrap\n * @param props.tamboUrl - The URL of the Tambo API\n * @param props.apiKey - The API key for the Tambo API\n * @param props.environment - The environment to use for the Tambo API\n * @param props.userToken - The oauth access token to use to identify the user in the Tambo API\n * @returns The TamboClientProvider component\n */\nexport const TamboClientProvider: React.FC<\n PropsWithChildren<TamboClientProviderProps>\n> = ({ children, tamboUrl, apiKey, environment, userToken }) => {\n const tamboConfig: ClientOptions = {\n apiKey,\n defaultHeaders: {\n \"X-Tambo-React-Version\": packageJson.version,\n },\n };\n if (tamboUrl) {\n tamboConfig.baseURL = tamboUrl;\n }\n if (environment) {\n tamboConfig.environment = environment;\n }\n const [client] = useState(() => new TamboAI(tamboConfig));\n const [queryClient] = useState(() => new QueryClient());\n\n // Keep the session token updated\n useTamboSessionToken(client, userToken);\n\n return (\n <TamboClientContext.Provider value={{ client, queryClient }}>\n {children}\n </TamboClientContext.Provider>\n );\n};\n\n/**\n * The useTamboClient hook provides access to the TamboAI client\n * to the descendants of the TamboClientProvider.\n * @returns The TamboAI client\n */\nexport const useTamboClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\"useTamboClient must be used within a TamboClientProvider\");\n }\n return context.client;\n};\n\n/**\n * The useTamboQueryClient hook provides access to the tambo-specific query client\n * to the descendants of the TamboClientProvider.\n * @returns The tambo-specific query client\n * @private\n */\nexport const useTamboQueryClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\n \"useTamboQueryClient must be used within a TamboClientProvider\",\n );\n }\n return context.queryClient;\n};\n\nfunction useTamboSessionToken(client: TamboAI, userToken: string | undefined) {\n const [tamboSessionToken, setTamboSessionToken] = useState<string | null>(\n null,\n );\n // we need to set this to true when the token is expired, this is effectively\n // like a dirty bit, which will trigger a new useEffect()\n const [isExpired, setIsExpired] = useState(true);\n useEffect(() => {\n let expireTimer: NodeJS.Timeout | null = null;\n\n async function updateToken(\n subjectToken: string,\n abortController: AbortController,\n ) {\n if (abortController.signal.aborted || !userToken) {\n return;\n }\n const tokenRequest = {\n grant_type: \"urn:ietf:params:oauth:grant-type:token-exchange\",\n subject_token: subjectToken,\n subject_token_type: \"urn:ietf:params:oauth:token-type:access_token\",\n };\n const tokenRequestFormEncoded = new URLSearchParams(\n tokenRequest,\n ).toString();\n const tokenAsArrayBuffer = new TextEncoder().encode(\n tokenRequestFormEncoded,\n );\n const tamboToken = await client.beta.auth.getToken(\n tokenAsArrayBuffer as any,\n );\n\n if (abortController.signal.aborted) {\n return;\n }\n setTamboSessionToken(tamboToken.access_token);\n client.bearer = tamboToken.access_token;\n\n // we need to set a timer to refresh the token when it expires\n const refreshTime = Math.max(tamboToken.expires_in - 60, 0);\n\n // careful with the assignment here: since this is an async function, this\n // code is executed outside the of the scope of the useEffect() hook, so\n // we need to use a let variable to store the timer\n expireTimer = setTimeout(() => {\n setIsExpired(true);\n }, refreshTime * 1000);\n }\n\n const abortController = new AbortController();\n if (userToken && isExpired) {\n updateToken(userToken, abortController);\n }\n\n return () => {\n // This fires when the component unmounts or the userToken changes\n abortController.abort();\n if (expireTimer) {\n clearTimeout(expireTimer);\n }\n };\n }, [client, isExpired, userToken]);\n\n return tamboSessionToken;\n}\n"]}
1
+ {"version":3,"file":"tambo-client-provider.js","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACb,8EAAkE;AAClE,uDAAoD;AACpD,+CAA0E;AAC1E,sEAA6C;AAC7C,6EAAuE;AAiC1D,QAAA,kBAAkB,GAAG,IAAA,qBAAa,EAE7C,SAAS,CAAC,CAAC;AAEb;;;;;;;;;;GAUG;AACI,MAAM,mBAAmB,GAE5B,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE;IAC7D,MAAM,WAAW,GAAkB;QACjC,MAAM;QACN,cAAc,EAAE;YACd,uBAAuB,EAAE,sBAAW,CAAC,OAAO;SAC7C;KACF,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACjC,CAAC;IACD,IAAI,WAAW,EAAE,CAAC;QAChB,WAAW,CAAC,WAAW,GAAG,WAAW,CAAC;IACxC,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAAC,IAAI,wBAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,WAAW,CAAC,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAAC,IAAI,yBAAW,EAAE,CAAC,CAAC;IAExD,iCAAiC;IACjC,IAAA,8CAAoB,EAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAExC,OAAO,CACL,8BAAC,0BAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,IACxD,QAAQ,CACmB,CAC/B,CAAC;AACJ,CAAC,CAAC;AA1BW,QAAA,mBAAmB,uBA0B9B;AAEF;;;;GAIG;AACI,MAAM,cAAc,GAAG,GAAG,EAAE;IACjC,MAAM,OAAO,GAAG,eAAK,CAAC,UAAU,CAAC,0BAAkB,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC,CAAC;AANW,QAAA,cAAc,kBAMzB;AAEF;;;;;GAKG;AACI,MAAM,mBAAmB,GAAG,GAAG,EAAE;IACtC,MAAM,OAAO,GAAG,eAAK,CAAC,UAAU,CAAC,0BAAkB,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,+DAA+D,CAChE,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,WAAW,CAAC;AAC7B,CAAC,CAAC;AARW,QAAA,mBAAmB,uBAQ9B","sourcesContent":["\"use client\";\nimport TamboAI, { ClientOptions } from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport React, { createContext, PropsWithChildren, useState } from \"react\";\nimport packageJson from \"../../package.json\";\nimport { useTamboSessionToken } from \"./hooks/use-tambo-session-token\";\n\nexport interface TamboClientProviderProps {\n /**\n * The URL of the Tambo API (only used for local development and debugging)\n */\n tamboUrl?: string;\n /**\n * The API key for the Tambo API. This typically comes from a variable like\n * `process.env.NEXT_PUBLIC_TAMBO_API_KEY`\n */\n apiKey: string;\n /**\n * The environment to use for the Tambo API\n */\n environment?: \"production\" | \"staging\";\n\n /**\n * The user token to use to identify the user in the Tambo API. This token is\n * a 3rd party token like a Google or GitHub access token, exchanged with the\n * Tambo API to get a session token. This is used to securely identify the\n * user when calling the Tambo API.\n */\n userToken?: string;\n}\n\nexport interface TamboClientContextProps {\n /** The TamboAI client */\n client: TamboAI;\n /** The tambo-specific query client */\n queryClient: QueryClient;\n}\n\nexport const TamboClientContext = createContext<\n TamboClientContextProps | undefined\n>(undefined);\n\n/**\n * The TamboClientProvider is a React provider that provides a TamboAI client\n * and a query client to the descendants of the provider.\n * @param props - The props for the TamboClientProvider\n * @param props.children - The children to wrap\n * @param props.tamboUrl - The URL of the Tambo API\n * @param props.apiKey - The API key for the Tambo API\n * @param props.environment - The environment to use for the Tambo API\n * @param props.userToken - The oauth access token to use to identify the user in the Tambo API\n * @returns The TamboClientProvider component\n */\nexport const TamboClientProvider: React.FC<\n PropsWithChildren<TamboClientProviderProps>\n> = ({ children, tamboUrl, apiKey, environment, userToken }) => {\n const tamboConfig: ClientOptions = {\n apiKey,\n defaultHeaders: {\n \"X-Tambo-React-Version\": packageJson.version,\n },\n };\n if (tamboUrl) {\n tamboConfig.baseURL = tamboUrl;\n }\n if (environment) {\n tamboConfig.environment = environment;\n }\n const [client] = useState(() => new TamboAI(tamboConfig));\n const [queryClient] = useState(() => new QueryClient());\n\n // Keep the session token updated\n useTamboSessionToken(client, userToken);\n\n return (\n <TamboClientContext.Provider value={{ client, queryClient }}>\n {children}\n </TamboClientContext.Provider>\n );\n};\n\n/**\n * The useTamboClient hook provides access to the TamboAI client\n * to the descendants of the TamboClientProvider.\n * @returns The TamboAI client\n */\nexport const useTamboClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\"useTamboClient must be used within a TamboClientProvider\");\n }\n return context.client;\n};\n\n/**\n * The useTamboQueryClient hook provides access to the tambo-specific query client\n * to the descendants of the TamboClientProvider.\n * @returns The tambo-specific query client\n * @private\n */\nexport const useTamboQueryClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\n \"useTamboQueryClient must be used within a TamboClientProvider\",\n );\n }\n return context.queryClient;\n};\n"]}
@@ -2,7 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  require("@tambo-ai/typescript-sdk/shims/node");
4
4
  require("@testing-library/jest-dom");
5
+ const util_1 = require("util");
5
6
  // Mock Date.now() to return a fixed timestamp for consistent testing
6
7
  const mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);
7
8
  global.Date.now = jest.fn(() => mockDate.getTime());
9
+ // Add TextEncoder/TextDecoder polyfills for Node.js test environment
10
+ Object.assign(global, { TextEncoder: util_1.TextEncoder, TextDecoder: util_1.TextDecoder });
8
11
  //# sourceMappingURL=setupTests.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":";;AAAA,+CAA6C;AAC7C,qCAAmC;AAEnC,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC","sourcesContent":["import \"@tambo-ai/typescript-sdk/shims/node\";\nimport \"@testing-library/jest-dom\";\n\n// Mock Date.now() to return a fixed timestamp for consistent testing\nconst mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);\nglobal.Date.now = jest.fn(() => mockDate.getTime());\n"]}
1
+ {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":";;AAAA,+CAA6C;AAC7C,qCAAmC;AACnC,+BAAgD;AAEhD,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;AAEpD,qEAAqE;AACrE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,WAAW,EAAX,kBAAW,EAAE,WAAW,EAAX,kBAAW,EAAE,CAAC,CAAC","sourcesContent":["import \"@tambo-ai/typescript-sdk/shims/node\";\nimport \"@testing-library/jest-dom\";\nimport { TextDecoder, TextEncoder } from \"util\";\n\n// Mock Date.now() to return a fixed timestamp for consistent testing\nconst mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);\nglobal.Date.now = jest.fn(() => mockDate.getTime());\n\n// Add TextEncoder/TextDecoder polyfills for Node.js test environment\nObject.assign(global, { TextEncoder, TextDecoder });\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-tambo-session-token.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.test.d.ts","sourceRoot":"","sources":["../../../../src/providers/hooks/__tests__/use-tambo-session-token.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,159 @@
1
+ import { act, renderHook } from "@testing-library/react";
2
+ import { useTamboSessionToken } from "../use-tambo-session-token";
3
+ describe("useTamboSessionToken", () => {
4
+ const mockTokenResponse = {
5
+ access_token: "test-access-token",
6
+ expires_in: 3600, // 1 hour
7
+ token_type: "Bearer",
8
+ };
9
+ const mockAuthApi = {
10
+ getToken: jest.fn(),
11
+ };
12
+ const mockBeta = {
13
+ auth: mockAuthApi,
14
+ };
15
+ const mockTamboAI = {
16
+ apiKey: "",
17
+ beta: mockBeta,
18
+ bearer: "",
19
+ };
20
+ beforeEach(() => {
21
+ jest.clearAllMocks();
22
+ jest.clearAllTimers();
23
+ jest.useFakeTimers();
24
+ mockTamboAI.bearer = "";
25
+ });
26
+ afterEach(() => {
27
+ jest.runOnlyPendingTimers();
28
+ jest.useRealTimers();
29
+ });
30
+ it("should return null initially when no userToken is provided", () => {
31
+ const { result } = renderHook(() => useTamboSessionToken(mockTamboAI, undefined));
32
+ expect(result.current).toBeNull();
33
+ expect(mockAuthApi.getToken).not.toHaveBeenCalled();
34
+ });
35
+ it("should fetch and return session token when userToken is provided", async () => {
36
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
37
+ const { result } = renderHook(() => useTamboSessionToken(mockTamboAI, "user-token"));
38
+ await act(async () => {
39
+ await jest.runOnlyPendingTimersAsync();
40
+ });
41
+ expect(result.current).toBe("test-access-token");
42
+ expect(mockTamboAI.bearer).toBe("test-access-token");
43
+ // Verify the hook was called with correct parameters
44
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);
45
+ expect(mockAuthApi.getToken).toHaveBeenCalledWith(expect.any(Object));
46
+ });
47
+ it("should call getToken with correct token exchange parameters", async () => {
48
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
49
+ renderHook(() => useTamboSessionToken(mockTamboAI, "user-token"));
50
+ await act(async () => {
51
+ await jest.runOnlyPendingTimersAsync();
52
+ });
53
+ const callArgs = jest.mocked(mockAuthApi.getToken).mock.calls[0][0];
54
+ const tokenRequestString = new TextDecoder().decode(callArgs);
55
+ const tokenRequest = new URLSearchParams(tokenRequestString);
56
+ expect(tokenRequest.get("grant_type")).toBe("urn:ietf:params:oauth:grant-type:token-exchange");
57
+ expect(tokenRequest.get("subject_token")).toBe("user-token");
58
+ expect(tokenRequest.get("subject_token_type")).toBe("urn:ietf:params:oauth:token-type:access_token");
59
+ });
60
+ it("should set bearer token on client", async () => {
61
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
62
+ const { result } = renderHook(() => useTamboSessionToken(mockTamboAI, "user-token"));
63
+ await act(async () => {
64
+ await jest.runOnlyPendingTimersAsync();
65
+ });
66
+ expect(result.current).toBe("test-access-token");
67
+ expect(mockTamboAI.bearer).toBe("test-access-token");
68
+ });
69
+ it("should handle different token responses", async () => {
70
+ const customTokenResponse = {
71
+ access_token: "custom-access-token",
72
+ expires_in: 7200, // 2 hours
73
+ token_type: "Bearer",
74
+ };
75
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(customTokenResponse);
76
+ const { result } = renderHook(() => useTamboSessionToken(mockTamboAI, "user-token"));
77
+ await act(async () => {
78
+ await jest.runOnlyPendingTimersAsync();
79
+ });
80
+ expect(result.current).toBe("custom-access-token");
81
+ expect(mockTamboAI.bearer).toBe("custom-access-token");
82
+ });
83
+ it("should not fetch token when userToken changes to undefined", async () => {
84
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
85
+ const { result, rerender } = renderHook(({ userToken }) => useTamboSessionToken(mockTamboAI, userToken), {
86
+ initialProps: { userToken: "user-token" },
87
+ });
88
+ await act(async () => {
89
+ await jest.runOnlyPendingTimersAsync();
90
+ });
91
+ expect(result.current).toBe("test-access-token");
92
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);
93
+ // Clear mock and change userToken to undefined
94
+ jest.clearAllMocks();
95
+ act(() => {
96
+ rerender({ userToken: undefined });
97
+ });
98
+ expect(mockAuthApi.getToken).not.toHaveBeenCalled();
99
+ });
100
+ it("should refetch token when userToken changes", async () => {
101
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
102
+ const { result, rerender } = renderHook(({ userToken }) => useTamboSessionToken(mockTamboAI, userToken), {
103
+ initialProps: { userToken: "user-token-1" },
104
+ });
105
+ await act(async () => {
106
+ await jest.runOnlyPendingTimersAsync();
107
+ });
108
+ expect(result.current).toBe("test-access-token");
109
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);
110
+ // Mock response for new token
111
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue({
112
+ ...mockTokenResponse,
113
+ access_token: "new-access-token",
114
+ });
115
+ // Change userToken
116
+ act(() => {
117
+ rerender({ userToken: "user-token-2" });
118
+ });
119
+ await act(async () => {
120
+ await jest.runOnlyPendingTimersAsync();
121
+ });
122
+ expect(result.current).toBe("new-access-token");
123
+ expect(mockAuthApi.getToken).toHaveBeenCalledTimes(2);
124
+ });
125
+ it("should reset token when userToken becomes null", async () => {
126
+ jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);
127
+ const { result, rerender } = renderHook(({ userToken }) => useTamboSessionToken(mockTamboAI, userToken), {
128
+ initialProps: { userToken: "user-token" },
129
+ });
130
+ await act(async () => {
131
+ await jest.runOnlyPendingTimersAsync();
132
+ });
133
+ expect(result.current).toBe("test-access-token");
134
+ // Change userToken to undefined
135
+ act(() => {
136
+ rerender({ userToken: undefined });
137
+ });
138
+ // Token should remain the same (hook doesn't reset it to null when userToken is undefined)
139
+ expect(result.current).toBe("test-access-token");
140
+ });
141
+ it("should not update state if component is unmounted during token fetch", async () => {
142
+ let resolvePromise;
143
+ const promise = new Promise((resolve) => {
144
+ resolvePromise = resolve;
145
+ });
146
+ jest.mocked(mockAuthApi.getToken).mockReturnValue(promise);
147
+ const { result, unmount } = renderHook(() => useTamboSessionToken(mockTamboAI, "user-token"));
148
+ expect(result.current).toBeNull();
149
+ // Unmount before the promise resolves
150
+ unmount();
151
+ // Now resolve the promise
152
+ act(() => {
153
+ resolvePromise(mockTokenResponse);
154
+ });
155
+ // Token should still be null since component was unmounted
156
+ expect(result.current).toBeNull();
157
+ });
158
+ });
159
+ //# sourceMappingURL=use-tambo-session-token.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.test.js","sourceRoot":"","sources":["../../../../src/providers/hooks/__tests__/use-tambo-session-token.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { act, renderHook } from \"@testing-library/react\";\nimport { DeepPartial } from \"ts-essentials\";\nimport { useTamboSessionToken } from \"../use-tambo-session-token\";\n\ntype PartialTamboAI = DeepPartial<TamboAI>;\n\ndescribe(\"useTamboSessionToken\", () => {\n const mockTokenResponse = {\n access_token: \"test-access-token\",\n expires_in: 3600, // 1 hour\n token_type: \"Bearer\",\n };\n\n const mockAuthApi = {\n getToken: jest.fn(),\n } satisfies DeepPartial<TamboAI[\"beta\"][\"auth\"]>;\n\n const mockBeta = {\n auth: mockAuthApi,\n } satisfies PartialTamboAI[\"beta\"];\n\n const mockTamboAI = {\n apiKey: \"\",\n beta: mockBeta,\n bearer: \"\",\n } satisfies PartialTamboAI as unknown as TamboAI;\n\n beforeEach(() => {\n jest.clearAllMocks();\n jest.clearAllTimers();\n jest.useFakeTimers();\n (mockTamboAI as any).bearer = \"\";\n });\n\n afterEach(() => {\n jest.runOnlyPendingTimers();\n jest.useRealTimers();\n });\n\n it(\"should return null initially when no userToken is provided\", () => {\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, undefined),\n );\n\n expect(result.current).toBeNull();\n expect(mockAuthApi.getToken).not.toHaveBeenCalled();\n });\n\n it(\"should fetch and return session token when userToken is provided\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockTamboAI.bearer).toBe(\"test-access-token\");\n // Verify the hook was called with correct parameters\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);\n expect(mockAuthApi.getToken).toHaveBeenCalledWith(expect.any(Object));\n });\n\n it(\"should call getToken with correct token exchange parameters\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n renderHook(() => useTamboSessionToken(mockTamboAI, \"user-token\"));\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n const callArgs = jest.mocked(mockAuthApi.getToken).mock.calls[0][0];\n const tokenRequestString = new TextDecoder().decode(callArgs);\n const tokenRequest = new URLSearchParams(tokenRequestString);\n\n expect(tokenRequest.get(\"grant_type\")).toBe(\n \"urn:ietf:params:oauth:grant-type:token-exchange\",\n );\n expect(tokenRequest.get(\"subject_token\")).toBe(\"user-token\");\n expect(tokenRequest.get(\"subject_token_type\")).toBe(\n \"urn:ietf:params:oauth:token-type:access_token\",\n );\n });\n\n it(\"should set bearer token on client\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockTamboAI.bearer).toBe(\"test-access-token\");\n });\n\n it(\"should handle different token responses\", async () => {\n const customTokenResponse = {\n access_token: \"custom-access-token\",\n expires_in: 7200, // 2 hours\n token_type: \"Bearer\",\n };\n\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(customTokenResponse);\n\n const { result } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"custom-access-token\");\n expect(mockTamboAI.bearer).toBe(\"custom-access-token\");\n });\n\n it(\"should not fetch token when userToken changes to undefined\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result, rerender } = renderHook(\n ({ userToken }) => useTamboSessionToken(mockTamboAI, userToken),\n {\n initialProps: { userToken: \"user-token\" as string | undefined },\n },\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);\n\n // Clear mock and change userToken to undefined\n jest.clearAllMocks();\n\n act(() => {\n rerender({ userToken: undefined });\n });\n\n expect(mockAuthApi.getToken).not.toHaveBeenCalled();\n });\n\n it(\"should refetch token when userToken changes\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result, rerender } = renderHook(\n ({ userToken }) => useTamboSessionToken(mockTamboAI, userToken),\n {\n initialProps: { userToken: \"user-token-1\" },\n },\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(1);\n\n // Mock response for new token\n jest.mocked(mockAuthApi.getToken).mockResolvedValue({\n ...mockTokenResponse,\n access_token: \"new-access-token\",\n });\n\n // Change userToken\n act(() => {\n rerender({ userToken: \"user-token-2\" });\n });\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"new-access-token\");\n expect(mockAuthApi.getToken).toHaveBeenCalledTimes(2);\n });\n\n it(\"should reset token when userToken becomes null\", async () => {\n jest.mocked(mockAuthApi.getToken).mockResolvedValue(mockTokenResponse);\n\n const { result, rerender } = renderHook(\n ({ userToken }) => useTamboSessionToken(mockTamboAI, userToken),\n {\n initialProps: { userToken: \"user-token\" as string | undefined },\n },\n );\n\n await act(async () => {\n await jest.runOnlyPendingTimersAsync();\n });\n\n expect(result.current).toBe(\"test-access-token\");\n\n // Change userToken to undefined\n act(() => {\n rerender({ userToken: undefined });\n });\n\n // Token should remain the same (hook doesn't reset it to null when userToken is undefined)\n expect(result.current).toBe(\"test-access-token\");\n });\n\n it(\"should not update state if component is unmounted during token fetch\", async () => {\n let resolvePromise: (value: any) => void;\n const promise = new Promise((resolve) => {\n resolvePromise = resolve;\n });\n\n jest.mocked(mockAuthApi.getToken).mockReturnValue(promise);\n\n const { result, unmount } = renderHook(() =>\n useTamboSessionToken(mockTamboAI, \"user-token\"),\n );\n\n expect(result.current).toBeNull();\n\n // Unmount before the promise resolves\n unmount();\n\n // Now resolve the promise\n act(() => {\n resolvePromise!(mockTokenResponse);\n });\n\n // Token should still be null since component was unmounted\n expect(result.current).toBeNull();\n });\n});\n"]}
@@ -0,0 +1,15 @@
1
+ import TamboAI from "@tambo-ai/typescript-sdk";
2
+ /**
3
+ * This internal hook is used to get the Tambo session token and keep it
4
+ * refreshed.
5
+ *
6
+ * It will refresh the token when it expires.
7
+ * It will also set the bearer token on the client.
8
+ *
9
+ * This hook is used by the TamboClientProvider.
10
+ * @param client - The Tambo client.
11
+ * @param userToken - The user token.
12
+ * @returns The Tambo session token.
13
+ */
14
+ export declare function useTamboSessionToken(client: TamboAI, userToken: string | undefined): string | null;
15
+ //# sourceMappingURL=use-tambo-session-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.d.ts","sourceRoot":"","sources":["../../../src/providers/hooks/use-tambo-session-token.tsx"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,0BAA0B,CAAC;AAG/C;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAAG,SAAS,iBAiE9B"}
@@ -0,0 +1,62 @@
1
+ "use client";
2
+ import { useEffect, useState } from "react";
3
+ /**
4
+ * This internal hook is used to get the Tambo session token and keep it
5
+ * refreshed.
6
+ *
7
+ * It will refresh the token when it expires.
8
+ * It will also set the bearer token on the client.
9
+ *
10
+ * This hook is used by the TamboClientProvider.
11
+ * @param client - The Tambo client.
12
+ * @param userToken - The user token.
13
+ * @returns The Tambo session token.
14
+ */
15
+ export function useTamboSessionToken(client, userToken) {
16
+ const [tamboSessionToken, setTamboSessionToken] = useState(null);
17
+ // we need to set this to true when the token is expired, this is effectively
18
+ // like a dirty bit, which will trigger a new useEffect()
19
+ const [isExpired, setIsExpired] = useState(true);
20
+ useEffect(() => {
21
+ let expireTimer = null;
22
+ async function updateToken(subjectToken, abortController) {
23
+ if (abortController.signal.aborted || !userToken) {
24
+ return;
25
+ }
26
+ const tokenRequest = {
27
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
28
+ subject_token: subjectToken,
29
+ subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
30
+ };
31
+ const tokenRequestFormEncoded = new URLSearchParams(tokenRequest).toString();
32
+ const tokenAsArrayBuffer = new TextEncoder().encode(tokenRequestFormEncoded);
33
+ const tamboToken = await client.beta.auth.getToken(tokenAsArrayBuffer);
34
+ if (abortController.signal.aborted) {
35
+ return;
36
+ }
37
+ setTamboSessionToken(tamboToken.access_token);
38
+ client.bearer = tamboToken.access_token;
39
+ // we need to set a timer to refresh the token when it expires
40
+ const refreshTime = Math.max(tamboToken.expires_in - 60, 0);
41
+ // careful with the assignment here: since this is an async function, this
42
+ // code is executed outside the of the scope of the useEffect() hook, so
43
+ // we need to use a let variable to store the timer
44
+ expireTimer = setTimeout(() => {
45
+ setIsExpired(true);
46
+ }, refreshTime * 1000);
47
+ }
48
+ const abortController = new AbortController();
49
+ if (userToken && isExpired) {
50
+ updateToken(userToken, abortController);
51
+ }
52
+ return () => {
53
+ // This fires when the component unmounts or the userToken changes
54
+ abortController.abort();
55
+ if (expireTimer) {
56
+ clearTimeout(expireTimer);
57
+ }
58
+ };
59
+ }, [client, isExpired, userToken]);
60
+ return tamboSessionToken;
61
+ }
62
+ //# sourceMappingURL=use-tambo-session-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-session-token.js","sourceRoot":"","sources":["../../../src/providers/hooks/use-tambo-session-token.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAe,EACf,SAA6B;IAE7B,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CACxD,IAAI,CACL,CAAC;IACF,6EAA6E;IAC7E,yDAAyD;IACzD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,WAAW,GAA0B,IAAI,CAAC;QAE9C,KAAK,UAAU,WAAW,CACxB,YAAoB,EACpB,eAAgC;YAEhC,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjD,OAAO;YACT,CAAC;YACD,MAAM,YAAY,GAAG;gBACnB,UAAU,EAAE,iDAAiD;gBAC7D,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,+CAA+C;aACpE,CAAC;YACF,MAAM,uBAAuB,GAAG,IAAI,eAAe,CACjD,YAAY,CACb,CAAC,QAAQ,EAAE,CAAC;YACb,MAAM,kBAAkB,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CACjD,uBAAuB,CACxB,CAAC;YACF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAChD,kBAAyB,CAC1B,CAAC;YAEF,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnC,OAAO;YACT,CAAC;YACD,oBAAoB,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC;YAExC,8DAA8D;YAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;YAE5D,0EAA0E;YAC1E,wEAAwE;YACxE,mDAAmD;YACnD,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,YAAY,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC;QACzB,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;YAC3B,WAAW,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;QAC1C,CAAC;QAED,OAAO,GAAG,EAAE;YACV,kEAAkE;YAClE,eAAe,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,WAAW,EAAE,CAAC;gBAChB,YAAY,CAAC,WAAW,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IAEnC,OAAO,iBAAiB,CAAC;AAC3B,CAAC","sourcesContent":["\"use client\";\nimport TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { useEffect, useState } from \"react\";\n\n/**\n * This internal hook is used to get the Tambo session token and keep it\n * refreshed.\n *\n * It will refresh the token when it expires.\n * It will also set the bearer token on the client.\n *\n * This hook is used by the TamboClientProvider.\n * @param client - The Tambo client.\n * @param userToken - The user token.\n * @returns The Tambo session token.\n */\nexport function useTamboSessionToken(\n client: TamboAI,\n userToken: string | undefined,\n) {\n const [tamboSessionToken, setTamboSessionToken] = useState<string | null>(\n null,\n );\n // we need to set this to true when the token is expired, this is effectively\n // like a dirty bit, which will trigger a new useEffect()\n const [isExpired, setIsExpired] = useState(true);\n useEffect(() => {\n let expireTimer: NodeJS.Timeout | null = null;\n\n async function updateToken(\n subjectToken: string,\n abortController: AbortController,\n ) {\n if (abortController.signal.aborted || !userToken) {\n return;\n }\n const tokenRequest = {\n grant_type: \"urn:ietf:params:oauth:grant-type:token-exchange\",\n subject_token: subjectToken,\n subject_token_type: \"urn:ietf:params:oauth:token-type:access_token\",\n };\n const tokenRequestFormEncoded = new URLSearchParams(\n tokenRequest,\n ).toString();\n const tokenAsArrayBuffer = new TextEncoder().encode(\n tokenRequestFormEncoded,\n );\n const tamboToken = await client.beta.auth.getToken(\n tokenAsArrayBuffer as any,\n );\n\n if (abortController.signal.aborted) {\n return;\n }\n setTamboSessionToken(tamboToken.access_token);\n client.bearer = tamboToken.access_token;\n\n // we need to set a timer to refresh the token when it expires\n const refreshTime = Math.max(tamboToken.expires_in - 60, 0);\n\n // careful with the assignment here: since this is an async function, this\n // code is executed outside the of the scope of the useEffect() hook, so\n // we need to use a let variable to store the timer\n expireTimer = setTimeout(() => {\n setIsExpired(true);\n }, refreshTime * 1000);\n }\n\n const abortController = new AbortController();\n if (userToken && isExpired) {\n updateToken(userToken, abortController);\n }\n\n return () => {\n // This fires when the component unmounts or the userToken changes\n abortController.abort();\n if (expireTimer) {\n clearTimeout(expireTimer);\n }\n };\n }, [client, isExpired, userToken]);\n\n return tamboSessionToken;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-client-provider.d.ts","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":"AACA,OAAO,OAA0B,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,EAEZ,iBAAiB,EAGlB,MAAM,OAAO,CAAC;AAGf,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAEvC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,yBAAyB;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,sCAAsC;IACtC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,eAAO,MAAM,kBAAkB,oDAEnB,CAAC;AAEb;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CACxC,iBAAiB,CAAC,wBAAwB,CAAC,CAyB5C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,eAM1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,mBAQ/B,CAAC"}
1
+ {"version":3,"file":"tambo-client-provider.d.ts","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":"AACA,OAAO,OAA0B,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,EAAiB,iBAAiB,EAAY,MAAM,OAAO,CAAC;AAI1E,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAEvC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,yBAAyB;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,sCAAsC;IACtC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,eAAO,MAAM,kBAAkB,oDAEnB,CAAC;AAEb;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CACxC,iBAAiB,CAAC,wBAAwB,CAAC,CAyB5C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,eAM1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,mBAQ/B,CAAC"}
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  import TamboAI from "@tambo-ai/typescript-sdk";
3
3
  import { QueryClient } from "@tanstack/react-query";
4
- import React, { createContext, useEffect, useState, } from "react";
4
+ import React, { createContext, useState } from "react";
5
5
  import packageJson from "../../package.json";
6
+ import { useTamboSessionToken } from "./hooks/use-tambo-session-token";
6
7
  export const TamboClientContext = createContext(undefined);
7
8
  /**
8
9
  * The TamboClientProvider is a React provider that provides a TamboAI client
@@ -59,51 +60,4 @@ export const useTamboQueryClient = () => {
59
60
  }
60
61
  return context.queryClient;
61
62
  };
62
- function useTamboSessionToken(client, userToken) {
63
- const [tamboSessionToken, setTamboSessionToken] = useState(null);
64
- // we need to set this to true when the token is expired, this is effectively
65
- // like a dirty bit, which will trigger a new useEffect()
66
- const [isExpired, setIsExpired] = useState(true);
67
- useEffect(() => {
68
- let expireTimer = null;
69
- async function updateToken(subjectToken, abortController) {
70
- if (abortController.signal.aborted || !userToken) {
71
- return;
72
- }
73
- const tokenRequest = {
74
- grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
75
- subject_token: subjectToken,
76
- subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
77
- };
78
- const tokenRequestFormEncoded = new URLSearchParams(tokenRequest).toString();
79
- const tokenAsArrayBuffer = new TextEncoder().encode(tokenRequestFormEncoded);
80
- const tamboToken = await client.beta.auth.getToken(tokenAsArrayBuffer);
81
- if (abortController.signal.aborted) {
82
- return;
83
- }
84
- setTamboSessionToken(tamboToken.access_token);
85
- client.bearer = tamboToken.access_token;
86
- // we need to set a timer to refresh the token when it expires
87
- const refreshTime = Math.max(tamboToken.expires_in - 60, 0);
88
- // careful with the assignment here: since this is an async function, this
89
- // code is executed outside the of the scope of the useEffect() hook, so
90
- // we need to use a let variable to store the timer
91
- expireTimer = setTimeout(() => {
92
- setIsExpired(true);
93
- }, refreshTime * 1000);
94
- }
95
- const abortController = new AbortController();
96
- if (userToken && isExpired) {
97
- updateToken(userToken, abortController);
98
- }
99
- return () => {
100
- // This fires when the component unmounts or the userToken changes
101
- abortController.abort();
102
- if (expireTimer) {
103
- clearTimeout(expireTimer);
104
- }
105
- };
106
- }, [client, isExpired, userToken]);
107
- return tamboSessionToken;
108
- }
109
63
  //# sourceMappingURL=tambo-client-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-client-provider.js","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport TamboAI, { ClientOptions } from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport React, {\n createContext,\n PropsWithChildren,\n useEffect,\n useState,\n} from \"react\";\nimport packageJson from \"../../package.json\";\n\nexport interface TamboClientProviderProps {\n /**\n * The URL of the Tambo API (only used for local development and debugging)\n */\n tamboUrl?: string;\n /**\n * The API key for the Tambo API. This typically comes from a variable like\n * `process.env.NEXT_PUBLIC_TAMBO_API_KEY`\n */\n apiKey: string;\n /**\n * The environment to use for the Tambo API\n */\n environment?: \"production\" | \"staging\";\n\n /**\n * The user token to use to identify the user in the Tambo API. This token is\n * a 3rd party token like a Google or GitHub access token, exchanged with the\n * Tambo API to get a session token. This is used to securely identify the\n * user when calling the Tambo API.\n */\n userToken?: string;\n}\n\nexport interface TamboClientContextProps {\n /** The TamboAI client */\n client: TamboAI;\n /** The tambo-specific query client */\n queryClient: QueryClient;\n}\n\nexport const TamboClientContext = createContext<\n TamboClientContextProps | undefined\n>(undefined);\n\n/**\n * The TamboClientProvider is a React provider that provides a TamboAI client\n * and a query client to the descendants of the provider.\n * @param props - The props for the TamboClientProvider\n * @param props.children - The children to wrap\n * @param props.tamboUrl - The URL of the Tambo API\n * @param props.apiKey - The API key for the Tambo API\n * @param props.environment - The environment to use for the Tambo API\n * @param props.userToken - The oauth access token to use to identify the user in the Tambo API\n * @returns The TamboClientProvider component\n */\nexport const TamboClientProvider: React.FC<\n PropsWithChildren<TamboClientProviderProps>\n> = ({ children, tamboUrl, apiKey, environment, userToken }) => {\n const tamboConfig: ClientOptions = {\n apiKey,\n defaultHeaders: {\n \"X-Tambo-React-Version\": packageJson.version,\n },\n };\n if (tamboUrl) {\n tamboConfig.baseURL = tamboUrl;\n }\n if (environment) {\n tamboConfig.environment = environment;\n }\n const [client] = useState(() => new TamboAI(tamboConfig));\n const [queryClient] = useState(() => new QueryClient());\n\n // Keep the session token updated\n useTamboSessionToken(client, userToken);\n\n return (\n <TamboClientContext.Provider value={{ client, queryClient }}>\n {children}\n </TamboClientContext.Provider>\n );\n};\n\n/**\n * The useTamboClient hook provides access to the TamboAI client\n * to the descendants of the TamboClientProvider.\n * @returns The TamboAI client\n */\nexport const useTamboClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\"useTamboClient must be used within a TamboClientProvider\");\n }\n return context.client;\n};\n\n/**\n * The useTamboQueryClient hook provides access to the tambo-specific query client\n * to the descendants of the TamboClientProvider.\n * @returns The tambo-specific query client\n * @private\n */\nexport const useTamboQueryClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\n \"useTamboQueryClient must be used within a TamboClientProvider\",\n );\n }\n return context.queryClient;\n};\n\nfunction useTamboSessionToken(client: TamboAI, userToken: string | undefined) {\n const [tamboSessionToken, setTamboSessionToken] = useState<string | null>(\n null,\n );\n // we need to set this to true when the token is expired, this is effectively\n // like a dirty bit, which will trigger a new useEffect()\n const [isExpired, setIsExpired] = useState(true);\n useEffect(() => {\n let expireTimer: NodeJS.Timeout | null = null;\n\n async function updateToken(\n subjectToken: string,\n abortController: AbortController,\n ) {\n if (abortController.signal.aborted || !userToken) {\n return;\n }\n const tokenRequest = {\n grant_type: \"urn:ietf:params:oauth:grant-type:token-exchange\",\n subject_token: subjectToken,\n subject_token_type: \"urn:ietf:params:oauth:token-type:access_token\",\n };\n const tokenRequestFormEncoded = new URLSearchParams(\n tokenRequest,\n ).toString();\n const tokenAsArrayBuffer = new TextEncoder().encode(\n tokenRequestFormEncoded,\n );\n const tamboToken = await client.beta.auth.getToken(\n tokenAsArrayBuffer as any,\n );\n\n if (abortController.signal.aborted) {\n return;\n }\n setTamboSessionToken(tamboToken.access_token);\n client.bearer = tamboToken.access_token;\n\n // we need to set a timer to refresh the token when it expires\n const refreshTime = Math.max(tamboToken.expires_in - 60, 0);\n\n // careful with the assignment here: since this is an async function, this\n // code is executed outside the of the scope of the useEffect() hook, so\n // we need to use a let variable to store the timer\n expireTimer = setTimeout(() => {\n setIsExpired(true);\n }, refreshTime * 1000);\n }\n\n const abortController = new AbortController();\n if (userToken && isExpired) {\n updateToken(userToken, abortController);\n }\n\n return () => {\n // This fires when the component unmounts or the userToken changes\n abortController.abort();\n if (expireTimer) {\n clearTimeout(expireTimer);\n }\n };\n }, [client, isExpired, userToken]);\n\n return tamboSessionToken;\n}\n"]}
1
+ {"version":3,"file":"tambo-client-provider.js","sourceRoot":"","sources":["../../src/providers/tambo-client-provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AACb,OAAO,OAA0B,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EAAE,EAAE,aAAa,EAAqB,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC1E,OAAO,WAAW,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAiCvE,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAE7C,SAAS,CAAC,CAAC;AAEb;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAE5B,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE;IAC7D,MAAM,WAAW,GAAkB;QACjC,MAAM;QACN,cAAc,EAAE;YACd,uBAAuB,EAAE,WAAW,CAAC,OAAO;SAC7C;KACF,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACjC,CAAC;IACD,IAAI,WAAW,EAAE,CAAC;QAChB,WAAW,CAAC,WAAW,GAAG,WAAW,CAAC;IACxC,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC;IAExD,iCAAiC;IACjC,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAExC,OAAO,CACL,oBAAC,kBAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,IACxD,QAAQ,CACmB,CAC/B,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,EAAE;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,EAAE;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,+DAA+D,CAChE,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,WAAW,CAAC;AAC7B,CAAC,CAAC","sourcesContent":["\"use client\";\nimport TamboAI, { ClientOptions } from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport React, { createContext, PropsWithChildren, useState } from \"react\";\nimport packageJson from \"../../package.json\";\nimport { useTamboSessionToken } from \"./hooks/use-tambo-session-token\";\n\nexport interface TamboClientProviderProps {\n /**\n * The URL of the Tambo API (only used for local development and debugging)\n */\n tamboUrl?: string;\n /**\n * The API key for the Tambo API. This typically comes from a variable like\n * `process.env.NEXT_PUBLIC_TAMBO_API_KEY`\n */\n apiKey: string;\n /**\n * The environment to use for the Tambo API\n */\n environment?: \"production\" | \"staging\";\n\n /**\n * The user token to use to identify the user in the Tambo API. This token is\n * a 3rd party token like a Google or GitHub access token, exchanged with the\n * Tambo API to get a session token. This is used to securely identify the\n * user when calling the Tambo API.\n */\n userToken?: string;\n}\n\nexport interface TamboClientContextProps {\n /** The TamboAI client */\n client: TamboAI;\n /** The tambo-specific query client */\n queryClient: QueryClient;\n}\n\nexport const TamboClientContext = createContext<\n TamboClientContextProps | undefined\n>(undefined);\n\n/**\n * The TamboClientProvider is a React provider that provides a TamboAI client\n * and a query client to the descendants of the provider.\n * @param props - The props for the TamboClientProvider\n * @param props.children - The children to wrap\n * @param props.tamboUrl - The URL of the Tambo API\n * @param props.apiKey - The API key for the Tambo API\n * @param props.environment - The environment to use for the Tambo API\n * @param props.userToken - The oauth access token to use to identify the user in the Tambo API\n * @returns The TamboClientProvider component\n */\nexport const TamboClientProvider: React.FC<\n PropsWithChildren<TamboClientProviderProps>\n> = ({ children, tamboUrl, apiKey, environment, userToken }) => {\n const tamboConfig: ClientOptions = {\n apiKey,\n defaultHeaders: {\n \"X-Tambo-React-Version\": packageJson.version,\n },\n };\n if (tamboUrl) {\n tamboConfig.baseURL = tamboUrl;\n }\n if (environment) {\n tamboConfig.environment = environment;\n }\n const [client] = useState(() => new TamboAI(tamboConfig));\n const [queryClient] = useState(() => new QueryClient());\n\n // Keep the session token updated\n useTamboSessionToken(client, userToken);\n\n return (\n <TamboClientContext.Provider value={{ client, queryClient }}>\n {children}\n </TamboClientContext.Provider>\n );\n};\n\n/**\n * The useTamboClient hook provides access to the TamboAI client\n * to the descendants of the TamboClientProvider.\n * @returns The TamboAI client\n */\nexport const useTamboClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\"useTamboClient must be used within a TamboClientProvider\");\n }\n return context.client;\n};\n\n/**\n * The useTamboQueryClient hook provides access to the tambo-specific query client\n * to the descendants of the TamboClientProvider.\n * @returns The tambo-specific query client\n * @private\n */\nexport const useTamboQueryClient = () => {\n const context = React.useContext(TamboClientContext);\n if (context === undefined) {\n throw new Error(\n \"useTamboQueryClient must be used within a TamboClientProvider\",\n );\n }\n return context.queryClient;\n};\n"]}
package/esm/setupTests.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import "@tambo-ai/typescript-sdk/shims/node";
2
2
  import "@testing-library/jest-dom";
3
+ import { TextDecoder, TextEncoder } from "util";
3
4
  // Mock Date.now() to return a fixed timestamp for consistent testing
4
5
  const mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);
5
6
  global.Date.now = jest.fn(() => mockDate.getTime());
7
+ // Add TextEncoder/TextDecoder polyfills for Node.js test environment
8
+ Object.assign(global, { TextEncoder, TextDecoder });
6
9
  //# sourceMappingURL=setupTests.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":"AAAA,OAAO,qCAAqC,CAAC;AAC7C,OAAO,2BAA2B,CAAC;AAEnC,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC","sourcesContent":["import \"@tambo-ai/typescript-sdk/shims/node\";\nimport \"@testing-library/jest-dom\";\n\n// Mock Date.now() to return a fixed timestamp for consistent testing\nconst mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);\nglobal.Date.now = jest.fn(() => mockDate.getTime());\n"]}
1
+ {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../src/setupTests.ts"],"names":[],"mappings":"AAAA,OAAO,qCAAqC,CAAC;AAC7C,OAAO,2BAA2B,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAEhD,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AACvD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;AAEpD,qEAAqE;AACrE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC","sourcesContent":["import \"@tambo-ai/typescript-sdk/shims/node\";\nimport \"@testing-library/jest-dom\";\nimport { TextDecoder, TextEncoder } from \"util\";\n\n// Mock Date.now() to return a fixed timestamp for consistent testing\nconst mockDate = new Date(2025, 0, 5, 12, 32, 58, 936);\nglobal.Date.now = jest.fn(() => mockDate.getTime());\n\n// Add TextEncoder/TextDecoder polyfills for Node.js test environment\nObject.assign(global, { TextEncoder, TextDecoder });\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tambo-ai/react",
3
- "version": "0.37.0",
3
+ "version": "0.37.1",
4
4
  "description": "React client package for Tambo AI",
5
5
  "repository": {
6
6
  "type": "git",