arlinkauth 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # arlinkauth
2
+
3
+ Authentication SDK for Arweave apps. Sign in with GitHub or Google, get an Arweave wallet automatically.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install arlinkauth
9
+ # or
10
+ bun add arlinkauth
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { createArlinkAuthClient } from 'arlinkauth';
17
+
18
+ const auth = createArlinkAuthClient({
19
+ apiUrl: 'https://your-worker.workers.dev'
20
+ });
21
+
22
+ // Initialize (checks for existing session)
23
+ await auth.init();
24
+
25
+ // Login with GitHub (opens popup)
26
+ await auth.login();
27
+ // or
28
+ await auth.loginWithGoogle();
29
+
30
+ // Get current user
31
+ const user = auth.getState().user;
32
+ console.log(user.arweave_address);
33
+
34
+ // Sign and upload data to Arweave
35
+ const result = await auth.dispatch({
36
+ data: 'Hello Arweave',
37
+ tags: [{ name: 'Content-Type', value: 'text/plain' }]
38
+ });
39
+ console.log(result.id); // Transaction ID
40
+
41
+ // Logout
42
+ auth.logout();
43
+ ```
44
+
45
+ ## React Integration
46
+
47
+ ```tsx
48
+ import { AuthProvider, useAuth } from 'arlinkauth/react';
49
+
50
+ function App() {
51
+ return (
52
+ <AuthProvider apiUrl="https://your-worker.workers.dev">
53
+ <YourApp />
54
+ </AuthProvider>
55
+ );
56
+ }
57
+
58
+ function YourApp() {
59
+ const { user, login, loginWithGoogle, logout, client } = useAuth();
60
+
61
+ if (!user) {
62
+ return (
63
+ <div>
64
+ <button onClick={login}>Sign in with GitHub</button>
65
+ <button onClick={loginWithGoogle}>Sign in with Google</button>
66
+ </div>
67
+ );
68
+ }
69
+
70
+ return (
71
+ <div>
72
+ <p>Welcome, {user.name}</p>
73
+ <p>Arweave address: {user.arweave_address}</p>
74
+ <button onClick={logout}>Sign out</button>
75
+ </div>
76
+ );
77
+ }
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### Client Methods
83
+
84
+ | Method | Description |
85
+ |--------|-------------|
86
+ | `init()` | Initialize client, check for existing session |
87
+ | `login()` | Login with GitHub (default) |
88
+ | `loginWithGithub()` | Login with GitHub |
89
+ | `loginWithGoogle()` | Login with Google |
90
+ | `logout()` | Clear session |
91
+ | `getState()` | Get current auth state |
92
+ | `getToken()` | Get JWT token |
93
+ | `isAuthenticated()` | Check if user is logged in |
94
+ | `onAuthChange(callback)` | Subscribe to auth state changes |
95
+
96
+ ### Wallet Methods
97
+
98
+ | Method | Description |
99
+ |--------|-------------|
100
+ | `sign(input)` | Sign an Arweave L1 transaction |
101
+ | `signDataItem(input)` | Sign an ANS-104 data item |
102
+ | `signature(input)` | Create raw signature of data |
103
+ | `dispatch(input)` | Sign and upload to Arweave via bundler |
104
+
105
+ ### Types
106
+
107
+ ```typescript
108
+ type ArlinkUser = {
109
+ id: string;
110
+ email: string | null;
111
+ name: string | null;
112
+ avatar_url: string | null;
113
+ github_id: number | null;
114
+ github_username: string | null;
115
+ google_id: string | null;
116
+ arweave_address: string | null;
117
+ created_at: string;
118
+ updated_at: string;
119
+ };
120
+
121
+ type AuthState = {
122
+ user: ArlinkUser | null;
123
+ token: string | null;
124
+ isLoading: boolean;
125
+ isAuthenticated: boolean;
126
+ };
127
+ ```
128
+
129
+ ## License
130
+
131
+ MIT
@@ -0,0 +1,22 @@
1
+ import { type WauthClientOptions, type WauthUser, type AuthState, type AuthChangeListener, type OAuthProvider, type SignTransactionInput, type SignTransactionResult, type SignDataItemInput, type SignDataItemResult, type SignatureInput, type SignatureResult, type DispatchInput, type DispatchResult } from "./types.js";
2
+ export declare function createWauthClient(options: WauthClientOptions): {
3
+ init: () => Promise<AuthState>;
4
+ login: () => Promise<boolean>;
5
+ loginWithGithub: () => Promise<boolean>;
6
+ loginWithGoogle: () => Promise<boolean>;
7
+ loginWithProvider: (provider: OAuthProvider) => Promise<boolean>;
8
+ logout: () => void;
9
+ handleCallback: () => boolean;
10
+ isAuthenticated: () => boolean;
11
+ getToken: () => string | null;
12
+ getState: () => AuthState;
13
+ onAuthChange: (listener: AuthChangeListener) => () => void;
14
+ api: {
15
+ getMe: () => Promise<WauthUser>;
16
+ };
17
+ sign: (input: SignTransactionInput) => Promise<SignTransactionResult>;
18
+ signDataItem: (input: SignDataItemInput) => Promise<SignDataItemResult>;
19
+ signature: (input: SignatureInput) => Promise<SignatureResult>;
20
+ dispatch: (input: DispatchInput) => Promise<DispatchResult>;
21
+ };
22
+ export type WauthClient = ReturnType<typeof createWauthClient>;
package/dist/client.js ADDED
@@ -0,0 +1,305 @@
1
+ import { WalletAction, } from "./types.js";
2
+ const DEFAULT_TOKEN_KEY = "arlinkauth_token";
3
+ export function createWauthClient(options) {
4
+ const { apiUrl } = options;
5
+ const tokenKey = options.tokenKey ?? DEFAULT_TOKEN_KEY;
6
+ const listeners = new Set();
7
+ let state = {
8
+ user: null,
9
+ token: getStoredToken(),
10
+ isLoading: false,
11
+ isAuthenticated: false,
12
+ };
13
+ // ── Token Storage ───────────────────────────────────
14
+ function getStoredToken() {
15
+ try {
16
+ return localStorage.getItem(tokenKey);
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ function setStoredToken(token) {
23
+ try {
24
+ localStorage.setItem(tokenKey, token);
25
+ }
26
+ catch {
27
+ // SSR or storage unavailable
28
+ }
29
+ }
30
+ function removeStoredToken() {
31
+ try {
32
+ localStorage.removeItem(tokenKey);
33
+ }
34
+ catch {
35
+ // SSR or storage unavailable
36
+ }
37
+ }
38
+ // ── State Management ────────────────────────────────
39
+ function setState(partial) {
40
+ state = { ...state, ...partial };
41
+ for (const listener of listeners) {
42
+ listener(state);
43
+ }
44
+ }
45
+ function getState() {
46
+ return state;
47
+ }
48
+ function onAuthChange(listener) {
49
+ listeners.add(listener);
50
+ return () => {
51
+ listeners.delete(listener);
52
+ };
53
+ }
54
+ // ── Authenticated Fetch ─────────────────────────────
55
+ async function fetchApi(path, init) {
56
+ const token = state.token;
57
+ if (!token) {
58
+ throw new Error("Not authenticated");
59
+ }
60
+ const res = await fetch(`${apiUrl}${path}`, {
61
+ ...init,
62
+ headers: {
63
+ ...init?.headers,
64
+ Authorization: `Bearer ${token}`,
65
+ },
66
+ });
67
+ if (res.status === 401) {
68
+ // Token is invalid/expired — clear it
69
+ removeStoredToken();
70
+ setState({ token: null, user: null, isAuthenticated: false });
71
+ throw new Error("Session expired");
72
+ }
73
+ if (!res.ok) {
74
+ const body = await res.json().catch(() => ({}));
75
+ throw new Error(body.error ?? `API error: ${res.status}`);
76
+ }
77
+ return res.json();
78
+ }
79
+ // ── Auth Actions ────────────────────────────────────
80
+ /** Open a popup for OAuth login with the specified provider */
81
+ function loginWithProvider(provider) {
82
+ return new Promise((resolve) => {
83
+ const loginUrl = `${apiUrl}/auth/${provider}`;
84
+ const width = 500;
85
+ const height = 700;
86
+ const left = window.screenX + (window.outerWidth - width) / 2;
87
+ const top = window.screenY + (window.outerHeight - height) / 2;
88
+ const popup = window.open(loginUrl, "wauth-login", `width=${width},height=${height},left=${left},top=${top},popup=1`);
89
+ if (!popup) {
90
+ console.error("[arlinkauth] Failed to open popup - check popup blocker");
91
+ resolve(false);
92
+ return;
93
+ }
94
+ // Listen for message from popup
95
+ const handleMessage = async (event) => {
96
+ // Strict origin validation
97
+ if (event.origin !== new URL(apiUrl).origin)
98
+ return;
99
+ // Strict message structure validation
100
+ if (typeof event.data !== "object" || event.data === null)
101
+ return;
102
+ if (event.data.type !== "arlinkauth:callback")
103
+ return;
104
+ if (typeof event.data.token !== "string" || event.data.token.length === 0)
105
+ return;
106
+ // Basic JWT structure validation (header.payload.signature)
107
+ const tokenParts = event.data.token.split(".");
108
+ if (tokenParts.length !== 3)
109
+ return;
110
+ window.removeEventListener("message", handleMessage);
111
+ popup.close();
112
+ // Store token and fetch user
113
+ setStoredToken(event.data.token);
114
+ setState({ token: event.data.token, isLoading: true, isAuthenticated: true });
115
+ try {
116
+ const user = await api.getMe();
117
+ setState({ user, isLoading: false, isAuthenticated: true });
118
+ resolve(true);
119
+ }
120
+ catch {
121
+ removeStoredToken();
122
+ setState({ token: null, user: null, isLoading: false, isAuthenticated: false });
123
+ resolve(false);
124
+ }
125
+ };
126
+ window.addEventListener("message", handleMessage);
127
+ // Clean up if popup is closed without completing auth
128
+ const checkClosed = setInterval(() => {
129
+ if (popup.closed) {
130
+ clearInterval(checkClosed);
131
+ window.removeEventListener("message", handleMessage);
132
+ resolve(false);
133
+ }
134
+ }, 500);
135
+ });
136
+ }
137
+ /** Open a popup for GitHub OAuth login (no page refresh) */
138
+ function login() {
139
+ return loginWithProvider("github");
140
+ }
141
+ /** Open a popup for GitHub OAuth login */
142
+ function loginWithGithub() {
143
+ return loginWithProvider("github");
144
+ }
145
+ /** Open a popup for Google OAuth login */
146
+ function loginWithGoogle() {
147
+ return loginWithProvider("google");
148
+ }
149
+ /**
150
+ * Check for existing token in localStorage.
151
+ * (URL-based callback is no longer used - popup flow uses postMessage)
152
+ */
153
+ function handleCallback() {
154
+ const existing = getStoredToken();
155
+ if (existing) {
156
+ setState({ token: existing, isAuthenticated: true });
157
+ return true;
158
+ }
159
+ return false;
160
+ }
161
+ /** Clear the token and user state */
162
+ function logout() {
163
+ removeStoredToken();
164
+ setState({ token: null, user: null, isAuthenticated: false });
165
+ }
166
+ function isAuthenticated() {
167
+ return state.isAuthenticated;
168
+ }
169
+ function getToken() {
170
+ return state.token;
171
+ }
172
+ // ── API Methods ─────────────────────────────────────
173
+ const api = {
174
+ getMe: () => fetchApi("/api/me"),
175
+ };
176
+ // ── Wallet Signing Methods ─────────────────────────────
177
+ /**
178
+ * Sign an Arweave transaction.
179
+ * The transaction must be created using arweave-js and passed as the raw object.
180
+ */
181
+ async function sign(input) {
182
+ return fetchApi("/api/wallet/action", {
183
+ method: "POST",
184
+ headers: { "Content-Type": "application/json" },
185
+ body: JSON.stringify({
186
+ action: WalletAction.SIGN,
187
+ payload: { transaction: input.transaction },
188
+ }),
189
+ });
190
+ }
191
+ /**
192
+ * Sign an ANS-104 data item (for AO/bundled transactions).
193
+ * Returns the signed data item ID and raw bytes.
194
+ */
195
+ async function signDataItem(input) {
196
+ // Convert Uint8Array to array for JSON serialization
197
+ const data = input.data instanceof Uint8Array
198
+ ? Array.from(input.data)
199
+ : input.data;
200
+ return fetchApi("/api/wallet/action", {
201
+ method: "POST",
202
+ headers: { "Content-Type": "application/json" },
203
+ body: JSON.stringify({
204
+ action: WalletAction.SIGN_DATA_ITEM,
205
+ payload: {
206
+ dataItem: {
207
+ data,
208
+ tags: input.tags,
209
+ target: input.target,
210
+ anchor: input.anchor,
211
+ },
212
+ },
213
+ }),
214
+ });
215
+ }
216
+ /**
217
+ * Create a raw signature of arbitrary data.
218
+ * Returns the raw signature bytes.
219
+ */
220
+ async function signature(input) {
221
+ // Convert Uint8Array to array for JSON serialization
222
+ const data = input.data instanceof Uint8Array
223
+ ? Array.from(input.data)
224
+ : input.data;
225
+ return fetchApi("/api/wallet/action", {
226
+ method: "POST",
227
+ headers: { "Content-Type": "application/json" },
228
+ body: JSON.stringify({
229
+ action: WalletAction.SIGNATURE,
230
+ payload: { data },
231
+ }),
232
+ });
233
+ }
234
+ /**
235
+ * Sign and dispatch a data item to Arweave via a bundler (Turbo/Irys).
236
+ * Returns the transaction ID and bundler response.
237
+ */
238
+ async function dispatch(input) {
239
+ // Convert Uint8Array to array for JSON serialization
240
+ const data = input.data instanceof Uint8Array
241
+ ? Array.from(input.data)
242
+ : input.data;
243
+ return fetchApi("/api/wallet/action", {
244
+ method: "POST",
245
+ headers: { "Content-Type": "application/json" },
246
+ body: JSON.stringify({
247
+ action: WalletAction.DISPATCH,
248
+ payload: {
249
+ data,
250
+ tags: input.tags,
251
+ target: input.target,
252
+ anchor: input.anchor,
253
+ bundler: input.bundler,
254
+ },
255
+ }),
256
+ });
257
+ }
258
+ // ── Initialize ──────────────────────────────────────
259
+ /**
260
+ * Initialize the client: check for existing session token
261
+ * and validate it by fetching /api/me.
262
+ */
263
+ async function init() {
264
+ handleCallback();
265
+ if (!state.token) {
266
+ setState({ isLoading: false, isAuthenticated: false });
267
+ return getState();
268
+ }
269
+ setState({ isLoading: true });
270
+ try {
271
+ const user = await api.getMe();
272
+ setState({ user, isLoading: false, isAuthenticated: true });
273
+ }
274
+ catch {
275
+ // Token invalid — clear everything
276
+ removeStoredToken();
277
+ setState({
278
+ token: null,
279
+ user: null,
280
+ isLoading: false,
281
+ isAuthenticated: false,
282
+ });
283
+ }
284
+ return getState();
285
+ }
286
+ return {
287
+ init,
288
+ login,
289
+ loginWithGithub,
290
+ loginWithGoogle,
291
+ loginWithProvider,
292
+ logout,
293
+ handleCallback,
294
+ isAuthenticated,
295
+ getToken,
296
+ getState,
297
+ onAuthChange,
298
+ api,
299
+ // Wallet signing methods
300
+ sign,
301
+ signDataItem,
302
+ signature,
303
+ dispatch,
304
+ };
305
+ }
@@ -0,0 +1,3 @@
1
+ export { createWauthClient, createWauthClient as createArlinkAuthClient } from "./client.js";
2
+ export type { WauthClient, WauthClient as ArlinkAuthClient } from "./client.js";
3
+ export { WalletAction, type WauthUser, type WauthUser as ArlinkUser, type WauthClientOptions, type WauthClientOptions as ArlinkAuthClientOptions, type AuthState, type AuthChangeListener, type OAuthProvider, type ArweaveTag, type SignTransactionInput, type SignTransactionResult, type SignDataItemInput, type SignDataItemResult, type SignatureInput, type SignatureResult, type BundlerType, type DispatchInput, type DispatchResult, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createWauthClient, createWauthClient as createArlinkAuthClient } from "./client.js";
2
+ export { WalletAction, } from "./types.js";
@@ -0,0 +1,23 @@
1
+ import { type ReactNode } from "react";
2
+ import { type WauthClient } from "./client.js";
3
+ import type { AuthState, WauthClientOptions, WauthUser } from "./types.js";
4
+ type AuthContextValue = {
5
+ user: WauthUser | null;
6
+ isLoading: boolean;
7
+ isAuthenticated: boolean;
8
+ login: () => void;
9
+ loginWithGithub: () => void;
10
+ loginWithGoogle: () => void;
11
+ logout: () => void;
12
+ getToken: () => string | null;
13
+ client: WauthClient;
14
+ api: WauthClient["api"];
15
+ };
16
+ type AuthProviderProps = WauthClientOptions & {
17
+ children: ReactNode;
18
+ };
19
+ export declare function AuthProvider({ children, ...options }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function useAuth(): AuthContextValue;
21
+ export type { WauthUser, AuthState, WauthClientOptions };
22
+ export type { OAuthProvider, ArweaveTag, SignTransactionInput, SignTransactionResult, SignDataItemInput, SignDataItemResult, SignatureInput, SignatureResult, BundlerType, DispatchInput, DispatchResult, } from "./types.js";
23
+ export { WalletAction } from "./types.js";
package/dist/react.js ADDED
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useEffect, useState, useMemo, } from "react";
3
+ import { createWauthClient } from "./client.js";
4
+ const AuthContext = createContext(null);
5
+ export function AuthProvider({ children, ...options }) {
6
+ const client = useMemo(() => createWauthClient(options), [options.apiUrl, options.tokenKey]);
7
+ const [authState, setAuthState] = useState(client.getState);
8
+ useEffect(() => {
9
+ // Subscribe to state changes
10
+ const unsubscribe = client.onAuthChange(setAuthState);
11
+ // Initialize: handle callback token + validate session
12
+ client.init();
13
+ return unsubscribe;
14
+ }, [client]);
15
+ const value = useMemo(() => ({
16
+ user: authState.user,
17
+ isLoading: authState.isLoading,
18
+ isAuthenticated: authState.isAuthenticated,
19
+ login: client.login,
20
+ loginWithGithub: client.loginWithGithub,
21
+ loginWithGoogle: client.loginWithGoogle,
22
+ logout: client.logout,
23
+ getToken: client.getToken,
24
+ client,
25
+ api: client.api,
26
+ }), [authState, client]);
27
+ return _jsx(AuthContext.Provider, { value: value, children: children });
28
+ }
29
+ // ── Hook ──────────────────────────────────────────────
30
+ export function useAuth() {
31
+ const ctx = useContext(AuthContext);
32
+ if (!ctx) {
33
+ throw new Error("useAuth must be used within an <AuthProvider>");
34
+ }
35
+ return ctx;
36
+ }
37
+ export { WalletAction } from "./types.js";
@@ -0,0 +1,95 @@
1
+ export type WauthUser = {
2
+ id: string;
3
+ email: string | null;
4
+ name: string | null;
5
+ avatar_url: string | null;
6
+ github_id: number | null;
7
+ github_username: string | null;
8
+ google_id: string | null;
9
+ arweave_address: string | null;
10
+ created_at: string;
11
+ updated_at: string;
12
+ };
13
+ export type OAuthProvider = "github" | "google";
14
+ export declare enum WalletAction {
15
+ SIGN = "sign",
16
+ SIGN_DATA_ITEM = "signDataItem",
17
+ SIGNATURE = "signature",
18
+ DISPATCH = "dispatch"
19
+ }
20
+ /** Tag for Arweave transactions/data items */
21
+ export type ArweaveTag = {
22
+ name: string;
23
+ value: string;
24
+ };
25
+ /** Input for signing an Arweave transaction */
26
+ export type SignTransactionInput = {
27
+ /** Raw transaction object from arweave-js */
28
+ transaction: unknown;
29
+ };
30
+ /** Result of signing a transaction */
31
+ export type SignTransactionResult = {
32
+ /** Signed transaction JSON */
33
+ [key: string]: unknown;
34
+ };
35
+ /** Input for signing an ANS-104 data item */
36
+ export type SignDataItemInput = {
37
+ data: string | Uint8Array | number[];
38
+ tags?: ArweaveTag[];
39
+ target?: string;
40
+ anchor?: string;
41
+ };
42
+ /** Result of signing a data item */
43
+ export type SignDataItemResult = {
44
+ /** Data item ID (transaction ID) */
45
+ id: string;
46
+ /** Raw signed data item bytes */
47
+ raw: number[];
48
+ };
49
+ /** Input for raw signature */
50
+ export type SignatureInput = {
51
+ /** Data to sign - can be string, Uint8Array, or array of numbers */
52
+ data: string | Uint8Array | number[];
53
+ };
54
+ /** Result of raw signature */
55
+ export type SignatureResult = {
56
+ /** Raw signature bytes */
57
+ signature: number[];
58
+ };
59
+ /** Bundler types for dispatching data items */
60
+ export type BundlerType = "turbo" | "irys";
61
+ /** Input for dispatching a data item to Arweave via bundler */
62
+ export type DispatchInput = {
63
+ /** Data to upload - can be string or array of bytes */
64
+ data: string | Uint8Array | number[];
65
+ /** Optional tags for the data item */
66
+ tags?: ArweaveTag[];
67
+ /** Optional target address */
68
+ target?: string;
69
+ /** Optional anchor */
70
+ anchor?: string;
71
+ /** Which bundler to use (default: "turbo") */
72
+ bundler?: BundlerType;
73
+ };
74
+ /** Result of dispatching a data item */
75
+ export type DispatchResult = {
76
+ /** Transaction/data item ID */
77
+ id: string;
78
+ /** Which bundler was used */
79
+ bundler: BundlerType;
80
+ /** Response from the bundler */
81
+ response: Record<string, unknown>;
82
+ };
83
+ export type WauthClientOptions = {
84
+ /** Base URL of the wauth worker (e.g. "https://wauth.example.workers.dev") */
85
+ apiUrl: string;
86
+ /** localStorage key for the JWT. Default: "wauth_token" */
87
+ tokenKey?: string;
88
+ };
89
+ export type AuthState = {
90
+ user: WauthUser | null;
91
+ token: string | null;
92
+ isLoading: boolean;
93
+ isAuthenticated: boolean;
94
+ };
95
+ export type AuthChangeListener = (state: AuthState) => void;
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ // ── API Response Types ──────────────────────────────────
2
+ // ── Wallet Action Types ──────────────────────────────────
3
+ export var WalletAction;
4
+ (function (WalletAction) {
5
+ WalletAction["SIGN"] = "sign";
6
+ WalletAction["SIGN_DATA_ITEM"] = "signDataItem";
7
+ WalletAction["SIGNATURE"] = "signature";
8
+ WalletAction["DISPATCH"] = "dispatch";
9
+ })(WalletAction || (WalletAction = {}));
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "arlinkauth",
3
+ "version": "0.1.0",
4
+ "description": "Authentication SDK for Arweave apps - OAuth login with automatic wallet generation",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./react": {
14
+ "types": "./dist/react.d.ts",
15
+ "import": "./dist/react.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "dev": "tsc --watch --preserveWatchOutput",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "arweave",
29
+ "auth",
30
+ "oauth",
31
+ "wallet",
32
+ "ao",
33
+ "arlink"
34
+ ],
35
+ "author": "Ankush Singh",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/ankushKun/arlinkauth.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/ankushKun/arlinkauth/issues"
43
+ },
44
+ "homepage": "https://github.com/ankushKun/arlinkauth#readme",
45
+ "peerDependencies": {
46
+ "react": ">=18"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "react": {
50
+ "optional": true
51
+ }
52
+ },
53
+ "devDependencies": {
54
+ "@types/react": "^19",
55
+ "typescript": "^5"
56
+ }
57
+ }