@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.js
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
TiquoAnalytics: () => TiquoAnalytics,
|
|
23
24
|
TiquoAuth: () => TiquoAuth,
|
|
24
25
|
TiquoAuthError: () => TiquoAuthError,
|
|
25
26
|
TiquoCMS: () => TiquoCMS,
|
|
@@ -678,8 +679,258 @@ function printTiquoBranding() {
|
|
|
678
679
|
}
|
|
679
680
|
}
|
|
680
681
|
printTiquoBranding();
|
|
682
|
+
var ANALYTICS_LOCATION_EVENT = "tiquo:locationchange";
|
|
683
|
+
var analyticsHistoryPatched = false;
|
|
684
|
+
function randomId() {
|
|
685
|
+
try {
|
|
686
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
687
|
+
return crypto.randomUUID();
|
|
688
|
+
}
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
return `ev_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
692
|
+
}
|
|
693
|
+
function normalizeHostname(hostname) {
|
|
694
|
+
if (!hostname) return void 0;
|
|
695
|
+
const clean = hostname.trim().toLowerCase().split(":")[0];
|
|
696
|
+
return clean.startsWith("www.") ? clean.slice(4) : clean;
|
|
697
|
+
}
|
|
698
|
+
function getBrowser() {
|
|
699
|
+
if (typeof navigator === "undefined") return "Other";
|
|
700
|
+
const ua = navigator.userAgent;
|
|
701
|
+
if (/Edg(e|A|iOS)?\//i.test(ua)) return "Edge";
|
|
702
|
+
if (/Firefox\//i.test(ua)) return "Firefox";
|
|
703
|
+
if (/Chrome\/|CriOS\//i.test(ua)) return "Chrome";
|
|
704
|
+
if (/Safari\//i.test(ua) && !/Chrome/i.test(ua)) return "Safari";
|
|
705
|
+
return "Other";
|
|
706
|
+
}
|
|
707
|
+
function getDevice() {
|
|
708
|
+
if (typeof navigator === "undefined") return "Desktop";
|
|
709
|
+
const ua = navigator.userAgent;
|
|
710
|
+
if (/iPad|tablet|PlayBook|Silk/i.test(ua)) return "Tablet";
|
|
711
|
+
if (/Android(?!.*Mobile)/i.test(ua) && /Android/i.test(ua) && !/Mobile/i.test(ua))
|
|
712
|
+
return "Tablet";
|
|
713
|
+
if (/Mobile|iPhone|iPod|Android.*Mobile|webOS|BlackBerry|Opera Mini|IEMobile/i.test(
|
|
714
|
+
ua
|
|
715
|
+
))
|
|
716
|
+
return "Mobile";
|
|
717
|
+
return "Desktop";
|
|
718
|
+
}
|
|
719
|
+
function getOperatingSystem() {
|
|
720
|
+
if (typeof navigator === "undefined") return "Other";
|
|
721
|
+
const ua = navigator.userAgent;
|
|
722
|
+
if (/iPhone|iPad|iPod/i.test(ua)) return "iOS";
|
|
723
|
+
if (/Android/i.test(ua)) return "Android";
|
|
724
|
+
if (/CrOS/i.test(ua)) return "Chrome OS";
|
|
725
|
+
if (/Windows/i.test(ua)) return "Windows";
|
|
726
|
+
if (/Macintosh|Mac OS X/i.test(ua)) return "macOS";
|
|
727
|
+
if (/Linux/i.test(ua)) return "Linux";
|
|
728
|
+
return "Other";
|
|
729
|
+
}
|
|
730
|
+
function getUtmParams() {
|
|
731
|
+
if (typeof window === "undefined") return {};
|
|
732
|
+
const params = new URLSearchParams(window.location.search);
|
|
733
|
+
return {
|
|
734
|
+
source: params.get("utm_source") || void 0,
|
|
735
|
+
medium: params.get("utm_medium") || void 0,
|
|
736
|
+
campaign: params.get("utm_campaign") || void 0,
|
|
737
|
+
content: params.get("utm_content") || void 0,
|
|
738
|
+
term: params.get("utm_term") || void 0
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function patchHistoryForAnalytics() {
|
|
742
|
+
if (analyticsHistoryPatched || typeof window === "undefined") return;
|
|
743
|
+
analyticsHistoryPatched = true;
|
|
744
|
+
const notify = () => {
|
|
745
|
+
window.dispatchEvent(
|
|
746
|
+
new Event(ANALYTICS_LOCATION_EVENT)
|
|
747
|
+
);
|
|
748
|
+
};
|
|
749
|
+
const pushState = history.pushState;
|
|
750
|
+
history.pushState = function patchedPushState(...args) {
|
|
751
|
+
const result = pushState.apply(this, args);
|
|
752
|
+
notify();
|
|
753
|
+
return result;
|
|
754
|
+
};
|
|
755
|
+
const replaceState = history.replaceState;
|
|
756
|
+
history.replaceState = function patchedReplaceState(...args) {
|
|
757
|
+
const result = replaceState.apply(this, args);
|
|
758
|
+
notify();
|
|
759
|
+
return result;
|
|
760
|
+
};
|
|
761
|
+
window.addEventListener("popstate", notify);
|
|
762
|
+
}
|
|
763
|
+
var _TiquoAnalytics = class _TiquoAnalytics {
|
|
764
|
+
constructor(config) {
|
|
765
|
+
this.pageStartedAt = Date.now();
|
|
766
|
+
this.lastPath = null;
|
|
767
|
+
this.started = false;
|
|
768
|
+
if (!config.publicKey) {
|
|
769
|
+
throw new TiquoAuthError("publicKey is required", "MISSING_PUBLIC_KEY");
|
|
770
|
+
}
|
|
771
|
+
if (!config.publicKey.startsWith("pk_dom_")) {
|
|
772
|
+
throw new TiquoAuthError(
|
|
773
|
+
"Invalid public key format. Expected pk_dom_xxx",
|
|
774
|
+
"INVALID_PUBLIC_KEY"
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
this.config = {
|
|
778
|
+
publicKey: config.publicKey,
|
|
779
|
+
apiEndpoint: config.apiEndpoint || "https://edge.tiquo.app",
|
|
780
|
+
debug: config.debug || false,
|
|
781
|
+
autoTrackPageviews: config.autoTrackPageviews !== false,
|
|
782
|
+
getAccessToken: config.getAccessToken,
|
|
783
|
+
siteSlug: config.siteSlug
|
|
784
|
+
};
|
|
785
|
+
this.clientSessionId = this.getClientSessionId();
|
|
786
|
+
if (this.config.autoTrackPageviews) {
|
|
787
|
+
this.start();
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
start() {
|
|
791
|
+
if (this.started || typeof window === "undefined") return;
|
|
792
|
+
const activeKey = `${this.config.publicKey}:${this.config.siteSlug || ""}`;
|
|
793
|
+
const existing = _TiquoAnalytics.activeInstances.get(activeKey);
|
|
794
|
+
if (existing && existing !== this) {
|
|
795
|
+
existing.adoptConfig(this.config);
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
_TiquoAnalytics.activeInstances.set(activeKey, this);
|
|
799
|
+
this.started = true;
|
|
800
|
+
patchHistoryForAnalytics();
|
|
801
|
+
this.trackPageview();
|
|
802
|
+
window.addEventListener(
|
|
803
|
+
ANALYTICS_LOCATION_EVENT,
|
|
804
|
+
() => this.trackPageview()
|
|
805
|
+
);
|
|
806
|
+
window.addEventListener("pagehide", () => this.trackEngagement());
|
|
807
|
+
document.addEventListener("visibilitychange", () => {
|
|
808
|
+
if (document.visibilityState === "hidden") this.trackEngagement();
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
async trackPageview(options = {}) {
|
|
812
|
+
if (typeof window === "undefined") return;
|
|
813
|
+
const path = options.path || `${window.location.pathname}${window.location.search}`;
|
|
814
|
+
if (path === this.lastPath) return;
|
|
815
|
+
if (this.lastPath) {
|
|
816
|
+
this.trackEngagement().catch(() => void 0);
|
|
817
|
+
}
|
|
818
|
+
this.lastPath = path;
|
|
819
|
+
this.pageStartedAt = Date.now();
|
|
820
|
+
await this.track("pageview", {
|
|
821
|
+
...options,
|
|
822
|
+
path,
|
|
823
|
+
url: options.url || window.location.href,
|
|
824
|
+
title: options.title || document.title,
|
|
825
|
+
referrer: options.referrer || document.referrer || void 0
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
async track(eventType, options = {}) {
|
|
829
|
+
if (typeof window === "undefined") return;
|
|
830
|
+
const utm = getUtmParams();
|
|
831
|
+
const payload = {
|
|
832
|
+
publicKey: this.config.publicKey,
|
|
833
|
+
eventId: randomId(),
|
|
834
|
+
eventType,
|
|
835
|
+
eventName: options.eventName,
|
|
836
|
+
eventProperties: options.eventProperties,
|
|
837
|
+
clientSessionId: this.clientSessionId,
|
|
838
|
+
siteSlug: options.siteSlug || this.config.siteSlug,
|
|
839
|
+
pageSlug: options.pageSlug,
|
|
840
|
+
hostname: normalizeHostname(window.location.hostname),
|
|
841
|
+
path: options.path || `${window.location.pathname}${window.location.search}`,
|
|
842
|
+
url: options.url || window.location.href,
|
|
843
|
+
title: options.title || document.title,
|
|
844
|
+
referrer: options.referrer || document.referrer || void 0,
|
|
845
|
+
timestamp: Date.now(),
|
|
846
|
+
browser: getBrowser(),
|
|
847
|
+
device: getDevice(),
|
|
848
|
+
operatingSystem: getOperatingSystem(),
|
|
849
|
+
language: navigator.language,
|
|
850
|
+
screenWidth: window.screen?.width,
|
|
851
|
+
screenHeight: window.screen?.height,
|
|
852
|
+
viewportWidth: window.innerWidth,
|
|
853
|
+
viewportHeight: window.innerHeight,
|
|
854
|
+
durationMs: options.durationMs,
|
|
855
|
+
scrollDepth: options.scrollDepth,
|
|
856
|
+
identityLinkType: options.identityLinkType,
|
|
857
|
+
...utm
|
|
858
|
+
};
|
|
859
|
+
await this.send(payload);
|
|
860
|
+
}
|
|
861
|
+
async identify(options = {}) {
|
|
862
|
+
await this.track("identify", {
|
|
863
|
+
...options,
|
|
864
|
+
identityLinkType: options.identityLinkType || "auth_dom_login"
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
async trackEngagement() {
|
|
868
|
+
if (typeof window === "undefined" || !this.lastPath) return;
|
|
869
|
+
const durationMs = Date.now() - this.pageStartedAt;
|
|
870
|
+
const scrollHeight = Math.max(
|
|
871
|
+
document.documentElement.scrollHeight,
|
|
872
|
+
document.body?.scrollHeight || 0,
|
|
873
|
+
window.innerHeight
|
|
874
|
+
);
|
|
875
|
+
const scrollDepth = scrollHeight > 0 ? Math.min(1, (window.scrollY + window.innerHeight) / scrollHeight) : void 0;
|
|
876
|
+
await this.track("engagement", {
|
|
877
|
+
path: this.lastPath,
|
|
878
|
+
durationMs,
|
|
879
|
+
scrollDepth
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
async send(payload) {
|
|
883
|
+
const headers = {
|
|
884
|
+
"Content-Type": "text/plain;charset=UTF-8",
|
|
885
|
+
"X-Public-Key": this.config.publicKey
|
|
886
|
+
};
|
|
887
|
+
const accessToken = this.config.getAccessToken?.();
|
|
888
|
+
if (accessToken) {
|
|
889
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
await fetch(`${this.config.apiEndpoint}/api/dom-analytics/event`, {
|
|
893
|
+
method: "POST",
|
|
894
|
+
headers,
|
|
895
|
+
credentials: "include",
|
|
896
|
+
keepalive: true,
|
|
897
|
+
body: JSON.stringify(payload)
|
|
898
|
+
});
|
|
899
|
+
} catch (error) {
|
|
900
|
+
this.log("Analytics event failed:", error);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
getClientSessionId() {
|
|
904
|
+
const key = `tiquo_analytics_session_${this.config.publicKey}`;
|
|
905
|
+
try {
|
|
906
|
+
const existing = sessionStorage.getItem(key);
|
|
907
|
+
if (existing) return existing;
|
|
908
|
+
const next = randomId();
|
|
909
|
+
sessionStorage.setItem(key, next);
|
|
910
|
+
return next;
|
|
911
|
+
} catch {
|
|
912
|
+
return randomId();
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
log(...args) {
|
|
916
|
+
if (this.config.debug) {
|
|
917
|
+
console.log("[TiquoAnalytics]", ...args);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
adoptConfig(nextConfig) {
|
|
921
|
+
if (nextConfig.getAccessToken) {
|
|
922
|
+
this.config.getAccessToken = nextConfig.getAccessToken;
|
|
923
|
+
}
|
|
924
|
+
if (nextConfig.siteSlug && !this.config.siteSlug) {
|
|
925
|
+
this.config.siteSlug = nextConfig.siteSlug;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
_TiquoAnalytics.activeInstances = /* @__PURE__ */ new Map();
|
|
930
|
+
var TiquoAnalytics = _TiquoAnalytics;
|
|
681
931
|
var TiquoCMS = class {
|
|
682
932
|
constructor(config) {
|
|
933
|
+
this.analytics = null;
|
|
683
934
|
if (!config.publicKey) {
|
|
684
935
|
throw new TiquoAuthError("publicKey is required", "MISSING_PUBLIC_KEY");
|
|
685
936
|
}
|
|
@@ -692,8 +943,16 @@ var TiquoCMS = class {
|
|
|
692
943
|
this.config = {
|
|
693
944
|
publicKey: config.publicKey,
|
|
694
945
|
apiEndpoint: config.apiEndpoint || "https://edge.tiquo.app",
|
|
695
|
-
debug: config.debug || false
|
|
946
|
+
debug: config.debug || false,
|
|
947
|
+
analytics: config.analytics !== false
|
|
696
948
|
};
|
|
949
|
+
if (this.config.analytics) {
|
|
950
|
+
this.analytics = new TiquoAnalytics({
|
|
951
|
+
publicKey: this.config.publicKey,
|
|
952
|
+
apiEndpoint: this.config.apiEndpoint,
|
|
953
|
+
debug: this.config.debug
|
|
954
|
+
});
|
|
955
|
+
}
|
|
697
956
|
}
|
|
698
957
|
/**
|
|
699
958
|
* Fetch a published CMS page for the website attached to this public key.
|
|
@@ -702,19 +961,22 @@ var TiquoCMS = class {
|
|
|
702
961
|
* Convex connection details to the consuming website.
|
|
703
962
|
*/
|
|
704
963
|
async getPage(request) {
|
|
705
|
-
const response = await fetch(
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
964
|
+
const response = await fetch(
|
|
965
|
+
`${this.config.apiEndpoint}/api/dom-cms/page`,
|
|
966
|
+
{
|
|
967
|
+
method: "POST",
|
|
968
|
+
headers: {
|
|
969
|
+
"Content-Type": "text/plain;charset=UTF-8"
|
|
970
|
+
},
|
|
971
|
+
credentials: "include",
|
|
972
|
+
body: JSON.stringify({
|
|
973
|
+
publicKey: this.config.publicKey,
|
|
974
|
+
siteSlug: request.siteSlug,
|
|
975
|
+
pageSlug: request.pageSlug || "home",
|
|
976
|
+
templateId: request.templateId
|
|
977
|
+
})
|
|
978
|
+
}
|
|
979
|
+
);
|
|
718
980
|
if (response.status === 404) {
|
|
719
981
|
return null;
|
|
720
982
|
}
|
|
@@ -725,7 +987,11 @@ var TiquoCMS = class {
|
|
|
725
987
|
if (typeof error?.error === "string") message = error.error;
|
|
726
988
|
} catch {
|
|
727
989
|
}
|
|
728
|
-
throw new TiquoAuthError(
|
|
990
|
+
throw new TiquoAuthError(
|
|
991
|
+
message,
|
|
992
|
+
"CMS_PAGE_FETCH_FAILED",
|
|
993
|
+
response.status
|
|
994
|
+
);
|
|
729
995
|
}
|
|
730
996
|
const result = await response.json();
|
|
731
997
|
if (!result?.success || !result?.data) {
|
|
@@ -777,6 +1043,7 @@ var TiquoAuth = class {
|
|
|
777
1043
|
this.visibilityHandler = null;
|
|
778
1044
|
this.iframeObserver = null;
|
|
779
1045
|
this.injectedIframes = /* @__PURE__ */ new WeakSet();
|
|
1046
|
+
this.analytics = null;
|
|
780
1047
|
// Multi-tab sync
|
|
781
1048
|
this.broadcastChannel = null;
|
|
782
1049
|
this.isProcessingTabSync = false;
|
|
@@ -797,10 +1064,19 @@ var TiquoAuth = class {
|
|
|
797
1064
|
debug: config.debug || false,
|
|
798
1065
|
enableTabSync: config.enableTabSync !== false,
|
|
799
1066
|
// Default true
|
|
1067
|
+
analytics: config.analytics !== false,
|
|
800
1068
|
accessToken: config.accessToken,
|
|
801
1069
|
refreshToken: config.refreshToken
|
|
802
1070
|
};
|
|
803
1071
|
this.tabId = this.generateTabId();
|
|
1072
|
+
if (this.config.analytics) {
|
|
1073
|
+
this.analytics = new TiquoAnalytics({
|
|
1074
|
+
publicKey: this.config.publicKey,
|
|
1075
|
+
apiEndpoint: this.config.apiEndpoint,
|
|
1076
|
+
debug: this.config.debug,
|
|
1077
|
+
getAccessToken: () => this.accessToken
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
804
1080
|
if (this.config.enableTabSync) {
|
|
805
1081
|
this.initTabSync();
|
|
806
1082
|
}
|
|
@@ -826,7 +1102,11 @@ var TiquoAuth = class {
|
|
|
826
1102
|
});
|
|
827
1103
|
if (!response.ok) {
|
|
828
1104
|
const error = await response.json().catch(() => ({ error: "Failed to send OTP" }));
|
|
829
|
-
throw new TiquoAuthError(
|
|
1105
|
+
throw new TiquoAuthError(
|
|
1106
|
+
error.error || "Failed to send OTP",
|
|
1107
|
+
"OTP_SEND_FAILED",
|
|
1108
|
+
response.status
|
|
1109
|
+
);
|
|
830
1110
|
}
|
|
831
1111
|
const result = await response.json();
|
|
832
1112
|
return {
|
|
@@ -849,7 +1129,11 @@ var TiquoAuth = class {
|
|
|
849
1129
|
});
|
|
850
1130
|
if (!response.ok) {
|
|
851
1131
|
const error = await response.json().catch(() => ({ error: "Invalid OTP" }));
|
|
852
|
-
throw new TiquoAuthError(
|
|
1132
|
+
throw new TiquoAuthError(
|
|
1133
|
+
error.error || "Invalid OTP",
|
|
1134
|
+
"OTP_VERIFY_FAILED",
|
|
1135
|
+
response.status
|
|
1136
|
+
);
|
|
853
1137
|
}
|
|
854
1138
|
const result = await response.json();
|
|
855
1139
|
this.accessToken = result.accessToken;
|
|
@@ -859,6 +1143,7 @@ var TiquoAuth = class {
|
|
|
859
1143
|
if (this.session?.user?.id) {
|
|
860
1144
|
addCustomerUserId(this.session.user.id);
|
|
861
1145
|
}
|
|
1146
|
+
this.analytics?.identify({ identityLinkType: "auth_dom_login" }).catch(() => void 0);
|
|
862
1147
|
this.broadcastTabSync("LOGIN");
|
|
863
1148
|
return {
|
|
864
1149
|
success: true,
|
|
@@ -906,7 +1191,9 @@ var TiquoAuth = class {
|
|
|
906
1191
|
async updateProfile(updates) {
|
|
907
1192
|
await this.ensureValidToken();
|
|
908
1193
|
const normalizedUpdates = { ...updates };
|
|
909
|
-
const profilePhotoUpload = this.extractUploadableProfilePhoto(
|
|
1194
|
+
const profilePhotoUpload = this.extractUploadableProfilePhoto(
|
|
1195
|
+
normalizedUpdates.profilePhoto
|
|
1196
|
+
);
|
|
910
1197
|
if (profilePhotoUpload) {
|
|
911
1198
|
delete normalizedUpdates.profilePhoto;
|
|
912
1199
|
}
|
|
@@ -914,7 +1201,12 @@ var TiquoAuth = class {
|
|
|
914
1201
|
const normalized = normalizePhone(normalizedUpdates.phone);
|
|
915
1202
|
const validation = validatePhone(normalizedUpdates.phone);
|
|
916
1203
|
if (!validation.valid) {
|
|
917
|
-
this.log(
|
|
1204
|
+
this.log(
|
|
1205
|
+
"\u26A0\uFE0F Phone validation warning:",
|
|
1206
|
+
validation.reason,
|
|
1207
|
+
"- Original:",
|
|
1208
|
+
normalizedUpdates.phone
|
|
1209
|
+
);
|
|
918
1210
|
} else {
|
|
919
1211
|
this.log("Phone normalized:", normalizedUpdates.phone, "\u2192", normalized);
|
|
920
1212
|
}
|
|
@@ -926,7 +1218,12 @@ var TiquoAuth = class {
|
|
|
926
1218
|
const normalized = normalizePhone(p.number);
|
|
927
1219
|
const validation = validatePhone(p.number);
|
|
928
1220
|
if (!validation.valid) {
|
|
929
|
-
this.log(
|
|
1221
|
+
this.log(
|
|
1222
|
+
"\u26A0\uFE0F Phone validation warning:",
|
|
1223
|
+
validation.reason,
|
|
1224
|
+
"- Original:",
|
|
1225
|
+
p.number
|
|
1226
|
+
);
|
|
930
1227
|
}
|
|
931
1228
|
return { ...p, number: normalized || p.number };
|
|
932
1229
|
});
|
|
@@ -940,7 +1237,11 @@ var TiquoAuth = class {
|
|
|
940
1237
|
});
|
|
941
1238
|
if (!response.ok) {
|
|
942
1239
|
const error = await response.json().catch(() => ({ error: "Failed to update profile" }));
|
|
943
|
-
throw new TiquoAuthError(
|
|
1240
|
+
throw new TiquoAuthError(
|
|
1241
|
+
error.error || "Failed to update profile",
|
|
1242
|
+
"PROFILE_UPDATE_FAILED",
|
|
1243
|
+
response.status
|
|
1244
|
+
);
|
|
944
1245
|
}
|
|
945
1246
|
const result = await response.json();
|
|
946
1247
|
if (this.session && result.data?.customer) {
|
|
@@ -972,12 +1273,18 @@ var TiquoAuth = class {
|
|
|
972
1273
|
await this.ensureValidToken();
|
|
973
1274
|
const blob = await this.profilePhotoToBlob(photo);
|
|
974
1275
|
if (!blob.type.startsWith("image/")) {
|
|
975
|
-
throw new TiquoAuthError(
|
|
1276
|
+
throw new TiquoAuthError(
|
|
1277
|
+
"Profile photo must be an image",
|
|
1278
|
+
"INVALID_PROFILE_PHOTO"
|
|
1279
|
+
);
|
|
976
1280
|
}
|
|
977
|
-
const uploadUrlResponse = await this.request(
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1281
|
+
const uploadUrlResponse = await this.request(
|
|
1282
|
+
"/api/client/v1/profile/photo-upload-url",
|
|
1283
|
+
{
|
|
1284
|
+
method: "POST",
|
|
1285
|
+
body: JSON.stringify({})
|
|
1286
|
+
}
|
|
1287
|
+
);
|
|
981
1288
|
if (!uploadUrlResponse.ok) {
|
|
982
1289
|
const error = await uploadUrlResponse.json().catch(() => ({ error: "Failed to create profile photo upload URL" }));
|
|
983
1290
|
throw new TiquoAuthError(
|
|
@@ -989,7 +1296,10 @@ var TiquoAuth = class {
|
|
|
989
1296
|
const uploadUrlResult = await uploadUrlResponse.json();
|
|
990
1297
|
const uploadUrl = uploadUrlResult.data?.uploadUrl;
|
|
991
1298
|
if (!uploadUrl) {
|
|
992
|
-
throw new TiquoAuthError(
|
|
1299
|
+
throw new TiquoAuthError(
|
|
1300
|
+
"Profile photo upload URL was missing",
|
|
1301
|
+
"PROFILE_PHOTO_UPLOAD_URL_FAILED"
|
|
1302
|
+
);
|
|
993
1303
|
}
|
|
994
1304
|
const uploadResponse = await fetch(uploadUrl, {
|
|
995
1305
|
method: "POST",
|
|
@@ -997,17 +1307,27 @@ var TiquoAuth = class {
|
|
|
997
1307
|
body: blob
|
|
998
1308
|
});
|
|
999
1309
|
if (!uploadResponse.ok) {
|
|
1000
|
-
throw new TiquoAuthError(
|
|
1310
|
+
throw new TiquoAuthError(
|
|
1311
|
+
"Failed to upload profile photo",
|
|
1312
|
+
"PROFILE_PHOTO_UPLOAD_FAILED",
|
|
1313
|
+
uploadResponse.status
|
|
1314
|
+
);
|
|
1001
1315
|
}
|
|
1002
1316
|
const uploadResult = await uploadResponse.json();
|
|
1003
1317
|
const storageId = uploadResult.storageId;
|
|
1004
1318
|
if (!storageId) {
|
|
1005
|
-
throw new TiquoAuthError(
|
|
1319
|
+
throw new TiquoAuthError(
|
|
1320
|
+
"Profile photo storage ID was missing",
|
|
1321
|
+
"PROFILE_PHOTO_UPLOAD_FAILED"
|
|
1322
|
+
);
|
|
1006
1323
|
}
|
|
1007
|
-
const finalizeResponse = await this.request(
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1324
|
+
const finalizeResponse = await this.request(
|
|
1325
|
+
"/api/client/v1/profile/photo",
|
|
1326
|
+
{
|
|
1327
|
+
method: "POST",
|
|
1328
|
+
body: JSON.stringify({ storageId })
|
|
1329
|
+
}
|
|
1330
|
+
);
|
|
1011
1331
|
if (!finalizeResponse.ok) {
|
|
1012
1332
|
const error = await finalizeResponse.json().catch(() => ({ error: "Failed to update profile photo" }));
|
|
1013
1333
|
throw new TiquoAuthError(
|
|
@@ -1020,7 +1340,10 @@ var TiquoAuth = class {
|
|
|
1020
1340
|
const customer = finalizeResult.data?.customer;
|
|
1021
1341
|
const profilePhoto = finalizeResult.data?.profilePhoto;
|
|
1022
1342
|
if (!customer || !profilePhoto) {
|
|
1023
|
-
throw new TiquoAuthError(
|
|
1343
|
+
throw new TiquoAuthError(
|
|
1344
|
+
"Profile photo update response was incomplete",
|
|
1345
|
+
"PROFILE_PHOTO_UPDATE_FAILED"
|
|
1346
|
+
);
|
|
1024
1347
|
}
|
|
1025
1348
|
if (this.session && customer) {
|
|
1026
1349
|
this.session = {
|
|
@@ -1095,13 +1418,17 @@ var TiquoAuth = class {
|
|
|
1095
1418
|
const response = await fetch(url.toString(), {
|
|
1096
1419
|
method: "GET",
|
|
1097
1420
|
headers: {
|
|
1098
|
-
|
|
1421
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1099
1422
|
},
|
|
1100
1423
|
credentials: "include"
|
|
1101
1424
|
});
|
|
1102
1425
|
if (!response.ok) {
|
|
1103
1426
|
const error = await response.json().catch(() => ({ error: "Failed to get orders" }));
|
|
1104
|
-
throw new TiquoAuthError(
|
|
1427
|
+
throw new TiquoAuthError(
|
|
1428
|
+
error.error || "Failed to get orders",
|
|
1429
|
+
"GET_ORDERS_FAILED",
|
|
1430
|
+
response.status
|
|
1431
|
+
);
|
|
1105
1432
|
}
|
|
1106
1433
|
const result = await response.json();
|
|
1107
1434
|
return result.data || { orders: [], hasMore: false };
|
|
@@ -1133,13 +1460,17 @@ var TiquoAuth = class {
|
|
|
1133
1460
|
const response = await fetch(url.toString(), {
|
|
1134
1461
|
method: "GET",
|
|
1135
1462
|
headers: {
|
|
1136
|
-
|
|
1463
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1137
1464
|
},
|
|
1138
1465
|
credentials: "include"
|
|
1139
1466
|
});
|
|
1140
1467
|
if (!response.ok) {
|
|
1141
1468
|
const error = await response.json().catch(() => ({ error: "Failed to get bookings" }));
|
|
1142
|
-
throw new TiquoAuthError(
|
|
1469
|
+
throw new TiquoAuthError(
|
|
1470
|
+
error.error || "Failed to get bookings",
|
|
1471
|
+
"GET_BOOKINGS_FAILED",
|
|
1472
|
+
response.status
|
|
1473
|
+
);
|
|
1143
1474
|
}
|
|
1144
1475
|
const result = await response.json();
|
|
1145
1476
|
return result.data || { bookings: [], hasMore: false };
|
|
@@ -1181,14 +1512,18 @@ var TiquoAuth = class {
|
|
|
1181
1512
|
const response = await fetch(url.toString(), {
|
|
1182
1513
|
method: "GET",
|
|
1183
1514
|
headers: {
|
|
1184
|
-
|
|
1515
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1185
1516
|
},
|
|
1186
1517
|
credentials: "include"
|
|
1187
1518
|
});
|
|
1188
1519
|
if (!response.ok) {
|
|
1189
1520
|
const error = await response.json().catch(() => ({ error: "Failed to get receipt" }));
|
|
1190
1521
|
const code = response.status === 404 ? "RECEIPT_NOT_AVAILABLE" : "GET_RECEIPT_FAILED";
|
|
1191
|
-
throw new TiquoAuthError(
|
|
1522
|
+
throw new TiquoAuthError(
|
|
1523
|
+
error.error || "Failed to get receipt",
|
|
1524
|
+
code,
|
|
1525
|
+
response.status
|
|
1526
|
+
);
|
|
1192
1527
|
}
|
|
1193
1528
|
const result = await response.json();
|
|
1194
1529
|
return result.data;
|
|
@@ -1213,13 +1548,17 @@ var TiquoAuth = class {
|
|
|
1213
1548
|
const response = await fetch(url.toString(), {
|
|
1214
1549
|
method: "GET",
|
|
1215
1550
|
headers: {
|
|
1216
|
-
|
|
1551
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1217
1552
|
},
|
|
1218
1553
|
credentials: "include"
|
|
1219
1554
|
});
|
|
1220
1555
|
if (!response.ok) {
|
|
1221
1556
|
const error = await response.json().catch(() => ({ error: "Failed to get enquiries" }));
|
|
1222
|
-
throw new TiquoAuthError(
|
|
1557
|
+
throw new TiquoAuthError(
|
|
1558
|
+
error.error || "Failed to get enquiries",
|
|
1559
|
+
"GET_ENQUIRIES_FAILED",
|
|
1560
|
+
response.status
|
|
1561
|
+
);
|
|
1223
1562
|
}
|
|
1224
1563
|
const result = await response.json();
|
|
1225
1564
|
return result.data || { enquiries: [], hasMore: false };
|
|
@@ -1237,16 +1576,23 @@ var TiquoAuth = class {
|
|
|
1237
1576
|
async getCompanies() {
|
|
1238
1577
|
await this.ensureValidToken();
|
|
1239
1578
|
this.log("Fetching customer companies");
|
|
1240
|
-
const response = await fetch(
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
"
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1579
|
+
const response = await fetch(
|
|
1580
|
+
`${this.config.apiEndpoint}/api/client/v1/companies`,
|
|
1581
|
+
{
|
|
1582
|
+
method: "GET",
|
|
1583
|
+
headers: {
|
|
1584
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1585
|
+
},
|
|
1586
|
+
credentials: "include"
|
|
1587
|
+
}
|
|
1588
|
+
);
|
|
1247
1589
|
if (!response.ok) {
|
|
1248
1590
|
const error = await response.json().catch(() => ({ error: "Failed to get companies" }));
|
|
1249
|
-
throw new TiquoAuthError(
|
|
1591
|
+
throw new TiquoAuthError(
|
|
1592
|
+
error.error || "Failed to get companies",
|
|
1593
|
+
"GET_COMPANIES_FAILED",
|
|
1594
|
+
response.status
|
|
1595
|
+
);
|
|
1250
1596
|
}
|
|
1251
1597
|
const result = await response.json();
|
|
1252
1598
|
return result.data || { companies: [] };
|
|
@@ -1268,19 +1614,25 @@ var TiquoAuth = class {
|
|
|
1268
1614
|
async getCompanyColleagues(companyId) {
|
|
1269
1615
|
await this.ensureValidToken();
|
|
1270
1616
|
this.log("Fetching company colleagues for:", companyId);
|
|
1271
|
-
const url = new URL(
|
|
1617
|
+
const url = new URL(
|
|
1618
|
+
`${this.config.apiEndpoint}/api/client/v1/companies/colleagues`
|
|
1619
|
+
);
|
|
1272
1620
|
url.searchParams.set("companyId", companyId);
|
|
1273
1621
|
const response = await fetch(url.toString(), {
|
|
1274
1622
|
method: "GET",
|
|
1275
1623
|
headers: {
|
|
1276
|
-
|
|
1624
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1277
1625
|
},
|
|
1278
1626
|
credentials: "include"
|
|
1279
1627
|
});
|
|
1280
1628
|
if (!response.ok) {
|
|
1281
1629
|
const error = await response.json().catch(() => ({ error: "Failed to get colleagues" }));
|
|
1282
1630
|
const code = response.status === 403 ? "NOT_COMPANY_ADMIN" : "GET_COLLEAGUES_FAILED";
|
|
1283
|
-
throw new TiquoAuthError(
|
|
1631
|
+
throw new TiquoAuthError(
|
|
1632
|
+
error.error || "Failed to get colleagues",
|
|
1633
|
+
code,
|
|
1634
|
+
response.status
|
|
1635
|
+
);
|
|
1284
1636
|
}
|
|
1285
1637
|
const result = await response.json();
|
|
1286
1638
|
return result.data;
|
|
@@ -1301,13 +1653,17 @@ var TiquoAuth = class {
|
|
|
1301
1653
|
});
|
|
1302
1654
|
if (!response.ok) {
|
|
1303
1655
|
const error = await response.json().catch(() => ({ error: "Failed to generate token" }));
|
|
1304
|
-
throw new TiquoAuthError(
|
|
1656
|
+
throw new TiquoAuthError(
|
|
1657
|
+
error.error || "Failed to generate token",
|
|
1658
|
+
"IFRAME_TOKEN_FAILED",
|
|
1659
|
+
response.status
|
|
1660
|
+
);
|
|
1305
1661
|
}
|
|
1306
1662
|
return response.json();
|
|
1307
1663
|
}
|
|
1308
1664
|
/**
|
|
1309
1665
|
* Embed a customer flow with automatic authentication
|
|
1310
|
-
*
|
|
1666
|
+
*
|
|
1311
1667
|
* @param flowUrl - The URL of the customer flow (book.tiquo.app/...)
|
|
1312
1668
|
* @param container - Container element or selector
|
|
1313
1669
|
* @param options - Optional iframe configuration
|
|
@@ -1315,7 +1671,10 @@ var TiquoAuth = class {
|
|
|
1315
1671
|
async embedCustomerFlow(flowUrl, container, options) {
|
|
1316
1672
|
const containerEl = typeof container === "string" ? document.querySelector(container) : container;
|
|
1317
1673
|
if (!containerEl) {
|
|
1318
|
-
throw new TiquoAuthError(
|
|
1674
|
+
throw new TiquoAuthError(
|
|
1675
|
+
"Container element not found",
|
|
1676
|
+
"CONTAINER_NOT_FOUND"
|
|
1677
|
+
);
|
|
1319
1678
|
}
|
|
1320
1679
|
let authToken;
|
|
1321
1680
|
if (this.accessToken) {
|
|
@@ -1341,7 +1700,10 @@ var TiquoAuth = class {
|
|
|
1341
1700
|
iframe.addEventListener("load", options.onLoad);
|
|
1342
1701
|
}
|
|
1343
1702
|
if (options?.onError) {
|
|
1344
|
-
iframe.addEventListener(
|
|
1703
|
+
iframe.addEventListener(
|
|
1704
|
+
"error",
|
|
1705
|
+
() => options.onError(new Error("Failed to load iframe"))
|
|
1706
|
+
);
|
|
1345
1707
|
}
|
|
1346
1708
|
containerEl.innerHTML = "";
|
|
1347
1709
|
containerEl.appendChild(iframe);
|
|
@@ -1470,7 +1832,11 @@ var TiquoAuth = class {
|
|
|
1470
1832
|
this.accessToken = accessToken;
|
|
1471
1833
|
this.refreshToken = params.get("refresh_token");
|
|
1472
1834
|
if (window.history?.replaceState) {
|
|
1473
|
-
window.history.replaceState(
|
|
1835
|
+
window.history.replaceState(
|
|
1836
|
+
null,
|
|
1837
|
+
"",
|
|
1838
|
+
window.location.pathname + window.location.search
|
|
1839
|
+
);
|
|
1474
1840
|
}
|
|
1475
1841
|
return;
|
|
1476
1842
|
}
|
|
@@ -1498,7 +1864,10 @@ var TiquoAuth = class {
|
|
|
1498
1864
|
if (typeof photo === "string" && this.isProfilePhotoBlobUrl(photo)) {
|
|
1499
1865
|
const response = await fetch(photo);
|
|
1500
1866
|
if (!response.ok) {
|
|
1501
|
-
throw new TiquoAuthError(
|
|
1867
|
+
throw new TiquoAuthError(
|
|
1868
|
+
"Failed to read profile photo URI",
|
|
1869
|
+
"INVALID_PROFILE_PHOTO"
|
|
1870
|
+
);
|
|
1502
1871
|
}
|
|
1503
1872
|
return response.blob();
|
|
1504
1873
|
}
|
|
@@ -1566,16 +1935,19 @@ var TiquoAuth = class {
|
|
|
1566
1935
|
this.isRefreshing = true;
|
|
1567
1936
|
this.log("Refreshing access token");
|
|
1568
1937
|
try {
|
|
1569
|
-
const response = await fetch(
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1938
|
+
const response = await fetch(
|
|
1939
|
+
`${this.config.apiEndpoint}/api/client/v1/refresh`,
|
|
1940
|
+
{
|
|
1941
|
+
method: "POST",
|
|
1942
|
+
headers: {
|
|
1943
|
+
"Content-Type": "application/json"
|
|
1944
|
+
},
|
|
1945
|
+
body: JSON.stringify({
|
|
1946
|
+
refresh_token: this.refreshToken
|
|
1947
|
+
}),
|
|
1948
|
+
credentials: "include"
|
|
1949
|
+
}
|
|
1950
|
+
);
|
|
1579
1951
|
if (!response.ok) {
|
|
1580
1952
|
this.log("Token refresh failed, clearing session");
|
|
1581
1953
|
this.clearTokens();
|
|
@@ -1612,13 +1984,16 @@ var TiquoAuth = class {
|
|
|
1612
1984
|
}
|
|
1613
1985
|
try {
|
|
1614
1986
|
await this.refreshTokenIfNeeded();
|
|
1615
|
-
const response = await fetch(
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
"
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1987
|
+
const response = await fetch(
|
|
1988
|
+
`${this.config.apiEndpoint}/api/client/v1/profile`,
|
|
1989
|
+
{
|
|
1990
|
+
method: "GET",
|
|
1991
|
+
headers: {
|
|
1992
|
+
Authorization: `Bearer ${this.accessToken}`
|
|
1993
|
+
},
|
|
1994
|
+
credentials: "include"
|
|
1995
|
+
}
|
|
1996
|
+
);
|
|
1622
1997
|
if (!response.ok) {
|
|
1623
1998
|
this.log("Session invalid, clearing");
|
|
1624
1999
|
this.clearTokens();
|
|
@@ -1636,6 +2011,9 @@ var TiquoAuth = class {
|
|
|
1636
2011
|
if (this.session.user?.id) {
|
|
1637
2012
|
addCustomerUserId(this.session.user.id);
|
|
1638
2013
|
}
|
|
2014
|
+
if (this.session.customer) {
|
|
2015
|
+
this.analytics?.identify({ identityLinkType: "auth_dom_login" }).catch(() => void 0);
|
|
2016
|
+
}
|
|
1639
2017
|
this.notifyListeners();
|
|
1640
2018
|
this.scheduleRefresh();
|
|
1641
2019
|
return this.session;
|
|
@@ -1670,8 +2048,12 @@ var TiquoAuth = class {
|
|
|
1670
2048
|
return;
|
|
1671
2049
|
}
|
|
1672
2050
|
try {
|
|
1673
|
-
const accessToken = localStorage.getItem(
|
|
1674
|
-
|
|
2051
|
+
const accessToken = localStorage.getItem(
|
|
2052
|
+
`${this.config.storagePrefix}access_token`
|
|
2053
|
+
);
|
|
2054
|
+
const refreshToken = localStorage.getItem(
|
|
2055
|
+
`${this.config.storagePrefix}refresh_token`
|
|
2056
|
+
);
|
|
1675
2057
|
if (accessToken) {
|
|
1676
2058
|
this.accessToken = accessToken;
|
|
1677
2059
|
this.refreshToken = refreshToken;
|
|
@@ -1783,7 +2165,12 @@ var TiquoAuth = class {
|
|
|
1783
2165
|
if (message.tabId === this.tabId) return;
|
|
1784
2166
|
if (this.isProcessingTabSync) return;
|
|
1785
2167
|
this.isProcessingTabSync = true;
|
|
1786
|
-
this.log(
|
|
2168
|
+
this.log(
|
|
2169
|
+
"Received tab sync message:",
|
|
2170
|
+
message.type,
|
|
2171
|
+
"from tab:",
|
|
2172
|
+
message.tabId
|
|
2173
|
+
);
|
|
1787
2174
|
try {
|
|
1788
2175
|
switch (message.type) {
|
|
1789
2176
|
case "LOGIN":
|
|
@@ -1810,7 +2197,9 @@ var TiquoAuth = class {
|
|
|
1810
2197
|
}
|
|
1811
2198
|
try {
|
|
1812
2199
|
localStorage.removeItem(`${this.config.storagePrefix}access_token`);
|
|
1813
|
-
localStorage.removeItem(
|
|
2200
|
+
localStorage.removeItem(
|
|
2201
|
+
`${this.config.storagePrefix}refresh_token`
|
|
2202
|
+
);
|
|
1814
2203
|
} catch {
|
|
1815
2204
|
}
|
|
1816
2205
|
this.notifyListeners();
|
|
@@ -1914,6 +2303,7 @@ TiquoPhone.buildPhone = buildPhone;
|
|
|
1914
2303
|
var index_default = TiquoAuth;
|
|
1915
2304
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1916
2305
|
0 && (module.exports = {
|
|
2306
|
+
TiquoAnalytics,
|
|
1917
2307
|
TiquoAuth,
|
|
1918
2308
|
TiquoAuthError,
|
|
1919
2309
|
TiquoCMS,
|