@thru/react-sdk 0.0.4

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,47 @@
1
+ import { BrowserSDK, BrowserSDKConfig, ConnectOptions } from '@thru/browser-sdk';
2
+ export { BrowserSDK, BrowserSDKConfig, ConnectOptions, ErrorCode, SDKEvent } from '@thru/browser-sdk';
3
+ import * as _thru_chain_interfaces from '@thru/chain-interfaces';
4
+ import { WalletAddress, IThruChain, ConnectResult } from '@thru/chain-interfaces';
5
+ export { ConnectResult, IThruChain, SignMessageParams, SignMessageResult, WalletAddress } from '@thru/chain-interfaces';
6
+ import { Thru } from '@thru/thru-sdk';
7
+ import * as react_jsx_runtime from 'react/jsx-runtime';
8
+ import { ReactNode } from 'react';
9
+
10
+ interface ThruContextValue {
11
+ wallet: BrowserSDK | null;
12
+ isConnected: boolean;
13
+ addresses: WalletAddress[];
14
+ isConnecting: boolean;
15
+ error: Error | null;
16
+ thru: Thru | null;
17
+ }
18
+
19
+ interface ThruProviderProps {
20
+ children: ReactNode;
21
+ config: BrowserSDKConfig;
22
+ }
23
+ /**
24
+ * ThruProvider - React context provider for Thru Wallet SDK
25
+ * Wraps the BrowserSDK and exposes state via context
26
+ */
27
+ declare function ThruProvider({ children, config }: ThruProviderProps): react_jsx_runtime.JSX.Element;
28
+
29
+ /**
30
+ * useThru - Access the Thru SDK context
31
+ * Must be used within a ThruProvider
32
+ */
33
+ declare function useThru(): ThruContextValue;
34
+
35
+ /**
36
+ * useThruChain - Hook for accessing the Thru chain API exposed by the Browser SDK.
37
+ * Returns the chain instance (if available) and a boolean indicating readiness.
38
+ */
39
+ declare function useWallet(): {
40
+ wallet: IThruChain | undefined;
41
+ addresses: _thru_chain_interfaces.WalletAddress[];
42
+ connect: (options?: ConnectOptions) => Promise<ConnectResult>;
43
+ disconnect: () => Promise<void>;
44
+ isConnected: boolean;
45
+ };
46
+
47
+ export { type ThruContextValue, ThruProvider, type ThruProviderProps, useThru, useWallet };
package/dist/index.js ADDED
@@ -0,0 +1,132 @@
1
+ "use client";
2
+ import { BrowserSDK } from '@thru/browser-sdk';
3
+ export { BrowserSDK, ErrorCode } from '@thru/browser-sdk';
4
+ import { createContext, useState, useEffect, useContext, useRef } from 'react';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ var defaultContextValue = {
8
+ wallet: null,
9
+ isConnected: false,
10
+ addresses: [],
11
+ isConnecting: false,
12
+ error: null,
13
+ thru: null
14
+ };
15
+ var ThruContext = createContext(defaultContextValue);
16
+ function ThruProvider({ children, config }) {
17
+ const [sdk, setSdk] = useState(null);
18
+ const [thru, setThru] = useState(null);
19
+ const [isConnected, setIsConnected] = useState(false);
20
+ const [addresses, setAddresses] = useState([]);
21
+ const [isConnecting, setIsConnecting] = useState(false);
22
+ const [error, setError] = useState(null);
23
+ useEffect(() => {
24
+ const sdkInstance = new BrowserSDK(config);
25
+ setSdk(sdkInstance);
26
+ setThru(sdkInstance.getThru());
27
+ sdkInstance.initialize().catch((err) => {
28
+ console.error("Failed to initialize SDK:", err);
29
+ setError(err);
30
+ });
31
+ const handleConnect = (result) => {
32
+ if (result.status === "connecting") {
33
+ setIsConnecting(true);
34
+ setError(null);
35
+ } else {
36
+ setIsConnected(true);
37
+ setAddresses(sdkInstance.getAddresses());
38
+ setIsConnecting(false);
39
+ setError(null);
40
+ }
41
+ };
42
+ const handleDisconnect = () => {
43
+ setIsConnected(false);
44
+ setAddresses([]);
45
+ setIsConnecting(false);
46
+ };
47
+ const handleError = (err) => {
48
+ setError(err.error || new Error("Unknown error"));
49
+ setIsConnecting(false);
50
+ };
51
+ const handleLock = () => {
52
+ setIsConnected(false);
53
+ setAddresses([]);
54
+ setIsConnecting(false);
55
+ };
56
+ sdkInstance.on("connect", handleConnect);
57
+ sdkInstance.on("disconnect", handleDisconnect);
58
+ sdkInstance.on("error", handleError);
59
+ sdkInstance.on("lock", handleLock);
60
+ return () => {
61
+ sdkInstance.off("connect", handleConnect);
62
+ sdkInstance.off("disconnect", handleDisconnect);
63
+ sdkInstance.off("error", handleError);
64
+ sdkInstance.off("lock", handleLock);
65
+ sdkInstance.destroy();
66
+ };
67
+ }, []);
68
+ return /* @__PURE__ */ jsx(
69
+ ThruContext.Provider,
70
+ {
71
+ value: {
72
+ thru,
73
+ wallet: sdk,
74
+ isConnected,
75
+ addresses,
76
+ isConnecting,
77
+ error
78
+ },
79
+ children
80
+ }
81
+ );
82
+ }
83
+ function useThru() {
84
+ const context = useContext(ThruContext);
85
+ return context;
86
+ }
87
+ function waitForWallet(getWallet, timeout = 5e3, interval = 100) {
88
+ return new Promise((resolve, reject) => {
89
+ const start = Date.now();
90
+ const check = () => {
91
+ const sdk = getWallet();
92
+ if (sdk) return resolve(sdk);
93
+ if (Date.now() - start > timeout) return reject(new Error("SDK not initialized in time"));
94
+ setTimeout(check, interval);
95
+ };
96
+ check();
97
+ });
98
+ }
99
+ function useWallet() {
100
+ const { wallet, isConnected, addresses } = useThru();
101
+ const walletRef = useRef(wallet);
102
+ useEffect(() => {
103
+ walletRef.current = wallet;
104
+ }, [wallet]);
105
+ const disconnect = async () => {
106
+ if (!wallet) {
107
+ throw new Error("SDK not initialized");
108
+ }
109
+ await wallet.disconnect();
110
+ };
111
+ const connect = async (options) => {
112
+ try {
113
+ const readySdk = walletRef.current ?? await waitForWallet(() => walletRef.current);
114
+ const result = await readySdk.connect(options);
115
+ return result;
116
+ } catch (err) {
117
+ const error = err;
118
+ throw error;
119
+ }
120
+ };
121
+ return {
122
+ wallet: wallet?.thru,
123
+ addresses,
124
+ connect,
125
+ disconnect,
126
+ isConnected: isConnected && !!wallet
127
+ };
128
+ }
129
+
130
+ export { ThruProvider, useThru, useWallet };
131
+ //# sourceMappingURL=index.js.map
132
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ThruContext.ts","../src/ThruProvider.tsx","../src/hooks/useThru.ts","../src/hooks/useWallet.ts"],"names":["useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { BrowserSDK } from \"@thru/browser-sdk\";\nimport { WalletAddress } from \"@thru/chain-interfaces\";\nimport { Thru } from \"@thru/thru-sdk\";\nimport { createContext } from \"react\";\n\nexport interface ThruContextValue {\n wallet: BrowserSDK | null;\n isConnected: boolean;\n addresses: WalletAddress[];\n isConnecting: boolean;\n error: Error | null;\n thru: Thru | null;\n}\n\nconst defaultContextValue: ThruContextValue = {\n wallet: null,\n isConnected: false,\n addresses: [],\n isConnecting: false,\n error: null,\n thru: null,\n};\n\nexport const ThruContext = createContext<ThruContextValue>(defaultContextValue);\n","'use client';\n\nimport { BrowserSDK, type BrowserSDKConfig, type WalletAddress } from '@thru/browser-sdk';\nimport type { Thru } from '@thru/thru-sdk';\nimport { ReactNode, useEffect, useState } from 'react';\nimport { ThruContext } from './ThruContext';\n\nexport interface ThruProviderProps {\n children: ReactNode;\n config: BrowserSDKConfig;\n}\n\n/**\n * ThruProvider - React context provider for Thru Wallet SDK\n * Wraps the BrowserSDK and exposes state via context\n */\nexport function ThruProvider({ children, config }: ThruProviderProps) {\n const [sdk, setSdk] = useState<BrowserSDK | null>(null);\n const [thru, setThru] = useState<Thru | null>(null);\n const [isConnected, setIsConnected] = useState(false);\n const [addresses, setAddresses] = useState<WalletAddress[]>([]);\n const [isConnecting, setIsConnecting] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n // Create SDK instance\n const sdkInstance = new BrowserSDK(config);\n setSdk(sdkInstance);\n\n setThru(sdkInstance.getThru());\n\n // Initialize SDK (creates iframe)\n sdkInstance.initialize().catch((err) => {\n console.error('Failed to initialize SDK:', err);\n setError(err);\n });\n\n // Listen to SDK events\n const handleConnect = (result: any) => {\n // Check if this is the initial \"connecting\" status or actual connection\n if (result.status === 'connecting') {\n setIsConnecting(true);\n setError(null);\n } else {\n setIsConnected(true);\n setAddresses(sdkInstance.getAddresses());\n setIsConnecting(false);\n setError(null);\n }\n };\n\n const handleDisconnect = () => {\n setIsConnected(false);\n setAddresses([]);\n setIsConnecting(false);\n };\n\n const handleError = (err: any) => {\n setError(err.error || new Error('Unknown error'));\n setIsConnecting(false);\n };\n\n const handleLock = () => {\n setIsConnected(false);\n setAddresses([]);\n setIsConnecting(false);\n };\n\n sdkInstance.on('connect', handleConnect);\n sdkInstance.on('disconnect', handleDisconnect);\n sdkInstance.on('error', handleError);\n sdkInstance.on('lock', handleLock);\n\n // Cleanup on unmount\n return () => {\n sdkInstance.off('connect', handleConnect);\n sdkInstance.off('disconnect', handleDisconnect);\n sdkInstance.off('error', handleError);\n sdkInstance.off('lock', handleLock);\n sdkInstance.destroy();\n };\n }, []); // Empty dependency array - only create SDK once\n\n return (\n <ThruContext.Provider\n value={{\n thru,\n wallet: sdk,\n isConnected,\n addresses,\n isConnecting,\n error,\n }}\n >\n {children}\n </ThruContext.Provider>\n );\n}\n","import { useContext } from \"react\";\nimport { ThruContext } from \"../ThruContext\";\n\n/**\n * useThru - Access the Thru SDK context\n * Must be used within a ThruProvider\n */\nexport function useThru() {\n const context = useContext(ThruContext);\n return context;\n}","import { BrowserSDK, ConnectOptions } from '@thru/browser-sdk';\nimport type { ConnectResult, IThruChain } from '@thru/chain-interfaces';\nimport { useEffect, useRef } from 'react';\nimport { useThru } from './useThru';\n\nfunction waitForWallet(getWallet: () => BrowserSDK | null, timeout = 5000, interval = 100): Promise<BrowserSDK> {\n return new Promise((resolve, reject) => {\n const start = Date.now();\n const check = () => {\n const sdk = getWallet();\n if (sdk) return resolve(sdk);\n if (Date.now() - start > timeout) return reject(new Error('SDK not initialized in time'));\n setTimeout(check, interval);\n };\n check();\n });\n}\n\n/**\n * useThruChain - Hook for accessing the Thru chain API exposed by the Browser SDK.\n * Returns the chain instance (if available) and a boolean indicating readiness.\n */\nexport function useWallet() {\n const { wallet, isConnected, addresses } = useThru();\n const walletRef = useRef(wallet);\n\n useEffect(() => {\n walletRef.current = wallet;\n }, [wallet]);\n\n const disconnect = async (): Promise<void> => {\n if (!wallet) {\n throw new Error('SDK not initialized');\n }\n await wallet.disconnect();\n };\n\n const connect = async (options?: ConnectOptions): Promise<ConnectResult> => {\n try {\n const readySdk =\n walletRef.current ?? (await waitForWallet(() => walletRef.current));\n const result = await readySdk.connect(options);\n return result;\n } catch (err) {\n const error = err as Error;\n throw error;\n }\n };\n\n return {\n wallet: wallet?.thru as IThruChain | undefined,\n addresses,\n connect,\n disconnect,\n isConnected: isConnected && !!wallet,\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@thru/react-sdk",
3
+ "version": "0.0.4",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "dependencies": {
14
+ "@thru/browser-sdk": "0.0.4",
15
+ "@thru/chain-interfaces": "0.0.4",
16
+ "@thru/thru-sdk": "0.0.4"
17
+ },
18
+ "peerDependencies": {
19
+ "react": "^18.0.0 || ^19.0.0",
20
+ "react-dom": "^18.0.0 || ^19.0.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19.0.0",
24
+ "@types/react-dom": "^19.0.0"
25
+ },
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "dev": "tsup --watch",
29
+ "lint": "eslint src/",
30
+ "clean": "rm -rf dist"
31
+ }
32
+ }
@@ -0,0 +1,24 @@
1
+ import { BrowserSDK } from "@thru/browser-sdk";
2
+ import { WalletAddress } from "@thru/chain-interfaces";
3
+ import { Thru } from "@thru/thru-sdk";
4
+ import { createContext } from "react";
5
+
6
+ export interface ThruContextValue {
7
+ wallet: BrowserSDK | null;
8
+ isConnected: boolean;
9
+ addresses: WalletAddress[];
10
+ isConnecting: boolean;
11
+ error: Error | null;
12
+ thru: Thru | null;
13
+ }
14
+
15
+ const defaultContextValue: ThruContextValue = {
16
+ wallet: null,
17
+ isConnected: false,
18
+ addresses: [],
19
+ isConnecting: false,
20
+ error: null,
21
+ thru: null,
22
+ };
23
+
24
+ export const ThruContext = createContext<ThruContextValue>(defaultContextValue);
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+
3
+ import { BrowserSDK, type BrowserSDKConfig, type WalletAddress } from '@thru/browser-sdk';
4
+ import type { Thru } from '@thru/thru-sdk';
5
+ import { ReactNode, useEffect, useState } from 'react';
6
+ import { ThruContext } from './ThruContext';
7
+
8
+ export interface ThruProviderProps {
9
+ children: ReactNode;
10
+ config: BrowserSDKConfig;
11
+ }
12
+
13
+ /**
14
+ * ThruProvider - React context provider for Thru Wallet SDK
15
+ * Wraps the BrowserSDK and exposes state via context
16
+ */
17
+ export function ThruProvider({ children, config }: ThruProviderProps) {
18
+ const [sdk, setSdk] = useState<BrowserSDK | null>(null);
19
+ const [thru, setThru] = useState<Thru | null>(null);
20
+ const [isConnected, setIsConnected] = useState(false);
21
+ const [addresses, setAddresses] = useState<WalletAddress[]>([]);
22
+ const [isConnecting, setIsConnecting] = useState(false);
23
+ const [error, setError] = useState<Error | null>(null);
24
+
25
+ useEffect(() => {
26
+ // Create SDK instance
27
+ const sdkInstance = new BrowserSDK(config);
28
+ setSdk(sdkInstance);
29
+
30
+ setThru(sdkInstance.getThru());
31
+
32
+ // Initialize SDK (creates iframe)
33
+ sdkInstance.initialize().catch((err) => {
34
+ console.error('Failed to initialize SDK:', err);
35
+ setError(err);
36
+ });
37
+
38
+ // Listen to SDK events
39
+ const handleConnect = (result: any) => {
40
+ // Check if this is the initial "connecting" status or actual connection
41
+ if (result.status === 'connecting') {
42
+ setIsConnecting(true);
43
+ setError(null);
44
+ } else {
45
+ setIsConnected(true);
46
+ setAddresses(sdkInstance.getAddresses());
47
+ setIsConnecting(false);
48
+ setError(null);
49
+ }
50
+ };
51
+
52
+ const handleDisconnect = () => {
53
+ setIsConnected(false);
54
+ setAddresses([]);
55
+ setIsConnecting(false);
56
+ };
57
+
58
+ const handleError = (err: any) => {
59
+ setError(err.error || new Error('Unknown error'));
60
+ setIsConnecting(false);
61
+ };
62
+
63
+ const handleLock = () => {
64
+ setIsConnected(false);
65
+ setAddresses([]);
66
+ setIsConnecting(false);
67
+ };
68
+
69
+ sdkInstance.on('connect', handleConnect);
70
+ sdkInstance.on('disconnect', handleDisconnect);
71
+ sdkInstance.on('error', handleError);
72
+ sdkInstance.on('lock', handleLock);
73
+
74
+ // Cleanup on unmount
75
+ return () => {
76
+ sdkInstance.off('connect', handleConnect);
77
+ sdkInstance.off('disconnect', handleDisconnect);
78
+ sdkInstance.off('error', handleError);
79
+ sdkInstance.off('lock', handleLock);
80
+ sdkInstance.destroy();
81
+ };
82
+ }, []); // Empty dependency array - only create SDK once
83
+
84
+ return (
85
+ <ThruContext.Provider
86
+ value={{
87
+ thru,
88
+ wallet: sdk,
89
+ isConnected,
90
+ addresses,
91
+ isConnecting,
92
+ error,
93
+ }}
94
+ >
95
+ {children}
96
+ </ThruContext.Provider>
97
+ );
98
+ }
@@ -0,0 +1,11 @@
1
+ import { useContext } from "react";
2
+ import { ThruContext } from "../ThruContext";
3
+
4
+ /**
5
+ * useThru - Access the Thru SDK context
6
+ * Must be used within a ThruProvider
7
+ */
8
+ export function useThru() {
9
+ const context = useContext(ThruContext);
10
+ return context;
11
+ }
@@ -0,0 +1,57 @@
1
+ import { BrowserSDK, ConnectOptions } from '@thru/browser-sdk';
2
+ import type { ConnectResult, IThruChain } from '@thru/chain-interfaces';
3
+ import { useEffect, useRef } from 'react';
4
+ import { useThru } from './useThru';
5
+
6
+ function waitForWallet(getWallet: () => BrowserSDK | null, timeout = 5000, interval = 100): Promise<BrowserSDK> {
7
+ return new Promise((resolve, reject) => {
8
+ const start = Date.now();
9
+ const check = () => {
10
+ const sdk = getWallet();
11
+ if (sdk) return resolve(sdk);
12
+ if (Date.now() - start > timeout) return reject(new Error('SDK not initialized in time'));
13
+ setTimeout(check, interval);
14
+ };
15
+ check();
16
+ });
17
+ }
18
+
19
+ /**
20
+ * useThruChain - Hook for accessing the Thru chain API exposed by the Browser SDK.
21
+ * Returns the chain instance (if available) and a boolean indicating readiness.
22
+ */
23
+ export function useWallet() {
24
+ const { wallet, isConnected, addresses } = useThru();
25
+ const walletRef = useRef(wallet);
26
+
27
+ useEffect(() => {
28
+ walletRef.current = wallet;
29
+ }, [wallet]);
30
+
31
+ const disconnect = async (): Promise<void> => {
32
+ if (!wallet) {
33
+ throw new Error('SDK not initialized');
34
+ }
35
+ await wallet.disconnect();
36
+ };
37
+
38
+ const connect = async (options?: ConnectOptions): Promise<ConnectResult> => {
39
+ try {
40
+ const readySdk =
41
+ walletRef.current ?? (await waitForWallet(() => walletRef.current));
42
+ const result = await readySdk.connect(options);
43
+ return result;
44
+ } catch (err) {
45
+ const error = err as Error;
46
+ throw error;
47
+ }
48
+ };
49
+
50
+ return {
51
+ wallet: wallet?.thru as IThruChain | undefined,
52
+ addresses,
53
+ connect,
54
+ disconnect,
55
+ isConnected: isConnected && !!wallet,
56
+ };
57
+ }
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ // Provider and context
2
+ export type { ThruContextValue } from './ThruContext';
3
+ export { ThruProvider, type ThruProviderProps } from './ThruProvider';
4
+
5
+
6
+ // Hooks
7
+ export { useThru } from './hooks/useThru';
8
+ export { useWallet } from './hooks/useWallet';
9
+
10
+ // Re-export from browser-sdk for convenience
11
+ export { BrowserSDK, ErrorCode, type BrowserSDKConfig, type ConnectOptions, type SDKEvent } from '@thru/browser-sdk';
12
+
13
+ // Re-export types from chain-interfaces
14
+ export type {
15
+ ConnectResult, IThruChain, SignMessageParams,
16
+ SignMessageResult, WalletAddress
17
+ } from '@thru/chain-interfaces';
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "jsx": "react-jsx"
7
+ },
8
+ "include": ["src"],
9
+ "exclude": ["node_modules", "dist"]
10
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { defineConfig } from 'tsup';
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { dirname, join } from 'node:path';
5
+
6
+ export default defineConfig({
7
+ entry: ['src/index.ts'],
8
+ format: ['esm'],
9
+ dts: true,
10
+ sourcemap: true,
11
+ clean: true,
12
+ treeshake: true,
13
+ platform: 'browser',
14
+ banner: {
15
+ js: '"use client";',
16
+ },
17
+ noExternal: ['@thru/chain-interfaces'],
18
+ external: [
19
+ '@thru/browser-sdk',
20
+ '@thru/embedded-provider',
21
+ 'react',
22
+ 'react-dom',
23
+ ],
24
+ onSuccess: () => {
25
+ const distDir = dirname(fileURLToPath(import.meta.url));
26
+ const outputPath = join(distDir, 'dist', 'index.js');
27
+ const content = readFileSync(outputPath, 'utf8');
28
+ if (!content.startsWith('"use client";')) {
29
+ writeFileSync(outputPath, `"use client";\n${content}`);
30
+ }
31
+ },
32
+ });