cabalspy-id 0.5.1 → 0.5.2
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/CHANGELOG.md +146 -112
- package/LICENSE +21 -21
- package/README.md +505 -475
- package/dist/api.cjs +5 -3
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +16 -14
- package/dist/chunk-A2ZAFAWC.cjs.map +1 -1
- package/dist/chunk-ABFIPCBY.cjs +109 -0
- package/dist/chunk-ABFIPCBY.cjs.map +1 -0
- package/dist/{chunk-Z3UPDOBY.js → chunk-DBYCRBVS.js} +9 -7
- package/dist/chunk-DBYCRBVS.js.map +1 -0
- package/dist/{chunk-P3MDLWLN.cjs → chunk-PKFIMBPW.cjs} +113 -54
- package/dist/chunk-PKFIMBPW.cjs.map +1 -0
- package/dist/{chunk-L4RHSWOA.cjs → chunk-ULEBNY2A.cjs} +121 -212
- package/dist/chunk-ULEBNY2A.cjs.map +1 -0
- package/dist/{chunk-X3OFGGSE.cjs → chunk-VDFCMYLR.cjs} +50 -48
- package/dist/chunk-VDFCMYLR.cjs.map +1 -0
- package/dist/chunk-VOTYIQAY.js +109 -0
- package/dist/chunk-VOTYIQAY.js.map +1 -0
- package/dist/{chunk-3LKLY5WQ.js → chunk-XLBO2RSL.js} +15 -106
- package/dist/chunk-XLBO2RSL.js.map +1 -0
- package/dist/{chunk-3P7QK2PG.js → chunk-Z23B5TY6.js} +89 -30
- package/dist/chunk-Z23B5TY6.js.map +1 -0
- package/dist/index.cjs +13 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +25 -17
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +55 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +28 -5
- package/dist/react.d.ts +28 -5
- package/dist/react.js +55 -2
- package/dist/react.js.map +1 -1
- package/dist/thesis.cjs +11 -3
- package/dist/thesis.cjs.map +1 -1
- package/dist/thesis.d.cts +42 -11
- package/dist/thesis.d.ts +42 -11
- package/dist/thesis.js +24 -16
- package/dist/{users-BDLkwW1p.d.cts → users-3DAw88_U.d.cts} +4 -4
- package/dist/{users-CDlDRuyv.d.ts → users-B4r33i4O.d.ts} +4 -4
- package/dist/widget.cjs +28 -12
- package/dist/widget.cjs.map +1 -1
- package/dist/widget.d.cts +37 -6
- package/dist/widget.d.ts +37 -6
- package/dist/widget.js +17 -1
- package/dist/widget.js.map +1 -1
- package/package.json +147 -147
- package/dist/chunk-3LKLY5WQ.js.map +0 -1
- package/dist/chunk-3P7QK2PG.js.map +0 -1
- package/dist/chunk-L4RHSWOA.cjs.map +0 -1
- package/dist/chunk-P3MDLWLN.cjs.map +0 -1
- package/dist/chunk-X3OFGGSE.cjs.map +0 -1
- package/dist/chunk-Z3UPDOBY.js.map +0 -1
package/dist/react.d.cts
CHANGED
|
@@ -137,9 +137,22 @@ interface CSIDGlobal {
|
|
|
137
137
|
thesis?: {
|
|
138
138
|
/** Lets thesis widgets write as this user (access token with the scope "thesis"). */
|
|
139
139
|
setAccessToken(token: string | null): void;
|
|
140
|
+
/**
|
|
141
|
+
* Your own sign-in for the widget's "Sign in to write" (null removes it).
|
|
142
|
+
* See {@link setThesisSignInHandler}.
|
|
143
|
+
*/
|
|
144
|
+
setSignInHandler?(handler: ThesisSignInHandler | null): void;
|
|
140
145
|
clear(): void;
|
|
141
146
|
};
|
|
142
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Your page's sign-in for thesis widgets: obtain a thesis-capable access token
|
|
150
|
+
* for the person (e.g. your platform's own CabalSpy ID access token with the
|
|
151
|
+
* scope "thesis", from your backend), call `setThesisAccessToken(token)` (or
|
|
152
|
+
* `CSID.thesis.setAccessToken(token)`) and resolve `true`; resolve `false` when
|
|
153
|
+
* the person is not signed in.
|
|
154
|
+
*/
|
|
155
|
+
type ThesisSignInHandler = () => Promise<boolean> | boolean;
|
|
143
156
|
declare global {
|
|
144
157
|
interface Window {
|
|
145
158
|
CSID?: CSIDGlobal;
|
|
@@ -184,13 +197,15 @@ interface ThesisWidgetOptions {
|
|
|
184
197
|
/** Token mint / contract address. */
|
|
185
198
|
mint: string;
|
|
186
199
|
/**
|
|
187
|
-
* Your instance's client_id
|
|
200
|
+
* Required. Your instance's PUBLIC client_id (`csid_...`, shown in the
|
|
201
|
+
* dashboard next to the API key; not the API key). The widget sends it on
|
|
202
|
+
* every thesis API call; it also loads the design saved in the dashboard (the
|
|
188
203
|
* options below override it), credits posts to your app and enables `scope: 'instance'`.
|
|
189
204
|
*/
|
|
190
|
-
clientId
|
|
205
|
+
clientId: string;
|
|
191
206
|
/** Default: dark */
|
|
192
207
|
theme?: 'dark' | 'light';
|
|
193
|
-
/** `global` (default): theses from every platform. `instance`: only the ones written on your instance
|
|
208
|
+
/** `global` (default): theses from every platform. `instance`: only the ones written on your instance. */
|
|
194
209
|
scope?: 'global' | 'instance';
|
|
195
210
|
/** Initial order. Default: new */
|
|
196
211
|
sort?: 'new' | 'top';
|
|
@@ -244,6 +259,14 @@ interface CabalSpyThesesProps extends ThesisWidgetOptions {
|
|
|
244
259
|
* through the widget this is set automatically. `null` signs the widget out.
|
|
245
260
|
*/
|
|
246
261
|
accessToken?: string | null;
|
|
262
|
+
/**
|
|
263
|
+
* Your own sign-in for the widget's "Sign in to write" (instead of the page's
|
|
264
|
+
* login button / popup): get a thesis-capable access token for the person,
|
|
265
|
+
* pass it on (the `accessToken` prop or `setThesisAccessToken`) and resolve
|
|
266
|
+
* `true`; resolve `false` when they are not signed in. While set, the page is
|
|
267
|
+
* the source of truth for sign-in. Removed on unmount.
|
|
268
|
+
*/
|
|
269
|
+
onSignIn?: ThesisSignInHandler;
|
|
247
270
|
scriptSrc?: string;
|
|
248
271
|
className?: string;
|
|
249
272
|
style?: CSSProperties;
|
|
@@ -253,7 +276,7 @@ interface CabalSpyThesesProps extends ThesisWidgetOptions {
|
|
|
253
276
|
* Token theses widget: holders' theses on a token with their real position,
|
|
254
277
|
* replies, reactions, live updates.
|
|
255
278
|
*
|
|
256
|
-
* <CabalSpyTheses chain="solana" mint={mint} theme="dark" accessToken={accessToken} />
|
|
279
|
+
* <CabalSpyTheses chain="solana" mint={mint} clientId="csid_..." theme="dark" accessToken={accessToken} />
|
|
257
280
|
*/
|
|
258
281
|
declare function CabalSpyTheses(props: CabalSpyThesesProps): react.DetailedReactHTMLElement<{
|
|
259
282
|
id: string | undefined;
|
|
@@ -289,4 +312,4 @@ interface UseCabalSpyIDResult {
|
|
|
289
312
|
/** Login state from the CabalSpy ID widget. */
|
|
290
313
|
declare function useCabalSpyID(options?: UseCabalSpyIDOptions): UseCabalSpyIDResult;
|
|
291
314
|
|
|
292
|
-
export { type CSIDGlobal, type CabalSpyIDErrorEvent, type CabalSpyIDLogin, type CabalSpyIDLogoutEvent, type CabalSpyIDUserSummary, CabalSpyLogin, type CabalSpyLoginProps, CabalSpyTheses, type CabalSpyThesesProps, type LoginButtonOptions, type ThesisWidgetOptions, type UseCabalSpyIDOptions, type UseCabalSpyIDResult, WIDGET_SRC, loadCabalSpyIDWidget, useCabalSpyID };
|
|
315
|
+
export { type CSIDGlobal, type CabalSpyIDErrorEvent, type CabalSpyIDLogin, type CabalSpyIDLogoutEvent, type CabalSpyIDUserSummary, CabalSpyLogin, type CabalSpyLoginProps, CabalSpyTheses, type CabalSpyThesesProps, type LoginButtonOptions, type ThesisSignInHandler, type ThesisWidgetOptions, type UseCabalSpyIDOptions, type UseCabalSpyIDResult, WIDGET_SRC, loadCabalSpyIDWidget, useCabalSpyID };
|
package/dist/react.d.ts
CHANGED
|
@@ -137,9 +137,22 @@ interface CSIDGlobal {
|
|
|
137
137
|
thesis?: {
|
|
138
138
|
/** Lets thesis widgets write as this user (access token with the scope "thesis"). */
|
|
139
139
|
setAccessToken(token: string | null): void;
|
|
140
|
+
/**
|
|
141
|
+
* Your own sign-in for the widget's "Sign in to write" (null removes it).
|
|
142
|
+
* See {@link setThesisSignInHandler}.
|
|
143
|
+
*/
|
|
144
|
+
setSignInHandler?(handler: ThesisSignInHandler | null): void;
|
|
140
145
|
clear(): void;
|
|
141
146
|
};
|
|
142
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Your page's sign-in for thesis widgets: obtain a thesis-capable access token
|
|
150
|
+
* for the person (e.g. your platform's own CabalSpy ID access token with the
|
|
151
|
+
* scope "thesis", from your backend), call `setThesisAccessToken(token)` (or
|
|
152
|
+
* `CSID.thesis.setAccessToken(token)`) and resolve `true`; resolve `false` when
|
|
153
|
+
* the person is not signed in.
|
|
154
|
+
*/
|
|
155
|
+
type ThesisSignInHandler = () => Promise<boolean> | boolean;
|
|
143
156
|
declare global {
|
|
144
157
|
interface Window {
|
|
145
158
|
CSID?: CSIDGlobal;
|
|
@@ -184,13 +197,15 @@ interface ThesisWidgetOptions {
|
|
|
184
197
|
/** Token mint / contract address. */
|
|
185
198
|
mint: string;
|
|
186
199
|
/**
|
|
187
|
-
* Your instance's client_id
|
|
200
|
+
* Required. Your instance's PUBLIC client_id (`csid_...`, shown in the
|
|
201
|
+
* dashboard next to the API key; not the API key). The widget sends it on
|
|
202
|
+
* every thesis API call; it also loads the design saved in the dashboard (the
|
|
188
203
|
* options below override it), credits posts to your app and enables `scope: 'instance'`.
|
|
189
204
|
*/
|
|
190
|
-
clientId
|
|
205
|
+
clientId: string;
|
|
191
206
|
/** Default: dark */
|
|
192
207
|
theme?: 'dark' | 'light';
|
|
193
|
-
/** `global` (default): theses from every platform. `instance`: only the ones written on your instance
|
|
208
|
+
/** `global` (default): theses from every platform. `instance`: only the ones written on your instance. */
|
|
194
209
|
scope?: 'global' | 'instance';
|
|
195
210
|
/** Initial order. Default: new */
|
|
196
211
|
sort?: 'new' | 'top';
|
|
@@ -244,6 +259,14 @@ interface CabalSpyThesesProps extends ThesisWidgetOptions {
|
|
|
244
259
|
* through the widget this is set automatically. `null` signs the widget out.
|
|
245
260
|
*/
|
|
246
261
|
accessToken?: string | null;
|
|
262
|
+
/**
|
|
263
|
+
* Your own sign-in for the widget's "Sign in to write" (instead of the page's
|
|
264
|
+
* login button / popup): get a thesis-capable access token for the person,
|
|
265
|
+
* pass it on (the `accessToken` prop or `setThesisAccessToken`) and resolve
|
|
266
|
+
* `true`; resolve `false` when they are not signed in. While set, the page is
|
|
267
|
+
* the source of truth for sign-in. Removed on unmount.
|
|
268
|
+
*/
|
|
269
|
+
onSignIn?: ThesisSignInHandler;
|
|
247
270
|
scriptSrc?: string;
|
|
248
271
|
className?: string;
|
|
249
272
|
style?: CSSProperties;
|
|
@@ -253,7 +276,7 @@ interface CabalSpyThesesProps extends ThesisWidgetOptions {
|
|
|
253
276
|
* Token theses widget: holders' theses on a token with their real position,
|
|
254
277
|
* replies, reactions, live updates.
|
|
255
278
|
*
|
|
256
|
-
* <CabalSpyTheses chain="solana" mint={mint} theme="dark" accessToken={accessToken} />
|
|
279
|
+
* <CabalSpyTheses chain="solana" mint={mint} clientId="csid_..." theme="dark" accessToken={accessToken} />
|
|
257
280
|
*/
|
|
258
281
|
declare function CabalSpyTheses(props: CabalSpyThesesProps): react.DetailedReactHTMLElement<{
|
|
259
282
|
id: string | undefined;
|
|
@@ -289,4 +312,4 @@ interface UseCabalSpyIDResult {
|
|
|
289
312
|
/** Login state from the CabalSpy ID widget. */
|
|
290
313
|
declare function useCabalSpyID(options?: UseCabalSpyIDOptions): UseCabalSpyIDResult;
|
|
291
314
|
|
|
292
|
-
export { type CSIDGlobal, type CabalSpyIDErrorEvent, type CabalSpyIDLogin, type CabalSpyIDLogoutEvent, type CabalSpyIDUserSummary, CabalSpyLogin, type CabalSpyLoginProps, CabalSpyTheses, type CabalSpyThesesProps, type LoginButtonOptions, type ThesisWidgetOptions, type UseCabalSpyIDOptions, type UseCabalSpyIDResult, WIDGET_SRC, loadCabalSpyIDWidget, useCabalSpyID };
|
|
315
|
+
export { type CSIDGlobal, type CabalSpyIDErrorEvent, type CabalSpyIDLogin, type CabalSpyIDLogoutEvent, type CabalSpyIDUserSummary, CabalSpyLogin, type CabalSpyLoginProps, CabalSpyTheses, type CabalSpyThesesProps, type LoginButtonOptions, type ThesisSignInHandler, type ThesisWidgetOptions, type UseCabalSpyIDOptions, type UseCabalSpyIDResult, WIDGET_SRC, loadCabalSpyIDWidget, useCabalSpyID };
|
package/dist/react.js
CHANGED
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
// src/react.tsx
|
|
4
4
|
import { createElement, useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
5
5
|
|
|
6
|
+
// src/errors.ts
|
|
7
|
+
var EMPTY_RATE_LIMIT = Object.freeze({ limit: null, remaining: null, reset: null });
|
|
8
|
+
var CabalSpyIDError = class extends Error {
|
|
9
|
+
status;
|
|
10
|
+
code;
|
|
11
|
+
requestId;
|
|
12
|
+
docs;
|
|
13
|
+
parameter;
|
|
14
|
+
allowed;
|
|
15
|
+
rateLimit;
|
|
16
|
+
/** The parsed response body, when there was one. */
|
|
17
|
+
body;
|
|
18
|
+
constructor(message, opts = {}) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = new.target.name;
|
|
21
|
+
this.status = opts.status ?? 0;
|
|
22
|
+
this.code = opts.code ?? "unknown_error";
|
|
23
|
+
this.requestId = opts.requestId;
|
|
24
|
+
this.docs = opts.docs;
|
|
25
|
+
this.parameter = opts.parameter;
|
|
26
|
+
this.allowed = opts.allowed;
|
|
27
|
+
this.rateLimit = opts.rateLimit ?? { ...EMPTY_RATE_LIMIT };
|
|
28
|
+
this.body = opts.body;
|
|
29
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var AuthenticationError = class extends CabalSpyIDError {
|
|
33
|
+
};
|
|
34
|
+
|
|
6
35
|
// src/widget.ts
|
|
7
36
|
var WIDGET_SRC = "https://id.cabalspy.xyz/widget.js";
|
|
8
37
|
var isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
|
|
@@ -71,11 +100,17 @@ function loginButtonAttributes(o) {
|
|
|
71
100
|
};
|
|
72
101
|
}
|
|
73
102
|
function thesisWidgetAttributes(o) {
|
|
103
|
+
if (typeof o?.clientId !== "string" || !o.clientId.trim()) {
|
|
104
|
+
throw new AuthenticationError(
|
|
105
|
+
"The theses widget needs your instance's public client_id (csid_..., shown in the dashboard next to the API key): { clientId: 'csid_...' }. It is not the API key; never put the API key in a browser.",
|
|
106
|
+
{ code: "client_id_required", parameter: "clientId", docs: "https://docs.cabalspy.xyz/cabalspy-id/errors#client_id_required" }
|
|
107
|
+
);
|
|
108
|
+
}
|
|
74
109
|
return {
|
|
75
110
|
"data-csid-thesis": "",
|
|
76
111
|
"data-chain": o.chain,
|
|
77
112
|
"data-mint": o.mint,
|
|
78
|
-
"data-client-id": o.clientId,
|
|
113
|
+
"data-client-id": o.clientId.trim(),
|
|
79
114
|
"data-theme": o.theme,
|
|
80
115
|
"data-scope": o.scope,
|
|
81
116
|
"data-sort": o.sort,
|
|
@@ -119,6 +154,11 @@ function onWidgetEvents(handlers, clientId) {
|
|
|
119
154
|
window.removeEventListener("csid:error", error);
|
|
120
155
|
};
|
|
121
156
|
}
|
|
157
|
+
async function setThesisSignInHandler(handler, scriptSrc) {
|
|
158
|
+
if (!isBrowser()) return;
|
|
159
|
+
const csid = await loadCabalSpyIDWidget(scriptSrc);
|
|
160
|
+
csid.thesis?.setSignInHandler?.(handler);
|
|
161
|
+
}
|
|
122
162
|
|
|
123
163
|
// src/react.tsx
|
|
124
164
|
function CabalSpyLogin(props) {
|
|
@@ -154,9 +194,22 @@ function CabalSpyLogin(props) {
|
|
|
154
194
|
return createElement("div", { ...attrs, id: props.id, className: props.className, style: props.style });
|
|
155
195
|
}
|
|
156
196
|
function CabalSpyTheses(props) {
|
|
157
|
-
const { accessToken, scriptSrc } = props;
|
|
197
|
+
const { accessToken, scriptSrc, onSignIn } = props;
|
|
158
198
|
const attrs = thesisWidgetAttributes(props);
|
|
159
199
|
const key = JSON.stringify(attrs);
|
|
200
|
+
const signIn = useRef(onSignIn);
|
|
201
|
+
signIn.current = onSignIn;
|
|
202
|
+
const hasSignIn = onSignIn !== void 0;
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (!isBrowser() || !hasSignIn) return;
|
|
205
|
+
let active = true;
|
|
206
|
+
const handler = () => active && signIn.current ? signIn.current() : false;
|
|
207
|
+
setThesisSignInHandler(handler, scriptSrc).catch(() => void 0);
|
|
208
|
+
return () => {
|
|
209
|
+
active = false;
|
|
210
|
+
setThesisSignInHandler(null, scriptSrc).catch(() => void 0);
|
|
211
|
+
};
|
|
212
|
+
}, [hasSignIn, scriptSrc]);
|
|
160
213
|
useEffect(() => {
|
|
161
214
|
if (!isBrowser()) return;
|
|
162
215
|
let cancelled = false;
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx","../src/widget.ts"],"sourcesContent":["/**\r\n * React bindings for \"Login with CabalSpy ID\" and the token theses widget.\r\n *\r\n * import { CabalSpyLogin, CabalSpyTheses, useCabalSpyID } from 'cabalspy-id/react';\r\n *\r\n * <CabalSpyLogin clientId=\"csid_…\" onLogin={({ idToken }) => sendToBackend(idToken)} />\r\n * <CabalSpyTheses chain=\"solana\" mint={mint} />\r\n *\r\n * Loads https://id.cabalspy.xyz/widget.js once and maps props to its data-*\r\n * attributes (the same mapping as `cabalspy-id/widget`). Always verify `idToken`\r\n * on your backend (CabalSpyIDAuth.verifyIdToken) before you trust it.\r\n */\r\n\r\nimport { type CSSProperties, createElement, useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';\r\n\r\nimport type { CabalSpyIDClaims, OidcScope } from './types';\r\nimport {\r\n type CabalSpyIDErrorEvent,\r\n type CabalSpyIDLogin,\r\n type CabalSpyIDLogoutEvent,\r\n type CabalSpyIDUserSummary,\r\n type LoginButtonOptions,\r\n type ThesisWidgetOptions,\r\n isBrowser,\r\n loadCabalSpyIDWidget,\r\n loginButtonAttributes,\r\n onWidgetEvents,\r\n thesisWidgetAttributes,\r\n} from './widget';\r\n\r\nexport {\r\n WIDGET_SRC,\r\n loadCabalSpyIDWidget,\r\n type CabalSpyIDLogin,\r\n type CabalSpyIDLogoutEvent,\r\n type CabalSpyIDErrorEvent,\r\n type CabalSpyIDUserSummary,\r\n type CSIDGlobal,\r\n type LoginButtonOptions,\r\n type ThesisWidgetOptions,\r\n} from './widget';\r\n\r\n// ── <CabalSpyLogin /> ─────────────────────────────────────────────────────────\r\n\r\nexport interface CabalSpyLoginProps extends LoginButtonOptions {\r\n /** Fired after a browser-app login (window event `csid:login`). */\r\n onLogin?: (result: CabalSpyIDLogin) => void;\r\n /** Fired when the person signs out from the menu (window event `csid:logout`). */\r\n onLogout?: (event: CabalSpyIDLogoutEvent) => void;\r\n /** Fired when the browser flow fails (window event `csid:error`). */\r\n onError?: (event: CabalSpyIDErrorEvent) => void;\r\n /** Override the widget URL (e.g. for a self-hosted copy). */\r\n scriptSrc?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n id?: string;\r\n}\r\n\r\n/** \"Sign in with CabalSpy ID\" button (the official widget). */\r\nexport function CabalSpyLogin(props: CabalSpyLoginProps) {\r\n const { clientId, onLogin, onLogout, onError, scriptSrc } = props;\r\n const handlers = useRef({ onLogin, onLogout, onError });\r\n handlers.current = { onLogin, onLogout, onError };\r\n\r\n useEffect(\r\n () =>\r\n onWidgetEvents(\r\n {\r\n onLogin: (d) => handlers.current.onLogin?.(d),\r\n onLogout: (d) => handlers.current.onLogout?.(d),\r\n onError: (d) => handlers.current.onError?.(d),\r\n },\r\n clientId,\r\n ),\r\n [clientId],\r\n );\r\n\r\n const attrs = loginButtonAttributes(props);\r\n const key = JSON.stringify(attrs);\r\n\r\n useEffect(() => {\r\n if (!isBrowser()) return;\r\n let cancelled = false;\r\n loadCabalSpyIDWidget(scriptSrc).then(\r\n (csid) => {\r\n if (!cancelled) csid.mount();\r\n },\r\n (err: Error) => handlers.current.onError?.({ error: 'widget_load_failed', description: err.message }),\r\n );\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [key, scriptSrc]);\r\n\r\n return createElement('div', { ...attrs, id: props.id, className: props.className, style: props.style });\r\n}\r\n\r\n// ── <CabalSpyTheses /> ────────────────────────────────────────────────────────\r\n\r\nexport interface CabalSpyThesesProps extends ThesisWidgetOptions {\r\n /**\r\n * The signed-in user's CabalSpy ID access token (scope `thesis`), so they\r\n * write without signing in again inside the widget. After a browser login\r\n * through the widget this is set automatically. `null` signs the widget out.\r\n */\r\n accessToken?: string | null;\r\n scriptSrc?: string;\r\n className?: string;\r\n style?: CSSProperties;\r\n id?: string;\r\n}\r\n\r\n/**\r\n * Token theses widget: holders' theses on a token with their real position,\r\n * replies, reactions, live updates.\r\n *\r\n * <CabalSpyTheses chain=\"solana\" mint={mint} theme=\"dark\" accessToken={accessToken} />\r\n */\r\nexport function CabalSpyTheses(props: CabalSpyThesesProps) {\r\n const { accessToken, scriptSrc } = props;\r\n const attrs = thesisWidgetAttributes(props);\r\n const key = JSON.stringify(attrs);\r\n\r\n useEffect(() => {\r\n if (!isBrowser()) return;\r\n let cancelled = false;\r\n loadCabalSpyIDWidget(scriptSrc).then(\r\n (csid) => {\r\n if (!cancelled) csid.mount();\r\n },\r\n () => undefined,\r\n );\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [key, scriptSrc]);\r\n\r\n useEffect(() => {\r\n if (!isBrowser() || accessToken === undefined) return;\r\n let cancelled = false;\r\n loadCabalSpyIDWidget(scriptSrc).then(\r\n (csid) => {\r\n if (!cancelled) csid.thesis?.setAccessToken(accessToken);\r\n },\r\n () => undefined,\r\n );\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [accessToken, scriptSrc]);\r\n\r\n // The widget mounts each element once; a new key gives a new element (and\r\n // iframe) when the token or the design changes.\r\n return createElement('div', {\r\n key,\r\n ...attrs,\r\n id: props.id,\r\n className: props.className,\r\n style: props.style,\r\n });\r\n}\r\n\r\n// ── useCabalSpyID() ───────────────────────────────────────────────────────────\r\n\r\ninterface AuthState {\r\n login: CabalSpyIDLogin | null;\r\n}\r\n\r\nlet state: AuthState = { login: null };\r\nconst subscribers = new Set<() => void>();\r\nlet listening = false;\r\n\r\nfunction setState(next: AuthState) {\r\n state = next;\r\n for (const s of subscribers) s();\r\n}\r\n\r\nfunction listen() {\r\n if (listening || !isBrowser()) return;\r\n listening = true;\r\n if (window.CSID?.user) state = { login: window.CSID.user };\r\n window.addEventListener('csid:login', (e) => setState({ login: (e as CustomEvent<CabalSpyIDLogin>).detail }));\r\n window.addEventListener('csid:logout', () => setState({ login: null }));\r\n}\r\n\r\nfunction subscribe(cb: () => void) {\r\n listen();\r\n subscribers.add(cb);\r\n return () => {\r\n subscribers.delete(cb);\r\n };\r\n}\r\n\r\nconst getSnapshot = () => state;\r\nconst getServerSnapshot = () => state;\r\n\r\nfunction remembered(clientId: string | undefined): CabalSpyIDUserSummary | null {\r\n if (!clientId || !isBrowser()) return null;\r\n try {\r\n return JSON.parse(localStorage.getItem(`csid:user:${clientId}`) ?? 'null') as CabalSpyIDUserSummary | null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport interface UseCabalSpyIDOptions {\r\n /** Your client_id; needed for login(), signOut() and the remembered user. */\r\n clientId?: string;\r\n /** Load widget.js on mount (so a returning OAuth redirect completes). Default: true */\r\n load?: boolean;\r\n scriptSrc?: string;\r\n}\r\n\r\nexport interface UseCabalSpyIDResult {\r\n /** userinfo claims from this page's login, else the person remembered on this device. */\r\n user: CabalSpyIDClaims | CabalSpyIDUserSummary | null;\r\n /** Only right after a browser login on this page. Verify it on your backend. */\r\n idToken: string | null;\r\n accessToken: string | null;\r\n /** True when the platform is over its plan's user limit for this person. */\r\n limited: boolean;\r\n /** widget.js is loaded. */\r\n ready: boolean;\r\n /** Starts the browser (public client) login flow. */\r\n login: (opts?: { redirectUri?: string; scope?: string | OidcScope[] }) => Promise<void>;\r\n /** Forgets the person on this device and fires `csid:logout`. */\r\n signOut: () => void;\r\n}\r\n\r\n/** Login state from the CabalSpy ID widget. */\r\nexport function useCabalSpyID(options: UseCabalSpyIDOptions = {}): UseCabalSpyIDResult {\r\n const { clientId, load = true, scriptSrc } = options;\r\n const snap = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\r\n const [ready, setReady] = useState(() => isBrowser() && !!window.CSID?.__loaded);\r\n const [stored, setStored] = useState<CabalSpyIDUserSummary | null>(null);\r\n\r\n useEffect(() => {\r\n setStored(remembered(clientId));\r\n if (!load || !isBrowser()) return;\r\n let cancelled = false;\r\n loadCabalSpyIDWidget(scriptSrc).then(\r\n () => !cancelled && setReady(true),\r\n () => undefined,\r\n );\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [clientId, load, scriptSrc]);\r\n\r\n useEffect(() => {\r\n if (!snap.login) setStored(remembered(clientId));\r\n }, [snap.login, clientId]);\r\n\r\n const login = useCallback(\r\n async (opts: { redirectUri?: string; scope?: string | OidcScope[] } = {}) => {\r\n if (!clientId) throw new Error('useCabalSpyID: pass { clientId } to use login()');\r\n const csid = await loadCabalSpyIDWidget(scriptSrc);\r\n await csid.login({\r\n clientId,\r\n redirectUri: opts.redirectUri,\r\n scope: Array.isArray(opts.scope) ? opts.scope.join(' ') : opts.scope,\r\n });\r\n },\r\n [clientId, scriptSrc],\r\n );\r\n\r\n const signOut = useCallback(() => {\r\n if (clientId && isBrowser() && window.CSID) window.CSID.signOut(clientId);\r\n else setState({ login: null });\r\n setStored(null);\r\n }, [clientId]);\r\n\r\n const user = snap.login?.user ?? stored;\r\n return {\r\n user,\r\n idToken: snap.login?.idToken ?? null,\r\n accessToken: snap.login?.accessToken ?? null,\r\n limited: !!user?.limited,\r\n ready,\r\n login,\r\n signOut,\r\n };\r\n}\r\n","/**\n * CabalSpy ID widgets for any web page (browser only, no framework):\n * the \"Sign in with CabalSpy ID\" button and the token theses widget.\n *\n * import { mountLoginButton, mountTheses } from 'cabalspy-id/widget';\n *\n * mountLoginButton('#login', { clientId: 'csid_…', onLogin: ({ idToken }) => sendToBackend(idToken) });\n * mountTheses('#theses', { chain: 'solana', mint, clientId: 'csid_…', theme: 'dark' });\n *\n * Loads https://id.cabalspy.xyz/widget.js once and maps options to its data-*\n * attributes. React components: `cabalspy-id/react` (also `cabalspy-id/widget/react`).\n */\n\nimport type { CabalSpyIDClaims, Chain, OidcScope } from './types';\n\nexport const WIDGET_SRC = 'https://id.cabalspy.xyz/widget.js';\n\n/** Detail of the widget's `csid:login` event. */\nexport interface CabalSpyIDLogin {\n /** userinfo claims; `limited: true` (anonymous sub) when the platform is over its plan's user limit. */\n user: CabalSpyIDClaims;\n /** Send this to your backend and verify it there. */\n idToken: string;\n accessToken: string;\n /** Seconds. */\n expiresIn: number;\n}\n\nexport interface CabalSpyIDLogoutEvent {\n clientId: string;\n}\n\nexport interface CabalSpyIDErrorEvent {\n error: string;\n description?: string;\n}\n\n/** The signed-in person as the widget remembers them on this device. */\nexport interface CabalSpyIDUserSummary {\n sub: string;\n username?: string;\n name?: string | null;\n picture?: string | null;\n kol_badge?: boolean;\n limited?: boolean;\n}\n\n/** The global the widget defines (window.CSID). */\nexport interface CSIDGlobal {\n __loaded: true;\n user: CabalSpyIDLogin | null;\n mount(): void;\n login(opts: { clientId: string; redirectUri?: string; scope?: string }): Promise<void>;\n setUser(clientId: string, user: CabalSpyIDUserSummary | CabalSpyIDClaims): void;\n signOut(clientId: string): void;\n onLogin(cb: (r: CabalSpyIDLogin) => void): void;\n /** Thesis widgets on the page (data-csid-thesis). */\n thesis?: {\n /** Lets thesis widgets write as this user (access token with the scope \"thesis\"). */\n setAccessToken(token: string | null): void;\n clear(): void;\n };\n}\n\ndeclare global {\n interface Window {\n CSID?: CSIDGlobal;\n }\n}\n\nexport const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined';\n\nconst loaders = new Map<string, Promise<CSIDGlobal>>();\n\n/** Loads widget.js once per page. Resolves with window.CSID. */\nexport function loadCabalSpyIDWidget(src: string = WIDGET_SRC): Promise<CSIDGlobal> {\n if (!isBrowser()) return Promise.reject(new Error('CabalSpy ID widget needs a browser'));\n if (window.CSID?.__loaded) return Promise.resolve(window.CSID);\n let p = loaders.get(src);\n // The script tag was removed (e.g. by a test or a head manager): load again.\n if (p && !document.querySelector(`script[src=\"${src}\"]`)) {\n loaders.delete(src);\n p = undefined;\n }\n if (!p) {\n p = new Promise<CSIDGlobal>((resolve, reject) => {\n const done = () => (window.CSID ? resolve(window.CSID) : reject(new Error('widget.js loaded but window.CSID is missing')));\n let script = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`);\n if (script && window.CSID) return done();\n if (!script) {\n script = document.createElement('script');\n script.src = src;\n script.async = true;\n document.head.appendChild(script);\n }\n script.addEventListener('load', done, { once: true });\n script.addEventListener(\n 'error',\n () => {\n loaders.delete(src);\n reject(new Error(`Could not load ${src}`));\n },\n { once: true },\n );\n });\n loaders.set(src, p);\n }\n return p;\n}\n\n/** The `aud` of an (unverified) ID token, to route widget events to the right client. */\nexport function audOf(idToken: string): string | undefined {\n try {\n const part = idToken.split('.')[1]!.replace(/-/g, '+').replace(/_/g, '/');\n const aud = (JSON.parse(atob(part + '='.repeat((4 - (part.length % 4)) % 4))) as { aud?: string | string[] }).aud;\n return Array.isArray(aud) ? aud[0] : aud;\n } catch {\n return undefined;\n }\n}\n\nconst bool = (v: boolean | undefined) => (v === undefined ? undefined : v ? 'true' : 'false');\nconst num = (v: number | undefined) => (v === undefined ? undefined : String(v));\n\n// ── options ──────────────────────────────────────────────────────────────────\n\n/** Design and behaviour of the login button. */\nexport interface LoginButtonOptions {\n /** Your instance's client_id (`csid_…`). */\n clientId: string;\n /** Button design overrides; defaults come from the dashboard's button builder. */\n theme?: 'dark' | 'light' | (string & {});\n color?: string;\n textColor?: string;\n shape?: 'square' | 'rounded' | 'pill';\n size?: 'sm' | 'md' | 'lg';\n label?: string;\n fullWidth?: boolean;\n showLogo?: boolean;\n /**\n * Server-side apps: your own login route that starts the OIDC flow with your\n * client secret. Without it, a public client runs PKCE in the browser.\n */\n href?: string;\n /** Browser apps: redirect URI (default: the current page). */\n redirectUri?: string;\n /**\n * Browser apps: requested scopes (default: openid profile). Add `thesis` so\n * the person can write in thesis widgets on your page right after sign-in.\n */\n scope?: string | OidcScope[];\n /** Server-side apps: the signed-in person's `sub`, to show the avatar menu. */\n user?: string;\n /** Server-side apps: where \"Sign out\" goes. */\n logoutHref?: string;\n}\n\n/** Design and data of the token theses widget. */\nexport interface ThesisWidgetOptions {\n /** solana | bnb | base | eth | rh */\n chain: Chain;\n /** Token mint / contract address. */\n mint: string;\n /**\n * Your instance's client_id: loads the design saved in the dashboard (the\n * options below override it), credits posts to your app and enables `scope: 'instance'`.\n */\n clientId?: string;\n /** Default: dark */\n theme?: 'dark' | 'light';\n /** `global` (default): theses from every platform. `instance`: only the ones written on your instance (needs clientId). */\n scope?: 'global' | 'instance';\n /** Initial order. Default: new */\n sort?: 'new' | 'top';\n /** Font family, e.g. \"Inter\". */\n font?: string;\n /** Corner radius in px. Default: 12 */\n radius?: number;\n /** Colors (CSS colors, e.g. \"#0d0f12\"). */\n bg?: string;\n card?: string;\n text?: string;\n muted?: string;\n border?: string;\n accent?: string;\n accentText?: string;\n bull?: string;\n bear?: string;\n /** Show the token header. Default: true */\n showHeader?: boolean;\n /** Show the bull/bear sentiment bar. */\n showSentiment?: boolean;\n /** Denser layout. Default: false */\n compact?: boolean;\n /** Initial height in px; the widget then sizes itself to its content. Default: 520 */\n height?: number;\n}\n\n/** The widget's data-* attributes for a login button. */\nexport function loginButtonAttributes(o: LoginButtonOptions): Record<string, string | undefined> {\n return {\n 'data-csid-login': '',\n 'data-client-id': o.clientId,\n 'data-theme': o.theme,\n 'data-color': o.color,\n 'data-text-color': o.textColor,\n 'data-shape': o.shape,\n 'data-size': o.size,\n 'data-label': o.label,\n 'data-full-width': bool(o.fullWidth),\n 'data-show-logo': bool(o.showLogo),\n 'data-href': o.href,\n 'data-redirect-uri': o.redirectUri,\n 'data-scope': Array.isArray(o.scope) ? o.scope.join(' ') : o.scope,\n 'data-user': o.user,\n 'data-logout-href': o.logoutHref,\n };\n}\n\n/** The widget's data-* attributes for a theses widget. */\nexport function thesisWidgetAttributes(o: ThesisWidgetOptions): Record<string, string | undefined> {\n return {\n 'data-csid-thesis': '',\n 'data-chain': o.chain,\n 'data-mint': o.mint,\n 'data-client-id': o.clientId,\n 'data-theme': o.theme,\n 'data-scope': o.scope,\n 'data-sort': o.sort,\n 'data-font': o.font,\n 'data-radius': num(o.radius),\n 'data-bg': o.bg,\n 'data-card': o.card,\n 'data-text': o.text,\n 'data-muted': o.muted,\n 'data-border': o.border,\n 'data-accent': o.accent,\n 'data-accent-text': o.accentText,\n 'data-bull': o.bull,\n 'data-bear': o.bear,\n 'data-show-header': bool(o.showHeader),\n 'data-show-sentiment': bool(o.showSentiment),\n 'data-compact': bool(o.compact),\n 'data-height': num(o.height),\n };\n}\n\n// ── events ───────────────────────────────────────────────────────────────────\n\nexport interface WidgetEventHandlers {\n /** A browser-app login finished (window event `csid:login`). */\n onLogin?: (result: CabalSpyIDLogin) => void;\n /** The person signed out from the menu (window event `csid:logout`). */\n onLogout?: (event: CabalSpyIDLogoutEvent) => void;\n /** The browser flow failed (window event `csid:error`). */\n onError?: (event: CabalSpyIDErrorEvent) => void;\n}\n\n/**\n * Listens to the widget's window events. With `clientId`, logins and logouts of\n * other clients on the same page are ignored. Returns a function that stops listening.\n */\nexport function onWidgetEvents(handlers: WidgetEventHandlers, clientId?: string): () => void {\n if (!isBrowser()) return () => undefined;\n const login = (e: Event) => {\n const d = (e as CustomEvent<CabalSpyIDLogin>).detail;\n const aud = d?.idToken ? audOf(d.idToken) : undefined;\n if (clientId && aud !== undefined && aud !== clientId) return;\n handlers.onLogin?.(d);\n };\n const logout = (e: Event) => {\n const d = (e as CustomEvent<CabalSpyIDLogoutEvent>).detail;\n if (clientId && d?.clientId && d.clientId !== clientId) return;\n handlers.onLogout?.(d);\n };\n const error = (e: Event) => handlers.onError?.((e as CustomEvent<CabalSpyIDErrorEvent>).detail);\n window.addEventListener('csid:login', login);\n window.addEventListener('csid:logout', logout);\n window.addEventListener('csid:error', error);\n return () => {\n window.removeEventListener('csid:login', login);\n window.removeEventListener('csid:logout', logout);\n window.removeEventListener('csid:error', error);\n };\n}\n\n// ── mounting (vanilla) ───────────────────────────────────────────────────────\n\nexport interface MountedWidget<O> {\n /** The element carrying the data-* attributes. */\n readonly element: HTMLElement;\n /** Re-renders with new options (a fresh element, so the widget mounts it again). */\n update(options: Partial<O>): void;\n /** Removes the widget and its event listeners. */\n remove(): void;\n}\n\nfunction resolveTarget(target: Element | string): Element {\n if (!isBrowser()) throw new Error('CabalSpy ID widget needs a browser');\n const el = typeof target === 'string' ? document.querySelector(target) : target;\n if (!el) throw new Error(`CabalSpy ID widget: no element matches ${String(target)}`);\n return el;\n}\n\nfunction applyAttributes(el: HTMLElement, attrs: Record<string, string | undefined>): void {\n for (const [k, v] of Object.entries(attrs)) if (v !== undefined) el.setAttribute(k, v);\n}\n\nfunction mountWith<O>(\n target: Element | string,\n options: O,\n attributes: (o: O) => Record<string, string | undefined>,\n after: (csid: CSIDGlobal, o: O) => void,\n scriptSrc: string | undefined,\n onLoadError?: (err: Error) => void,\n): MountedWidget<O> {\n const host = resolveTarget(target);\n let current = options;\n let el = document.createElement('div');\n const render = () => {\n const next = document.createElement('div');\n applyAttributes(next, attributes(current));\n if (el.parentNode) el.replaceWith(next);\n else host.appendChild(next);\n el = next;\n loadCabalSpyIDWidget(scriptSrc).then(\n (csid) => {\n if (el.isConnected) {\n csid.mount();\n after(csid, current);\n }\n },\n (err: Error) => onLoadError?.(err),\n );\n };\n render();\n return {\n get element() {\n return el;\n },\n update(partial) {\n current = { ...current, ...partial };\n render();\n },\n remove() {\n el.remove();\n },\n };\n}\n\n/**\n * Mounts the \"Sign in with CabalSpy ID\" button into `target` (an element or a\n * CSS selector).\n */\nexport function mountLoginButton(\n target: Element | string,\n options: LoginButtonOptions & WidgetEventHandlers & { scriptSrc?: string },\n): MountedWidget<LoginButtonOptions> {\n const stop = onWidgetEvents(options, options.clientId);\n const mounted = mountWith<LoginButtonOptions>(\n target,\n options,\n loginButtonAttributes,\n () => undefined,\n options.scriptSrc,\n (err) => options.onError?.({ error: 'widget_load_failed', description: err.message }),\n );\n return {\n get element() {\n return mounted.element;\n },\n update: (o) => mounted.update(o),\n remove() {\n stop();\n mounted.remove();\n },\n };\n}\n\n/**\n * Mounts the token theses widget into `target` (an element or a CSS selector).\n * `accessToken` (scope `thesis`) lets the signed-in person write right away.\n */\nexport function mountTheses(\n target: Element | string,\n options: ThesisWidgetOptions & { accessToken?: string | null; scriptSrc?: string },\n): MountedWidget<ThesisWidgetOptions> {\n return mountWith<ThesisWidgetOptions>(\n target,\n options,\n thesisWidgetAttributes,\n (csid) => {\n if (options.accessToken !== undefined) csid.thesis?.setAccessToken(options.accessToken);\n },\n options.scriptSrc,\n );\n}\n\n/**\n * Lets thesis widgets on the page write as this user (an access token with the\n * scope `thesis`), or signs them out with `null`.\n */\nexport async function setThesisAccessToken(token: string | null, scriptSrc?: string): Promise<void> {\n const csid = await loadCabalSpyIDWidget(scriptSrc);\n csid.thesis?.setAccessToken(token);\n}\n\n/** Starts the browser (public client) login flow. */\nexport async function startLogin(\n options: { clientId: string; redirectUri?: string; scope?: string | OidcScope[] },\n scriptSrc?: string,\n): Promise<void> {\n const csid = await loadCabalSpyIDWidget(scriptSrc);\n await csid.login({\n clientId: options.clientId,\n redirectUri: options.redirectUri,\n scope: Array.isArray(options.scope) ? options.scope.join(' ') : options.scope,\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx","../src/errors.ts","../src/widget.ts"],"sourcesContent":["/**\n * React bindings for \"Login with CabalSpy ID\" and the token theses widget.\n *\n * import { CabalSpyLogin, CabalSpyTheses, useCabalSpyID } from 'cabalspy-id/react';\n *\n * <CabalSpyLogin clientId=\"csid_…\" onLogin={({ idToken }) => sendToBackend(idToken)} />\n * <CabalSpyTheses chain=\"solana\" mint={mint} />\n *\n * Loads https://id.cabalspy.xyz/widget.js once and maps props to its data-*\n * attributes (the same mapping as `cabalspy-id/widget`). Always verify `idToken`\n * on your backend (CabalSpyIDAuth.verifyIdToken) before you trust it.\n */\n\nimport { type CSSProperties, createElement, useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';\n\nimport type { CabalSpyIDClaims, OidcScope } from './types';\nimport {\n type CabalSpyIDErrorEvent,\n type CabalSpyIDLogin,\n type CabalSpyIDLogoutEvent,\n type CabalSpyIDUserSummary,\n type LoginButtonOptions,\n type ThesisSignInHandler,\n type ThesisWidgetOptions,\n isBrowser,\n loadCabalSpyIDWidget,\n loginButtonAttributes,\n onWidgetEvents,\n setThesisSignInHandler,\n thesisWidgetAttributes,\n} from './widget';\n\nexport {\n WIDGET_SRC,\n loadCabalSpyIDWidget,\n type CabalSpyIDLogin,\n type CabalSpyIDLogoutEvent,\n type CabalSpyIDErrorEvent,\n type CabalSpyIDUserSummary,\n type CSIDGlobal,\n type LoginButtonOptions,\n type ThesisSignInHandler,\n type ThesisWidgetOptions,\n} from './widget';\n\n// ── <CabalSpyLogin /> ─────────────────────────────────────────────────────────\n\nexport interface CabalSpyLoginProps extends LoginButtonOptions {\n /** Fired after a browser-app login (window event `csid:login`). */\n onLogin?: (result: CabalSpyIDLogin) => void;\n /** Fired when the person signs out from the menu (window event `csid:logout`). */\n onLogout?: (event: CabalSpyIDLogoutEvent) => void;\n /** Fired when the browser flow fails (window event `csid:error`). */\n onError?: (event: CabalSpyIDErrorEvent) => void;\n /** Override the widget URL (e.g. for a self-hosted copy). */\n scriptSrc?: string;\n className?: string;\n style?: CSSProperties;\n id?: string;\n}\n\n/** \"Sign in with CabalSpy ID\" button (the official widget). */\nexport function CabalSpyLogin(props: CabalSpyLoginProps) {\n const { clientId, onLogin, onLogout, onError, scriptSrc } = props;\n const handlers = useRef({ onLogin, onLogout, onError });\n handlers.current = { onLogin, onLogout, onError };\n\n useEffect(\n () =>\n onWidgetEvents(\n {\n onLogin: (d) => handlers.current.onLogin?.(d),\n onLogout: (d) => handlers.current.onLogout?.(d),\n onError: (d) => handlers.current.onError?.(d),\n },\n clientId,\n ),\n [clientId],\n );\n\n const attrs = loginButtonAttributes(props);\n const key = JSON.stringify(attrs);\n\n useEffect(() => {\n if (!isBrowser()) return;\n let cancelled = false;\n loadCabalSpyIDWidget(scriptSrc).then(\n (csid) => {\n if (!cancelled) csid.mount();\n },\n (err: Error) => handlers.current.onError?.({ error: 'widget_load_failed', description: err.message }),\n );\n return () => {\n cancelled = true;\n };\n }, [key, scriptSrc]);\n\n return createElement('div', { ...attrs, id: props.id, className: props.className, style: props.style });\n}\n\n// ── <CabalSpyTheses /> ────────────────────────────────────────────────────────\n\nexport interface CabalSpyThesesProps extends ThesisWidgetOptions {\n /**\n * The signed-in user's CabalSpy ID access token (scope `thesis`), so they\n * write without signing in again inside the widget. After a browser login\n * through the widget this is set automatically. `null` signs the widget out.\n */\n accessToken?: string | null;\n /**\n * Your own sign-in for the widget's \"Sign in to write\" (instead of the page's\n * login button / popup): get a thesis-capable access token for the person,\n * pass it on (the `accessToken` prop or `setThesisAccessToken`) and resolve\n * `true`; resolve `false` when they are not signed in. While set, the page is\n * the source of truth for sign-in. Removed on unmount.\n */\n onSignIn?: ThesisSignInHandler;\n scriptSrc?: string;\n className?: string;\n style?: CSSProperties;\n id?: string;\n}\n\n/**\n * Token theses widget: holders' theses on a token with their real position,\n * replies, reactions, live updates.\n *\n * <CabalSpyTheses chain=\"solana\" mint={mint} clientId=\"csid_...\" theme=\"dark\" accessToken={accessToken} />\n */\nexport function CabalSpyTheses(props: CabalSpyThesesProps) {\n const { accessToken, scriptSrc, onSignIn } = props;\n const attrs = thesisWidgetAttributes(props);\n const key = JSON.stringify(attrs);\n const signIn = useRef(onSignIn);\n signIn.current = onSignIn;\n const hasSignIn = onSignIn !== undefined;\n\n useEffect(() => {\n if (!isBrowser() || !hasSignIn) return;\n let active = true;\n // A stable wrapper, so a new onSignIn function does not re-register.\n const handler: ThesisSignInHandler = () => (active && signIn.current ? signIn.current() : false);\n setThesisSignInHandler(handler, scriptSrc).catch(() => undefined);\n return () => {\n active = false;\n setThesisSignInHandler(null, scriptSrc).catch(() => undefined);\n };\n }, [hasSignIn, scriptSrc]);\n\n useEffect(() => {\n if (!isBrowser()) return;\n let cancelled = false;\n loadCabalSpyIDWidget(scriptSrc).then(\n (csid) => {\n if (!cancelled) csid.mount();\n },\n () => undefined,\n );\n return () => {\n cancelled = true;\n };\n }, [key, scriptSrc]);\n\n useEffect(() => {\n if (!isBrowser() || accessToken === undefined) return;\n let cancelled = false;\n loadCabalSpyIDWidget(scriptSrc).then(\n (csid) => {\n if (!cancelled) csid.thesis?.setAccessToken(accessToken);\n },\n () => undefined,\n );\n return () => {\n cancelled = true;\n };\n }, [accessToken, scriptSrc]);\n\n // The widget mounts each element once; a new key gives a new element (and\n // iframe) when the token or the design changes.\n return createElement('div', {\n key,\n ...attrs,\n id: props.id,\n className: props.className,\n style: props.style,\n });\n}\n\n// ── useCabalSpyID() ───────────────────────────────────────────────────────────\n\ninterface AuthState {\n login: CabalSpyIDLogin | null;\n}\n\nlet state: AuthState = { login: null };\nconst subscribers = new Set<() => void>();\nlet listening = false;\n\nfunction setState(next: AuthState) {\n state = next;\n for (const s of subscribers) s();\n}\n\nfunction listen() {\n if (listening || !isBrowser()) return;\n listening = true;\n if (window.CSID?.user) state = { login: window.CSID.user };\n window.addEventListener('csid:login', (e) => setState({ login: (e as CustomEvent<CabalSpyIDLogin>).detail }));\n window.addEventListener('csid:logout', () => setState({ login: null }));\n}\n\nfunction subscribe(cb: () => void) {\n listen();\n subscribers.add(cb);\n return () => {\n subscribers.delete(cb);\n };\n}\n\nconst getSnapshot = () => state;\nconst getServerSnapshot = () => state;\n\nfunction remembered(clientId: string | undefined): CabalSpyIDUserSummary | null {\n if (!clientId || !isBrowser()) return null;\n try {\n return JSON.parse(localStorage.getItem(`csid:user:${clientId}`) ?? 'null') as CabalSpyIDUserSummary | null;\n } catch {\n return null;\n }\n}\n\nexport interface UseCabalSpyIDOptions {\n /** Your client_id; needed for login(), signOut() and the remembered user. */\n clientId?: string;\n /** Load widget.js on mount (so a returning OAuth redirect completes). Default: true */\n load?: boolean;\n scriptSrc?: string;\n}\n\nexport interface UseCabalSpyIDResult {\n /** userinfo claims from this page's login, else the person remembered on this device. */\n user: CabalSpyIDClaims | CabalSpyIDUserSummary | null;\n /** Only right after a browser login on this page. Verify it on your backend. */\n idToken: string | null;\n accessToken: string | null;\n /** True when the platform is over its plan's user limit for this person. */\n limited: boolean;\n /** widget.js is loaded. */\n ready: boolean;\n /** Starts the browser (public client) login flow. */\n login: (opts?: { redirectUri?: string; scope?: string | OidcScope[] }) => Promise<void>;\n /** Forgets the person on this device and fires `csid:logout`. */\n signOut: () => void;\n}\n\n/** Login state from the CabalSpy ID widget. */\nexport function useCabalSpyID(options: UseCabalSpyIDOptions = {}): UseCabalSpyIDResult {\n const { clientId, load = true, scriptSrc } = options;\n const snap = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const [ready, setReady] = useState(() => isBrowser() && !!window.CSID?.__loaded);\n const [stored, setStored] = useState<CabalSpyIDUserSummary | null>(null);\n\n useEffect(() => {\n setStored(remembered(clientId));\n if (!load || !isBrowser()) return;\n let cancelled = false;\n loadCabalSpyIDWidget(scriptSrc).then(\n () => !cancelled && setReady(true),\n () => undefined,\n );\n return () => {\n cancelled = true;\n };\n }, [clientId, load, scriptSrc]);\n\n useEffect(() => {\n if (!snap.login) setStored(remembered(clientId));\n }, [snap.login, clientId]);\n\n const login = useCallback(\n async (opts: { redirectUri?: string; scope?: string | OidcScope[] } = {}) => {\n if (!clientId) throw new Error('useCabalSpyID: pass { clientId } to use login()');\n const csid = await loadCabalSpyIDWidget(scriptSrc);\n await csid.login({\n clientId,\n redirectUri: opts.redirectUri,\n scope: Array.isArray(opts.scope) ? opts.scope.join(' ') : opts.scope,\n });\n },\n [clientId, scriptSrc],\n );\n\n const signOut = useCallback(() => {\n if (clientId && isBrowser() && window.CSID) window.CSID.signOut(clientId);\n else setState({ login: null });\n setStored(null);\n }, [clientId]);\n\n const user = snap.login?.user ?? stored;\n return {\n user,\n idToken: snap.login?.idToken ?? null,\n accessToken: snap.login?.accessToken ?? null,\n limited: !!user?.limited,\n ready,\n login,\n signOut,\n };\n}\n","// ═══════════════════════════════════════════════════════════════════════════\n// ERRORS\n// ═══════════════════════════════════════════════════════════════════════════\n\nexport interface RateLimitInfo {\n /** Requests per second allowed by the plan. `null` when the header is absent. */\n limit: number | null;\n remaining: number | null;\n /** Seconds until the current one-second window resets. */\n reset: number | null;\n}\n\nexport const EMPTY_RATE_LIMIT: RateLimitInfo = Object.freeze({ limit: null, remaining: null, reset: null });\n\n/** Error body of the Data API: `{\"success\": false, \"error\": {...}}`. */\nexport interface ApiErrorBody {\n code: string;\n message: string;\n request_id: string;\n /** Link to the error's documentation, e.g. https://docs.cabalspy.xyz/cabalspy-id/errors#wallet_not_found */\n docs: string;\n parameter?: string;\n allowed?: unknown[];\n /** Set on `plan_user_limit`. */\n limited?: boolean;\n [key: string]: unknown;\n}\n\nexport interface CabalSpyIDErrorOptions {\n status?: number;\n code?: string;\n requestId?: string;\n docs?: string;\n parameter?: string;\n allowed?: unknown[];\n rateLimit?: RateLimitInfo;\n body?: unknown;\n}\n\n/** Base class for every error thrown by this SDK. */\nexport class CabalSpyIDError extends Error {\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n readonly docs?: string;\n readonly parameter?: string;\n readonly allowed?: unknown[];\n readonly rateLimit: RateLimitInfo;\n /** The parsed response body, when there was one. */\n readonly body?: unknown;\n\n constructor(message: string, opts: CabalSpyIDErrorOptions = {}) {\n super(message);\n this.name = new.target.name;\n this.status = opts.status ?? 0;\n this.code = opts.code ?? 'unknown_error';\n this.requestId = opts.requestId;\n this.docs = opts.docs;\n this.parameter = opts.parameter;\n this.allowed = opts.allowed;\n this.rateLimit = opts.rateLimit ?? { ...EMPTY_RATE_LIMIT };\n this.body = opts.body;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** 400 — missing_parameter, invalid_parameter, instance_required */\nexport class BadRequestError extends CabalSpyIDError {}\n/** 401 — missing_api_key; thesis API: client_id_required, invalid_client */\nexport class AuthenticationError extends CabalSpyIDError {}\n/** 403 — invalid_api_key, product_not_enabled, no_instance, scope_not_allowed; thesis API: origin_not_allowed */\nexport class PermissionError extends CabalSpyIDError {}\n/**\n * 403 — plan_user_limit. The person signed in beyond the plan's user limit:\n * the platform receives no data for them until it upgrades.\n */\nexport class PlanUserLimitError extends PermissionError {\n readonly limited = true as const;\n}\n/** 404 — profile_not_found, wallet_not_found, instance_not_found */\nexport class NotFoundError extends CabalSpyIDError {}\n/** 429 — rate_limit_exceeded; thesis API: rate_limited (Retry-After 60) */\nexport class RateLimitError extends CabalSpyIDError {\n /** Seconds until the next request is allowed, taken from Retry-After. */\n readonly retryAfter: number | null;\n constructor(message: string, opts: CabalSpyIDErrorOptions & { retryAfter?: number | null } = {}) {\n super(message, opts);\n this.retryAfter = opts.retryAfter ?? null;\n }\n}\n/** 5xx — internal_error, service_unavailable */\nexport class ServerError extends CabalSpyIDError {}\n/** Network failure, timeout or aborted connection. */\nexport class ConnectionError extends CabalSpyIDError {}\n/** Response was not valid JSON, or did not have the expected envelope. */\nexport class InvalidResponseError extends CabalSpyIDError {}\n\n/**\n * An error from the OpenID Connect endpoints (`/oauth/token`, `/oauth/userinfo`,\n * `/oauth/revoke`) or from the authorization redirect, in OAuth's own format:\n * `code` is the OAuth `error` (invalid_grant, invalid_client, access_denied …).\n */\nexport class OAuthError extends CabalSpyIDError {\n readonly error: string;\n readonly errorDescription?: string;\n constructor(error: string, description?: string, opts: CabalSpyIDErrorOptions = {}) {\n super(description ? `${error}: ${description}` : error, { ...opts, code: error });\n this.error = error;\n this.errorDescription = description;\n }\n}\n\n/** An ID or access token failed verification (signature, iss, aud, exp, nonce …). */\nexport class TokenVerificationError extends CabalSpyIDError {}\n\n/** A webhook failed signature or timestamp verification. */\nexport class WebhookVerificationError extends CabalSpyIDError {}\n\n/** Maps an HTTP status and Data API error body onto the right error class. */\nexport function errorFromStatus(\n status: number,\n body: ApiErrorBody | null,\n rateLimit: RateLimitInfo,\n retryAfter: number | null,\n fallbackMessage: string,\n): CabalSpyIDError {\n const opts: CabalSpyIDErrorOptions = {\n status,\n code: body?.code ?? `http_${status}`,\n requestId: body?.request_id,\n docs: body?.docs,\n parameter: body?.parameter,\n allowed: body?.allowed,\n rateLimit,\n body: body ?? undefined,\n };\n const msg = body?.message ?? fallbackMessage;\n\n if (status === 400) return new BadRequestError(msg, opts);\n if (status === 401) return new AuthenticationError(msg, opts);\n if (status === 403) {\n return body?.code === 'plan_user_limit' ? new PlanUserLimitError(msg, opts) : new PermissionError(msg, opts);\n }\n if (status === 404) return new NotFoundError(msg, opts);\n if (status === 429) return new RateLimitError(msg, { ...opts, retryAfter });\n if (status >= 500) return new ServerError(msg, opts);\n return new CabalSpyIDError(msg, opts);\n}\n\nexport function isRetryable(err: unknown): boolean {\n return err instanceof RateLimitError || err instanceof ServerError || err instanceof ConnectionError;\n}\n","/**\n * CabalSpy ID widgets for any web page (browser only, no framework):\n * the \"Sign in with CabalSpy ID\" button and the token theses widget.\n *\n * import { mountLoginButton, mountTheses } from 'cabalspy-id/widget';\n *\n * mountLoginButton('#login', { clientId: 'csid_…', onLogin: ({ idToken }) => sendToBackend(idToken) });\n * mountTheses('#theses', { chain: 'solana', mint, clientId: 'csid_…', theme: 'dark' });\n *\n * Loads https://id.cabalspy.xyz/widget.js once and maps options to its data-*\n * attributes. React components: `cabalspy-id/react` (also `cabalspy-id/widget/react`).\n */\n\nimport { AuthenticationError } from './errors';\nimport type { CabalSpyIDClaims, Chain, OidcScope } from './types';\n\nexport const WIDGET_SRC = 'https://id.cabalspy.xyz/widget.js';\n\n/** Detail of the widget's `csid:login` event. */\nexport interface CabalSpyIDLogin {\n /** userinfo claims; `limited: true` (anonymous sub) when the platform is over its plan's user limit. */\n user: CabalSpyIDClaims;\n /** Send this to your backend and verify it there. */\n idToken: string;\n accessToken: string;\n /** Seconds. */\n expiresIn: number;\n}\n\nexport interface CabalSpyIDLogoutEvent {\n clientId: string;\n}\n\nexport interface CabalSpyIDErrorEvent {\n error: string;\n description?: string;\n}\n\n/** The signed-in person as the widget remembers them on this device. */\nexport interface CabalSpyIDUserSummary {\n sub: string;\n username?: string;\n name?: string | null;\n picture?: string | null;\n kol_badge?: boolean;\n limited?: boolean;\n}\n\n/** The global the widget defines (window.CSID). */\nexport interface CSIDGlobal {\n __loaded: true;\n user: CabalSpyIDLogin | null;\n mount(): void;\n login(opts: { clientId: string; redirectUri?: string; scope?: string }): Promise<void>;\n setUser(clientId: string, user: CabalSpyIDUserSummary | CabalSpyIDClaims): void;\n signOut(clientId: string): void;\n onLogin(cb: (r: CabalSpyIDLogin) => void): void;\n /** Thesis widgets on the page (data-csid-thesis). */\n thesis?: {\n /** Lets thesis widgets write as this user (access token with the scope \"thesis\"). */\n setAccessToken(token: string | null): void;\n /**\n * Your own sign-in for the widget's \"Sign in to write\" (null removes it).\n * See {@link setThesisSignInHandler}.\n */\n setSignInHandler?(handler: ThesisSignInHandler | null): void;\n clear(): void;\n };\n}\n\n/**\n * Your page's sign-in for thesis widgets: obtain a thesis-capable access token\n * for the person (e.g. your platform's own CabalSpy ID access token with the\n * scope \"thesis\", from your backend), call `setThesisAccessToken(token)` (or\n * `CSID.thesis.setAccessToken(token)`) and resolve `true`; resolve `false` when\n * the person is not signed in.\n */\nexport type ThesisSignInHandler = () => Promise<boolean> | boolean;\n\ndeclare global {\n interface Window {\n CSID?: CSIDGlobal;\n }\n}\n\nexport const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined';\n\nconst loaders = new Map<string, Promise<CSIDGlobal>>();\n\n/** Loads widget.js once per page. Resolves with window.CSID. */\nexport function loadCabalSpyIDWidget(src: string = WIDGET_SRC): Promise<CSIDGlobal> {\n if (!isBrowser()) return Promise.reject(new Error('CabalSpy ID widget needs a browser'));\n if (window.CSID?.__loaded) return Promise.resolve(window.CSID);\n let p = loaders.get(src);\n // The script tag was removed (e.g. by a test or a head manager): load again.\n if (p && !document.querySelector(`script[src=\"${src}\"]`)) {\n loaders.delete(src);\n p = undefined;\n }\n if (!p) {\n p = new Promise<CSIDGlobal>((resolve, reject) => {\n const done = () => (window.CSID ? resolve(window.CSID) : reject(new Error('widget.js loaded but window.CSID is missing')));\n let script = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`);\n if (script && window.CSID) return done();\n if (!script) {\n script = document.createElement('script');\n script.src = src;\n script.async = true;\n document.head.appendChild(script);\n }\n script.addEventListener('load', done, { once: true });\n script.addEventListener(\n 'error',\n () => {\n loaders.delete(src);\n reject(new Error(`Could not load ${src}`));\n },\n { once: true },\n );\n });\n loaders.set(src, p);\n }\n return p;\n}\n\n/** The `aud` of an (unverified) ID token, to route widget events to the right client. */\nexport function audOf(idToken: string): string | undefined {\n try {\n const part = idToken.split('.')[1]!.replace(/-/g, '+').replace(/_/g, '/');\n const aud = (JSON.parse(atob(part + '='.repeat((4 - (part.length % 4)) % 4))) as { aud?: string | string[] }).aud;\n return Array.isArray(aud) ? aud[0] : aud;\n } catch {\n return undefined;\n }\n}\n\nconst bool = (v: boolean | undefined) => (v === undefined ? undefined : v ? 'true' : 'false');\nconst num = (v: number | undefined) => (v === undefined ? undefined : String(v));\n\n// ── options ──────────────────────────────────────────────────────────────────\n\n/** Design and behaviour of the login button. */\nexport interface LoginButtonOptions {\n /** Your instance's client_id (`csid_…`). */\n clientId: string;\n /** Button design overrides; defaults come from the dashboard's button builder. */\n theme?: 'dark' | 'light' | (string & {});\n color?: string;\n textColor?: string;\n shape?: 'square' | 'rounded' | 'pill';\n size?: 'sm' | 'md' | 'lg';\n label?: string;\n fullWidth?: boolean;\n showLogo?: boolean;\n /**\n * Server-side apps: your own login route that starts the OIDC flow with your\n * client secret. Without it, a public client runs PKCE in the browser.\n */\n href?: string;\n /** Browser apps: redirect URI (default: the current page). */\n redirectUri?: string;\n /**\n * Browser apps: requested scopes (default: openid profile). Add `thesis` so\n * the person can write in thesis widgets on your page right after sign-in.\n */\n scope?: string | OidcScope[];\n /** Server-side apps: the signed-in person's `sub`, to show the avatar menu. */\n user?: string;\n /** Server-side apps: where \"Sign out\" goes. */\n logoutHref?: string;\n}\n\n/** Design and data of the token theses widget. */\nexport interface ThesisWidgetOptions {\n /** solana | bnb | base | eth | rh */\n chain: Chain;\n /** Token mint / contract address. */\n mint: string;\n /**\n * Required. Your instance's PUBLIC client_id (`csid_...`, shown in the\n * dashboard next to the API key; not the API key). The widget sends it on\n * every thesis API call; it also loads the design saved in the dashboard (the\n * options below override it), credits posts to your app and enables `scope: 'instance'`.\n */\n clientId: string;\n /** Default: dark */\n theme?: 'dark' | 'light';\n /** `global` (default): theses from every platform. `instance`: only the ones written on your instance. */\n scope?: 'global' | 'instance';\n /** Initial order. Default: new */\n sort?: 'new' | 'top';\n /** Font family, e.g. \"Inter\". */\n font?: string;\n /** Corner radius in px. Default: 12 */\n radius?: number;\n /** Colors (CSS colors, e.g. \"#0d0f12\"). */\n bg?: string;\n card?: string;\n text?: string;\n muted?: string;\n border?: string;\n accent?: string;\n accentText?: string;\n bull?: string;\n bear?: string;\n /** Show the token header. Default: true */\n showHeader?: boolean;\n /** Show the bull/bear sentiment bar. */\n showSentiment?: boolean;\n /** Denser layout. Default: false */\n compact?: boolean;\n /** Initial height in px; the widget then sizes itself to its content. Default: 520 */\n height?: number;\n}\n\n/** The widget's data-* attributes for a login button. */\nexport function loginButtonAttributes(o: LoginButtonOptions): Record<string, string | undefined> {\n return {\n 'data-csid-login': '',\n 'data-client-id': o.clientId,\n 'data-theme': o.theme,\n 'data-color': o.color,\n 'data-text-color': o.textColor,\n 'data-shape': o.shape,\n 'data-size': o.size,\n 'data-label': o.label,\n 'data-full-width': bool(o.fullWidth),\n 'data-show-logo': bool(o.showLogo),\n 'data-href': o.href,\n 'data-redirect-uri': o.redirectUri,\n 'data-scope': Array.isArray(o.scope) ? o.scope.join(' ') : o.scope,\n 'data-user': o.user,\n 'data-logout-href': o.logoutHref,\n };\n}\n\n/** The widget's data-* attributes for a theses widget. Throws `client_id_required` without `clientId`. */\nexport function thesisWidgetAttributes(o: ThesisWidgetOptions): Record<string, string | undefined> {\n if (typeof o?.clientId !== 'string' || !o.clientId.trim()) {\n throw new AuthenticationError(\n \"The theses widget needs your instance's public client_id (csid_..., shown in the dashboard next to the API key): \" +\n \"{ clientId: 'csid_...' }. It is not the API key; never put the API key in a browser.\",\n { code: 'client_id_required', parameter: 'clientId', docs: 'https://docs.cabalspy.xyz/cabalspy-id/errors#client_id_required' },\n );\n }\n return {\n 'data-csid-thesis': '',\n 'data-chain': o.chain,\n 'data-mint': o.mint,\n 'data-client-id': o.clientId.trim(),\n 'data-theme': o.theme,\n 'data-scope': o.scope,\n 'data-sort': o.sort,\n 'data-font': o.font,\n 'data-radius': num(o.radius),\n 'data-bg': o.bg,\n 'data-card': o.card,\n 'data-text': o.text,\n 'data-muted': o.muted,\n 'data-border': o.border,\n 'data-accent': o.accent,\n 'data-accent-text': o.accentText,\n 'data-bull': o.bull,\n 'data-bear': o.bear,\n 'data-show-header': bool(o.showHeader),\n 'data-show-sentiment': bool(o.showSentiment),\n 'data-compact': bool(o.compact),\n 'data-height': num(o.height),\n };\n}\n\n// ── events ───────────────────────────────────────────────────────────────────\n\nexport interface WidgetEventHandlers {\n /** A browser-app login finished (window event `csid:login`). */\n onLogin?: (result: CabalSpyIDLogin) => void;\n /** The person signed out from the menu (window event `csid:logout`). */\n onLogout?: (event: CabalSpyIDLogoutEvent) => void;\n /** The browser flow failed (window event `csid:error`). */\n onError?: (event: CabalSpyIDErrorEvent) => void;\n}\n\n/**\n * Listens to the widget's window events. With `clientId`, logins and logouts of\n * other clients on the same page are ignored. Returns a function that stops listening.\n */\nexport function onWidgetEvents(handlers: WidgetEventHandlers, clientId?: string): () => void {\n if (!isBrowser()) return () => undefined;\n const login = (e: Event) => {\n const d = (e as CustomEvent<CabalSpyIDLogin>).detail;\n const aud = d?.idToken ? audOf(d.idToken) : undefined;\n if (clientId && aud !== undefined && aud !== clientId) return;\n handlers.onLogin?.(d);\n };\n const logout = (e: Event) => {\n const d = (e as CustomEvent<CabalSpyIDLogoutEvent>).detail;\n if (clientId && d?.clientId && d.clientId !== clientId) return;\n handlers.onLogout?.(d);\n };\n const error = (e: Event) => handlers.onError?.((e as CustomEvent<CabalSpyIDErrorEvent>).detail);\n window.addEventListener('csid:login', login);\n window.addEventListener('csid:logout', logout);\n window.addEventListener('csid:error', error);\n return () => {\n window.removeEventListener('csid:login', login);\n window.removeEventListener('csid:logout', logout);\n window.removeEventListener('csid:error', error);\n };\n}\n\n// ── mounting (vanilla) ───────────────────────────────────────────────────────\n\nexport interface MountedWidget<O> {\n /** The element carrying the data-* attributes. */\n readonly element: HTMLElement;\n /** Re-renders with new options (a fresh element, so the widget mounts it again). */\n update(options: Partial<O>): void;\n /** Removes the widget and its event listeners. */\n remove(): void;\n}\n\nfunction resolveTarget(target: Element | string): Element {\n if (!isBrowser()) throw new Error('CabalSpy ID widget needs a browser');\n const el = typeof target === 'string' ? document.querySelector(target) : target;\n if (!el) throw new Error(`CabalSpy ID widget: no element matches ${String(target)}`);\n return el;\n}\n\nfunction applyAttributes(el: HTMLElement, attrs: Record<string, string | undefined>): void {\n for (const [k, v] of Object.entries(attrs)) if (v !== undefined) el.setAttribute(k, v);\n}\n\nfunction mountWith<O>(\n target: Element | string,\n options: O,\n attributes: (o: O) => Record<string, string | undefined>,\n after: (csid: CSIDGlobal, o: O) => void,\n scriptSrc: string | undefined,\n onLoadError?: (err: Error) => void,\n): MountedWidget<O> {\n const host = resolveTarget(target);\n let current = options;\n let el = document.createElement('div');\n const render = () => {\n const next = document.createElement('div');\n applyAttributes(next, attributes(current));\n if (el.parentNode) el.replaceWith(next);\n else host.appendChild(next);\n el = next;\n loadCabalSpyIDWidget(scriptSrc).then(\n (csid) => {\n if (el.isConnected) {\n csid.mount();\n after(csid, current);\n }\n },\n (err: Error) => onLoadError?.(err),\n );\n };\n render();\n return {\n get element() {\n return el;\n },\n update(partial) {\n current = { ...current, ...partial };\n render();\n },\n remove() {\n el.remove();\n },\n };\n}\n\n/**\n * Mounts the \"Sign in with CabalSpy ID\" button into `target` (an element or a\n * CSS selector).\n */\nexport function mountLoginButton(\n target: Element | string,\n options: LoginButtonOptions & WidgetEventHandlers & { scriptSrc?: string },\n): MountedWidget<LoginButtonOptions> {\n const stop = onWidgetEvents(options, options.clientId);\n const mounted = mountWith<LoginButtonOptions>(\n target,\n options,\n loginButtonAttributes,\n () => undefined,\n options.scriptSrc,\n (err) => options.onError?.({ error: 'widget_load_failed', description: err.message }),\n );\n return {\n get element() {\n return mounted.element;\n },\n update: (o) => mounted.update(o),\n remove() {\n stop();\n mounted.remove();\n },\n };\n}\n\n/**\n * Mounts the token theses widget into `target` (an element or a CSS selector).\n * `clientId` (your instance's public client_id) is required. `accessToken`\n * (scope `thesis`) lets the signed-in person write right away.\n */\nexport function mountTheses(\n target: Element | string,\n options: ThesisWidgetOptions & { accessToken?: string | null; scriptSrc?: string },\n): MountedWidget<ThesisWidgetOptions> {\n return mountWith<ThesisWidgetOptions>(\n target,\n options,\n thesisWidgetAttributes,\n (csid) => {\n if (options.accessToken !== undefined) csid.thesis?.setAccessToken(options.accessToken);\n },\n options.scriptSrc,\n );\n}\n\n/**\n * Lets thesis widgets on the page write as this user (an access token with the\n * scope `thesis`), or signs them out with `null`.\n */\nexport async function setThesisAccessToken(token: string | null, scriptSrc?: string): Promise<void> {\n const csid = await loadCabalSpyIDWidget(scriptSrc);\n csid.thesis?.setAccessToken(token);\n}\n\n/**\n * Lets your page handle the thesis widgets' \"Sign in to write\": the widget\n * calls `handler` instead of the page's login button / popup. While a handler\n * is set, the widget treats the page as the source of truth for sign-in (signed\n * out on the page = signed out in the widget). `null` removes it. Waits for\n * widget.js when it is not loaded yet; a no-op on the server.\n *\n * setThesisSignInHandler(async () => {\n * const token = await fetch('/api/csid-thesis-token').then((r) => (r.ok ? r.text() : null));\n * if (!token) return false;\n * await setThesisAccessToken(token);\n * return true;\n * });\n */\nexport async function setThesisSignInHandler(handler: ThesisSignInHandler | null, scriptSrc?: string): Promise<void> {\n if (!isBrowser()) return; // server rendering: nothing to register\n const csid = await loadCabalSpyIDWidget(scriptSrc);\n csid.thesis?.setSignInHandler?.(handler);\n}\n\n/** Starts the browser (public client) login flow. */\nexport async function startLogin(\n options: { clientId: string; redirectUri?: string; scope?: string | OidcScope[] },\n scriptSrc?: string,\n): Promise<void> {\n const csid = await loadCabalSpyIDWidget(scriptSrc);\n await csid.login({\n clientId: options.clientId,\n redirectUri: options.redirectUri,\n scope: Array.isArray(options.scope) ? options.scope.join(' ') : options.scope,\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/thesis.cjs
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
var _chunkPKFIMBPWcjs = require('./chunk-PKFIMBPW.cjs');
|
|
7
10
|
require('./chunk-A2ZAFAWC.cjs');
|
|
8
11
|
|
|
9
12
|
|
|
@@ -19,6 +22,11 @@ require('./chunk-A2ZAFAWC.cjs');
|
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
|
|
25
|
+
var _chunkULEBNY2Acjs = require('./chunk-ULEBNY2A.cjs');
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
22
30
|
|
|
23
31
|
|
|
24
32
|
|
|
@@ -29,10 +37,10 @@ require('./chunk-A2ZAFAWC.cjs');
|
|
|
29
37
|
|
|
30
38
|
|
|
31
39
|
|
|
40
|
+
var _chunkABFIPCBYcjs = require('./chunk-ABFIPCBY.cjs');
|
|
32
41
|
|
|
33
42
|
|
|
34
43
|
|
|
35
|
-
var _chunkL4RHSWOAcjs = require('./chunk-L4RHSWOA.cjs');
|
|
36
44
|
|
|
37
45
|
|
|
38
46
|
|
|
@@ -64,5 +72,5 @@ var _chunkL4RHSWOAcjs = require('./chunk-L4RHSWOA.cjs');
|
|
|
64
72
|
|
|
65
73
|
|
|
66
74
|
|
|
67
|
-
exports.AuthenticationError =
|
|
75
|
+
exports.AuthenticationError = _chunkABFIPCBYcjs.AuthenticationError; exports.BadRequestError = _chunkABFIPCBYcjs.BadRequestError; exports.CHAINS = _chunkULEBNY2Acjs.CHAINS; exports.CLIENT_ID_HEADER = _chunkPKFIMBPWcjs.CLIENT_ID_HEADER; exports.CabalSpyID = _chunkULEBNY2Acjs.CabalSpyID; exports.CabalSpyIDError = _chunkABFIPCBYcjs.CabalSpyIDError; exports.ConnectionError = _chunkABFIPCBYcjs.ConnectionError; exports.DEFAULT_THESES_STREAM_URL = _chunkULEBNY2Acjs.DEFAULT_THESES_STREAM_URL; exports.DEFAULT_THESIS_URL = _chunkPKFIMBPWcjs.DEFAULT_THESIS_URL; exports.DEFAULT_THESIS_WS_URL = _chunkPKFIMBPWcjs.DEFAULT_THESIS_WS_URL; exports.InvalidResponseError = _chunkABFIPCBYcjs.InvalidResponseError; exports.MAX_THESES_STREAM_TOKENS = _chunkULEBNY2Acjs.MAX_THESES_STREAM_TOKENS; exports.NotFoundError = _chunkABFIPCBYcjs.NotFoundError; exports.OAuthError = _chunkABFIPCBYcjs.OAuthError; exports.PermissionError = _chunkABFIPCBYcjs.PermissionError; exports.PlanUserLimitError = _chunkABFIPCBYcjs.PlanUserLimitError; exports.RateLimitError = _chunkABFIPCBYcjs.RateLimitError; exports.SDK_VERSION = _chunkULEBNY2Acjs.SDK_VERSION; exports.ServerError = _chunkABFIPCBYcjs.ServerError; exports.THESIS_EMOJIS = _chunkULEBNY2Acjs.THESIS_EMOJIS; exports.THESIS_MAX_BODY = _chunkULEBNY2Acjs.THESIS_MAX_BODY; exports.THESIS_REPORT_REASONS = _chunkULEBNY2Acjs.THESIS_REPORT_REASONS; exports.THESIS_WS_CLOSE_CODES = _chunkPKFIMBPWcjs.THESIS_WS_CLOSE_CODES; exports.ThesesStream = _chunkULEBNY2Acjs.ThesesStream; exports.ThesisClient = _chunkPKFIMBPWcjs.ThesisClient; exports.ThesisLive = _chunkPKFIMBPWcjs.ThesisLive; exports.TokenVerificationError = _chunkABFIPCBYcjs.TokenVerificationError; exports.WebhookVerificationError = _chunkABFIPCBYcjs.WebhookVerificationError; exports.handleKind = _chunkULEBNY2Acjs.handleKind; exports.hasX = _chunkULEBNY2Acjs.hasX; exports.signWebhook = _chunkULEBNY2Acjs.signWebhook; exports.thesisCloseError = _chunkPKFIMBPWcjs.thesisCloseError; exports.verifyWebhook = _chunkULEBNY2Acjs.verifyWebhook;
|
|
68
76
|
//# sourceMappingURL=thesis.cjs.map
|
package/dist/thesis.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/cabalspy-id-js/cabalspy-id-js/dist/thesis.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,s+DAAC","file":"/home/runner/work/cabalspy-id-js/cabalspy-id-js/dist/thesis.cjs"}
|
package/dist/thesis.d.cts
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
import { b as CabalSpyIDError } from './users-
|
|
2
|
-
export { A as ApiErrorBody, a as AuthenticationError, B as BadRequestError, C as CabalSpyID, c as CabalSpyIDErrorOptions, d as CabalSpyIDOptions, g as ConnectionError, i as DEFAULT_THESES_STREAM_URL, j as DeleteThesisParams, H as HandleKind, k as HeaderSource, I as InvalidResponseError, L as LatestThesesParams, p as ListThesesParams, M as MAX_THESES_STREAM_TOKENS, N as NotFoundError, O as OAuthError, P as PermissionError, q as PlanUserLimitError, r as PostThesisParams, R as RateLimitError, t as RateLimitInfo, u as ReactToThesisParams, w as ReplyToThesisParams, x as ReportThesisParams, S as SDK_VERSION, z as ServerError, U as ThesesReadyEvent, V as ThesesStream, W as ThesesStreamEventMap, X as ThesesStreamOptions, Y as ThesesSubscribedEvent, Z as ThesisRepliesParams, _ as ThesisScopeParam, $ as ThesisStreamEvent, a0 as ThesisTokenParams, a1 as ThesisTokenRef, a2 as ThesisWriter, a3 as TokenVerificationError, a5 as UserLike, a6 as UserThesesParams, a8 as VerifyWebhookOptions, aa as WebhookVerificationError, ab as handleKind, ac as hasX, ad as signWebhook, ae as verifyWebhook } from './users-
|
|
1
|
+
import { b as CabalSpyIDError } from './users-3DAw88_U.cjs';
|
|
2
|
+
export { A as ApiErrorBody, a as AuthenticationError, B as BadRequestError, C as CabalSpyID, c as CabalSpyIDErrorOptions, d as CabalSpyIDOptions, g as ConnectionError, i as DEFAULT_THESES_STREAM_URL, j as DeleteThesisParams, H as HandleKind, k as HeaderSource, I as InvalidResponseError, L as LatestThesesParams, p as ListThesesParams, M as MAX_THESES_STREAM_TOKENS, N as NotFoundError, O as OAuthError, P as PermissionError, q as PlanUserLimitError, r as PostThesisParams, R as RateLimitError, t as RateLimitInfo, u as ReactToThesisParams, w as ReplyToThesisParams, x as ReportThesisParams, S as SDK_VERSION, z as ServerError, U as ThesesReadyEvent, V as ThesesStream, W as ThesesStreamEventMap, X as ThesesStreamOptions, Y as ThesesSubscribedEvent, Z as ThesisRepliesParams, _ as ThesisScopeParam, $ as ThesisStreamEvent, a0 as ThesisTokenParams, a1 as ThesisTokenRef, a2 as ThesisWriter, a3 as TokenVerificationError, a5 as UserLike, a6 as UserThesesParams, a8 as VerifyWebhookOptions, aa as WebhookVerificationError, ab as handleKind, ac as hasX, ad as signWebhook, ae as verifyWebhook } from './users-3DAw88_U.cjs';
|
|
3
3
|
import { d as Chain, a3 as ThesisSort, a4 as ThesisStance, y as Thesis, o as Pagination, a6 as ThesisToken, B as ThesisCounts, a5 as ThesisSummary, F as ThesisEligibility, J as ThesisNotices, G as ThesisEmoji, _ as ThesisReactions, a2 as ThesisReportReason, Q as ThesisNotifications } from './types-UZeY9n1e.cjs';
|
|
4
4
|
export { C as CHAINS, S as Scope, T as THESIS_EMOJIS, w as THESIS_MAX_BODY, x as THESIS_REPORT_REASONS, z as ThesisAuthor, E as ThesisCreatedData, I as ThesisLiveItem, K as ThesisNotification, N as ThesisNotificationKind, U as ThesisPnl, V as ThesisPosition, W as ThesisPositionPrivate, X as ThesisPositionPublic, Y as ThesisReactionData, Z as ThesisReactionUpdate, $ as ThesisReactor, a0 as ThesisRemoved, a1 as ThesisReplyData, ah as WebhookEvent } from './types-UZeY9n1e.cjs';
|
|
5
5
|
|
|
6
6
|
declare const DEFAULT_THESIS_URL = "https://id.cabalspy.xyz/thesis/v1";
|
|
7
7
|
declare const DEFAULT_THESIS_WS_URL = "wss://id.cabalspy.xyz/thesis/v1/ws";
|
|
8
|
+
/** Header that carries the instance's public client_id on every thesis API call. */
|
|
9
|
+
declare const CLIENT_ID_HEADER = "X-CSID-Client-Id";
|
|
8
10
|
interface ThesisScopeOptions {
|
|
9
11
|
/**
|
|
10
12
|
* `global` (default): theses written on every platform. `instance`: only the
|
|
11
13
|
* ones written on the instance named by `clientId`.
|
|
12
14
|
*/
|
|
13
15
|
scope?: 'global' | 'instance';
|
|
14
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Instance whose theses `scope: 'instance'` reads. Defaults to the client's
|
|
18
|
+
* own `clientId`; you rarely need to set it per call.
|
|
19
|
+
*/
|
|
15
20
|
clientId?: string;
|
|
16
21
|
}
|
|
17
|
-
interface ThesisClientOptions
|
|
22
|
+
interface ThesisClientOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Required. Your instance's PUBLIC client_id (`csid_...`), shown in the
|
|
25
|
+
* dashboard next to the API key. It is not a secret and not the API key. Sent
|
|
26
|
+
* as `X-CSID-Client-Id` on every call and as `client_id` on the live socket;
|
|
27
|
+
* also used for `scope: 'instance'` and credits posts to your app.
|
|
28
|
+
*/
|
|
29
|
+
clientId: string;
|
|
30
|
+
/**
|
|
31
|
+
* `global` (default): theses written on every platform. `instance`: only the
|
|
32
|
+
* ones written on your instance.
|
|
33
|
+
*/
|
|
34
|
+
scope?: 'global' | 'instance';
|
|
18
35
|
/** The signed-in user's access token (scope `thesis`). Needed only for writing. */
|
|
19
36
|
accessToken?: string | null;
|
|
20
37
|
/** Default: https://id.cabalspy.xyz/thesis/v1 */
|
|
@@ -57,7 +74,7 @@ type ThesisMe = ThesisEligibility & {
|
|
|
57
74
|
* Client for the public thesis API, e.g. in a browser app that builds its own
|
|
58
75
|
* thesis UI.
|
|
59
76
|
*
|
|
60
|
-
* const theses = new ThesisClient({ accessToken });
|
|
77
|
+
* const theses = new ThesisClient({ clientId: 'csid_...', accessToken }); // token from csid:login
|
|
61
78
|
* const page = await theses.list('solana', mint, { sort: 'top' });
|
|
62
79
|
* await theses.post('solana', mint, { body: 'Strong community, early.', stance: 'bull' });
|
|
63
80
|
* const live = theses.live('solana', mint);
|
|
@@ -69,10 +86,11 @@ declare class ThesisClient {
|
|
|
69
86
|
readonly wsUrl: string;
|
|
70
87
|
readonly timeout: number;
|
|
71
88
|
readonly scope?: 'global' | 'instance';
|
|
72
|
-
|
|
89
|
+
/** Your instance's public client_id, sent on every call. */
|
|
90
|
+
readonly clientId: string;
|
|
73
91
|
private accessToken;
|
|
74
92
|
private readonly fetchImpl;
|
|
75
|
-
constructor(options
|
|
93
|
+
constructor(options: ThesisClientOptions);
|
|
76
94
|
/** Sets (or with null clears) the user's access token, e.g. after sign-in or sign-out. */
|
|
77
95
|
setAccessToken(token: string | null | undefined): void;
|
|
78
96
|
private call;
|
|
@@ -125,8 +143,8 @@ declare class ThesisClient {
|
|
|
125
143
|
appealed: true;
|
|
126
144
|
}>;
|
|
127
145
|
/**
|
|
128
|
-
* Live updates for one token: thesis, reply, reaction, removed.
|
|
129
|
-
* `scope: 'instance'`
|
|
146
|
+
* Live updates for one token: thesis, reply, reaction, removed. The socket URL
|
|
147
|
+
* carries `client_id`. With `scope: 'instance'` only theses written on your instance.
|
|
130
148
|
*/
|
|
131
149
|
live(chain: Chain, mint: string, options?: ThesisLiveOptions & ThesisScopeOptions): ThesisLive;
|
|
132
150
|
}
|
|
@@ -158,10 +176,23 @@ interface ThesisLiveEventMap {
|
|
|
158
176
|
error: CabalSpyIDError;
|
|
159
177
|
}
|
|
160
178
|
type LiveHandler<T> = (payload: T) => void;
|
|
179
|
+
/** Close codes of the live socket for client_id checks (the close reason is the error code). */
|
|
180
|
+
declare const THESIS_WS_CLOSE_CODES: {
|
|
181
|
+
/** client_id_required or invalid_client: final. */
|
|
182
|
+
readonly CLIENT: 4401;
|
|
183
|
+
/** origin_not_allowed: final. */
|
|
184
|
+
readonly ORIGIN: 4403;
|
|
185
|
+
/** rate_limited: reconnects after 60 s. */
|
|
186
|
+
readonly RATE_LIMITED: 4429;
|
|
187
|
+
};
|
|
188
|
+
/** The error for a refusing close code of the live socket, or null for an ordinary close. */
|
|
189
|
+
declare function thesisCloseError(code: number | undefined, reason?: string): CabalSpyIDError | null;
|
|
161
190
|
/**
|
|
162
191
|
* Public live socket for one token. Messages arrive as `thesis`, `reply`,
|
|
163
192
|
* `reaction` and `removed`; it reconnects after drops or missed heartbeats.
|
|
164
|
-
*
|
|
193
|
+
* Codes 1008 (invalid token address, too many sockets), 4401 (client_id
|
|
194
|
+
* missing or invalid) and 4403 (origin not allowed) are final; after 4429
|
|
195
|
+
* (rate limited) it waits 60 s before reconnecting.
|
|
165
196
|
*/
|
|
166
197
|
declare class ThesisLive {
|
|
167
198
|
readonly url: string;
|
|
@@ -187,4 +218,4 @@ declare class ThesisLive {
|
|
|
187
218
|
close(): void;
|
|
188
219
|
}
|
|
189
220
|
|
|
190
|
-
export { CabalSpyIDError, Chain, DEFAULT_THESIS_URL, DEFAULT_THESIS_WS_URL, Pagination, Thesis, ThesisClient, type ThesisClientOptions, ThesisCounts, ThesisEligibility, ThesisEmoji, ThesisLive, type ThesisLiveEventMap, type ThesisLiveOptions, type ThesisMe, ThesisNotices, ThesisNotifications, type ThesisPage, ThesisReactions, ThesisReportReason, type ThesisScopeOptions, ThesisSort, ThesisStance, ThesisSummary, ThesisToken, type ThesisViewer, type TokenThesesOptions, type TokenThesesPage };
|
|
221
|
+
export { CLIENT_ID_HEADER, CabalSpyIDError, Chain, DEFAULT_THESIS_URL, DEFAULT_THESIS_WS_URL, Pagination, THESIS_WS_CLOSE_CODES, Thesis, ThesisClient, type ThesisClientOptions, ThesisCounts, ThesisEligibility, ThesisEmoji, ThesisLive, type ThesisLiveEventMap, type ThesisLiveOptions, type ThesisMe, ThesisNotices, ThesisNotifications, type ThesisPage, ThesisReactions, ThesisReportReason, type ThesisScopeOptions, ThesisSort, ThesisStance, ThesisSummary, ThesisToken, type ThesisViewer, type TokenThesesOptions, type TokenThesesPage, thesisCloseError };
|