@probat/react 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1 -30
- package/dist/index.d.ts +1 -30
- package/dist/index.js +189 -157
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +191 -155
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/src/context/ProbatContext.tsx +1 -12
- package/src/hoc/itrt-frontend.code-workspace +3 -0
- package/src/hoc/withExperiment.tsx +4 -12
- package/src/index.ts +0 -6
- package/src/utils/api.ts +155 -16
- package/src/utils/documentClickTracker.ts +61 -46
package/dist/index.d.mts
CHANGED
|
@@ -190,33 +190,4 @@ declare function writeChoice(proposalId: string, experiment_id: string, label: s
|
|
|
190
190
|
declare function hasTrackedVisit(proposalId: string, label: string): boolean;
|
|
191
191
|
declare function markTrackedVisit(proposalId: string, label: string): void;
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
* Document-level click tracking for Probat experiments
|
|
195
|
-
*
|
|
196
|
-
* This module implements event delegation at the document level to track clicks
|
|
197
|
-
* even when components don't have onClick handlers or when stopPropagation() is called.
|
|
198
|
-
*/
|
|
199
|
-
/**
|
|
200
|
-
* Initialize document-level click tracking
|
|
201
|
-
* Call this once when your app initializes (typically in ProbatProvider)
|
|
202
|
-
*/
|
|
203
|
-
declare function initDocumentClickTracking(): void;
|
|
204
|
-
/**
|
|
205
|
-
* Clean up the listener (useful for testing or cleanup)
|
|
206
|
-
*/
|
|
207
|
-
declare function cleanupDocumentClickTracking(): void;
|
|
208
|
-
/**
|
|
209
|
-
* Update proposal metadata cache (call this when proposal data changes)
|
|
210
|
-
* This is useful if you want to update the cache without waiting for DOM queries
|
|
211
|
-
*/
|
|
212
|
-
declare function updateProposalMetadata(proposalId: string, metadata: {
|
|
213
|
-
experimentId?: string | null;
|
|
214
|
-
variantLabel?: string;
|
|
215
|
-
apiBaseUrl?: string;
|
|
216
|
-
}): void;
|
|
217
|
-
/**
|
|
218
|
-
* Clear the proposal cache (useful for testing)
|
|
219
|
-
*/
|
|
220
|
-
declare function clearProposalCache(): void;
|
|
221
|
-
|
|
222
|
-
export { type Choice, type ProbatContextValue, ProbatProvider, ProbatProviderClient, type ProbatProviderProps as ProbatProviderClientProps, type ProbatProviderProps, type RetrieveResponse, type UseExperimentReturn, type UseProbatMetricsReturn, type WithExperimentOptions, cleanupDocumentClickTracking, clearProposalCache, detectEnvironment, extractClickMeta, fetchDecision, hasTrackedVisit, initDocumentClickTracking, markTrackedVisit, readChoice, sendMetric, updateProposalMetadata, useExperiment, useProbatContext, useProbatMetrics, withExperiment, writeChoice };
|
|
193
|
+
export { type Choice, type ProbatContextValue, ProbatProvider, ProbatProviderClient, type ProbatProviderProps as ProbatProviderClientProps, type ProbatProviderProps, type RetrieveResponse, type UseExperimentReturn, type UseProbatMetricsReturn, type WithExperimentOptions, detectEnvironment, extractClickMeta, fetchDecision, hasTrackedVisit, markTrackedVisit, readChoice, sendMetric, useExperiment, useProbatContext, useProbatMetrics, withExperiment, writeChoice };
|
package/dist/index.d.ts
CHANGED
|
@@ -190,33 +190,4 @@ declare function writeChoice(proposalId: string, experiment_id: string, label: s
|
|
|
190
190
|
declare function hasTrackedVisit(proposalId: string, label: string): boolean;
|
|
191
191
|
declare function markTrackedVisit(proposalId: string, label: string): void;
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
* Document-level click tracking for Probat experiments
|
|
195
|
-
*
|
|
196
|
-
* This module implements event delegation at the document level to track clicks
|
|
197
|
-
* even when components don't have onClick handlers or when stopPropagation() is called.
|
|
198
|
-
*/
|
|
199
|
-
/**
|
|
200
|
-
* Initialize document-level click tracking
|
|
201
|
-
* Call this once when your app initializes (typically in ProbatProvider)
|
|
202
|
-
*/
|
|
203
|
-
declare function initDocumentClickTracking(): void;
|
|
204
|
-
/**
|
|
205
|
-
* Clean up the listener (useful for testing or cleanup)
|
|
206
|
-
*/
|
|
207
|
-
declare function cleanupDocumentClickTracking(): void;
|
|
208
|
-
/**
|
|
209
|
-
* Update proposal metadata cache (call this when proposal data changes)
|
|
210
|
-
* This is useful if you want to update the cache without waiting for DOM queries
|
|
211
|
-
*/
|
|
212
|
-
declare function updateProposalMetadata(proposalId: string, metadata: {
|
|
213
|
-
experimentId?: string | null;
|
|
214
|
-
variantLabel?: string;
|
|
215
|
-
apiBaseUrl?: string;
|
|
216
|
-
}): void;
|
|
217
|
-
/**
|
|
218
|
-
* Clear the proposal cache (useful for testing)
|
|
219
|
-
*/
|
|
220
|
-
declare function clearProposalCache(): void;
|
|
221
|
-
|
|
222
|
-
export { type Choice, type ProbatContextValue, ProbatProvider, ProbatProviderClient, type ProbatProviderProps as ProbatProviderClientProps, type ProbatProviderProps, type RetrieveResponse, type UseExperimentReturn, type UseProbatMetricsReturn, type WithExperimentOptions, cleanupDocumentClickTracking, clearProposalCache, detectEnvironment, extractClickMeta, fetchDecision, hasTrackedVisit, initDocumentClickTracking, markTrackedVisit, readChoice, sendMetric, updateProposalMetadata, useExperiment, useProbatContext, useProbatMetrics, withExperiment, writeChoice };
|
|
193
|
+
export { type Choice, type ProbatContextValue, ProbatProvider, ProbatProviderClient, type ProbatProviderProps as ProbatProviderClientProps, type ProbatProviderProps, type RetrieveResponse, type UseExperimentReturn, type UseProbatMetricsReturn, type WithExperimentOptions, detectEnvironment, extractClickMeta, fetchDecision, hasTrackedVisit, markTrackedVisit, readChoice, sendMetric, useExperiment, useProbatContext, useProbatMetrics, withExperiment, writeChoice };
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,41 @@ function detectEnvironment() {
|
|
|
20
20
|
}
|
|
21
21
|
return "prod";
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
// src/context/ProbatContext.tsx
|
|
25
|
+
var ProbatContext = React4.createContext(null);
|
|
26
|
+
function ProbatProvider({
|
|
27
|
+
apiBaseUrl,
|
|
28
|
+
clientKey,
|
|
29
|
+
environment: explicitEnvironment,
|
|
30
|
+
repoFullName: explicitRepoFullName,
|
|
31
|
+
children
|
|
32
|
+
}) {
|
|
33
|
+
const contextValue = React4.useMemo(() => {
|
|
34
|
+
const resolvedApiBaseUrl = apiBaseUrl || typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_PROBAT_API || typeof globalThis !== "undefined" && globalThis.process?.env?.NEXT_PUBLIC_PROBAT_API || typeof window !== "undefined" && window.__PROBAT_API || "https://gushi.onrender.com";
|
|
35
|
+
const environment = explicitEnvironment || detectEnvironment();
|
|
36
|
+
const resolvedRepoFullName = explicitRepoFullName || typeof globalThis !== "undefined" && globalThis.process?.env?.NEXT_PUBLIC_PROBAT_REPO || typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_PROBAT_REPO || typeof window !== "undefined" && window.__PROBAT_REPO || void 0;
|
|
37
|
+
return {
|
|
38
|
+
apiBaseUrl: resolvedApiBaseUrl,
|
|
39
|
+
environment,
|
|
40
|
+
clientKey,
|
|
41
|
+
repoFullName: resolvedRepoFullName
|
|
42
|
+
};
|
|
43
|
+
}, [apiBaseUrl, clientKey, explicitEnvironment, explicitRepoFullName]);
|
|
44
|
+
return /* @__PURE__ */ React4__default.default.createElement(ProbatContext.Provider, { value: contextValue }, children);
|
|
45
|
+
}
|
|
46
|
+
function useProbatContext() {
|
|
47
|
+
const context = React4.useContext(ProbatContext);
|
|
48
|
+
if (!context) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
"useProbatContext must be used within a ProbatProvider. Please wrap your app with <ProbatProvider>."
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return context;
|
|
54
|
+
}
|
|
55
|
+
function ProbatProviderClient(props) {
|
|
56
|
+
return React4__default.default.createElement(ProbatProvider, props);
|
|
57
|
+
}
|
|
23
58
|
var pendingFetches = /* @__PURE__ */ new Map();
|
|
24
59
|
async function fetchDecision(baseUrl, proposalId) {
|
|
25
60
|
const existingFetch = pendingFetches.get(proposalId);
|
|
@@ -133,7 +168,7 @@ if (typeof window !== "undefined") {
|
|
|
133
168
|
window.__probatReact = React4__default.default;
|
|
134
169
|
window.React = window.React || React4__default.default;
|
|
135
170
|
}
|
|
136
|
-
async function loadVariantComponent(baseUrl, proposalId, experimentId, filePath) {
|
|
171
|
+
async function loadVariantComponent(baseUrl, proposalId, experimentId, filePath, repoFullName, baseRef) {
|
|
137
172
|
if (!filePath) {
|
|
138
173
|
return null;
|
|
139
174
|
}
|
|
@@ -144,16 +179,151 @@ async function loadVariantComponent(baseUrl, proposalId, experimentId, filePath)
|
|
|
144
179
|
}
|
|
145
180
|
const loadPromise = (async () => {
|
|
146
181
|
try {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
182
|
+
try {
|
|
183
|
+
const variantUrl = `/probat/${filePath}`;
|
|
184
|
+
const mod = await import(
|
|
185
|
+
/* @vite-ignore */
|
|
186
|
+
variantUrl
|
|
187
|
+
);
|
|
188
|
+
const VariantComponent2 = mod?.default || mod;
|
|
189
|
+
if (VariantComponent2 && typeof VariantComponent2 === "function") {
|
|
190
|
+
return VariantComponent2;
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
let code = "";
|
|
195
|
+
let rawCode = "";
|
|
196
|
+
let rawCodeFetched = false;
|
|
197
|
+
const localUrl = `/probat/${filePath}`;
|
|
198
|
+
try {
|
|
199
|
+
const localRes = await fetch(localUrl, {
|
|
200
|
+
method: "GET",
|
|
201
|
+
headers: { Accept: "text/plain" }
|
|
202
|
+
});
|
|
203
|
+
if (localRes.ok) {
|
|
204
|
+
rawCode = await localRes.text();
|
|
205
|
+
rawCodeFetched = true;
|
|
206
|
+
console.log(`[PROBAT] \u2705 Loaded variant from local (user's repo): ${localUrl}`);
|
|
207
|
+
}
|
|
208
|
+
} catch {
|
|
209
|
+
console.debug(`[PROBAT] Local file not available (${localUrl}), trying GitHub...`);
|
|
210
|
+
}
|
|
211
|
+
if (!rawCodeFetched && repoFullName) {
|
|
212
|
+
const githubPath = `probat/${filePath}`;
|
|
213
|
+
const gitRef = baseRef || "main";
|
|
214
|
+
const githubUrl = `https://raw.githubusercontent.com/${repoFullName}/${gitRef}/${githubPath}`;
|
|
215
|
+
const res = await fetch(githubUrl, { method: "GET", headers: { Accept: "text/plain" } });
|
|
216
|
+
if (res.ok) {
|
|
217
|
+
rawCode = await res.text();
|
|
218
|
+
rawCodeFetched = true;
|
|
219
|
+
console.log(`[PROBAT] \u26A0\uFE0F Loaded variant from GitHub (fallback): ${githubUrl}`);
|
|
220
|
+
} else {
|
|
221
|
+
console.warn(`[PROBAT] \u26A0\uFE0F GitHub fetch failed (${res.status}), falling back to server compilation`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (rawCodeFetched && rawCode) {
|
|
225
|
+
let Babel;
|
|
226
|
+
if (typeof window !== "undefined" && window.Babel) {
|
|
227
|
+
Babel = window.Babel;
|
|
228
|
+
} else {
|
|
229
|
+
try {
|
|
230
|
+
const babelModule = await import('@babel/standalone');
|
|
231
|
+
Babel = babelModule.default || babelModule;
|
|
232
|
+
} catch (importError) {
|
|
233
|
+
try {
|
|
234
|
+
await new Promise((resolve, reject) => {
|
|
235
|
+
if (typeof document === "undefined") {
|
|
236
|
+
reject(new Error("Document not available"));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (window.Babel) {
|
|
240
|
+
Babel = window.Babel;
|
|
241
|
+
resolve();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const script = document.createElement("script");
|
|
245
|
+
script.src = "https://unpkg.com/@babel/standalone/babel.min.js";
|
|
246
|
+
script.async = true;
|
|
247
|
+
script.onload = () => {
|
|
248
|
+
Babel = window.Babel;
|
|
249
|
+
if (!Babel) reject(new Error("Babel not found after script load"));
|
|
250
|
+
else resolve();
|
|
251
|
+
};
|
|
252
|
+
script.onerror = () => reject(new Error("Failed to load Babel from CDN"));
|
|
253
|
+
document.head.appendChild(script);
|
|
254
|
+
});
|
|
255
|
+
} catch (babelError) {
|
|
256
|
+
console.error("[PROBAT] Failed to load Babel, falling back to server compilation", babelError);
|
|
257
|
+
rawCodeFetched = false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (rawCodeFetched && rawCode && Babel) {
|
|
262
|
+
const isTSX = filePath.endsWith(".tsx");
|
|
263
|
+
rawCode = rawCode.replace(/^import\s+['"].*\.css['"];?\s*$/gm, "");
|
|
264
|
+
rawCode = rawCode.replace(/^import\s+.*from\s+['"].*\.css['"];?\s*$/gm, "");
|
|
265
|
+
rawCode = rawCode.replace(
|
|
266
|
+
/^import\s+React(?:\s*,\s*\{[^}]*\})?\s+from\s+['"]react['"];?\s*$/m,
|
|
267
|
+
"const React = window.React || globalThis.React;"
|
|
268
|
+
);
|
|
269
|
+
rawCode = rawCode.replace(
|
|
270
|
+
/^import\s+\*\s+as\s+React\s+from\s+['"]react['"];?\s*$/m,
|
|
271
|
+
"const React = window.React || globalThis.React;"
|
|
272
|
+
);
|
|
273
|
+
rawCode = rawCode.replace(
|
|
274
|
+
/^import\s+\{([^}]+)\}\s+from\s+['"]react['"];?\s*$/m,
|
|
275
|
+
(match, imports) => `const {${imports}} = window.React || globalThis.React;`
|
|
276
|
+
);
|
|
277
|
+
rawCode = rawCode.replace(/import\.meta\.env\.[\w$]+/g, "undefined");
|
|
278
|
+
rawCode = rawCode.replace(/\bimport\.meta\b/g, "({})");
|
|
279
|
+
rawCode = rawCode.replace(/^import\s+.*\/@vite\/client.*$/gm, "");
|
|
280
|
+
rawCode = rawCode.replace(/import\.meta\.hot(?:\.[\w$]+)*/g, "undefined");
|
|
281
|
+
const compiled = Babel.transform(rawCode, {
|
|
282
|
+
presets: [
|
|
283
|
+
["react", { runtime: "classic" }],
|
|
284
|
+
["typescript", { allExtensions: true, isTSX }]
|
|
285
|
+
],
|
|
286
|
+
plugins: [["transform-modules-commonjs", { allowTopLevelThis: true }]],
|
|
287
|
+
sourceType: "module",
|
|
288
|
+
filename: filePath
|
|
289
|
+
}).code;
|
|
290
|
+
code = `
|
|
291
|
+
var __probatVariant = (function() {
|
|
292
|
+
var require = function(name) {
|
|
293
|
+
if (name === "react" || name === "react/jsx-runtime") {
|
|
294
|
+
return window.React || globalThis.React;
|
|
295
|
+
}
|
|
296
|
+
if (name.startsWith("/@vite") || name.includes("@vite/client") || name.includes(".vite/deps")) {
|
|
297
|
+
return {};
|
|
298
|
+
}
|
|
299
|
+
if (name === "react/jsx-runtime.js") {
|
|
300
|
+
return window.React || globalThis.React;
|
|
301
|
+
}
|
|
302
|
+
throw new Error("Unsupported module: " + name);
|
|
303
|
+
};
|
|
304
|
+
var module = { exports: {} };
|
|
305
|
+
var exports = module.exports;
|
|
306
|
+
${compiled}
|
|
307
|
+
return module.exports.default || module.exports;
|
|
308
|
+
})();
|
|
309
|
+
`;
|
|
310
|
+
} else {
|
|
311
|
+
rawCodeFetched = false;
|
|
312
|
+
code = "";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (!rawCodeFetched || code === "") {
|
|
316
|
+
const variantUrl = `${baseUrl.replace(/\/$/, "")}/variants/${filePath}`;
|
|
317
|
+
const serverRes = await fetch(variantUrl, {
|
|
318
|
+
method: "GET",
|
|
319
|
+
headers: { Accept: "text/javascript" },
|
|
320
|
+
credentials: "include"
|
|
321
|
+
});
|
|
322
|
+
if (!serverRes.ok) {
|
|
323
|
+
throw new Error(`HTTP ${serverRes.status}`);
|
|
324
|
+
}
|
|
325
|
+
code = await serverRes.text();
|
|
155
326
|
}
|
|
156
|
-
const code = await res.text();
|
|
157
327
|
if (typeof window !== "undefined") {
|
|
158
328
|
window.React = window.React || React4__default.default;
|
|
159
329
|
}
|
|
@@ -180,131 +350,7 @@ async function loadVariantComponent(baseUrl, proposalId, experimentId, filePath)
|
|
|
180
350
|
return loadPromise;
|
|
181
351
|
}
|
|
182
352
|
|
|
183
|
-
// src/
|
|
184
|
-
var proposalCache = /* @__PURE__ */ new Map();
|
|
185
|
-
var isListenerAttached = false;
|
|
186
|
-
var lastClickTime = /* @__PURE__ */ new Map();
|
|
187
|
-
var DEBOUNCE_MS = 100;
|
|
188
|
-
function getProposalMetadata(element) {
|
|
189
|
-
const probatWrapper = element.closest("[data-probat-proposal]");
|
|
190
|
-
if (!probatWrapper) return null;
|
|
191
|
-
const proposalId = probatWrapper.getAttribute("data-probat-proposal");
|
|
192
|
-
if (!proposalId) return null;
|
|
193
|
-
const cacheKey = `${proposalId}`;
|
|
194
|
-
const cached = proposalCache.get(cacheKey);
|
|
195
|
-
if (cached) return cached;
|
|
196
|
-
const experimentId = probatWrapper.getAttribute("data-probat-experiment-id");
|
|
197
|
-
const variantLabel = probatWrapper.getAttribute("data-probat-variant-label") || "control";
|
|
198
|
-
const apiBaseUrl = probatWrapper.getAttribute("data-probat-api-base-url") || typeof window !== "undefined" && window.__PROBAT_API || "https://gushi.onrender.com";
|
|
199
|
-
const metadata = {
|
|
200
|
-
proposalId,
|
|
201
|
-
experimentId: experimentId || null,
|
|
202
|
-
variantLabel,
|
|
203
|
-
apiBaseUrl
|
|
204
|
-
};
|
|
205
|
-
proposalCache.set(cacheKey, metadata);
|
|
206
|
-
return metadata;
|
|
207
|
-
}
|
|
208
|
-
function handleDocumentClick(event) {
|
|
209
|
-
const target = event.target;
|
|
210
|
-
if (!target) return;
|
|
211
|
-
const metadata = getProposalMetadata(target);
|
|
212
|
-
if (!metadata) return;
|
|
213
|
-
const now2 = Date.now();
|
|
214
|
-
const lastClick = lastClickTime.get(metadata.proposalId) || 0;
|
|
215
|
-
if (now2 - lastClick < DEBOUNCE_MS) {
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
lastClickTime.set(metadata.proposalId, now2);
|
|
219
|
-
const clickMeta = extractClickMeta(event);
|
|
220
|
-
const hasTrackAttribute = target.hasAttribute("data-probat-track") || target.closest("[data-probat-track]") !== null;
|
|
221
|
-
const shouldTrack = clickMeta !== void 0 || hasTrackAttribute;
|
|
222
|
-
if (!shouldTrack) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
void sendMetric(
|
|
226
|
-
metadata.apiBaseUrl,
|
|
227
|
-
metadata.proposalId,
|
|
228
|
-
"click",
|
|
229
|
-
metadata.variantLabel,
|
|
230
|
-
metadata.experimentId || void 0,
|
|
231
|
-
clickMeta
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
function initDocumentClickTracking() {
|
|
235
|
-
if (isListenerAttached) {
|
|
236
|
-
console.warn("[PROBAT] Document click listener already attached");
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
if (typeof document === "undefined") {
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
document.addEventListener("click", handleDocumentClick, true);
|
|
243
|
-
isListenerAttached = true;
|
|
244
|
-
console.log("[PROBAT] Document-level click tracking initialized");
|
|
245
|
-
}
|
|
246
|
-
function cleanupDocumentClickTracking() {
|
|
247
|
-
if (!isListenerAttached) return;
|
|
248
|
-
if (typeof document !== "undefined") {
|
|
249
|
-
document.removeEventListener("click", handleDocumentClick, true);
|
|
250
|
-
}
|
|
251
|
-
isListenerAttached = false;
|
|
252
|
-
proposalCache.clear();
|
|
253
|
-
lastClickTime.clear();
|
|
254
|
-
}
|
|
255
|
-
function updateProposalMetadata(proposalId, metadata) {
|
|
256
|
-
const existing = proposalCache.get(proposalId);
|
|
257
|
-
if (existing) {
|
|
258
|
-
proposalCache.set(proposalId, {
|
|
259
|
-
...existing,
|
|
260
|
-
...metadata
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
function clearProposalCache() {
|
|
265
|
-
proposalCache.clear();
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// src/context/ProbatContext.tsx
|
|
269
|
-
var ProbatContext = React4.createContext(null);
|
|
270
|
-
function ProbatProvider({
|
|
271
|
-
apiBaseUrl,
|
|
272
|
-
clientKey,
|
|
273
|
-
environment: explicitEnvironment,
|
|
274
|
-
repoFullName: explicitRepoFullName,
|
|
275
|
-
children
|
|
276
|
-
}) {
|
|
277
|
-
const contextValue = React4.useMemo(() => {
|
|
278
|
-
const resolvedApiBaseUrl = apiBaseUrl || typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_PROBAT_API || typeof globalThis !== "undefined" && globalThis.process?.env?.NEXT_PUBLIC_PROBAT_API || typeof window !== "undefined" && window.__PROBAT_API || "https://gushi.onrender.com";
|
|
279
|
-
const environment = explicitEnvironment || detectEnvironment();
|
|
280
|
-
const resolvedRepoFullName = explicitRepoFullName || typeof globalThis !== "undefined" && globalThis.process?.env?.NEXT_PUBLIC_PROBAT_REPO || typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_PROBAT_REPO || typeof window !== "undefined" && window.__PROBAT_REPO || void 0;
|
|
281
|
-
return {
|
|
282
|
-
apiBaseUrl: resolvedApiBaseUrl,
|
|
283
|
-
environment,
|
|
284
|
-
clientKey,
|
|
285
|
-
repoFullName: resolvedRepoFullName
|
|
286
|
-
};
|
|
287
|
-
}, [apiBaseUrl, clientKey, explicitEnvironment, explicitRepoFullName]);
|
|
288
|
-
React4.useEffect(() => {
|
|
289
|
-
initDocumentClickTracking();
|
|
290
|
-
return () => {
|
|
291
|
-
cleanupDocumentClickTracking();
|
|
292
|
-
};
|
|
293
|
-
}, []);
|
|
294
|
-
return /* @__PURE__ */ React4__default.default.createElement(ProbatContext.Provider, { value: contextValue }, children);
|
|
295
|
-
}
|
|
296
|
-
function useProbatContext() {
|
|
297
|
-
const context = React4.useContext(ProbatContext);
|
|
298
|
-
if (!context) {
|
|
299
|
-
throw new Error(
|
|
300
|
-
"useProbatContext must be used within a ProbatProvider. Please wrap your app with <ProbatProvider>."
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
return context;
|
|
304
|
-
}
|
|
305
|
-
function ProbatProviderClient(props) {
|
|
306
|
-
return React4__default.default.createElement(ProbatProvider, props);
|
|
307
|
-
}
|
|
353
|
+
// src/hooks/useProbatMetrics.ts
|
|
308
354
|
function useProbatMetrics() {
|
|
309
355
|
const { apiBaseUrl } = useProbatContext();
|
|
310
356
|
const trackClick = React4.useCallback(
|
|
@@ -568,7 +614,9 @@ function withExperiment(Control, options) {
|
|
|
568
614
|
apiBaseUrl,
|
|
569
615
|
componentConfig.proposal_id,
|
|
570
616
|
variantInfo.experiment_id,
|
|
571
|
-
variantInfo.file_path
|
|
617
|
+
variantInfo.file_path,
|
|
618
|
+
componentConfig.repo_full_name,
|
|
619
|
+
componentConfig.base_ref
|
|
572
620
|
);
|
|
573
621
|
if (VariantComp && typeof VariantComp === "function" && alive) {
|
|
574
622
|
variantComponents[label2] = VariantComp;
|
|
@@ -667,23 +715,11 @@ function withExperiment(Control, options) {
|
|
|
667
715
|
}
|
|
668
716
|
const label = choice?.label ?? "control";
|
|
669
717
|
const Variant = registry[label] || registry.control || ControlComponent;
|
|
670
|
-
return /* @__PURE__ */ React4__default.default.createElement(
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
},
|
|
676
|
-
"data-probat-proposal": proposalId,
|
|
677
|
-
"data-probat-experiment-id": choice?.experiment_id || "",
|
|
678
|
-
"data-probat-variant-label": label,
|
|
679
|
-
"data-probat-api-base-url": apiBaseUrl
|
|
680
|
-
},
|
|
681
|
-
React4__default.default.createElement(Variant, {
|
|
682
|
-
key: `${proposalId}:${label}`,
|
|
683
|
-
...props,
|
|
684
|
-
probat: { trackClick: () => trackClick(null, { force: true }) }
|
|
685
|
-
})
|
|
686
|
-
);
|
|
718
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", { onClick: (event) => trackClick(event), "data-probat-proposal": proposalId }, React4__default.default.createElement(Variant, {
|
|
719
|
+
key: `${proposalId}:${label}`,
|
|
720
|
+
...props,
|
|
721
|
+
probat: { trackClick: () => trackClick(null, { force: true }) }
|
|
722
|
+
}));
|
|
687
723
|
}
|
|
688
724
|
Wrapped.displayName = `withExperiment(${Control.displayName || Control.name || "Component"})`;
|
|
689
725
|
return Wrapped;
|
|
@@ -691,17 +727,13 @@ function withExperiment(Control, options) {
|
|
|
691
727
|
|
|
692
728
|
exports.ProbatProvider = ProbatProvider;
|
|
693
729
|
exports.ProbatProviderClient = ProbatProviderClient;
|
|
694
|
-
exports.cleanupDocumentClickTracking = cleanupDocumentClickTracking;
|
|
695
|
-
exports.clearProposalCache = clearProposalCache;
|
|
696
730
|
exports.detectEnvironment = detectEnvironment;
|
|
697
731
|
exports.extractClickMeta = extractClickMeta;
|
|
698
732
|
exports.fetchDecision = fetchDecision;
|
|
699
733
|
exports.hasTrackedVisit = hasTrackedVisit;
|
|
700
|
-
exports.initDocumentClickTracking = initDocumentClickTracking;
|
|
701
734
|
exports.markTrackedVisit = markTrackedVisit;
|
|
702
735
|
exports.readChoice = readChoice;
|
|
703
736
|
exports.sendMetric = sendMetric;
|
|
704
|
-
exports.updateProposalMetadata = updateProposalMetadata;
|
|
705
737
|
exports.useExperiment = useExperiment;
|
|
706
738
|
exports.useProbatContext = useProbatContext;
|
|
707
739
|
exports.useProbatMetrics = useProbatMetrics;
|