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.
Files changed (57) hide show
  1. package/CHANGELOG.md +146 -112
  2. package/LICENSE +21 -21
  3. package/README.md +505 -475
  4. package/dist/api.cjs +5 -3
  5. package/dist/api.cjs.map +1 -1
  6. package/dist/api.d.cts +1 -1
  7. package/dist/api.d.ts +1 -1
  8. package/dist/api.js +16 -14
  9. package/dist/chunk-A2ZAFAWC.cjs.map +1 -1
  10. package/dist/chunk-ABFIPCBY.cjs +109 -0
  11. package/dist/chunk-ABFIPCBY.cjs.map +1 -0
  12. package/dist/{chunk-Z3UPDOBY.js → chunk-DBYCRBVS.js} +9 -7
  13. package/dist/chunk-DBYCRBVS.js.map +1 -0
  14. package/dist/{chunk-P3MDLWLN.cjs → chunk-PKFIMBPW.cjs} +113 -54
  15. package/dist/chunk-PKFIMBPW.cjs.map +1 -0
  16. package/dist/{chunk-L4RHSWOA.cjs → chunk-ULEBNY2A.cjs} +121 -212
  17. package/dist/chunk-ULEBNY2A.cjs.map +1 -0
  18. package/dist/{chunk-X3OFGGSE.cjs → chunk-VDFCMYLR.cjs} +50 -48
  19. package/dist/chunk-VDFCMYLR.cjs.map +1 -0
  20. package/dist/chunk-VOTYIQAY.js +109 -0
  21. package/dist/chunk-VOTYIQAY.js.map +1 -0
  22. package/dist/{chunk-3LKLY5WQ.js → chunk-XLBO2RSL.js} +15 -106
  23. package/dist/chunk-XLBO2RSL.js.map +1 -0
  24. package/dist/{chunk-3P7QK2PG.js → chunk-Z23B5TY6.js} +89 -30
  25. package/dist/chunk-Z23B5TY6.js.map +1 -0
  26. package/dist/index.cjs +13 -5
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +3 -3
  29. package/dist/index.d.ts +3 -3
  30. package/dist/index.js +25 -17
  31. package/dist/index.js.map +1 -1
  32. package/dist/react.cjs +55 -2
  33. package/dist/react.cjs.map +1 -1
  34. package/dist/react.d.cts +28 -5
  35. package/dist/react.d.ts +28 -5
  36. package/dist/react.js +55 -2
  37. package/dist/react.js.map +1 -1
  38. package/dist/thesis.cjs +11 -3
  39. package/dist/thesis.cjs.map +1 -1
  40. package/dist/thesis.d.cts +42 -11
  41. package/dist/thesis.d.ts +42 -11
  42. package/dist/thesis.js +24 -16
  43. package/dist/{users-BDLkwW1p.d.cts → users-3DAw88_U.d.cts} +4 -4
  44. package/dist/{users-CDlDRuyv.d.ts → users-B4r33i4O.d.ts} +4 -4
  45. package/dist/widget.cjs +28 -12
  46. package/dist/widget.cjs.map +1 -1
  47. package/dist/widget.d.cts +37 -6
  48. package/dist/widget.d.ts +37 -6
  49. package/dist/widget.js +17 -1
  50. package/dist/widget.js.map +1 -1
  51. package/package.json +147 -147
  52. package/dist/chunk-3LKLY5WQ.js.map +0 -1
  53. package/dist/chunk-3P7QK2PG.js.map +0 -1
  54. package/dist/chunk-L4RHSWOA.cjs.map +0 -1
  55. package/dist/chunk-P3MDLWLN.cjs.map +0 -1
  56. package/dist/chunk-X3OFGGSE.cjs.map +0 -1
  57. package/dist/chunk-Z3UPDOBY.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/widget.ts"],"sourcesContent":["/**\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/widget.ts"],"sourcesContent":["/**\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/package.json CHANGED
@@ -1,147 +1,147 @@
1
- {
2
- "name": "cabalspy-id",
3
- "version": "0.5.1",
4
- "description": "SocialFi API & SDK for CabalSpy ID — add a social layer to any app: login with X + verified wallets, live trades and PnL of your users, token theses, leaderboards and copy trading on Solana, BNB Chain, Base, Ethereum and Robinhood Chain.",
5
- "keywords": [
6
- "socialfi",
7
- "socialfi-api",
8
- "socialfi-sdk",
9
- "socialfi-widget",
10
- "social-trading",
11
- "copy-trading",
12
- "crypto-social",
13
- "web3-social",
14
- "kol",
15
- "kol-tracker",
16
- "wallet-tracker",
17
- "trading-leaderboard",
18
- "pnl",
19
- "token-thesis",
20
- "crypto-login",
21
- "oidc",
22
- "openid-connect",
23
- "solana",
24
- "bnb",
25
- "base",
26
- "ethereum",
27
- "cabalspy",
28
- "react",
29
- "typescript",
30
- "sdk"
31
- ],
32
- "license": "MIT",
33
- "homepage": "https://id.cabalspy.xyz",
34
- "repository": {
35
- "type": "git",
36
- "url": "git+https://github.com/CabalSpy/cabalspy-id-js.git"
37
- },
38
- "bugs": {
39
- "url": "https://github.com/CabalSpy/cabalspy-id-js/issues"
40
- },
41
- "type": "module",
42
- "main": "./dist/index.cjs",
43
- "module": "./dist/index.js",
44
- "types": "./dist/index.d.ts",
45
- "exports": {
46
- ".": {
47
- "types": "./dist/index.d.ts",
48
- "import": "./dist/index.js",
49
- "require": "./dist/index.cjs"
50
- },
51
- "./api": {
52
- "types": "./dist/api.d.ts",
53
- "import": "./dist/api.js",
54
- "require": "./dist/api.cjs"
55
- },
56
- "./thesis": {
57
- "types": "./dist/thesis.d.ts",
58
- "import": "./dist/thesis.js",
59
- "require": "./dist/thesis.cjs"
60
- },
61
- "./widget": {
62
- "types": "./dist/widget.d.ts",
63
- "import": "./dist/widget.js",
64
- "require": "./dist/widget.cjs"
65
- },
66
- "./widget/react": {
67
- "types": "./dist/react.d.ts",
68
- "import": "./dist/react.js",
69
- "require": "./dist/react.cjs"
70
- },
71
- "./react": {
72
- "types": "./dist/react.d.ts",
73
- "import": "./dist/react.js",
74
- "require": "./dist/react.cjs"
75
- },
76
- "./package.json": "./package.json"
77
- },
78
- "typesVersions": {
79
- "*": {
80
- "api": [
81
- "./dist/api.d.ts"
82
- ],
83
- "thesis": [
84
- "./dist/thesis.d.ts"
85
- ],
86
- "widget": [
87
- "./dist/widget.d.ts"
88
- ],
89
- "widget/react": [
90
- "./dist/react.d.ts"
91
- ],
92
- "react": [
93
- "./dist/react.d.ts"
94
- ]
95
- }
96
- },
97
- "files": [
98
- "dist",
99
- "README.md",
100
- "CHANGELOG.md",
101
- "LICENSE"
102
- ],
103
- "sideEffects": false,
104
- "engines": {
105
- "node": ">=18"
106
- },
107
- "scripts": {
108
- "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsup",
109
- "typecheck": "tsc --noEmit",
110
- "lint": "eslint .",
111
- "test": "vitest run",
112
- "test:live": "vitest run --config vitest.live.config.ts",
113
- "prepublishOnly": "npm run typecheck && npm run lint && npm test && npm run build"
114
- },
115
- "peerDependencies": {
116
- "react": ">=18",
117
- "ws": "^8.0.0"
118
- },
119
- "peerDependenciesMeta": {
120
- "react": {
121
- "optional": true
122
- },
123
- "ws": {
124
- "optional": true
125
- }
126
- },
127
- "devDependencies": {
128
- "@eslint/js": "^9.39.5",
129
- "@types/node": "^22.20.5",
130
- "@types/react": "^18.3.31",
131
- "@types/react-dom": "^18.3.7",
132
- "@types/ws": "^8.18.2",
133
- "eslint": "^9.39.5",
134
- "globals": "^17.13.0",
135
- "jsdom": "^25.0.1",
136
- "react": "^18.3.1",
137
- "react-dom": "^18.3.1",
138
- "tsup": "^8.5.1",
139
- "typescript": "^5.9.3",
140
- "typescript-eslint": "^8.71.2",
141
- "vitest": "^5.0.4",
142
- "ws": "^8.22.0"
143
- },
144
- "publishConfig": {
145
- "access": "public"
146
- }
147
- }
1
+ {
2
+ "name": "cabalspy-id",
3
+ "version": "0.5.2",
4
+ "description": "SocialFi API & SDK for CabalSpy ID — add a social layer to any app: login with X + verified wallets, live trades and PnL of your users, token theses, leaderboards and copy trading on Solana, BNB Chain, Base, Ethereum and Robinhood Chain.",
5
+ "keywords": [
6
+ "socialfi",
7
+ "socialfi-api",
8
+ "socialfi-sdk",
9
+ "socialfi-widget",
10
+ "social-trading",
11
+ "copy-trading",
12
+ "crypto-social",
13
+ "web3-social",
14
+ "kol",
15
+ "kol-tracker",
16
+ "wallet-tracker",
17
+ "trading-leaderboard",
18
+ "pnl",
19
+ "token-thesis",
20
+ "crypto-login",
21
+ "oidc",
22
+ "openid-connect",
23
+ "solana",
24
+ "bnb",
25
+ "base",
26
+ "ethereum",
27
+ "cabalspy",
28
+ "react",
29
+ "typescript",
30
+ "sdk"
31
+ ],
32
+ "license": "MIT",
33
+ "homepage": "https://id.cabalspy.xyz",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/CabalSpy/cabalspy-id-js.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/CabalSpy/cabalspy-id-js/issues"
40
+ },
41
+ "type": "module",
42
+ "main": "./dist/index.cjs",
43
+ "module": "./dist/index.js",
44
+ "types": "./dist/index.d.ts",
45
+ "exports": {
46
+ ".": {
47
+ "types": "./dist/index.d.ts",
48
+ "import": "./dist/index.js",
49
+ "require": "./dist/index.cjs"
50
+ },
51
+ "./api": {
52
+ "types": "./dist/api.d.ts",
53
+ "import": "./dist/api.js",
54
+ "require": "./dist/api.cjs"
55
+ },
56
+ "./thesis": {
57
+ "types": "./dist/thesis.d.ts",
58
+ "import": "./dist/thesis.js",
59
+ "require": "./dist/thesis.cjs"
60
+ },
61
+ "./widget": {
62
+ "types": "./dist/widget.d.ts",
63
+ "import": "./dist/widget.js",
64
+ "require": "./dist/widget.cjs"
65
+ },
66
+ "./widget/react": {
67
+ "types": "./dist/react.d.ts",
68
+ "import": "./dist/react.js",
69
+ "require": "./dist/react.cjs"
70
+ },
71
+ "./react": {
72
+ "types": "./dist/react.d.ts",
73
+ "import": "./dist/react.js",
74
+ "require": "./dist/react.cjs"
75
+ },
76
+ "./package.json": "./package.json"
77
+ },
78
+ "typesVersions": {
79
+ "*": {
80
+ "api": [
81
+ "./dist/api.d.ts"
82
+ ],
83
+ "thesis": [
84
+ "./dist/thesis.d.ts"
85
+ ],
86
+ "widget": [
87
+ "./dist/widget.d.ts"
88
+ ],
89
+ "widget/react": [
90
+ "./dist/react.d.ts"
91
+ ],
92
+ "react": [
93
+ "./dist/react.d.ts"
94
+ ]
95
+ }
96
+ },
97
+ "files": [
98
+ "dist",
99
+ "README.md",
100
+ "CHANGELOG.md",
101
+ "LICENSE"
102
+ ],
103
+ "sideEffects": false,
104
+ "engines": {
105
+ "node": ">=18"
106
+ },
107
+ "scripts": {
108
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsup",
109
+ "typecheck": "tsc --noEmit",
110
+ "lint": "eslint .",
111
+ "test": "vitest run",
112
+ "test:live": "vitest run --config vitest.live.config.ts",
113
+ "prepublishOnly": "npm run typecheck && npm run lint && npm test && npm run build"
114
+ },
115
+ "peerDependencies": {
116
+ "react": ">=18",
117
+ "ws": "^8.0.0"
118
+ },
119
+ "peerDependenciesMeta": {
120
+ "react": {
121
+ "optional": true
122
+ },
123
+ "ws": {
124
+ "optional": true
125
+ }
126
+ },
127
+ "devDependencies": {
128
+ "@eslint/js": "^9.39.5",
129
+ "@types/node": "^22.20.5",
130
+ "@types/react": "^18.3.31",
131
+ "@types/react-dom": "^18.3.7",
132
+ "@types/ws": "^8.18.2",
133
+ "eslint": "^9.39.5",
134
+ "globals": "^17.13.0",
135
+ "jsdom": "^25.0.1",
136
+ "react": "^18.3.1",
137
+ "react-dom": "^18.3.1",
138
+ "tsup": "^8.5.1",
139
+ "typescript": "^5.9.3",
140
+ "typescript-eslint": "^8.71.2",
141
+ "vitest": "^5.0.4",
142
+ "ws": "^8.22.0"
143
+ },
144
+ "publishConfig": {
145
+ "access": "public"
146
+ }
147
+ }