@bounded-sh/core 0.0.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.
@@ -0,0 +1,157 @@
1
+ import { PublicKey, TransactionInstruction, Transaction, VersionedTransaction } from "@solana/web3.js";
2
+ import { SetOptions } from "./client/operations";
3
+ export interface AuthProvider {
4
+ login(): Promise<User | null>;
5
+ runTransaction(evmTransactionData?: EVMTransaction, solTransactionData?: SolTransaction, options?: SetOptions): Promise<TransactionResult>;
6
+ signMessage(message: string): Promise<string>;
7
+ signMessageMock?(message: string): Promise<string>;
8
+ signTransaction(transaction: Transaction | VersionedTransaction): Promise<Transaction | VersionedTransaction>;
9
+ /**
10
+ * Signs and submits a Solana transaction to the network.
11
+ *
12
+ * This method handles blockhash and transaction confirmation automatically - you do NOT need to
13
+ * set recentBlockhash or lastValidBlockHeight on the transaction before calling this method.
14
+ * The network/RPC URL is derived from the provider's configuration (set during initialization).
15
+ *
16
+ * @param transaction - The transaction to sign and submit (Transaction or VersionedTransaction)
17
+ * @param feePayer - Optional fee payer public key. If not provided and the transaction doesn't
18
+ * already have a feePayer set, the connected wallet address will be used.
19
+ * Useful for co-signing scenarios where a different account pays the fees.
20
+ * @returns The transaction signature
21
+ */
22
+ signAndSubmitTransaction(transaction: Transaction | VersionedTransaction, feePayer?: PublicKey): Promise<string>;
23
+ restoreSession(): Promise<User | null>;
24
+ logout(): Promise<void>;
25
+ getNativeMethods(): Promise<any>;
26
+ }
27
+ export interface User {
28
+ /**
29
+ * Universal stable identity of the user (resolves @user.id). For wallet/SIWS
30
+ * logins this EQUALS `address`; for email/social (Bounded Better Auth) logins
31
+ * it is the account identity (and `address` may be null). Derived from the
32
+ * idToken's `custom:userId` claim, falling back to `custom:walletAddress` for
33
+ * tokens minted before the claim-split. Used for ownership/membership/identity.
34
+ *
35
+ * Optional for backwards compatibility: existing apps that only read
36
+ * `user.address` keep working unchanged. New code should prefer `user.id` as
37
+ * the principal. Always present for a session derived from a real idToken;
38
+ * may be undefined only for legacy/manually-constructed User objects.
39
+ */
40
+ id?: string;
41
+ /**
42
+ * REAL onchain wallet address. Present for wallet/SIWS logins; `null` for
43
+ * email-only logins (which have no wallet until they link one). Kept for
44
+ * backwards compatibility — for wallet users `id === address`.
45
+ */
46
+ address: string;
47
+ /**
48
+ * Verified, lowercased email of the user — present only for email-login
49
+ * (Bounded Better Auth) sessions; `null`/absent for wallet/SIWS sessions.
50
+ * Resolves @user.email in offchain policies.
51
+ */
52
+ email?: string | null;
53
+ /**
54
+ * True when this is a zero-friction GUEST (anonymous) session created via
55
+ * `signInAnonymously()` — a durable device-keypair identity that owns data
56
+ * but has no email/real credential yet. Mirrors Firebase's `user.isAnonymous`
57
+ * / Supabase's `is_anonymous`. Flips to `false` once the guest is upgraded
58
+ * (links an email/social credential), with `id` preserved across the upgrade.
59
+ * Apps use this to decide whether to show an "upgrade / save your account"
60
+ * prompt. `false`/absent for real (email/social/wallet) logins.
61
+ */
62
+ isAnonymous?: boolean;
63
+ provider: AuthProvider;
64
+ }
65
+ export interface EVMTransaction {
66
+ contractAddress: string;
67
+ contractAbi: string | any;
68
+ functionName: string;
69
+ value: string;
70
+ txArgs: any[];
71
+ }
72
+ export interface SolTransaction {
73
+ appId: string;
74
+ txArgs: any[];
75
+ lutKey: PublicKey | null;
76
+ /** Complete set of LUT addresses to use (includes lutKey when present). When provided, takes precedence over lutKey. */
77
+ additionalLutAddresses?: string[];
78
+ network: string;
79
+ preInstructions: TransactionInstruction[];
80
+ /** Base64-encoded VersionedTransaction prebuilt server-side for sponsorship and/or required co-signers. */
81
+ signedTransaction?: string;
82
+ }
83
+ export interface TransactionResult {
84
+ transactionSignature?: any;
85
+ signedTransaction?: any;
86
+ blockNumber: number;
87
+ gasUsed: string;
88
+ data?: any;
89
+ }
90
+ export interface TransactionReceipt {
91
+ }
92
+ export interface SubscriptionOptions {
93
+ /**
94
+ * Structured MongoDB-style filter (same shape as GetOptions.filter), e.g.
95
+ * `{ status: "open", amount: { $gt: 10 } }`. Deterministic; the live feed only
96
+ * delivers documents matching the filter (and obeying the read rule).
97
+ */
98
+ filter?: Record<string, any>;
99
+ /**
100
+ * Sort spec applied server-side to the live feed (same shape/semantics as
101
+ * GetOptions.sort), e.g. `{ createdAt: -1 }` (1 = asc, -1 = desc). The server
102
+ * sorts the read-authorized result set before delivering it.
103
+ */
104
+ sort?: Record<string, number>;
105
+ prompt?: string;
106
+ /**
107
+ * Include documents from sub-paths (nested collections) in the live feed, same
108
+ * as GetOptions.includeSubPaths. The server applies it to both initial data and
109
+ * deltas.
110
+ */
111
+ includeSubPaths?: boolean;
112
+ /** Relationship shape for joined data (e.g., { owner: {}, posts: { comments: {} } }) */
113
+ shape?: Record<string, any>;
114
+ /** Maximum number of items to return (opt-in pagination) */
115
+ limit?: number;
116
+ /** Opaque cursor for cursor-based pagination (used with limit) */
117
+ cursor?: string;
118
+ /** Override the app ID for this subscription (instead of the configured default) */
119
+ appId?: string;
120
+ onData?: (data: any) => void;
121
+ onError?: (error: any) => void;
122
+ /**
123
+ * @internal Per-subscription auth override. The server `WalletClient` sets this
124
+ * (via `subscribe()`) so the WS connection authenticates as that wallet's
125
+ * identity instead of the ambient `BOUNDED_PRIVATE_KEY` env keypair. Mirrors
126
+ * `RequestOverrides`; only the fields the subscribe path consumes. App code
127
+ * never sets this — it's threaded internally.
128
+ */
129
+ _overrides?: {
130
+ _getAuthHeaders?: () => Promise<Record<string, string>>;
131
+ _walletAddress?: string;
132
+ _clearAuth?: () => Promise<void>;
133
+ };
134
+ }
135
+ export type TransactionOptions = {
136
+ shouldSubmit?: boolean;
137
+ };
138
+ export interface OffchainInstruction {
139
+ type: 'set' | 'delete';
140
+ path: string;
141
+ data?: Record<string, any>;
142
+ }
143
+ export interface OffchainTransaction {
144
+ id: string;
145
+ version: 1;
146
+ feePayer: string;
147
+ instructions: OffchainInstruction[];
148
+ message: string;
149
+ createdAt: number;
150
+ expiresAt: number;
151
+ appId: string;
152
+ nonce: string;
153
+ }
154
+ export interface SignedOffchainTransaction {
155
+ transaction: OffchainTransaction;
156
+ signature: string;
157
+ }
@@ -0,0 +1,11 @@
1
+ declare global {
2
+ interface Window {
3
+ CUSTOM_TAROBASE_APP_ID_HEADER?: string;
4
+ }
5
+ }
6
+ export declare function makeApiRequest(method: string, urlPath: string, data?: any, _overrides?: {
7
+ headers?: Record<string, string>;
8
+ _getAuthHeaders?: () => Promise<Record<string, string>>;
9
+ _clearAuth?: () => Promise<void>;
10
+ timeout?: number;
11
+ }): Promise<any>;
@@ -0,0 +1,10 @@
1
+ export declare function genNonce(): Promise<any>;
2
+ export declare function genAuthNonce(): Promise<any>;
3
+ export declare function createSessionWithSignature(address: string, message: string, signature: string): Promise<any>;
4
+ export declare function refreshSession(refreshToken: string, issuer?: string): Promise<any>;
5
+ /**
6
+ * Revoke a session's refresh-token family server-side (logout). Best-effort: if the
7
+ * call fails the local logout still proceeds. Routes to the minting issuer.
8
+ */
9
+ export declare function revokeSession(refreshToken: string, issuer?: string): Promise<void>;
10
+ export declare function signSessionCreateMessage(_signMessageFunction: (message: string) => Promise<string>): Promise<void>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Safe base64 helpers for tarobase-core.
3
+ *
4
+ * Uses the global atob/btoa when available (browser, RN with polyfill),
5
+ * falls back to the 'buffer' package (Node.js / SSR / RN without polyfill).
6
+ *
7
+ * These are used instead of bare `atob()`/`btoa()` so the core package
8
+ * works in React Native without requiring the consumer to polyfill globals.
9
+ */
10
+ export declare function safeAtob(input: string): string;
11
+ export declare function safeBtoa(input: string): string;
12
+ /**
13
+ * Decode a base64url-encoded string (used by JWT payloads).
14
+ * Normalises base64url → standard base64 before decoding.
15
+ */
16
+ export declare function decodeBase64Url(input: string): string;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Session manager for React Native environments.
3
+ *
4
+ * Drop-in replacement for WebSessionManager that uses a pluggable
5
+ * StorageAdapter instead of browser localStorage, and a pluggable
6
+ * base64-decode instead of the browser's atob().
7
+ *
8
+ * Consumers must call ReactNativeSessionManager.configure() once at
9
+ * startup to supply these adapters (e.g. MMKV, AsyncStorage wrapper,
10
+ * base64 polyfill).
11
+ */
12
+ /**
13
+ * Synchronous storage adapter for React Native session persistence.
14
+ *
15
+ * **Security: tokens (access, identity, refresh) are stored as plaintext JSON.**
16
+ * Use an encrypted storage backend in production — for example:
17
+ * - MMKV with an encryption key (`new MMKV({ encryptionKey: '...' })`)
18
+ * - expo-secure-store
19
+ * - react-native-keychain
20
+ *
21
+ * Plaintext token storage on a rooted/jailbroken device is a credential theft risk.
22
+ */
23
+ export interface RNStorageAdapter {
24
+ getItem(key: string): string | null;
25
+ setItem(key: string, value: string): void;
26
+ removeItem(key: string): void;
27
+ }
28
+ interface RNSessionConfig {
29
+ storage: RNStorageAdapter;
30
+ /** base64 → binary string (replaces browser atob). */
31
+ atob: (input: string) => string;
32
+ }
33
+ export declare class ReactNativeSessionManager {
34
+ private static TAROBASE_SESSION_STORAGE_KEY;
35
+ /**
36
+ * Must be called once before any other method.
37
+ *
38
+ * ```ts
39
+ * import { ReactNativeSessionManager } from '@bounded-sh/core';
40
+ * import { MMKV } from 'react-native-mmkv';
41
+ * import { decode } from 'base-64';
42
+ *
43
+ * const mmkv = new MMKV();
44
+ * ReactNativeSessionManager.configure({
45
+ * storage: {
46
+ * getItem: (k) => mmkv.getString(k) ?? null,
47
+ * setItem: (k, v) => mmkv.set(k, v),
48
+ * removeItem: (k) => mmkv.delete(k),
49
+ * },
50
+ * atob: decode,
51
+ * });
52
+ * ```
53
+ */
54
+ static configure(cfg: RNSessionConfig): void;
55
+ /**
56
+ * Whether configure() has been called. This is the canonical "are we on
57
+ * React Native?" signal used by getActiveSessionManager(): RN consumers MUST
58
+ * call configure() at startup, while web/Node never do. Far more reliable
59
+ * than sniffing `typeof window` (React Native defines a global `window`, so
60
+ * that test misclassifies RN as web and routes session writes to a
61
+ * non-existent localStorage).
62
+ */
63
+ static isConfigured(): boolean;
64
+ private static getStorage;
65
+ private static decodeBase64;
66
+ /**
67
+ * Decode a base64url-encoded string (used by JWT payloads).
68
+ * Normalises base64url → standard base64 before decoding.
69
+ */
70
+ private static decodeBase64Url;
71
+ static storeSession(address: string, accessToken: string, idToken: string, refreshToken: string, issuer?: string): Promise<void>;
72
+ static getSession(): Promise<{
73
+ address: string;
74
+ session: any;
75
+ } | null>;
76
+ static clearSession(): void;
77
+ static isAuthenticated(): boolean;
78
+ static getIdToken(): string | null;
79
+ /** The issuer base that minted this session (for routing silent refresh). */
80
+ static getIssuer(): string | null;
81
+ static getRefreshToken(): string | null;
82
+ static updateIdTokenAndAccessToken(idToken: string, accessToken: string, refreshToken?: string): Promise<void>;
83
+ }
84
+ export {};
@@ -0,0 +1,35 @@
1
+ import { Keypair } from "@solana/web3.js";
2
+ /**
3
+ * Server-side SessionManager
4
+ * --------------------------
5
+ * • Default singleton via `ServerSessionManager.instance` (reads keypair from env)
6
+ * • Per-keypair instances via `ServerSessionManager.forKeypair(kp)`
7
+ * • Keeps session data in-memory for the life of the instance
8
+ * • If `getSession()` finds no cached session it calls `createSession()`
9
+ * to obtain fresh tokens.
10
+ */
11
+ export interface ServerSession {
12
+ address: string;
13
+ accessToken: string;
14
+ idToken: string;
15
+ refreshToken: string;
16
+ }
17
+ export declare class ServerSessionManager {
18
+ static readonly instance: ServerSessionManager;
19
+ private session;
20
+ private pendingSession;
21
+ private readonly keypair;
22
+ private constructor();
23
+ /**
24
+ * Create a session manager for a specific keypair.
25
+ * Each instance has its own independent session cache.
26
+ */
27
+ static forKeypair(keypair: Keypair): ServerSessionManager;
28
+ private createSession;
29
+ getSession(): Promise<ServerSession>;
30
+ setSession(session: ServerSession): void;
31
+ clearSession(): void;
32
+ isAuthenticated(): boolean;
33
+ getIdToken(): string | null;
34
+ getRefreshToken(): string | null;
35
+ }
@@ -0,0 +1,31 @@
1
+ import { WebSessionManager } from './web-session-manager';
2
+ /**
3
+ * The shared static shape of both client session managers. WebSessionManager
4
+ * (localStorage) and ReactNativeSessionManager (pluggable StorageAdapter) expose
5
+ * an identical static API over the SAME storage key, so either one is a drop-in
6
+ * for the other.
7
+ */
8
+ export type ClientSessionManager = typeof WebSessionManager;
9
+ /**
10
+ * Return the active CLIENT session manager for the current runtime.
11
+ *
12
+ * This is the single source of truth for "which store holds the session token"
13
+ * and MUST be used everywhere the client reads/writes the session (login,
14
+ * restore, logout, and — critically — the per-request auth-header path in
15
+ * utils.ts). Previously this decision was made ad-hoc with
16
+ * `typeof window !== 'undefined' ? WebSessionManager : ReactNativeSessionManager`,
17
+ * which is wrong on React Native: RN defines a global `window`, so that test
18
+ * picked WebSessionManager and then hit a non-existent `localStorage` — the
19
+ * session was silently dropped (or threw), so every authenticated request 403'd.
20
+ *
21
+ * Decision rule: if a consumer configured the React Native session manager
22
+ * (ReactNativeSessionManager.configure(), which RN apps call once at startup and
23
+ * web/Node never do), we're on RN — use it. Otherwise use the localStorage-backed
24
+ * WebSessionManager (which itself no-ops gracefully in Node/SSR where there is no
25
+ * `window`). This changes ONLY React Native behavior; web and Node are unchanged.
26
+ *
27
+ * NOTE: this intentionally does NOT cover the SERVER (keypair) path — callers that
28
+ * branch on `isServer` keep using ServerSessionManager and only fall through to
29
+ * getActiveSessionManager() for the browser/RN client case.
30
+ */
31
+ export declare function getActiveSessionManager(): ClientSessionManager;