sparkle-react 0.0.46 → 0.0.48

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/dist/index.d.mts CHANGED
@@ -1,7 +1,5 @@
1
1
  import React, { ReactNode } from 'react';
2
2
  import { z } from 'zod';
3
- import { OBSRequestTypes, OBSResponseTypes } from 'obs-websocket-js';
4
- export { OBSRequestTypes, OBSResponseTypes } from 'obs-websocket-js';
5
3
 
6
4
  declare const sparkleConfig: z.ZodObject<{
7
5
  userId: z.ZodString;
@@ -336,13 +334,6 @@ declare const SparkleHookKeys: readonly ["notification.follow", "notification.ch
336
334
  type SparkleHookKey = (typeof SparkleHookKeys)[number];
337
335
  declare function useHook<T extends keyof EventTypeMap>(hook: T): EventTypeMap[T] | undefined;
338
336
 
339
- declare function useObs(): {
340
- status: "error" | "disconnected" | "connecting" | "connected";
341
- connected: boolean;
342
- call: <T extends keyof OBSRequestTypes>(requestType: T, requestData?: OBSRequestTypes[T]) => Promise<OBSResponseTypes[T]>;
343
- };
344
- declare function useObsCall<T extends keyof OBSRequestTypes>(requestType: T): (requestData?: OBSRequestTypes[T]) => Promise<OBSResponseTypes[T]>;
345
-
346
337
  /**
347
338
  * Exécute une tâche Sparkle dans la runtime (équivalent à `ctx.invoke` / `ctx.invokeUntil`).
348
339
  * En preview Sandpack, les appels passent par `postMessage` vers l’éditeur (session).
@@ -460,4 +451,4 @@ type TwitchGetApiBody = {
460
451
  */
461
452
  declare function useTwitch(): TwitchGetClient;
462
453
 
463
- export { type SparkleHookKey, SparkleHookKeys, SparkleProvider, type SparkleProviderProps, type TwitchGetApiBody, type TwitchGetClient, type UseAuthResult, useAuth, useHook, useObs, useObsCall, useRealtime, useTask, useTwitch };
454
+ export { type SparkleHookKey, SparkleHookKeys, SparkleProvider, type SparkleProviderProps, type TwitchGetApiBody, type TwitchGetClient, type UseAuthResult, useAuth, useHook, useRealtime, useTask, useTwitch };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import k,{useMemo as Be,useEffect as Ce,useState as Re}from"react";import*as U from"react";import{createContext as re}from"react";var P=re({token:null}),M=({children:r,config:e})=>U.createElement(P.Provider,{value:{token:e.userId}},r);import pe,{useCallback as _,useContext as ge,useRef as v}from"react";import{createContext as fe,useEffect as N,useState as D}from"react";import ne from"events";import d from"zod";var oe=d.object({type:d.literal("subscribe"),channels:d.array(d.string())}),se=d.object({type:d.literal("hook"),channels:d.array(d.string())}),ie=d.object({type:d.literal("unsubscribe"),channels:d.string()}),st=d.object({event:d.literal("update"),type:d.enum(["value","hook"]),data:d.object({key:d.string(),value:d.string()})}),it=d.union([oe,ie,se]),S=class extends ne{constructor(t){super();this.socket=null;this.queue=[];this.subscriptions=[];this.hooks=[];this.isAuthenticating=!1;this.jwt=null;this.url=t}send(t){this.socket?.readyState===WebSocket.OPEN?this.socket?.send(JSON.stringify(t)):this.queue.push(t)}authenticate(t){this.isAuthenticating||(this.isAuthenticating=!0,this.jwt=t,this.connect())}connect(){let t=this.url+"?jwt="+this.jwt;this.socket=new WebSocket(t),this.socket.onopen=()=>{this.queue.forEach(n=>this.send(n)),this.queue=[]},this.socket.onmessage=n=>{let s=JSON.parse(n.data);this.emit("update",s)},this.socket.onclose=()=>{this.reconnect()}}reconnect(){setTimeout(()=>{this.queue=[{type:"subscribe",channels:this.subscriptions},{type:"hook",channels:this.hooks}],console.log("try reconnecting"),this.connect()},5e3)}subscribe(t){let n=t.filter(s=>!this.subscriptions.includes(s));n.length&&(this.subscriptions=[...this.subscriptions,...n],console.log("subscribe",t),this.send({type:"subscribe",channels:t}))}hook(t){let n=[t].filter(s=>!this.hooks.includes(s));n.length&&(this.hooks=[...this.hooks,...n],this.send({type:"hook",channels:[t]}))}unsubscribe(t){this.send({type:"unsubscribe",channels:t})}disconnect(){this.socket&&(this.socket.onclose=()=>{},this.socket.close())}};function w(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function ce(){if(typeof window>"u")return null;let r=window.top??window.parent;return!r||r===window?null:r}function y(r){let e=ce();e&&e.postMessage(r,"*")}var ae="sparkle-realtime-connect",ue="sparkle-realtime-disconnect",de="sparkle-realtime-send",le="sparkle-realtime-update";function O(){return w()}function q(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function j(r,e,t){y({type:ae,bridgeId:r,wsUrl:e,userId:t})}function L(r){y({type:ue,bridgeId:r})}function B(r,e){y({type:de,bridgeId:r,message:e})}function A(r){let e=t=>{let n=t.data;!n||n.type!==le||r(n.update)};return window.addEventListener("message",e),()=>window.removeEventListener("message",e)}var b=fe({subscribe:()=>{},listen:()=>{},values:{},hooks:{}});function G(r,e,t){let{data:n,type:s}=r;if(s==="hook"){let o=JSON.parse(n.value);t(u=>({...u,[o.hook]:o.payload}));return}let i=n.value;try{let o=parseFloat(n.value);if(!isNaN(o)&&String(o)===String(n.value).trim())i=o;else try{i=JSON.parse(n.value)}catch{i=n.value}}catch{i=n.value}e(o=>({...o,[n.key]:i}))}var F=({children:r,config:e})=>{let[t,n]=D({}),[s,i]=D({}),o=v(null),u=v(null),l=v([]),c=v([]),{token:g}=ge(P),p=O();N(()=>p?A(a=>{G(a,n,i)}):(o.current=new S(e.wsUrl),o.current.on("update",a=>{G(a,n,i)}),()=>{o.current?.disconnect()}),[p,e.wsUrl]),N(()=>{if(g){if(p){let a=q();return u.current=a,j(a,e.wsUrl,g),()=>{L(a),u.current=null,l.current=[],c.current=[]}}o.current&&o.current.authenticate(g)}},[g,p,e.wsUrl]);let f=_(a=>{if(p){let h=u.current;if(!h)return;let E=a.filter(te=>!l.current.includes(te));if(!E.length)return;l.current=[...l.current,...E],B(h,{type:"subscribe",channels:a});return}o.current?.subscribe(a)},[p]),m=_(a=>{if(p){let h=u.current;if(!h||c.current.includes(a))return;c.current=[...c.current,a],B(h,{type:"hook",channels:[a]});return}o.current?.hook(a)},[p]);return pe.createElement(b.Provider,{value:{subscribe:f,listen:m,values:t,hooks:s}},r)};import be,{createContext as ke,useCallback as Se,useEffect as ve,useRef as C,useState as Te}from"react";var me="sparkle-obs-call",we="sparkle-obs-call-result",ye="sparkle-obs-status-request",he="sparkle-obs-status-result";function W(){return w()}function $(r){try{return new URL(r).searchParams.get("clientId")}catch{return null}}function V(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function z(r){return new Promise(e=>{let t=V(),n=i=>{let o=i.data;!o||o.type!==he||o.requestId!==t||(window.removeEventListener("message",n),window.clearTimeout(s),e(!!o.connected))},s=window.setTimeout(()=>{window.removeEventListener("message",n),e(!1)},12e3);window.addEventListener("message",n),y({type:ye,requestId:t,clientId:r})})}function H(r,e,t){return new Promise((n,s)=>{let i=V(),o=l=>{let c=l.data;!c||c.type!==we||c.requestId!==i||(window.removeEventListener("message",o),window.clearTimeout(u),c.ok?n(c.data):s(new Error(typeof c.error=="string"?c.error:"obs_call_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",o),s(new Error("useObs: d\xE9lai d\xE9pass\xE9"))},2e4);window.addEventListener("message",o),y({type:me,requestId:i,clientId:r,requestType:e,requestData:t})})}var T=ke({status:"disconnected",call:()=>Promise.reject(new Error("ObsProvider not mounted"))});function J({url:r,children:e}){let[t,n]=Te("disconnected"),s=C(null),i=C(!1),o=C($(r)),u=W()&&!!o.current;ve(()=>{if(!r)return;let c=o.current;if(u&&c){i.current=!1;let f,m=async()=>{if(!i.current){n(a=>a==="connected"?a:"connecting");try{let a=await z(c);i.current||n(a?"connected":"disconnected")}catch{i.current||n("error")}}};return m(),f=setInterval(()=>{m()},4e3),()=>{i.current=!0,clearInterval(f),n("disconnected")}}i.current=!1;let g,p=async()=>{try{let{default:f}=await import("obs-websocket-js"),m=new f;s.current=m;let a=async()=>{if(!i.current){n("connecting");try{await m.connect(r),i.current||n("connected")}catch{i.current||(n("error"),g=setTimeout(a,3e3))}}};m.on("ConnectionClosed",()=>{i.current||(n("disconnected"),g=setTimeout(a,3e3))}),m.on("ConnectionError",()=>{i.current||n("error")}),await a()}catch{i.current||(n("error"),g=setTimeout(()=>{p()},3e3))}};return p(),()=>{i.current=!0,clearTimeout(g),s.current?.disconnect(),s.current=null,n("disconnected")}},[r,u]);let l=Se(async(c,g)=>{let p=o.current;if(u&&p)return H(p,c,g);let f=s.current;if(!f)throw new Error("OBS not connected");return f.call(c,g)},[u]);return be.createElement(T.Provider,{value:{status:t,call:l}},e)}import{createContext as Ie,useContext as Pe}from"react";var R=Ie(null);function I(){let r=Pe(R);if(!r)throw new Error("Sparkle hooks require SparkleProvider");return r}var xe=({children:r})=>{let[e,t]=Re(!1);return Ce(()=>{t(!0)},[]),e?r:null},Ee={userId:"",wsUrl:"wss://sparkle-bot-v2.fly.dev/ws"},Ut=({children:r,config:e})=>{let t={...Ee,...e},n=Be(()=>t.obs?.clientId?`${t.obs.bridgeUrl??"wss://sparkle-bot-v2.fly.dev/obs-bridge"}?role=client&clientId=${encodeURIComponent(t.obs.clientId)}`:"",[t.obs?.clientId,t.obs?.bridgeUrl]),s=k.createElement(R.Provider,{value:t},k.createElement(M,{config:t},k.createElement(F,{config:t},k.createElement(xe,null,r))));return n?k.createElement(J,{url:n},s):s};import{useContext as Ue,useEffect as Me,useMemo as Oe}from"react";function Lt(r,e){let t=Ue(b),n=Oe(()=>[r],[r]);if(!t)throw new Error("You must use useRealtime inside a SparkleProvider");return Me(()=>{t.subscribe([r])},[n]),n.map(s=>t.values[s]??e)}import{useEffect as qe,useState as x}from"react";var je="sparkle-widget-session-request",Le="sparkle-widget-session-result";function Ae(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function _e(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function Y(r){if(!r||typeof r!="object")return{userId:null,username:null,profilePicture:null};let e=r,t=typeof e.userId=="string"&&e.userId.trim()?e.userId:null,n=typeof e.username=="string"&&e.username.trim()?e.username:null,s=typeof e.profilePicture=="string"&&e.profilePicture.trim()?e.profilePicture:null;return{userId:t,username:n,profilePicture:s}}function Ne(){if(typeof window>"u")return Promise.resolve({userId:null,username:null,profilePicture:null});let r=window.top??window.parent;return!r||r===window?Promise.resolve({userId:null,username:null,profilePicture:null}):new Promise(e=>{let t=Ae(),n=i=>{let o=i.data;!o||o.type!==Le||o.requestId!==t||(window.removeEventListener("message",n),window.clearTimeout(s),e(Y(o)))},s=window.setTimeout(()=>{window.removeEventListener("message",n),e({userId:null,username:null,profilePicture:null})},12e3);window.addEventListener("message",n),r.postMessage({type:je,requestId:t},"*")})}async function De(){let r=await fetch("/api/sparkle-widget-session",{method:"GET",credentials:"include"});if(!r.ok)return{userId:null,username:null,profilePicture:null};let e=await r.json().catch(()=>null);return Y(e)}function Nt(){let[r,e]=x(void 0),[t,n]=x(void 0),[s,i]=x(void 0);return qe(()=>{let o=!1;return(async()=>{let l=_e()?await Ne():await De();o||(e(l.userId),n(l.username),i(l.profilePicture))})(),()=>{o=!0}},[]),{userId:r,username:t,profilePicture:s}}import{useContext as Ge,useEffect as Fe}from"react";var $t=["notification.follow","notification.cheer","notification.tips","notification.subscribe","notification.subscribe.end","notification.subscribe.gift","notification.raid","chat.message","stream.online","stream.offline","stream.update","reward.create","reward.update","reward.remove","reward.redemption.claim","reward.redemption.update","poll.begin","poll.progress","poll.end","prediction.begin","prediction.progress","prediction.lock","prediction.end","moderator.add","moderator.remove","moderator.ban","moderator.unban","moderator.shield_mode.begin"];function Vt(r){let e=Ge(b);if(!e)throw new Error("You must use useHook inside a SparkleProvider");return Fe(()=>{e.listen(r)},[r]),e.hooks[r]}import{useCallback as We,useContext as K}from"react";function Yt(){let{status:r,call:e}=K(T);return{status:r,connected:r==="connected",call:e}}function Kt(r){let{call:e}=K(T);return We(t=>e(r,t),[e,r])}import{useCallback as Q,useMemo as $e}from"react";var Ve="sparkle-invoke-task",ze="sparkle-invoke-task-result";function He(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function X(r,e){if(typeof window>"u")return Promise.reject(new Error("useTask requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTask: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,s)=>{let i=He(),o=l=>{let c=l.data;!c||c.type!==ze||c.requestId!==i||(window.removeEventListener("message",o),window.clearTimeout(u),c.ok?n(!!c.result):s(new Error(typeof c.error=="string"?c.error:"invoke_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",o),s(new Error("useTask: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",o),t.postMessage({type:Ve,requestId:i,projectId:r,...e},"*")})}async function Z(r,e){let t=await fetch(`/api/projects/${r}/invoke-task`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let n=await t.json().catch(()=>null);throw new Error(n?.error??t.statusText)}return!0}function tr(){let e=I().projectId,t=Q(async(s,i)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let o={kind:"invoke",taskId:s,payload:i};return w()?X(e,o):Z(e,o)},[e]),n=Q(async(s,i,o)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let u={kind:"invokeUntil",taskId:s,payload:o,delaySeconds:i};return w()?X(e,u):Z(e,u)},[e]);return $e(()=>({invoke:t,invokeUntil:n}),[t,n])}import{useCallback as Je,useMemo as Ye}from"react";function ee(r){let e=(t,n)=>(...s)=>r(t,n,s);return{bits:{getLeaderboard:e("bits","getLeaderboard")},channelPoints:{getCustomRewards:e("channelPoints","getCustomRewards"),getCustomRewardsByIds:e("channelPoints","getCustomRewardsByIds"),getCustomRewardById:e("channelPoints","getCustomRewardById"),getRedemptionsByIds:e("channelPoints","getRedemptionsByIds"),getRedemptionById:e("channelPoints","getRedemptionById"),getRedemptions:e("channelPoints","getRedemptions")},channel:{getChannelInfoById:e("channel","getChannelInfoById"),getFollowDate:e("channel","getFollowDate"),getChannelEditors:e("channel","getChannelEditors"),getVips:e("channel","getVips"),checkVipForUsers:e("channel","checkVipForUsers"),checkVipForUser:e("channel","checkVipForUser"),getChannelFollowerCount:e("channel","getChannelFollowerCount")},chat:{getChatters:e("chat","getChatters"),getSettings:e("chat","getSettings"),getSettingsPrivileged:e("chat","getSettingsPrivileged"),getColorsForUsers:e("chat","getColorsForUsers"),getColorForUser:e("chat","getColorForUser")},clips:{getClips:e("clips","getClips"),getClipById:e("clips","getClipById"),getClipsByIds:e("clips","getClipsByIds")},goals:{getGoals:e("goals","getGoals")},polls:{getPolls:e("polls","getPolls"),getPollsByIds:e("polls","getPollsByIds"),getPollById:e("polls","getPollById")},predictions:{getPredictions:e("predictions","getPredictions"),getPredictionsByIds:e("predictions","getPredictionsByIds"),getPredictionById:e("predictions","getPredictionById")},streams:{getStream:e("streams","getStream"),getStreamMarkers:e("streams","getStreamMarkers")},subscriptions:{getSubscriptions:e("subscriptions","getSubscriptions"),getSubscriptionsForUsers:e("subscriptions","getSubscriptionsForUsers"),getSubscriptionForUser:e("subscriptions","getSubscriptionForUser"),checkUserSubscription:e("subscriptions","checkUserSubscription")},moderation:{getBanned:e("moderation","getBanned"),checkBan:e("moderation","checkBan"),getModerators:e("moderation","getModerators"),checkModerator:e("moderation","checkModerator"),getBlockedTerms:e("moderation","getBlockedTerms")},utils:{getUserById:e("utils","getUserById"),getUserByName:e("utils","getUserByName"),getUserByIds:e("utils","getUserByIds"),getUserByNames:e("utils","getUserByNames")}}}var Ke="sparkle-twitch-get",Qe="sparkle-twitch-get-result";function Xe(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function Ze(r,e){if(typeof window>"u")return Promise.reject(new Error("useTwitch requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTwitch: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,s)=>{let i=Xe(),o=l=>{let c=l.data;!c||c.type!==Qe||c.requestId!==i||(window.removeEventListener("message",o),window.clearTimeout(u),c.ok?n(c.result):s(new Error(typeof c.error=="string"?c.error:"twitch_get_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",o),s(new Error("useTwitch: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",o),t.postMessage({type:Ke,requestId:i,projectId:r,...e},"*")})}async function et(r,e){let t=await fetch(`/api/projects/${encodeURIComponent(r)}/twitch-get`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let s=await t.json().catch(()=>null);throw new Error(s?.error??t.statusText)}return(await t.json()).result}function ar(){let e=I().projectId,t=Je(async(n,s,i)=>{if(!e?.trim())throw new Error("useTwitch: renseignez `config.projectId` sur SparkleProvider.");let o={path:[n,s],args:i};return w()?Ze(e,o):et(e,o)},[e]);return Ye(()=>ee(t),[t])}export{$t as SparkleHookKeys,Ut as SparkleProvider,Nt as useAuth,Vt as useHook,Yt as useObs,Kt as useObsCall,Lt as useRealtime,tr as useTask,ar as useTwitch};
1
+ import h,{useMemo as ee,useEffect as te,useState as re}from"react";import*as S from"react";import{createContext as N}from"react";var k=N({token:null}),P=({children:r,config:e})=>S.createElement(k.Provider,{value:{token:e.userId}},r);import _,{useCallback as C,useContext as L,useRef as V}from"react";import{createContext as z,useEffect as I,useState as T}from"react";import A from"events";import a from"zod";var D=a.object({type:a.literal("subscribe"),channels:a.array(a.string())}),F=a.object({type:a.literal("hook"),channels:a.array(a.string())}),W=a.object({type:a.literal("unsubscribe"),channels:a.string()}),Oe=a.object({event:a.literal("update"),type:a.enum(["value","hook"]),data:a.object({key:a.string(),value:a.string()})}),je=a.union([D,W,F]),w=class extends A{constructor(t){super();this.socket=null;this.queue=[];this.subscriptions=[];this.hooks=[];this.isAuthenticating=!1;this.jwt=null;this.url=t}send(t){this.socket?.readyState===WebSocket.OPEN?this.socket?.send(JSON.stringify(t)):this.queue.push(t)}authenticate(t){this.isAuthenticating||(this.isAuthenticating=!0,this.jwt=t,this.connect())}connect(){let t=this.url+"?jwt="+this.jwt;this.socket=new WebSocket(t),this.socket.onopen=()=>{this.queue.forEach(n=>this.send(n)),this.queue=[]},this.socket.onmessage=n=>{let o=JSON.parse(n.data);this.emit("update",o)},this.socket.onclose=()=>{this.reconnect()}}reconnect(){setTimeout(()=>{this.queue=[{type:"subscribe",channels:this.subscriptions},{type:"hook",channels:this.hooks}],console.log("try reconnecting"),this.connect()},5e3)}subscribe(t){let n=t.filter(o=>!this.subscriptions.includes(o));n.length&&(this.subscriptions=[...this.subscriptions,...n],console.log("subscribe",t),this.send({type:"subscribe",channels:t}))}hook(t){let n=[t].filter(o=>!this.hooks.includes(o));n.length&&(this.hooks=[...this.hooks,...n],this.send({type:"hook",channels:[t]}))}unsubscribe(t){this.send({type:"unsubscribe",channels:t})}disconnect(){this.socket&&(this.socket.onclose=()=>{},this.socket.close())}};var m=z({subscribe:()=>{},listen:()=>{},values:{},hooks:{}}),x=({children:r,config:e})=>{let[t,n]=T({}),[o,i]=T({}),s=V(null),{token:u}=L(k);I(()=>(s.current=new w(e.wsUrl),s.current.on("update",p=>{let{data:d,type:q}=p;if(q==="hook"){let g=JSON.parse(d.value);i(f=>({...f,[g.hook]:g.payload})),console.log("receive hooks",g)}else{let g=d.value;console.log("value",g);try{let f=parseFloat(d.value);if(!isNaN(f)&&String(f)===String(d.value).trim())g=f;else try{g=JSON.parse(d.value)}catch{g=d.value}}catch{g=d.value}n(f=>({...f,[d.key]:g})),console.log("receive values",t)}}),()=>{s.current?.disconnect()}),[]),I(()=>{!s.current||!u||s.current.authenticate(u)},[u]);let l=C(p=>{s.current?.subscribe(p)},[s]),c=C(p=>{s.current?.hook(p)},[s]);return _.createElement(m.Provider,{value:{subscribe:l,listen:c,values:t,hooks:o}},r)};import $,{createContext as H,useCallback as J,useEffect as Y,useRef as B,useState as K}from"react";var Q=H({status:"disconnected",call:()=>Promise.reject(new Error("ObsProvider not mounted"))});function E({url:r,children:e}){let[t,n]=K("disconnected"),o=B(null),i=B(!1);Y(()=>{if(!r)return;i.current=!1;let u;return(async()=>{let{default:c}=await import("obs-websocket-js"),p=new c;o.current=p;let d=async()=>{if(!i.current){n("connecting");try{await p.connect(r),i.current||n("connected")}catch{i.current||(n("error"),u=setTimeout(d,3e3))}}};p.on("ConnectionClosed",()=>{i.current||(n("disconnected"),u=setTimeout(d,3e3))}),p.on("ConnectionError",()=>{i.current||n("error")}),await d()})(),()=>{i.current=!0,clearTimeout(u),o.current?.disconnect(),o.current=null,n("disconnected")}},[r]);let s=J(async(u,l)=>{let c=o.current;if(!c)throw new Error("OBS not connected");return c.call(u,l)},[]);return $.createElement(Q.Provider,{value:{status:t,call:s}},e)}import{createContext as X,useContext as Z}from"react";var b=X(null);function y(){let r=Z(b);if(!r)throw new Error("Sparkle hooks require SparkleProvider");return r}var ne=({children:r})=>{let[e,t]=re(!1);return te(()=>{t(!0)},[]),e?r:null},oe={userId:"",wsUrl:"wss://sparkle-bot-v2.fly.dev/ws"},Xe=({children:r,config:e})=>{let t={...oe,...e},n=ee(()=>t.obs?.clientId?`${t.obs.bridgeUrl??"wss://sparkle-bot-v2.fly.dev/obs-bridge"}?role=client&clientId=${encodeURIComponent(t.obs.clientId)}`:"",[t.obs?.clientId,t.obs?.bridgeUrl]),o=h.createElement(b.Provider,{value:t},h.createElement(P,{config:t},h.createElement(x,{config:t},h.createElement(ne,null,r))));return n?h.createElement(E,{url:n},o):o};import{useContext as se,useEffect as ie,useMemo as ce}from"react";function nt(r,e){let t=se(m),n=ce(()=>[r],[r]);if(!t)throw new Error("You must use useRealtime inside a SparkleProvider");return ie(()=>{t.subscribe([r])},[n]),n.map(o=>t.values[o]??e)}import{useEffect as ae,useState as v}from"react";var ue="sparkle-widget-session-request",le="sparkle-widget-session-result";function de(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function pe(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function R(r){if(!r||typeof r!="object")return{userId:null,username:null,profilePicture:null};let e=r,t=typeof e.userId=="string"&&e.userId.trim()?e.userId:null,n=typeof e.username=="string"&&e.username.trim()?e.username:null,o=typeof e.profilePicture=="string"&&e.profilePicture.trim()?e.profilePicture:null;return{userId:t,username:n,profilePicture:o}}function ge(){if(typeof window>"u")return Promise.resolve({userId:null,username:null,profilePicture:null});let r=window.top??window.parent;return!r||r===window?Promise.resolve({userId:null,username:null,profilePicture:null}):new Promise(e=>{let t=de(),n=i=>{let s=i.data;!s||s.type!==le||s.requestId!==t||(window.removeEventListener("message",n),window.clearTimeout(o),e(R(s)))},o=window.setTimeout(()=>{window.removeEventListener("message",n),e({userId:null,username:null,profilePicture:null})},12e3);window.addEventListener("message",n),r.postMessage({type:ue,requestId:t},"*")})}async function fe(){let r=await fetch("/api/sparkle-widget-session",{method:"GET",credentials:"include"});if(!r.ok)return{userId:null,username:null,profilePicture:null};let e=await r.json().catch(()=>null);return R(e)}function it(){let[r,e]=v(void 0),[t,n]=v(void 0),[o,i]=v(void 0);return ae(()=>{let s=!1;return(async()=>{let l=pe()?await ge():await fe();s||(e(l.userId),n(l.username),i(l.profilePicture))})(),()=>{s=!0}},[]),{userId:r,username:t,profilePicture:o}}import{useContext as me,useEffect as he}from"react";var dt=["notification.follow","notification.cheer","notification.tips","notification.subscribe","notification.subscribe.end","notification.subscribe.gift","notification.raid","chat.message","stream.online","stream.offline","stream.update","reward.create","reward.update","reward.remove","reward.redemption.claim","reward.redemption.update","poll.begin","poll.progress","poll.end","prediction.begin","prediction.progress","prediction.lock","prediction.end","moderator.add","moderator.remove","moderator.ban","moderator.unban","moderator.shield_mode.begin"];function pt(r){let e=me(m);if(!e)throw new Error("You must use useHook inside a SparkleProvider");return he(()=>{e.listen(r)},[r]),e.hooks[r]}import{useCallback as U,useMemo as we}from"react";var ye="sparkle-invoke-task",ke="sparkle-invoke-task-result";function be(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function M(r,e){if(typeof window>"u")return Promise.reject(new Error("useTask requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTask: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,o)=>{let i=be(),s=l=>{let c=l.data;!c||c.type!==ke||c.requestId!==i||(window.removeEventListener("message",s),window.clearTimeout(u),c.ok?n(!!c.result):o(new Error(typeof c.error=="string"?c.error:"invoke_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",s),o(new Error("useTask: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",s),t.postMessage({type:ye,requestId:i,projectId:r,...e},"*")})}function O(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}async function j(r,e){let t=await fetch(`/api/projects/${r}/invoke-task`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let n=await t.json().catch(()=>null);throw new Error(n?.error??t.statusText)}return!0}function ht(){let e=y().projectId,t=U(async(o,i)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let s={kind:"invoke",taskId:o,payload:i};return O()?M(e,s):j(e,s)},[e]),n=U(async(o,i,s)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let u={kind:"invokeUntil",taskId:o,payload:s,delaySeconds:i};return O()?M(e,u):j(e,u)},[e]);return we(()=>({invoke:t,invokeUntil:n}),[t,n])}import{useCallback as ve,useMemo as Se}from"react";function G(r){let e=(t,n)=>(...o)=>r(t,n,o);return{bits:{getLeaderboard:e("bits","getLeaderboard")},channelPoints:{getCustomRewards:e("channelPoints","getCustomRewards"),getCustomRewardsByIds:e("channelPoints","getCustomRewardsByIds"),getCustomRewardById:e("channelPoints","getCustomRewardById"),getRedemptionsByIds:e("channelPoints","getRedemptionsByIds"),getRedemptionById:e("channelPoints","getRedemptionById"),getRedemptions:e("channelPoints","getRedemptions")},channel:{getChannelInfoById:e("channel","getChannelInfoById"),getFollowDate:e("channel","getFollowDate"),getChannelEditors:e("channel","getChannelEditors"),getVips:e("channel","getVips"),checkVipForUsers:e("channel","checkVipForUsers"),checkVipForUser:e("channel","checkVipForUser"),getChannelFollowerCount:e("channel","getChannelFollowerCount")},chat:{getChatters:e("chat","getChatters"),getSettings:e("chat","getSettings"),getSettingsPrivileged:e("chat","getSettingsPrivileged"),getColorsForUsers:e("chat","getColorsForUsers"),getColorForUser:e("chat","getColorForUser")},clips:{getClips:e("clips","getClips"),getClipById:e("clips","getClipById"),getClipsByIds:e("clips","getClipsByIds")},goals:{getGoals:e("goals","getGoals")},polls:{getPolls:e("polls","getPolls"),getPollsByIds:e("polls","getPollsByIds"),getPollById:e("polls","getPollById")},predictions:{getPredictions:e("predictions","getPredictions"),getPredictionsByIds:e("predictions","getPredictionsByIds"),getPredictionById:e("predictions","getPredictionById")},streams:{getStream:e("streams","getStream"),getStreamMarkers:e("streams","getStreamMarkers")},subscriptions:{getSubscriptions:e("subscriptions","getSubscriptions"),getSubscriptionsForUsers:e("subscriptions","getSubscriptionsForUsers"),getSubscriptionForUser:e("subscriptions","getSubscriptionForUser"),checkUserSubscription:e("subscriptions","checkUserSubscription")},moderation:{getBanned:e("moderation","getBanned"),checkBan:e("moderation","checkBan"),getModerators:e("moderation","getModerators"),checkModerator:e("moderation","checkModerator"),getBlockedTerms:e("moderation","getBlockedTerms")},utils:{getUserById:e("utils","getUserById"),getUserByName:e("utils","getUserByName"),getUserByIds:e("utils","getUserByIds"),getUserByNames:e("utils","getUserByNames")}}}var Pe="sparkle-twitch-get",Ce="sparkle-twitch-get-result";function Ie(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function Te(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function xe(r,e){if(typeof window>"u")return Promise.reject(new Error("useTwitch requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTwitch: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,o)=>{let i=Ie(),s=l=>{let c=l.data;!c||c.type!==Ce||c.requestId!==i||(window.removeEventListener("message",s),window.clearTimeout(u),c.ok?n(c.result):o(new Error(typeof c.error=="string"?c.error:"twitch_get_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",s),o(new Error("useTwitch: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",s),t.postMessage({type:Pe,requestId:i,projectId:r,...e},"*")})}async function Be(r,e){let t=await fetch(`/api/projects/${encodeURIComponent(r)}/twitch-get`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let o=await t.json().catch(()=>null);throw new Error(o?.error??t.statusText)}return(await t.json()).result}function St(){let e=y().projectId,t=ve(async(n,o,i)=>{if(!e?.trim())throw new Error("useTwitch: renseignez `config.projectId` sur SparkleProvider.");let s={path:[n,o],args:i};return Te()?xe(e,s):Be(e,s)},[e]);return Se(()=>G(t),[t])}export{dt as SparkleHookKeys,Xe as SparkleProvider,it as useAuth,pt as useHook,nt as useRealtime,ht as useTask,St as useTwitch};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.tsx","../src/providers/authProvider.tsx","../src/providers/realtimeProvider.tsx","../src/lib/websocket.ts","../src/lib/iframe-bridge.ts","../src/lib/realtime-bridge.ts","../src/providers/obsProvider.tsx","../src/lib/obs-bridge-messages.ts","../src/sparkle-config-context.tsx","../src/hooks/useRealtime.ts","../src/hooks/useAuth.ts","../src/hooks/useHook.ts","../src/hooks/useObs.ts","../src/hooks/useTask.ts","../src/hooks/useTwitch.ts","../src/twitch/create-twitch-get-client.ts"],"sourcesContent":["import React, { useMemo, type ReactNode, useEffect, useState } from \"react\"\nimport { AuthProvider } from \"./providers/authProvider\"\nimport { WebSocketProvider } from \"./providers/realtimeProvider\"\nimport { ObsProvider } from \"./providers/obsProvider\"\nimport { SparkleConfigContext } from \"./sparkle-config-context\"\nimport { SparkleConfig } from \"./utils/config\"\n\nconst ClientGate = ({ children }: { children: ReactNode }) => {\n const [ready, setReady] = useState(false)\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n if (!ready) return null\n return children\n}\n\nexport interface SparkleProviderProps {\n config?: SparkleConfig\n children: ReactNode\n}\n\nconst defaultConfig: SparkleConfig = {\n userId: \"\",\n wsUrl: \"wss://sparkle-bot-v2.fly.dev/ws\",\n}\n\nexport const SparkleProvider = ({ children, config }: SparkleProviderProps) => {\n const mergedConfig = { ...defaultConfig, ...config }\n\n const obsUrl = useMemo(() => {\n if (!mergedConfig.obs?.clientId) return \"\"\n const base =\n mergedConfig.obs.bridgeUrl ?? \"wss://sparkle-bot-v2.fly.dev/obs-bridge\"\n return `${base}?role=client&clientId=${encodeURIComponent(mergedConfig.obs.clientId)}`\n }, [mergedConfig.obs?.clientId, mergedConfig.obs?.bridgeUrl])\n\n const inner = (\n <SparkleConfigContext.Provider value={mergedConfig}>\n <AuthProvider config={mergedConfig}>\n <WebSocketProvider config={mergedConfig}>\n <ClientGate>{children}</ClientGate>\n </WebSocketProvider>\n </AuthProvider>\n </SparkleConfigContext.Provider>\n )\n\n if (obsUrl) {\n return <ObsProvider url={obsUrl}>{inner}</ObsProvider>\n }\n\n return inner\n}\n","import * as React from \"react\"\r\n\r\nimport { type ReactNode, createContext } from \"react\"\r\n\r\nimport { SparkleConfig } from \"../utils/config\"\r\n\r\ninterface AuthContextType {\r\n token: string | undefined | null\r\n}\r\n\r\nexport const SparkleAuthContext = createContext<AuthContextType>({\r\n token: null,\r\n})\r\n\r\ninterface AuthProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nexport const AuthProvider = ({ children, config }: AuthProviderProps) => {\r\n return (\r\n <SparkleAuthContext.Provider\r\n value={{\r\n token: config.userId,\r\n }}\r\n >\r\n {children}\r\n </SparkleAuthContext.Provider>\r\n )\r\n}\r\n","import React, { ReactNode, useCallback, useContext, useRef } from \"react\"\r\n\r\nimport { createContext, useEffect, useState } from \"react\"\r\nimport { SparkleAuthContext } from \"./authProvider\"\r\nimport { UpdateMessage, WebSocketClient } from \"../lib/websocket\"\r\nimport { SparkleConfig } from \"../utils/config\"\r\nimport {\r\n connectRealtimeBridge,\r\n disconnectRealtimeBridge,\r\n isRealtimeBridgeEnabled,\r\n newRealtimeBridgeId,\r\n onRealtimeBridgeUpdate,\r\n sendRealtimeBridgeMessage,\r\n} from \"../lib/realtime-bridge\"\r\n\r\ninterface WebSocketContextType {\r\n subscribe: (keys: string[]) => void\r\n listen: (hook: string) => void\r\n values: Record<string, any>\r\n hooks: Record<string, any>\r\n}\r\n\r\nexport const RealtimeContext = createContext<WebSocketContextType>({\r\n subscribe: () => {},\r\n listen: () => {},\r\n values: {},\r\n hooks: {},\r\n})\r\n\r\nexport type RealtimeValue = string | number | boolean | { [key: string]: any }\r\n\r\ntype State = Record<string, RealtimeValue>\r\n\r\ninterface WebSocketProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nfunction applyUpdateMessage(\r\n message: UpdateMessage,\r\n setValues: React.Dispatch<React.SetStateAction<State>>,\r\n setHooks: React.Dispatch<React.SetStateAction<Record<string, any>>>\r\n) {\r\n const { data, type } = message\r\n\r\n if (type === \"hook\") {\r\n const payload = JSON.parse(data.value)\r\n setHooks((prev) => ({\r\n ...prev,\r\n [payload.hook]: payload.payload,\r\n }))\r\n return\r\n }\r\n\r\n let value: any = data.value\r\n\r\n try {\r\n const num = parseFloat(data.value)\r\n if (!isNaN(num) && String(num) === String(data.value).trim()) {\r\n value = num\r\n } else {\r\n try {\r\n value = JSON.parse(data.value)\r\n } catch {\r\n value = data.value\r\n }\r\n }\r\n } catch {\r\n value = data.value\r\n }\r\n\r\n setValues((prev) => ({ ...prev, [data.key]: value }))\r\n}\r\n\r\nexport const WebSocketProvider = ({\r\n children,\r\n config,\r\n}: WebSocketProviderProps) => {\r\n const [values, setValues] = useState<State>({})\r\n const [hooks, setHooks] = useState<Record<string, any>>({})\r\n\r\n const socket = useRef<WebSocketClient>(null)\r\n const bridgeIdRef = useRef<string | null>(null)\r\n const bridgeSubscriptionsRef = useRef<string[]>([])\r\n const bridgeHooksRef = useRef<string[]>([])\r\n\r\n const { token } = useContext(SparkleAuthContext)\r\n const useBridge = isRealtimeBridgeEnabled()\r\n\r\n useEffect(() => {\r\n if (useBridge) {\r\n return onRealtimeBridgeUpdate((message) => {\r\n applyUpdateMessage(message, setValues, setHooks)\r\n })\r\n }\r\n\r\n socket.current = new WebSocketClient(config.wsUrl!)\r\n\r\n socket.current.on(\"update\", (message: UpdateMessage) => {\r\n applyUpdateMessage(message, setValues, setHooks)\r\n })\r\n\r\n return () => {\r\n socket.current?.disconnect()\r\n }\r\n }, [useBridge, config.wsUrl])\r\n\r\n useEffect(() => {\r\n if (!token) return\r\n\r\n if (useBridge) {\r\n const bridgeId = newRealtimeBridgeId()\r\n bridgeIdRef.current = bridgeId\r\n connectRealtimeBridge(bridgeId, config.wsUrl!, token)\r\n\r\n return () => {\r\n disconnectRealtimeBridge(bridgeId)\r\n bridgeIdRef.current = null\r\n bridgeSubscriptionsRef.current = []\r\n bridgeHooksRef.current = []\r\n }\r\n }\r\n\r\n if (!socket.current) return\r\n socket.current.authenticate(token)\r\n }, [token, useBridge, config.wsUrl])\r\n\r\n const subscribe = useCallback(\r\n (keys: string[]) => {\r\n if (useBridge) {\r\n const bridgeId = bridgeIdRef.current\r\n if (!bridgeId) return\r\n const newChannels = keys.filter(\r\n (k) => !bridgeSubscriptionsRef.current.includes(k)\r\n )\r\n if (!newChannels.length) return\r\n bridgeSubscriptionsRef.current = [\r\n ...bridgeSubscriptionsRef.current,\r\n ...newChannels,\r\n ]\r\n sendRealtimeBridgeMessage(bridgeId, {\r\n type: \"subscribe\",\r\n channels: keys,\r\n })\r\n return\r\n }\r\n socket.current?.subscribe(keys)\r\n },\r\n [useBridge]\r\n )\r\n\r\n const listen = useCallback(\r\n (hook: string) => {\r\n if (useBridge) {\r\n const bridgeId = bridgeIdRef.current\r\n if (!bridgeId) return\r\n if (bridgeHooksRef.current.includes(hook)) return\r\n bridgeHooksRef.current = [...bridgeHooksRef.current, hook]\r\n sendRealtimeBridgeMessage(bridgeId, {\r\n type: \"hook\",\r\n channels: [hook],\r\n })\r\n return\r\n }\r\n socket.current?.hook(hook)\r\n },\r\n [useBridge]\r\n )\r\n\r\n return (\r\n <RealtimeContext.Provider\r\n value={{\r\n subscribe,\r\n listen,\r\n values,\r\n hooks,\r\n }}\r\n >\r\n {children}\r\n </RealtimeContext.Provider>\r\n )\r\n}\r\n","import EventEmitter from \"events\"\r\nimport z from \"zod\"\r\n\r\nconst subscribeSchema = z.object({\r\n type: z.literal(\"subscribe\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst hookSchema = z.object({\r\n type: z.literal(\"hook\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst unsubscribeSchema = z.object({\r\n type: z.literal(\"unsubscribe\"),\r\n channels: z.string(),\r\n})\r\n\r\nconst updateSchema = z.object({\r\n event: z.literal(\"update\"),\r\n type: z.enum([\"value\", \"hook\"]),\r\n data: z.object({\r\n key: z.string(),\r\n value: z.string(),\r\n }),\r\n})\r\n\r\nconst messageSchema = z.union([subscribeSchema, unsubscribeSchema, hookSchema])\r\n\r\nexport type Message = z.infer<typeof messageSchema>\r\n\r\nexport type UpdateMessage = z.infer<typeof updateSchema>\r\nexport type SubscribeMessage = z.infer<typeof subscribeSchema>\r\nexport type UnsubscribeMessage = z.infer<typeof unsubscribeSchema>\r\n\r\nexport class WebSocketClient extends EventEmitter {\r\n private socket: WebSocket | null = null\r\n private queue: Message[] = []\r\n private subscriptions: string[] = []\r\n private hooks: string[] = []\r\n private isAuthenticating = false\r\n\r\n private url: string\r\n private jwt: string | null = null\r\n\r\n constructor(url: string) {\r\n super()\r\n this.url = url\r\n }\r\n\r\n private send(data: Message) {\r\n if (this.socket?.readyState === WebSocket.OPEN) {\r\n this.socket?.send(JSON.stringify(data))\r\n } else {\r\n this.queue.push(data)\r\n }\r\n }\r\n\r\n authenticate(jwt: string) {\r\n if (this.isAuthenticating) return\r\n this.isAuthenticating = true\r\n this.jwt = jwt\r\n this.connect()\r\n }\r\n\r\n private connect() {\r\n const authenticatedUrl = this.url + \"?jwt=\" + this.jwt\r\n this.socket = new WebSocket(authenticatedUrl)\r\n\r\n this.socket.onopen = () => {\r\n this.queue.forEach((data) => this.send(data))\r\n this.queue = []\r\n }\r\n\r\n this.socket.onmessage = (event) => {\r\n const data = JSON.parse(event.data)\r\n this.emit(\"update\", data)\r\n }\r\n\r\n this.socket.onclose = () => {\r\n this.reconnect()\r\n }\r\n }\r\n\r\n reconnect() {\r\n setTimeout(() => {\r\n this.queue = [\r\n {\r\n type: \"subscribe\",\r\n channels: this.subscriptions,\r\n },\r\n {\r\n type: \"hook\",\r\n channels: this.hooks,\r\n },\r\n ]\r\n\r\n console.log(\"try reconnecting\")\r\n\r\n this.connect()\r\n }, 5000)\r\n }\r\n\r\n subscribe(channels: string[]) {\r\n const newChannels = channels.filter(\r\n (channel) => !this.subscriptions.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.subscriptions = [...this.subscriptions, ...newChannels]\r\n\r\n console.log(\"subscribe\", channels)\r\n\r\n this.send({\r\n type: \"subscribe\",\r\n channels,\r\n })\r\n }\r\n\r\n hook(hook: string) {\r\n const newChannels = [hook].filter(\r\n (channel) => !this.hooks.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.hooks = [...this.hooks, ...newChannels]\r\n\r\n this.send({\r\n type: \"hook\",\r\n channels: [hook],\r\n })\r\n }\r\n\r\n unsubscribe(channels: string) {\r\n this.send({ type: \"unsubscribe\", channels })\r\n }\r\n\r\n disconnect() {\r\n if (!this.socket) return\r\n\r\n this.socket.onclose = () => {}\r\n this.socket.close()\r\n }\r\n}\r\n","export function shouldUsePostMessageBridge(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nexport function getPostMessageBridgeTarget(): Window | null {\r\n if (typeof window === \"undefined\") return null\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) return null\r\n return target\r\n}\r\n\r\nexport function postToParentBridge(data: Record<string, unknown>): void {\r\n const target = getPostMessageBridgeTarget()\r\n if (!target) return\r\n target.postMessage(data, \"*\")\r\n}\r\n","import { postToParentBridge, shouldUsePostMessageBridge } from \"./iframe-bridge\"\r\nimport type { Message, UpdateMessage } from \"./websocket\"\r\n\r\nexport const REALTIME_CONNECT = \"sparkle-realtime-connect\"\r\nexport const REALTIME_DISCONNECT = \"sparkle-realtime-disconnect\"\r\nexport const REALTIME_SEND = \"sparkle-realtime-send\"\r\nexport const REALTIME_UPDATE = \"sparkle-realtime-update\"\r\n\r\nexport function isRealtimeBridgeEnabled(): boolean {\r\n return shouldUsePostMessageBridge()\r\n}\r\n\r\nexport function newRealtimeBridgeId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nexport function connectRealtimeBridge(\r\n bridgeId: string,\r\n wsUrl: string,\r\n userId: string\r\n): void {\r\n postToParentBridge({\r\n type: REALTIME_CONNECT,\r\n bridgeId,\r\n wsUrl,\r\n userId,\r\n })\r\n}\r\n\r\nexport function disconnectRealtimeBridge(bridgeId: string): void {\r\n postToParentBridge({ type: REALTIME_DISCONNECT, bridgeId })\r\n}\r\n\r\nexport function sendRealtimeBridgeMessage(\r\n bridgeId: string,\r\n message: Message\r\n): void {\r\n postToParentBridge({\r\n type: REALTIME_SEND,\r\n bridgeId,\r\n message,\r\n })\r\n}\r\n\r\nexport function onRealtimeBridgeUpdate(\r\n handler: (update: UpdateMessage) => void\r\n): () => void {\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== REALTIME_UPDATE) return\r\n handler(d.update as UpdateMessage)\r\n }\r\n window.addEventListener(\"message\", onMsg)\r\n return () => window.removeEventListener(\"message\", onMsg)\r\n}\r\n","import React, {\r\n createContext,\r\n useCallback,\r\n useEffect,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from \"react\"\r\nimport type OBSWebSocket from \"obs-websocket-js\"\r\nimport type { OBSRequestTypes, OBSResponseTypes } from \"obs-websocket-js\"\r\nimport {\r\n bridgeObsCall,\r\n isObsPostMessageBridgeEnabled,\r\n parseObsClientIdFromUrl,\r\n requestObsBridgeStatus,\r\n} from \"../lib/obs-bridge-messages\"\r\n\r\ntype ObsStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\"\r\n\r\ninterface ObsContextType {\r\n status: ObsStatus\r\n call: <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ) => Promise<OBSResponseTypes[T]>\r\n}\r\n\r\nexport const ObsContext = createContext<ObsContextType>({\r\n status: \"disconnected\",\r\n call: () => Promise.reject(new Error(\"ObsProvider not mounted\")),\r\n})\r\n\r\nexport interface ObsProviderProps {\r\n url: string\r\n children: ReactNode\r\n}\r\n\r\nexport function ObsProvider({ url, children }: ObsProviderProps) {\r\n const [status, setStatus] = useState<ObsStatus>(\"disconnected\")\r\n const obsRef = useRef<OBSWebSocket | null>(null)\r\n const cancelledRef = useRef(false)\r\n const clientIdRef = useRef(parseObsClientIdFromUrl(url))\r\n const useBridge = isObsPostMessageBridgeEnabled() && !!clientIdRef.current\r\n\r\n useEffect(() => {\r\n if (!url) return\r\n\r\n const clientId = clientIdRef.current\r\n if (useBridge && clientId) {\r\n cancelledRef.current = false\r\n let statusTimer: ReturnType<typeof setInterval>\r\n\r\n const pollStatus = async () => {\r\n if (cancelledRef.current) return\r\n setStatus((prev) => (prev === \"connected\" ? prev : \"connecting\"))\r\n try {\r\n const connected = await requestObsBridgeStatus(clientId)\r\n if (!cancelledRef.current) {\r\n setStatus(connected ? \"connected\" : \"disconnected\")\r\n }\r\n } catch {\r\n if (!cancelledRef.current) setStatus(\"error\")\r\n }\r\n }\r\n\r\n void pollStatus()\r\n statusTimer = setInterval(() => void pollStatus(), 4000)\r\n\r\n return () => {\r\n cancelledRef.current = true\r\n clearInterval(statusTimer)\r\n setStatus(\"disconnected\")\r\n }\r\n }\r\n\r\n cancelledRef.current = false\r\n let reconnectTimer: ReturnType<typeof setTimeout>\r\n\r\n const setup = async () => {\r\n try {\r\n const { default: OBSWebSocket } = await import(\"obs-websocket-js\")\r\n const obs = new OBSWebSocket()\r\n obsRef.current = obs\r\n\r\n const doConnect = async () => {\r\n if (cancelledRef.current) return\r\n setStatus(\"connecting\")\r\n try {\r\n await obs.connect(url)\r\n if (!cancelledRef.current) setStatus(\"connected\")\r\n } catch {\r\n if (!cancelledRef.current) {\r\n setStatus(\"error\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n }\r\n }\r\n\r\n obs.on(\"ConnectionClosed\", () => {\r\n if (!cancelledRef.current) {\r\n setStatus(\"disconnected\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n })\r\n\r\n obs.on(\"ConnectionError\", () => {\r\n if (!cancelledRef.current) setStatus(\"error\")\r\n })\r\n\r\n await doConnect()\r\n } catch {\r\n if (!cancelledRef.current) {\r\n setStatus(\"error\")\r\n reconnectTimer = setTimeout(() => {\r\n void setup()\r\n }, 3000)\r\n }\r\n }\r\n }\r\n\r\n void setup()\r\n\r\n return () => {\r\n cancelledRef.current = true\r\n clearTimeout(reconnectTimer!)\r\n obsRef.current?.disconnect()\r\n obsRef.current = null\r\n setStatus(\"disconnected\")\r\n }\r\n }, [url, useBridge])\r\n\r\n const call = useCallback(\r\n async <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ): Promise<OBSResponseTypes[T]> => {\r\n const clientId = clientIdRef.current\r\n if (useBridge && clientId) {\r\n return bridgeObsCall(clientId, requestType, requestData)\r\n }\r\n const obs = obsRef.current\r\n if (!obs) throw new Error(\"OBS not connected\")\r\n return obs.call(requestType, requestData)\r\n },\r\n [useBridge]\r\n )\r\n\r\n return (\r\n <ObsContext.Provider value={{ status, call }}>\r\n {children}\r\n </ObsContext.Provider>\r\n )\r\n}\r\n","import { postToParentBridge, shouldUsePostMessageBridge } from \"./iframe-bridge\"\r\nimport type { OBSRequestTypes, OBSResponseTypes } from \"obs-websocket-js\"\r\n\r\nexport const OBS_CALL = \"sparkle-obs-call\"\r\nexport const OBS_CALL_RESULT = \"sparkle-obs-call-result\"\r\nexport const OBS_STATUS_REQUEST = \"sparkle-obs-status-request\"\r\nexport const OBS_STATUS_RESULT = \"sparkle-obs-status-result\"\r\n\r\nexport function isObsPostMessageBridgeEnabled(): boolean {\r\n return shouldUsePostMessageBridge()\r\n}\r\n\r\nexport function parseObsClientIdFromUrl(url: string): string | null {\r\n try {\r\n return new URL(url).searchParams.get(\"clientId\")\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nexport function requestObsBridgeStatus(clientId: string): Promise<boolean> {\r\n return new Promise((resolve) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (\r\n !d ||\r\n d.type !== OBS_STATUS_RESULT ||\r\n d.requestId !== requestId\r\n ) {\r\n return\r\n }\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n resolve(Boolean(d.connected))\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n resolve(false)\r\n }, 12_000)\r\n window.addEventListener(\"message\", onMsg)\r\n postToParentBridge({\r\n type: OBS_STATUS_REQUEST,\r\n requestId,\r\n clientId,\r\n })\r\n })\r\n}\r\n\r\nexport function bridgeObsCall<T extends keyof OBSRequestTypes>(\r\n clientId: string,\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n): Promise<OBSResponseTypes[T]> {\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== OBS_CALL_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(d.data as OBSResponseTypes[T])\r\n else reject(new Error(typeof d.error === \"string\" ? d.error : \"obs_call_failed\"))\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useObs: délai dépassé\"))\r\n }, 20_000)\r\n window.addEventListener(\"message\", onMsg)\r\n postToParentBridge({\r\n type: OBS_CALL,\r\n requestId,\r\n clientId,\r\n requestType,\r\n requestData,\r\n })\r\n })\r\n}\r\n","import { createContext, useContext } from \"react\"\r\nimport type { SparkleConfig } from \"./utils/config\"\r\n\r\nexport const SparkleConfigContext = createContext<SparkleConfig | null>(null)\r\n\r\nexport function useSparkleConfig(): SparkleConfig {\r\n const cfg = useContext(SparkleConfigContext)\r\n if (!cfg) {\r\n throw new Error(\"Sparkle hooks require SparkleProvider\")\r\n }\r\n return cfg\r\n}\r\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext, RealtimeValue } from \"../providers/realtimeProvider\"\r\n\r\nexport function useRealtime<T extends RealtimeValue>(\r\n key: string,\r\n defaultValue?: T\r\n): T[] {\r\n const context = useContext(RealtimeContext)\r\n\r\n const keys = useMemo(() => [key], [key])\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useRealtime inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.subscribe([key])\r\n }, [keys])\r\n\r\n return keys.map((k) => (context.values[k] ?? defaultValue) as T)\r\n}\r\n","import { useEffect, useState } from \"react\"\n\nconst WIDGET_SESSION_REQ = \"sparkle-widget-session-request\"\nconst WIDGET_SESSION_RES = \"sparkle-widget-session-result\"\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbedded(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction parseSessionMessage(d: unknown): {\n userId: string | null\n username: string | null\n profilePicture: string | null\n} {\n if (!d || typeof d !== \"object\") {\n return { userId: null, username: null, profilePicture: null }\n }\n const o = d as Record<string, unknown>\n const userId =\n typeof o.userId === \"string\" && o.userId.trim() ? o.userId : null\n const username =\n typeof o.username === \"string\" && o.username.trim() ? o.username : null\n const profilePicture =\n typeof o.profilePicture === \"string\" && o.profilePicture.trim()\n ? o.profilePicture\n : null\n return { userId, username, profilePicture }\n}\n\nfunction requestSessionViaParent(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n if (typeof window === \"undefined\") {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n return new Promise((resolve) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== WIDGET_SESSION_RES || d.requestId !== requestId)\n return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n resolve(parseSessionMessage(d))\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n resolve({ userId: null, username: null, profilePicture: null })\n }, 12_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: WIDGET_SESSION_REQ, requestId }, \"*\")\n })\n}\n\nasync function fetchSessionPayload(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n const res = await fetch(\"/api/sparkle-widget-session\", {\n method: \"GET\",\n credentials: \"include\",\n })\n if (!res.ok) {\n return { userId: null, username: null, profilePicture: null }\n }\n const data = await res.json().catch(() => null)\n return parseSessionMessage(data)\n}\n\nexport type UseAuthResult = {\n /** Identifiant Twitch (`Account.accountId`), pas l’id interne Sparkle. */\n userId: string | null | undefined\n /** `User.name` Prisma (nom affiché OAuth). */\n username: string | null | undefined\n /** `User.image` Prisma (URL avatar). */\n profilePicture: string | null | undefined\n}\n\n/**\n * Profil session navigateur : `userId` = id Twitch (`Account.accountId` OAuth) ;\n * `username` / `profilePicture` depuis `User`. Champs à `undefined` pendant le chargement.\n */\nexport function useAuth(): UseAuthResult {\n const [userId, setUserId] = useState<string | null | undefined>(undefined)\n const [username, setUsername] = useState<string | null | undefined>(undefined)\n const [profilePicture, setProfilePicture] = useState<\n string | null | undefined\n >(undefined)\n\n useEffect(() => {\n let cancelled = false\n const run = async () => {\n const p = isEmbedded()\n ? await requestSessionViaParent()\n : await fetchSessionPayload()\n if (!cancelled) {\n setUserId(p.userId)\n setUsername(p.username)\n setProfilePicture(p.profilePicture)\n }\n }\n void run()\n return () => {\n cancelled = true\n }\n }, [])\n\n return { userId, username, profilePicture }\n}\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext } from \"../providers/realtimeProvider\"\r\nimport { EventTypeMap } from \"./hook\"\r\n\r\nexport const SparkleHookKeys = [\r\n \"notification.follow\",\r\n \"notification.cheer\",\r\n \"notification.tips\",\r\n \"notification.subscribe\",\r\n \"notification.subscribe.end\",\r\n \"notification.subscribe.gift\",\r\n \"notification.raid\",\r\n\r\n \"chat.message\",\r\n\r\n \"stream.online\",\r\n \"stream.offline\",\r\n \"stream.update\",\r\n\r\n \"reward.create\",\r\n \"reward.update\",\r\n \"reward.remove\",\r\n\r\n \"reward.redemption.claim\",\r\n \"reward.redemption.update\",\r\n\r\n \"poll.begin\",\r\n \"poll.progress\",\r\n \"poll.end\",\r\n\r\n \"prediction.begin\",\r\n \"prediction.progress\",\r\n \"prediction.lock\",\r\n \"prediction.end\",\r\n\r\n \"moderator.add\",\r\n \"moderator.remove\",\r\n \"moderator.ban\",\r\n \"moderator.unban\",\r\n \"moderator.shield_mode.begin\",\r\n] as const\r\n\r\nexport type SparkleHookKey = (typeof SparkleHookKeys)[number]\r\n\r\nexport function useHook<T extends keyof EventTypeMap>(\r\n hook: T\r\n): EventTypeMap[T] | undefined {\r\n const context = useContext(RealtimeContext)\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useHook inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.listen(hook)\r\n }, [hook])\r\n\r\n return context.hooks[hook] as EventTypeMap[T]\r\n}\r\n","import { useCallback, useContext } from \"react\"\r\nimport type { OBSRequestTypes, OBSResponseTypes } from \"obs-websocket-js\"\r\nimport { ObsContext } from \"../providers/obsProvider\"\r\n\r\nexport type { OBSRequestTypes, OBSResponseTypes }\r\n\r\nexport function useObs() {\r\n const { status, call } = useContext(ObsContext)\r\n\r\n return {\r\n status,\r\n connected: status === \"connected\",\r\n call,\r\n }\r\n}\r\n\r\nexport function useObsCall<T extends keyof OBSRequestTypes>(requestType: T) {\r\n const { call } = useContext(ObsContext)\r\n\r\n return useCallback(\r\n (requestData?: OBSRequestTypes[T]) => call(requestType, requestData),\r\n [call, requestType]\r\n )\r\n}\r\n","import { useCallback, useMemo } from \"react\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\nimport { shouldUsePostMessageBridge } from \"../lib/iframe-bridge\"\r\n\r\nconst BRIDGE_TYPE = \"sparkle-invoke-task\"\r\nconst BRIDGE_RESULT = \"sparkle-invoke-task-result\"\r\n\r\ntype InvokeBridgeBody =\r\n | { kind: \"invoke\"; taskId: string; payload?: unknown }\r\n | {\r\n kind: \"invokeUntil\"\r\n taskId: string\r\n payload?: unknown\r\n delaySeconds: number\r\n }\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction bridgeInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useTask requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useTask: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(Boolean(d.result))\r\n else reject(new Error(typeof d.error === \"string\" ? d.error : \"invoke_failed\"))\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useTask: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage({ type: BRIDGE_TYPE, requestId, projectId, ...body }, \"*\")\r\n })\r\n}\r\n\r\nasync function fetchInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n const res = await fetch(`/api/projects/${projectId}/invoke-task`, {\r\n method: \"POST\",\r\n credentials: \"include\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n })\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Exécute une tâche Sparkle dans la runtime (équivalent à `ctx.invoke` / `ctx.invokeUntil`).\r\n * En preview Sandpack, les appels passent par `postMessage` vers l’éditeur (session).\r\n */\r\nexport function useTask() {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId\r\n\r\n const invoke = useCallback(\r\n async (taskId: string, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = { kind: \"invoke\", taskId, payload }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n const invokeUntil = useCallback(\r\n async (taskId: string, delaySeconds: number, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = {\r\n kind: \"invokeUntil\",\r\n taskId,\r\n payload,\r\n delaySeconds,\r\n }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n return useMemo(() => ({ invoke, invokeUntil }), [invoke, invokeUntil])\r\n}\r\n","import { useCallback, useMemo } from \"react\"\nimport { useSparkleConfig } from \"../sparkle-config-context\"\nimport { shouldUsePostMessageBridge } from \"../lib/iframe-bridge\"\nimport { createTwitchGetClient } from \"../twitch/create-twitch-get-client\"\nimport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\n\nexport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\n\nconst BRIDGE_TYPE = \"sparkle-twitch-get\"\nconst BRIDGE_RESULT = \"sparkle-twitch-get-result\"\n\nexport type TwitchGetApiBody = {\n path: string[]\n args?: unknown[]\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction bridgeTwitchGet(\n projectId: string,\n body: TwitchGetApiBody\n): Promise<unknown> {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"useTwitch requires a browser environment\"))\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.reject(\n new Error(\n \"useTwitch: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\n )\n )\n }\n\n return new Promise((resolve, reject) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n if (d.ok) resolve(d.result)\n else\n reject(\n new Error(typeof d.error === \"string\" ? d.error : \"twitch_get_failed\")\n )\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n reject(new Error(\"useTwitch: délai dépassé\"))\n }, 45_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage(\n { type: BRIDGE_TYPE, requestId, projectId, ...body },\n \"*\"\n )\n })\n}\n\nasync function fetchTwitchGet(\n projectId: string,\n body: TwitchGetApiBody\n): Promise<unknown> {\n const res = await fetch(\n `/api/projects/${encodeURIComponent(projectId)}/twitch-get`,\n {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n }\n )\n if (!res.ok) {\n const err = (await res.json().catch(() => null)) as { error?: string } | null\n throw new Error(err?.error ?? res.statusText)\n }\n const json = (await res.json()) as { result?: unknown }\n return json.result\n}\n\n/**\n * Client Twitch en lecture seule (Twurple via le backend du projet).\n * Même surface GET que `ctx.twitch` dans les handlers runtime — pas les mutations.\n *\n * Requiert `config.projectId` sur `SparkleProvider`.\n */\nexport function useTwitch(): TwitchGetClient {\n const config = useSparkleConfig()\n const projectId = config.projectId\n\n const invoke = useCallback(\n async (namespace: string, method: string, args: unknown[]) => {\n if (!projectId?.trim()) {\n throw new Error(\n \"useTwitch: renseignez `config.projectId` sur SparkleProvider.\"\n )\n }\n const body: TwitchGetApiBody = {\n path: [namespace, method],\n args,\n }\n if (shouldUsePostMessageBridge()) {\n return bridgeTwitchGet(projectId, body)\n }\n return fetchTwitchGet(projectId, body)\n },\n [projectId]\n )\n\n return useMemo(() => createTwitchGetClient(invoke), [invoke])\n}\n","import type { TwitchGetClient } from \"./twitch-get-types\"\n\nexport type TwitchGetInvoker = (\n namespace: string,\n method: string,\n args: unknown[]\n) => Promise<unknown>\n\nexport function createTwitchGetClient(\n invoke: TwitchGetInvoker\n): TwitchGetClient {\n const call =\n (ns: string, method: string) =>\n (...args: unknown[]) =>\n invoke(ns, method, args)\n\n return {\n bits: {\n getLeaderboard: call(\"bits\", \"getLeaderboard\"),\n },\n channelPoints: {\n getCustomRewards: call(\"channelPoints\", \"getCustomRewards\"),\n getCustomRewardsByIds: call(\"channelPoints\", \"getCustomRewardsByIds\"),\n getCustomRewardById: call(\"channelPoints\", \"getCustomRewardById\"),\n getRedemptionsByIds: call(\"channelPoints\", \"getRedemptionsByIds\"),\n getRedemptionById: call(\"channelPoints\", \"getRedemptionById\"),\n getRedemptions: call(\"channelPoints\", \"getRedemptions\"),\n },\n channel: {\n getChannelInfoById: call(\"channel\", \"getChannelInfoById\"),\n getFollowDate: call(\"channel\", \"getFollowDate\"),\n getChannelEditors: call(\"channel\", \"getChannelEditors\"),\n getVips: call(\"channel\", \"getVips\"),\n checkVipForUsers: call(\"channel\", \"checkVipForUsers\"),\n checkVipForUser: call(\"channel\", \"checkVipForUser\"),\n getChannelFollowerCount: call(\"channel\", \"getChannelFollowerCount\"),\n },\n chat: {\n getChatters: call(\"chat\", \"getChatters\"),\n getSettings: call(\"chat\", \"getSettings\"),\n getSettingsPrivileged: call(\"chat\", \"getSettingsPrivileged\"),\n getColorsForUsers: call(\"chat\", \"getColorsForUsers\"),\n getColorForUser: call(\"chat\", \"getColorForUser\"),\n },\n clips: {\n getClips: call(\"clips\", \"getClips\"),\n getClipById: call(\"clips\", \"getClipById\"),\n getClipsByIds: call(\"clips\", \"getClipsByIds\"),\n },\n goals: {\n getGoals: call(\"goals\", \"getGoals\"),\n },\n polls: {\n getPolls: call(\"polls\", \"getPolls\"),\n getPollsByIds: call(\"polls\", \"getPollsByIds\"),\n getPollById: call(\"polls\", \"getPollById\"),\n },\n predictions: {\n getPredictions: call(\"predictions\", \"getPredictions\"),\n getPredictionsByIds: call(\"predictions\", \"getPredictionsByIds\"),\n getPredictionById: call(\"predictions\", \"getPredictionById\"),\n },\n streams: {\n getStream: call(\"streams\", \"getStream\"),\n getStreamMarkers: call(\"streams\", \"getStreamMarkers\"),\n },\n subscriptions: {\n getSubscriptions: call(\"subscriptions\", \"getSubscriptions\"),\n getSubscriptionsForUsers: call(\n \"subscriptions\",\n \"getSubscriptionsForUsers\"\n ),\n getSubscriptionForUser: call(\"subscriptions\", \"getSubscriptionForUser\"),\n checkUserSubscription: call(\"subscriptions\", \"checkUserSubscription\"),\n },\n moderation: {\n getBanned: call(\"moderation\", \"getBanned\"),\n checkBan: call(\"moderation\", \"checkBan\"),\n getModerators: call(\"moderation\", \"getModerators\"),\n checkModerator: call(\"moderation\", \"checkModerator\"),\n getBlockedTerms: call(\"moderation\", \"getBlockedTerms\"),\n },\n utils: {\n getUserById: call(\"utils\", \"getUserById\"),\n getUserByName: call(\"utils\", \"getUserByName\"),\n getUserByIds: call(\"utils\", \"getUserByIds\"),\n getUserByNames: call(\"utils\", \"getUserByNames\"),\n },\n } as unknown as TwitchGetClient\n}\n"],"mappings":"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","names":["React","useMemo","useEffect","useState","React","createContext","SparkleAuthContext","AuthProvider","children","config","React","useCallback","useContext","useRef","createContext","useEffect","useState","EventEmitter","z","subscribeSchema","hookSchema","unsubscribeSchema","updateSchema","messageSchema","WebSocketClient","url","data","jwt","authenticatedUrl","event","channels","newChannels","channel","hook","shouldUsePostMessageBridge","getPostMessageBridgeTarget","target","postToParentBridge","data","REALTIME_CONNECT","REALTIME_DISCONNECT","REALTIME_SEND","REALTIME_UPDATE","isRealtimeBridgeEnabled","shouldUsePostMessageBridge","newRealtimeBridgeId","connectRealtimeBridge","bridgeId","wsUrl","userId","postToParentBridge","disconnectRealtimeBridge","sendRealtimeBridgeMessage","message","onRealtimeBridgeUpdate","handler","onMsg","ev","d","RealtimeContext","createContext","applyUpdateMessage","message","setValues","setHooks","data","type","payload","prev","value","num","WebSocketProvider","children","config","values","useState","hooks","socket","useRef","bridgeIdRef","bridgeSubscriptionsRef","bridgeHooksRef","token","useContext","SparkleAuthContext","useBridge","isRealtimeBridgeEnabled","useEffect","onRealtimeBridgeUpdate","WebSocketClient","bridgeId","newRealtimeBridgeId","connectRealtimeBridge","disconnectRealtimeBridge","subscribe","useCallback","keys","newChannels","k","sendRealtimeBridgeMessage","listen","hook","React","React","createContext","useCallback","useEffect","useRef","useState","OBS_CALL","OBS_CALL_RESULT","OBS_STATUS_REQUEST","OBS_STATUS_RESULT","isObsPostMessageBridgeEnabled","shouldUsePostMessageBridge","parseObsClientIdFromUrl","url","newRequestId","requestObsBridgeStatus","clientId","resolve","requestId","onMsg","ev","d","timer","postToParentBridge","bridgeObsCall","requestType","requestData","reject","ObsContext","createContext","ObsProvider","url","children","status","setStatus","useState","obsRef","useRef","cancelledRef","clientIdRef","parseObsClientIdFromUrl","useBridge","isObsPostMessageBridgeEnabled","useEffect","clientId","statusTimer","pollStatus","prev","connected","requestObsBridgeStatus","reconnectTimer","setup","OBSWebSocket","obs","doConnect","call","useCallback","requestType","requestData","bridgeObsCall","React","createContext","useContext","SparkleConfigContext","useSparkleConfig","cfg","ClientGate","children","ready","setReady","useState","useEffect","defaultConfig","SparkleProvider","config","mergedConfig","obsUrl","useMemo","inner","React","SparkleConfigContext","AuthProvider","WebSocketProvider","ObsProvider","useContext","useEffect","useMemo","useRealtime","key","defaultValue","context","useContext","RealtimeContext","keys","useMemo","useEffect","k","useEffect","useState","WIDGET_SESSION_REQ","WIDGET_SESSION_RES","newRequestId","isEmbedded","parseSessionMessage","d","o","userId","username","profilePicture","requestSessionViaParent","target","resolve","requestId","onMsg","ev","timer","fetchSessionPayload","res","data","useAuth","setUserId","setUsername","setProfilePicture","cancelled","p","useContext","useEffect","SparkleHookKeys","useHook","hook","context","useContext","RealtimeContext","useEffect","useCallback","useContext","useObs","status","call","useContext","ObsContext","useObsCall","requestType","useCallback","requestData","useCallback","useMemo","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","bridgeInvoke","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchInvoke","res","err","useTask","useSparkleConfig","invoke","useCallback","taskId","payload","shouldUsePostMessageBridge","invokeUntil","delaySeconds","useMemo","useCallback","useMemo","createTwitchGetClient","invoke","call","ns","method","args","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","bridgeTwitchGet","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchTwitchGet","res","err","useTwitch","useSparkleConfig","invoke","useCallback","namespace","method","args","shouldUsePostMessageBridge","useMemo","createTwitchGetClient"]}
1
+ {"version":3,"sources":["../src/provider.tsx","../src/providers/authProvider.tsx","../src/providers/realtimeProvider.tsx","../src/lib/websocket.ts","../src/providers/obsProvider.tsx","../src/sparkle-config-context.tsx","../src/hooks/useRealtime.ts","../src/hooks/useAuth.ts","../src/hooks/useHook.ts","../src/hooks/useTask.ts","../src/hooks/useTwitch.ts","../src/twitch/create-twitch-get-client.ts"],"sourcesContent":["import React, { useMemo, type ReactNode, useEffect, useState } from \"react\"\nimport { AuthProvider } from \"./providers/authProvider\"\nimport { WebSocketProvider } from \"./providers/realtimeProvider\"\nimport { ObsProvider } from \"./providers/obsProvider\"\nimport { SparkleConfigContext } from \"./sparkle-config-context\"\nimport { SparkleConfig } from \"./utils/config\"\n\nconst ClientGate = ({ children }: { children: ReactNode }) => {\n const [ready, setReady] = useState(false)\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n if (!ready) return null\n return children\n}\n\nexport interface SparkleProviderProps {\n config?: SparkleConfig\n children: ReactNode\n}\n\nconst defaultConfig: SparkleConfig = {\n userId: \"\",\n wsUrl: \"wss://sparkle-bot-v2.fly.dev/ws\",\n}\n\nexport const SparkleProvider = ({ children, config }: SparkleProviderProps) => {\n const mergedConfig = { ...defaultConfig, ...config }\n\n const obsUrl = useMemo(() => {\n if (!mergedConfig.obs?.clientId) return \"\"\n const base =\n mergedConfig.obs.bridgeUrl ?? \"wss://sparkle-bot-v2.fly.dev/obs-bridge\"\n return `${base}?role=client&clientId=${encodeURIComponent(mergedConfig.obs.clientId)}`\n }, [mergedConfig.obs?.clientId, mergedConfig.obs?.bridgeUrl])\n\n const inner = (\n <SparkleConfigContext.Provider value={mergedConfig}>\n <AuthProvider config={mergedConfig}>\n <WebSocketProvider config={mergedConfig}>\n <ClientGate>{children}</ClientGate>\n </WebSocketProvider>\n </AuthProvider>\n </SparkleConfigContext.Provider>\n )\n\n if (obsUrl) {\n return <ObsProvider url={obsUrl}>{inner}</ObsProvider>\n }\n\n return inner\n}\n","import * as React from \"react\"\r\n\r\nimport { type ReactNode, createContext } from \"react\"\r\n\r\nimport { SparkleConfig } from \"../utils/config\"\r\n\r\ninterface AuthContextType {\r\n token: string | undefined | null\r\n}\r\n\r\nexport const SparkleAuthContext = createContext<AuthContextType>({\r\n token: null,\r\n})\r\n\r\ninterface AuthProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nexport const AuthProvider = ({ children, config }: AuthProviderProps) => {\r\n return (\r\n <SparkleAuthContext.Provider\r\n value={{\r\n token: config.userId,\r\n }}\r\n >\r\n {children}\r\n </SparkleAuthContext.Provider>\r\n )\r\n}\r\n","import React, { ReactNode, useCallback, useContext, useRef } from \"react\"\r\n\r\nimport { createContext, useEffect, useState } from \"react\"\r\nimport { SparkleAuthContext } from \"./authProvider\"\r\nimport { UpdateMessage, WebSocketClient } from \"../lib/websocket\"\r\nimport { SparkleConfig } from \"../utils/config\"\r\n\r\ninterface WebSocketContextType {\r\n subscribe: (keys: string[]) => void\r\n listen: (hook: string) => void\r\n values: Record<string, any>\r\n hooks: Record<string, any>\r\n}\r\n\r\nexport const RealtimeContext = createContext<WebSocketContextType>({\r\n subscribe: () => {},\r\n listen: () => {},\r\n values: {},\r\n hooks: {},\r\n})\r\n\r\nexport type RealtimeValue = string | number | boolean | { [key: string]: any }\r\n\r\ntype State = Record<string, RealtimeValue>\r\n\r\ninterface WebSocketProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nexport const WebSocketProvider = ({\r\n children,\r\n config,\r\n}: WebSocketProviderProps) => {\r\n const [values, setValues] = useState<State>({})\r\n const [hooks, setHooks] = useState<Record<string, any>>({})\r\n\r\n const socket = useRef<WebSocketClient>(null)\r\n\r\n const { token } = useContext(SparkleAuthContext)\r\n\r\n useEffect(() => {\r\n socket.current = new WebSocketClient(config.wsUrl)\r\n\r\n socket.current.on(\"update\", (message: UpdateMessage) => {\r\n const { data, type } = message\r\n\r\n if (type === \"hook\") {\r\n const payload = JSON.parse(data.value)\r\n\r\n setHooks((prev) => ({\r\n ...prev,\r\n [payload.hook]: payload.payload,\r\n }))\r\n console.log(\"receive hooks\", payload)\r\n } else {\r\n let value: any = data.value\r\n\r\n console.log(\"value\", value)\r\n\r\n try {\r\n const num = parseFloat(data.value)\r\n if (!isNaN(num) && String(num) === String(data.value).trim()) {\r\n value = num\r\n } else {\r\n try {\r\n value = JSON.parse(data.value)\r\n } catch {\r\n value = data.value\r\n }\r\n }\r\n } catch {\r\n value = data.value\r\n }\r\n\r\n setValues((prev) => ({ ...prev, [data.key]: value }))\r\n console.log(\"receive values\", values)\r\n }\r\n })\r\n\r\n return () => {\r\n socket.current?.disconnect()\r\n }\r\n }, [])\r\n\r\n useEffect(() => {\r\n if (!socket.current || !token) return\r\n\r\n socket.current.authenticate(token)\r\n }, [token])\r\n\r\n const subscribe = useCallback(\r\n (keys: string[]) => {\r\n socket.current?.subscribe(keys)\r\n },\r\n [socket]\r\n )\r\n\r\n const listen = useCallback(\r\n (hook: string) => {\r\n socket.current?.hook(hook)\r\n },\r\n [socket]\r\n )\r\n\r\n return (\r\n <RealtimeContext.Provider\r\n value={{\r\n subscribe,\r\n listen,\r\n values,\r\n hooks,\r\n }}\r\n >\r\n {children}\r\n </RealtimeContext.Provider>\r\n )\r\n}\r\n","import EventEmitter from \"events\"\r\nimport z from \"zod\"\r\n\r\nconst subscribeSchema = z.object({\r\n type: z.literal(\"subscribe\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst hookSchema = z.object({\r\n type: z.literal(\"hook\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst unsubscribeSchema = z.object({\r\n type: z.literal(\"unsubscribe\"),\r\n channels: z.string(),\r\n})\r\n\r\nconst updateSchema = z.object({\r\n event: z.literal(\"update\"),\r\n type: z.enum([\"value\", \"hook\"]),\r\n data: z.object({\r\n key: z.string(),\r\n value: z.string(),\r\n }),\r\n})\r\n\r\nconst messageSchema = z.union([subscribeSchema, unsubscribeSchema, hookSchema])\r\n\r\nexport type Message = z.infer<typeof messageSchema>\r\n\r\nexport type UpdateMessage = z.infer<typeof updateSchema>\r\nexport type SubscribeMessage = z.infer<typeof subscribeSchema>\r\nexport type UnsubscribeMessage = z.infer<typeof unsubscribeSchema>\r\n\r\nexport class WebSocketClient extends EventEmitter {\r\n private socket: WebSocket | null = null\r\n private queue: Message[] = []\r\n private subscriptions: string[] = []\r\n private hooks: string[] = []\r\n private isAuthenticating = false\r\n\r\n private url: string\r\n private jwt: string | null = null\r\n\r\n constructor(url: string) {\r\n super()\r\n this.url = url\r\n }\r\n\r\n private send(data: Message) {\r\n if (this.socket?.readyState === WebSocket.OPEN) {\r\n this.socket?.send(JSON.stringify(data))\r\n } else {\r\n this.queue.push(data)\r\n }\r\n }\r\n\r\n authenticate(jwt: string) {\r\n if (this.isAuthenticating) return\r\n this.isAuthenticating = true\r\n this.jwt = jwt\r\n this.connect()\r\n }\r\n\r\n private connect() {\r\n const authenticatedUrl = this.url + \"?jwt=\" + this.jwt\r\n this.socket = new WebSocket(authenticatedUrl)\r\n\r\n this.socket.onopen = () => {\r\n this.queue.forEach((data) => this.send(data))\r\n this.queue = []\r\n }\r\n\r\n this.socket.onmessage = (event) => {\r\n const data = JSON.parse(event.data)\r\n this.emit(\"update\", data)\r\n }\r\n\r\n this.socket.onclose = () => {\r\n this.reconnect()\r\n }\r\n }\r\n\r\n reconnect() {\r\n setTimeout(() => {\r\n this.queue = [\r\n {\r\n type: \"subscribe\",\r\n channels: this.subscriptions,\r\n },\r\n {\r\n type: \"hook\",\r\n channels: this.hooks,\r\n },\r\n ]\r\n\r\n console.log(\"try reconnecting\")\r\n\r\n this.connect()\r\n }, 5000)\r\n }\r\n\r\n subscribe(channels: string[]) {\r\n const newChannels = channels.filter(\r\n (channel) => !this.subscriptions.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.subscriptions = [...this.subscriptions, ...newChannels]\r\n\r\n console.log(\"subscribe\", channels)\r\n\r\n this.send({\r\n type: \"subscribe\",\r\n channels,\r\n })\r\n }\r\n\r\n hook(hook: string) {\r\n const newChannels = [hook].filter(\r\n (channel) => !this.hooks.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.hooks = [...this.hooks, ...newChannels]\r\n\r\n this.send({\r\n type: \"hook\",\r\n channels: [hook],\r\n })\r\n }\r\n\r\n unsubscribe(channels: string) {\r\n this.send({ type: \"unsubscribe\", channels })\r\n }\r\n\r\n disconnect() {\r\n if (!this.socket) return\r\n\r\n this.socket.onclose = () => {}\r\n this.socket.close()\r\n }\r\n}\r\n","import React, {\r\n createContext,\r\n useCallback,\r\n useEffect,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from \"react\"\r\nimport type OBSWebSocket from \"obs-websocket-js\"\r\nimport type { OBSRequestTypes, OBSResponseTypes } from \"obs-websocket-js\"\r\n\r\ntype ObsStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\"\r\n\r\ninterface ObsContextType {\r\n status: ObsStatus\r\n call: <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ) => Promise<OBSResponseTypes[T]>\r\n}\r\n\r\nexport const ObsContext = createContext<ObsContextType>({\r\n status: \"disconnected\",\r\n call: () => Promise.reject(new Error(\"ObsProvider not mounted\")),\r\n})\r\n\r\nexport interface ObsProviderProps {\r\n url: string\r\n children: ReactNode\r\n}\r\n\r\nexport function ObsProvider({ url, children }: ObsProviderProps) {\r\n const [status, setStatus] = useState<ObsStatus>(\"disconnected\")\r\n const obsRef = useRef<OBSWebSocket | null>(null)\r\n const cancelledRef = useRef(false)\r\n\r\n useEffect(() => {\r\n if (!url) return\r\n\r\n cancelledRef.current = false\r\n let reconnectTimer: ReturnType<typeof setTimeout>\r\n\r\n const setup = async () => {\r\n const { default: OBSWebSocket } = await import(\"obs-websocket-js\")\r\n const obs = new OBSWebSocket()\r\n obsRef.current = obs\r\n\r\n const doConnect = async () => {\r\n if (cancelledRef.current) return\r\n setStatus(\"connecting\")\r\n try {\r\n await obs.connect(url)\r\n if (!cancelledRef.current) setStatus(\"connected\")\r\n } catch {\r\n if (!cancelledRef.current) {\r\n setStatus(\"error\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n }\r\n }\r\n\r\n obs.on(\"ConnectionClosed\" as any, () => {\r\n if (!cancelledRef.current) {\r\n setStatus(\"disconnected\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n })\r\n\r\n obs.on(\"ConnectionError\" as any, () => {\r\n if (!cancelledRef.current) setStatus(\"error\")\r\n })\r\n\r\n await doConnect()\r\n }\r\n\r\n setup()\r\n\r\n return () => {\r\n cancelledRef.current = true\r\n clearTimeout(reconnectTimer!)\r\n obsRef.current?.disconnect()\r\n obsRef.current = null\r\n setStatus(\"disconnected\")\r\n }\r\n }, [url])\r\n\r\n const call = useCallback(\r\n async <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ): Promise<OBSResponseTypes[T]> => {\r\n const obs = obsRef.current\r\n if (!obs) throw new Error(\"OBS not connected\")\r\n return obs.call(requestType, requestData)\r\n },\r\n []\r\n )\r\n\r\n return (\r\n <ObsContext.Provider value={{ status, call }}>\r\n {children}\r\n </ObsContext.Provider>\r\n )\r\n}\r\n","import { createContext, useContext } from \"react\"\r\nimport type { SparkleConfig } from \"./utils/config\"\r\n\r\nexport const SparkleConfigContext = createContext<SparkleConfig | null>(null)\r\n\r\nexport function useSparkleConfig(): SparkleConfig {\r\n const cfg = useContext(SparkleConfigContext)\r\n if (!cfg) {\r\n throw new Error(\"Sparkle hooks require SparkleProvider\")\r\n }\r\n return cfg\r\n}\r\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext, RealtimeValue } from \"../providers/realtimeProvider\"\r\n\r\nexport function useRealtime<T extends RealtimeValue>(\r\n key: string,\r\n defaultValue?: T\r\n): T[] {\r\n const context = useContext(RealtimeContext)\r\n\r\n const keys = useMemo(() => [key], [key])\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useRealtime inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.subscribe([key])\r\n }, [keys])\r\n\r\n return keys.map((k) => (context.values[k] ?? defaultValue) as T)\r\n}\r\n","import { useEffect, useState } from \"react\"\n\nconst WIDGET_SESSION_REQ = \"sparkle-widget-session-request\"\nconst WIDGET_SESSION_RES = \"sparkle-widget-session-result\"\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbedded(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction parseSessionMessage(d: unknown): {\n userId: string | null\n username: string | null\n profilePicture: string | null\n} {\n if (!d || typeof d !== \"object\") {\n return { userId: null, username: null, profilePicture: null }\n }\n const o = d as Record<string, unknown>\n const userId =\n typeof o.userId === \"string\" && o.userId.trim() ? o.userId : null\n const username =\n typeof o.username === \"string\" && o.username.trim() ? o.username : null\n const profilePicture =\n typeof o.profilePicture === \"string\" && o.profilePicture.trim()\n ? o.profilePicture\n : null\n return { userId, username, profilePicture }\n}\n\nfunction requestSessionViaParent(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n if (typeof window === \"undefined\") {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n return new Promise((resolve) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== WIDGET_SESSION_RES || d.requestId !== requestId)\n return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n resolve(parseSessionMessage(d))\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n resolve({ userId: null, username: null, profilePicture: null })\n }, 12_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: WIDGET_SESSION_REQ, requestId }, \"*\")\n })\n}\n\nasync function fetchSessionPayload(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n const res = await fetch(\"/api/sparkle-widget-session\", {\n method: \"GET\",\n credentials: \"include\",\n })\n if (!res.ok) {\n return { userId: null, username: null, profilePicture: null }\n }\n const data = await res.json().catch(() => null)\n return parseSessionMessage(data)\n}\n\nexport type UseAuthResult = {\n /** Identifiant Twitch (`Account.accountId`), pas l’id interne Sparkle. */\n userId: string | null | undefined\n /** `User.name` Prisma (nom affiché OAuth). */\n username: string | null | undefined\n /** `User.image` Prisma (URL avatar). */\n profilePicture: string | null | undefined\n}\n\n/**\n * Profil session navigateur : `userId` = id Twitch (`Account.accountId` OAuth) ;\n * `username` / `profilePicture` depuis `User`. Champs à `undefined` pendant le chargement.\n */\nexport function useAuth(): UseAuthResult {\n const [userId, setUserId] = useState<string | null | undefined>(undefined)\n const [username, setUsername] = useState<string | null | undefined>(undefined)\n const [profilePicture, setProfilePicture] = useState<\n string | null | undefined\n >(undefined)\n\n useEffect(() => {\n let cancelled = false\n const run = async () => {\n const p = isEmbedded()\n ? await requestSessionViaParent()\n : await fetchSessionPayload()\n if (!cancelled) {\n setUserId(p.userId)\n setUsername(p.username)\n setProfilePicture(p.profilePicture)\n }\n }\n void run()\n return () => {\n cancelled = true\n }\n }, [])\n\n return { userId, username, profilePicture }\n}\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext } from \"../providers/realtimeProvider\"\r\nimport { EventTypeMap } from \"./hook\"\r\n\r\nexport const SparkleHookKeys = [\r\n \"notification.follow\",\r\n \"notification.cheer\",\r\n \"notification.tips\",\r\n \"notification.subscribe\",\r\n \"notification.subscribe.end\",\r\n \"notification.subscribe.gift\",\r\n \"notification.raid\",\r\n\r\n \"chat.message\",\r\n\r\n \"stream.online\",\r\n \"stream.offline\",\r\n \"stream.update\",\r\n\r\n \"reward.create\",\r\n \"reward.update\",\r\n \"reward.remove\",\r\n\r\n \"reward.redemption.claim\",\r\n \"reward.redemption.update\",\r\n\r\n \"poll.begin\",\r\n \"poll.progress\",\r\n \"poll.end\",\r\n\r\n \"prediction.begin\",\r\n \"prediction.progress\",\r\n \"prediction.lock\",\r\n \"prediction.end\",\r\n\r\n \"moderator.add\",\r\n \"moderator.remove\",\r\n \"moderator.ban\",\r\n \"moderator.unban\",\r\n \"moderator.shield_mode.begin\",\r\n] as const\r\n\r\nexport type SparkleHookKey = (typeof SparkleHookKeys)[number]\r\n\r\nexport function useHook<T extends keyof EventTypeMap>(\r\n hook: T\r\n): EventTypeMap[T] | undefined {\r\n const context = useContext(RealtimeContext)\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useHook inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.listen(hook)\r\n }, [hook])\r\n\r\n return context.hooks[hook] as EventTypeMap[T]\r\n}\r\n","import { useCallback, useMemo } from \"react\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\n\r\nconst BRIDGE_TYPE = \"sparkle-invoke-task\"\r\nconst BRIDGE_RESULT = \"sparkle-invoke-task-result\"\r\n\r\ntype InvokeBridgeBody =\r\n | { kind: \"invoke\"; taskId: string; payload?: unknown }\r\n | {\r\n kind: \"invokeUntil\"\r\n taskId: string\r\n payload?: unknown\r\n delaySeconds: number\r\n }\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction bridgeInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useTask requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useTask: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(Boolean(d.result))\r\n else reject(new Error(typeof d.error === \"string\" ? d.error : \"invoke_failed\"))\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useTask: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage({ type: BRIDGE_TYPE, requestId, projectId, ...body }, \"*\")\r\n })\r\n}\r\n\r\n/**\r\n * Dès que le widget tourne dans une iframe, un `fetch` vers `/api/...` n’emporte en général\r\n * pas la session (partitionnement / contexte imbriqué). Le relais `postMessage` → fenêtre\r\n * parente (même onglet, même origine) envoie les cookies comme une navigation classique.\r\n */\r\nfunction shouldUsePostMessageBridge(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nasync function fetchInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n const res = await fetch(`/api/projects/${projectId}/invoke-task`, {\r\n method: \"POST\",\r\n credentials: \"include\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n })\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Exécute une tâche Sparkle dans la runtime (équivalent à `ctx.invoke` / `ctx.invokeUntil`).\r\n * En preview Sandpack, les appels passent par `postMessage` vers l’éditeur (session).\r\n */\r\nexport function useTask() {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId\r\n\r\n const invoke = useCallback(\r\n async (taskId: string, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = { kind: \"invoke\", taskId, payload }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n const invokeUntil = useCallback(\r\n async (taskId: string, delaySeconds: number, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = {\r\n kind: \"invokeUntil\",\r\n taskId,\r\n payload,\r\n delaySeconds,\r\n }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n return useMemo(() => ({ invoke, invokeUntil }), [invoke, invokeUntil])\r\n}\r\n","import { useCallback, useMemo } from \"react\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\nimport { createTwitchGetClient } from \"../twitch/create-twitch-get-client\"\r\nimport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\r\n\r\nexport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\r\n\r\nconst BRIDGE_TYPE = \"sparkle-twitch-get\"\r\nconst BRIDGE_RESULT = \"sparkle-twitch-get-result\"\r\n\r\nexport type TwitchGetApiBody = {\r\n path: string[]\r\n args?: unknown[]\r\n}\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction shouldUsePostMessageBridge(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nfunction bridgeTwitchGet(\r\n projectId: string,\r\n body: TwitchGetApiBody\r\n): Promise<unknown> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useTwitch requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useTwitch: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(d.result)\r\n else\r\n reject(\r\n new Error(typeof d.error === \"string\" ? d.error : \"twitch_get_failed\")\r\n )\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useTwitch: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage(\r\n { type: BRIDGE_TYPE, requestId, projectId, ...body },\r\n \"*\"\r\n )\r\n })\r\n}\r\n\r\nasync function fetchTwitchGet(\r\n projectId: string,\r\n body: TwitchGetApiBody\r\n): Promise<unknown> {\r\n const res = await fetch(\r\n `/api/projects/${encodeURIComponent(projectId)}/twitch-get`,\r\n {\r\n method: \"POST\",\r\n credentials: \"include\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n }\r\n )\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n const json = (await res.json()) as { result?: unknown }\r\n return json.result\r\n}\r\n\r\n/**\r\n * Client Twitch en lecture seule (Twurple via le backend du projet).\r\n * Même surface GET que `ctx.twitch` dans les handlers runtime — pas les mutations.\r\n *\r\n * Requiert `config.projectId` sur `SparkleProvider`.\r\n */\r\nexport function useTwitch(): TwitchGetClient {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId\r\n\r\n const invoke = useCallback(\r\n async (namespace: string, method: string, args: unknown[]) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\r\n \"useTwitch: renseignez `config.projectId` sur SparkleProvider.\"\r\n )\r\n }\r\n const body: TwitchGetApiBody = {\r\n path: [namespace, method],\r\n args,\r\n }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeTwitchGet(projectId, body)\r\n }\r\n return fetchTwitchGet(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n return useMemo(() => createTwitchGetClient(invoke), [invoke])\r\n}\r\n","import type { TwitchGetClient } from \"./twitch-get-types\"\n\nexport type TwitchGetInvoker = (\n namespace: string,\n method: string,\n args: unknown[]\n) => Promise<unknown>\n\nexport function createTwitchGetClient(\n invoke: TwitchGetInvoker\n): TwitchGetClient {\n const call =\n (ns: string, method: string) =>\n (...args: unknown[]) =>\n invoke(ns, method, args)\n\n return {\n bits: {\n getLeaderboard: call(\"bits\", \"getLeaderboard\"),\n },\n channelPoints: {\n getCustomRewards: call(\"channelPoints\", \"getCustomRewards\"),\n getCustomRewardsByIds: call(\"channelPoints\", \"getCustomRewardsByIds\"),\n getCustomRewardById: call(\"channelPoints\", \"getCustomRewardById\"),\n getRedemptionsByIds: call(\"channelPoints\", \"getRedemptionsByIds\"),\n getRedemptionById: call(\"channelPoints\", \"getRedemptionById\"),\n getRedemptions: call(\"channelPoints\", \"getRedemptions\"),\n },\n channel: {\n getChannelInfoById: call(\"channel\", \"getChannelInfoById\"),\n getFollowDate: call(\"channel\", \"getFollowDate\"),\n getChannelEditors: call(\"channel\", \"getChannelEditors\"),\n getVips: call(\"channel\", \"getVips\"),\n checkVipForUsers: call(\"channel\", \"checkVipForUsers\"),\n checkVipForUser: call(\"channel\", \"checkVipForUser\"),\n getChannelFollowerCount: call(\"channel\", \"getChannelFollowerCount\"),\n },\n chat: {\n getChatters: call(\"chat\", \"getChatters\"),\n getSettings: call(\"chat\", \"getSettings\"),\n getSettingsPrivileged: call(\"chat\", \"getSettingsPrivileged\"),\n getColorsForUsers: call(\"chat\", \"getColorsForUsers\"),\n getColorForUser: call(\"chat\", \"getColorForUser\"),\n },\n clips: {\n getClips: call(\"clips\", \"getClips\"),\n getClipById: call(\"clips\", \"getClipById\"),\n getClipsByIds: call(\"clips\", \"getClipsByIds\"),\n },\n goals: {\n getGoals: call(\"goals\", \"getGoals\"),\n },\n polls: {\n getPolls: call(\"polls\", \"getPolls\"),\n getPollsByIds: call(\"polls\", \"getPollsByIds\"),\n getPollById: call(\"polls\", \"getPollById\"),\n },\n predictions: {\n getPredictions: call(\"predictions\", \"getPredictions\"),\n getPredictionsByIds: call(\"predictions\", \"getPredictionsByIds\"),\n getPredictionById: call(\"predictions\", \"getPredictionById\"),\n },\n streams: {\n getStream: call(\"streams\", \"getStream\"),\n getStreamMarkers: call(\"streams\", \"getStreamMarkers\"),\n },\n subscriptions: {\n getSubscriptions: call(\"subscriptions\", \"getSubscriptions\"),\n getSubscriptionsForUsers: call(\n \"subscriptions\",\n \"getSubscriptionsForUsers\"\n ),\n getSubscriptionForUser: call(\"subscriptions\", \"getSubscriptionForUser\"),\n checkUserSubscription: call(\"subscriptions\", \"checkUserSubscription\"),\n },\n moderation: {\n getBanned: call(\"moderation\", \"getBanned\"),\n checkBan: call(\"moderation\", \"checkBan\"),\n getModerators: call(\"moderation\", \"getModerators\"),\n checkModerator: call(\"moderation\", \"checkModerator\"),\n getBlockedTerms: call(\"moderation\", \"getBlockedTerms\"),\n },\n utils: {\n getUserById: call(\"utils\", \"getUserById\"),\n getUserByName: call(\"utils\", \"getUserByName\"),\n getUserByIds: call(\"utils\", \"getUserByIds\"),\n getUserByNames: call(\"utils\", \"getUserByNames\"),\n },\n } as unknown as TwitchGetClient\n}\n"],"mappings":"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","names":["React","useMemo","useEffect","useState","React","createContext","SparkleAuthContext","AuthProvider","children","config","React","useCallback","useContext","useRef","createContext","useEffect","useState","EventEmitter","z","subscribeSchema","hookSchema","unsubscribeSchema","updateSchema","messageSchema","WebSocketClient","url","data","jwt","authenticatedUrl","event","channels","newChannels","channel","hook","RealtimeContext","createContext","WebSocketProvider","children","config","values","setValues","useState","hooks","setHooks","socket","useRef","token","useContext","SparkleAuthContext","useEffect","WebSocketClient","message","data","type","payload","prev","value","num","subscribe","useCallback","keys","listen","hook","React","React","createContext","useCallback","useEffect","useRef","useState","ObsContext","ObsProvider","url","children","status","setStatus","obsRef","cancelledRef","reconnectTimer","OBSWebSocket","obs","doConnect","call","requestType","requestData","createContext","useContext","SparkleConfigContext","useSparkleConfig","cfg","ClientGate","children","ready","setReady","useState","useEffect","defaultConfig","SparkleProvider","config","mergedConfig","obsUrl","useMemo","inner","React","SparkleConfigContext","AuthProvider","WebSocketProvider","ObsProvider","useContext","useEffect","useMemo","useRealtime","key","defaultValue","context","useContext","RealtimeContext","keys","useMemo","useEffect","k","useEffect","useState","WIDGET_SESSION_REQ","WIDGET_SESSION_RES","newRequestId","isEmbedded","parseSessionMessage","d","o","userId","username","profilePicture","requestSessionViaParent","target","resolve","requestId","onMsg","ev","timer","fetchSessionPayload","res","data","useAuth","setUserId","setUsername","setProfilePicture","cancelled","p","useContext","useEffect","SparkleHookKeys","useHook","hook","context","useContext","RealtimeContext","useEffect","useCallback","useMemo","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","bridgeInvoke","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","shouldUsePostMessageBridge","fetchInvoke","res","err","useTask","useSparkleConfig","invoke","useCallback","taskId","payload","invokeUntil","delaySeconds","useMemo","useCallback","useMemo","createTwitchGetClient","invoke","call","ns","method","args","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","shouldUsePostMessageBridge","bridgeTwitchGet","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchTwitchGet","res","err","useTwitch","useSparkleConfig","invoke","useCallback","namespace","method","args","useMemo","createTwitchGetClient"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkle-react",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -23,7 +23,7 @@
23
23
  "jsonwebtoken": "^9.0.2",
24
24
  "lucide-react": "^0.294.0",
25
25
  "obs-websocket-js": "^5.0.8",
26
- "react": "^18.2.0",
26
+ "react": "^19.2.6",
27
27
  "zod": "^3.22.4"
28
28
  },
29
29
  "scripts": {