@probat/react 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Experiment.tsx +1 -1
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context/ProbatContext.tsx","../src/components/ProbatProviderClient.tsx","../src/utils/environment.ts","../src/utils/eventContext.ts","../src/utils/api.ts","../src/utils/dedupeStorage.ts","../src/utils/stableInstanceId.ts","../src/components/Experiment.tsx","../src/hooks/useProbatMetrics.ts"],"names":["createContext","useMemo","React","useContext","useRef","useState","useEffect","useCallback"],"mappings":";;;;;;;;AAUA,IAAM,aAAA,GAAgBA,qBAAyC,IAAI,CAAA;AAEnE,IAAM,YAAA,GAAe,4BAAA;AAgBd,SAAS,cAAA,CAAe;AAAA,EAC3B,MAAA;AAAA,EACA,IAAA,GAAO,YAAA;AAAA,EACP,SAAA;AAAA,EACA;AACJ,CAAA,EAAwB;AACpB,EAAA,MAAM,KAAA,GAAQC,cAAA;AAAA,IACV,OAAO;AAAA,MACH,IAAA,EAAM,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAAA,MAC5B,MAAA;AAAA,MACA,SAAA,EAAW,aAAa;AAAC,KAC7B,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,IAAA,EAAM,SAAS;AAAA,GAC5B;AAEA,EAAA,uBACIC,uBAAA,CAAA,aAAA,CAAC,aAAA,CAAc,QAAA,EAAd,EAAuB,SACnB,QACL,CAAA;AAER;AAEO,SAAS,gBAAA,GAAuC;AACnD,EAAA,MAAM,GAAA,GAAMC,kBAAW,aAAa,CAAA;AACpC,EAAA,IAAI,CAAC,GAAA,EAAK;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACN;AAAA,KACJ;AAAA,EACJ;AACA,EAAA,OAAO,GAAA;AACX;;;ACjCO,SAAS,qBAAqB,KAAA,EAA4B;AAC7D,EAAA,OAAOD,uBAAAA,CAAM,aAAA,CAAc,cAAA,EAAgB,KAAK,CAAA;AACpD;;;ACvBO,SAAS,iBAAA,GAAoC;AAChD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,QAAA,GAAW,OAAO,QAAA,CAAS,QAAA;AAGjC,EAAA,IACI,aAAa,WAAA,IACb,QAAA,KAAa,WAAA,IACb,QAAA,KAAa,aACb,QAAA,CAAS,UAAA,CAAW,UAAU,CAAA,IAC9B,SAAS,UAAA,CAAW,KAAK,CAAA,IACzB,QAAA,CAAS,WAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,SAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,WAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,SAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,WAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,SAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,EAC/B;AACE,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,OAAO,MAAA;AACX;;;AClCA,IAAM,eAAA,GAAkB,oBAAA;AACxB,IAAM,cAAA,GAAiB,mBAAA;AAEvB,IAAI,gBAAA,GAAkC,IAAA;AACtC,IAAI,eAAA,GAAiC,IAAA;AAErC,SAAS,UAAA,GAAqB;AAE1B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,UAAA,EAAY;AACpD,IAAA,OAAO,OAAO,UAAA,EAAW;AAAA,EAC7B;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,EAAE,CAAA;AAC/B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,eAAA,EAAiB;AACzD,IAAA,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAAA,EAChC,CAAA,MAAO;AACH,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,EAC1E;AACA,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AAC5E;AAEO,SAAS,aAAA,GAAwB;AACpC,EAAA,IAAI,kBAAkB,OAAO,gBAAA;AAC7B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,QAAA;AAC1C,EAAA,IAAI;AACA,IAAA,MAAM,MAAA,GAAS,YAAA,CAAa,OAAA,CAAQ,eAAe,CAAA;AACnD,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,gBAAA,GAAmB,MAAA;AACnB,MAAA,OAAO,MAAA;AAAA,IACX;AAAA,EACJ,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,UAAA,EAAY,CAAA,CAAA;AAC/B,EAAA,gBAAA,GAAmB,EAAA;AACnB,EAAA,IAAI;AACA,IAAA,YAAA,CAAa,OAAA,CAAQ,iBAAiB,EAAE,CAAA;AAAA,EAC5C,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,OAAO,EAAA;AACX;AAEO,SAAS,YAAA,GAAuB;AACnC,EAAA,IAAI,iBAAiB,OAAO,eAAA;AAC5B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,QAAA;AAC1C,EAAA,IAAI;AACA,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,OAAA,CAAQ,cAAc,CAAA;AACpD,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,eAAA,GAAkB,MAAA;AAClB,MAAA,OAAO,MAAA;AAAA,IACX;AAAA,EACJ,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,UAAA,EAAY,CAAA,CAAA;AAC/B,EAAA,eAAA,GAAkB,EAAA;AAClB,EAAA,IAAI;AACA,IAAA,cAAA,CAAe,OAAA,CAAQ,gBAAgB,EAAE,CAAA;AAAA,EAC7C,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,OAAO,EAAA;AACX;AAEO,SAAS,UAAA,GAAqB;AACjC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAA;AAC1C,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,QAAA,GAAW,MAAA,CAAO,QAAA,CAAS,MAAA;AACtD;AAEO,SAAS,UAAA,GAAqB;AACjC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAA;AAC1C,EAAA,OAAO,OAAO,QAAA,CAAS,IAAA;AAC3B;AAEO,SAAS,WAAA,GAAsB;AAClC,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,EAAA;AAC5C,EAAA,OAAO,QAAA,CAAS,QAAA;AACpB;AAUO,SAAS,iBAAA,GAAkC;AAC9C,EAAA,OAAO;AAAA,IACH,aAAa,aAAA,EAAc;AAAA,IAC3B,YAAY,YAAA,EAAa;AAAA,IACzB,WAAW,UAAA,EAAW;AAAA,IACtB,WAAW,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,QAAA,GAAW,EAAA;AAAA,IACtE,WAAW,WAAA;AAAY,GAC3B;AACJ;;;AC7EA,IAAM,gBAAA,uBAAuB,GAAA,EAA6B;AAO1D,eAAsB,aAAA,CAClB,IAAA,EACA,YAAA,EACA,UAAA,EACe;AACf,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,GAAA,CAAI,YAAY,CAAA;AAClD,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,MAAM,WAAW,YAAY;AACzB,IAAA,IAAI;AACA,MAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,kBAAA,CAAA;AACtC,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QACzB,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACL,cAAA,EAAgB,kBAAA;AAAA,UAChB,MAAA,EAAQ;AAAA,SACZ;AAAA,QACA,WAAA,EAAa,SAAA;AAAA,QACb,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,UACjB,aAAA,EAAe,YAAA;AAAA,UACf,WAAA,EAAa;AAAA,SAChB;AAAA,OACJ,CAAA;AACD,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AACjD,MAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,MAAA,OAAO,KAAK,WAAA,IAAe,SAAA;AAAA,IAC/B,CAAA,SAAE;AACE,MAAA,gBAAA,CAAiB,OAAO,YAAY,CAAA;AAAA,IACxC;AAAA,EACJ,CAAA,GAAG;AAEH,EAAA,gBAAA,CAAiB,GAAA,CAAI,cAAc,OAAO,CAAA;AAC1C,EAAA,OAAO,OAAA;AACX;AAOO,SAAS,UAAA,CACZ,IAAA,EACA,KAAA,EACA,UAAA,EACI;AACJ,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEnC,EAAA,MAAM,MAAM,iBAAA,EAAkB;AAC9B,EAAA,MAAM,OAAA,GAAyB;AAAA,IAC3B,KAAA;AAAA,IACA,UAAA,EAAY;AAAA,MACR,GAAG,GAAA;AAAA,MACH,aAAa,iBAAA,EAAkB;AAAA,MAC/B,MAAA,EAAQ,WAAA;AAAA,MACR,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,GAAG;AAAA;AACP,GACJ;AAEA,EAAA,IAAI;AACA,IAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,mBAAA,CAAA;AACtC,IAAA,KAAA,CAAM,GAAA,EAAK;AAAA,MACP,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,WAAA,EAAa,SAAA;AAAA,MACb,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,KAC/B,CAAA,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AAAA,EAER;AACJ;AAgBO,SAAS,iBAAiB,MAAA,EAA8C;AAC3E,EAAA,IAAI,CAAC,MAAA,IAAU,EAAE,MAAA,YAAkB,cAAc,OAAO,IAAA;AAGxD,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,+BAA+B,CAAA;AAC9D,EAAA,IAAI,OAAA,EAAS,OAAO,SAAA,CAAU,OAAA,EAAwB,IAAI,CAAA;AAG1D,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,OAAA,CAAQ,4BAA4B,CAAA;AAC/D,EAAA,IAAI,WAAA,EAAa,OAAO,SAAA,CAAU,WAAA,EAA4B,KAAK,CAAA;AAEnE,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,SAAA,CAAU,IAAiB,SAAA,EAA+B;AAC/D,EAAA,MAAM,IAAA,GAAkB;AAAA,IACpB,kBAAkB,EAAA,CAAG,OAAA;AAAA,IACrB,gBAAA,EAAkB;AAAA,GACtB;AACA,EAAA,IAAI,EAAA,CAAG,EAAA,EAAI,IAAA,CAAK,eAAA,GAAkB,EAAA,CAAG,EAAA;AACrC,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,WAAA,EAAa,IAAA,EAAK;AAClC,EAAA,IAAI,MAAM,IAAA,CAAK,iBAAA,GAAoB,IAAA,CAAK,KAAA,CAAM,GAAG,GAAG,CAAA;AACpD,EAAA,OAAO,IAAA;AACX;;;AC9HA,IAAM,MAAA,GAAS,cAAA;AACf,IAAM,SAAA,uBAAgB,GAAA,EAAY;AAE3B,SAAS,aAAA,CACZ,YAAA,EACA,UAAA,EACA,UAAA,EACA,OAAA,EACM;AACN,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,YAAY,IAAI,UAAU,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AAC1E;AAEO,SAAS,QAAQ,GAAA,EAAsB;AAC1C,EAAA,IAAI,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA,EAAG,OAAO,IAAA;AAC/B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,EAAA,IAAI;AACA,IAAA,OAAO,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAA,KAAM,GAAA;AAAA,EAC3C,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,KAAA;AAAA,EACX;AACJ;AAEO,SAAS,SAAS,GAAA,EAAmB;AACxC,EAAA,SAAA,CAAU,IAAI,GAAG,CAAA;AACjB,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,EAAA,IAAI;AACA,IAAA,cAAA,CAAe,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,EACnC,CAAA,CAAA,MAAQ;AAAA,EAAC;AACb;ACjBA,IAAM,eAAA,GAAkB,kBAAA;AAIxB,SAAS,OAAA,GAAkB;AACvB,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,CAAC,CAAA;AAC9B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,eAAA,EAAiB;AACzD,IAAA,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAAA,EAChC,CAAA,MAAO;AACH,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,EACzE;AACA,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AAC5E;AAKA,SAAS,gBAAgB,UAAA,EAA4B;AACjD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,IAAI;AACA,MAAA,MAAM,MAAA,GAAS,cAAA,CAAe,OAAA,CAAQ,UAAU,CAAA;AAChD,MAAA,IAAI,QAAQ,OAAO,MAAA;AAAA,IACvB,CAAA,CAAA,MAAQ;AAAA,IAAC;AAAA,EACb;AACA,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,OAAA,EAAS,CAAA,CAAA;AAC5B,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,IAAI;AACA,MAAA,cAAA,CAAe,OAAA,CAAQ,YAAY,EAAE,CAAA;AAAA,IACzC,CAAA,CAAA,MAAQ;AAAA,IAAC;AAAA,EACb;AACA,EAAA,OAAO,EAAA;AACX;AAOA,IAAM,YAAA,uBAAmB,GAAA,EAAoB;AAC7C,IAAI,cAAA,GAAiB,KAAA;AAErB,SAAS,UAAU,QAAA,EAA0B;AACzC,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA,IAAK,CAAA;AAC1C,EAAA,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,GAAA,GAAM,CAAC,CAAA;AAClC,EAAA,IAAI,CAAC,cAAA,EAAgB;AACjB,IAAA,cAAA,GAAiB,IAAA;AACjB,IAAA,OAAA,CAAQ,OAAA,EAAQ,CAAE,IAAA,CAAK,MAAM;AACzB,MAAA,YAAA,CAAa,KAAA,EAAM;AACnB,MAAA,cAAA,GAAiB,KAAA;AAAA,IACrB,CAAC,CAAA;AAAA,EACL;AACA,EAAA,OAAO,GAAA;AACX;AAIA,SAAS,uBAAuB,YAAA,EAA8B;AAC1D,EAAA,MAAM,OAAA,GAAWA,wBAAc,KAAA,EAAM;AACrC,EAAA,MAAM,GAAA,GAAME,cAAO,EAAE,CAAA;AACrB,EAAA,IAAI,CAAC,IAAI,OAAA,EAAS;AACd,IAAA,MAAM,GAAA,GAAM,GAAG,eAAe,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,UAAA,EAAY,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AACxE,IAAA,GAAA,CAAI,OAAA,GAAU,gBAAgB,GAAG,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,GAAA,CAAI,OAAA;AACf;AAIA,SAAS,4BAA4B,YAAA,EAA8B;AAC/D,EAAA,MAAM,OAAA,GAAUA,cAAO,EAAE,CAAA;AACzB,EAAA,MAAM,GAAA,GAAMA,cAAO,EAAE,CAAA;AACrB,EAAA,IAAI,OAAA,CAAQ,YAAY,EAAA,EAAI;AACxB,IAAA,OAAA,CAAQ,UAAU,SAAA,CAAU,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,UAAA,EAAY,CAAA,CAAE,CAAA;AAAA,EACjE;AACA,EAAA,IAAI,CAAC,IAAI,OAAA,EAAS;AACd,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,eAAe,CAAA,EAAG,YAAY,IAAI,UAAA,EAAY,CAAA,CAAA,EAAI,OAAA,CAAQ,OAAO,CAAA,CAAA;AAChF,IAAA,GAAA,CAAI,OAAA,GAAU,gBAAgB,GAAG,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,GAAA,CAAI,OAAA;AACf;AAMO,IAAM,mBAAA,GACT,OAAQF,uBAAAA,CAAc,KAAA,KAAU,aAC1B,sBAAA,GACA,2BAAA;;;ACxFV,IAAM,iBAAA,GAAoB,oBAAA;AAO1B,SAAS,eAAe,EAAA,EAA2B;AAC/C,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,IAAA;AAC1C,EAAA,IAAI;AACA,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,CAAQ,iBAAA,GAAoB,EAAE,CAAA;AACvD,IAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,IAAA,MAAM,MAAA,GAA2B,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC/C,IAAA,OAAO,OAAO,UAAA,IAAc,IAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;AAEA,SAAS,eAAA,CAAgB,IAAY,UAAA,EAA0B;AAC3D,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,EAAA,IAAI;AACA,IAAA,MAAM,QAA0B,EAAE,UAAA,EAAY,EAAA,EAAI,IAAA,CAAK,KAAI,EAAE;AAC7D,IAAA,YAAA,CAAa,QAAQ,iBAAA,GAAoB,EAAA,EAAI,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,EACtE,CAAA,CAAA,MAAQ;AAAA,EAAC;AACb;AAgCO,SAAS,UAAA,CAAW;AAAA,EACvB,EAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,mBAAA;AAAA,EACA,QAAA,GAAW,SAAA;AAAA,EACX,KAAA,GAAQ;AACZ,CAAA,EAAoB;AAChB,EAAA,MAAM,EAAE,IAAA,EAAM,SAAA,EAAU,GAAI,gBAAA,EAAiB;AAG7C,EAAA,MAAM,cAAA,GAAiB,oBAAoB,EAAE,CAAA;AAC7C,EAAA,MAAM,aAAa,mBAAA,IAAuB,cAAA;AAG1C,EAAA,MAAM,eAAA,GAAkB,OAAO,UAAA,KAAe,KAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,OAAO,YAAA,KAAiB,KAAA;AAC3C,EAAA,MAAM,eAAA,GAAkB,OAAO,mBAAA,IAAuB,sBAAA;AACtD,EAAA,MAAM,UAAA,GAAa,OAAO,cAAA,IAAkB,mBAAA;AAI5C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIG,gBAAiB,MAAM;AAEvD,IAAA,IAAI,SAAA,CAAU,EAAE,CAAA,EAAG,OAAO,UAAU,EAAE,CAAA;AACtC,IAAA,MAAM,MAAA,GAAS,eAAe,EAAE,CAAA;AAChC,IAAA,IAAI,QAAQ,OAAO,MAAA;AACnB,IAAA,OAAO,SAAA;AAAA,EACX,CAAC,CAAA;AACD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,gBAAkB,MAAM;AACpD,IAAA,OAAO,CAAC,EAAE,SAAA,CAAU,EAAE,CAAA,IAAK,eAAe,EAAE,CAAA,CAAA;AAAA,EAChD,CAAC,CAAA;AAED,EAAAC,gBAAA,CAAU,MAAM;AAEZ,IAAA,IAAI,SAAA,CAAU,EAAE,CAAA,IAAK,cAAA,CAAe,EAAE,CAAA,EAAG;AAErC,MAAA,MAAM,MAAM,SAAA,CAAU,EAAE,CAAA,IAAK,cAAA,CAAe,EAAE,CAAA,IAAK,SAAA;AACnD,MAAA,aAAA,CAAc,GAAG,CAAA;AACjB,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA;AAAA,IACJ;AAEA,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,CAAC,YAAY;AACT,MAAA,IAAI;AACA,QAAA,MAAM,aAAa,aAAA,EAAc;AACjC,QAAA,MAAM,GAAA,GAAM,MAAM,aAAA,CAAc,IAAA,EAAM,IAAI,UAAU,CAAA;AACpD,QAAA,IAAI,SAAA,EAAW;AAGf,QAAA,IAAI,GAAA,KAAQ,SAAA,IAAa,EAAE,GAAA,IAAO,QAAA,CAAA,EAAW;AACzC,UAAA,IAAI,KAAA,EAAO;AACP,YAAA,OAAA,CAAQ,IAAA;AAAA,cACJ,CAAA,0BAAA,EAA6B,GAAG,CAAA,kBAAA,EAAqB,EAAE,CAAA,0BAAA;AAAA,aAC3D;AAAA,UACJ;AACA,UAAA,aAAA,CAAc,SAAS,CAAA;AAAA,QAC3B,CAAA,MAAO;AACH,UAAA,aAAA,CAAc,GAAG,CAAA;AACjB,UAAA,eAAA,CAAgB,IAAI,GAAG,CAAA;AAAA,QAC3B;AAAA,MACJ,SAAS,GAAA,EAAK;AACV,QAAA,IAAI,SAAA,EAAW;AACf,QAAA,IAAI,KAAA,EAAO;AACP,UAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,mCAAA,EAAsC,EAAE,CAAA,EAAA,CAAA,EAAM,GAAG,CAAA;AAAA,QACnE;AACA,QAAA,IAAI,QAAA,KAAa,WAAW,MAAM,GAAA;AAClC,QAAA,aAAA,CAAc,SAAS,CAAA;AAAA,MAC3B,CAAA,SAAE;AACE,QAAA,IAAI,CAAC,SAAA,EAAW,WAAA,CAAY,IAAI,CAAA;AAAA,MACpC;AAAA,IACJ,CAAA,GAAG;AAEH,IAAA,OAAO,MAAM;AACT,MAAA,SAAA,GAAY,IAAA;AAAA,IAChB,CAAA;AAAA,EACJ,CAAA,EAAG,CAAC,EAAA,EAAI,IAAI,CAAC,CAAA;AAIb,EAAAA,gBAAA,CAAU,MAAM;AACZ,IAAA,IAAI,SAAS,QAAA,EAAU;AACnB,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,qBAAA,EAAwB,EAAE,CAAA,kBAAA,EAAgB,UAAU,CAAA,CAAA,CAAA,EAAK;AAAA,QACjE,UAAA;AAAA,QACA,SAAS,UAAA;AAAW,OACvB,CAAA;AAAA,IACL;AAAA,EACJ,GAAG,CAAC,KAAA,EAAO,IAAI,UAAA,EAAY,QAAA,EAAU,UAAU,CAAC,CAAA;AAIhD,EAAA,MAAM,UAAA,GAAaL,cAAAA;AAAA,IACf,OAAO;AAAA,MACH,aAAA,EAAe,EAAA;AAAA,MACf,WAAA,EAAa,UAAA;AAAA,MACb,qBAAA,EAAuB;AAAA,KAC3B,CAAA;AAAA,IACA,CAAC,EAAA,EAAI,UAAA,EAAY,UAAU;AAAA,GAC/B;AAIA,EAAA,MAAM,YAAA,GAAeG,cAAuB,IAAI,CAAA;AAChD,EAAA,MAAM,cAAA,GAAiBA,cAAO,KAAK,CAAA;AAEnC,EAAAE,gBAAA,CAAU,MAAM;AACZ,IAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,QAAA,EAAU;AAGnC,IAAA,cAAA,CAAe,OAAA,GAAU,KAAA;AAEzB,IAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,IAAA,MAAM,SAAA,GAAY,aAAA,CAAc,EAAA,EAAI,UAAA,EAAY,YAAY,OAAO,CAAA;AAGnE,IAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACpB,MAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,KAAK,YAAA,CAAa,OAAA;AACxB,IAAA,IAAI,CAAC,EAAA,EAAI;AAGT,IAAA,IAAI,OAAO,yBAAyB,WAAA,EAAa;AAC7C,MAAA,IAAI,CAAC,eAAe,OAAA,EAAS;AACzB,QAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,QAAA,QAAA,CAAS,SAAS,CAAA;AAClB,QAAA,UAAA,CAAW,IAAA,EAAM,iBAAiB,UAAU,CAAA;AAC5C,QAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,sCAAA,EAAyC,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA,MACzE;AACA,MAAA;AAAA,IACJ;AAEA,IAAA,IAAI,KAAA,GAA8C,IAAA;AAElD,IAAA,MAAM,WAAW,IAAI,oBAAA;AAAA,MACjB,CAAC,CAAC,KAAK,CAAA,KAAM;AACT,QAAA,IAAI,CAAC,KAAA,IAAS,cAAA,CAAe,OAAA,EAAS;AAEtC,QAAA,IAAI,MAAM,cAAA,EAAgB;AACtB,UAAA,KAAA,GAAQ,WAAW,MAAM;AACrB,YAAA,IAAI,eAAe,OAAA,EAAS;AAC5B,YAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,YAAA,QAAA,CAAS,SAAS,CAAA;AAClB,YAAA,UAAA,CAAW,IAAA,EAAM,iBAAiB,UAAU,CAAA;AAC5C,YAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,8BAAA,EAAiC,EAAE,CAAA,CAAA,CAAG,CAAA;AAC7D,YAAA,QAAA,CAAS,UAAA,EAAW;AAAA,UACxB,GAAG,GAAG,CAAA;AAAA,QACV,WAAW,KAAA,EAAO;AACd,UAAA,YAAA,CAAa,KAAK,CAAA;AAClB,UAAA,KAAA,GAAQ,IAAA;AAAA,QACZ;AAAA,MACJ,CAAA;AAAA,MACA,EAAE,WAAW,GAAA;AAAI,KACrB;AAEA,IAAA,QAAA,CAAS,QAAQ,EAAE,CAAA;AAEnB,IAAA,OAAO,MAAM;AACT,MAAA,QAAA,CAAS,UAAA,EAAW;AACpB,MAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAAA,IACjC,CAAA;AAAA,EACJ,CAAA,EAAG;AAAA,IACC,eAAA;AAAA,IACA,QAAA;AAAA,IACA,EAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,IAAA;AAAA,IACA,eAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACH,CAAA;AAID,EAAA,MAAM,WAAA,GAAcC,kBAAA;AAAA,IAChB,CAAC,CAAA,KAAwB;AACrB,MAAA,IAAI,CAAC,UAAA,EAAY;AAEjB,MAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,CAAA,CAAE,MAAqB,CAAA;AACrD,MAAA,IAAI,CAAC,IAAA,EAAM;AAEX,MAAA,UAAA,CAAW,MAAM,UAAA,EAAY;AAAA,QACzB,GAAG,UAAA;AAAA,QACH,GAAG;AAAA,OACN,CAAA;AACD,MAAA,IAAI,KAAA,EAAO;AACP,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,4BAAA,EAA+B,EAAE,CAAA,CAAA,CAAA,EAAK,IAAI,CAAA;AAAA,MAC1D;AAAA,IACJ,CAAA;AAAA,IACA,CAAC,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,UAAA,EAAY,IAAI,KAAK;AAAA,GACxD;AAIA,EAAA,MAAM,OAAA,GACF,eAAe,SAAA,IAAa,EAAE,cAAc,QAAA,CAAA,GACtC,OAAA,GACA,SAAS,UAAU,CAAA;AAE7B,EAAA,uBACIL,uBAAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACG,GAAA,EAAK,YAAA;AAAA,MACL,OAAA,EAAS,WAAA;AAAA,MACT,wBAAA,EAAwB,EAAA;AAAA,MACxB,qBAAA,EAAqB,UAAA;AAAA,MACrB,KAAA,EAAO,EAAE,OAAA,EAAS,UAAA;AAAW,KAAA;AAAA,IAE5B;AAAA,GACL;AAER;AC1QO,SAAS,gBAAA,GAA2C;AACvD,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,gBAAA,EAAiB;AAElC,EAAA,MAAM,OAAA,GAAUK,kBAAAA;AAAA,IACZ,CAAC,KAAA,EAAe,UAAA,GAAsC,EAAC,KAAM;AACzD,MAAA,UAAA,CAAW,IAAA,EAAM,OAAO,UAAU,CAAA;AAAA,IACtC,CAAA;AAAA,IACA,CAAC,IAAI;AAAA,GACT;AAEA,EAAA,OAAO,EAAE,OAAA,EAAQ;AACrB","file":"index.js","sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ProbatContextValue {\n host: string;\n userId: string;\n bootstrap: Record<string, string>;\n}\n\nconst ProbatContext = createContext<ProbatContextValue | null>(null);\n\nconst DEFAULT_HOST = \"https://gushi.onrender.com\";\n\nexport interface ProbatProviderProps {\n /** Gushi user ID (UUID) — used to scope SDK requests to a customer */\n userId: string;\n /** Base URL for the Probat API. Defaults to https://gushi.onrender.com */\n host?: string;\n /**\n * Bootstrap assignments to avoid flash on first render.\n * Map of experiment id → variant key.\n * e.g. { \"cta-copy-test\": \"ai_v1\" }\n */\n bootstrap?: Record<string, string>;\n children: React.ReactNode;\n}\n\nexport function ProbatProvider({\n userId,\n host = DEFAULT_HOST,\n bootstrap,\n children,\n}: ProbatProviderProps) {\n const value = useMemo<ProbatContextValue>(\n () => ({\n host: host.replace(/\\/$/, \"\"),\n userId,\n bootstrap: bootstrap ?? {},\n }),\n [userId, host, bootstrap]\n );\n\n return (\n <ProbatContext.Provider value={value}>\n {children}\n </ProbatContext.Provider>\n );\n}\n\nexport function useProbatContext(): ProbatContextValue {\n const ctx = useContext(ProbatContext);\n if (!ctx) {\n throw new Error(\n \"useProbatContext must be used within <ProbatProviderClient>. Wrap your app with <ProbatProviderClient userId={...}>.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { ProbatProvider } from \"../context/ProbatContext\";\nimport type { ProbatProviderProps } from \"../context/ProbatContext\";\n\n/**\n * Client-only provider for Next.js App Router.\n * Import this in your layout/providers file.\n *\n * @example\n * ```tsx\n * // app/providers.tsx\n * \"use client\";\n * import { ProbatProviderClient } from \"@probat/react\";\n *\n * export function Providers({ children }) {\n * return (\n * <ProbatProviderClient userId=\"your-user-uuid\">\n * {children}\n * </ProbatProviderClient>\n * );\n * }\n * ```\n */\nexport function ProbatProviderClient(props: ProbatProviderProps) {\n return React.createElement(ProbatProvider, props);\n}\n\nexport type { ProbatProviderProps };\n","/**\n * Detect if the code is running on localhost (development environment).\n * Returns \"dev\" for localhost, \"prod\" for production.\n */\nexport function detectEnvironment(): \"dev\" | \"prod\" {\n if (typeof window === \"undefined\") {\n return \"prod\"; // Server-side, default to prod\n }\n\n const hostname = window.location.hostname;\n\n // Check for localhost, 127.0.0.1, or local IP addresses\n if (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"0.0.0.0\" ||\n hostname.startsWith(\"192.168.\") ||\n hostname.startsWith(\"10.\") ||\n hostname.startsWith(\"172.16.\") ||\n hostname.startsWith(\"172.17.\") ||\n hostname.startsWith(\"172.18.\") ||\n hostname.startsWith(\"172.19.\") ||\n hostname.startsWith(\"172.20.\") ||\n hostname.startsWith(\"172.21.\") ||\n hostname.startsWith(\"172.22.\") ||\n hostname.startsWith(\"172.23.\") ||\n hostname.startsWith(\"172.24.\") ||\n hostname.startsWith(\"172.25.\") ||\n hostname.startsWith(\"172.26.\") ||\n hostname.startsWith(\"172.27.\") ||\n hostname.startsWith(\"172.28.\") ||\n hostname.startsWith(\"172.29.\") ||\n hostname.startsWith(\"172.30.\") ||\n hostname.startsWith(\"172.31.\")\n ) {\n return \"dev\";\n }\n\n return \"prod\";\n}\n\n","/**\n * Event context helpers: distinct_id, session_id, page info.\n * All browser-safe — no-ops when window is unavailable.\n */\n\nconst DISTINCT_ID_KEY = \"probat:distinct_id\";\nconst SESSION_ID_KEY = \"probat:session_id\";\n\nlet cachedDistinctId: string | null = null;\nlet cachedSessionId: string | null = null;\n\nfunction generateId(): string {\n // crypto.randomUUID where available, else fallback\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // fallback: random hex\n const bytes = new Uint8Array(16);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function getDistinctId(): string {\n if (cachedDistinctId) return cachedDistinctId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = localStorage.getItem(DISTINCT_ID_KEY);\n if (stored) {\n cachedDistinctId = stored;\n return stored;\n }\n } catch {}\n const id = `anon_${generateId()}`;\n cachedDistinctId = id;\n try {\n localStorage.setItem(DISTINCT_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getSessionId(): string {\n if (cachedSessionId) return cachedSessionId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = sessionStorage.getItem(SESSION_ID_KEY);\n if (stored) {\n cachedSessionId = stored;\n return stored;\n }\n } catch {}\n const id = `sess_${generateId()}`;\n cachedSessionId = id;\n try {\n sessionStorage.setItem(SESSION_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getPageKey(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.pathname + window.location.search;\n}\n\nexport function getPageUrl(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.href;\n}\n\nexport function getReferrer(): string {\n if (typeof document === \"undefined\") return \"\";\n return document.referrer;\n}\n\nexport interface EventContext {\n distinct_id: string;\n session_id: string;\n $page_url: string;\n $pathname: string;\n $referrer: string;\n}\n\nexport function buildEventContext(): EventContext {\n return {\n distinct_id: getDistinctId(),\n session_id: getSessionId(),\n $page_url: getPageUrl(),\n $pathname: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n $referrer: getReferrer(),\n };\n}\n","import { detectEnvironment } from \"./environment\";\nimport { buildEventContext } from \"./eventContext\";\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface DecisionResponse {\n variant_key: string;\n}\n\nexport interface MetricPayload {\n event: string;\n properties: Record<string, unknown>;\n}\n\n// ── Assignment fetching ────────────────────────────────────────────────────\n\nconst pendingDecisions = new Map<string, Promise<string>>();\n\n/**\n * Fetch the variant assignment for an experiment.\n * Returns the variant key string (e.g. \"control\", \"ai_v1\").\n * Deduplicates concurrent calls for the same experiment.\n */\nexport async function fetchDecision(\n host: string,\n experimentId: string,\n distinctId: string\n): Promise<string> {\n const existing = pendingDecisions.get(experimentId);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/decide`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify({\n experiment_id: experimentId,\n distinct_id: distinctId,\n }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as DecisionResponse;\n return data.variant_key || \"control\";\n } finally {\n pendingDecisions.delete(experimentId);\n }\n })();\n\n pendingDecisions.set(experimentId, promise);\n return promise;\n}\n\n// ── Metric sending ─────────────────────────────────────────────────────────\n\n/**\n * Fire-and-forget metric send. Never throws.\n */\nexport function sendMetric(\n host: string,\n event: string,\n properties: Record<string, unknown>\n): void {\n if (typeof window === \"undefined\") return;\n\n const ctx = buildEventContext();\n const payload: MetricPayload = {\n event,\n properties: {\n ...ctx,\n environment: detectEnvironment(),\n source: \"react-sdk\",\n captured_at: new Date().toISOString(),\n ...properties,\n },\n };\n\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/metrics`;\n fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(payload),\n }).catch(() => {});\n } catch {\n // silently drop\n }\n}\n\n// ── Click metadata extraction ──────────────────────────────────────────────\n\nexport interface ClickMeta {\n click_target_tag: string;\n click_target_text?: string;\n click_target_id?: string;\n click_is_primary: boolean;\n}\n\n/**\n * Given a click event inside an experiment boundary, extract metadata.\n * Prioritizes elements with data-probat-click=\"primary\",\n * then falls back to button/a/role=button.\n */\nexport function extractClickMeta(target: EventTarget | null): ClickMeta | null {\n if (!target || !(target instanceof HTMLElement)) return null;\n\n // Priority 1: explicit primary marker\n const primary = target.closest('[data-probat-click=\"primary\"]');\n if (primary) return buildMeta(primary as HTMLElement, true);\n\n // Priority 2: interactive elements\n const interactive = target.closest('button, a, [role=\"button\"]');\n if (interactive) return buildMeta(interactive as HTMLElement, false);\n\n return null;\n}\n\nfunction buildMeta(el: HTMLElement, isPrimary: boolean): ClickMeta {\n const meta: ClickMeta = {\n click_target_tag: el.tagName,\n click_is_primary: isPrimary,\n };\n if (el.id) meta.click_target_id = el.id;\n const text = el.textContent?.trim();\n if (text) meta.click_target_text = text.slice(0, 120);\n return meta;\n}\n","/**\n * Dedupe storage for experiment exposures.\n * Uses sessionStorage + in-memory Set fallback.\n * Key format: probat:seen:{id}:{variantKey}:{instanceId}:{pageKey}\n */\n\nconst PREFIX = \"probat:seen:\";\nconst memorySet = new Set<string>();\n\nexport function makeDedupeKey(\n experimentId: string,\n variantKey: string,\n instanceId: string,\n pageKey: string\n): string {\n return `${PREFIX}${experimentId}:${variantKey}:${instanceId}:${pageKey}`;\n}\n\nexport function hasSeen(key: string): boolean {\n if (memorySet.has(key)) return true;\n if (typeof window === \"undefined\") return false;\n try {\n return sessionStorage.getItem(key) === \"1\";\n } catch {\n return false;\n }\n}\n\nexport function markSeen(key: string): void {\n memorySet.add(key);\n if (typeof window === \"undefined\") return;\n try {\n sessionStorage.setItem(key, \"1\");\n } catch {}\n}\n\n/** Reset all dedupe state — useful for testing. */\nexport function resetDedupe(): void {\n memorySet.clear();\n}\n","/**\n * Stable auto-generated instance IDs for <Experiment />.\n *\n * Problem: a naïve useRef + module counter gives a different ID on every mount,\n * so StrictMode double-mount or unmount/remount changes the dedupe key.\n *\n * Solution:\n * 1. React 18+ → useId() is stable per fiber position.\n * 2. Fallback → sessionStorage-backed slot counter per (experimentId, pageKey).\n * 3. Both paths persist a mapping in sessionStorage:\n * probat:instance:{experimentId}:{pageKey}:{positionKey} → stableId\n * so the same position resolves to the same ID across mounts.\n */\n\nimport React, { useRef } from \"react\";\nimport { getPageKey } from \"./eventContext\";\n\nconst INSTANCE_PREFIX = \"probat:instance:\";\n\n// ── Helpers ────────────────────────────────────────────────────────────────\n\nfunction shortId(): string {\n const bytes = new Uint8Array(4);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 4; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Look up or create a stable instance ID in sessionStorage.\n */\nfunction resolveStableId(storageKey: string): string {\n if (typeof window !== \"undefined\") {\n try {\n const stored = sessionStorage.getItem(storageKey);\n if (stored) return stored;\n } catch {}\n }\n const id = `inst_${shortId()}`;\n if (typeof window !== \"undefined\") {\n try {\n sessionStorage.setItem(storageKey, id);\n } catch {}\n }\n return id;\n}\n\n// ── Fallback: render-wave slot counter ─────────────────────────────────────\n// Each synchronous render batch claims sequential slots per (experimentId,\n// pageKey). A microtask resets the counters so the next batch starts at 0,\n// giving the same component position the same slot across mounts.\n\nconst slotCounters = new Map<string, number>();\nlet resetScheduled = false;\n\nfunction claimSlot(groupKey: string): number {\n const idx = slotCounters.get(groupKey) ?? 0;\n slotCounters.set(groupKey, idx + 1);\n if (!resetScheduled) {\n resetScheduled = true;\n Promise.resolve().then(() => {\n slotCounters.clear();\n resetScheduled = false;\n });\n }\n return idx;\n}\n\n// ── Hook: React 18+ path (useId available) ─────────────────────────────────\n\nfunction useStableInstanceIdV18(experimentId: string): string {\n const reactId = (React as any).useId() as string;\n const ref = useRef(\"\");\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${reactId}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Hook: fallback path (no useId) ─────────────────────────────────────────\n\nfunction useStableInstanceIdFallback(experimentId: string): string {\n const slotRef = useRef(-1);\n const ref = useRef(\"\");\n if (slotRef.current === -1) {\n slotRef.current = claimSlot(`${experimentId}:${getPageKey()}`);\n }\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${slotRef.current}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Exported hook ──────────────────────────────────────────────────────────\n// Selection is a module-level constant so the hook-call count never changes\n// between renders — safe for the rules of hooks.\n\nexport const useStableInstanceId: (experimentId: string) => string =\n typeof (React as any).useId === \"function\"\n ? useStableInstanceIdV18\n : useStableInstanceIdFallback;\n\n// ── Test utility ───────────────────────────────────────────────────────────\n\nexport function resetInstanceIdState(): void {\n slotCounters.clear();\n resetScheduled = false;\n}\n","\"use client\";\n\nimport React, {\n useEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n} from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { fetchDecision, sendMetric, extractClickMeta } from \"../utils/api\";\nimport { getDistinctId, getPageKey } from \"../utils/eventContext\";\nimport { makeDedupeKey, hasSeen, markSeen } from \"../utils/dedupeStorage\";\nimport { useStableInstanceId } from \"../utils/stableInstanceId\";\n\n// ── localStorage assignment cache ──────────────────────────────────────────\n\nconst ASSIGNMENT_PREFIX = \"probat:assignment:\";\n\ninterface StoredAssignment {\n variantKey: string;\n ts: number;\n}\n\nfunction readAssignment(id: string): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(ASSIGNMENT_PREFIX + id);\n if (!raw) return null;\n const parsed: StoredAssignment = JSON.parse(raw);\n return parsed.variantKey ?? null;\n } catch {\n return null;\n }\n}\n\nfunction writeAssignment(id: string, variantKey: string): void {\n if (typeof window === \"undefined\") return;\n try {\n const entry: StoredAssignment = { variantKey, ts: Date.now() };\n localStorage.setItem(ASSIGNMENT_PREFIX + id, JSON.stringify(entry));\n } catch {}\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ExperimentTrackOptions {\n /** Auto-track impressions (default true) */\n impression?: boolean;\n /** Auto-track clicks (default true) */\n primaryClick?: boolean;\n /** Custom impression event name (default \"$experiment_exposure\") */\n impressionEventName?: string;\n /** Custom click event name (default \"$experiment_click\") */\n clickEventName?: string;\n}\n\nexport interface ExperimentProps {\n /** Experiment key / identifier */\n id: string;\n /** Control variant ReactNode */\n control: React.ReactNode;\n /** Named variant ReactNodes, keyed by variant key (e.g. { ai_v1: <MyVariant /> }) */\n variants: Record<string, React.ReactNode>;\n /** Tracking configuration */\n track?: ExperimentTrackOptions;\n /** Stable instance id when multiple instances of the same experiment exist on a page */\n componentInstanceId?: string;\n /** Behavior when assignment fetch fails: \"control\" (default) renders control, \"suspend\" throws */\n fallback?: \"control\" | \"suspend\";\n /** Log decisions + events to console */\n debug?: boolean;\n}\n\nexport function Experiment({\n id,\n control,\n variants,\n track,\n componentInstanceId,\n fallback = \"control\",\n debug = false,\n}: ExperimentProps) {\n const { host, bootstrap } = useProbatContext();\n\n // Stable instance id (useId + sessionStorage for cross-mount stability)\n const autoInstanceId = useStableInstanceId(id);\n const instanceId = componentInstanceId ?? autoInstanceId;\n\n // Track options with defaults\n const trackImpression = track?.impression !== false;\n const trackClick = track?.primaryClick !== false;\n const impressionEvent = track?.impressionEventName ?? \"$experiment_exposure\";\n const clickEvent = track?.clickEventName ?? \"$experiment_click\";\n\n // ── Assignment resolution ──────────────────────────────────────────────\n\n const [variantKey, setVariantKey] = useState<string>(() => {\n // Sync resolution order: bootstrap → localStorage → \"control\"\n if (bootstrap[id]) return bootstrap[id];\n const cached = readAssignment(id);\n if (cached) return cached;\n return \"control\";\n });\n const [resolved, setResolved] = useState<boolean>(() => {\n return !!(bootstrap[id] || readAssignment(id));\n });\n\n useEffect(() => {\n // Already resolved from bootstrap or cache\n if (bootstrap[id] || readAssignment(id)) {\n // Ensure state is synced (StrictMode may re-mount)\n const key = bootstrap[id] ?? readAssignment(id) ?? \"control\";\n setVariantKey(key);\n setResolved(true);\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n try {\n const distinctId = getDistinctId();\n const key = await fetchDecision(host, id, distinctId);\n if (cancelled) return;\n\n // Validate variant key\n if (key !== \"control\" && !(key in variants)) {\n if (debug) {\n console.warn(\n `[probat] Unknown variant \"${key}\" for experiment \"${id}\", falling back to control`\n );\n }\n setVariantKey(\"control\");\n } else {\n setVariantKey(key);\n writeAssignment(id, key);\n }\n } catch (err) {\n if (cancelled) return;\n if (debug) {\n console.error(`[probat] fetchDecision failed for \"${id}\":`, err);\n }\n if (fallback === \"suspend\") throw err;\n setVariantKey(\"control\");\n } finally {\n if (!cancelled) setResolved(true);\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [id, host]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // ── Debug logging ──────────────────────────────────────────────────────\n\n useEffect(() => {\n if (debug && resolved) {\n console.log(`[probat] Experiment \"${id}\" → variant \"${variantKey}\"`, {\n instanceId,\n pageKey: getPageKey(),\n });\n }\n }, [debug, id, variantKey, resolved, instanceId]);\n\n // ── Shared event properties ────────────────────────────────────────────\n\n const eventProps = useMemo(\n () => ({\n experiment_id: id,\n variant_key: variantKey,\n component_instance_id: instanceId,\n }),\n [id, variantKey, instanceId]\n );\n\n // ── Impression tracking via IntersectionObserver ────────────────────────\n\n const containerRef = useRef<HTMLDivElement>(null);\n const impressionSent = useRef(false);\n\n useEffect(() => {\n if (!trackImpression || !resolved) return;\n\n // Reset on re-mount (StrictMode safety)\n impressionSent.current = false;\n\n const pageKey = getPageKey();\n const dedupeKey = makeDedupeKey(id, variantKey, instanceId, pageKey);\n\n // Already seen this session\n if (hasSeen(dedupeKey)) {\n impressionSent.current = true;\n return;\n }\n\n const el = containerRef.current;\n if (!el) return;\n\n // Fallback: no IntersectionObserver (SSR, old browser)\n if (typeof IntersectionObserver === \"undefined\") {\n if (!impressionSent.current) {\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent (no IO) for \"${id}\"`);\n }\n return;\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry || impressionSent.current) return;\n\n if (entry.isIntersecting) {\n timer = setTimeout(() => {\n if (impressionSent.current) return;\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent for \"${id}\"`);\n observer.disconnect();\n }, 250);\n } else if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n },\n { threshold: 0.5 }\n );\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n if (timer) clearTimeout(timer);\n };\n }, [\n trackImpression,\n resolved,\n id,\n variantKey,\n instanceId,\n host,\n impressionEvent,\n eventProps,\n debug,\n ]);\n\n // ── Click tracking ─────────────────────────────────────────────────────\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n if (!trackClick) return;\n\n const meta = extractClickMeta(e.target as EventTarget);\n if (!meta) return;\n\n sendMetric(host, clickEvent, {\n ...eventProps,\n ...meta,\n });\n if (debug) {\n console.log(`[probat] Click tracked for \"${id}\"`, meta);\n }\n },\n [trackClick, host, clickEvent, eventProps, id, debug]\n );\n\n // ── Render ─────────────────────────────────────────────────────────────\n\n const content =\n variantKey === \"control\" || !(variantKey in variants)\n ? control\n : variants[variantKey];\n\n return (\n <div\n ref={containerRef}\n onClick={handleClick}\n data-probat-experiment={id}\n data-probat-variant={variantKey}\n style={{ display: \"contents\" }}\n >\n {content}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { sendMetric } from \"../utils/api\";\n\nexport interface UseProbatMetricsReturn {\n /**\n * Send a custom event with arbitrary properties.\n * Never throws — failures are silently dropped.\n *\n * @example\n * ```tsx\n * const { capture } = useProbatMetrics();\n * capture(\"purchase\", { revenue: 42, currency: \"USD\" });\n * ```\n */\n capture: (event: string, properties?: Record<string, unknown>) => void;\n}\n\n/**\n * Minimal metrics hook. Provides a single `capture(event, props)` function\n * that sends events to the Probat backend using the provider's host config.\n */\nexport function useProbatMetrics(): UseProbatMetricsReturn {\n const { host } = useProbatContext();\n\n const capture = useCallback(\n (event: string, properties: Record<string, unknown> = {}) => {\n sendMetric(host, event, properties);\n },\n [host]\n );\n\n return { capture };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context/ProbatContext.tsx","../src/components/ProbatProviderClient.tsx","../src/utils/environment.ts","../src/utils/eventContext.ts","../src/utils/api.ts","../src/utils/dedupeStorage.ts","../src/utils/stableInstanceId.ts","../src/components/Experiment.tsx","../src/hooks/useProbatMetrics.ts"],"names":["createContext","useMemo","React","useContext","useRef","useState","useEffect","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ProbatContextValue {\n host: string;\n userId: string;\n bootstrap: Record<string, string>;\n}\n\nconst ProbatContext = createContext<ProbatContextValue | null>(null);\n\nconst DEFAULT_HOST = \"https://gushi.onrender.com\";\n\nexport interface ProbatProviderProps {\n /** Gushi user ID (UUID) — used to scope SDK requests to a customer */\n userId: string;\n /** Base URL for the Probat API. Defaults to https://gushi.onrender.com */\n host?: string;\n /**\n * Bootstrap assignments to avoid flash on first render.\n * Map of experiment id → variant key.\n * e.g. { \"cta-copy-test\": \"ai_v1\" }\n */\n bootstrap?: Record<string, string>;\n children: React.ReactNode;\n}\n\nexport function ProbatProvider({\n userId,\n host = DEFAULT_HOST,\n bootstrap,\n children,\n}: ProbatProviderProps) {\n const value = useMemo<ProbatContextValue>(\n () => ({\n host: host.replace(/\\/$/, \"\"),\n userId,\n bootstrap: bootstrap ?? {},\n }),\n [userId, host, bootstrap]\n );\n\n return (\n <ProbatContext.Provider value={value}>\n {children}\n </ProbatContext.Provider>\n );\n}\n\nexport function useProbatContext(): ProbatContextValue {\n const ctx = useContext(ProbatContext);\n if (!ctx) {\n throw new Error(\n \"useProbatContext must be used within <ProbatProviderClient>. Wrap your app with <ProbatProviderClient userId={...}>.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { ProbatProvider } from \"../context/ProbatContext\";\nimport type { ProbatProviderProps } from \"../context/ProbatContext\";\n\n/**\n * Client-only provider for Next.js App Router.\n * Import this in your layout/providers file.\n *\n * @example\n * ```tsx\n * // app/providers.tsx\n * \"use client\";\n * import { ProbatProviderClient } from \"@probat/react\";\n *\n * export function Providers({ children }) {\n * return (\n * <ProbatProviderClient userId=\"your-user-uuid\">\n * {children}\n * </ProbatProviderClient>\n * );\n * }\n * ```\n */\nexport function ProbatProviderClient(props: ProbatProviderProps) {\n return React.createElement(ProbatProvider, props);\n}\n\nexport type { ProbatProviderProps };\n","/**\n * Detect if the code is running on localhost (development environment).\n * Returns \"dev\" for localhost, \"prod\" for production.\n */\nexport function detectEnvironment(): \"dev\" | \"prod\" {\n if (typeof window === \"undefined\") {\n return \"prod\"; // Server-side, default to prod\n }\n\n const hostname = window.location.hostname;\n\n // Check for localhost, 127.0.0.1, or local IP addresses\n if (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"0.0.0.0\" ||\n hostname.startsWith(\"192.168.\") ||\n hostname.startsWith(\"10.\") ||\n hostname.startsWith(\"172.16.\") ||\n hostname.startsWith(\"172.17.\") ||\n hostname.startsWith(\"172.18.\") ||\n hostname.startsWith(\"172.19.\") ||\n hostname.startsWith(\"172.20.\") ||\n hostname.startsWith(\"172.21.\") ||\n hostname.startsWith(\"172.22.\") ||\n hostname.startsWith(\"172.23.\") ||\n hostname.startsWith(\"172.24.\") ||\n hostname.startsWith(\"172.25.\") ||\n hostname.startsWith(\"172.26.\") ||\n hostname.startsWith(\"172.27.\") ||\n hostname.startsWith(\"172.28.\") ||\n hostname.startsWith(\"172.29.\") ||\n hostname.startsWith(\"172.30.\") ||\n hostname.startsWith(\"172.31.\")\n ) {\n return \"dev\";\n }\n\n return \"prod\";\n}\n\n","/**\n * Event context helpers: distinct_id, session_id, page info.\n * All browser-safe — no-ops when window is unavailable.\n */\n\nconst DISTINCT_ID_KEY = \"probat:distinct_id\";\nconst SESSION_ID_KEY = \"probat:session_id\";\n\nlet cachedDistinctId: string | null = null;\nlet cachedSessionId: string | null = null;\n\nfunction generateId(): string {\n // crypto.randomUUID where available, else fallback\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // fallback: random hex\n const bytes = new Uint8Array(16);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function getDistinctId(): string {\n if (cachedDistinctId) return cachedDistinctId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = localStorage.getItem(DISTINCT_ID_KEY);\n if (stored) {\n cachedDistinctId = stored;\n return stored;\n }\n } catch {}\n const id = `anon_${generateId()}`;\n cachedDistinctId = id;\n try {\n localStorage.setItem(DISTINCT_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getSessionId(): string {\n if (cachedSessionId) return cachedSessionId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = sessionStorage.getItem(SESSION_ID_KEY);\n if (stored) {\n cachedSessionId = stored;\n return stored;\n }\n } catch {}\n const id = `sess_${generateId()}`;\n cachedSessionId = id;\n try {\n sessionStorage.setItem(SESSION_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getPageKey(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.pathname + window.location.search;\n}\n\nexport function getPageUrl(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.href;\n}\n\nexport function getReferrer(): string {\n if (typeof document === \"undefined\") return \"\";\n return document.referrer;\n}\n\nexport interface EventContext {\n distinct_id: string;\n session_id: string;\n $page_url: string;\n $pathname: string;\n $referrer: string;\n}\n\nexport function buildEventContext(): EventContext {\n return {\n distinct_id: getDistinctId(),\n session_id: getSessionId(),\n $page_url: getPageUrl(),\n $pathname: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n $referrer: getReferrer(),\n };\n}\n","import { detectEnvironment } from \"./environment\";\nimport { buildEventContext } from \"./eventContext\";\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface DecisionResponse {\n variant_key: string;\n}\n\nexport interface MetricPayload {\n event: string;\n properties: Record<string, unknown>;\n}\n\n// ── Assignment fetching ────────────────────────────────────────────────────\n\nconst pendingDecisions = new Map<string, Promise<string>>();\n\n/**\n * Fetch the variant assignment for an experiment.\n * Returns the variant key string (e.g. \"control\", \"ai_v1\").\n * Deduplicates concurrent calls for the same experiment.\n */\nexport async function fetchDecision(\n host: string,\n experimentId: string,\n distinctId: string\n): Promise<string> {\n const existing = pendingDecisions.get(experimentId);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/decide`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify({\n experiment_id: experimentId,\n distinct_id: distinctId,\n }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as DecisionResponse;\n return data.variant_key || \"control\";\n } finally {\n pendingDecisions.delete(experimentId);\n }\n })();\n\n pendingDecisions.set(experimentId, promise);\n return promise;\n}\n\n// ── Metric sending ─────────────────────────────────────────────────────────\n\n/**\n * Fire-and-forget metric send. Never throws.\n */\nexport function sendMetric(\n host: string,\n event: string,\n properties: Record<string, unknown>\n): void {\n if (typeof window === \"undefined\") return;\n\n const ctx = buildEventContext();\n const payload: MetricPayload = {\n event,\n properties: {\n ...ctx,\n environment: detectEnvironment(),\n source: \"react-sdk\",\n captured_at: new Date().toISOString(),\n ...properties,\n },\n };\n\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/metrics`;\n fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(payload),\n }).catch(() => {});\n } catch {\n // silently drop\n }\n}\n\n// ── Click metadata extraction ──────────────────────────────────────────────\n\nexport interface ClickMeta {\n click_target_tag: string;\n click_target_text?: string;\n click_target_id?: string;\n click_is_primary: boolean;\n}\n\n/**\n * Given a click event inside an experiment boundary, extract metadata.\n * Prioritizes elements with data-probat-click=\"primary\",\n * then falls back to button/a/role=button.\n */\nexport function extractClickMeta(target: EventTarget | null): ClickMeta | null {\n if (!target || !(target instanceof HTMLElement)) return null;\n\n // Priority 1: explicit primary marker\n const primary = target.closest('[data-probat-click=\"primary\"]');\n if (primary) return buildMeta(primary as HTMLElement, true);\n\n // Priority 2: interactive elements\n const interactive = target.closest('button, a, [role=\"button\"]');\n if (interactive) return buildMeta(interactive as HTMLElement, false);\n\n return null;\n}\n\nfunction buildMeta(el: HTMLElement, isPrimary: boolean): ClickMeta {\n const meta: ClickMeta = {\n click_target_tag: el.tagName,\n click_is_primary: isPrimary,\n };\n if (el.id) meta.click_target_id = el.id;\n const text = el.textContent?.trim();\n if (text) meta.click_target_text = text.slice(0, 120);\n return meta;\n}\n","/**\n * Dedupe storage for experiment exposures.\n * Uses sessionStorage + in-memory Set fallback.\n * Key format: probat:seen:{id}:{variantKey}:{instanceId}:{pageKey}\n */\n\nconst PREFIX = \"probat:seen:\";\nconst memorySet = new Set<string>();\n\nexport function makeDedupeKey(\n experimentId: string,\n variantKey: string,\n instanceId: string,\n pageKey: string\n): string {\n return `${PREFIX}${experimentId}:${variantKey}:${instanceId}:${pageKey}`;\n}\n\nexport function hasSeen(key: string): boolean {\n if (memorySet.has(key)) return true;\n if (typeof window === \"undefined\") return false;\n try {\n return sessionStorage.getItem(key) === \"1\";\n } catch {\n return false;\n }\n}\n\nexport function markSeen(key: string): void {\n memorySet.add(key);\n if (typeof window === \"undefined\") return;\n try {\n sessionStorage.setItem(key, \"1\");\n } catch {}\n}\n\n/** Reset all dedupe state — useful for testing. */\nexport function resetDedupe(): void {\n memorySet.clear();\n}\n","/**\n * Stable auto-generated instance IDs for <Experiment />.\n *\n * Problem: a naïve useRef + module counter gives a different ID on every mount,\n * so StrictMode double-mount or unmount/remount changes the dedupe key.\n *\n * Solution:\n * 1. React 18+ → useId() is stable per fiber position.\n * 2. Fallback → sessionStorage-backed slot counter per (experimentId, pageKey).\n * 3. Both paths persist a mapping in sessionStorage:\n * probat:instance:{experimentId}:{pageKey}:{positionKey} → stableId\n * so the same position resolves to the same ID across mounts.\n */\n\nimport React, { useRef } from \"react\";\nimport { getPageKey } from \"./eventContext\";\n\nconst INSTANCE_PREFIX = \"probat:instance:\";\n\n// ── Helpers ────────────────────────────────────────────────────────────────\n\nfunction shortId(): string {\n const bytes = new Uint8Array(4);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 4; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Look up or create a stable instance ID in sessionStorage.\n */\nfunction resolveStableId(storageKey: string): string {\n if (typeof window !== \"undefined\") {\n try {\n const stored = sessionStorage.getItem(storageKey);\n if (stored) return stored;\n } catch {}\n }\n const id = `inst_${shortId()}`;\n if (typeof window !== \"undefined\") {\n try {\n sessionStorage.setItem(storageKey, id);\n } catch {}\n }\n return id;\n}\n\n// ── Fallback: render-wave slot counter ─────────────────────────────────────\n// Each synchronous render batch claims sequential slots per (experimentId,\n// pageKey). A microtask resets the counters so the next batch starts at 0,\n// giving the same component position the same slot across mounts.\n\nconst slotCounters = new Map<string, number>();\nlet resetScheduled = false;\n\nfunction claimSlot(groupKey: string): number {\n const idx = slotCounters.get(groupKey) ?? 0;\n slotCounters.set(groupKey, idx + 1);\n if (!resetScheduled) {\n resetScheduled = true;\n Promise.resolve().then(() => {\n slotCounters.clear();\n resetScheduled = false;\n });\n }\n return idx;\n}\n\n// ── Hook: React 18+ path (useId available) ─────────────────────────────────\n\nfunction useStableInstanceIdV18(experimentId: string): string {\n const reactId = (React as any).useId() as string;\n const ref = useRef(\"\");\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${reactId}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Hook: fallback path (no useId) ─────────────────────────────────────────\n\nfunction useStableInstanceIdFallback(experimentId: string): string {\n const slotRef = useRef(-1);\n const ref = useRef(\"\");\n if (slotRef.current === -1) {\n slotRef.current = claimSlot(`${experimentId}:${getPageKey()}`);\n }\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${slotRef.current}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Exported hook ──────────────────────────────────────────────────────────\n// Selection is a module-level constant so the hook-call count never changes\n// between renders — safe for the rules of hooks.\n\nexport const useStableInstanceId: (experimentId: string) => string =\n typeof (React as any).useId === \"function\"\n ? useStableInstanceIdV18\n : useStableInstanceIdFallback;\n\n// ── Test utility ───────────────────────────────────────────────────────────\n\nexport function resetInstanceIdState(): void {\n slotCounters.clear();\n resetScheduled = false;\n}\n","\"use client\";\n\nimport React, {\n useEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n} from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { fetchDecision, sendMetric, extractClickMeta } from \"../utils/api\";\nimport { getDistinctId, getPageKey } from \"../utils/eventContext\";\nimport { makeDedupeKey, hasSeen, markSeen } from \"../utils/dedupeStorage\";\nimport { useStableInstanceId } from \"../utils/stableInstanceId\";\n\n// ── localStorage assignment cache ──────────────────────────────────────────\n\nconst ASSIGNMENT_PREFIX = \"probat:assignment:\";\n\ninterface StoredAssignment {\n variantKey: string;\n ts: number;\n}\n\nfunction readAssignment(id: string): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(ASSIGNMENT_PREFIX + id);\n if (!raw) return null;\n const parsed: StoredAssignment = JSON.parse(raw);\n return parsed.variantKey ?? null;\n } catch {\n return null;\n }\n}\n\nfunction writeAssignment(id: string, variantKey: string): void {\n if (typeof window === \"undefined\") return;\n try {\n const entry: StoredAssignment = { variantKey, ts: Date.now() };\n localStorage.setItem(ASSIGNMENT_PREFIX + id, JSON.stringify(entry));\n } catch {}\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ExperimentTrackOptions {\n /** Auto-track impressions (default true) */\n impression?: boolean;\n /** Auto-track clicks (default true) */\n primaryClick?: boolean;\n /** Custom impression event name (default \"$experiment_exposure\") */\n impressionEventName?: string;\n /** Custom click event name (default \"$experiment_click\") */\n clickEventName?: string;\n}\n\nexport interface ExperimentProps {\n /** Experiment key / identifier */\n id: string;\n /** Control variant ReactNode */\n control: React.ReactNode;\n /** Named variant ReactNodes, keyed by variant key (e.g. { ai_v1: <MyVariant /> }) */\n variants: Record<string, React.ReactNode>;\n /** Tracking configuration */\n track?: ExperimentTrackOptions;\n /** Stable instance id when multiple instances of the same experiment exist on a page */\n componentInstanceId?: string;\n /** Behavior when assignment fetch fails: \"control\" (default) renders control, \"suspend\" throws */\n fallback?: \"control\" | \"suspend\";\n /** Log decisions + events to console */\n debug?: boolean;\n}\n\nexport function Experiment({\n id,\n control,\n variants,\n track,\n componentInstanceId,\n fallback = \"control\",\n debug = false,\n}: ExperimentProps) {\n const { host, bootstrap } = useProbatContext();\n\n // Stable instance id (useId + sessionStorage for cross-mount stability)\n const autoInstanceId = useStableInstanceId(id);\n const instanceId = componentInstanceId ?? autoInstanceId;\n\n // Track options with defaults\n const trackImpression = track?.impression !== false;\n const trackClick = track?.primaryClick !== false;\n const impressionEvent = track?.impressionEventName ?? \"$experiment_exposure\";\n const clickEvent = track?.clickEventName ?? \"$experiment_click\";\n\n // ── Assignment resolution ──────────────────────────────────────────────\n\n const [variantKey, setVariantKey] = useState<string>(() => {\n // Sync resolution order: bootstrap → localStorage → \"control\"\n if (bootstrap[id]) return bootstrap[id];\n const cached = readAssignment(id);\n if (cached) return cached;\n return \"control\";\n });\n const [resolved, setResolved] = useState<boolean>(() => {\n return !!(bootstrap[id] || readAssignment(id));\n });\n\n useEffect(() => {\n // Already resolved from bootstrap or cache\n if (bootstrap[id] || readAssignment(id)) {\n // Ensure state is synced (StrictMode may re-mount)\n const key = bootstrap[id] ?? readAssignment(id) ?? \"control\";\n setVariantKey(key);\n setResolved(true);\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n try {\n const distinctId = getDistinctId();\n const key = await fetchDecision(host, id, distinctId);\n if (cancelled) return;\n\n // Validate variant key\n if (key !== \"control\" && !(key in variants)) {\n if (debug) {\n console.warn(\n `[probat] Unknown variant \"${key}\" for experiment \"${id}\", falling back to control`\n );\n }\n setVariantKey(\"control\");\n } else {\n setVariantKey(key);\n writeAssignment(id, key);\n }\n } catch (err) {\n if (cancelled) return;\n if (debug) {\n console.error(`[probat] fetchDecision failed for \"${id}\":`, err);\n }\n if (fallback === \"suspend\") throw err;\n setVariantKey(\"control\");\n } finally {\n if (!cancelled) setResolved(true);\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [id, host]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // ── Debug logging ──────────────────────────────────────────────────────\n\n useEffect(() => {\n if (debug && resolved) {\n console.log(`[probat] Experiment \"${id}\" → variant \"${variantKey}\"`, {\n instanceId,\n pageKey: getPageKey(),\n });\n }\n }, [debug, id, variantKey, resolved, instanceId]);\n\n // ── Shared event properties ────────────────────────────────────────────\n\n const eventProps = useMemo(\n () => ({\n experiment_id: id,\n variant_key: variantKey,\n component_instance_id: instanceId,\n }),\n [id, variantKey, instanceId]\n );\n\n // ── Impression tracking via IntersectionObserver ────────────────────────\n\n const containerRef = useRef<HTMLDivElement>(null);\n const impressionSent = useRef(false);\n\n useEffect(() => {\n if (!trackImpression || !resolved) return;\n\n // Reset on re-mount (StrictMode safety)\n impressionSent.current = false;\n\n const pageKey = getPageKey();\n const dedupeKey = makeDedupeKey(id, variantKey, instanceId, pageKey);\n\n // Already seen this session\n if (hasSeen(dedupeKey)) {\n impressionSent.current = true;\n return;\n }\n\n const el = containerRef.current;\n if (!el) return;\n\n // Fallback: no IntersectionObserver (SSR, old browser)\n if (typeof IntersectionObserver === \"undefined\") {\n if (!impressionSent.current) {\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent (no IO) for \"${id}\"`);\n }\n return;\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry || impressionSent.current) return;\n\n if (entry.isIntersecting) {\n timer = setTimeout(() => {\n if (impressionSent.current) return;\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent for \"${id}\"`);\n observer.disconnect();\n }, 250);\n } else if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n },\n { threshold: 0.5 }\n );\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n if (timer) clearTimeout(timer);\n };\n }, [\n trackImpression,\n resolved,\n id,\n variantKey,\n instanceId,\n host,\n impressionEvent,\n eventProps,\n debug,\n ]);\n\n // ── Click tracking ─────────────────────────────────────────────────────\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n if (!trackClick) return;\n\n const meta = extractClickMeta(e.target as EventTarget);\n if (!meta) return;\n\n sendMetric(host, clickEvent, {\n ...eventProps,\n ...meta,\n });\n if (debug) {\n console.log(`[probat] Click tracked for \"${id}\"`, meta);\n }\n },\n [trackClick, host, clickEvent, eventProps, id, debug]\n );\n\n // ── Render ─────────────────────────────────────────────────────────────\n\n const content =\n variantKey === \"control\" || !(variantKey in variants)\n ? control\n : variants[variantKey];\n\n return (\n <div\n ref={containerRef}\n onClick={handleClick}\n data-probat-experiment={id}\n data-probat-variant={variantKey}\n style={{ display: \"block\", margin: 0, padding: 0 }}\n >\n {content}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { sendMetric } from \"../utils/api\";\n\nexport interface UseProbatMetricsReturn {\n /**\n * Send a custom event with arbitrary properties.\n * Never throws — failures are silently dropped.\n *\n * @example\n * ```tsx\n * const { capture } = useProbatMetrics();\n * capture(\"purchase\", { revenue: 42, currency: \"USD\" });\n * ```\n */\n capture: (event: string, properties?: Record<string, unknown>) => void;\n}\n\n/**\n * Minimal metrics hook. Provides a single `capture(event, props)` function\n * that sends events to the Probat backend using the provider's host config.\n */\nexport function useProbatMetrics(): UseProbatMetricsReturn {\n const { host } = useProbatContext();\n\n const capture = useCallback(\n (event: string, properties: Record<string, unknown> = {}) => {\n sendMetric(host, event, properties);\n },\n [host]\n );\n\n return { capture };\n}\n"]}
|
package/dist/index.mjs
CHANGED
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context/ProbatContext.tsx","../src/components/ProbatProviderClient.tsx","../src/utils/environment.ts","../src/utils/eventContext.ts","../src/utils/api.ts","../src/utils/dedupeStorage.ts","../src/utils/stableInstanceId.ts","../src/components/Experiment.tsx","../src/hooks/useProbatMetrics.ts"],"names":["React","useMemo","useRef","useCallback"],"mappings":"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","file":"index.mjs","sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ProbatContextValue {\n host: string;\n userId: string;\n bootstrap: Record<string, string>;\n}\n\nconst ProbatContext = createContext<ProbatContextValue | null>(null);\n\nconst DEFAULT_HOST = \"https://gushi.onrender.com\";\n\nexport interface ProbatProviderProps {\n /** Gushi user ID (UUID) — used to scope SDK requests to a customer */\n userId: string;\n /** Base URL for the Probat API. Defaults to https://gushi.onrender.com */\n host?: string;\n /**\n * Bootstrap assignments to avoid flash on first render.\n * Map of experiment id → variant key.\n * e.g. { \"cta-copy-test\": \"ai_v1\" }\n */\n bootstrap?: Record<string, string>;\n children: React.ReactNode;\n}\n\nexport function ProbatProvider({\n userId,\n host = DEFAULT_HOST,\n bootstrap,\n children,\n}: ProbatProviderProps) {\n const value = useMemo<ProbatContextValue>(\n () => ({\n host: host.replace(/\\/$/, \"\"),\n userId,\n bootstrap: bootstrap ?? {},\n }),\n [userId, host, bootstrap]\n );\n\n return (\n <ProbatContext.Provider value={value}>\n {children}\n </ProbatContext.Provider>\n );\n}\n\nexport function useProbatContext(): ProbatContextValue {\n const ctx = useContext(ProbatContext);\n if (!ctx) {\n throw new Error(\n \"useProbatContext must be used within <ProbatProviderClient>. Wrap your app with <ProbatProviderClient userId={...}>.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { ProbatProvider } from \"../context/ProbatContext\";\nimport type { ProbatProviderProps } from \"../context/ProbatContext\";\n\n/**\n * Client-only provider for Next.js App Router.\n * Import this in your layout/providers file.\n *\n * @example\n * ```tsx\n * // app/providers.tsx\n * \"use client\";\n * import { ProbatProviderClient } from \"@probat/react\";\n *\n * export function Providers({ children }) {\n * return (\n * <ProbatProviderClient userId=\"your-user-uuid\">\n * {children}\n * </ProbatProviderClient>\n * );\n * }\n * ```\n */\nexport function ProbatProviderClient(props: ProbatProviderProps) {\n return React.createElement(ProbatProvider, props);\n}\n\nexport type { ProbatProviderProps };\n","/**\n * Detect if the code is running on localhost (development environment).\n * Returns \"dev\" for localhost, \"prod\" for production.\n */\nexport function detectEnvironment(): \"dev\" | \"prod\" {\n if (typeof window === \"undefined\") {\n return \"prod\"; // Server-side, default to prod\n }\n\n const hostname = window.location.hostname;\n\n // Check for localhost, 127.0.0.1, or local IP addresses\n if (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"0.0.0.0\" ||\n hostname.startsWith(\"192.168.\") ||\n hostname.startsWith(\"10.\") ||\n hostname.startsWith(\"172.16.\") ||\n hostname.startsWith(\"172.17.\") ||\n hostname.startsWith(\"172.18.\") ||\n hostname.startsWith(\"172.19.\") ||\n hostname.startsWith(\"172.20.\") ||\n hostname.startsWith(\"172.21.\") ||\n hostname.startsWith(\"172.22.\") ||\n hostname.startsWith(\"172.23.\") ||\n hostname.startsWith(\"172.24.\") ||\n hostname.startsWith(\"172.25.\") ||\n hostname.startsWith(\"172.26.\") ||\n hostname.startsWith(\"172.27.\") ||\n hostname.startsWith(\"172.28.\") ||\n hostname.startsWith(\"172.29.\") ||\n hostname.startsWith(\"172.30.\") ||\n hostname.startsWith(\"172.31.\")\n ) {\n return \"dev\";\n }\n\n return \"prod\";\n}\n\n","/**\n * Event context helpers: distinct_id, session_id, page info.\n * All browser-safe — no-ops when window is unavailable.\n */\n\nconst DISTINCT_ID_KEY = \"probat:distinct_id\";\nconst SESSION_ID_KEY = \"probat:session_id\";\n\nlet cachedDistinctId: string | null = null;\nlet cachedSessionId: string | null = null;\n\nfunction generateId(): string {\n // crypto.randomUUID where available, else fallback\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // fallback: random hex\n const bytes = new Uint8Array(16);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function getDistinctId(): string {\n if (cachedDistinctId) return cachedDistinctId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = localStorage.getItem(DISTINCT_ID_KEY);\n if (stored) {\n cachedDistinctId = stored;\n return stored;\n }\n } catch {}\n const id = `anon_${generateId()}`;\n cachedDistinctId = id;\n try {\n localStorage.setItem(DISTINCT_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getSessionId(): string {\n if (cachedSessionId) return cachedSessionId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = sessionStorage.getItem(SESSION_ID_KEY);\n if (stored) {\n cachedSessionId = stored;\n return stored;\n }\n } catch {}\n const id = `sess_${generateId()}`;\n cachedSessionId = id;\n try {\n sessionStorage.setItem(SESSION_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getPageKey(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.pathname + window.location.search;\n}\n\nexport function getPageUrl(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.href;\n}\n\nexport function getReferrer(): string {\n if (typeof document === \"undefined\") return \"\";\n return document.referrer;\n}\n\nexport interface EventContext {\n distinct_id: string;\n session_id: string;\n $page_url: string;\n $pathname: string;\n $referrer: string;\n}\n\nexport function buildEventContext(): EventContext {\n return {\n distinct_id: getDistinctId(),\n session_id: getSessionId(),\n $page_url: getPageUrl(),\n $pathname: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n $referrer: getReferrer(),\n };\n}\n","import { detectEnvironment } from \"./environment\";\nimport { buildEventContext } from \"./eventContext\";\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface DecisionResponse {\n variant_key: string;\n}\n\nexport interface MetricPayload {\n event: string;\n properties: Record<string, unknown>;\n}\n\n// ── Assignment fetching ────────────────────────────────────────────────────\n\nconst pendingDecisions = new Map<string, Promise<string>>();\n\n/**\n * Fetch the variant assignment for an experiment.\n * Returns the variant key string (e.g. \"control\", \"ai_v1\").\n * Deduplicates concurrent calls for the same experiment.\n */\nexport async function fetchDecision(\n host: string,\n experimentId: string,\n distinctId: string\n): Promise<string> {\n const existing = pendingDecisions.get(experimentId);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/decide`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify({\n experiment_id: experimentId,\n distinct_id: distinctId,\n }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as DecisionResponse;\n return data.variant_key || \"control\";\n } finally {\n pendingDecisions.delete(experimentId);\n }\n })();\n\n pendingDecisions.set(experimentId, promise);\n return promise;\n}\n\n// ── Metric sending ─────────────────────────────────────────────────────────\n\n/**\n * Fire-and-forget metric send. Never throws.\n */\nexport function sendMetric(\n host: string,\n event: string,\n properties: Record<string, unknown>\n): void {\n if (typeof window === \"undefined\") return;\n\n const ctx = buildEventContext();\n const payload: MetricPayload = {\n event,\n properties: {\n ...ctx,\n environment: detectEnvironment(),\n source: \"react-sdk\",\n captured_at: new Date().toISOString(),\n ...properties,\n },\n };\n\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/metrics`;\n fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(payload),\n }).catch(() => {});\n } catch {\n // silently drop\n }\n}\n\n// ── Click metadata extraction ──────────────────────────────────────────────\n\nexport interface ClickMeta {\n click_target_tag: string;\n click_target_text?: string;\n click_target_id?: string;\n click_is_primary: boolean;\n}\n\n/**\n * Given a click event inside an experiment boundary, extract metadata.\n * Prioritizes elements with data-probat-click=\"primary\",\n * then falls back to button/a/role=button.\n */\nexport function extractClickMeta(target: EventTarget | null): ClickMeta | null {\n if (!target || !(target instanceof HTMLElement)) return null;\n\n // Priority 1: explicit primary marker\n const primary = target.closest('[data-probat-click=\"primary\"]');\n if (primary) return buildMeta(primary as HTMLElement, true);\n\n // Priority 2: interactive elements\n const interactive = target.closest('button, a, [role=\"button\"]');\n if (interactive) return buildMeta(interactive as HTMLElement, false);\n\n return null;\n}\n\nfunction buildMeta(el: HTMLElement, isPrimary: boolean): ClickMeta {\n const meta: ClickMeta = {\n click_target_tag: el.tagName,\n click_is_primary: isPrimary,\n };\n if (el.id) meta.click_target_id = el.id;\n const text = el.textContent?.trim();\n if (text) meta.click_target_text = text.slice(0, 120);\n return meta;\n}\n","/**\n * Dedupe storage for experiment exposures.\n * Uses sessionStorage + in-memory Set fallback.\n * Key format: probat:seen:{id}:{variantKey}:{instanceId}:{pageKey}\n */\n\nconst PREFIX = \"probat:seen:\";\nconst memorySet = new Set<string>();\n\nexport function makeDedupeKey(\n experimentId: string,\n variantKey: string,\n instanceId: string,\n pageKey: string\n): string {\n return `${PREFIX}${experimentId}:${variantKey}:${instanceId}:${pageKey}`;\n}\n\nexport function hasSeen(key: string): boolean {\n if (memorySet.has(key)) return true;\n if (typeof window === \"undefined\") return false;\n try {\n return sessionStorage.getItem(key) === \"1\";\n } catch {\n return false;\n }\n}\n\nexport function markSeen(key: string): void {\n memorySet.add(key);\n if (typeof window === \"undefined\") return;\n try {\n sessionStorage.setItem(key, \"1\");\n } catch {}\n}\n\n/** Reset all dedupe state — useful for testing. */\nexport function resetDedupe(): void {\n memorySet.clear();\n}\n","/**\n * Stable auto-generated instance IDs for <Experiment />.\n *\n * Problem: a naïve useRef + module counter gives a different ID on every mount,\n * so StrictMode double-mount or unmount/remount changes the dedupe key.\n *\n * Solution:\n * 1. React 18+ → useId() is stable per fiber position.\n * 2. Fallback → sessionStorage-backed slot counter per (experimentId, pageKey).\n * 3. Both paths persist a mapping in sessionStorage:\n * probat:instance:{experimentId}:{pageKey}:{positionKey} → stableId\n * so the same position resolves to the same ID across mounts.\n */\n\nimport React, { useRef } from \"react\";\nimport { getPageKey } from \"./eventContext\";\n\nconst INSTANCE_PREFIX = \"probat:instance:\";\n\n// ── Helpers ────────────────────────────────────────────────────────────────\n\nfunction shortId(): string {\n const bytes = new Uint8Array(4);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 4; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Look up or create a stable instance ID in sessionStorage.\n */\nfunction resolveStableId(storageKey: string): string {\n if (typeof window !== \"undefined\") {\n try {\n const stored = sessionStorage.getItem(storageKey);\n if (stored) return stored;\n } catch {}\n }\n const id = `inst_${shortId()}`;\n if (typeof window !== \"undefined\") {\n try {\n sessionStorage.setItem(storageKey, id);\n } catch {}\n }\n return id;\n}\n\n// ── Fallback: render-wave slot counter ─────────────────────────────────────\n// Each synchronous render batch claims sequential slots per (experimentId,\n// pageKey). A microtask resets the counters so the next batch starts at 0,\n// giving the same component position the same slot across mounts.\n\nconst slotCounters = new Map<string, number>();\nlet resetScheduled = false;\n\nfunction claimSlot(groupKey: string): number {\n const idx = slotCounters.get(groupKey) ?? 0;\n slotCounters.set(groupKey, idx + 1);\n if (!resetScheduled) {\n resetScheduled = true;\n Promise.resolve().then(() => {\n slotCounters.clear();\n resetScheduled = false;\n });\n }\n return idx;\n}\n\n// ── Hook: React 18+ path (useId available) ─────────────────────────────────\n\nfunction useStableInstanceIdV18(experimentId: string): string {\n const reactId = (React as any).useId() as string;\n const ref = useRef(\"\");\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${reactId}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Hook: fallback path (no useId) ─────────────────────────────────────────\n\nfunction useStableInstanceIdFallback(experimentId: string): string {\n const slotRef = useRef(-1);\n const ref = useRef(\"\");\n if (slotRef.current === -1) {\n slotRef.current = claimSlot(`${experimentId}:${getPageKey()}`);\n }\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${slotRef.current}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Exported hook ──────────────────────────────────────────────────────────\n// Selection is a module-level constant so the hook-call count never changes\n// between renders — safe for the rules of hooks.\n\nexport const useStableInstanceId: (experimentId: string) => string =\n typeof (React as any).useId === \"function\"\n ? useStableInstanceIdV18\n : useStableInstanceIdFallback;\n\n// ── Test utility ───────────────────────────────────────────────────────────\n\nexport function resetInstanceIdState(): void {\n slotCounters.clear();\n resetScheduled = false;\n}\n","\"use client\";\n\nimport React, {\n useEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n} from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { fetchDecision, sendMetric, extractClickMeta } from \"../utils/api\";\nimport { getDistinctId, getPageKey } from \"../utils/eventContext\";\nimport { makeDedupeKey, hasSeen, markSeen } from \"../utils/dedupeStorage\";\nimport { useStableInstanceId } from \"../utils/stableInstanceId\";\n\n// ── localStorage assignment cache ──────────────────────────────────────────\n\nconst ASSIGNMENT_PREFIX = \"probat:assignment:\";\n\ninterface StoredAssignment {\n variantKey: string;\n ts: number;\n}\n\nfunction readAssignment(id: string): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(ASSIGNMENT_PREFIX + id);\n if (!raw) return null;\n const parsed: StoredAssignment = JSON.parse(raw);\n return parsed.variantKey ?? null;\n } catch {\n return null;\n }\n}\n\nfunction writeAssignment(id: string, variantKey: string): void {\n if (typeof window === \"undefined\") return;\n try {\n const entry: StoredAssignment = { variantKey, ts: Date.now() };\n localStorage.setItem(ASSIGNMENT_PREFIX + id, JSON.stringify(entry));\n } catch {}\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ExperimentTrackOptions {\n /** Auto-track impressions (default true) */\n impression?: boolean;\n /** Auto-track clicks (default true) */\n primaryClick?: boolean;\n /** Custom impression event name (default \"$experiment_exposure\") */\n impressionEventName?: string;\n /** Custom click event name (default \"$experiment_click\") */\n clickEventName?: string;\n}\n\nexport interface ExperimentProps {\n /** Experiment key / identifier */\n id: string;\n /** Control variant ReactNode */\n control: React.ReactNode;\n /** Named variant ReactNodes, keyed by variant key (e.g. { ai_v1: <MyVariant /> }) */\n variants: Record<string, React.ReactNode>;\n /** Tracking configuration */\n track?: ExperimentTrackOptions;\n /** Stable instance id when multiple instances of the same experiment exist on a page */\n componentInstanceId?: string;\n /** Behavior when assignment fetch fails: \"control\" (default) renders control, \"suspend\" throws */\n fallback?: \"control\" | \"suspend\";\n /** Log decisions + events to console */\n debug?: boolean;\n}\n\nexport function Experiment({\n id,\n control,\n variants,\n track,\n componentInstanceId,\n fallback = \"control\",\n debug = false,\n}: ExperimentProps) {\n const { host, bootstrap } = useProbatContext();\n\n // Stable instance id (useId + sessionStorage for cross-mount stability)\n const autoInstanceId = useStableInstanceId(id);\n const instanceId = componentInstanceId ?? autoInstanceId;\n\n // Track options with defaults\n const trackImpression = track?.impression !== false;\n const trackClick = track?.primaryClick !== false;\n const impressionEvent = track?.impressionEventName ?? \"$experiment_exposure\";\n const clickEvent = track?.clickEventName ?? \"$experiment_click\";\n\n // ── Assignment resolution ──────────────────────────────────────────────\n\n const [variantKey, setVariantKey] = useState<string>(() => {\n // Sync resolution order: bootstrap → localStorage → \"control\"\n if (bootstrap[id]) return bootstrap[id];\n const cached = readAssignment(id);\n if (cached) return cached;\n return \"control\";\n });\n const [resolved, setResolved] = useState<boolean>(() => {\n return !!(bootstrap[id] || readAssignment(id));\n });\n\n useEffect(() => {\n // Already resolved from bootstrap or cache\n if (bootstrap[id] || readAssignment(id)) {\n // Ensure state is synced (StrictMode may re-mount)\n const key = bootstrap[id] ?? readAssignment(id) ?? \"control\";\n setVariantKey(key);\n setResolved(true);\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n try {\n const distinctId = getDistinctId();\n const key = await fetchDecision(host, id, distinctId);\n if (cancelled) return;\n\n // Validate variant key\n if (key !== \"control\" && !(key in variants)) {\n if (debug) {\n console.warn(\n `[probat] Unknown variant \"${key}\" for experiment \"${id}\", falling back to control`\n );\n }\n setVariantKey(\"control\");\n } else {\n setVariantKey(key);\n writeAssignment(id, key);\n }\n } catch (err) {\n if (cancelled) return;\n if (debug) {\n console.error(`[probat] fetchDecision failed for \"${id}\":`, err);\n }\n if (fallback === \"suspend\") throw err;\n setVariantKey(\"control\");\n } finally {\n if (!cancelled) setResolved(true);\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [id, host]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // ── Debug logging ──────────────────────────────────────────────────────\n\n useEffect(() => {\n if (debug && resolved) {\n console.log(`[probat] Experiment \"${id}\" → variant \"${variantKey}\"`, {\n instanceId,\n pageKey: getPageKey(),\n });\n }\n }, [debug, id, variantKey, resolved, instanceId]);\n\n // ── Shared event properties ────────────────────────────────────────────\n\n const eventProps = useMemo(\n () => ({\n experiment_id: id,\n variant_key: variantKey,\n component_instance_id: instanceId,\n }),\n [id, variantKey, instanceId]\n );\n\n // ── Impression tracking via IntersectionObserver ────────────────────────\n\n const containerRef = useRef<HTMLDivElement>(null);\n const impressionSent = useRef(false);\n\n useEffect(() => {\n if (!trackImpression || !resolved) return;\n\n // Reset on re-mount (StrictMode safety)\n impressionSent.current = false;\n\n const pageKey = getPageKey();\n const dedupeKey = makeDedupeKey(id, variantKey, instanceId, pageKey);\n\n // Already seen this session\n if (hasSeen(dedupeKey)) {\n impressionSent.current = true;\n return;\n }\n\n const el = containerRef.current;\n if (!el) return;\n\n // Fallback: no IntersectionObserver (SSR, old browser)\n if (typeof IntersectionObserver === \"undefined\") {\n if (!impressionSent.current) {\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent (no IO) for \"${id}\"`);\n }\n return;\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry || impressionSent.current) return;\n\n if (entry.isIntersecting) {\n timer = setTimeout(() => {\n if (impressionSent.current) return;\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent for \"${id}\"`);\n observer.disconnect();\n }, 250);\n } else if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n },\n { threshold: 0.5 }\n );\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n if (timer) clearTimeout(timer);\n };\n }, [\n trackImpression,\n resolved,\n id,\n variantKey,\n instanceId,\n host,\n impressionEvent,\n eventProps,\n debug,\n ]);\n\n // ── Click tracking ─────────────────────────────────────────────────────\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n if (!trackClick) return;\n\n const meta = extractClickMeta(e.target as EventTarget);\n if (!meta) return;\n\n sendMetric(host, clickEvent, {\n ...eventProps,\n ...meta,\n });\n if (debug) {\n console.log(`[probat] Click tracked for \"${id}\"`, meta);\n }\n },\n [trackClick, host, clickEvent, eventProps, id, debug]\n );\n\n // ── Render ─────────────────────────────────────────────────────────────\n\n const content =\n variantKey === \"control\" || !(variantKey in variants)\n ? control\n : variants[variantKey];\n\n return (\n <div\n ref={containerRef}\n onClick={handleClick}\n data-probat-experiment={id}\n data-probat-variant={variantKey}\n style={{ display: \"contents\" }}\n >\n {content}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { sendMetric } from \"../utils/api\";\n\nexport interface UseProbatMetricsReturn {\n /**\n * Send a custom event with arbitrary properties.\n * Never throws — failures are silently dropped.\n *\n * @example\n * ```tsx\n * const { capture } = useProbatMetrics();\n * capture(\"purchase\", { revenue: 42, currency: \"USD\" });\n * ```\n */\n capture: (event: string, properties?: Record<string, unknown>) => void;\n}\n\n/**\n * Minimal metrics hook. Provides a single `capture(event, props)` function\n * that sends events to the Probat backend using the provider's host config.\n */\nexport function useProbatMetrics(): UseProbatMetricsReturn {\n const { host } = useProbatContext();\n\n const capture = useCallback(\n (event: string, properties: Record<string, unknown> = {}) => {\n sendMetric(host, event, properties);\n },\n [host]\n );\n\n return { capture };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context/ProbatContext.tsx","../src/components/ProbatProviderClient.tsx","../src/utils/environment.ts","../src/utils/eventContext.ts","../src/utils/api.ts","../src/utils/dedupeStorage.ts","../src/utils/stableInstanceId.ts","../src/components/Experiment.tsx","../src/hooks/useProbatMetrics.ts"],"names":["React","useMemo","useRef","useCallback"],"mappings":";;AAUA,IAAM,aAAA,GAAgB,cAAyC,IAAI,CAAA;AAEnE,IAAM,YAAA,GAAe,4BAAA;AAgBd,SAAS,cAAA,CAAe;AAAA,EAC3B,MAAA;AAAA,EACA,IAAA,GAAO,YAAA;AAAA,EACP,SAAA;AAAA,EACA;AACJ,CAAA,EAAwB;AACpB,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACV,OAAO;AAAA,MACH,IAAA,EAAM,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAAA,MAC5B,MAAA;AAAA,MACA,SAAA,EAAW,aAAa;AAAC,KAC7B,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,IAAA,EAAM,SAAS;AAAA,GAC5B;AAEA,EAAA,uBACIA,MAAA,CAAA,aAAA,CAAC,aAAA,CAAc,QAAA,EAAd,EAAuB,SACnB,QACL,CAAA;AAER;AAEO,SAAS,gBAAA,GAAuC;AACnD,EAAA,MAAM,GAAA,GAAM,WAAW,aAAa,CAAA;AACpC,EAAA,IAAI,CAAC,GAAA,EAAK;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACN;AAAA,KACJ;AAAA,EACJ;AACA,EAAA,OAAO,GAAA;AACX;;;ACjCO,SAAS,qBAAqB,KAAA,EAA4B;AAC7D,EAAA,OAAOA,MAAAA,CAAM,aAAA,CAAc,cAAA,EAAgB,KAAK,CAAA;AACpD;;;ACvBO,SAAS,iBAAA,GAAoC;AAChD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,QAAA,GAAW,OAAO,QAAA,CAAS,QAAA;AAGjC,EAAA,IACI,aAAa,WAAA,IACb,QAAA,KAAa,WAAA,IACb,QAAA,KAAa,aACb,QAAA,CAAS,UAAA,CAAW,UAAU,CAAA,IAC9B,SAAS,UAAA,CAAW,KAAK,CAAA,IACzB,QAAA,CAAS,WAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,SAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,WAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,SAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,WAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,IAC7B,SAAS,UAAA,CAAW,SAAS,KAC7B,QAAA,CAAS,UAAA,CAAW,SAAS,CAAA,EAC/B;AACE,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,OAAO,MAAA;AACX;;;AClCA,IAAM,eAAA,GAAkB,oBAAA;AACxB,IAAM,cAAA,GAAiB,mBAAA;AAEvB,IAAI,gBAAA,GAAkC,IAAA;AACtC,IAAI,eAAA,GAAiC,IAAA;AAErC,SAAS,UAAA,GAAqB;AAE1B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,UAAA,EAAY;AACpD,IAAA,OAAO,OAAO,UAAA,EAAW;AAAA,EAC7B;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,EAAE,CAAA;AAC/B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,eAAA,EAAiB;AACzD,IAAA,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAAA,EAChC,CAAA,MAAO;AACH,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,EAC1E;AACA,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AAC5E;AAEO,SAAS,aAAA,GAAwB;AACpC,EAAA,IAAI,kBAAkB,OAAO,gBAAA;AAC7B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,QAAA;AAC1C,EAAA,IAAI;AACA,IAAA,MAAM,MAAA,GAAS,YAAA,CAAa,OAAA,CAAQ,eAAe,CAAA;AACnD,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,gBAAA,GAAmB,MAAA;AACnB,MAAA,OAAO,MAAA;AAAA,IACX;AAAA,EACJ,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,UAAA,EAAY,CAAA,CAAA;AAC/B,EAAA,gBAAA,GAAmB,EAAA;AACnB,EAAA,IAAI;AACA,IAAA,YAAA,CAAa,OAAA,CAAQ,iBAAiB,EAAE,CAAA;AAAA,EAC5C,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,OAAO,EAAA;AACX;AAEO,SAAS,YAAA,GAAuB;AACnC,EAAA,IAAI,iBAAiB,OAAO,eAAA;AAC5B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,QAAA;AAC1C,EAAA,IAAI;AACA,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,OAAA,CAAQ,cAAc,CAAA;AACpD,IAAA,IAAI,MAAA,EAAQ;AACR,MAAA,eAAA,GAAkB,MAAA;AAClB,MAAA,OAAO,MAAA;AAAA,IACX;AAAA,EACJ,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,UAAA,EAAY,CAAA,CAAA;AAC/B,EAAA,eAAA,GAAkB,EAAA;AAClB,EAAA,IAAI;AACA,IAAA,cAAA,CAAe,OAAA,CAAQ,gBAAgB,EAAE,CAAA;AAAA,EAC7C,CAAA,CAAA,MAAQ;AAAA,EAAC;AACT,EAAA,OAAO,EAAA;AACX;AAEO,SAAS,UAAA,GAAqB;AACjC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAA;AAC1C,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,QAAA,GAAW,MAAA,CAAO,QAAA,CAAS,MAAA;AACtD;AAEO,SAAS,UAAA,GAAqB;AACjC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAA;AAC1C,EAAA,OAAO,OAAO,QAAA,CAAS,IAAA;AAC3B;AAEO,SAAS,WAAA,GAAsB;AAClC,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,EAAA;AAC5C,EAAA,OAAO,QAAA,CAAS,QAAA;AACpB;AAUO,SAAS,iBAAA,GAAkC;AAC9C,EAAA,OAAO;AAAA,IACH,aAAa,aAAA,EAAc;AAAA,IAC3B,YAAY,YAAA,EAAa;AAAA,IACzB,WAAW,UAAA,EAAW;AAAA,IACtB,WAAW,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,QAAA,GAAW,EAAA;AAAA,IACtE,WAAW,WAAA;AAAY,GAC3B;AACJ;;;AC7EA,IAAM,gBAAA,uBAAuB,GAAA,EAA6B;AAO1D,eAAsB,aAAA,CAClB,IAAA,EACA,YAAA,EACA,UAAA,EACe;AACf,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,GAAA,CAAI,YAAY,CAAA;AAClD,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,MAAM,WAAW,YAAY;AACzB,IAAA,IAAI;AACA,MAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,kBAAA,CAAA;AACtC,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,QACzB,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACL,cAAA,EAAgB,kBAAA;AAAA,UAChB,MAAA,EAAQ;AAAA,SACZ;AAAA,QACA,WAAA,EAAa,SAAA;AAAA,QACb,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,UACjB,aAAA,EAAe,YAAA;AAAA,UACf,WAAA,EAAa;AAAA,SAChB;AAAA,OACJ,CAAA;AACD,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AACjD,MAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,MAAA,OAAO,KAAK,WAAA,IAAe,SAAA;AAAA,IAC/B,CAAA,SAAE;AACE,MAAA,gBAAA,CAAiB,OAAO,YAAY,CAAA;AAAA,IACxC;AAAA,EACJ,CAAA,GAAG;AAEH,EAAA,gBAAA,CAAiB,GAAA,CAAI,cAAc,OAAO,CAAA;AAC1C,EAAA,OAAO,OAAA;AACX;AAOO,SAAS,UAAA,CACZ,IAAA,EACA,KAAA,EACA,UAAA,EACI;AACJ,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEnC,EAAA,MAAM,MAAM,iBAAA,EAAkB;AAC9B,EAAA,MAAM,OAAA,GAAyB;AAAA,IAC3B,KAAA;AAAA,IACA,UAAA,EAAY;AAAA,MACR,GAAG,GAAA;AAAA,MACH,aAAa,iBAAA,EAAkB;AAAA,MAC/B,MAAA,EAAQ,WAAA;AAAA,MACR,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MACpC,GAAG;AAAA;AACP,GACJ;AAEA,EAAA,IAAI;AACA,IAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,mBAAA,CAAA;AACtC,IAAA,KAAA,CAAM,GAAA,EAAK;AAAA,MACP,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,WAAA,EAAa,SAAA;AAAA,MACb,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,KAC/B,CAAA,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AAAA,EAER;AACJ;AAgBO,SAAS,iBAAiB,MAAA,EAA8C;AAC3E,EAAA,IAAI,CAAC,MAAA,IAAU,EAAE,MAAA,YAAkB,cAAc,OAAO,IAAA;AAGxD,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,+BAA+B,CAAA;AAC9D,EAAA,IAAI,OAAA,EAAS,OAAO,SAAA,CAAU,OAAA,EAAwB,IAAI,CAAA;AAG1D,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,OAAA,CAAQ,4BAA4B,CAAA;AAC/D,EAAA,IAAI,WAAA,EAAa,OAAO,SAAA,CAAU,WAAA,EAA4B,KAAK,CAAA;AAEnE,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,SAAA,CAAU,IAAiB,SAAA,EAA+B;AAC/D,EAAA,MAAM,IAAA,GAAkB;AAAA,IACpB,kBAAkB,EAAA,CAAG,OAAA;AAAA,IACrB,gBAAA,EAAkB;AAAA,GACtB;AACA,EAAA,IAAI,EAAA,CAAG,EAAA,EAAI,IAAA,CAAK,eAAA,GAAkB,EAAA,CAAG,EAAA;AACrC,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,WAAA,EAAa,IAAA,EAAK;AAClC,EAAA,IAAI,MAAM,IAAA,CAAK,iBAAA,GAAoB,IAAA,CAAK,KAAA,CAAM,GAAG,GAAG,CAAA;AACpD,EAAA,OAAO,IAAA;AACX;;;AC9HA,IAAM,MAAA,GAAS,cAAA;AACf,IAAM,SAAA,uBAAgB,GAAA,EAAY;AAE3B,SAAS,aAAA,CACZ,YAAA,EACA,UAAA,EACA,UAAA,EACA,OAAA,EACM;AACN,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,YAAY,IAAI,UAAU,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AAC1E;AAEO,SAAS,QAAQ,GAAA,EAAsB;AAC1C,EAAA,IAAI,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA,EAAG,OAAO,IAAA;AAC/B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,EAAA,IAAI;AACA,IAAA,OAAO,cAAA,CAAe,OAAA,CAAQ,GAAG,CAAA,KAAM,GAAA;AAAA,EAC3C,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,KAAA;AAAA,EACX;AACJ;AAEO,SAAS,SAAS,GAAA,EAAmB;AACxC,EAAA,SAAA,CAAU,IAAI,GAAG,CAAA;AACjB,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,EAAA,IAAI;AACA,IAAA,cAAA,CAAe,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,EACnC,CAAA,CAAA,MAAQ;AAAA,EAAC;AACb;ACjBA,IAAM,eAAA,GAAkB,kBAAA;AAIxB,SAAS,OAAA,GAAkB;AACvB,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,CAAC,CAAA;AAC9B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,eAAA,EAAiB;AACzD,IAAA,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAAA,EAChC,CAAA,MAAO;AACH,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,EACzE;AACA,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAO,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AAC5E;AAKA,SAAS,gBAAgB,UAAA,EAA4B;AACjD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,IAAI;AACA,MAAA,MAAM,MAAA,GAAS,cAAA,CAAe,OAAA,CAAQ,UAAU,CAAA;AAChD,MAAA,IAAI,QAAQ,OAAO,MAAA;AAAA,IACvB,CAAA,CAAA,MAAQ;AAAA,IAAC;AAAA,EACb;AACA,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,OAAA,EAAS,CAAA,CAAA;AAC5B,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,IAAI;AACA,MAAA,cAAA,CAAe,OAAA,CAAQ,YAAY,EAAE,CAAA;AAAA,IACzC,CAAA,CAAA,MAAQ;AAAA,IAAC;AAAA,EACb;AACA,EAAA,OAAO,EAAA;AACX;AAOA,IAAM,YAAA,uBAAmB,GAAA,EAAoB;AAC7C,IAAI,cAAA,GAAiB,KAAA;AAErB,SAAS,UAAU,QAAA,EAA0B;AACzC,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA,IAAK,CAAA;AAC1C,EAAA,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,GAAA,GAAM,CAAC,CAAA;AAClC,EAAA,IAAI,CAAC,cAAA,EAAgB;AACjB,IAAA,cAAA,GAAiB,IAAA;AACjB,IAAA,OAAA,CAAQ,OAAA,EAAQ,CAAE,IAAA,CAAK,MAAM;AACzB,MAAA,YAAA,CAAa,KAAA,EAAM;AACnB,MAAA,cAAA,GAAiB,KAAA;AAAA,IACrB,CAAC,CAAA;AAAA,EACL;AACA,EAAA,OAAO,GAAA;AACX;AAIA,SAAS,uBAAuB,YAAA,EAA8B;AAC1D,EAAA,MAAM,OAAA,GAAWA,OAAc,KAAA,EAAM;AACrC,EAAA,MAAM,GAAA,GAAM,OAAO,EAAE,CAAA;AACrB,EAAA,IAAI,CAAC,IAAI,OAAA,EAAS;AACd,IAAA,MAAM,GAAA,GAAM,GAAG,eAAe,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,UAAA,EAAY,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AACxE,IAAA,GAAA,CAAI,OAAA,GAAU,gBAAgB,GAAG,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,GAAA,CAAI,OAAA;AACf;AAIA,SAAS,4BAA4B,YAAA,EAA8B;AAC/D,EAAA,MAAM,OAAA,GAAU,OAAO,EAAE,CAAA;AACzB,EAAA,MAAM,GAAA,GAAM,OAAO,EAAE,CAAA;AACrB,EAAA,IAAI,OAAA,CAAQ,YAAY,EAAA,EAAI;AACxB,IAAA,OAAA,CAAQ,UAAU,SAAA,CAAU,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,UAAA,EAAY,CAAA,CAAE,CAAA;AAAA,EACjE;AACA,EAAA,IAAI,CAAC,IAAI,OAAA,EAAS;AACd,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,eAAe,CAAA,EAAG,YAAY,IAAI,UAAA,EAAY,CAAA,CAAA,EAAI,OAAA,CAAQ,OAAO,CAAA,CAAA;AAChF,IAAA,GAAA,CAAI,OAAA,GAAU,gBAAgB,GAAG,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,GAAA,CAAI,OAAA;AACf;AAMO,IAAM,mBAAA,GACT,OAAQA,MAAAA,CAAc,KAAA,KAAU,aAC1B,sBAAA,GACA,2BAAA;;;ACxFV,IAAM,iBAAA,GAAoB,oBAAA;AAO1B,SAAS,eAAe,EAAA,EAA2B;AAC/C,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,IAAA;AAC1C,EAAA,IAAI;AACA,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,CAAQ,iBAAA,GAAoB,EAAE,CAAA;AACvD,IAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,IAAA,MAAM,MAAA,GAA2B,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC/C,IAAA,OAAO,OAAO,UAAA,IAAc,IAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;AAEA,SAAS,eAAA,CAAgB,IAAY,UAAA,EAA0B;AAC3D,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,EAAA,IAAI;AACA,IAAA,MAAM,QAA0B,EAAE,UAAA,EAAY,EAAA,EAAI,IAAA,CAAK,KAAI,EAAE;AAC7D,IAAA,YAAA,CAAa,QAAQ,iBAAA,GAAoB,EAAA,EAAI,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,EACtE,CAAA,CAAA,MAAQ;AAAA,EAAC;AACb;AAgCO,SAAS,UAAA,CAAW;AAAA,EACvB,EAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,mBAAA;AAAA,EACA,QAAA,GAAW,SAAA;AAAA,EACX,KAAA,GAAQ;AACZ,CAAA,EAAoB;AAChB,EAAA,MAAM,EAAE,IAAA,EAAM,SAAA,EAAU,GAAI,gBAAA,EAAiB;AAG7C,EAAA,MAAM,cAAA,GAAiB,oBAAoB,EAAE,CAAA;AAC7C,EAAA,MAAM,aAAa,mBAAA,IAAuB,cAAA;AAG1C,EAAA,MAAM,eAAA,GAAkB,OAAO,UAAA,KAAe,KAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,OAAO,YAAA,KAAiB,KAAA;AAC3C,EAAA,MAAM,eAAA,GAAkB,OAAO,mBAAA,IAAuB,sBAAA;AACtD,EAAA,MAAM,UAAA,GAAa,OAAO,cAAA,IAAkB,mBAAA;AAI5C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAiB,MAAM;AAEvD,IAAA,IAAI,SAAA,CAAU,EAAE,CAAA,EAAG,OAAO,UAAU,EAAE,CAAA;AACtC,IAAA,MAAM,MAAA,GAAS,eAAe,EAAE,CAAA;AAChC,IAAA,IAAI,QAAQ,OAAO,MAAA;AACnB,IAAA,OAAO,SAAA;AAAA,EACX,CAAC,CAAA;AACD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAkB,MAAM;AACpD,IAAA,OAAO,CAAC,EAAE,SAAA,CAAU,EAAE,CAAA,IAAK,eAAe,EAAE,CAAA,CAAA;AAAA,EAChD,CAAC,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AAEZ,IAAA,IAAI,SAAA,CAAU,EAAE,CAAA,IAAK,cAAA,CAAe,EAAE,CAAA,EAAG;AAErC,MAAA,MAAM,MAAM,SAAA,CAAU,EAAE,CAAA,IAAK,cAAA,CAAe,EAAE,CAAA,IAAK,SAAA;AACnD,MAAA,aAAA,CAAc,GAAG,CAAA;AACjB,MAAA,WAAA,CAAY,IAAI,CAAA;AAChB,MAAA;AAAA,IACJ;AAEA,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,CAAC,YAAY;AACT,MAAA,IAAI;AACA,QAAA,MAAM,aAAa,aAAA,EAAc;AACjC,QAAA,MAAM,GAAA,GAAM,MAAM,aAAA,CAAc,IAAA,EAAM,IAAI,UAAU,CAAA;AACpD,QAAA,IAAI,SAAA,EAAW;AAGf,QAAA,IAAI,GAAA,KAAQ,SAAA,IAAa,EAAE,GAAA,IAAO,QAAA,CAAA,EAAW;AACzC,UAAA,IAAI,KAAA,EAAO;AACP,YAAA,OAAA,CAAQ,IAAA;AAAA,cACJ,CAAA,0BAAA,EAA6B,GAAG,CAAA,kBAAA,EAAqB,EAAE,CAAA,0BAAA;AAAA,aAC3D;AAAA,UACJ;AACA,UAAA,aAAA,CAAc,SAAS,CAAA;AAAA,QAC3B,CAAA,MAAO;AACH,UAAA,aAAA,CAAc,GAAG,CAAA;AACjB,UAAA,eAAA,CAAgB,IAAI,GAAG,CAAA;AAAA,QAC3B;AAAA,MACJ,SAAS,GAAA,EAAK;AACV,QAAA,IAAI,SAAA,EAAW;AACf,QAAA,IAAI,KAAA,EAAO;AACP,UAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,mCAAA,EAAsC,EAAE,CAAA,EAAA,CAAA,EAAM,GAAG,CAAA;AAAA,QACnE;AACA,QAAA,IAAI,QAAA,KAAa,WAAW,MAAM,GAAA;AAClC,QAAA,aAAA,CAAc,SAAS,CAAA;AAAA,MAC3B,CAAA,SAAE;AACE,QAAA,IAAI,CAAC,SAAA,EAAW,WAAA,CAAY,IAAI,CAAA;AAAA,MACpC;AAAA,IACJ,CAAA,GAAG;AAEH,IAAA,OAAO,MAAM;AACT,MAAA,SAAA,GAAY,IAAA;AAAA,IAChB,CAAA;AAAA,EACJ,CAAA,EAAG,CAAC,EAAA,EAAI,IAAI,CAAC,CAAA;AAIb,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,SAAS,QAAA,EAAU;AACnB,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,qBAAA,EAAwB,EAAE,CAAA,kBAAA,EAAgB,UAAU,CAAA,CAAA,CAAA,EAAK;AAAA,QACjE,UAAA;AAAA,QACA,SAAS,UAAA;AAAW,OACvB,CAAA;AAAA,IACL;AAAA,EACJ,GAAG,CAAC,KAAA,EAAO,IAAI,UAAA,EAAY,QAAA,EAAU,UAAU,CAAC,CAAA;AAIhD,EAAA,MAAM,UAAA,GAAaC,OAAAA;AAAA,IACf,OAAO;AAAA,MACH,aAAA,EAAe,EAAA;AAAA,MACf,WAAA,EAAa,UAAA;AAAA,MACb,qBAAA,EAAuB;AAAA,KAC3B,CAAA;AAAA,IACA,CAAC,EAAA,EAAI,UAAA,EAAY,UAAU;AAAA,GAC/B;AAIA,EAAA,MAAM,YAAA,GAAeC,OAAuB,IAAI,CAAA;AAChD,EAAA,MAAM,cAAA,GAAiBA,OAAO,KAAK,CAAA;AAEnC,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,CAAC,eAAA,IAAmB,CAAC,QAAA,EAAU;AAGnC,IAAA,cAAA,CAAe,OAAA,GAAU,KAAA;AAEzB,IAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,IAAA,MAAM,SAAA,GAAY,aAAA,CAAc,EAAA,EAAI,UAAA,EAAY,YAAY,OAAO,CAAA;AAGnE,IAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACpB,MAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,KAAK,YAAA,CAAa,OAAA;AACxB,IAAA,IAAI,CAAC,EAAA,EAAI;AAGT,IAAA,IAAI,OAAO,yBAAyB,WAAA,EAAa;AAC7C,MAAA,IAAI,CAAC,eAAe,OAAA,EAAS;AACzB,QAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,QAAA,QAAA,CAAS,SAAS,CAAA;AAClB,QAAA,UAAA,CAAW,IAAA,EAAM,iBAAiB,UAAU,CAAA;AAC5C,QAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,sCAAA,EAAyC,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA,MACzE;AACA,MAAA;AAAA,IACJ;AAEA,IAAA,IAAI,KAAA,GAA8C,IAAA;AAElD,IAAA,MAAM,WAAW,IAAI,oBAAA;AAAA,MACjB,CAAC,CAAC,KAAK,CAAA,KAAM;AACT,QAAA,IAAI,CAAC,KAAA,IAAS,cAAA,CAAe,OAAA,EAAS;AAEtC,QAAA,IAAI,MAAM,cAAA,EAAgB;AACtB,UAAA,KAAA,GAAQ,WAAW,MAAM;AACrB,YAAA,IAAI,eAAe,OAAA,EAAS;AAC5B,YAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,YAAA,QAAA,CAAS,SAAS,CAAA;AAClB,YAAA,UAAA,CAAW,IAAA,EAAM,iBAAiB,UAAU,CAAA;AAC5C,YAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,CAAA,8BAAA,EAAiC,EAAE,CAAA,CAAA,CAAG,CAAA;AAC7D,YAAA,QAAA,CAAS,UAAA,EAAW;AAAA,UACxB,GAAG,GAAG,CAAA;AAAA,QACV,WAAW,KAAA,EAAO;AACd,UAAA,YAAA,CAAa,KAAK,CAAA;AAClB,UAAA,KAAA,GAAQ,IAAA;AAAA,QACZ;AAAA,MACJ,CAAA;AAAA,MACA,EAAE,WAAW,GAAA;AAAI,KACrB;AAEA,IAAA,QAAA,CAAS,QAAQ,EAAE,CAAA;AAEnB,IAAA,OAAO,MAAM;AACT,MAAA,QAAA,CAAS,UAAA,EAAW;AACpB,MAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAAA,IACjC,CAAA;AAAA,EACJ,CAAA,EAAG;AAAA,IACC,eAAA;AAAA,IACA,QAAA;AAAA,IACA,EAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,IAAA;AAAA,IACA,eAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACH,CAAA;AAID,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAChB,CAAC,CAAA,KAAwB;AACrB,MAAA,IAAI,CAAC,UAAA,EAAY;AAEjB,MAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,CAAA,CAAE,MAAqB,CAAA;AACrD,MAAA,IAAI,CAAC,IAAA,EAAM;AAEX,MAAA,UAAA,CAAW,MAAM,UAAA,EAAY;AAAA,QACzB,GAAG,UAAA;AAAA,QACH,GAAG;AAAA,OACN,CAAA;AACD,MAAA,IAAI,KAAA,EAAO;AACP,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,4BAAA,EAA+B,EAAE,CAAA,CAAA,CAAA,EAAK,IAAI,CAAA;AAAA,MAC1D;AAAA,IACJ,CAAA;AAAA,IACA,CAAC,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,UAAA,EAAY,IAAI,KAAK;AAAA,GACxD;AAIA,EAAA,MAAM,OAAA,GACF,eAAe,SAAA,IAAa,EAAE,cAAc,QAAA,CAAA,GACtC,OAAA,GACA,SAAS,UAAU,CAAA;AAE7B,EAAA,uBACIF,MAAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACG,GAAA,EAAK,YAAA;AAAA,MACL,OAAA,EAAS,WAAA;AAAA,MACT,wBAAA,EAAwB,EAAA;AAAA,MACxB,qBAAA,EAAqB,UAAA;AAAA,MACrB,OAAO,EAAE,OAAA,EAAS,SAAS,MAAA,EAAQ,CAAA,EAAG,SAAS,CAAA;AAAE,KAAA;AAAA,IAEhD;AAAA,GACL;AAER;AC1QO,SAAS,gBAAA,GAA2C;AACvD,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,gBAAA,EAAiB;AAElC,EAAA,MAAM,OAAA,GAAUG,WAAAA;AAAA,IACZ,CAAC,KAAA,EAAe,UAAA,GAAsC,EAAC,KAAM;AACzD,MAAA,UAAA,CAAW,IAAA,EAAM,OAAO,UAAU,CAAA;AAAA,IACtC,CAAA;AAAA,IACA,CAAC,IAAI;AAAA,GACT;AAEA,EAAA,OAAO,EAAE,OAAA,EAAQ;AACrB","file":"index.mjs","sourcesContent":["\"use client\";\n\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ProbatContextValue {\n host: string;\n userId: string;\n bootstrap: Record<string, string>;\n}\n\nconst ProbatContext = createContext<ProbatContextValue | null>(null);\n\nconst DEFAULT_HOST = \"https://gushi.onrender.com\";\n\nexport interface ProbatProviderProps {\n /** Gushi user ID (UUID) — used to scope SDK requests to a customer */\n userId: string;\n /** Base URL for the Probat API. Defaults to https://gushi.onrender.com */\n host?: string;\n /**\n * Bootstrap assignments to avoid flash on first render.\n * Map of experiment id → variant key.\n * e.g. { \"cta-copy-test\": \"ai_v1\" }\n */\n bootstrap?: Record<string, string>;\n children: React.ReactNode;\n}\n\nexport function ProbatProvider({\n userId,\n host = DEFAULT_HOST,\n bootstrap,\n children,\n}: ProbatProviderProps) {\n const value = useMemo<ProbatContextValue>(\n () => ({\n host: host.replace(/\\/$/, \"\"),\n userId,\n bootstrap: bootstrap ?? {},\n }),\n [userId, host, bootstrap]\n );\n\n return (\n <ProbatContext.Provider value={value}>\n {children}\n </ProbatContext.Provider>\n );\n}\n\nexport function useProbatContext(): ProbatContextValue {\n const ctx = useContext(ProbatContext);\n if (!ctx) {\n throw new Error(\n \"useProbatContext must be used within <ProbatProviderClient>. Wrap your app with <ProbatProviderClient userId={...}>.\"\n );\n }\n return ctx;\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { ProbatProvider } from \"../context/ProbatContext\";\nimport type { ProbatProviderProps } from \"../context/ProbatContext\";\n\n/**\n * Client-only provider for Next.js App Router.\n * Import this in your layout/providers file.\n *\n * @example\n * ```tsx\n * // app/providers.tsx\n * \"use client\";\n * import { ProbatProviderClient } from \"@probat/react\";\n *\n * export function Providers({ children }) {\n * return (\n * <ProbatProviderClient userId=\"your-user-uuid\">\n * {children}\n * </ProbatProviderClient>\n * );\n * }\n * ```\n */\nexport function ProbatProviderClient(props: ProbatProviderProps) {\n return React.createElement(ProbatProvider, props);\n}\n\nexport type { ProbatProviderProps };\n","/**\n * Detect if the code is running on localhost (development environment).\n * Returns \"dev\" for localhost, \"prod\" for production.\n */\nexport function detectEnvironment(): \"dev\" | \"prod\" {\n if (typeof window === \"undefined\") {\n return \"prod\"; // Server-side, default to prod\n }\n\n const hostname = window.location.hostname;\n\n // Check for localhost, 127.0.0.1, or local IP addresses\n if (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"0.0.0.0\" ||\n hostname.startsWith(\"192.168.\") ||\n hostname.startsWith(\"10.\") ||\n hostname.startsWith(\"172.16.\") ||\n hostname.startsWith(\"172.17.\") ||\n hostname.startsWith(\"172.18.\") ||\n hostname.startsWith(\"172.19.\") ||\n hostname.startsWith(\"172.20.\") ||\n hostname.startsWith(\"172.21.\") ||\n hostname.startsWith(\"172.22.\") ||\n hostname.startsWith(\"172.23.\") ||\n hostname.startsWith(\"172.24.\") ||\n hostname.startsWith(\"172.25.\") ||\n hostname.startsWith(\"172.26.\") ||\n hostname.startsWith(\"172.27.\") ||\n hostname.startsWith(\"172.28.\") ||\n hostname.startsWith(\"172.29.\") ||\n hostname.startsWith(\"172.30.\") ||\n hostname.startsWith(\"172.31.\")\n ) {\n return \"dev\";\n }\n\n return \"prod\";\n}\n\n","/**\n * Event context helpers: distinct_id, session_id, page info.\n * All browser-safe — no-ops when window is unavailable.\n */\n\nconst DISTINCT_ID_KEY = \"probat:distinct_id\";\nconst SESSION_ID_KEY = \"probat:session_id\";\n\nlet cachedDistinctId: string | null = null;\nlet cachedSessionId: string | null = null;\n\nfunction generateId(): string {\n // crypto.randomUUID where available, else fallback\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // fallback: random hex\n const bytes = new Uint8Array(16);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function getDistinctId(): string {\n if (cachedDistinctId) return cachedDistinctId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = localStorage.getItem(DISTINCT_ID_KEY);\n if (stored) {\n cachedDistinctId = stored;\n return stored;\n }\n } catch {}\n const id = `anon_${generateId()}`;\n cachedDistinctId = id;\n try {\n localStorage.setItem(DISTINCT_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getSessionId(): string {\n if (cachedSessionId) return cachedSessionId;\n if (typeof window === \"undefined\") return \"server\";\n try {\n const stored = sessionStorage.getItem(SESSION_ID_KEY);\n if (stored) {\n cachedSessionId = stored;\n return stored;\n }\n } catch {}\n const id = `sess_${generateId()}`;\n cachedSessionId = id;\n try {\n sessionStorage.setItem(SESSION_ID_KEY, id);\n } catch {}\n return id;\n}\n\nexport function getPageKey(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.pathname + window.location.search;\n}\n\nexport function getPageUrl(): string {\n if (typeof window === \"undefined\") return \"\";\n return window.location.href;\n}\n\nexport function getReferrer(): string {\n if (typeof document === \"undefined\") return \"\";\n return document.referrer;\n}\n\nexport interface EventContext {\n distinct_id: string;\n session_id: string;\n $page_url: string;\n $pathname: string;\n $referrer: string;\n}\n\nexport function buildEventContext(): EventContext {\n return {\n distinct_id: getDistinctId(),\n session_id: getSessionId(),\n $page_url: getPageUrl(),\n $pathname: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n $referrer: getReferrer(),\n };\n}\n","import { detectEnvironment } from \"./environment\";\nimport { buildEventContext } from \"./eventContext\";\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface DecisionResponse {\n variant_key: string;\n}\n\nexport interface MetricPayload {\n event: string;\n properties: Record<string, unknown>;\n}\n\n// ── Assignment fetching ────────────────────────────────────────────────────\n\nconst pendingDecisions = new Map<string, Promise<string>>();\n\n/**\n * Fetch the variant assignment for an experiment.\n * Returns the variant key string (e.g. \"control\", \"ai_v1\").\n * Deduplicates concurrent calls for the same experiment.\n */\nexport async function fetchDecision(\n host: string,\n experimentId: string,\n distinctId: string\n): Promise<string> {\n const existing = pendingDecisions.get(experimentId);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/decide`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify({\n experiment_id: experimentId,\n distinct_id: distinctId,\n }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as DecisionResponse;\n return data.variant_key || \"control\";\n } finally {\n pendingDecisions.delete(experimentId);\n }\n })();\n\n pendingDecisions.set(experimentId, promise);\n return promise;\n}\n\n// ── Metric sending ─────────────────────────────────────────────────────────\n\n/**\n * Fire-and-forget metric send. Never throws.\n */\nexport function sendMetric(\n host: string,\n event: string,\n properties: Record<string, unknown>\n): void {\n if (typeof window === \"undefined\") return;\n\n const ctx = buildEventContext();\n const payload: MetricPayload = {\n event,\n properties: {\n ...ctx,\n environment: detectEnvironment(),\n source: \"react-sdk\",\n captured_at: new Date().toISOString(),\n ...properties,\n },\n };\n\n try {\n const url = `${host.replace(/\\/$/, \"\")}/experiment/metrics`;\n fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"include\",\n body: JSON.stringify(payload),\n }).catch(() => {});\n } catch {\n // silently drop\n }\n}\n\n// ── Click metadata extraction ──────────────────────────────────────────────\n\nexport interface ClickMeta {\n click_target_tag: string;\n click_target_text?: string;\n click_target_id?: string;\n click_is_primary: boolean;\n}\n\n/**\n * Given a click event inside an experiment boundary, extract metadata.\n * Prioritizes elements with data-probat-click=\"primary\",\n * then falls back to button/a/role=button.\n */\nexport function extractClickMeta(target: EventTarget | null): ClickMeta | null {\n if (!target || !(target instanceof HTMLElement)) return null;\n\n // Priority 1: explicit primary marker\n const primary = target.closest('[data-probat-click=\"primary\"]');\n if (primary) return buildMeta(primary as HTMLElement, true);\n\n // Priority 2: interactive elements\n const interactive = target.closest('button, a, [role=\"button\"]');\n if (interactive) return buildMeta(interactive as HTMLElement, false);\n\n return null;\n}\n\nfunction buildMeta(el: HTMLElement, isPrimary: boolean): ClickMeta {\n const meta: ClickMeta = {\n click_target_tag: el.tagName,\n click_is_primary: isPrimary,\n };\n if (el.id) meta.click_target_id = el.id;\n const text = el.textContent?.trim();\n if (text) meta.click_target_text = text.slice(0, 120);\n return meta;\n}\n","/**\n * Dedupe storage for experiment exposures.\n * Uses sessionStorage + in-memory Set fallback.\n * Key format: probat:seen:{id}:{variantKey}:{instanceId}:{pageKey}\n */\n\nconst PREFIX = \"probat:seen:\";\nconst memorySet = new Set<string>();\n\nexport function makeDedupeKey(\n experimentId: string,\n variantKey: string,\n instanceId: string,\n pageKey: string\n): string {\n return `${PREFIX}${experimentId}:${variantKey}:${instanceId}:${pageKey}`;\n}\n\nexport function hasSeen(key: string): boolean {\n if (memorySet.has(key)) return true;\n if (typeof window === \"undefined\") return false;\n try {\n return sessionStorage.getItem(key) === \"1\";\n } catch {\n return false;\n }\n}\n\nexport function markSeen(key: string): void {\n memorySet.add(key);\n if (typeof window === \"undefined\") return;\n try {\n sessionStorage.setItem(key, \"1\");\n } catch {}\n}\n\n/** Reset all dedupe state — useful for testing. */\nexport function resetDedupe(): void {\n memorySet.clear();\n}\n","/**\n * Stable auto-generated instance IDs for <Experiment />.\n *\n * Problem: a naïve useRef + module counter gives a different ID on every mount,\n * so StrictMode double-mount or unmount/remount changes the dedupe key.\n *\n * Solution:\n * 1. React 18+ → useId() is stable per fiber position.\n * 2. Fallback → sessionStorage-backed slot counter per (experimentId, pageKey).\n * 3. Both paths persist a mapping in sessionStorage:\n * probat:instance:{experimentId}:{pageKey}:{positionKey} → stableId\n * so the same position resolves to the same ID across mounts.\n */\n\nimport React, { useRef } from \"react\";\nimport { getPageKey } from \"./eventContext\";\n\nconst INSTANCE_PREFIX = \"probat:instance:\";\n\n// ── Helpers ────────────────────────────────────────────────────────────────\n\nfunction shortId(): string {\n const bytes = new Uint8Array(4);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 4; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Look up or create a stable instance ID in sessionStorage.\n */\nfunction resolveStableId(storageKey: string): string {\n if (typeof window !== \"undefined\") {\n try {\n const stored = sessionStorage.getItem(storageKey);\n if (stored) return stored;\n } catch {}\n }\n const id = `inst_${shortId()}`;\n if (typeof window !== \"undefined\") {\n try {\n sessionStorage.setItem(storageKey, id);\n } catch {}\n }\n return id;\n}\n\n// ── Fallback: render-wave slot counter ─────────────────────────────────────\n// Each synchronous render batch claims sequential slots per (experimentId,\n// pageKey). A microtask resets the counters so the next batch starts at 0,\n// giving the same component position the same slot across mounts.\n\nconst slotCounters = new Map<string, number>();\nlet resetScheduled = false;\n\nfunction claimSlot(groupKey: string): number {\n const idx = slotCounters.get(groupKey) ?? 0;\n slotCounters.set(groupKey, idx + 1);\n if (!resetScheduled) {\n resetScheduled = true;\n Promise.resolve().then(() => {\n slotCounters.clear();\n resetScheduled = false;\n });\n }\n return idx;\n}\n\n// ── Hook: React 18+ path (useId available) ─────────────────────────────────\n\nfunction useStableInstanceIdV18(experimentId: string): string {\n const reactId = (React as any).useId() as string;\n const ref = useRef(\"\");\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${reactId}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Hook: fallback path (no useId) ─────────────────────────────────────────\n\nfunction useStableInstanceIdFallback(experimentId: string): string {\n const slotRef = useRef(-1);\n const ref = useRef(\"\");\n if (slotRef.current === -1) {\n slotRef.current = claimSlot(`${experimentId}:${getPageKey()}`);\n }\n if (!ref.current) {\n const key = `${INSTANCE_PREFIX}${experimentId}:${getPageKey()}:${slotRef.current}`;\n ref.current = resolveStableId(key);\n }\n return ref.current;\n}\n\n// ── Exported hook ──────────────────────────────────────────────────────────\n// Selection is a module-level constant so the hook-call count never changes\n// between renders — safe for the rules of hooks.\n\nexport const useStableInstanceId: (experimentId: string) => string =\n typeof (React as any).useId === \"function\"\n ? useStableInstanceIdV18\n : useStableInstanceIdFallback;\n\n// ── Test utility ───────────────────────────────────────────────────────────\n\nexport function resetInstanceIdState(): void {\n slotCounters.clear();\n resetScheduled = false;\n}\n","\"use client\";\n\nimport React, {\n useEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n} from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { fetchDecision, sendMetric, extractClickMeta } from \"../utils/api\";\nimport { getDistinctId, getPageKey } from \"../utils/eventContext\";\nimport { makeDedupeKey, hasSeen, markSeen } from \"../utils/dedupeStorage\";\nimport { useStableInstanceId } from \"../utils/stableInstanceId\";\n\n// ── localStorage assignment cache ──────────────────────────────────────────\n\nconst ASSIGNMENT_PREFIX = \"probat:assignment:\";\n\ninterface StoredAssignment {\n variantKey: string;\n ts: number;\n}\n\nfunction readAssignment(id: string): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = localStorage.getItem(ASSIGNMENT_PREFIX + id);\n if (!raw) return null;\n const parsed: StoredAssignment = JSON.parse(raw);\n return parsed.variantKey ?? null;\n } catch {\n return null;\n }\n}\n\nfunction writeAssignment(id: string, variantKey: string): void {\n if (typeof window === \"undefined\") return;\n try {\n const entry: StoredAssignment = { variantKey, ts: Date.now() };\n localStorage.setItem(ASSIGNMENT_PREFIX + id, JSON.stringify(entry));\n } catch {}\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ExperimentTrackOptions {\n /** Auto-track impressions (default true) */\n impression?: boolean;\n /** Auto-track clicks (default true) */\n primaryClick?: boolean;\n /** Custom impression event name (default \"$experiment_exposure\") */\n impressionEventName?: string;\n /** Custom click event name (default \"$experiment_click\") */\n clickEventName?: string;\n}\n\nexport interface ExperimentProps {\n /** Experiment key / identifier */\n id: string;\n /** Control variant ReactNode */\n control: React.ReactNode;\n /** Named variant ReactNodes, keyed by variant key (e.g. { ai_v1: <MyVariant /> }) */\n variants: Record<string, React.ReactNode>;\n /** Tracking configuration */\n track?: ExperimentTrackOptions;\n /** Stable instance id when multiple instances of the same experiment exist on a page */\n componentInstanceId?: string;\n /** Behavior when assignment fetch fails: \"control\" (default) renders control, \"suspend\" throws */\n fallback?: \"control\" | \"suspend\";\n /** Log decisions + events to console */\n debug?: boolean;\n}\n\nexport function Experiment({\n id,\n control,\n variants,\n track,\n componentInstanceId,\n fallback = \"control\",\n debug = false,\n}: ExperimentProps) {\n const { host, bootstrap } = useProbatContext();\n\n // Stable instance id (useId + sessionStorage for cross-mount stability)\n const autoInstanceId = useStableInstanceId(id);\n const instanceId = componentInstanceId ?? autoInstanceId;\n\n // Track options with defaults\n const trackImpression = track?.impression !== false;\n const trackClick = track?.primaryClick !== false;\n const impressionEvent = track?.impressionEventName ?? \"$experiment_exposure\";\n const clickEvent = track?.clickEventName ?? \"$experiment_click\";\n\n // ── Assignment resolution ──────────────────────────────────────────────\n\n const [variantKey, setVariantKey] = useState<string>(() => {\n // Sync resolution order: bootstrap → localStorage → \"control\"\n if (bootstrap[id]) return bootstrap[id];\n const cached = readAssignment(id);\n if (cached) return cached;\n return \"control\";\n });\n const [resolved, setResolved] = useState<boolean>(() => {\n return !!(bootstrap[id] || readAssignment(id));\n });\n\n useEffect(() => {\n // Already resolved from bootstrap or cache\n if (bootstrap[id] || readAssignment(id)) {\n // Ensure state is synced (StrictMode may re-mount)\n const key = bootstrap[id] ?? readAssignment(id) ?? \"control\";\n setVariantKey(key);\n setResolved(true);\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n try {\n const distinctId = getDistinctId();\n const key = await fetchDecision(host, id, distinctId);\n if (cancelled) return;\n\n // Validate variant key\n if (key !== \"control\" && !(key in variants)) {\n if (debug) {\n console.warn(\n `[probat] Unknown variant \"${key}\" for experiment \"${id}\", falling back to control`\n );\n }\n setVariantKey(\"control\");\n } else {\n setVariantKey(key);\n writeAssignment(id, key);\n }\n } catch (err) {\n if (cancelled) return;\n if (debug) {\n console.error(`[probat] fetchDecision failed for \"${id}\":`, err);\n }\n if (fallback === \"suspend\") throw err;\n setVariantKey(\"control\");\n } finally {\n if (!cancelled) setResolved(true);\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [id, host]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // ── Debug logging ──────────────────────────────────────────────────────\n\n useEffect(() => {\n if (debug && resolved) {\n console.log(`[probat] Experiment \"${id}\" → variant \"${variantKey}\"`, {\n instanceId,\n pageKey: getPageKey(),\n });\n }\n }, [debug, id, variantKey, resolved, instanceId]);\n\n // ── Shared event properties ────────────────────────────────────────────\n\n const eventProps = useMemo(\n () => ({\n experiment_id: id,\n variant_key: variantKey,\n component_instance_id: instanceId,\n }),\n [id, variantKey, instanceId]\n );\n\n // ── Impression tracking via IntersectionObserver ────────────────────────\n\n const containerRef = useRef<HTMLDivElement>(null);\n const impressionSent = useRef(false);\n\n useEffect(() => {\n if (!trackImpression || !resolved) return;\n\n // Reset on re-mount (StrictMode safety)\n impressionSent.current = false;\n\n const pageKey = getPageKey();\n const dedupeKey = makeDedupeKey(id, variantKey, instanceId, pageKey);\n\n // Already seen this session\n if (hasSeen(dedupeKey)) {\n impressionSent.current = true;\n return;\n }\n\n const el = containerRef.current;\n if (!el) return;\n\n // Fallback: no IntersectionObserver (SSR, old browser)\n if (typeof IntersectionObserver === \"undefined\") {\n if (!impressionSent.current) {\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent (no IO) for \"${id}\"`);\n }\n return;\n }\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry || impressionSent.current) return;\n\n if (entry.isIntersecting) {\n timer = setTimeout(() => {\n if (impressionSent.current) return;\n impressionSent.current = true;\n markSeen(dedupeKey);\n sendMetric(host, impressionEvent, eventProps);\n if (debug) console.log(`[probat] Impression sent for \"${id}\"`);\n observer.disconnect();\n }, 250);\n } else if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n },\n { threshold: 0.5 }\n );\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n if (timer) clearTimeout(timer);\n };\n }, [\n trackImpression,\n resolved,\n id,\n variantKey,\n instanceId,\n host,\n impressionEvent,\n eventProps,\n debug,\n ]);\n\n // ── Click tracking ─────────────────────────────────────────────────────\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n if (!trackClick) return;\n\n const meta = extractClickMeta(e.target as EventTarget);\n if (!meta) return;\n\n sendMetric(host, clickEvent, {\n ...eventProps,\n ...meta,\n });\n if (debug) {\n console.log(`[probat] Click tracked for \"${id}\"`, meta);\n }\n },\n [trackClick, host, clickEvent, eventProps, id, debug]\n );\n\n // ── Render ─────────────────────────────────────────────────────────────\n\n const content =\n variantKey === \"control\" || !(variantKey in variants)\n ? control\n : variants[variantKey];\n\n return (\n <div\n ref={containerRef}\n onClick={handleClick}\n data-probat-experiment={id}\n data-probat-variant={variantKey}\n style={{ display: \"block\", margin: 0, padding: 0 }}\n >\n {content}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useProbatContext } from \"../context/ProbatContext\";\nimport { sendMetric } from \"../utils/api\";\n\nexport interface UseProbatMetricsReturn {\n /**\n * Send a custom event with arbitrary properties.\n * Never throws — failures are silently dropped.\n *\n * @example\n * ```tsx\n * const { capture } = useProbatMetrics();\n * capture(\"purchase\", { revenue: 42, currency: \"USD\" });\n * ```\n */\n capture: (event: string, properties?: Record<string, unknown>) => void;\n}\n\n/**\n * Minimal metrics hook. Provides a single `capture(event, props)` function\n * that sends events to the Probat backend using the provider's host config.\n */\nexport function useProbatMetrics(): UseProbatMetricsReturn {\n const { host } = useProbatContext();\n\n const capture = useCallback(\n (event: string, properties: Record<string, unknown> = {}) => {\n sendMetric(host, event, properties);\n },\n [host]\n );\n\n return { capture };\n}\n"]}
|
package/package.json
CHANGED