@tiquo/dom-package 1.5.1 → 1.5.2
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/README.md +15 -0
- package/dist/index.d.mts +72 -21
- package/dist/index.d.ts +72 -21
- package/dist/index.js +470 -80
- package/dist/index.mjs +469 -80
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -641,8 +641,258 @@ function printTiquoBranding() {
|
|
|
641
641
|
}
|
|
642
642
|
}
|
|
643
643
|
printTiquoBranding();
|
|
644
|
+
var ANALYTICS_LOCATION_EVENT = "tiquo:locationchange";
|
|
645
|
+
var analyticsHistoryPatched = false;
|
|
646
|
+
function randomId() {
|
|
647
|
+
try {
|
|
648
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
649
|
+
return crypto.randomUUID();
|
|
650
|
+
}
|
|
651
|
+
} catch {
|
|
652
|
+
}
|
|
653
|
+
return `ev_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
654
|
+
}
|
|
655
|
+
function normalizeHostname(hostname) {
|
|
656
|
+
if (!hostname) return void 0;
|
|
657
|
+
const clean = hostname.trim().toLowerCase().split(":")[0];
|
|
658
|
+
return clean.startsWith("www.") ? clean.slice(4) : clean;
|
|
659
|
+
}
|
|
660
|
+
function getBrowser() {
|
|
661
|
+
if (typeof navigator === "undefined") return "Other";
|
|
662
|
+
const ua = navigator.userAgent;
|
|
663
|
+
if (/Edg(e|A|iOS)?\//i.test(ua)) return "Edge";
|
|
664
|
+
if (/Firefox\//i.test(ua)) return "Firefox";
|
|
665
|
+
if (/Chrome\/|CriOS\//i.test(ua)) return "Chrome";
|
|
666
|
+
if (/Safari\//i.test(ua) && !/Chrome/i.test(ua)) return "Safari";
|
|
667
|
+
return "Other";
|
|
668
|
+
}
|
|
669
|
+
function getDevice() {
|
|
670
|
+
if (typeof navigator === "undefined") return "Desktop";
|
|
671
|
+
const ua = navigator.userAgent;
|
|
672
|
+
if (/iPad|tablet|PlayBook|Silk/i.test(ua)) return "Tablet";
|
|
673
|
+
if (/Android(?!.*Mobile)/i.test(ua) && /Android/i.test(ua) && !/Mobile/i.test(ua))
|
|
674
|
+
return "Tablet";
|
|
675
|
+
if (/Mobile|iPhone|iPod|Android.*Mobile|webOS|BlackBerry|Opera Mini|IEMobile/i.test(
|
|
676
|
+
ua
|
|
677
|
+
))
|
|
678
|
+
return "Mobile";
|
|
679
|
+
return "Desktop";
|
|
680
|
+
}
|
|
681
|
+
function getOperatingSystem() {
|
|
682
|
+
if (typeof navigator === "undefined") return "Other";
|
|
683
|
+
const ua = navigator.userAgent;
|
|
684
|
+
if (/iPhone|iPad|iPod/i.test(ua)) return "iOS";
|
|
685
|
+
if (/Android/i.test(ua)) return "Android";
|
|
686
|
+
if (/CrOS/i.test(ua)) return "Chrome OS";
|
|
687
|
+
if (/Windows/i.test(ua)) return "Windows";
|
|
688
|
+
if (/Macintosh|Mac OS X/i.test(ua)) return "macOS";
|
|
689
|
+
if (/Linux/i.test(ua)) return "Linux";
|
|
690
|
+
return "Other";
|
|
691
|
+
}
|
|
692
|
+
function getUtmParams() {
|
|
693
|
+
if (typeof window === "undefined") return {};
|
|
694
|
+
const params = new URLSearchParams(window.location.search);
|
|
695
|
+
return {
|
|
696
|
+
source: params.get("utm_source") || void 0,
|
|
697
|
+
medium: params.get("utm_medium") || void 0,
|
|
698
|
+
campaign: params.get("utm_campaign") || void 0,
|
|
699
|
+
content: params.get("utm_content") || void 0,
|
|
700
|
+
term: params.get("utm_term") || void 0
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
function patchHistoryForAnalytics() {
|
|
704
|
+
if (analyticsHistoryPatched || typeof window === "undefined") return;
|
|
705
|
+
analyticsHistoryPatched = true;
|
|
706
|
+
const notify = () => {
|
|
707
|
+
window.dispatchEvent(
|
|
708
|
+
new Event(ANALYTICS_LOCATION_EVENT)
|
|
709
|
+
);
|
|
710
|
+
};
|
|
711
|
+
const pushState = history.pushState;
|
|
712
|
+
history.pushState = function patchedPushState(...args) {
|
|
713
|
+
const result = pushState.apply(this, args);
|
|
714
|
+
notify();
|
|
715
|
+
return result;
|
|
716
|
+
};
|
|
717
|
+
const replaceState = history.replaceState;
|
|
718
|
+
history.replaceState = function patchedReplaceState(...args) {
|
|
719
|
+
const result = replaceState.apply(this, args);
|
|
720
|
+
notify();
|
|
721
|
+
return result;
|
|
722
|
+
};
|
|
723
|
+
window.addEventListener("popstate", notify);
|
|
724
|
+
}
|
|
725
|
+
var _TiquoAnalytics = class _TiquoAnalytics {
|
|
726
|
+
constructor(config) {
|
|
727
|
+
this.pageStartedAt = Date.now();
|
|
728
|
+
this.lastPath = null;
|
|
729
|
+
this.started = false;
|
|
730
|
+
if (!config.publicKey) {
|
|
731
|
+
throw new TiquoAuthError("publicKey is required", "MISSING_PUBLIC_KEY");
|
|
732
|
+
}
|
|
733
|
+
if (!config.publicKey.startsWith("pk_dom_")) {
|
|
734
|
+
throw new TiquoAuthError(
|
|
735
|
+
"Invalid public key format. Expected pk_dom_xxx",
|
|
736
|
+
"INVALID_PUBLIC_KEY"
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
this.config = {
|
|
740
|
+
publicKey: config.publicKey,
|
|
741
|
+
apiEndpoint: config.apiEndpoint || "https://edge.tiquo.app",
|
|
742
|
+
debug: config.debug || false,
|
|
743
|
+
autoTrackPageviews: config.autoTrackPageviews !== false,
|
|
744
|
+
getAccessToken: config.getAccessToken,
|
|
745
|
+
siteSlug: config.siteSlug
|
|
746
|
+
};
|
|
747
|
+
this.clientSessionId = this.getClientSessionId();
|
|
748
|
+
if (this.config.autoTrackPageviews) {
|
|
749
|
+
this.start();
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
start() {
|
|
753
|
+
if (this.started || typeof window === "undefined") return;
|
|
754
|
+
const activeKey = `${this.config.publicKey}:${this.config.siteSlug || ""}`;
|
|
755
|
+
const existing = _TiquoAnalytics.activeInstances.get(activeKey);
|
|
756
|
+
if (existing && existing !== this) {
|
|
757
|
+
existing.adoptConfig(this.config);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
_TiquoAnalytics.activeInstances.set(activeKey, this);
|
|
761
|
+
this.started = true;
|
|
762
|
+
patchHistoryForAnalytics();
|
|
763
|
+
this.trackPageview();
|
|
764
|
+
window.addEventListener(
|
|
765
|
+
ANALYTICS_LOCATION_EVENT,
|
|
766
|
+
() => this.trackPageview()
|
|
767
|
+
);
|
|
768
|
+
window.addEventListener("pagehide", () => this.trackEngagement());
|
|
769
|
+
document.addEventListener("visibilitychange", () => {
|
|
770
|
+
if (document.visibilityState === "hidden") this.trackEngagement();
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
async trackPageview(options = {}) {
|
|
774
|
+
if (typeof window === "undefined") return;
|
|
775
|
+
const path = options.path || `${window.location.pathname}${window.location.search}`;
|
|
776
|
+
if (path === this.lastPath) return;
|
|
777
|
+
if (this.lastPath) {
|
|
778
|
+
this.trackEngagement().catch(() => void 0);
|
|
779
|
+
}
|
|
780
|
+
this.lastPath = path;
|
|
781
|
+
this.pageStartedAt = Date.now();
|
|
782
|
+
await this.track("pageview", {
|
|
783
|
+
...options,
|
|
784
|
+
path,
|
|
785
|
+
url: options.url || window.location.href,
|
|
786
|
+
title: options.title || document.title,
|
|
787
|
+
referrer: options.referrer || document.referrer || void 0
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
async track(eventType, options = {}) {
|
|
791
|
+
if (typeof window === "undefined") return;
|
|
792
|
+
const utm = getUtmParams();
|
|
793
|
+
const payload = {
|
|
794
|
+
publicKey: this.config.publicKey,
|
|
795
|
+
eventId: randomId(),
|
|
796
|
+
eventType,
|
|
797
|
+
eventName: options.eventName,
|
|
798
|
+
eventProperties: options.eventProperties,
|
|
799
|
+
clientSessionId: this.clientSessionId,
|
|
800
|
+
siteSlug: options.siteSlug || this.config.siteSlug,
|
|
801
|
+
pageSlug: options.pageSlug,
|
|
802
|
+
hostname: normalizeHostname(window.location.hostname),
|
|
803
|
+
path: options.path || `${window.location.pathname}${window.location.search}`,
|
|
804
|
+
url: options.url || window.location.href,
|
|
805
|
+
title: options.title || document.title,
|
|
806
|
+
referrer: options.referrer || document.referrer || void 0,
|
|
807
|
+
timestamp: Date.now(),
|
|
808
|
+
browser: getBrowser(),
|
|
809
|
+
device: getDevice(),
|
|
810
|
+
operatingSystem: getOperatingSystem(),
|
|
811
|
+
language: navigator.language,
|
|
812
|
+
screenWidth: window.screen?.width,
|
|
813
|
+
screenHeight: window.screen?.height,
|
|
814
|
+
viewportWidth: window.innerWidth,
|
|
815
|
+
viewportHeight: window.innerHeight,
|
|
816
|
+
durationMs: options.durationMs,
|
|
817
|
+
scrollDepth: options.scrollDepth,
|
|
818
|
+
identityLinkType: options.identityLinkType,
|
|
819
|
+
...utm
|
|
820
|
+
};
|
|
821
|
+
await this.send(payload);
|
|
822
|
+
}
|
|
823
|
+
async identify(options = {}) {
|
|
824
|
+
await this.track("identify", {
|
|
825
|
+
...options,
|
|
826
|
+
identityLinkType: options.identityLinkType || "auth_dom_login"
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
async trackEngagement() {
|
|
830
|
+
if (typeof window === "undefined" || !this.lastPath) return;
|
|
831
|
+
const durationMs = Date.now() - this.pageStartedAt;
|
|
832
|
+
const scrollHeight = Math.max(
|
|
833
|
+
document.documentElement.scrollHeight,
|
|
834
|
+
document.body?.scrollHeight || 0,
|
|
835
|
+
window.innerHeight
|
|
836
|
+
);
|
|
837
|
+
const scrollDepth = scrollHeight > 0 ? Math.min(1, (window.scrollY + window.innerHeight) / scrollHeight) : void 0;
|
|
838
|
+
await this.track("engagement", {
|
|
839
|
+
path: this.lastPath,
|
|
840
|
+
durationMs,
|
|
841
|
+
scrollDepth
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
async send(payload) {
|
|
845
|
+
const headers = {
|
|
846
|
+
"Content-Type": "text/plain;charset=UTF-8",
|
|
847
|
+
"X-Public-Key": this.config.publicKey
|
|
848
|
+
};
|
|
849
|
+
const accessToken = this.config.getAccessToken?.();
|
|
850
|
+
if (accessToken) {
|
|
851
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
852
|
+
}
|
|
853
|
+
try {
|
|
854
|
+
await fetch(`${this.config.apiEndpoint}/api/dom-analytics/event`, {
|
|
855
|
+
method: "POST",
|
|
856
|
+
headers,
|
|
857
|
+
credentials: "include",
|
|
858
|
+
keepalive: true,
|
|
859
|
+
body: JSON.stringify(payload)
|
|
860
|
+
});
|
|
861
|
+
} catch (error) {
|
|
862
|
+
this.log("Analytics event failed:", error);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
getClientSessionId() {
|
|
866
|
+
const key = `tiquo_analytics_session_${this.config.publicKey}`;
|
|
867
|
+
try {
|
|
868
|
+
const existing = sessionStorage.getItem(key);
|
|
869
|
+
if (existing) return existing;
|
|
870
|
+
const next = randomId();
|
|
871
|
+
sessionStorage.setItem(key, next);
|
|
872
|
+
return next;
|
|
873
|
+
} catch {
|
|
874
|
+
return randomId();
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
log(...args) {
|
|
878
|
+
if (this.config.debug) {
|
|
879
|
+
console.log("[TiquoAnalytics]", ...args);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
adoptConfig(nextConfig) {
|
|
883
|
+
if (nextConfig.getAccessToken) {
|
|
884
|
+
this.config.getAccessToken = nextConfig.getAccessToken;
|
|
885
|
+
}
|
|
886
|
+
if (nextConfig.siteSlug && !this.config.siteSlug) {
|
|
887
|
+
this.config.siteSlug = nextConfig.siteSlug;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
_TiquoAnalytics.activeInstances = /* @__PURE__ */ new Map();
|
|
892
|
+
var TiquoAnalytics = _TiquoAnalytics;
|
|
644
893
|
var TiquoCMS = class {
|
|
645
894
|
constructor(config) {
|
|
895
|
+
this.analytics = null;
|
|
646
896
|
if (!config.publicKey) {
|
|
647
897
|
throw new TiquoAuthError("publicKey is required", "MISSING_PUBLIC_KEY");
|
|
648
898
|
}
|
|
@@ -655,8 +905,16 @@ var TiquoCMS = class {
|
|
|
655
905
|
this.config = {
|
|
656
906
|
publicKey: config.publicKey,
|
|
657
907
|
apiEndpoint: config.apiEndpoint || "https://edge.tiquo.app",
|
|
658
|
-
debug: config.debug || false
|
|
908
|
+
debug: config.debug || false,
|
|
909
|
+
analytics: config.analytics !== false
|
|
659
910
|
};
|
|
911
|
+
if (this.config.analytics) {
|
|
912
|
+
this.analytics = new TiquoAnalytics({
|
|
913
|
+
publicKey: this.config.publicKey,
|
|
914
|
+
apiEndpoint: this.config.apiEndpoint,
|
|
915
|
+
debug: this.config.debug
|
|
916
|
+
});
|
|
917
|
+
}
|
|
660
918
|
}
|
|
661
919
|
/**
|
|
662
920
|
* Fetch a published CMS page for the website attached to this public key.
|
|
@@ -665,19 +923,22 @@ var TiquoCMS = class {
|
|
|
665
923
|
* Convex connection details to the consuming website.
|
|
666
924
|
*/
|
|
667
925
|
async getPage(request) {
|
|
668
|
-
const response = await fetch(
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
926
|
+
const response = await fetch(
|
|
927
|
+
`${this.config.apiEndpoint}/api/dom-cms/page`,
|
|
928
|
+
{
|
|
929
|
+
method: "POST",
|
|
930
|
+
headers: {
|
|
931
|
+
"Content-Type": "text/plain;charset=UTF-8"
|
|
932
|
+
},
|
|
933
|
+
credentials: "include",
|
|
934
|
+
body: JSON.stringify({
|
|
935
|
+
publicKey: this.config.publicKey,
|
|
936
|
+
siteSlug: request.siteSlug,
|
|
937
|
+
pageSlug: request.pageSlug || "home",
|
|
938
|
+
templateId: request.templateId
|
|
939
|
+
})
|
|
940
|
+
}
|
|
941
|
+
);
|
|
681
942
|
if (response.status === 404) {
|
|
682
943
|
return null;
|
|
683
944
|
}
|
|
@@ -688,7 +949,11 @@ var TiquoCMS = class {
|
|
|
688
949
|
if (typeof error?.error === "string") message = error.error;
|
|
689
950
|
} catch {
|
|
690
951
|
}
|
|
691
|
-
throw new TiquoAuthError(
|
|
952
|
+
throw new TiquoAuthError(
|
|
953
|
+
message,
|
|
954
|
+
"CMS_PAGE_FETCH_FAILED",
|
|
955
|
+
response.status
|
|
956
|
+
);
|
|
692
957
|
}
|
|
693
958
|
const result = await response.json();
|
|
694
959
|
if (!result?.success || !result?.data) {
|
|
@@ -740,6 +1005,7 @@ var TiquoAuth = class {
|
|
|
740
1005
|
this.visibilityHandler = null;
|
|
741
1006
|
this.iframeObserver = null;
|
|
742
1007
|
this.injectedIframes = /* @__PURE__ */ new WeakSet();
|
|
1008
|
+
this.analytics = null;
|
|
743
1009
|
// Multi-tab sync
|
|
744
1010
|
this.broadcastChannel = null;
|
|
745
1011
|
this.isProcessingTabSync = false;
|
|
@@ -760,10 +1026,19 @@ var TiquoAuth = class {
|
|
|
760
1026
|
debug: config.debug || false,
|
|
761
1027
|
enableTabSync: config.enableTabSync !== false,
|
|
762
1028
|
// Default true
|
|
1029
|
+
analytics: config.analytics !== false,
|
|
763
1030
|
accessToken: config.accessToken,
|
|
764
1031
|
refreshToken: config.refreshToken
|
|
765
1032
|
};
|
|
766
1033
|
this.tabId = this.generateTabId();
|
|
1034
|
+
if (this.config.analytics) {
|
|
1035
|
+
this.analytics = new TiquoAnalytics({
|
|
1036
|
+
publicKey: this.config.publicKey,
|
|
1037
|
+
apiEndpoint: this.config.apiEndpoint,
|
|
1038
|
+
debug: this.config.debug,
|
|
1039
|
+
getAccessToken: () => this.accessToken
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
767
1042
|
if (this.config.enableTabSync) {
|
|
768
1043
|
this.initTabSync();
|
|
769
1044
|
}
|
|
@@ -789,7 +1064,11 @@ var TiquoAuth = class {
|
|
|
789
1064
|
});
|
|
790
1065
|
if (!response.ok) {
|
|
791
1066
|
const error = await response.json().catch(() => ({ error: "Failed to send OTP" }));
|
|
792
|
-
throw new TiquoAuthError(
|
|
1067
|
+
throw new TiquoAuthError(
|
|
1068
|
+
error.error || "Failed to send OTP",
|
|
1069
|
+
"OTP_SEND_FAILED",
|
|
1070
|
+
response.status
|
|
1071
|
+
);
|
|
793
1072
|
}
|
|
794
1073
|
const result = await response.json();
|
|
795
1074
|
return {
|
|
@@ -812,7 +1091,11 @@ var TiquoAuth = class {
|
|
|
812
1091
|
});
|
|
813
1092
|
if (!response.ok) {
|
|
814
1093
|
const error = await response.json().catch(() => ({ error: "Invalid OTP" }));
|
|
815
|
-
throw new TiquoAuthError(
|
|
1094
|
+
throw new TiquoAuthError(
|
|
1095
|
+
error.error || "Invalid OTP",
|
|
1096
|
+
"OTP_VERIFY_FAILED",
|
|
1097
|
+
response.status
|
|
1098
|
+
);
|
|
816
1099
|
}
|
|
817
1100
|
const result = await response.json();
|
|
818
1101
|
this.accessToken = result.accessToken;
|
|
@@ -822,6 +1105,7 @@ var TiquoAuth = class {
|
|
|
822
1105
|
if (this.session?.user?.id) {
|
|
823
1106
|
addCustomerUserId(this.session.user.id);
|
|
824
1107
|
}
|
|
1108
|
+
this.analytics?.identify({ identityLinkType: "auth_dom_login" }).catch(() => void 0);
|
|
825
1109
|
this.broadcastTabSync("LOGIN");
|
|
826
1110
|
return {
|
|
827
1111
|
success: true,
|
|
@@ -869,7 +1153,9 @@ var TiquoAuth = class {
|
|
|
869
1153
|
async updateProfile(updates) {
|
|
870
1154
|
await this.ensureValidToken();
|
|
871
1155
|
const normalizedUpdates = { ...updates };
|
|
872
|
-
const profilePhotoUpload = this.extractUploadableProfilePhoto(
|
|
1156
|
+
const profilePhotoUpload = this.extractUploadableProfilePhoto(
|
|
1157
|
+
normalizedUpdates.profilePhoto
|
|
1158
|
+
);
|
|
873
1159
|
if (profilePhotoUpload) {
|
|
874
1160
|
delete normalizedUpdates.profilePhoto;
|
|
875
1161
|
}
|
|
@@ -877,7 +1163,12 @@ var TiquoAuth = class {
|
|
|
877
1163
|
const normalized = normalizePhone(normalizedUpdates.phone);
|
|
878
1164
|
const validation = validatePhone(normalizedUpdates.phone);
|
|
879
1165
|
if (!validation.valid) {
|
|
880
|
-
this.log(
|
|
1166
|
+
this.log(
|
|
1167
|
+
"\u26A0\uFE0F Phone validation warning:",
|
|
1168
|
+
validation.reason,
|
|
1169
|
+
"- Original:",
|
|
1170
|
+
normalizedUpdates.phone
|
|
1171
|
+
);
|
|
881
1172
|
} else {
|
|
882
1173
|
this.log("Phone normalized:", normalizedUpdates.phone, "\u2192", normalized);
|
|
883
1174
|
}
|
|
@@ -889,7 +1180,12 @@ var TiquoAuth = class {
|
|
|
889
1180
|
const normalized = normalizePhone(p.number);
|
|
890
1181
|
const validation = validatePhone(p.number);
|
|
891
1182
|
if (!validation.valid) {
|
|
892
|
-
this.log(
|
|
1183
|
+
this.log(
|
|
1184
|
+
"\u26A0\uFE0F Phone validation warning:",
|
|
1185
|
+
validation.reason,
|
|
1186
|
+
"- Original:",
|
|
1187
|
+
p.number
|
|
1188
|
+
);
|
|
893
1189
|
}
|
|
894
1190
|
return { ...p, number: normalized || p.number };
|
|
895
1191
|
});
|
|
@@ -903,7 +1199,11 @@ var TiquoAuth = class {
|
|
|
903
1199
|
});
|
|
904
1200
|
if (!response.ok) {
|
|
905
1201
|
const error = await response.json().catch(() => ({ error: "Failed to update profile" }));
|
|
906
|
-
throw new TiquoAuthError(
|
|
1202
|
+
throw new TiquoAuthError(
|
|
1203
|
+
error.error || "Failed to update profile",
|
|
1204
|
+
"PROFILE_UPDATE_FAILED",
|
|
1205
|
+
response.status
|
|
1206
|
+
);
|
|
907
1207
|
}
|
|
908
1208
|
const result = await response.json();
|
|
909
1209
|
if (this.session && result.data?.customer) {
|
|
@@ -935,12 +1235,18 @@ var TiquoAuth = class {
|
|
|
935
1235
|
await this.ensureValidToken();
|
|
936
1236
|
const blob = await this.profilePhotoToBlob(photo);
|
|
937
1237
|
if (!blob.type.startsWith("image/")) {
|
|
938
|
-
throw new TiquoAuthError(
|
|
1238
|
+
throw new TiquoAuthError(
|
|
1239
|
+
"Profile photo must be an image",
|
|
1240
|
+
"INVALID_PROFILE_PHOTO"
|
|
1241
|
+
);
|
|
939
1242
|
}
|
|
940
|
-
const uploadUrlResponse = await this.request(
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1243
|
+
const uploadUrlResponse = await this.request(
|
|
1244
|
+
"/api/client/v1/profile/photo-upload-url",
|
|
1245
|
+
{
|
|
1246
|
+
method: "POST",
|
|
1247
|
+
body: JSON.stringify({})
|
|
1248
|
+
}
|
|
1249
|
+
);
|
|
944
1250
|
if (!uploadUrlResponse.ok) {
|
|
945
1251
|
const error = await uploadUrlResponse.json().catch(() => ({ error: "Failed to create profile photo upload URL" }));
|
|
946
1252
|
throw new TiquoAuthError(
|
|
@@ -952,7 +1258,10 @@ var TiquoAuth = class {
|
|
|
952
1258
|
const uploadUrlResult = await uploadUrlResponse.json();
|
|
953
1259
|
const uploadUrl = uploadUrlResult.data?.uploadUrl;
|
|
954
1260
|
if (!uploadUrl) {
|
|
955
|
-
throw new TiquoAuthError(
|
|
1261
|
+
throw new TiquoAuthError(
|
|
1262
|
+
"Profile photo upload URL was missing",
|
|
1263
|
+
"PROFILE_PHOTO_UPLOAD_URL_FAILED"
|
|
1264
|
+
);
|
|
956
1265
|
}
|
|
957
1266
|
const uploadResponse = await fetch(uploadUrl, {
|
|
958
1267
|
method: "POST",
|
|
@@ -960,17 +1269,27 @@ var TiquoAuth = class {
|
|
|
960
1269
|
body: blob
|
|
961
1270
|
});
|
|
962
1271
|
if (!uploadResponse.ok) {
|
|
963
|
-
throw new TiquoAuthError(
|
|
1272
|
+
throw new TiquoAuthError(
|
|
1273
|
+
"Failed to upload profile photo",
|
|
1274
|
+
"PROFILE_PHOTO_UPLOAD_FAILED",
|
|
1275
|
+
uploadResponse.status
|
|
1276
|
+
);
|
|
964
1277
|
}
|
|
965
1278
|
const uploadResult = await uploadResponse.json();
|
|
966
1279
|
const storageId = uploadResult.storageId;
|
|
967
1280
|
if (!storageId) {
|
|
968
|
-
throw new TiquoAuthError(
|
|
1281
|
+
throw new TiquoAuthError(
|
|
1282
|
+
"Profile photo storage ID was missing",
|
|
1283
|
+
"PROFILE_PHOTO_UPLOAD_FAILED"
|
|
1284
|
+
);
|
|
969
1285
|
}
|
|
970
|
-
const finalizeResponse = await this.request(
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1286
|
+
const finalizeResponse = await this.request(
|
|
1287
|
+
"/api/client/v1/profile/photo",
|
|
1288
|
+
{
|
|
1289
|
+
method: "POST",
|
|
1290
|
+
body: JSON.stringify({ storageId })
|
|
1291
|
+
}
|
|
1292
|
+
);
|
|
974
1293
|
if (!finalizeResponse.ok) {
|
|
975
1294
|
const error = await finalizeResponse.json().catch(() => ({ error: "Failed to update profile photo" }));
|
|
976
1295
|
throw new TiquoAuthError(
|
|
@@ -983,7 +1302,10 @@ var TiquoAuth = class {
|
|
|
983
1302
|
const customer = finalizeResult.data?.customer;
|
|
984
1303
|
const profilePhoto = finalizeResult.data?.profilePhoto;
|
|
985
1304
|
if (!customer || !profilePhoto) {
|
|
986
|
-
throw new TiquoAuthError(
|
|
1305
|
+
throw new TiquoAuthError(
|
|
1306
|
+
"Profile photo update response was incomplete",
|
|
1307
|
+
"PROFILE_PHOTO_UPDATE_FAILED"
|
|
1308
|
+
);
|
|
987
1309
|
}
|
|
988
1310
|
if (this.session && customer) {
|
|
989
1311
|
this.session = {
|
|
@@ -1058,13 +1380,17 @@ var TiquoAuth = class {
|
|
|
1058
1380
|
const response = await fetch(url.toString(), {
|
|
1059
1381
|
method: "GET",
|
|
1060
1382
|
headers: {
|
|
1061
|
-
|
|
1383
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1062
1384
|
},
|
|
1063
1385
|
credentials: "include"
|
|
1064
1386
|
});
|
|
1065
1387
|
if (!response.ok) {
|
|
1066
1388
|
const error = await response.json().catch(() => ({ error: "Failed to get orders" }));
|
|
1067
|
-
throw new TiquoAuthError(
|
|
1389
|
+
throw new TiquoAuthError(
|
|
1390
|
+
error.error || "Failed to get orders",
|
|
1391
|
+
"GET_ORDERS_FAILED",
|
|
1392
|
+
response.status
|
|
1393
|
+
);
|
|
1068
1394
|
}
|
|
1069
1395
|
const result = await response.json();
|
|
1070
1396
|
return result.data || { orders: [], hasMore: false };
|
|
@@ -1096,13 +1422,17 @@ var TiquoAuth = class {
|
|
|
1096
1422
|
const response = await fetch(url.toString(), {
|
|
1097
1423
|
method: "GET",
|
|
1098
1424
|
headers: {
|
|
1099
|
-
|
|
1425
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1100
1426
|
},
|
|
1101
1427
|
credentials: "include"
|
|
1102
1428
|
});
|
|
1103
1429
|
if (!response.ok) {
|
|
1104
1430
|
const error = await response.json().catch(() => ({ error: "Failed to get bookings" }));
|
|
1105
|
-
throw new TiquoAuthError(
|
|
1431
|
+
throw new TiquoAuthError(
|
|
1432
|
+
error.error || "Failed to get bookings",
|
|
1433
|
+
"GET_BOOKINGS_FAILED",
|
|
1434
|
+
response.status
|
|
1435
|
+
);
|
|
1106
1436
|
}
|
|
1107
1437
|
const result = await response.json();
|
|
1108
1438
|
return result.data || { bookings: [], hasMore: false };
|
|
@@ -1144,14 +1474,18 @@ var TiquoAuth = class {
|
|
|
1144
1474
|
const response = await fetch(url.toString(), {
|
|
1145
1475
|
method: "GET",
|
|
1146
1476
|
headers: {
|
|
1147
|
-
|
|
1477
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1148
1478
|
},
|
|
1149
1479
|
credentials: "include"
|
|
1150
1480
|
});
|
|
1151
1481
|
if (!response.ok) {
|
|
1152
1482
|
const error = await response.json().catch(() => ({ error: "Failed to get receipt" }));
|
|
1153
1483
|
const code = response.status === 404 ? "RECEIPT_NOT_AVAILABLE" : "GET_RECEIPT_FAILED";
|
|
1154
|
-
throw new TiquoAuthError(
|
|
1484
|
+
throw new TiquoAuthError(
|
|
1485
|
+
error.error || "Failed to get receipt",
|
|
1486
|
+
code,
|
|
1487
|
+
response.status
|
|
1488
|
+
);
|
|
1155
1489
|
}
|
|
1156
1490
|
const result = await response.json();
|
|
1157
1491
|
return result.data;
|
|
@@ -1176,13 +1510,17 @@ var TiquoAuth = class {
|
|
|
1176
1510
|
const response = await fetch(url.toString(), {
|
|
1177
1511
|
method: "GET",
|
|
1178
1512
|
headers: {
|
|
1179
|
-
|
|
1513
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1180
1514
|
},
|
|
1181
1515
|
credentials: "include"
|
|
1182
1516
|
});
|
|
1183
1517
|
if (!response.ok) {
|
|
1184
1518
|
const error = await response.json().catch(() => ({ error: "Failed to get enquiries" }));
|
|
1185
|
-
throw new TiquoAuthError(
|
|
1519
|
+
throw new TiquoAuthError(
|
|
1520
|
+
error.error || "Failed to get enquiries",
|
|
1521
|
+
"GET_ENQUIRIES_FAILED",
|
|
1522
|
+
response.status
|
|
1523
|
+
);
|
|
1186
1524
|
}
|
|
1187
1525
|
const result = await response.json();
|
|
1188
1526
|
return result.data || { enquiries: [], hasMore: false };
|
|
@@ -1200,16 +1538,23 @@ var TiquoAuth = class {
|
|
|
1200
1538
|
async getCompanies() {
|
|
1201
1539
|
await this.ensureValidToken();
|
|
1202
1540
|
this.log("Fetching customer companies");
|
|
1203
|
-
const response = await fetch(
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
"
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1541
|
+
const response = await fetch(
|
|
1542
|
+
`${this.config.apiEndpoint}/api/client/v1/companies`,
|
|
1543
|
+
{
|
|
1544
|
+
method: "GET",
|
|
1545
|
+
headers: {
|
|
1546
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1547
|
+
},
|
|
1548
|
+
credentials: "include"
|
|
1549
|
+
}
|
|
1550
|
+
);
|
|
1210
1551
|
if (!response.ok) {
|
|
1211
1552
|
const error = await response.json().catch(() => ({ error: "Failed to get companies" }));
|
|
1212
|
-
throw new TiquoAuthError(
|
|
1553
|
+
throw new TiquoAuthError(
|
|
1554
|
+
error.error || "Failed to get companies",
|
|
1555
|
+
"GET_COMPANIES_FAILED",
|
|
1556
|
+
response.status
|
|
1557
|
+
);
|
|
1213
1558
|
}
|
|
1214
1559
|
const result = await response.json();
|
|
1215
1560
|
return result.data || { companies: [] };
|
|
@@ -1231,19 +1576,25 @@ var TiquoAuth = class {
|
|
|
1231
1576
|
async getCompanyColleagues(companyId) {
|
|
1232
1577
|
await this.ensureValidToken();
|
|
1233
1578
|
this.log("Fetching company colleagues for:", companyId);
|
|
1234
|
-
const url = new URL(
|
|
1579
|
+
const url = new URL(
|
|
1580
|
+
`${this.config.apiEndpoint}/api/client/v1/companies/colleagues`
|
|
1581
|
+
);
|
|
1235
1582
|
url.searchParams.set("companyId", companyId);
|
|
1236
1583
|
const response = await fetch(url.toString(), {
|
|
1237
1584
|
method: "GET",
|
|
1238
1585
|
headers: {
|
|
1239
|
-
|
|
1586
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1240
1587
|
},
|
|
1241
1588
|
credentials: "include"
|
|
1242
1589
|
});
|
|
1243
1590
|
if (!response.ok) {
|
|
1244
1591
|
const error = await response.json().catch(() => ({ error: "Failed to get colleagues" }));
|
|
1245
1592
|
const code = response.status === 403 ? "NOT_COMPANY_ADMIN" : "GET_COLLEAGUES_FAILED";
|
|
1246
|
-
throw new TiquoAuthError(
|
|
1593
|
+
throw new TiquoAuthError(
|
|
1594
|
+
error.error || "Failed to get colleagues",
|
|
1595
|
+
code,
|
|
1596
|
+
response.status
|
|
1597
|
+
);
|
|
1247
1598
|
}
|
|
1248
1599
|
const result = await response.json();
|
|
1249
1600
|
return result.data;
|
|
@@ -1264,13 +1615,17 @@ var TiquoAuth = class {
|
|
|
1264
1615
|
});
|
|
1265
1616
|
if (!response.ok) {
|
|
1266
1617
|
const error = await response.json().catch(() => ({ error: "Failed to generate token" }));
|
|
1267
|
-
throw new TiquoAuthError(
|
|
1618
|
+
throw new TiquoAuthError(
|
|
1619
|
+
error.error || "Failed to generate token",
|
|
1620
|
+
"IFRAME_TOKEN_FAILED",
|
|
1621
|
+
response.status
|
|
1622
|
+
);
|
|
1268
1623
|
}
|
|
1269
1624
|
return response.json();
|
|
1270
1625
|
}
|
|
1271
1626
|
/**
|
|
1272
1627
|
* Embed a customer flow with automatic authentication
|
|
1273
|
-
*
|
|
1628
|
+
*
|
|
1274
1629
|
* @param flowUrl - The URL of the customer flow (book.tiquo.app/...)
|
|
1275
1630
|
* @param container - Container element or selector
|
|
1276
1631
|
* @param options - Optional iframe configuration
|
|
@@ -1278,7 +1633,10 @@ var TiquoAuth = class {
|
|
|
1278
1633
|
async embedCustomerFlow(flowUrl, container, options) {
|
|
1279
1634
|
const containerEl = typeof container === "string" ? document.querySelector(container) : container;
|
|
1280
1635
|
if (!containerEl) {
|
|
1281
|
-
throw new TiquoAuthError(
|
|
1636
|
+
throw new TiquoAuthError(
|
|
1637
|
+
"Container element not found",
|
|
1638
|
+
"CONTAINER_NOT_FOUND"
|
|
1639
|
+
);
|
|
1282
1640
|
}
|
|
1283
1641
|
let authToken;
|
|
1284
1642
|
if (this.accessToken) {
|
|
@@ -1304,7 +1662,10 @@ var TiquoAuth = class {
|
|
|
1304
1662
|
iframe.addEventListener("load", options.onLoad);
|
|
1305
1663
|
}
|
|
1306
1664
|
if (options?.onError) {
|
|
1307
|
-
iframe.addEventListener(
|
|
1665
|
+
iframe.addEventListener(
|
|
1666
|
+
"error",
|
|
1667
|
+
() => options.onError(new Error("Failed to load iframe"))
|
|
1668
|
+
);
|
|
1308
1669
|
}
|
|
1309
1670
|
containerEl.innerHTML = "";
|
|
1310
1671
|
containerEl.appendChild(iframe);
|
|
@@ -1433,7 +1794,11 @@ var TiquoAuth = class {
|
|
|
1433
1794
|
this.accessToken = accessToken;
|
|
1434
1795
|
this.refreshToken = params.get("refresh_token");
|
|
1435
1796
|
if (window.history?.replaceState) {
|
|
1436
|
-
window.history.replaceState(
|
|
1797
|
+
window.history.replaceState(
|
|
1798
|
+
null,
|
|
1799
|
+
"",
|
|
1800
|
+
window.location.pathname + window.location.search
|
|
1801
|
+
);
|
|
1437
1802
|
}
|
|
1438
1803
|
return;
|
|
1439
1804
|
}
|
|
@@ -1461,7 +1826,10 @@ var TiquoAuth = class {
|
|
|
1461
1826
|
if (typeof photo === "string" && this.isProfilePhotoBlobUrl(photo)) {
|
|
1462
1827
|
const response = await fetch(photo);
|
|
1463
1828
|
if (!response.ok) {
|
|
1464
|
-
throw new TiquoAuthError(
|
|
1829
|
+
throw new TiquoAuthError(
|
|
1830
|
+
"Failed to read profile photo URI",
|
|
1831
|
+
"INVALID_PROFILE_PHOTO"
|
|
1832
|
+
);
|
|
1465
1833
|
}
|
|
1466
1834
|
return response.blob();
|
|
1467
1835
|
}
|
|
@@ -1529,16 +1897,19 @@ var TiquoAuth = class {
|
|
|
1529
1897
|
this.isRefreshing = true;
|
|
1530
1898
|
this.log("Refreshing access token");
|
|
1531
1899
|
try {
|
|
1532
|
-
const response = await fetch(
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1900
|
+
const response = await fetch(
|
|
1901
|
+
`${this.config.apiEndpoint}/api/client/v1/refresh`,
|
|
1902
|
+
{
|
|
1903
|
+
method: "POST",
|
|
1904
|
+
headers: {
|
|
1905
|
+
"Content-Type": "application/json"
|
|
1906
|
+
},
|
|
1907
|
+
body: JSON.stringify({
|
|
1908
|
+
refresh_token: this.refreshToken
|
|
1909
|
+
}),
|
|
1910
|
+
credentials: "include"
|
|
1911
|
+
}
|
|
1912
|
+
);
|
|
1542
1913
|
if (!response.ok) {
|
|
1543
1914
|
this.log("Token refresh failed, clearing session");
|
|
1544
1915
|
this.clearTokens();
|
|
@@ -1575,13 +1946,16 @@ var TiquoAuth = class {
|
|
|
1575
1946
|
}
|
|
1576
1947
|
try {
|
|
1577
1948
|
await this.refreshTokenIfNeeded();
|
|
1578
|
-
const response = await fetch(
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
"
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1949
|
+
const response = await fetch(
|
|
1950
|
+
`${this.config.apiEndpoint}/api/client/v1/profile`,
|
|
1951
|
+
{
|
|
1952
|
+
method: "GET",
|
|
1953
|
+
headers: {
|
|
1954
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1955
|
+
},
|
|
1956
|
+
credentials: "include"
|
|
1957
|
+
}
|
|
1958
|
+
);
|
|
1585
1959
|
if (!response.ok) {
|
|
1586
1960
|
this.log("Session invalid, clearing");
|
|
1587
1961
|
this.clearTokens();
|
|
@@ -1599,6 +1973,9 @@ var TiquoAuth = class {
|
|
|
1599
1973
|
if (this.session.user?.id) {
|
|
1600
1974
|
addCustomerUserId(this.session.user.id);
|
|
1601
1975
|
}
|
|
1976
|
+
if (this.session.customer) {
|
|
1977
|
+
this.analytics?.identify({ identityLinkType: "auth_dom_login" }).catch(() => void 0);
|
|
1978
|
+
}
|
|
1602
1979
|
this.notifyListeners();
|
|
1603
1980
|
this.scheduleRefresh();
|
|
1604
1981
|
return this.session;
|
|
@@ -1633,8 +2010,12 @@ var TiquoAuth = class {
|
|
|
1633
2010
|
return;
|
|
1634
2011
|
}
|
|
1635
2012
|
try {
|
|
1636
|
-
const accessToken = localStorage.getItem(
|
|
1637
|
-
|
|
2013
|
+
const accessToken = localStorage.getItem(
|
|
2014
|
+
`${this.config.storagePrefix}access_token`
|
|
2015
|
+
);
|
|
2016
|
+
const refreshToken = localStorage.getItem(
|
|
2017
|
+
`${this.config.storagePrefix}refresh_token`
|
|
2018
|
+
);
|
|
1638
2019
|
if (accessToken) {
|
|
1639
2020
|
this.accessToken = accessToken;
|
|
1640
2021
|
this.refreshToken = refreshToken;
|
|
@@ -1746,7 +2127,12 @@ var TiquoAuth = class {
|
|
|
1746
2127
|
if (message.tabId === this.tabId) return;
|
|
1747
2128
|
if (this.isProcessingTabSync) return;
|
|
1748
2129
|
this.isProcessingTabSync = true;
|
|
1749
|
-
this.log(
|
|
2130
|
+
this.log(
|
|
2131
|
+
"Received tab sync message:",
|
|
2132
|
+
message.type,
|
|
2133
|
+
"from tab:",
|
|
2134
|
+
message.tabId
|
|
2135
|
+
);
|
|
1750
2136
|
try {
|
|
1751
2137
|
switch (message.type) {
|
|
1752
2138
|
case "LOGIN":
|
|
@@ -1773,7 +2159,9 @@ var TiquoAuth = class {
|
|
|
1773
2159
|
}
|
|
1774
2160
|
try {
|
|
1775
2161
|
localStorage.removeItem(`${this.config.storagePrefix}access_token`);
|
|
1776
|
-
localStorage.removeItem(
|
|
2162
|
+
localStorage.removeItem(
|
|
2163
|
+
`${this.config.storagePrefix}refresh_token`
|
|
2164
|
+
);
|
|
1777
2165
|
} catch {
|
|
1778
2166
|
}
|
|
1779
2167
|
this.notifyListeners();
|
|
@@ -1876,6 +2264,7 @@ TiquoPhone.getDialCode = getDialCode;
|
|
|
1876
2264
|
TiquoPhone.buildPhone = buildPhone;
|
|
1877
2265
|
var index_default = TiquoAuth;
|
|
1878
2266
|
export {
|
|
2267
|
+
TiquoAnalytics,
|
|
1879
2268
|
TiquoAuth,
|
|
1880
2269
|
TiquoAuthError,
|
|
1881
2270
|
TiquoCMS,
|