sitepong 0.1.13 → 0.2.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.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import 'web-vitals';
2
+
1
3
  // src/flags/anonymous-id.ts
2
4
  var STORAGE_KEY = "sitepong_anonymous_id";
3
5
  function generateUUID() {
@@ -516,6 +518,60 @@ function clearSession() {
516
518
  memorySessionTs = null;
517
519
  }
518
520
 
521
+ // src/analytics/utm.ts
522
+ var STORAGE_KEY2 = "sp_utm";
523
+ var UTM_KEYS = [
524
+ ["source", "utm_source"],
525
+ ["medium", "utm_medium"],
526
+ ["campaign", "utm_campaign"],
527
+ ["term", "utm_term"],
528
+ ["content", "utm_content"]
529
+ ];
530
+ function parseFromLocation() {
531
+ if (typeof window === "undefined" || !window.location || !window.location.search) return null;
532
+ let params;
533
+ try {
534
+ params = new URLSearchParams(window.location.search);
535
+ } catch {
536
+ return null;
537
+ }
538
+ const result = {};
539
+ let found = false;
540
+ for (const [key, queryKey] of UTM_KEYS) {
541
+ const value = params.get(queryKey);
542
+ if (value) {
543
+ result[key] = value;
544
+ found = true;
545
+ }
546
+ }
547
+ return found ? result : null;
548
+ }
549
+ function readStored() {
550
+ if (typeof sessionStorage === "undefined") return null;
551
+ try {
552
+ const raw = sessionStorage.getItem(STORAGE_KEY2);
553
+ if (!raw) return null;
554
+ const parsed = JSON.parse(raw);
555
+ return parsed && typeof parsed === "object" ? parsed : null;
556
+ } catch {
557
+ return null;
558
+ }
559
+ }
560
+ function writeStored(utm) {
561
+ if (typeof sessionStorage === "undefined") return;
562
+ try {
563
+ sessionStorage.setItem(STORAGE_KEY2, JSON.stringify(utm));
564
+ } catch {
565
+ }
566
+ }
567
+ function getSessionUtm() {
568
+ const stored = readStored();
569
+ if (stored) return stored;
570
+ const fresh = parseFromLocation();
571
+ if (fresh) writeStored(fresh);
572
+ return fresh;
573
+ }
574
+
519
575
  // src/analytics/autocapture.ts
520
576
  var DEFAULT_BLOCK_SELECTORS = [
521
577
  "[data-sp-no-capture]",
@@ -922,7 +978,9 @@ var AnalyticsManager = class {
922
978
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
923
979
  url: typeof window !== "undefined" && window.location ? window.location.href : void 0,
924
980
  referrer: typeof document !== "undefined" && typeof document.referrer === "string" ? document.referrer : void 0,
925
- userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0
981
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
982
+ utm: getSessionUtm() || void 0,
983
+ appVersion: this.config.appVersion || void 0
926
984
  };
927
985
  }
928
986
  enqueue(event) {
@@ -1620,7 +1678,7 @@ var DEFAULT_REMOTE_CONFIG = {
1620
1678
  };
1621
1679
 
1622
1680
  // src/remote-config/manager.ts
1623
- var STORAGE_KEY2 = "sitepong_remote_config";
1681
+ var STORAGE_KEY3 = "sitepong_remote_config";
1624
1682
  var STORAGE_TS_KEY = "sitepong_remote_config_ts";
1625
1683
  var RemoteConfigManager = class {
1626
1684
  constructor(options) {
@@ -1663,7 +1721,7 @@ var RemoteConfigManager = class {
1663
1721
  const storage = this.options.storage;
1664
1722
  if (!storage) return;
1665
1723
  try {
1666
- const cached = await storage.getItem(STORAGE_KEY2);
1724
+ const cached = await storage.getItem(STORAGE_KEY3);
1667
1725
  const cachedTs = await storage.getItem(STORAGE_TS_KEY);
1668
1726
  if (cached && cachedTs) {
1669
1727
  const age = Date.now() - parseInt(cachedTs, 10);
@@ -1724,7 +1782,7 @@ var RemoteConfigManager = class {
1724
1782
  const storage = this.options.storage;
1725
1783
  if (!storage) return;
1726
1784
  try {
1727
- await storage.setItem(STORAGE_KEY2, JSON.stringify(this.config));
1785
+ await storage.setItem(STORAGE_KEY3, JSON.stringify(this.config));
1728
1786
  await storage.setItem(STORAGE_TS_KEY, String(Date.now()));
1729
1787
  } catch {
1730
1788
  this.log("Failed to cache remote config");
@@ -1903,6 +1961,292 @@ var TracePropagator = class {
1903
1961
  }
1904
1962
  };
1905
1963
 
1964
+ // src/superlink/client.ts
1965
+ var DEFAULT_SUPERLINK_ENDPOINT = "https://pongl.ink";
1966
+ var SuperLinkClient = class {
1967
+ constructor(config = {}) {
1968
+ this.config = {
1969
+ endpoint: stripTrailingSlash(config.endpoint || DEFAULT_SUPERLINK_ENDPOINT),
1970
+ appId: config.appId,
1971
+ installId: config.installId,
1972
+ debug: config.debug ?? false
1973
+ };
1974
+ }
1975
+ /** Replace config in place (used by init() so module-level helpers see updates). */
1976
+ configure(config) {
1977
+ if (config.endpoint) this.config.endpoint = stripTrailingSlash(config.endpoint);
1978
+ if (config.appId !== void 0) this.config.appId = config.appId;
1979
+ if (config.installId !== void 0) this.config.installId = config.installId;
1980
+ if (config.debug !== void 0) this.config.debug = config.debug;
1981
+ }
1982
+ log(...args) {
1983
+ if (this.config.debug) console.warn("[SuperLink]", ...args);
1984
+ }
1985
+ /**
1986
+ * POST /match — ask the redirect engine to resolve a deferred deep link from
1987
+ * a click token (Android referrer / iOS clipboard) and/or a device
1988
+ * fingerprint. Returns `{ matched: false }` on any error.
1989
+ */
1990
+ async match(body) {
1991
+ const payload = {
1992
+ ...body,
1993
+ install_id: body.install_id ?? this.config.installId
1994
+ };
1995
+ try {
1996
+ const res = await fetch(`${this.config.endpoint}/match`, {
1997
+ method: "POST",
1998
+ headers: { "Content-Type": "application/json" },
1999
+ body: JSON.stringify(payload)
2000
+ });
2001
+ if (!res.ok) {
2002
+ this.log("match failed", res.status);
2003
+ return { matched: false };
2004
+ }
2005
+ const data = await res.json();
2006
+ return data ?? { matched: false };
2007
+ } catch (err) {
2008
+ this.log("match error", err);
2009
+ return { matched: false };
2010
+ }
2011
+ }
2012
+ /**
2013
+ * POST /events — report a lifecycle event (opened / converted) back to the
2014
+ * redirect engine, which fans out to analytics + webhooks. Best-effort.
2015
+ */
2016
+ async reportEvent(input) {
2017
+ try {
2018
+ const res = await fetch(`${this.config.endpoint}/events`, {
2019
+ method: "POST",
2020
+ headers: { "Content-Type": "application/json" },
2021
+ body: JSON.stringify({
2022
+ app_id: this.config.appId,
2023
+ install_id: this.config.installId,
2024
+ ...input
2025
+ })
2026
+ });
2027
+ if (!res.ok) this.log("event failed", input.type, res.status);
2028
+ return res.ok;
2029
+ } catch (err) {
2030
+ this.log("event error", err);
2031
+ return false;
2032
+ }
2033
+ }
2034
+ };
2035
+ function stripTrailingSlash(url) {
2036
+ return url.endsWith("/") ? url.slice(0, -1) : url;
2037
+ }
2038
+ var superlinkClient = new SuperLinkClient();
2039
+
2040
+ // src/superlink/parse.ts
2041
+ var UTM_KEYS2 = [
2042
+ ["source", "utm_source"],
2043
+ ["medium", "utm_medium"],
2044
+ ["campaign", "utm_campaign"],
2045
+ ["term", "utm_term"],
2046
+ ["content", "utm_content"]
2047
+ ];
2048
+ function parseUniversalLink(url) {
2049
+ let parsed;
2050
+ try {
2051
+ parsed = new URL(url);
2052
+ } catch {
2053
+ return null;
2054
+ }
2055
+ const params = parsed.searchParams;
2056
+ const utm = {};
2057
+ for (const [key, queryKey] of UTM_KEYS2) {
2058
+ const value = params.get(queryKey);
2059
+ if (value) utm[key] = value;
2060
+ }
2061
+ const referral = {};
2062
+ const deep_link_data = {};
2063
+ params.forEach((value, key) => {
2064
+ if (key.startsWith("r_")) {
2065
+ referral[key.slice(2)] = value;
2066
+ } else if (key.startsWith("~")) {
2067
+ deep_link_data[key.slice(1)] = value;
2068
+ }
2069
+ });
2070
+ const explicitPath = params.get("$deep_link_path") ?? params.get("dlp");
2071
+ const deep_link_path = explicitPath ?? (parsed.pathname && parsed.pathname !== "/" ? parsed.pathname : null);
2072
+ return {
2073
+ deep_link_path,
2074
+ deep_link_data,
2075
+ utm,
2076
+ referral,
2077
+ click_id: params.get("click_id"),
2078
+ match_type: "none",
2079
+ confidence: 1
2080
+ };
2081
+ }
2082
+
2083
+ // src/superlink/deferred.ts
2084
+ var handlers = /* @__PURE__ */ new Set();
2085
+ var lastMatched = null;
2086
+ function toDeepLink(res) {
2087
+ return {
2088
+ deep_link_path: res.deep_link_path ?? null,
2089
+ deep_link_data: res.deep_link_data ?? {},
2090
+ utm: res.utm ?? {},
2091
+ referral: res.referral ?? {},
2092
+ click_id: res.click_id ?? null,
2093
+ match_type: res.match_type ?? "fingerprint",
2094
+ confidence: res.confidence ?? (res.match_type && res.match_type !== "none" ? 1 : 0)
2095
+ };
2096
+ }
2097
+ function emitDeferredDeepLink(link) {
2098
+ lastMatched = link;
2099
+ for (const handler of handlers) {
2100
+ try {
2101
+ handler(link);
2102
+ } catch (err) {
2103
+ if (superlinkClient.config.debug) console.warn("[SuperLink] handler threw", err);
2104
+ }
2105
+ }
2106
+ }
2107
+ function onDeferredDeepLink(handler) {
2108
+ handlers.add(handler);
2109
+ if (lastMatched) {
2110
+ try {
2111
+ handler(lastMatched);
2112
+ } catch {
2113
+ }
2114
+ }
2115
+ return () => {
2116
+ handlers.delete(handler);
2117
+ };
2118
+ }
2119
+ function getMatchedDeepLink() {
2120
+ return lastMatched;
2121
+ }
2122
+
2123
+ // src/superlink/web.ts
2124
+ var STORAGE_KEY4 = "sp_superlink";
2125
+ var captured = null;
2126
+ var didCapture = false;
2127
+ function readStored2() {
2128
+ if (typeof sessionStorage === "undefined") return null;
2129
+ try {
2130
+ const raw = sessionStorage.getItem(STORAGE_KEY4);
2131
+ if (!raw) return null;
2132
+ const parsed = JSON.parse(raw);
2133
+ return parsed && typeof parsed === "object" ? parsed : null;
2134
+ } catch {
2135
+ return null;
2136
+ }
2137
+ }
2138
+ function writeStored2(link) {
2139
+ if (typeof sessionStorage === "undefined") return;
2140
+ try {
2141
+ sessionStorage.setItem(STORAGE_KEY4, JSON.stringify(link));
2142
+ } catch {
2143
+ }
2144
+ }
2145
+ function hasPayload(link) {
2146
+ return Boolean(
2147
+ link.deep_link_path || link.click_id || Object.keys(link.deep_link_data).length > 0 || Object.keys(link.referral).length > 0 || Object.keys(link.utm).length > 0
2148
+ );
2149
+ }
2150
+ function writeClipboardToken(clickId) {
2151
+ if (typeof navigator === "undefined" || !navigator.clipboard) return;
2152
+ const url = `${superlinkClient.config.endpoint}/c/${clickId}`;
2153
+ navigator.clipboard.writeText(url).catch(() => {
2154
+ });
2155
+ }
2156
+ function extractIdentityMetadata(url) {
2157
+ const href = url ?? (typeof window !== "undefined" && window.location ? window.location.href : null);
2158
+ if (!href) return void 0;
2159
+ let loc;
2160
+ try {
2161
+ loc = new URL(href);
2162
+ } catch {
2163
+ return void 0;
2164
+ }
2165
+ const email = loc.searchParams.get("email");
2166
+ const phone = loc.searchParams.get("phone");
2167
+ const userId = loc.searchParams.get("user_id");
2168
+ const id = loc.searchParams.get("id");
2169
+ const customRaw = loc.searchParams.get("custom");
2170
+ const identity = {};
2171
+ if (email) identity.email = email;
2172
+ if (phone) identity.phone = phone;
2173
+ if (userId) identity.user_id = userId;
2174
+ if (id) identity.id = id;
2175
+ if (customRaw) {
2176
+ try {
2177
+ const custom = JSON.parse(decodeURIComponent(customRaw));
2178
+ if (typeof custom === "object" && custom !== null) {
2179
+ identity.custom = custom;
2180
+ }
2181
+ } catch {
2182
+ }
2183
+ }
2184
+ return Object.keys(identity).length > 0 ? identity : void 0;
2185
+ }
2186
+ function captureWebDeepLink() {
2187
+ if (didCapture) return captured;
2188
+ didCapture = true;
2189
+ const stored = readStored2();
2190
+ if (stored) {
2191
+ captured = stored;
2192
+ return captured;
2193
+ }
2194
+ if (typeof window === "undefined" || !window.location) return null;
2195
+ const link = parseUniversalLink(window.location.href);
2196
+ if (link && hasPayload(link)) {
2197
+ captured = link;
2198
+ writeStored2(link);
2199
+ void superlinkClient.reportEvent({
2200
+ type: "opened",
2201
+ click_id: link.click_id,
2202
+ platform: "desktop",
2203
+ properties: { surface: "web" }
2204
+ });
2205
+ }
2206
+ return captured;
2207
+ }
2208
+ function getDeepLink() {
2209
+ if (!didCapture) return captureWebDeepLink();
2210
+ return captured;
2211
+ }
2212
+ function webFingerprint() {
2213
+ const fp = { platform: "desktop" };
2214
+ if (typeof navigator !== "undefined") {
2215
+ if (navigator.userAgent) fp.user_agent = navigator.userAgent;
2216
+ const lang = navigator.language || navigator.languages && navigator.languages[0];
2217
+ if (lang) fp.language = lang;
2218
+ }
2219
+ return fp;
2220
+ }
2221
+ async function identify(identity) {
2222
+ if (!identity || Object.keys(identity).length === 0) return null;
2223
+ const res = await superlinkClient.match({
2224
+ identity,
2225
+ fingerprint: webFingerprint()
2226
+ });
2227
+ if (!res.matched) return null;
2228
+ const link = toDeepLink(res);
2229
+ emitDeferredDeepLink(link);
2230
+ return link;
2231
+ }
2232
+ async function completeFromScan(scanned) {
2233
+ if (!scanned) return null;
2234
+ const res = await superlinkClient.match({
2235
+ qr_token: scanned,
2236
+ fingerprint: webFingerprint()
2237
+ });
2238
+ if (!res.matched) return null;
2239
+ const link = toDeepLink(res);
2240
+ emitDeferredDeepLink(link);
2241
+ return link;
2242
+ }
2243
+
2244
+ // src/superlink/index.ts
2245
+ function initSuperLink(config = {}) {
2246
+ superlinkClient.configure(config);
2247
+ captureWebDeepLink();
2248
+ }
2249
+
1906
2250
  // src/index.ts
1907
2251
  function installFallbackEnvironment() {
1908
2252
  if (getEnvironment()) return;
@@ -2112,6 +2456,10 @@ var SitePongClient = class {
2112
2456
  webVitals: config.performance.webVitals,
2113
2457
  navigationTiming: config.performance.navigationTiming,
2114
2458
  resourceTiming: config.performance.resourceTiming,
2459
+ capturePageLoadTimings: config.performance.capturePageLoadTimings,
2460
+ capturePageRenderTimings: config.performance.capturePageRenderTimings,
2461
+ excludedResourceUrls: config.performance.excludedResourceUrls,
2462
+ resourceNameSanitizer: config.performance.resourceNameSanitizer,
2115
2463
  sampleRate: config.performance.sampleRate,
2116
2464
  flushInterval: config.performance.flushInterval,
2117
2465
  performanceEndpoint: config.performance.performanceEndpoint,
@@ -2820,7 +3168,7 @@ var areFlagsReady = sitepong.areFlagsReady.bind(sitepong);
2820
3168
  var refreshFlags = sitepong.refreshFlags.bind(sitepong);
2821
3169
  var track = sitepong.track.bind(sitepong);
2822
3170
  var trackPageView = sitepong.trackPageView.bind(sitepong);
2823
- var identify = sitepong.identify.bind(sitepong);
3171
+ var identify2 = sitepong.identify.bind(sitepong);
2824
3172
  var group = sitepong.group.bind(sitepong);
2825
3173
  var resetAnalytics = sitepong.resetAnalytics.bind(sitepong);
2826
3174
  var getVisitorId = sitepong.getVisitorId.bind(sitepong);
@@ -2861,6 +3209,6 @@ var onRemoteConfigChange = sitepong.onRemoteConfigChange.bind(sitepong);
2861
3209
  var registerIdentifyHook = sitepong.registerIdentifyHook.bind(sitepong);
2862
3210
  var src_default = sitepong;
2863
3211
 
2864
- export { TracePropagator, addBreadcrumb, areFlagsReady, captureError, captureMessage, clearAnonymousId, clearUser, sitepong as client, createTraceContext, cronCheckin, cronStart, cronWrap, dbTrack, dbTrackSync, src_default as default, endSpan, endTransaction, extractTrace, flush, flushMetrics, flushProfiles, generateSpanId, generateTraceId, getAllFlags, getAnonymousId, getDbNPlusOnePatterns, getDbQueryCount, getDeviceSignals, getFlag, getFraudCheck, getLatestProfile, getProfiles, getRemoteConfig, getReplaySessionId, getVariant, getVariantPayload2 as getVariantPayload, getVisitorId, getWebVitals, group, identify, init, initRN, isInitialized, isRemoteConfigFeatureEnabled, isReplayRecording, metricDistribution, metricGauge, metricHistogram, metricIncrement, metricStartTimer, metricTime, onRemoteConfigChange, profile, propagateTrace, refreshFlags, registerIdentifyHook, registerWebManagerFactories, resetAnalytics, resetDbQueryCount, setAnonymousId, setContext, setCurrentScreen, setEnvironment, setRNGetCurrentScreen, setTags, setUser, startProfileSpan, startReplay, startSpan, startTransaction, stopReplay, track, trackPageView, waitForFlags };
3212
+ export { DEFAULT_SUPERLINK_ENDPOINT, SuperLinkClient, TracePropagator, addBreadcrumb, areFlagsReady, captureError, captureMessage, captureWebDeepLink, clearAnonymousId, clearUser, sitepong as client, completeFromScan, createTraceContext, cronCheckin, cronStart, cronWrap, dbTrack, dbTrackSync, src_default as default, endSpan, endTransaction, extractIdentityMetadata, extractTrace, flush, flushMetrics, flushProfiles, generateSpanId, generateTraceId, getAllFlags, getAnonymousId, getDbNPlusOnePatterns, getDbQueryCount, getDeepLink, getDeviceSignals, getFlag, getFraudCheck, getLatestProfile, getMatchedDeepLink, getProfiles, getRemoteConfig, getReplaySessionId, getVariant, getVariantPayload2 as getVariantPayload, getVisitorId, getWebVitals, group, identify2 as identify, init, initRN, initSuperLink, isInitialized, isRemoteConfigFeatureEnabled, isReplayRecording, metricDistribution, metricGauge, metricHistogram, metricIncrement, metricStartTimer, metricTime, onDeferredDeepLink, onRemoteConfigChange, parseUniversalLink, profile, propagateTrace, refreshFlags, registerIdentifyHook, registerWebManagerFactories, resetAnalytics, resetDbQueryCount, setAnonymousId, setContext, setCurrentScreen, setEnvironment, setRNGetCurrentScreen, setTags, setUser, startProfileSpan, startReplay, startSpan, startTransaction, stopReplay, superlinkClient, identify as superlinkIdentify, track, trackPageView, waitForFlags, writeClipboardToken };
2865
3213
  //# sourceMappingURL=index.mjs.map
2866
3214
  //# sourceMappingURL=index.mjs.map