sitepong 0.1.13 → 0.2.0

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.
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
+ require('web-vitals');
4
5
  var reactNative = require('react-native');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
7
 
@@ -1773,9 +1774,9 @@ var RemoteConfigManager = class {
1773
1774
  }
1774
1775
  async fetchAndApply() {
1775
1776
  try {
1776
- const platform = detectPlatform();
1777
+ const platform2 = detectPlatform();
1777
1778
  const params = new URLSearchParams({
1778
- platform,
1779
+ platform: platform2,
1779
1780
  ...this.options.sdkVersion ? { sdkVersion: this.options.sdkVersion } : {}
1780
1781
  });
1781
1782
  const response = await fetch(
@@ -1852,18 +1853,209 @@ var RemoteConfigManager = class {
1852
1853
  }
1853
1854
  };
1854
1855
 
1856
+ // src/superlink/client.ts
1857
+ var DEFAULT_SUPERLINK_ENDPOINT = "https://pongl.ink";
1858
+ var SuperLinkClient = class {
1859
+ constructor(config = {}) {
1860
+ this.config = {
1861
+ endpoint: stripTrailingSlash(config.endpoint || DEFAULT_SUPERLINK_ENDPOINT),
1862
+ appId: config.appId,
1863
+ installId: config.installId,
1864
+ debug: config.debug ?? false
1865
+ };
1866
+ }
1867
+ /** Replace config in place (used by init() so module-level helpers see updates). */
1868
+ configure(config) {
1869
+ if (config.endpoint) this.config.endpoint = stripTrailingSlash(config.endpoint);
1870
+ if (config.appId !== void 0) this.config.appId = config.appId;
1871
+ if (config.installId !== void 0) this.config.installId = config.installId;
1872
+ if (config.debug !== void 0) this.config.debug = config.debug;
1873
+ }
1874
+ log(...args) {
1875
+ if (this.config.debug) console.warn("[SuperLink]", ...args);
1876
+ }
1877
+ /**
1878
+ * POST /match — ask the redirect engine to resolve a deferred deep link from
1879
+ * a click token (Android referrer / iOS clipboard) and/or a device
1880
+ * fingerprint. Returns `{ matched: false }` on any error.
1881
+ */
1882
+ async match(body) {
1883
+ const payload = {
1884
+ ...body,
1885
+ install_id: body.install_id ?? this.config.installId
1886
+ };
1887
+ try {
1888
+ const res = await fetch(`${this.config.endpoint}/match`, {
1889
+ method: "POST",
1890
+ headers: { "Content-Type": "application/json" },
1891
+ body: JSON.stringify(payload)
1892
+ });
1893
+ if (!res.ok) {
1894
+ this.log("match failed", res.status);
1895
+ return { matched: false };
1896
+ }
1897
+ const data = await res.json();
1898
+ return data ?? { matched: false };
1899
+ } catch (err) {
1900
+ this.log("match error", err);
1901
+ return { matched: false };
1902
+ }
1903
+ }
1904
+ /**
1905
+ * POST /events — report a lifecycle event (opened / converted) back to the
1906
+ * redirect engine, which fans out to analytics + webhooks. Best-effort.
1907
+ */
1908
+ async reportEvent(input) {
1909
+ try {
1910
+ const res = await fetch(`${this.config.endpoint}/events`, {
1911
+ method: "POST",
1912
+ headers: { "Content-Type": "application/json" },
1913
+ body: JSON.stringify({
1914
+ app_id: this.config.appId,
1915
+ install_id: this.config.installId,
1916
+ ...input
1917
+ })
1918
+ });
1919
+ if (!res.ok) this.log("event failed", input.type, res.status);
1920
+ return res.ok;
1921
+ } catch (err) {
1922
+ this.log("event error", err);
1923
+ return false;
1924
+ }
1925
+ }
1926
+ };
1927
+ function stripTrailingSlash(url) {
1928
+ return url.endsWith("/") ? url.slice(0, -1) : url;
1929
+ }
1930
+ var superlinkClient = new SuperLinkClient();
1931
+
1932
+ // src/superlink/parse.ts
1933
+ var UTM_KEYS = [
1934
+ ["source", "utm_source"],
1935
+ ["medium", "utm_medium"],
1936
+ ["campaign", "utm_campaign"],
1937
+ ["term", "utm_term"],
1938
+ ["content", "utm_content"]
1939
+ ];
1940
+ function parseUniversalLink(url) {
1941
+ let parsed;
1942
+ try {
1943
+ parsed = new URL(url);
1944
+ } catch {
1945
+ return null;
1946
+ }
1947
+ const params = parsed.searchParams;
1948
+ const utm = {};
1949
+ for (const [key, queryKey] of UTM_KEYS) {
1950
+ const value = params.get(queryKey);
1951
+ if (value) utm[key] = value;
1952
+ }
1953
+ const referral = {};
1954
+ const deep_link_data = {};
1955
+ params.forEach((value, key) => {
1956
+ if (key.startsWith("r_")) {
1957
+ referral[key.slice(2)] = value;
1958
+ } else if (key.startsWith("~")) {
1959
+ deep_link_data[key.slice(1)] = value;
1960
+ }
1961
+ });
1962
+ const explicitPath = params.get("$deep_link_path") ?? params.get("dlp");
1963
+ const deep_link_path = explicitPath ?? (parsed.pathname && parsed.pathname !== "/" ? parsed.pathname : null);
1964
+ return {
1965
+ deep_link_path,
1966
+ deep_link_data,
1967
+ utm,
1968
+ referral,
1969
+ click_id: params.get("click_id"),
1970
+ match_type: "none",
1971
+ confidence: 1
1972
+ };
1973
+ }
1974
+
1975
+ // src/superlink/deferred.ts
1976
+ var handlers = /* @__PURE__ */ new Set();
1977
+ var lastMatched = null;
1978
+ function toDeepLink(res) {
1979
+ return {
1980
+ deep_link_path: res.deep_link_path ?? null,
1981
+ deep_link_data: res.deep_link_data ?? {},
1982
+ utm: res.utm ?? {},
1983
+ referral: res.referral ?? {},
1984
+ click_id: res.click_id ?? null,
1985
+ match_type: res.match_type ?? "fingerprint",
1986
+ confidence: res.confidence ?? (res.match_type && res.match_type !== "none" ? 1 : 0)
1987
+ };
1988
+ }
1989
+ function emitDeferredDeepLink(link) {
1990
+ lastMatched = link;
1991
+ for (const handler of handlers) {
1992
+ try {
1993
+ handler(link);
1994
+ } catch (err) {
1995
+ if (superlinkClient.config.debug) console.warn("[SuperLink] handler threw", err);
1996
+ }
1997
+ }
1998
+ }
1999
+ function onDeferredDeepLink(handler) {
2000
+ handlers.add(handler);
2001
+ if (lastMatched) {
2002
+ try {
2003
+ handler(lastMatched);
2004
+ } catch {
2005
+ }
2006
+ }
2007
+ return () => {
2008
+ handlers.delete(handler);
2009
+ };
2010
+ }
2011
+ function getMatchedDeepLink() {
2012
+ return lastMatched;
2013
+ }
2014
+
2015
+ // src/superlink/web.ts
2016
+ function extractIdentityMetadata(url) {
2017
+ const href = url ?? (typeof window !== "undefined" && window.location ? window.location.href : null);
2018
+ if (!href) return void 0;
2019
+ let loc;
2020
+ try {
2021
+ loc = new URL(href);
2022
+ } catch {
2023
+ return void 0;
2024
+ }
2025
+ const email = loc.searchParams.get("email");
2026
+ const phone = loc.searchParams.get("phone");
2027
+ const userId = loc.searchParams.get("user_id");
2028
+ const id = loc.searchParams.get("id");
2029
+ const customRaw = loc.searchParams.get("custom");
2030
+ const identity = {};
2031
+ if (email) identity.email = email;
2032
+ if (phone) identity.phone = phone;
2033
+ if (userId) identity.user_id = userId;
2034
+ if (id) identity.id = id;
2035
+ if (customRaw) {
2036
+ try {
2037
+ const custom = JSON.parse(decodeURIComponent(customRaw));
2038
+ if (typeof custom === "object" && custom !== null) {
2039
+ identity.custom = custom;
2040
+ }
2041
+ } catch {
2042
+ }
2043
+ }
2044
+ return Object.keys(identity).length > 0 ? identity : void 0;
2045
+ }
2046
+
1855
2047
  // src/index.ts
1856
2048
  function installFallbackEnvironment() {
1857
2049
  if (getEnvironment()) return;
1858
2050
  const g = globalThis;
1859
2051
  const hasBrowser = typeof g.window !== "undefined" && typeof g.document !== "undefined";
1860
2052
  const hasNode = !hasBrowser && !!g.process?.versions?.node;
1861
- const platform = hasBrowser ? "browser" : "node";
2053
+ const platform2 = hasBrowser ? "browser" : "node";
1862
2054
  const env2 = {
1863
- platform,
2055
+ platform: platform2,
1864
2056
  storage: null,
1865
2057
  getDeviceInfo() {
1866
- const info = { platform };
2058
+ const info = { platform: platform2 };
1867
2059
  if (g.navigator?.userAgent) info.userAgent = g.navigator.userAgent;
1868
2060
  if (hasBrowser && g.window?.screen) {
1869
2061
  info.screenWidth = g.window.screen.width;
@@ -2058,6 +2250,10 @@ var SitePongClient = class {
2058
2250
  webVitals: config.performance.webVitals,
2059
2251
  navigationTiming: config.performance.navigationTiming,
2060
2252
  resourceTiming: config.performance.resourceTiming,
2253
+ capturePageLoadTimings: config.performance.capturePageLoadTimings,
2254
+ capturePageRenderTimings: config.performance.capturePageRenderTimings,
2255
+ excludedResourceUrls: config.performance.excludedResourceUrls,
2256
+ resourceNameSanitizer: config.performance.resourceNameSanitizer,
2061
2257
  sampleRate: config.performance.sampleRate,
2062
2258
  flushInterval: config.performance.flushInterval,
2063
2259
  performanceEndpoint: config.performance.performanceEndpoint,
@@ -2766,7 +2962,7 @@ var areFlagsReady = sitepong.areFlagsReady.bind(sitepong);
2766
2962
  var refreshFlags = sitepong.refreshFlags.bind(sitepong);
2767
2963
  var track = sitepong.track.bind(sitepong);
2768
2964
  sitepong.trackPageView.bind(sitepong);
2769
- var identify = sitepong.identify.bind(sitepong);
2965
+ var identify2 = sitepong.identify.bind(sitepong);
2770
2966
  var group = sitepong.group.bind(sitepong);
2771
2967
  var resetAnalytics = sitepong.resetAnalytics.bind(sitepong);
2772
2968
  sitepong.getVisitorId.bind(sitepong);
@@ -3532,6 +3728,243 @@ function createTrackedDatabase(db, options = {}) {
3532
3728
  });
3533
3729
  }
3534
3730
 
3731
+ // src/react-native/widgets.ts
3732
+ var syncTimer = null;
3733
+ var cachedVersion = 0;
3734
+ function getNativeModule2() {
3735
+ try {
3736
+ return __require("@sitepong/expo-live-activity");
3737
+ } catch {
3738
+ return null;
3739
+ }
3740
+ }
3741
+ function getEndpoint2() {
3742
+ const cfg = sitepong.config;
3743
+ return cfg?.endpoint || "https://api.sitepong.com";
3744
+ }
3745
+ function getApiKey2() {
3746
+ const cfg = sitepong.config;
3747
+ return cfg?.apiKey || "";
3748
+ }
3749
+ async function syncWidgets(options) {
3750
+ const apiKey = getApiKey2();
3751
+ if (!apiKey) return;
3752
+ const mod = getNativeModule2();
3753
+ if (!mod) return;
3754
+ try {
3755
+ const res = await fetch(`${getEndpoint2()}/api/sdk/widgets/config`, {
3756
+ headers: { "X-API-Key": apiKey }
3757
+ });
3758
+ if (!res.ok) return;
3759
+ const data = await res.json();
3760
+ const widgets = data.widgets || [];
3761
+ const maxVersion = widgets.reduce((max, w) => Math.max(max, w.version), 0);
3762
+ if (maxVersion <= cachedVersion && cachedVersion > 0) return;
3763
+ cachedVersion = maxVersion;
3764
+ await mod.syncWidgetConfigs(JSON.stringify(widgets));
3765
+ } catch (err) {
3766
+ console.warn("[SitePong] Widget sync failed:", err);
3767
+ }
3768
+ if (syncTimer) clearInterval(syncTimer);
3769
+ const interval = options?.refreshInterval ?? 9e5;
3770
+ syncTimer = setInterval(() => syncWidgets(options), interval);
3771
+ }
3772
+ async function updateWidgetData(data) {
3773
+ const mod = getNativeModule2();
3774
+ if (!mod) return;
3775
+ try {
3776
+ await mod.updateWidgetData(JSON.stringify(data));
3777
+ } catch (err) {
3778
+ console.warn("[SitePong] Widget data update failed:", err);
3779
+ }
3780
+ }
3781
+ function triggerWidgetRefresh() {
3782
+ const mod = getNativeModule2();
3783
+ if (!mod) return;
3784
+ mod.updateWidgetData("{}").catch(() => {
3785
+ });
3786
+ }
3787
+ function stopWidgetSync() {
3788
+ if (syncTimer) {
3789
+ clearInterval(syncTimer);
3790
+ syncTimer = null;
3791
+ }
3792
+ }
3793
+ var started = false;
3794
+ function platform() {
3795
+ if (reactNative.Platform.OS === "ios") return "ios";
3796
+ if (reactNative.Platform.OS === "android") return "android";
3797
+ return "other";
3798
+ }
3799
+ function locale() {
3800
+ try {
3801
+ const tag = typeof Intl !== "undefined" && typeof Intl.DateTimeFormat === "function" ? Intl.DateTimeFormat().resolvedOptions().locale : void 0;
3802
+ return tag || void 0;
3803
+ } catch {
3804
+ return void 0;
3805
+ }
3806
+ }
3807
+ function collectFingerprint() {
3808
+ const fp = { platform: platform() };
3809
+ const osVersion = reactNative.Platform.Version;
3810
+ if (osVersion !== void 0 && osVersion !== null) fp.os_version = String(osVersion);
3811
+ fp.os = reactNative.Platform.OS;
3812
+ const lang = locale();
3813
+ if (lang) fp.language = lang;
3814
+ try {
3815
+ const device = __require("expo-device");
3816
+ if (device?.modelName) fp.device_model = device.modelName;
3817
+ } catch {
3818
+ }
3819
+ return fp;
3820
+ }
3821
+ async function readPlayInstallReferrer() {
3822
+ if (reactNative.Platform.OS !== "android") return void 0;
3823
+ let mod = null;
3824
+ try {
3825
+ mod = __require("react-native-play-install-referrer");
3826
+ } catch {
3827
+ return void 0;
3828
+ }
3829
+ const PlayInstallReferrer = mod?.PlayInstallReferrer;
3830
+ if (!PlayInstallReferrer) return void 0;
3831
+ const referrer = await new Promise((resolve) => {
3832
+ try {
3833
+ PlayInstallReferrer.getInstallReferrerInfo((info, error) => {
3834
+ if (error || !info?.installReferrer) return resolve(void 0);
3835
+ resolve(info.installReferrer);
3836
+ });
3837
+ } catch {
3838
+ resolve(void 0);
3839
+ }
3840
+ });
3841
+ if (!referrer) return void 0;
3842
+ try {
3843
+ const params = new URLSearchParams(referrer);
3844
+ return params.get("click_id") ?? params.get("referrer") ?? void 0;
3845
+ } catch {
3846
+ return void 0;
3847
+ }
3848
+ }
3849
+ async function readClipboardToken() {
3850
+ if (reactNative.Platform.OS !== "ios") return void 0;
3851
+ let value;
3852
+ try {
3853
+ const clip = __require("@react-native-clipboard/clipboard");
3854
+ const Clipboard = clip?.default ?? clip;
3855
+ if (Clipboard?.getString) {
3856
+ value = await Clipboard.getString();
3857
+ }
3858
+ } catch {
3859
+ }
3860
+ if (value == null) {
3861
+ try {
3862
+ const expoClip = __require("expo-clipboard");
3863
+ if (expoClip?.getStringAsync) {
3864
+ value = await expoClip.getStringAsync();
3865
+ }
3866
+ } catch {
3867
+ }
3868
+ }
3869
+ return hasSuperLinkToken(value) ? value.trim() : void 0;
3870
+ }
3871
+ function hasSuperLinkToken(value) {
3872
+ if (!value) return false;
3873
+ const trimmed = value.trim();
3874
+ if (/^splk:[\w-]+$/i.test(trimmed)) return true;
3875
+ if (/\/c\/[\w-]+/i.test(trimmed)) return true;
3876
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmed)) return true;
3877
+ return false;
3878
+ }
3879
+ async function initSuperLinkRN(config = {}, identityOverride) {
3880
+ superlinkClient.configure(config);
3881
+ if (started) return getMatchedDeepLink();
3882
+ started = true;
3883
+ const fingerprint = collectFingerprint();
3884
+ const [referrer, clipboardToken] = await Promise.all([
3885
+ readPlayInstallReferrer(),
3886
+ readClipboardToken()
3887
+ ]);
3888
+ let identity = identityOverride;
3889
+ if (!identity && clipboardToken) {
3890
+ try {
3891
+ identity = extractIdentityMetadata(clipboardToken);
3892
+ } catch {
3893
+ }
3894
+ }
3895
+ const res = await superlinkClient.match({
3896
+ fingerprint,
3897
+ ...referrer ? { referrer } : {},
3898
+ ...clipboardToken ? { clipboard_token: clipboardToken } : {},
3899
+ ...identity ? { identity } : {}
3900
+ });
3901
+ if (!res.matched) return null;
3902
+ const link = toDeepLink(res);
3903
+ emitDeferredDeepLink(link);
3904
+ return link;
3905
+ }
3906
+ async function identify3(identity) {
3907
+ if (!identity || Object.keys(identity).length === 0) return null;
3908
+ const res = await superlinkClient.match({
3909
+ identity,
3910
+ fingerprint: collectFingerprint()
3911
+ });
3912
+ if (!res.matched) return null;
3913
+ const link = toDeepLink(res);
3914
+ emitDeferredDeepLink(link);
3915
+ return link;
3916
+ }
3917
+ async function completeFromScan2(scanned) {
3918
+ if (!scanned) return null;
3919
+ const res = await superlinkClient.match({
3920
+ qr_token: scanned,
3921
+ fingerprint: collectFingerprint()
3922
+ });
3923
+ if (!res.matched) return null;
3924
+ const link = toDeepLink(res);
3925
+ emitDeferredDeepLink(link);
3926
+ return link;
3927
+ }
3928
+ function handleUniversalLink(url) {
3929
+ const link = parseUniversalLink(url);
3930
+ if (!link) return null;
3931
+ void superlinkClient.reportEvent({
3932
+ type: "opened",
3933
+ click_id: link.click_id,
3934
+ platform: platform(),
3935
+ properties: { surface: "native", url }
3936
+ });
3937
+ return link;
3938
+ }
3939
+ function createSuperLinkListener(Linking, onLink) {
3940
+ const onUrl = (event) => {
3941
+ const link = handleUniversalLink(event.url);
3942
+ if (link) onLink(link);
3943
+ };
3944
+ Linking.getInitialURL().then((url) => {
3945
+ if (url) onUrl({ url });
3946
+ }).catch(() => {
3947
+ });
3948
+ const subscription = Linking.addEventListener("url", onUrl);
3949
+ return () => {
3950
+ if (subscription && typeof subscription.remove === "function") {
3951
+ subscription.remove();
3952
+ } else if (Linking.removeEventListener) {
3953
+ Linking.removeEventListener("url", onUrl);
3954
+ }
3955
+ };
3956
+ }
3957
+ async function reportConversion(props = {}) {
3958
+ const matched = getMatchedDeepLink();
3959
+ return superlinkClient.reportEvent({
3960
+ type: "converted",
3961
+ click_id: matched?.click_id ?? null,
3962
+ platform: platform(),
3963
+ match_type: matched?.match_type,
3964
+ properties: props
3965
+ });
3966
+ }
3967
+
3535
3968
  // src/entries/rn.ts
3536
3969
  var env = createRNEnvironment();
3537
3970
  setEnvironment(env);
@@ -3549,8 +3982,10 @@ exports.captureMessage = captureMessage;
3549
3982
  exports.clearAnonymousId = clearAnonymousId;
3550
3983
  exports.clearUser = clearUser;
3551
3984
  exports.collectDeviceInfo = collectDeviceInfo;
3985
+ exports.completeFromScan = completeFromScan2;
3552
3986
  exports.createAsyncStorageAdapter = createAsyncStorageAdapter;
3553
3987
  exports.createNavigationTracker = createNavigationTracker;
3988
+ exports.createSuperLinkListener = createSuperLinkListener;
3554
3989
  exports.createTrackedDatabase = createTrackedDatabase;
3555
3990
  exports.endLiveActivity = endLiveActivity;
3556
3991
  exports.fetchNativeDeviceSignals = fetchNativeDeviceSignals;
@@ -3561,22 +3996,28 @@ exports.flushScreenRecordingOnError = flushScreenRecordingOnError;
3561
3996
  exports.getAllFlags = getAllFlags;
3562
3997
  exports.getAnonymousId = getAnonymousId;
3563
3998
  exports.getFlag = getFlag;
3999
+ exports.getMatchedDeepLink = getMatchedDeepLink;
3564
4000
  exports.getRemoteConfig = getRemoteConfig;
3565
4001
  exports.getVariant = getVariant;
3566
4002
  exports.getVariantPayload = getVariantPayload2;
3567
4003
  exports.group = group;
3568
- exports.identify = identify;
4004
+ exports.handleUniversalLink = handleUniversalLink;
4005
+ exports.identify = identify2;
3569
4006
  exports.initRN = initRN;
4007
+ exports.initSuperLinkRN = initSuperLinkRN;
3570
4008
  exports.isInitialized = isInitialized;
3571
4009
  exports.isRemoteConfigFeatureEnabled = isRemoteConfigFeatureEnabled;
3572
4010
  exports.isScreenRecording = isScreenRecording;
3573
4011
  exports.markColdStart = markColdStart;
4012
+ exports.onDeferredDeepLink = onDeferredDeepLink;
3574
4013
  exports.onRemoteConfigChange = onRemoteConfigChange;
4014
+ exports.parseUniversalLink = parseUniversalLink;
3575
4015
  exports.refreshFlags = refreshFlags;
3576
4016
  exports.registerDeviceToken = registerDeviceToken;
3577
4017
  exports.registerLiveActivityToken = registerLiveActivityToken;
3578
4018
  exports.registerPushToStartToken = registerPushToStartToken;
3579
4019
  exports.registerPushToken = registerPushToken;
4020
+ exports.reportConversion = reportConversion;
3580
4021
  exports.resetAnalytics = resetAnalytics;
3581
4022
  exports.setAnonymousId = setAnonymousId;
3582
4023
  exports.setContext = setContext;
@@ -3588,7 +4029,12 @@ exports.setupNetworkInterception = setupNetworkInterception;
3588
4029
  exports.setupRNErrorHandler = setupRNErrorHandler;
3589
4030
  exports.startScreenRecording = startScreenRecording;
3590
4031
  exports.stopScreenRecording = stopScreenRecording;
4032
+ exports.stopWidgetSync = stopWidgetSync;
4033
+ exports.superlinkIdentify = identify3;
4034
+ exports.syncWidgets = syncWidgets;
3591
4035
  exports.track = track;
4036
+ exports.triggerWidgetRefresh = triggerWidgetRefresh;
4037
+ exports.updateWidgetData = updateWidgetData;
3592
4038
  exports.useAppState = useAppState;
3593
4039
  exports.useRNPerformance = useRNPerformance;
3594
4040
  exports.useRemoteConfig = useRemoteConfig;