@behio/storefront-sdk 0.32.0 → 0.34.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.
@@ -147,6 +147,7 @@ var BehioStorefront = class {
147
147
  this.addresses = new AddressModule(this);
148
148
  this.shipping = new ShippingModule(this);
149
149
  this.newsletter = new NewsletterModule(this);
150
+ this.subscriptions = new SubscriptionsModule(this);
150
151
  }
151
152
  // --- Public methods ---
152
153
  /**
@@ -1033,6 +1034,40 @@ var WishlistModule = class {
1033
1034
  return this.client.request("GET", `/customer/wishlist/${productId}/check`);
1034
1035
  }
1035
1036
  };
1037
+ var SubscriptionsModule = class {
1038
+ constructor(client) {
1039
+ this.client = client;
1040
+ }
1041
+ /**
1042
+ * List the logged-in customer's recurring-order subscriptions (products,
1043
+ * cadence, next order date, status). Requires an authenticated customer
1044
+ * session. Subscriptions are created by the merchant in v1.
1045
+ */
1046
+ async list() {
1047
+ return this.client.request("GET", "/customer/subscriptions");
1048
+ }
1049
+ /** Pause an active subscription (no orders are generated while paused). */
1050
+ async pause(subscriptionId) {
1051
+ return this.client.request(
1052
+ "POST",
1053
+ `/customer/subscriptions/${subscriptionId}/pause`
1054
+ );
1055
+ }
1056
+ /** Resume a paused subscription (re-schedules the next order). */
1057
+ async resume(subscriptionId) {
1058
+ return this.client.request(
1059
+ "POST",
1060
+ `/customer/subscriptions/${subscriptionId}/resume`
1061
+ );
1062
+ }
1063
+ /** Cancel a subscription permanently (no more orders). */
1064
+ async cancel(subscriptionId) {
1065
+ return this.client.request(
1066
+ "POST",
1067
+ `/customer/subscriptions/${subscriptionId}/cancel`
1068
+ );
1069
+ }
1070
+ };
1036
1071
  var ReviewsModule = class {
1037
1072
  constructor(client) {
1038
1073
  this.client = client;
@@ -1104,6 +1139,14 @@ var QuotesModule = class {
1104
1139
  async getStatus(quoteId, email) {
1105
1140
  return this.client.request("POST", `/quotes/${quoteId}/status`, { body: { email } });
1106
1141
  }
1142
+ /**
1143
+ * The logged-in customer's own quote requests ("Moje poptávky", GAP-18).
1144
+ * Requires an authenticated session; ownership is the auth token (customer id
1145
+ * + verified email), never a payload. Newest first.
1146
+ */
1147
+ async listMine() {
1148
+ return this.client.request("GET", "/customer/quotes");
1149
+ }
1107
1150
  };
1108
1151
  var AddressModule = class {
1109
1152
  constructor(client) {
@@ -0,0 +1,118 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/react/utils/format-price.ts
2
+ function formatPrice(amount, currency, locale) {
3
+ const resolvedLocale = _nullishCoalesce(locale, () => ( "cs"));
4
+ try {
5
+ return new Intl.NumberFormat(resolvedLocale, {
6
+ style: "currency",
7
+ currency,
8
+ minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
9
+ maximumFractionDigits: 2
10
+ }).format(amount);
11
+ } catch (e) {
12
+ return `${amount} ${currency}`;
13
+ }
14
+ }
15
+
16
+ // src/analytics.ts
17
+ var GA4_NAME_MAP = {
18
+ newsletter_signup: "generate_lead"
19
+ };
20
+ function trackEcommerceEvent(event, payload) {
21
+ if (typeof window === "undefined") return;
22
+ const w = window;
23
+ try {
24
+ _optionalChain([w, 'access', _ => _.__behioEcommerceSink, 'optionalCall', _2 => _2(event, payload)]);
25
+ } catch (e2) {
26
+ }
27
+ const gaName = _nullishCoalesce(GA4_NAME_MAP[event], () => ( event));
28
+ try {
29
+ if (typeof w.gtag === "function") {
30
+ w.gtag("event", gaName, payload);
31
+ return;
32
+ }
33
+ if (Array.isArray(w.dataLayer)) {
34
+ w.dataLayer.push({ ecommerce: null });
35
+ w.dataLayer.push({ event: gaName, ecommerce: payload });
36
+ }
37
+ } catch (e3) {
38
+ }
39
+ }
40
+
41
+ // src/consent-visitor.ts
42
+ var VISITOR_KEY = "behio_visitor_id";
43
+ var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
44
+ function getStoredVisitorId() {
45
+ if (typeof window === "undefined") return null;
46
+ try {
47
+ const ls = localStorage.getItem(VISITOR_KEY);
48
+ if (ls) return ls;
49
+ } catch (e4) {
50
+ }
51
+ return readCookie(VISITOR_KEY);
52
+ }
53
+ function generateVisitorId() {
54
+ const bytes = new Uint8Array(18);
55
+ try {
56
+ _optionalChain([globalThis, 'access', _3 => _3.crypto, 'optionalAccess', _4 => _4.getRandomValues, 'optionalCall', _5 => _5(bytes)]);
57
+ } catch (e5) {
58
+ }
59
+ let filled = false;
60
+ for (const b of bytes) if (b !== 0) filled = true;
61
+ if (!filled) for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
62
+ let bin = "";
63
+ for (const b of bytes) bin += String.fromCharCode(b);
64
+ const b64 = typeof btoa === "function" ? btoa(bin) : Buffer.from(bytes).toString("base64");
65
+ return `v${b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")}`.slice(0, 40);
66
+ }
67
+ function writeVisitorId(id) {
68
+ if (typeof window === "undefined") return;
69
+ try {
70
+ localStorage.setItem(VISITOR_KEY, id);
71
+ } catch (e6) {
72
+ }
73
+ try {
74
+ document.cookie = `${VISITOR_KEY}=${encodeURIComponent(id)}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax`;
75
+ } catch (e7) {
76
+ }
77
+ }
78
+ function readCookie(name) {
79
+ if (typeof document === "undefined") return null;
80
+ const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
81
+ return match ? decodeURIComponent(match[1]) : null;
82
+ }
83
+ function emitConsentChanged() {
84
+ if (typeof window === "undefined") return;
85
+ try {
86
+ window.dispatchEvent(new Event("behio:consent-changed"));
87
+ } catch (e8) {
88
+ }
89
+ }
90
+ async function grantAnalyticsConsent(client, categories) {
91
+ const id = _nullishCoalesce(getStoredVisitorId(), () => ( generateVisitorId()));
92
+ writeVisitorId(id);
93
+ client.setAnalyticsVisitorId(id);
94
+ const res = await client.consent.record({
95
+ visitorId: id,
96
+ analytics: true,
97
+ marketing: _nullishCoalesce(_optionalChain([categories, 'optionalAccess', _6 => _6.marketing]), () => ( false)),
98
+ preferences: _nullishCoalesce(_optionalChain([categories, 'optionalAccess', _7 => _7.preferences]), () => ( false))
99
+ });
100
+ emitConsentChanged();
101
+ return res;
102
+ }
103
+ async function revokeAnalyticsConsent(client) {
104
+ const id = getStoredVisitorId();
105
+ client.setAnalyticsVisitorId(null);
106
+ emitConsentChanged();
107
+ if (!id) return { data: { success: true }, error: null };
108
+ return client.consent.revoke(id);
109
+ }
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+ exports.formatPrice = formatPrice; exports.trackEcommerceEvent = trackEcommerceEvent; exports.getStoredVisitorId = getStoredVisitorId; exports.generateVisitorId = generateVisitorId; exports.grantAnalyticsConsent = grantAnalyticsConsent; exports.revokeAnalyticsConsent = revokeAnalyticsConsent;
@@ -0,0 +1,118 @@
1
+ // src/react/utils/format-price.ts
2
+ function formatPrice(amount, currency, locale) {
3
+ const resolvedLocale = locale ?? "cs";
4
+ try {
5
+ return new Intl.NumberFormat(resolvedLocale, {
6
+ style: "currency",
7
+ currency,
8
+ minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,
9
+ maximumFractionDigits: 2
10
+ }).format(amount);
11
+ } catch {
12
+ return `${amount} ${currency}`;
13
+ }
14
+ }
15
+
16
+ // src/analytics.ts
17
+ var GA4_NAME_MAP = {
18
+ newsletter_signup: "generate_lead"
19
+ };
20
+ function trackEcommerceEvent(event, payload) {
21
+ if (typeof window === "undefined") return;
22
+ const w = window;
23
+ try {
24
+ w.__behioEcommerceSink?.(event, payload);
25
+ } catch {
26
+ }
27
+ const gaName = GA4_NAME_MAP[event] ?? event;
28
+ try {
29
+ if (typeof w.gtag === "function") {
30
+ w.gtag("event", gaName, payload);
31
+ return;
32
+ }
33
+ if (Array.isArray(w.dataLayer)) {
34
+ w.dataLayer.push({ ecommerce: null });
35
+ w.dataLayer.push({ event: gaName, ecommerce: payload });
36
+ }
37
+ } catch {
38
+ }
39
+ }
40
+
41
+ // src/consent-visitor.ts
42
+ var VISITOR_KEY = "behio_visitor_id";
43
+ var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
44
+ function getStoredVisitorId() {
45
+ if (typeof window === "undefined") return null;
46
+ try {
47
+ const ls = localStorage.getItem(VISITOR_KEY);
48
+ if (ls) return ls;
49
+ } catch {
50
+ }
51
+ return readCookie(VISITOR_KEY);
52
+ }
53
+ function generateVisitorId() {
54
+ const bytes = new Uint8Array(18);
55
+ try {
56
+ globalThis.crypto?.getRandomValues?.(bytes);
57
+ } catch {
58
+ }
59
+ let filled = false;
60
+ for (const b of bytes) if (b !== 0) filled = true;
61
+ if (!filled) for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
62
+ let bin = "";
63
+ for (const b of bytes) bin += String.fromCharCode(b);
64
+ const b64 = typeof btoa === "function" ? btoa(bin) : Buffer.from(bytes).toString("base64");
65
+ return `v${b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")}`.slice(0, 40);
66
+ }
67
+ function writeVisitorId(id) {
68
+ if (typeof window === "undefined") return;
69
+ try {
70
+ localStorage.setItem(VISITOR_KEY, id);
71
+ } catch {
72
+ }
73
+ try {
74
+ document.cookie = `${VISITOR_KEY}=${encodeURIComponent(id)}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax`;
75
+ } catch {
76
+ }
77
+ }
78
+ function readCookie(name) {
79
+ if (typeof document === "undefined") return null;
80
+ const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
81
+ return match ? decodeURIComponent(match[1]) : null;
82
+ }
83
+ function emitConsentChanged() {
84
+ if (typeof window === "undefined") return;
85
+ try {
86
+ window.dispatchEvent(new Event("behio:consent-changed"));
87
+ } catch {
88
+ }
89
+ }
90
+ async function grantAnalyticsConsent(client, categories) {
91
+ const id = getStoredVisitorId() ?? generateVisitorId();
92
+ writeVisitorId(id);
93
+ client.setAnalyticsVisitorId(id);
94
+ const res = await client.consent.record({
95
+ visitorId: id,
96
+ analytics: true,
97
+ marketing: categories?.marketing ?? false,
98
+ preferences: categories?.preferences ?? false
99
+ });
100
+ emitConsentChanged();
101
+ return res;
102
+ }
103
+ async function revokeAnalyticsConsent(client) {
104
+ const id = getStoredVisitorId();
105
+ client.setAnalyticsVisitorId(null);
106
+ emitConsentChanged();
107
+ if (!id) return { data: { success: true }, error: null };
108
+ return client.consent.revoke(id);
109
+ }
110
+
111
+ export {
112
+ formatPrice,
113
+ trackEcommerceEvent,
114
+ getStoredVisitorId,
115
+ generateVisitorId,
116
+ grantAnalyticsConsent,
117
+ revokeAnalyticsConsent
118
+ };
@@ -147,6 +147,7 @@ var BehioStorefront = class {
147
147
  this.addresses = new AddressModule(this);
148
148
  this.shipping = new ShippingModule(this);
149
149
  this.newsletter = new NewsletterModule(this);
150
+ this.subscriptions = new SubscriptionsModule(this);
150
151
  }
151
152
  // --- Public methods ---
152
153
  /**
@@ -1033,6 +1034,40 @@ var WishlistModule = class {
1033
1034
  return this.client.request("GET", `/customer/wishlist/${productId}/check`);
1034
1035
  }
1035
1036
  };
1037
+ var SubscriptionsModule = class {
1038
+ constructor(client) {
1039
+ this.client = client;
1040
+ }
1041
+ /**
1042
+ * List the logged-in customer's recurring-order subscriptions (products,
1043
+ * cadence, next order date, status). Requires an authenticated customer
1044
+ * session. Subscriptions are created by the merchant in v1.
1045
+ */
1046
+ async list() {
1047
+ return this.client.request("GET", "/customer/subscriptions");
1048
+ }
1049
+ /** Pause an active subscription (no orders are generated while paused). */
1050
+ async pause(subscriptionId) {
1051
+ return this.client.request(
1052
+ "POST",
1053
+ `/customer/subscriptions/${subscriptionId}/pause`
1054
+ );
1055
+ }
1056
+ /** Resume a paused subscription (re-schedules the next order). */
1057
+ async resume(subscriptionId) {
1058
+ return this.client.request(
1059
+ "POST",
1060
+ `/customer/subscriptions/${subscriptionId}/resume`
1061
+ );
1062
+ }
1063
+ /** Cancel a subscription permanently (no more orders). */
1064
+ async cancel(subscriptionId) {
1065
+ return this.client.request(
1066
+ "POST",
1067
+ `/customer/subscriptions/${subscriptionId}/cancel`
1068
+ );
1069
+ }
1070
+ };
1036
1071
  var ReviewsModule = class {
1037
1072
  constructor(client) {
1038
1073
  this.client = client;
@@ -1104,6 +1139,14 @@ var QuotesModule = class {
1104
1139
  async getStatus(quoteId, email) {
1105
1140
  return this.client.request("POST", `/quotes/${quoteId}/status`, { body: { email } });
1106
1141
  }
1142
+ /**
1143
+ * The logged-in customer's own quote requests ("Moje poptávky", GAP-18).
1144
+ * Requires an authenticated session; ownership is the auth token (customer id
1145
+ * + verified email), never a payload. Newest first.
1146
+ */
1147
+ async listMine() {
1148
+ return this.client.request("GET", "/customer/quotes");
1149
+ }
1107
1150
  };
1108
1151
  var AddressModule = class {
1109
1152
  constructor(client) {