@nuitee/booking-widget 1.0.7 → 1.0.8

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 CHANGED
@@ -94,15 +94,15 @@ widget.open();
94
94
  No bundler: load the script and CSS from the CDN, then create the widget.
95
95
 
96
96
  ```html
97
- <link rel="stylesheet" href="https://cdn.thehotelplanet.com/booking-widget/v1.0.7/dist/booking-widget.css">
98
- <script src="https://cdn.thehotelplanet.com/booking-widget/v1.0.7/dist/booking-widget-standalone.js"></script>
97
+ <link rel="stylesheet" href="https://cdn.thehotelplanet.com/booking-widget/v1.0.8/dist/booking-widget.css">
98
+ <script src="https://cdn.thehotelplanet.com/booking-widget/v1.0.8/dist/booking-widget-standalone.js"></script>
99
99
 
100
100
  <div id="booking-widget-container"></div>
101
101
 
102
102
  <script>
103
103
  const widget = new BookingWidget({
104
104
  containerId: 'booking-widget-container',
105
- cssUrl: 'https://cdn.thehotelplanet.com/booking-widget/v1.0.7/dist/booking-widget.css',
105
+ cssUrl: 'https://cdn.thehotelplanet.com/booking-widget/v1.0.8/dist/booking-widget.css',
106
106
  propertyKey: 'your-property-key',
107
107
  onOpen: () => console.log('Opened'),
108
108
  onClose: () => console.log('Closed'),
@@ -20,6 +20,11 @@ const DEFAULT_ROOMS = [];
20
20
 
21
21
  const DEFAULT_RATES = [];
22
22
 
23
+ // Capture the native browser fetch at module-load time — before PostHog (or any other
24
+ // library) has a chance to wrap window.fetch for session recording. PostHog's wrapped
25
+ // fetch drops non-standard request headers such as 'Source', breaking API auth.
26
+ const _fetch = typeof fetch === 'function' ? fetch : /* istanbul ignore next */ undefined;
27
+
23
28
  /**
24
29
  * Returns date as YYYY-MM-DD using local date (no timezone shift).
25
30
  * Calendar dates are created at local midnight; using toISOString() would convert to UTC and can shift the day.
@@ -237,7 +242,7 @@ function createBookingApi(config = {}) {
237
242
  if (url.includes('ngrok')) headers['ngrok-skip-browser-warning'] = 'true';
238
243
  const options = { method, headers };
239
244
  if (body && (method === 'POST' || method === 'PUT')) options.body = JSON.stringify(body);
240
- const res = await fetch(url, options);
245
+ const res = await _fetch(url, options);
241
246
  if (!res.ok) {
242
247
  let body;
243
248
  try { body = await res.json(); } catch (_) {}
@@ -294,7 +299,7 @@ function createBookingApi(config = {}) {
294
299
  try {
295
300
  const getOpts = { method: 'GET', headers: { 'Source': 'booking_engine' } };
296
301
  if (propFullUrl.includes('ngrok')) getOpts.headers['ngrok-skip-browser-warning'] = 'true';
297
- const propRes = await fetch(propFullUrl, getOpts);
302
+ const propRes = await _fetch(propFullUrl, getOpts);
298
303
  if (propRes.ok) {
299
304
  const propData = await propRes.json();
300
305
  propertyRooms = propData?.rooms ?? {};
@@ -691,7 +696,7 @@ async function decryptPropertyId(options = {}) {
691
696
  if (!propertyKey) return null;
692
697
  const base = (baseUrl || '').replace(/\/$/, '');
693
698
  const url = `${base}/${locale}/booking_engine/decrypt_property_id`;
694
- const res = await fetch(url, {
699
+ const res = await _fetch(url, {
695
700
  method: 'POST',
696
701
  headers: { 'Content-Type': 'application/json', 'Source': 'booking_engine', ...headers },
697
702
  body: JSON.stringify({ hash: propertyKey }),
@@ -718,7 +723,7 @@ async function fetchBookingEnginePref(options = {}) {
718
723
  }
719
724
  const base = (baseUrl || '').replace(/\/$/, '');
720
725
  const url = `${base}/api/core/${locale}/booking_engine/booking_engine_pref?property_id=${encodeURIComponent(propertyId)}`;
721
- const res = await fetch(url, {
726
+ const res = await _fetch(url, {
722
727
  method: 'GET',
723
728
  headers: { 'Content-Type': 'application/json', 'Source': 'booking_engine', ...headers },
724
729
  });
@@ -872,8 +877,9 @@ if (typeof window !== 'undefined') {
872
877
  var primaryRgb = hexToRgb(primary), bgHsl = hexToHsl(bg), fgHsl = hexToHsl(fg);
873
878
  var styles = { '--primary': primary, '--primary-fg': primaryFg, '--bg': bg, '--fg': fg, '--card-fg': fg };
874
879
  if (primaryRgb) styles['--primary-rgb'] = primaryRgb[0] + ', ' + primaryRgb[1] + ', ' + primaryRgb[2];
875
- styles['--card'] = c.card || (bgHsl ? 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 2) + '%)' : bg);
876
- styles['--card-solid'] = styles['--card'];
880
+ var _cardBase = c.card || (bgHsl ? 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 2) + '%)' : bg);
881
+ styles['--card'] = c.card ? _cardBase + '40' : _cardBase;
882
+ styles['--card-solid'] = _cardBase;
877
883
  if (bgHsl) {
878
884
  styles['--secondary'] = 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 10) + '%)';
879
885
  styles['--border'] = 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 14) + '%)';
@@ -612,7 +612,7 @@
612
612
  max-width: calc(100vw - 2em);
613
613
  box-sizing: border-box;
614
614
  z-index: 10;
615
- background: var(--card-solid);
615
+ background: var(--bg);
616
616
  border: 1px solid var(--border);
617
617
  border-radius: var(--radius);
618
618
  padding: 1em;
@@ -83,8 +83,9 @@ function deriveWidgetStyles(c) {
83
83
  var primaryRgb = hexToRgb(primary), bgHsl = hexToHsl(bg), fgHsl = hexToHsl(fg);
84
84
  var styles = { '--primary': primary, '--primary-fg': primaryFg, '--bg': bg, '--fg': fg, '--card-fg': fg };
85
85
  if (primaryRgb) styles['--primary-rgb'] = primaryRgb[0] + ', ' + primaryRgb[1] + ', ' + primaryRgb[2];
86
- styles['--card'] = c.card || (bgHsl ? 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 2) + '%)' : bg);
87
- styles['--card-solid'] = styles['--card'];
86
+ var _cardBase = c.card || (bgHsl ? 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 2) + '%)' : bg);
87
+ styles['--card'] = c.card ? _cardBase + '40' : _cardBase;
88
+ styles['--card-solid'] = _cardBase;
88
89
  if (bgHsl) {
89
90
  styles['--secondary'] = 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 10) + '%)';
90
91
  styles['--border'] = 'hsl(' + bgHsl[0] + ', ' + bgHsl[1] + '%, ' + Math.min(95, bgHsl[2] + 14) + '%)';
@@ -19,6 +19,11 @@ const DEFAULT_ROOMS = [];
19
19
 
20
20
  const DEFAULT_RATES = [];
21
21
 
22
+ // Capture the native browser fetch at module-load time — before PostHog (or any other
23
+ // library) has a chance to wrap window.fetch for session recording. PostHog's wrapped
24
+ // fetch drops non-standard request headers such as 'Source', breaking API auth.
25
+ const _fetch = typeof fetch === 'function' ? fetch : /* istanbul ignore next */ undefined;
26
+
22
27
  /**
23
28
  * Returns date as YYYY-MM-DD using local date (no timezone shift).
24
29
  * Calendar dates are created at local midnight; using toISOString() would convert to UTC and can shift the day.
@@ -236,7 +241,7 @@ function createBookingApi(config = {}) {
236
241
  if (url.includes('ngrok')) headers['ngrok-skip-browser-warning'] = 'true';
237
242
  const options = { method, headers };
238
243
  if (body && (method === 'POST' || method === 'PUT')) options.body = JSON.stringify(body);
239
- const res = await fetch(url, options);
244
+ const res = await _fetch(url, options);
240
245
  if (!res.ok) {
241
246
  let body;
242
247
  try { body = await res.json(); } catch (_) {}
@@ -293,7 +298,7 @@ function createBookingApi(config = {}) {
293
298
  try {
294
299
  const getOpts = { method: 'GET', headers: { 'Source': 'booking_engine' } };
295
300
  if (propFullUrl.includes('ngrok')) getOpts.headers['ngrok-skip-browser-warning'] = 'true';
296
- const propRes = await fetch(propFullUrl, getOpts);
301
+ const propRes = await _fetch(propFullUrl, getOpts);
297
302
  if (propRes.ok) {
298
303
  const propData = await propRes.json();
299
304
  propertyRooms = propData?.rooms ?? {};
@@ -690,7 +695,7 @@ async function decryptPropertyId(options = {}) {
690
695
  if (!propertyKey) return null;
691
696
  const base = (baseUrl || '').replace(/\/$/, '');
692
697
  const url = `${base}/${locale}/booking_engine/decrypt_property_id`;
693
- const res = await fetch(url, {
698
+ const res = await _fetch(url, {
694
699
  method: 'POST',
695
700
  headers: { 'Content-Type': 'application/json', 'Source': 'booking_engine', ...headers },
696
701
  body: JSON.stringify({ hash: propertyKey }),
@@ -717,7 +722,7 @@ async function fetchBookingEnginePref(options = {}) {
717
722
  }
718
723
  const base = (baseUrl || '').replace(/\/$/, '');
719
724
  const url = `${base}/api/core/${locale}/booking_engine/booking_engine_pref?property_id=${encodeURIComponent(propertyId)}`;
720
- const res = await fetch(url, {
725
+ const res = await _fetch(url, {
721
726
  method: 'GET',
722
727
  headers: { 'Content-Type': 'application/json', 'Source': 'booking_engine', ...headers },
723
728
  });
@@ -73,7 +73,7 @@ export function deriveWidgetStyles(c) {
73
73
  if (primaryRgb) styles['--primary-rgb'] = `${primaryRgb[0]}, ${primaryRgb[1]}, ${primaryRgb[2]}`;
74
74
 
75
75
  if (cardExplicit) {
76
- styles['--card'] = cardExplicit + "40";
76
+ styles['--card'] = cardExplicit + '40';
77
77
  styles['--card-solid'] = cardExplicit;
78
78
  } else if (bgHsl) {
79
79
  const cardVal = `hsl(${bgHsl[0]}, ${bgHsl[1]}%, ${Math.min(95, bgHsl[2] + 2)}%)`;
@@ -612,7 +612,7 @@
612
612
  max-width: calc(100vw - 2em);
613
613
  box-sizing: border-box;
614
614
  z-index: 10;
615
- background: var(--card-solid);
615
+ background: var(--bg);
616
616
  border: 1px solid var(--border);
617
617
  border-radius: var(--radius);
618
618
  padding: 1em;
@@ -7,6 +7,11 @@ import { STRIPE_PUBLISHABLE_KEY, API_BASE_URL } from '../core/stripe-config.js';
7
7
  import { fetchRuntimeConfig } from '../utils/config-service.js';
8
8
  import { init as initAnalytics, capture as captureEvent, identify as identifyAnalytics } from '../utils/analytics.js';
9
9
 
10
+ // Capture the native browser fetch at module-load time — before PostHog (or any other
11
+ // library) wraps window.fetch for session recording. The wrapped version drops
12
+ // non-standard request headers such as 'Source'.
13
+ const _nativeFetch = typeof fetch === 'function' ? fetch : /* istanbul ignore next */ undefined;
14
+
10
15
  const BASE_STEPS = [
11
16
  { key: 'dates', label: 'Dates & Guests', num: '01' },
12
17
  { key: 'rooms', label: 'Room', num: '02' },
@@ -64,7 +69,7 @@ const BookingWidget = ({
64
69
  const url = effectivePaymentIntentUrl + (isSandbox ? '?sandbox=true' : '');
65
70
  const headers = { 'Content-Type': 'application/json', 'Source': 'booking_engine' };
66
71
  if (url.includes('ngrok')) headers['ngrok-skip-browser-warning'] = 'true';
67
- const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(payload) });
72
+ const res = await _nativeFetch(url, { method: 'POST', headers, body: JSON.stringify(payload) });
68
73
  if (!res.ok) throw new Error(await res.text());
69
74
  const data = await res.json();
70
75
  const clientSecret = data.clientSecret ?? data.client_secret ?? data.data?.clientSecret ?? data.data?.client_secret ?? data.paymentIntent?.client_secret;
@@ -227,7 +232,7 @@ const BookingWidget = ({
227
232
  if (cancelled) return;
228
233
  try {
229
234
  const url = `${effectiveConfirmationBaseUrl}/proxy/confirmation/${encodeURIComponent(String(confirmationToken).trim())}${isSandbox ? '?sandbox=true' : ''}`;
230
- const res = await fetch(url, { method: 'POST', headers: { 'Source': 'booking_engine' } });
235
+ const res = await _nativeFetch(url, { method: 'POST', headers: { 'Source': 'booking_engine' } });
231
236
  if (!res.ok) throw new Error(await res.text());
232
237
  const data = await res.json();
233
238
  const status = data?.status != null ? String(data.status) : '';
@@ -612,7 +612,7 @@
612
612
  max-width: calc(100vw - 2em);
613
613
  box-sizing: border-box;
614
614
  z-index: 10;
615
- background: var(--card-solid);
615
+ background: var(--bg);
616
616
  border: 1px solid var(--border);
617
617
  border-radius: var(--radius);
618
618
  padding: 1em;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * PostHog analytics for the booking widget.
3
+ * The key is sourced from /load-config at runtime; VITE_POSTHOG_KEY/.env or prop overrides are
4
+ * also supported but the config-fetched key is preferred so no hardcoded key is needed.
5
+ * Used by React and Vue; core/standalone use window.posthog when host provides it.
6
+ */
7
+ import posthog from 'posthog-js';
8
+
9
+ let initialized = false;
10
+
11
+ function getConfig(overrides = {}) {
12
+ if (overrides.key != null || overrides.host != null) {
13
+ return {
14
+ key: (overrides.key && String(overrides.key).trim()) || '',
15
+ host: overrides.host || 'https://us.i.posthog.com',
16
+ };
17
+ }
18
+ if (typeof import.meta !== 'undefined' && import.meta.env) {
19
+ const key = import.meta.env.VITE_POSTHOG_KEY || import.meta.env.POSTHOG_KEY || '';
20
+ const host = import.meta.env.VITE_POSTHOG_HOST || import.meta.env.POSTHOG_HOST || 'https://us.i.posthog.com';
21
+ return { key: (key && String(key).trim()) || '', host };
22
+ }
23
+ if (typeof window !== 'undefined') {
24
+ const key = (window.__BOOKING_WIDGET_POSTHOG_KEY__ || '').trim();
25
+ const host = window.__BOOKING_WIDGET_POSTHOG_HOST__ || 'https://us.i.posthog.com';
26
+ return { key, host };
27
+ }
28
+ return { key: '', host: 'https://us.i.posthog.com' };
29
+ }
30
+
31
+ /**
32
+ * Initialize PostHog when key is available. Call once at app/widget load.
33
+ * @param {object} [overrides] - Optional { key, host } to override env.
34
+ */
35
+ export function init(overrides = {}) {
36
+ if (initialized) return posthog;
37
+ const { key, host } = getConfig(overrides);
38
+ if (!key) return null;
39
+ try {
40
+ // Snapshot the native fetch/XHR before posthog.init() wraps them for session recording.
41
+ // PostHog's wrapped fetch drops non-standard headers (e.g. 'Source'), so we restore the
42
+ // originals immediately after init so all subsequent widget API calls are unaffected.
43
+ const _savedFetch = typeof window !== 'undefined' ? window.fetch : undefined;
44
+ const _savedXHROpen = typeof window !== 'undefined' && window.XMLHttpRequest
45
+ ? window.XMLHttpRequest.prototype.open
46
+ : undefined;
47
+
48
+ posthog.init(key, { api_host: host });
49
+
50
+ if (_savedFetch && typeof window !== 'undefined') window.fetch = _savedFetch;
51
+ if (_savedXHROpen && typeof window !== 'undefined') window.XMLHttpRequest.prototype.open = _savedXHROpen;
52
+
53
+ initialized = true;
54
+ return posthog;
55
+ } catch (e) {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Identify the current context (e.g. property/tenant) so events are associated with it.
62
+ * Call with propertyKey when the widget has a known property.
63
+ * @param {string} id - Distinct ID (e.g. propertyKey).
64
+ */
65
+ export function identify(id) {
66
+ try {
67
+ if (initialized && id != null && String(id).trim() && typeof posthog.identify === 'function') {
68
+ posthog.identify(String(id).trim());
69
+ }
70
+ } catch (e) {
71
+ // ignore
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Capture an event. No-op if PostHog is not initialized.
77
+ * @param {string} eventName - Event name (e.g. 'widgetLoaded', 'search').
78
+ * @param {object} [properties] - Optional event properties.
79
+ */
80
+ export function capture(eventName, properties = {}) {
81
+ try {
82
+ if (initialized && typeof posthog.capture === 'function') {
83
+ posthog.capture(eventName, properties);
84
+ }
85
+ } catch (e) {
86
+ // ignore
87
+ }
88
+ }
@@ -10,6 +10,10 @@
10
10
  import { DEFAULT_COLORS } from '../core/stripe-config.js';
11
11
  import { deriveWidgetStyles } from '../core/color-utils.js';
12
12
 
13
+ // Capture native fetch before PostHog can wrap it (PostHog session recording
14
+ // wraps window.fetch and may strip non-standard headers such as 'Source').
15
+ const _nativeFetch = typeof fetch === 'function' ? fetch : /* istanbul ignore next */ undefined;
16
+
13
17
  const CONFIG_BASE_URL = 'https://ai.thehotelplanet.com/load-config';
14
18
 
15
19
  /** In-memory cache: propertyKey → { apiColors, posthogKey } */
@@ -89,7 +93,7 @@ export async function fetchRuntimeConfig(propertyKey, installerColors = null, mo
89
93
  }
90
94
 
91
95
  const url = `${CONFIG_BASE_URL}?apikey=${encodeURIComponent(key)}${isSandbox ? '&mode=sandbox' : ''}`;
92
- const res = await fetch(url, { headers: { 'Source': 'booking_engine' } });
96
+ const res = await _nativeFetch(url, { headers: { 'Source': 'booking_engine' } });
93
97
  if (!res.ok) {
94
98
  throw new Error(`Failed to load widget configuration (HTTP ${res.status}).`);
95
99
  }
@@ -484,6 +484,11 @@ import { STRIPE_PUBLISHABLE_KEY, API_BASE_URL } from '../core/stripe-config.js';
484
484
  import { fetchRuntimeConfig } from '../utils/config-service.js';
485
485
  import { init as initAnalytics, capture as captureEvent, identify as identifyAnalytics } from '../utils/analytics.js';
486
486
 
487
+ // Capture the native browser fetch at module-load time — before PostHog (or any other
488
+ // library) wraps window.fetch for session recording. The wrapped version drops
489
+ // non-standard request headers such as 'Source'.
490
+ const _nativeFetch = typeof fetch === 'function' ? fetch : /* istanbul ignore next */ undefined;
491
+
487
492
 
488
493
  const BASE_STEPS = [
489
494
  { key: 'dates', label: 'Dates & Guests', num: '01' },
@@ -611,7 +616,7 @@ export default {
611
616
  const requestUrl = url + (this.isSandbox ? '?sandbox=true' : '');
612
617
  const headers = { 'Content-Type': 'application/json', 'Source': 'booking_engine' };
613
618
  if (requestUrl.includes('ngrok')) headers['ngrok-skip-browser-warning'] = 'true';
614
- const res = await fetch(requestUrl, { method: 'POST', headers, body: JSON.stringify(payload) });
619
+ const res = await _nativeFetch(requestUrl, { method: 'POST', headers, body: JSON.stringify(payload) });
615
620
  if (!res.ok) throw new Error(await res.text());
616
621
  const data = await res.json();
617
622
  const clientSecret = data.clientSecret ?? data.client_secret ?? data.data?.clientSecret ?? data.data?.client_secret ?? data.paymentIntent?.client_secret;
@@ -821,7 +826,7 @@ export default {
821
826
  const t = String(token || '').trim();
822
827
  if (!t) throw new Error('Missing confirmation token');
823
828
  const url = `${this.effectiveConfirmationBaseUrl}/proxy/confirmation/${encodeURIComponent(t)}${this.isSandbox ? '?sandbox=true' : ''}`;
824
- const res = await fetch(url, { method: 'POST', headers: { 'Source': 'booking_engine' } });
829
+ const res = await _nativeFetch(url, { method: 'POST', headers: { 'Source': 'booking_engine' } });
825
830
  if (!res.ok) throw new Error(await res.text());
826
831
  return await res.json();
827
832
  },
@@ -612,7 +612,7 @@
612
612
  max-width: calc(100vw - 2em);
613
613
  box-sizing: border-box;
614
614
  z-index: 10;
615
- background: var(--card-solid);
615
+ background: var(--bg);
616
616
  border: 1px solid var(--border);
617
617
  border-radius: var(--radius);
618
618
  padding: 1em;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuitee/booking-widget",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "A beautiful, customizable booking widget modal that can be embedded in any website. Supports vanilla JavaScript, React, and Vue.js.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -34,7 +34,7 @@
34
34
  "build": "node scripts/generate-stripe-config.js && npm run build:css && npm run build:js && npm run build:react && npm run build:vue && npm run build:types",
35
35
  "build:stripe-config": "node scripts/generate-stripe-config.js",
36
36
  "build:css": "mkdir -p dist dist/core && cp src/core/styles.css dist/booking-widget.css && cp src/core/styles.css dist/core/styles.css",
37
- "build:js": "mkdir -p dist dist/core dist/utils && cp src/core/widget.js dist/booking-widget.js && cp src/core/booking-api.js dist/core/booking-api.js && cp src/core/stripe-config.js dist/core/stripe-config.js && cp src/core/color-utils.js dist/core/color-utils.js && cp src/utils/config-service.js dist/utils/config-service.js && node scripts/inject-widget-bootstrap.js && cp src/standalone/bundle.js dist/booking-widget-standalone.js && node scripts/build-standalone.js && npm run build:entry",
37
+ "build:js": "mkdir -p dist dist/core dist/utils && cp src/core/widget.js dist/booking-widget.js && cp src/core/booking-api.js dist/core/booking-api.js && cp src/core/stripe-config.js dist/core/stripe-config.js && cp src/core/color-utils.js dist/core/color-utils.js && cp src/utils/config-service.js dist/utils/config-service.js && cp src/utils/analytics.js dist/utils/analytics.js && node scripts/inject-widget-bootstrap.js && cp src/standalone/bundle.js dist/booking-widget-standalone.js && node scripts/build-standalone.js && npm run build:entry",
38
38
  "build:react": "mkdir -p dist/react && cp src/react/BookingWidget.jsx dist/react/BookingWidget.jsx && cp src/core/styles.css dist/react/styles.css",
39
39
  "build:vue": "mkdir -p dist/vue && cp src/vue/BookingWidget.vue dist/vue/BookingWidget.vue && cp src/core/styles.css dist/vue/styles.css",
40
40
  "build:types": "npm run build:types:main && npm run build:types:react && npm run build:types:vue",