@tokenite/sdk 2.2.0 → 2.4.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/LICENSE +21 -21
- package/README.md +631 -528
- package/dist/client.d.ts +18 -1
- package/dist/client.js +30 -10
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +100 -0
- package/package.json +54 -53
- package/dist/client.test.d.ts +0 -2
- package/dist/client.test.js +0 -66
package/dist/client.d.ts
CHANGED
|
@@ -40,7 +40,15 @@ import type { TokeniteConfig, AuthorizeOptions, PopupOptions, PopupResult, Token
|
|
|
40
40
|
export declare const Tokenite: (config: TokeniteConfig) => {
|
|
41
41
|
/**
|
|
42
42
|
* Build the authorization URL for a full-page redirect.
|
|
43
|
-
*
|
|
43
|
+
*
|
|
44
|
+
* Pass `prompt: 'select_account'` on "Sign in with Tokenite" buttons
|
|
45
|
+
* that follow a sign-out — Tokenite shows a "Continue as <email>?"
|
|
46
|
+
* card with a "Use a different account" option instead of silently
|
|
47
|
+
* dropping the user back into the app. The user's existing spending
|
|
48
|
+
* limit is preserved (no budget re-entry):
|
|
49
|
+
* ```typescript
|
|
50
|
+
* res.redirect(tk.getAuthorizeUrl({ prompt: 'select_account' }));
|
|
51
|
+
* ```
|
|
44
52
|
*/
|
|
45
53
|
getAuthorizeUrl: (options?: AuthorizeOptions) => string;
|
|
46
54
|
/**
|
|
@@ -64,6 +72,15 @@ export declare const Tokenite: (config: TokeniteConfig) => {
|
|
|
64
72
|
* body: JSON.stringify({ code }),
|
|
65
73
|
* });
|
|
66
74
|
* ```
|
|
75
|
+
*
|
|
76
|
+
* On a "Sign in with Tokenite" button shown after the user signed
|
|
77
|
+
* out, pass `prompt: 'select_account'` — Tokenite shows a "Continue
|
|
78
|
+
* as <email>?" card with a "Use a different account" option instead
|
|
79
|
+
* of silently re-authorizing. The user's existing spending limit is
|
|
80
|
+
* preserved:
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const { code } = await tk.popup({ prompt: 'select_account' });
|
|
83
|
+
* ```
|
|
67
84
|
*/
|
|
68
85
|
popup: (options?: PopupOptions) => Promise<PopupResult>;
|
|
69
86
|
/**
|
package/dist/client.js
CHANGED
|
@@ -92,12 +92,22 @@ export const Tokenite = (config) => {
|
|
|
92
92
|
params.set('suggested_budget', String(options.suggestedBudget));
|
|
93
93
|
if (options?.mode)
|
|
94
94
|
params.set('mode', options.mode);
|
|
95
|
+
if (options?.prompt)
|
|
96
|
+
params.set('prompt', options.prompt);
|
|
95
97
|
return `${baseUrl}/oauth/authorize?${params}`;
|
|
96
98
|
};
|
|
97
99
|
return {
|
|
98
100
|
/**
|
|
99
101
|
* Build the authorization URL for a full-page redirect.
|
|
100
|
-
*
|
|
102
|
+
*
|
|
103
|
+
* Pass `prompt: 'select_account'` on "Sign in with Tokenite" buttons
|
|
104
|
+
* that follow a sign-out — Tokenite shows a "Continue as <email>?"
|
|
105
|
+
* card with a "Use a different account" option instead of silently
|
|
106
|
+
* dropping the user back into the app. The user's existing spending
|
|
107
|
+
* limit is preserved (no budget re-entry):
|
|
108
|
+
* ```typescript
|
|
109
|
+
* res.redirect(tk.getAuthorizeUrl({ prompt: 'select_account' }));
|
|
110
|
+
* ```
|
|
101
111
|
*/
|
|
102
112
|
getAuthorizeUrl: (options) => buildAuthorizeUrl(options),
|
|
103
113
|
/**
|
|
@@ -121,6 +131,15 @@ export const Tokenite = (config) => {
|
|
|
121
131
|
* body: JSON.stringify({ code }),
|
|
122
132
|
* });
|
|
123
133
|
* ```
|
|
134
|
+
*
|
|
135
|
+
* On a "Sign in with Tokenite" button shown after the user signed
|
|
136
|
+
* out, pass `prompt: 'select_account'` — Tokenite shows a "Continue
|
|
137
|
+
* as <email>?" card with a "Use a different account" option instead
|
|
138
|
+
* of silently re-authorizing. The user's existing spending limit is
|
|
139
|
+
* preserved:
|
|
140
|
+
* ```typescript
|
|
141
|
+
* const { code } = await tk.popup({ prompt: 'select_account' });
|
|
142
|
+
* ```
|
|
124
143
|
*/
|
|
125
144
|
popup: (options) => {
|
|
126
145
|
const mode = options?.mode ?? 'iframe';
|
|
@@ -133,6 +152,7 @@ export const Tokenite = (config) => {
|
|
|
133
152
|
const url = buildAuthorizeUrl({
|
|
134
153
|
suggestedBudget: options?.suggestedBudget,
|
|
135
154
|
mode: mode === 'window' ? 'popup' : 'iframe',
|
|
155
|
+
prompt: options?.prompt,
|
|
136
156
|
});
|
|
137
157
|
return mode === 'window'
|
|
138
158
|
? openWindowPopup(url, width, height, baseUrl, config.redirectUri)
|
|
@@ -299,17 +319,17 @@ const handleAuthMessage = (event, baseUrl, resolve, reject, cleanup) => {
|
|
|
299
319
|
};
|
|
300
320
|
const openIframeModal = (url, width, height, baseUrl) => {
|
|
301
321
|
const overlay = document.createElement('div');
|
|
302
|
-
overlay.style.cssText = `
|
|
303
|
-
position: fixed; inset: 0; z-index: 999999;
|
|
304
|
-
background: rgba(0,0,0,0.5); backdrop-filter: blur(2px);
|
|
305
|
-
display: flex; align-items: center; justify-content: center;
|
|
322
|
+
overlay.style.cssText = `
|
|
323
|
+
position: fixed; inset: 0; z-index: 999999;
|
|
324
|
+
background: rgba(0,0,0,0.5); backdrop-filter: blur(2px);
|
|
325
|
+
display: flex; align-items: center; justify-content: center;
|
|
306
326
|
`;
|
|
307
327
|
const container = document.createElement('div');
|
|
308
|
-
container.style.cssText = `
|
|
309
|
-
background: white; border-radius: 12px; overflow: hidden;
|
|
310
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
311
|
-
width: ${width}px; max-width: calc(100vw - 32px);
|
|
312
|
-
height: ${height}px; max-height: calc(100vh - 32px);
|
|
328
|
+
container.style.cssText = `
|
|
329
|
+
background: white; border-radius: 12px; overflow: hidden;
|
|
330
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
331
|
+
width: ${width}px; max-width: calc(100vw - 32px);
|
|
332
|
+
height: ${height}px; max-height: calc(100vh - 32px);
|
|
313
333
|
`;
|
|
314
334
|
const iframe = document.createElement('iframe');
|
|
315
335
|
iframe.src = url;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Tokenite } from './client.js';
|
|
2
|
-
export type { TokeniteConfig, AuthorizeOptions, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyUsage, ProxySuccess, ProxyError, ProxyResponse, ErrorSource, ProviderInfo, AppInfo, UserInfo, AccessContext, TopUpOptions, TopUpResult, CallWithRecoveryOptions, } from './types.js';
|
|
2
|
+
export type { TokeniteConfig, AuthorizeOptions, OAuthPrompt, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyUsage, ProxySuccess, ProxyError, ProxyResponse, ErrorSource, ProviderInfo, ModelInfo, TierInfo, AppInfo, UserInfo, AccessContext, TopUpOptions, TopUpResult, CallWithRecoveryOptions, } from './types.js';
|
|
3
3
|
export { isProxyError, isProxySuccess } from './types.js';
|
|
4
4
|
export { parseCallback } from './parse-callback.js';
|
|
5
5
|
export type { CallbackResult, CallbackSuccess, CallbackError, CallbackReason, ParseCallbackOptions, } from './parse-callback.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -10,11 +10,46 @@ export type TokeniteConfig = {
|
|
|
10
10
|
/** Tokenite proxy URL. Default: https://api.tokenite.ai */
|
|
11
11
|
readonly proxyUrl?: string;
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* OAuth 2.0 / OIDC `prompt` parameter. Tells Tokenite whether to
|
|
15
|
+
* re-prompt the user even when they have an existing session and/or
|
|
16
|
+
* an existing grant for this app.
|
|
17
|
+
*
|
|
18
|
+
* - `'select_account'` — **recommended for "Sign in with Tokenite"
|
|
19
|
+
* buttons that follow a sign-out.** Interrupts the silent re-auth
|
|
20
|
+
* that would otherwise drop the user straight back into your app:
|
|
21
|
+
* instead Tokenite shows a "Continue as <email>?" confirmation card
|
|
22
|
+
* with a "Use a different account" option. The user's existing
|
|
23
|
+
* spending limit is preserved — they aren't asked to re-set it.
|
|
24
|
+
* - `'consent'` — re-show the **full** consent screen (budget input
|
|
25
|
+
* and all), discarding any existing grant. Use this only when you
|
|
26
|
+
* want the user to actively re-set their budget; for the common
|
|
27
|
+
* "they signed out, now they're signing back in" case, prefer
|
|
28
|
+
* `'select_account'`.
|
|
29
|
+
* - `'login'` — request that the user re-authenticate. Today this
|
|
30
|
+
* behaves the same as `'consent'` on Tokenite (full session-cookie
|
|
31
|
+
* clear is a TODO); the consent screen still shows.
|
|
32
|
+
* - `'none'` — never show UI; fail if interaction is required.
|
|
33
|
+
* Currently treated as the default (silent reauth).
|
|
34
|
+
*
|
|
35
|
+
* The OAuth 2.0 spec allows a space-separated combination
|
|
36
|
+
* (e.g. `'login consent'`); for that, pass the raw string. The
|
|
37
|
+
* union above is just for autocomplete on the common values.
|
|
38
|
+
*/
|
|
39
|
+
export type OAuthPrompt = 'login' | 'consent' | 'select_account' | 'none' | (string & {});
|
|
13
40
|
export type AuthorizeOptions = {
|
|
14
41
|
/** Custom state parameter for CSRF protection. Auto-generated if not provided. */
|
|
15
42
|
readonly state?: string;
|
|
16
43
|
/** Suggested budget amount (user can override on consent screen) */
|
|
17
44
|
readonly suggestedBudget?: number;
|
|
45
|
+
/**
|
|
46
|
+
* OAuth `prompt` parameter. Most common use: pass `'select_account'`
|
|
47
|
+
* on "Sign in with Tokenite" buttons that follow a sign-out — it
|
|
48
|
+
* stops the silent reauth that would otherwise drop the user
|
|
49
|
+
* straight back into your app, and shows a "Continue as <email>?"
|
|
50
|
+
* confirmation card instead. See {@link OAuthPrompt}.
|
|
51
|
+
*/
|
|
52
|
+
readonly prompt?: OAuthPrompt;
|
|
18
53
|
};
|
|
19
54
|
export type PopupOptions = {
|
|
20
55
|
/** Suggested budget amount (user can override on consent screen) */
|
|
@@ -35,6 +70,14 @@ export type PopupOptions = {
|
|
|
35
70
|
readonly width?: number;
|
|
36
71
|
/** Modal/popup height in pixels. Default: 620 */
|
|
37
72
|
readonly height?: number;
|
|
73
|
+
/**
|
|
74
|
+
* OAuth `prompt` parameter. Most common use: pass `'select_account'`
|
|
75
|
+
* on "Sign in with Tokenite" buttons that follow a sign-out — it
|
|
76
|
+
* stops the silent reauth that would otherwise drop the user
|
|
77
|
+
* straight back into your app, and shows a "Continue as <email>?"
|
|
78
|
+
* confirmation card instead. See {@link OAuthPrompt}.
|
|
79
|
+
*/
|
|
80
|
+
readonly prompt?: OAuthPrompt;
|
|
38
81
|
};
|
|
39
82
|
export type PopupResult = {
|
|
40
83
|
/**
|
|
@@ -207,6 +250,50 @@ export type ProviderInfo = {
|
|
|
207
250
|
/** Whether the logo is a glyph/symbol or a full wordmark */
|
|
208
251
|
readonly logoStyle: 'symbol' | 'wordmark';
|
|
209
252
|
};
|
|
253
|
+
/**
|
|
254
|
+
* A model the access token may call, scoped to the app's model strategy
|
|
255
|
+
* and the holder's provider keys.
|
|
256
|
+
*
|
|
257
|
+
* - `servedBy` — every provider that hosts this model (catalog fact).
|
|
258
|
+
* - `callableNow` — the subset the holder can run *right now* (they hold
|
|
259
|
+
* a key for it). Empty means the model is visible but not yet usable —
|
|
260
|
+
* render it disabled, or prompt the user to add a key.
|
|
261
|
+
*
|
|
262
|
+
* Pass `slug` as the `model` field in `tk.call()`.
|
|
263
|
+
*/
|
|
264
|
+
export type ModelInfo = {
|
|
265
|
+
/** Stable Tokenite model slug — pass this as `model` in tk.call() */
|
|
266
|
+
readonly slug: string;
|
|
267
|
+
/** Human-readable name, e.g. "Claude Haiku 4.5" */
|
|
268
|
+
readonly displayName: string;
|
|
269
|
+
/** The lab that built the model */
|
|
270
|
+
readonly creator: 'anthropic' | 'openai' | 'google' | 'grok';
|
|
271
|
+
/** Capability tiers this model satisfies (cheap / fast / smart / reasoning) */
|
|
272
|
+
readonly tiers: readonly string[];
|
|
273
|
+
/** Feature capabilities, e.g. "vision", "tools", "thinking" */
|
|
274
|
+
readonly capabilities: readonly string[];
|
|
275
|
+
/** Every provider that serves this model */
|
|
276
|
+
readonly servedBy: readonly Provider[];
|
|
277
|
+
/** Providers the holder can run it through right now (subset of servedBy) */
|
|
278
|
+
readonly callableNow: readonly Provider[];
|
|
279
|
+
/** Indicative price per million tokens */
|
|
280
|
+
readonly pricing: {
|
|
281
|
+
readonly inputPerMillion: number;
|
|
282
|
+
readonly outputPerMillion: number;
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* A provider-agnostic capability bucket. Use this for a "pick a speed /
|
|
287
|
+
* quality" UI where the user never sees a model name.
|
|
288
|
+
*/
|
|
289
|
+
export type TierInfo = {
|
|
290
|
+
/** Tier id: "cheap" | "fast" | "smart" | "reasoning" */
|
|
291
|
+
readonly id: string;
|
|
292
|
+
/** Whether the holder can run at least one model in this tier */
|
|
293
|
+
readonly reachable: boolean;
|
|
294
|
+
/** A representative callable model slug for this tier, or null */
|
|
295
|
+
readonly recommendedModel: string | null;
|
|
296
|
+
};
|
|
210
297
|
/** Summary of the app the access token belongs to */
|
|
211
298
|
export type AppInfo = {
|
|
212
299
|
readonly id: string;
|
|
@@ -231,5 +318,18 @@ export type AccessContext = {
|
|
|
231
318
|
readonly app: AppInfo;
|
|
232
319
|
readonly user: UserInfo;
|
|
233
320
|
readonly providers: readonly ProviderInfo[];
|
|
321
|
+
/**
|
|
322
|
+
* Models the token may call — already filtered to the app's strategy
|
|
323
|
+
* and the user's keys. Render a picker from this; no need to maintain
|
|
324
|
+
* your own model list. Each entry's `callableNow` says whether it's
|
|
325
|
+
* usable now or needs a key.
|
|
326
|
+
*/
|
|
327
|
+
readonly models: readonly ModelInfo[];
|
|
328
|
+
/**
|
|
329
|
+
* Provider-agnostic capability buckets. For a "pick a tier" UI where
|
|
330
|
+
* the user never sees a model name — `recommendedModel` gives you a
|
|
331
|
+
* concrete slug to pass to `tk.call()`.
|
|
332
|
+
*/
|
|
333
|
+
readonly tiers: readonly TierInfo[];
|
|
234
334
|
};
|
|
235
335
|
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tokenite/sdk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "SDK for integrating \"Login with Tokenite\" into your app. Your users bring their own AI tokens — you pay nothing.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"exports": {
|
|
7
|
-
".": {
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"./admin": {
|
|
12
|
-
"types": "./dist/admin/index.d.ts",
|
|
13
|
-
"import": "./dist/admin/index.js"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"files": [
|
|
17
|
-
"dist",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tokenite/sdk",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "SDK for integrating \"Login with Tokenite\" into your app. Your users bring their own AI tokens — you pay nothing.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./admin": {
|
|
12
|
+
"types": "./dist/admin/index.d.ts",
|
|
13
|
+
"import": "./dist/admin/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc && npm run generate-readme",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"generate-readme": "npx tsx scripts/generate-readme.ts",
|
|
25
|
+
"prepack": "tsc && npm run generate-readme && node -e \"import('node:fs/promises').then(async fs => { const files = await fs.readdir('dist', { recursive: true }); for (const f of files) if (f.endsWith('.map') || f.endsWith('.tsbuildinfo')) await fs.rm('dist/' + f); })\""
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"tokenite",
|
|
29
|
+
"llm",
|
|
30
|
+
"ai",
|
|
31
|
+
"proxy",
|
|
32
|
+
"oauth",
|
|
33
|
+
"byok",
|
|
34
|
+
"anthropic",
|
|
35
|
+
"openai",
|
|
36
|
+
"google"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/eran-broder/tokenite-sdk.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/eran-broder/tokenite-sdk#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/eran-broder/tokenite-sdk/issues"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"tsx": "^4.0.0",
|
|
52
|
+
"typescript": "^5.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/dist/client.test.d.ts
DELETED
package/dist/client.test.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { TokenWallet } from './client.js';
|
|
3
|
-
const tw = TokenWallet({
|
|
4
|
-
clientId: 'test-app-id',
|
|
5
|
-
clientSecret: 'test-secret',
|
|
6
|
-
redirectUri: 'https://myapp.com/callback',
|
|
7
|
-
baseUrl: 'https://tokenwallet.ai',
|
|
8
|
-
proxyUrl: 'https://api.tokenwallet.ai',
|
|
9
|
-
});
|
|
10
|
-
describe('TokenWallet', () => {
|
|
11
|
-
describe('getAuthorizeUrl', () => {
|
|
12
|
-
it('builds correct OAuth URL', () => {
|
|
13
|
-
const url = tw.getAuthorizeUrl({ state: 'abc123' });
|
|
14
|
-
expect(url).toBe('https://tokenwallet.ai/oauth/authorize?client_id=test-app-id&redirect_uri=https%3A%2F%2Fmyapp.com%2Fcallback&response_type=code&state=abc123');
|
|
15
|
-
});
|
|
16
|
-
it('auto-generates state if not provided', () => {
|
|
17
|
-
const url = tw.getAuthorizeUrl();
|
|
18
|
-
expect(url).toContain('state=');
|
|
19
|
-
expect(url).toContain('client_id=test-app-id');
|
|
20
|
-
});
|
|
21
|
-
it('generates different state each time', () => {
|
|
22
|
-
const url1 = tw.getAuthorizeUrl();
|
|
23
|
-
const url2 = tw.getAuthorizeUrl();
|
|
24
|
-
const state1 = new URL(url1).searchParams.get('state');
|
|
25
|
-
const state2 = new URL(url2).searchParams.get('state');
|
|
26
|
-
expect(state1).not.toBe(state2);
|
|
27
|
-
});
|
|
28
|
-
it('includes suggested budget and period when provided', () => {
|
|
29
|
-
const url = tw.getAuthorizeUrl({ state: 's1', suggestedBudget: 10, suggestedPeriod: 'weekly' });
|
|
30
|
-
const parsed = new URL(url);
|
|
31
|
-
expect(parsed.searchParams.get('suggested_budget')).toBe('10');
|
|
32
|
-
expect(parsed.searchParams.get('suggested_period')).toBe('weekly');
|
|
33
|
-
});
|
|
34
|
-
it('omits budget params when not provided', () => {
|
|
35
|
-
const url = tw.getAuthorizeUrl({ state: 's2' });
|
|
36
|
-
const parsed = new URL(url);
|
|
37
|
-
expect(parsed.searchParams.has('suggested_budget')).toBe(false);
|
|
38
|
-
expect(parsed.searchParams.has('suggested_period')).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
describe('proxyUrl', () => {
|
|
42
|
-
it('returns correct URL for anthropic', () => {
|
|
43
|
-
expect(tw.proxyUrl('anthropic')).toBe('https://api.tokenwallet.ai/anthropic');
|
|
44
|
-
});
|
|
45
|
-
it('returns correct URL for openai', () => {
|
|
46
|
-
expect(tw.proxyUrl('openai')).toBe('https://api.tokenwallet.ai/openai');
|
|
47
|
-
});
|
|
48
|
-
it('returns correct URL for google', () => {
|
|
49
|
-
expect(tw.proxyUrl('google')).toBe('https://api.tokenwallet.ai/google');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
describe('exchangeCode', () => {
|
|
53
|
-
it('throws if clientSecret is not set', async () => {
|
|
54
|
-
const noSecret = TokenWallet({ clientId: 'x', redirectUri: 'http://x.com/cb' });
|
|
55
|
-
await expect(noSecret.exchangeCode('code123')).rejects.toThrow('clientSecret is required');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
describe('defaults', () => {
|
|
59
|
-
it('uses default base URL', () => {
|
|
60
|
-
const client = TokenWallet({ clientId: 'x', redirectUri: 'http://x.com/cb' });
|
|
61
|
-
expect(client.baseUrl).toBe('https://tokenwallet.ai');
|
|
62
|
-
expect(client.proxyBase).toBe('https://api.tokenwallet.ai');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
//# sourceMappingURL=client.test.js.map
|