@getspot/spot-widget 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,13 @@
1
1
 
2
- > @getspot/spot-widget@0.1.1 build /builds/getspot/spot-widget/packages/core
2
+ > @getspot/spot-widget@0.1.3 build /builds/getspot/spot-widget/packages/core
3
3
  > vite build
4
4
 
5
5
  The CJS build of Vite's Node API is deprecated. See https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.
6
6
  vite v5.4.18 building for production...
7
7
  transforming...
8
- ✓ 4 modules transformed.
8
+ ✓ 5 modules transformed.
9
9
  rendering chunks...
10
10
  computing gzip size...
11
- dist/index.umd.js 19.31 kB │ gzip: 6.19 kB
12
- dist/index.es.js 20.80 kB │ gzip: 6.36 kB
13
- ✓ built in 132ms
11
+ dist/index.umd.js 21.85 kB │ gzip: 7.00 kB
12
+ dist/index.es.js 23.88 kB │ gzip: 7.23 kB
13
+ ✓ built in 145ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @getspot/spot-widget
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 4fe833f: add validation
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 5d7ddb4: add configuration validation
14
+
3
15
  ## 0.1.1
4
16
 
5
17
  ### Patch Changes
package/dist/index.es.js CHANGED
@@ -1,112 +1,196 @@
1
- async function f(s, t, o) {
1
+ async function b(s, e, t) {
2
2
  try {
3
- const r = await fetch(s, {
3
+ const o = await fetch(s, {
4
4
  method: "POST",
5
5
  headers: {
6
6
  "Content-Type": "application/json",
7
- "X-Spot-Partner-Id": t
7
+ "X-Spot-Partner-Id": e
8
8
  },
9
- body: JSON.stringify(o)
10
- }), i = await r.json();
11
- if (!r.ok) {
12
- const n = new Error((i == null ? void 0 : i.message) || "Failed to fetch quote");
13
- throw n.status = r.status, n.responseBody = i, n;
9
+ body: JSON.stringify(t)
10
+ }), r = await o.json();
11
+ if (!o.ok) {
12
+ const n = new Error((r == null ? void 0 : r.message) || "Failed to fetch quote");
13
+ throw n.status = o.status, n.responseBody = r, n;
14
14
  }
15
- return i;
16
- } catch (r) {
17
- throw r instanceof Error ? r : new Error("Unknown error occurred while fetching quote");
15
+ return r;
16
+ } catch (o) {
17
+ throw o instanceof Error ? o : new Error("Unknown error occurred while fetching quote");
18
18
  }
19
19
  }
20
- function e(s, { text: t, className: o, parent: r, innerHTML: i } = {}) {
20
+ const w = {
21
+ sandbox: "https://api.sandbox.getspot.com/v1/quote",
22
+ production: "https://api.getspot.com/v1/quote"
23
+ };
24
+ function _(s) {
25
+ const {
26
+ apiConfig: e = {},
27
+ quoteRequestData: t,
28
+ callbacks: o = {},
29
+ location: r,
30
+ theme: n
31
+ } = s, {
32
+ environment: a = "sandbox",
33
+ partnerId: p,
34
+ endpoint: l
35
+ } = e;
36
+ if (!p || typeof p != "string")
37
+ throw new Error("Invalid or missing partnerId in apiConfig");
38
+ if (!(l || w[a]))
39
+ throw new Error(`Invalid environment in apiConfig: ${a}`);
40
+ if (!t || typeof t != "object")
41
+ throw new Error("quoteRequestData must be a non-null object");
42
+ [
43
+ "startDate",
44
+ "endDate",
45
+ "currencyCode",
46
+ "eventType",
47
+ "productType",
48
+ "productDuration",
49
+ "productPrice",
50
+ "productId",
51
+ "productName"
52
+ ].forEach((d) => {
53
+ if (!Object.prototype.hasOwnProperty.call(t, d) || t[d] === void 0 || t[d] === null)
54
+ throw new Error(`Missing required quoteRequestData field: '${d}'`);
55
+ });
56
+ const c = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
57
+ if (!c.test(t.startDate))
58
+ throw new Error("startDate must be a valid ISO8601 string");
59
+ if (!c.test(t.endDate))
60
+ throw new Error("endDate must be a valid ISO8601 string");
61
+ if (typeof t.currencyCode != "string")
62
+ throw new Error("currencyCode must be a string");
63
+ if (!["USD", "CAD", "AUD"].includes(t.currencyCode))
64
+ throw new Error(`Invalid currency code: ${t.currencyCode}`);
65
+ if (typeof t.eventType != "string")
66
+ throw new Error("eventType must be a string");
67
+ if (typeof t.productType != "string")
68
+ throw new Error("productType must be a string");
69
+ const u = ["Pass", "Trip", "Registration"];
70
+ if (!u.includes(t.productType))
71
+ throw new Error(
72
+ `productType must be one of ${u.join(", ")}`
73
+ );
74
+ if (typeof t.productDuration != "string")
75
+ throw new Error("productDuration must be a string");
76
+ const h = ["Daily", "Seasonal", "Trip", "Event"];
77
+ if (!h.includes(t.productDuration))
78
+ throw new Error(
79
+ `productDuration must be one of ${h.join(", ")}`
80
+ );
81
+ if (typeof t.productPrice != "number" || isNaN(t.productPrice))
82
+ throw new Error("productPrice must be a valid number");
83
+ if (typeof t.productId != "string")
84
+ throw new Error("productId must be a string");
85
+ if (typeof t.productName != "string")
86
+ throw new Error("productName must be a string");
87
+ if ([
88
+ "onOptIn",
89
+ "onOptOut",
90
+ "onQuoteRetrieved",
91
+ "onError",
92
+ "noMatchingQuote"
93
+ ].forEach((d) => {
94
+ const g = o[d];
95
+ if (g && typeof g != "function")
96
+ throw new Error(`Callback '${d}' must be a function.`);
97
+ }), typeof r == "string" && !document.querySelector(r))
98
+ throw new Error(`Invalid location selector: '${r}'`);
99
+ if (n && typeof n != "object")
100
+ throw new Error(
101
+ "Theme must be an object with CSS variables, do not include the '--' prefix"
102
+ );
103
+ }
104
+ function i(s, { text: e, className: t, parent: o, innerHTML: r } = {}) {
21
105
  const n = document.createElement(s);
22
- return o && (n.className = o), t != null && (n.textContent = t), i != null && (n.innerHTML = i), r && r.appendChild(n), n;
106
+ return t && (n.className = t), e != null && (n.textContent = e), r != null && (n.innerHTML = r), o && o.appendChild(n), n;
23
107
  }
24
- function h(s, { name: t, description: o }) {
25
- e("div", {
108
+ function y(s, { name: e, description: t }) {
109
+ i("div", {
26
110
  className: "spot-header__title",
27
- text: t,
111
+ text: e,
28
112
  parent: s
29
- }), e("div", {
113
+ }), i("div", {
30
114
  className: "spot-header__description",
31
- text: o,
115
+ text: t,
32
116
  parent: s
33
117
  });
34
118
  }
35
- function u(s, t = []) {
36
- const o = e("ul", {
119
+ function C(s, e = []) {
120
+ const t = i("ul", {
37
121
  className: "spot-benefits__list",
38
122
  parent: s
39
123
  });
40
- t.forEach((r) => {
41
- const i = e("li", { parent: o });
42
- i.innerHTML = `
124
+ e.forEach((o) => {
125
+ const r = i("li", { parent: t });
126
+ r.innerHTML = `
43
127
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
44
128
  <path d="M11.6666 3.5L5.24998 9.91667L2.33331 7"
45
129
  stroke="#2E2E2E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
46
- </svg>`, e("span", { text: r, parent: i });
130
+ </svg>`, i("span", { text: o, parent: r });
47
131
  });
48
132
  }
49
- function g(s, t = []) {
50
- const o = e("div", {
133
+ function v(s, e = []) {
134
+ const t = i("div", {
51
135
  className: "spot-table__container",
52
136
  parent: s
53
- }), r = e("table", {
137
+ }), o = i("table", {
54
138
  className: "spot-refund__table spot-table--dynamic",
55
- parent: o
56
- }), i = e("thead", { parent: r }), n = e("tr", { parent: i });
57
- e("th", { text: "When you cancel", parent: n }), e("th", { text: "You will receive", parent: n });
58
- const a = e("tbody", { parent: r });
59
- t.forEach(({ text: l, percent: p, amount: c }) => {
60
- const d = e("tr", { parent: a });
61
- e("td", { text: l, parent: d });
62
- const m = p === "Not eligible for refund" ? "Not eligible for a refund" : `$${c} refund`;
63
- e("td", { text: m, parent: d });
139
+ parent: t
140
+ }), r = i("thead", { parent: o }), n = i("tr", { parent: r });
141
+ i("th", { text: "When you cancel", parent: n }), i("th", { text: "You will receive", parent: n });
142
+ const a = i("tbody", { parent: o });
143
+ e.forEach(({ text: p, percent: l, amount: f }) => {
144
+ const m = i("tr", { parent: a });
145
+ i("td", { text: p, parent: m });
146
+ const c = l === "Not eligible for refund" ? "Not eligible for a refund" : `$${f} refund`;
147
+ i("td", { text: c, parent: m });
64
148
  });
65
149
  }
66
- function _(s, t, o) {
67
- const r = e("div", {
150
+ function E(s, e, t) {
151
+ const o = i("div", {
68
152
  className: "spot-selection__options",
69
153
  parent: s
70
- }), i = e("label", {
71
- className: `spot-selection__option ${o ? "selected" : ""}`,
154
+ }), r = i("label", {
155
+ className: `spot-selection__option ${t ? "selected" : ""}`,
156
+ parent: o
157
+ }), n = i("input", { parent: r });
158
+ n.type = "radio", n.name = "selection", n.value = "yes", t && (n.checked = !0), i("strong", {
159
+ text: `Yes, protect my booking for $${e}`,
72
160
  parent: r
73
- }), n = e("input", { parent: i });
74
- n.type = "radio", n.name = "selection", n.value = "yes", o && (n.checked = !0), e("strong", {
75
- text: `Yes, protect my booking for $${t}`,
76
- parent: i
77
- }), e("span", {
161
+ }), i("span", {
78
162
  className: "spot-selection__recommended-tag",
79
163
  text: "Recommended",
80
- parent: i
164
+ parent: r
81
165
  });
82
- const a = e("label", {
166
+ const a = i("label", {
83
167
  className: "spot-selection__option",
84
- parent: r
85
- }), l = e("input", { parent: a });
86
- return l.type = "radio", l.name = "selection", l.value = "no", e("span", { text: "No, do not protect my booking", parent: a }), r;
168
+ parent: o
169
+ }), p = i("input", { parent: a });
170
+ return p.type = "radio", p.name = "selection", p.value = "no", i("span", { text: "No, do not protect my booking", parent: a }), o;
87
171
  }
88
- function b(s, t) {
89
- const o = e("div", {
172
+ function k(s, e) {
173
+ const t = i("div", {
90
174
  className: "spot-footer__container",
91
175
  parent: s
92
- }), r = e("div", {
176
+ }), o = i("div", {
93
177
  className: "spot-footer__terms",
94
- parent: o
178
+ parent: t
95
179
  });
96
- e("span", {
97
- innerHTML: t.communication.legalDisclaimer,
98
- parent: r
99
- }), e("br", { parent: r }), e("a", {
100
- href: t.communication.termsAndConditionsUrl,
180
+ i("span", {
181
+ innerHTML: e.communication.legalDisclaimer,
182
+ parent: o
183
+ }), i("br", { parent: o }), i("a", {
184
+ href: e.communication.termsAndConditionsUrl,
101
185
  className: "spot-footer__terms-link",
102
186
  text: "Refund Guarantee Terms and Conditions",
103
- parent: r
187
+ parent: o
104
188
  });
105
- const i = e("p", {
189
+ const r = i("p", {
106
190
  className: "spot-footer__powered-by",
107
- parent: o
191
+ parent: t
108
192
  });
109
- return i.innerHTML = `
193
+ return r.innerHTML = `
110
194
  <svg width="145" height="28" viewBox="0 0 145 28" fill="none" xmlns="http://www.w3.org/2000/svg">
111
195
  <rect width="145" height="28"/>
112
196
  <rect x="-655" y="-270" width="819" height="325" rx="10"/>
@@ -121,78 +205,87 @@ function b(s, t) {
121
205
  <rect width="45.405" height="14.8867" fill="white" transform="translate(87 8)"/>
122
206
  </clipPath>
123
207
  </defs>
124
- </svg>`, o;
208
+ </svg>`, t;
125
209
  }
126
- const C = ":root{--spot-font-family: Arial;--spot-padding: 1.25rem;--spot-background-color: #ffffff;--spot-font-color: #000000;--spot-border-radius: .5rem;--spot-title-font-size: 1.25rem;--spot-title-font-weight: 700;--spot-title-padding: 0 0 1.25rem 0;--spot-description-font-size: .875rem;--spot-description-font-weight: 400;--spot-description-padding: 0 0 .5rem 0;--spot-bullets-font-size: .875rem;--spot-bullets-font-weight: 400;--spot-bullets-padding: .3125rem;--spot-table-border-radius: .625rem;--spot-table-header-font-size: .875rem;--spot-table-header-font-weight: 700;--spot-table-header-padding: 0 .5rem .625rem;--spot-table-cell-font-size: .815rem;--spot-table-cell-font-weight: 400;--spot-table-cell-padding: 0 .625rem;--spot-radio-border: #000000;--spot-radio-border-radius: .625rem;--spot-radio-checked-background: #000000;--spot-radio-text-font-size: .875rem;--spot-radio-text-font-weight: 400;--spot-radio-text-padding: .625rem;--spot-radio-selection-background: #f4f4f4;--spot-radio-selection-border-radius: .625rem;--spot-radio-selection-padding: .625rem;--spot-recommended-tag-background: #000000;--spot-recommended-tag-font-color: #ffffff;--spot-recommended-tag-font-size: .875rem;--spot-recommended-tag-font-weight: 700;--spot-recommended-tag-padding: .25rem .5rem;--spot-recommended-tag-border-radius: .5rem;--spot-selection-error-font-color: #ff0000;--spot-selection-error-font-size: .875rem;--spot-selection-error-padding: .5rem;--spot-terms-font-size: .75rem;--spot-terms-font-weight: 400;--spot-terms-font-color: #636569;--spot-terms-padding: 0;--spot-terms-link-text-decoration: underline;--spot-terms-link-font-size: .75rem;--spot-terms-link-font-weight: 400;--spot-terms-link-font-color: #636569;--spot-terms-link-padding: 0}.spot-refund-guarantee{font-family:var(--spot-font-family);padding:var(--spot-padding);background-color:var(--spot-background-color);color:var(--spot-font-color);border:.0625rem solid #e0e0e0;border-radius:var(--spot-border-radius);max-width:51rem;margin:1rem}.spot-refund-guarantee *{color:inherit}.spot-header__title{font-size:var(--spot-title-font-size);font-weight:var(--spot-title-font-weight);padding:var(--spot-title-padding);color:var(--spot-title-font-color);font-family:var(--spot-title-font-family);line-height:120%;letter-spacing:-.03125rem}.spot-header__description{font-size:var(--spot-description-font-size);font-weight:var(--spot-description-font-weight);color:var(--spot-description-font-color);font-family:var(--spot-description-font-family);padding:var(--spot-description-padding);line-height:125%;letter-spacing:-.025rem}.spot-content__wrapper{display:flex;flex-direction:column}@media (min-width: 48rem){.spot-content__wrapper.desktop-layout{display:grid;grid-template-columns:1fr 20.3125rem;align-items:start;gap:1rem}.desktop-layout .spot-benefits__list{grid-row:1}.desktop-layout .spot-selection__options{grid-row:2}.desktop-layout .spot-table__container{grid-row:1 / span 2}}@media (max-width: 52.438rem){.spot-selection__recommended-tag{display:inline-block;margin-left:0}}@media (max-width: 47.938rem){.spot-selection__recommended-tag{margin-top:0rem}}@media (max-width: 32.125rem){.spot-selection__recommended-tag{margin-top:.5rem}}@media (max-width: 47.9375rem){.spot-table__container{display:flex;justify-content:center}.spot-selection__recommended-tag{display:inline-block;margin-left:0}.spot-footer__container{flex-direction:column;margin-top:.5rem}.spot-refund__table{width:100%;table-layout:auto}.spot-refund__table th{padding:0rem}}.spot-benefits__list{list-style-type:none;line-height:125%;gap:.5625rem;font-size:var(--spot-bullets-font-size);font-weight:var(--spot-bullets-font-weight);color:var(--spot-bullets-font-color);font-family:var(--spot-bullets-font-family);padding:var(--spot-bullets-padding);margin-block-start:0rem;margin-block-end:0rem}.spot-benefits__list li{margin-bottom:.5rem;display:flex;align-items:flex-start;gap:.5rem}.spot-benefits__list li svg{flex-shrink:0;position:relative;top:.125rem}.spot-table__container{width:100%}.spot-refund__table{max-width:22rem;border-radius:var(--spot-table-border-radius);overflow:hidden;border:.09375rem solid #636569;table-layout:fixed;margin-bottom:1.5rem;margin-top:.25rem;padding:.625rem}.spot-refund__table--dynamic{height:auto!important;min-height:7.5rem}.spot-refund__table td,.spot-refund__table th{padding:.375rem .625rem;text-align:left}.spot-refund__table th{text-align:left;font-size:var(--spot-table-header-font-size);font-weight:var(--spot-table-header-font-weight);color:var(--spot-table-header-font-color);font-family:var(--spot-table-header-font-family);padding:var(--spot-table-header-padding)}.spot-refund__table td{text-align:left;font-size:var(--spot-table-cell-font-size);font-weight:var(--spot-table-cell-font-weight);color:var(--spot-table-cell-font-color);font-family:var(--spot-table-cell-font-family);padding:var(--spot-table-cell-padding)}input[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:.75rem;height:.75rem;border:.0625rem solid var(--spot-radio-border);border-radius:var(--spot-radio-border-radius);margin-right:.5rem;position:relative;vertical-align:middle;top:-.0625rem;cursor:pointer}input[type=radio]:checked{background:var(--spot-radio-checked-background);box-shadow:inset 0 0 0 .0625rem #fff}.spot-selection__options{display:flex;flex-direction:column;gap:.5rem}.spot-selection__option{display:block;position:relative;transition:background .2s;cursor:pointer;font-size:var(--spot-radio-text-font-size);font-weight:var(--spot-radio-text-font-weight);color:var(--spot-radio-text-font-color);font-family:var(--spot-radio-text-font-family);padding:var(--spot-radio-text-padding);margin-right:.5rem;gap:.5rem;flex:1 0 0;align-self:stretch}.spot-selection__option.selected{background:var(--spot-radio-selection-background);border-radius:var(--spot-radio-selection-border-radius);padding:var(--spot-radio-selection-padding)}.spot-selection__recommended-tag{background:var(--spot-recommended-tag-background);color:var(--spot-recommended-tag-font-color);font-size:var(--spot-recommended-tag-font-size);font-weight:var(--spot-recommended-tag-font-weight);padding:var(--spot-recommended-tag-padding);border-radius:var(--spot-recommended-tag-border-radius);margin-left:1.5rem;white-space:nowrap}.spot-selection__error{color:var(--spot-selection-error-font-color);font-size:var(--spot-selection-error-font-size);padding:var(--spot-selection-error-padding);display:none}.spot-footer__terms{margin-top:.625rem;margin-right:.25rem;font-size:var(--spot-terms-font-size);font-weight:var(--spot-terms-font-weight);color:var(--spot-terms-font-color);font-family:var(--spot-terms-font-family);padding:var(--spot-terms-padding)}.spot-footer__terms-link{text-decoration:var(--spot-terms-link-text-decoration);font-size:var(--spot-terms-link-font-size);font-weight:var(--spot-terms-link-font-weight);color:var(--spot-terms-link-font-color);font-family:var(--spot-terms-link-font-family);padding:var(--spot-terms-link-padding)}.spot-footer__container{display:flex;justify-content:space-between;align-items:center}.spot-footer__powered-by{margin-top:1.5rem}";
127
- function v(s) {
128
- const t = document.createElement("style");
129
- t.textContent = s, document.head.appendChild(t);
210
+ const x = ":root{--spot-font-family: Arial;--spot-padding: 1.25rem;--spot-background-color: #ffffff;--spot-font-color: #000000;--spot-border-radius: .5rem;--spot-title-font-size: 1.25rem;--spot-title-font-weight: 700;--spot-title-padding: 0 0 1.25rem 0;--spot-description-font-size: .875rem;--spot-description-font-weight: 400;--spot-description-padding: 0 0 .5rem 0;--spot-bullets-font-size: .875rem;--spot-bullets-font-weight: 400;--spot-bullets-padding: .3125rem;--spot-table-border-radius: .625rem;--spot-table-header-font-size: .875rem;--spot-table-header-font-weight: 700;--spot-table-header-padding: 0 .5rem .625rem;--spot-table-cell-font-size: .815rem;--spot-table-cell-font-weight: 400;--spot-table-cell-padding: 0 .625rem;--spot-radio-border: #000000;--spot-radio-border-radius: .625rem;--spot-radio-checked-background: #000000;--spot-radio-text-font-size: .875rem;--spot-radio-text-font-weight: 400;--spot-radio-text-padding: .625rem;--spot-radio-selection-background: #f4f4f4;--spot-radio-selection-border-radius: .625rem;--spot-radio-selection-padding: .625rem;--spot-recommended-tag-background: #000000;--spot-recommended-tag-font-color: #ffffff;--spot-recommended-tag-font-size: .875rem;--spot-recommended-tag-font-weight: 700;--spot-recommended-tag-padding: .25rem .5rem;--spot-recommended-tag-border-radius: .5rem;--spot-selection-error-font-color: #ff0000;--spot-selection-error-font-size: .875rem;--spot-selection-error-padding: .5rem;--spot-terms-font-size: .75rem;--spot-terms-font-weight: 400;--spot-terms-font-color: #636569;--spot-terms-padding: 0;--spot-terms-link-text-decoration: underline;--spot-terms-link-font-size: .75rem;--spot-terms-link-font-weight: 400;--spot-terms-link-font-color: #636569;--spot-terms-link-padding: 0}.spot-refund-guarantee{font-family:var(--spot-font-family);padding:var(--spot-padding);background-color:var(--spot-background-color);color:var(--spot-font-color);border:.0625rem solid #e0e0e0;border-radius:var(--spot-border-radius);max-width:51rem;margin:1rem}.spot-refund-guarantee *{color:inherit}.spot-header__title{font-size:var(--spot-title-font-size);font-weight:var(--spot-title-font-weight);padding:var(--spot-title-padding);color:var(--spot-title-font-color);font-family:var(--spot-title-font-family);line-height:120%;letter-spacing:-.03125rem}.spot-header__description{font-size:var(--spot-description-font-size);font-weight:var(--spot-description-font-weight);color:var(--spot-description-font-color);font-family:var(--spot-description-font-family);padding:var(--spot-description-padding);line-height:125%;letter-spacing:-.025rem}.spot-content__wrapper{display:flex;flex-direction:column}@media (min-width: 48rem){.spot-content__wrapper.desktop-layout{display:grid;grid-template-columns:1fr 20.3125rem;align-items:start;gap:1rem}.desktop-layout .spot-benefits__list{grid-row:1}.desktop-layout .spot-selection__options{grid-row:2}.desktop-layout .spot-table__container{grid-row:1 / span 2}}@media (max-width: 52.438rem){.spot-selection__recommended-tag{display:inline-block;margin-left:0}}@media (max-width: 47.938rem){.spot-selection__recommended-tag{margin-top:0rem}}@media (max-width: 32.125rem){.spot-selection__recommended-tag{margin-top:.5rem}}@media (max-width: 47.9375rem){.spot-table__container{display:flex;justify-content:center}.spot-selection__recommended-tag{display:inline-block;margin-left:0}.spot-footer__container{flex-direction:column;margin-top:.5rem}.spot-refund__table{width:100%;table-layout:auto}.spot-refund__table th{padding:0rem}}.spot-benefits__list{list-style-type:none;line-height:125%;gap:.5625rem;font-size:var(--spot-bullets-font-size);font-weight:var(--spot-bullets-font-weight);color:var(--spot-bullets-font-color);font-family:var(--spot-bullets-font-family);padding:var(--spot-bullets-padding);margin-block-start:0rem;margin-block-end:0rem}.spot-benefits__list li{margin-bottom:.5rem;display:flex;align-items:flex-start;gap:.5rem}.spot-benefits__list li svg{flex-shrink:0;position:relative;top:.125rem}.spot-table__container{width:100%}.spot-refund__table{max-width:22rem;border-radius:var(--spot-table-border-radius);overflow:hidden;border:.09375rem solid #636569;table-layout:fixed;margin-bottom:1.5rem;margin-top:.25rem;padding:.625rem}.spot-refund__table--dynamic{height:auto!important;min-height:7.5rem}.spot-refund__table td,.spot-refund__table th{padding:.375rem .625rem;text-align:left}.spot-refund__table th{text-align:left;font-size:var(--spot-table-header-font-size);font-weight:var(--spot-table-header-font-weight);color:var(--spot-table-header-font-color);font-family:var(--spot-table-header-font-family);padding:var(--spot-table-header-padding)}.spot-refund__table td{text-align:left;font-size:var(--spot-table-cell-font-size);font-weight:var(--spot-table-cell-font-weight);color:var(--spot-table-cell-font-color);font-family:var(--spot-table-cell-font-family);padding:var(--spot-table-cell-padding)}input[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:.75rem;height:.75rem;border:.0625rem solid var(--spot-radio-border);border-radius:var(--spot-radio-border-radius);margin-right:.5rem;position:relative;vertical-align:middle;top:-.0625rem;cursor:pointer}input[type=radio]:checked{background:var(--spot-radio-checked-background);box-shadow:inset 0 0 0 .0625rem #fff}.spot-selection__options{display:flex;flex-direction:column;gap:.5rem}.spot-selection__option{display:block;position:relative;transition:background .2s;cursor:pointer;font-size:var(--spot-radio-text-font-size);font-weight:var(--spot-radio-text-font-weight);color:var(--spot-radio-text-font-color);font-family:var(--spot-radio-text-font-family);padding:var(--spot-radio-text-padding);margin-right:.5rem;gap:.5rem;flex:1 0 0;align-self:stretch}.spot-selection__option.selected{background:var(--spot-radio-selection-background);border-radius:var(--spot-radio-selection-border-radius);padding:var(--spot-radio-selection-padding)}.spot-selection__recommended-tag{background:var(--spot-recommended-tag-background);color:var(--spot-recommended-tag-font-color);font-size:var(--spot-recommended-tag-font-size);font-weight:var(--spot-recommended-tag-font-weight);padding:var(--spot-recommended-tag-padding);border-radius:var(--spot-recommended-tag-border-radius);margin-left:1.5rem;white-space:nowrap}.spot-selection__error{color:var(--spot-selection-error-font-color);font-size:var(--spot-selection-error-font-size);padding:var(--spot-selection-error-padding);display:none}.spot-footer__terms{margin-top:.625rem;margin-right:.25rem;font-size:var(--spot-terms-font-size);font-weight:var(--spot-terms-font-weight);color:var(--spot-terms-font-color);font-family:var(--spot-terms-font-family);padding:var(--spot-terms-padding)}.spot-footer__terms-link{text-decoration:var(--spot-terms-link-text-decoration);font-size:var(--spot-terms-link-font-size);font-weight:var(--spot-terms-link-font-weight);color:var(--spot-terms-link-font-color);font-family:var(--spot-terms-link-font-family);padding:var(--spot-terms-link-padding)}.spot-footer__container{display:flex;justify-content:space-between;align-items:center}.spot-footer__powered-by{margin-top:1.5rem}";
211
+ function H(s) {
212
+ const e = document.createElement("style");
213
+ e.textContent = s, document.head.appendChild(e);
130
214
  }
131
- v(C);
132
- class y {
133
- constructor(t = {}) {
215
+ H(x);
216
+ const q = {
217
+ sandbox: "https://api.sandbox.getspot.com/v1/quote",
218
+ production: "https://api.getspot.com/v1/quote"
219
+ };
220
+ class S {
221
+ constructor(e = {}) {
134
222
  this.options = {
135
223
  location: "body",
136
224
  showTable: !0,
137
225
  optInSelected: !1,
138
- apiConfig: { endpoint: "", partnerId: "" },
226
+ apiConfig: { environment: "production", partnerId: "" },
139
227
  quoteRequestData: {},
140
228
  callbacks: {},
141
- ...t
229
+ ...e
142
230
  }, this._onResize = this._updateLayout.bind(this), this.root = typeof this.options.location == "string" ? document.querySelector(this.options.location) : this.options.location, this.currentSelection = this.options.optInSelected ? "yes" : null, this._init();
143
231
  }
144
232
  async _init() {
145
233
  try {
146
- const t = await f(
147
- this.options.apiConfig.endpoint,
148
- this.options.apiConfig.partnerId,
234
+ _(this.options);
235
+ const {
236
+ environment: e,
237
+ partnerId: t,
238
+ endpoint: o
239
+ } = this.options.apiConfig, r = o || q[e], n = await b(
240
+ r,
241
+ t,
149
242
  this.options.quoteRequestData
150
243
  );
151
- if (t.status !== "QUOTE_AVAILABLE") {
152
- t.status === "NO_MATCHING_QUOTE" && this.options.callbacks.noMatchingQuote({
244
+ if (n.status !== "QUOTE_AVAILABLE") {
245
+ n.status === "NO_MATCHING_QUOTE" && this.options.callbacks.noMatchingQuote({
153
246
  status: "NO_MATCHING_QUOTE",
154
247
  data: this.options.quoteRequestData
155
248
  });
156
249
  return;
157
250
  }
158
- this.quote = t.data, this._renderWidget(), this.options.optInSelected && this.options.callbacks.onOptIn && this.options.callbacks.onOptIn({
251
+ this.quote = n.data, this._renderWidget(), this.options.optInSelected && this.options.callbacks.onOptIn && this.options.callbacks.onOptIn({
159
252
  status: "QUOTE_ACCEPTED",
160
253
  spotPrice: this.quote.spotPrice,
161
254
  quoteId: this.quote.id
162
255
  }), this.options.callbacks.onQuoteRetrieved && this.options.callbacks.onQuoteRetrieved(this.quote);
163
- } catch (t) {
256
+ } catch (e) {
164
257
  this.options.callbacks.onError({
165
- message: t.message,
166
- status: t.status,
167
- responseBody: t.responseBody
258
+ message: e.message,
259
+ status: e.status,
260
+ responseBody: e.responseBody
168
261
  });
169
262
  }
170
263
  }
171
264
  _renderWidget() {
172
- this.container = document.createElement("div"), this.container.className = "spot-refund-guarantee", this.root.appendChild(this.container), Object.entries(this.options.theme || {}).forEach(([r, i]) => {
173
- const n = `--${r}`;
174
- this.container.style.setProperty(n, i);
175
- }), h(this.container, this.quote.communication);
176
- const t = document.createElement("div");
177
- t.className = "spot-content__wrapper", this.container.appendChild(t), u(t, this.quote.communication.bulletPoints), this.options.showTable && g(t, this.quote.payoutSchedule);
178
- const o = _(
179
- t,
265
+ this.container = document.createElement("div"), this.container.className = "spot-refund-guarantee", this.root.appendChild(this.container), Object.entries(this.options.theme || {}).forEach(([o, r]) => {
266
+ const n = `--${o}`;
267
+ this.container.style.setProperty(n, r);
268
+ }), y(this.container, this.quote.communication);
269
+ const e = document.createElement("div");
270
+ e.className = "spot-content__wrapper", this.container.appendChild(e), C(e, this.quote.communication.bulletPoints), this.options.showTable && v(e, this.quote.payoutSchedule);
271
+ const t = E(
272
+ e,
180
273
  this.quote.spotPrice,
181
274
  this.options.optInSelected
182
275
  );
183
- t.appendChild(o), b(this.container, this.quote), window.addEventListener("resize", this._onResize), this._updateLayout(), this._setupOptionListeners(o);
276
+ e.appendChild(t), k(this.container, this.quote), window.addEventListener("resize", this._onResize), this._updateLayout(), this._setupOptionListeners(t);
184
277
  }
185
278
  _updateLayout() {
186
- const t = window.matchMedia("(min-width: 768px)").matches;
187
- this.container.querySelector(".spot-content__wrapper").classList.toggle("desktop-layout", t && this.options.showTable);
279
+ const e = window.matchMedia("(min-width: 768px)").matches;
280
+ this.container.querySelector(".spot-content__wrapper").classList.toggle("desktop-layout", e && this.options.showTable);
188
281
  }
189
- _setupOptionListeners(t) {
190
- const o = t.querySelectorAll('input[type="radio"]'), r = t.querySelectorAll(".spot-selection__option");
191
- o.forEach((i) => {
192
- i.addEventListener("change", (n) => {
193
- var l;
282
+ _setupOptionListeners(e) {
283
+ const t = e.querySelectorAll('input[type="radio"]'), o = e.querySelectorAll(".spot-selection__option");
284
+ t.forEach((r) => {
285
+ r.addEventListener("change", (n) => {
286
+ var p;
194
287
  const a = n.target.value;
195
- this.hideSelectionError(), this.currentSelection = a, r.forEach((p) => p.classList.remove("selected")), (l = n.target.closest(".spot-selection__option")) == null || l.classList.add("selected"), a === "yes" && this.options.callbacks.onOptIn && this.options.callbacks.onOptIn({
288
+ this.hideSelectionError(), this.currentSelection = a, o.forEach((l) => l.classList.remove("selected")), (p = n.target.closest(".spot-selection__option")) == null || p.classList.add("selected"), a === "yes" && this.options.callbacks.onOptIn && this.options.callbacks.onOptIn({
196
289
  status: "QUOTE_ACCEPTED",
197
290
  spotPrice: this.quote.spotPrice,
198
291
  quoteId: this.quote.id
@@ -204,13 +297,13 @@ class y {
204
297
  });
205
298
  }
206
299
  showSelectionError() {
207
- var t;
300
+ var e;
208
301
  if (!this.errorEl) {
209
302
  this.errorEl = document.createElement("div"), this.errorEl.className = "spot-selection__error", this.errorEl.textContent = "Please make a selection";
210
- const o = (t = this.container) == null ? void 0 : t.querySelector(
303
+ const t = (e = this.container) == null ? void 0 : e.querySelector(
211
304
  ".spot-selection__options"
212
305
  );
213
- o && o.insertAdjacentElement("afterend", this.errorEl);
306
+ t && t.insertAdjacentElement("afterend", this.errorEl);
214
307
  }
215
308
  this.errorEl.style.display = "block";
216
309
  }
@@ -219,17 +312,17 @@ class y {
219
312
  }
220
313
  validateSelection() {
221
314
  if (!this.container) return !1;
222
- const t = !!this.container.querySelector(
315
+ const e = !!this.container.querySelector(
223
316
  'input[name="selection"]:checked'
224
317
  );
225
- return t ? this.hideSelectionError() : this.showSelectionError(), t;
318
+ return e ? this.hideSelectionError() : this.showSelectionError(), e;
226
319
  }
227
320
  getSelection() {
228
- var t, o;
321
+ var e, t;
229
322
  return this.currentSelection == null ? null : {
230
323
  selection: this.currentSelection,
231
- quoteId: (t = this.quote) == null ? void 0 : t.id,
232
- spotPrice: (o = this.quote) == null ? void 0 : o.spotPrice,
324
+ quoteId: (e = this.quote) == null ? void 0 : e.id,
325
+ spotPrice: (t = this.quote) == null ? void 0 : t.spotPrice,
233
326
  status: this.currentSelection === "yes" ? "QUOTE_ACCEPTED" : "QUOTE_DECLINED"
234
327
  };
235
328
  }
@@ -238,5 +331,5 @@ class y {
238
331
  }
239
332
  }
240
333
  export {
241
- y as default
334
+ S as default
242
335
  };
package/dist/index.umd.js CHANGED
@@ -1,8 +1,8 @@
1
- (function(l,e){typeof exports=="object"&&typeof module<"u"?module.exports=e():typeof define=="function"&&define.amd?define(e):(l=typeof globalThis<"u"?globalThis:l||self,l.SpotWidget=e())})(this,function(){"use strict";async function l(s,t,o){try{const r=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json","X-Spot-Partner-Id":t},body:JSON.stringify(o)}),i=await r.json();if(!r.ok){const n=new Error((i==null?void 0:i.message)||"Failed to fetch quote");throw n.status=r.status,n.responseBody=i,n}return i}catch(r){throw r instanceof Error?r:new Error("Unknown error occurred while fetching quote")}}function e(s,{text:t,className:o,parent:r,innerHTML:i}={}){const n=document.createElement(s);return o&&(n.className=o),t!=null&&(n.textContent=t),i!=null&&(n.innerHTML=i),r&&r.appendChild(n),n}function m(s,{name:t,description:o}){e("div",{className:"spot-header__title",text:t,parent:s}),e("div",{className:"spot-header__description",text:o,parent:s})}function f(s,t=[]){const o=e("ul",{className:"spot-benefits__list",parent:s});t.forEach(r=>{const i=e("li",{parent:o});i.innerHTML=`
1
+ (function(l,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(l=typeof globalThis<"u"?globalThis:l||self,l.SpotWidget=c())})(this,function(){"use strict";async function l(s,e,t){try{const o=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json","X-Spot-Partner-Id":e},body:JSON.stringify(t)}),n=await o.json();if(!o.ok){const i=new Error((n==null?void 0:n.message)||"Failed to fetch quote");throw i.status=o.status,i.responseBody=n,i}return n}catch(o){throw o instanceof Error?o:new Error("Unknown error occurred while fetching quote")}}const c={sandbox:"https://api.sandbox.getspot.com/v1/quote",production:"https://api.getspot.com/v1/quote"};function _(s){const{apiConfig:e={},quoteRequestData:t,callbacks:o={},location:n,theme:i}=s,{environment:a="sandbox",partnerId:p,endpoint:f}=e;if(!p||typeof p!="string")throw new Error("Invalid or missing partnerId in apiConfig");if(!(f||c[a]))throw new Error(`Invalid environment in apiConfig: ${a}`);if(!t||typeof t!="object")throw new Error("quoteRequestData must be a non-null object");["startDate","endDate","currencyCode","eventType","productType","productDuration","productPrice","productId","productName"].forEach(d=>{if(!Object.prototype.hasOwnProperty.call(t,d)||t[d]===void 0||t[d]===null)throw new Error(`Missing required quoteRequestData field: '${d}'`)});const m=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;if(!m.test(t.startDate))throw new Error("startDate must be a valid ISO8601 string");if(!m.test(t.endDate))throw new Error("endDate must be a valid ISO8601 string");if(typeof t.currencyCode!="string")throw new Error("currencyCode must be a string");if(!["USD","CAD","AUD"].includes(t.currencyCode))throw new Error(`Invalid currency code: ${t.currencyCode}`);if(typeof t.eventType!="string")throw new Error("eventType must be a string");if(typeof t.productType!="string")throw new Error("productType must be a string");const g=["Pass","Trip","Registration"];if(!g.includes(t.productType))throw new Error(`productType must be one of ${g.join(", ")}`);if(typeof t.productDuration!="string")throw new Error("productDuration must be a string");const b=["Daily","Seasonal","Trip","Event"];if(!b.includes(t.productDuration))throw new Error(`productDuration must be one of ${b.join(", ")}`);if(typeof t.productPrice!="number"||isNaN(t.productPrice))throw new Error("productPrice must be a valid number");if(typeof t.productId!="string")throw new Error("productId must be a string");if(typeof t.productName!="string")throw new Error("productName must be a string");if(["onOptIn","onOptOut","onQuoteRetrieved","onError","noMatchingQuote"].forEach(d=>{const w=o[d];if(w&&typeof w!="function")throw new Error(`Callback '${d}' must be a function.`)}),typeof n=="string"&&!document.querySelector(n))throw new Error(`Invalid location selector: '${n}'`);if(i&&typeof i!="object")throw new Error("Theme must be an object with CSS variables, do not include the '--' prefix")}function r(s,{text:e,className:t,parent:o,innerHTML:n}={}){const i=document.createElement(s);return t&&(i.className=t),e!=null&&(i.textContent=e),n!=null&&(i.innerHTML=n),o&&o.appendChild(i),i}function y(s,{name:e,description:t}){r("div",{className:"spot-header__title",text:e,parent:s}),r("div",{className:"spot-header__description",text:t,parent:s})}function C(s,e=[]){const t=r("ul",{className:"spot-benefits__list",parent:s});e.forEach(o=>{const n=r("li",{parent:t});n.innerHTML=`
2
2
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
3
3
  <path d="M11.6666 3.5L5.24998 9.91667L2.33331 7"
4
4
  stroke="#2E2E2E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
5
- </svg>`,e("span",{text:r,parent:i})})}function h(s,t=[]){const o=e("div",{className:"spot-table__container",parent:s}),r=e("table",{className:"spot-refund__table spot-table--dynamic",parent:o}),i=e("thead",{parent:r}),n=e("tr",{parent:i});e("th",{text:"When you cancel",parent:n}),e("th",{text:"You will receive",parent:n});const a=e("tbody",{parent:r});t.forEach(({text:p,percent:d,amount:v})=>{const c=e("tr",{parent:a});e("td",{text:p,parent:c});const y=d==="Not eligible for refund"?"Not eligible for a refund":`$${v} refund`;e("td",{text:y,parent:c})})}function u(s,t,o){const r=e("div",{className:"spot-selection__options",parent:s}),i=e("label",{className:`spot-selection__option ${o?"selected":""}`,parent:r}),n=e("input",{parent:i});n.type="radio",n.name="selection",n.value="yes",o&&(n.checked=!0),e("strong",{text:`Yes, protect my booking for $${t}`,parent:i}),e("span",{className:"spot-selection__recommended-tag",text:"Recommended",parent:i});const a=e("label",{className:"spot-selection__option",parent:r}),p=e("input",{parent:a});return p.type="radio",p.name="selection",p.value="no",e("span",{text:"No, do not protect my booking",parent:a}),r}function g(s,t){const o=e("div",{className:"spot-footer__container",parent:s}),r=e("div",{className:"spot-footer__terms",parent:o});e("span",{innerHTML:t.communication.legalDisclaimer,parent:r}),e("br",{parent:r}),e("a",{href:t.communication.termsAndConditionsUrl,className:"spot-footer__terms-link",text:"Refund Guarantee Terms and Conditions",parent:r});const i=e("p",{className:"spot-footer__powered-by",parent:o});return i.innerHTML=`
5
+ </svg>`,r("span",{text:o,parent:n})})}function v(s,e=[]){const t=r("div",{className:"spot-table__container",parent:s}),o=r("table",{className:"spot-refund__table spot-table--dynamic",parent:t}),n=r("thead",{parent:o}),i=r("tr",{parent:n});r("th",{text:"When you cancel",parent:i}),r("th",{text:"You will receive",parent:i});const a=r("tbody",{parent:o});e.forEach(({text:p,percent:f,amount:h})=>{const u=r("tr",{parent:a});r("td",{text:p,parent:u});const m=f==="Not eligible for refund"?"Not eligible for a refund":`$${h} refund`;r("td",{text:m,parent:u})})}function E(s,e,t){const o=r("div",{className:"spot-selection__options",parent:s}),n=r("label",{className:`spot-selection__option ${t?"selected":""}`,parent:o}),i=r("input",{parent:n});i.type="radio",i.name="selection",i.value="yes",t&&(i.checked=!0),r("strong",{text:`Yes, protect my booking for $${e}`,parent:n}),r("span",{className:"spot-selection__recommended-tag",text:"Recommended",parent:n});const a=r("label",{className:"spot-selection__option",parent:o}),p=r("input",{parent:a});return p.type="radio",p.name="selection",p.value="no",r("span",{text:"No, do not protect my booking",parent:a}),o}function k(s,e){const t=r("div",{className:"spot-footer__container",parent:s}),o=r("div",{className:"spot-footer__terms",parent:t});r("span",{innerHTML:e.communication.legalDisclaimer,parent:o}),r("br",{parent:o}),r("a",{href:e.communication.termsAndConditionsUrl,className:"spot-footer__terms-link",text:"Refund Guarantee Terms and Conditions",parent:o});const n=r("p",{className:"spot-footer__powered-by",parent:t});return n.innerHTML=`
6
6
  <svg width="145" height="28" viewBox="0 0 145 28" fill="none" xmlns="http://www.w3.org/2000/svg">
7
7
  <rect width="145" height="28"/>
8
8
  <rect x="-655" y="-270" width="819" height="325" rx="10"/>
@@ -17,4 +17,4 @@
17
17
  <rect width="45.405" height="14.8867" fill="white" transform="translate(87 8)"/>
18
18
  </clipPath>
19
19
  </defs>
20
- </svg>`,o}const _=":root{--spot-font-family: Arial;--spot-padding: 1.25rem;--spot-background-color: #ffffff;--spot-font-color: #000000;--spot-border-radius: .5rem;--spot-title-font-size: 1.25rem;--spot-title-font-weight: 700;--spot-title-padding: 0 0 1.25rem 0;--spot-description-font-size: .875rem;--spot-description-font-weight: 400;--spot-description-padding: 0 0 .5rem 0;--spot-bullets-font-size: .875rem;--spot-bullets-font-weight: 400;--spot-bullets-padding: .3125rem;--spot-table-border-radius: .625rem;--spot-table-header-font-size: .875rem;--spot-table-header-font-weight: 700;--spot-table-header-padding: 0 .5rem .625rem;--spot-table-cell-font-size: .815rem;--spot-table-cell-font-weight: 400;--spot-table-cell-padding: 0 .625rem;--spot-radio-border: #000000;--spot-radio-border-radius: .625rem;--spot-radio-checked-background: #000000;--spot-radio-text-font-size: .875rem;--spot-radio-text-font-weight: 400;--spot-radio-text-padding: .625rem;--spot-radio-selection-background: #f4f4f4;--spot-radio-selection-border-radius: .625rem;--spot-radio-selection-padding: .625rem;--spot-recommended-tag-background: #000000;--spot-recommended-tag-font-color: #ffffff;--spot-recommended-tag-font-size: .875rem;--spot-recommended-tag-font-weight: 700;--spot-recommended-tag-padding: .25rem .5rem;--spot-recommended-tag-border-radius: .5rem;--spot-selection-error-font-color: #ff0000;--spot-selection-error-font-size: .875rem;--spot-selection-error-padding: .5rem;--spot-terms-font-size: .75rem;--spot-terms-font-weight: 400;--spot-terms-font-color: #636569;--spot-terms-padding: 0;--spot-terms-link-text-decoration: underline;--spot-terms-link-font-size: .75rem;--spot-terms-link-font-weight: 400;--spot-terms-link-font-color: #636569;--spot-terms-link-padding: 0}.spot-refund-guarantee{font-family:var(--spot-font-family);padding:var(--spot-padding);background-color:var(--spot-background-color);color:var(--spot-font-color);border:.0625rem solid #e0e0e0;border-radius:var(--spot-border-radius);max-width:51rem;margin:1rem}.spot-refund-guarantee *{color:inherit}.spot-header__title{font-size:var(--spot-title-font-size);font-weight:var(--spot-title-font-weight);padding:var(--spot-title-padding);color:var(--spot-title-font-color);font-family:var(--spot-title-font-family);line-height:120%;letter-spacing:-.03125rem}.spot-header__description{font-size:var(--spot-description-font-size);font-weight:var(--spot-description-font-weight);color:var(--spot-description-font-color);font-family:var(--spot-description-font-family);padding:var(--spot-description-padding);line-height:125%;letter-spacing:-.025rem}.spot-content__wrapper{display:flex;flex-direction:column}@media (min-width: 48rem){.spot-content__wrapper.desktop-layout{display:grid;grid-template-columns:1fr 20.3125rem;align-items:start;gap:1rem}.desktop-layout .spot-benefits__list{grid-row:1}.desktop-layout .spot-selection__options{grid-row:2}.desktop-layout .spot-table__container{grid-row:1 / span 2}}@media (max-width: 52.438rem){.spot-selection__recommended-tag{display:inline-block;margin-left:0}}@media (max-width: 47.938rem){.spot-selection__recommended-tag{margin-top:0rem}}@media (max-width: 32.125rem){.spot-selection__recommended-tag{margin-top:.5rem}}@media (max-width: 47.9375rem){.spot-table__container{display:flex;justify-content:center}.spot-selection__recommended-tag{display:inline-block;margin-left:0}.spot-footer__container{flex-direction:column;margin-top:.5rem}.spot-refund__table{width:100%;table-layout:auto}.spot-refund__table th{padding:0rem}}.spot-benefits__list{list-style-type:none;line-height:125%;gap:.5625rem;font-size:var(--spot-bullets-font-size);font-weight:var(--spot-bullets-font-weight);color:var(--spot-bullets-font-color);font-family:var(--spot-bullets-font-family);padding:var(--spot-bullets-padding);margin-block-start:0rem;margin-block-end:0rem}.spot-benefits__list li{margin-bottom:.5rem;display:flex;align-items:flex-start;gap:.5rem}.spot-benefits__list li svg{flex-shrink:0;position:relative;top:.125rem}.spot-table__container{width:100%}.spot-refund__table{max-width:22rem;border-radius:var(--spot-table-border-radius);overflow:hidden;border:.09375rem solid #636569;table-layout:fixed;margin-bottom:1.5rem;margin-top:.25rem;padding:.625rem}.spot-refund__table--dynamic{height:auto!important;min-height:7.5rem}.spot-refund__table td,.spot-refund__table th{padding:.375rem .625rem;text-align:left}.spot-refund__table th{text-align:left;font-size:var(--spot-table-header-font-size);font-weight:var(--spot-table-header-font-weight);color:var(--spot-table-header-font-color);font-family:var(--spot-table-header-font-family);padding:var(--spot-table-header-padding)}.spot-refund__table td{text-align:left;font-size:var(--spot-table-cell-font-size);font-weight:var(--spot-table-cell-font-weight);color:var(--spot-table-cell-font-color);font-family:var(--spot-table-cell-font-family);padding:var(--spot-table-cell-padding)}input[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:.75rem;height:.75rem;border:.0625rem solid var(--spot-radio-border);border-radius:var(--spot-radio-border-radius);margin-right:.5rem;position:relative;vertical-align:middle;top:-.0625rem;cursor:pointer}input[type=radio]:checked{background:var(--spot-radio-checked-background);box-shadow:inset 0 0 0 .0625rem #fff}.spot-selection__options{display:flex;flex-direction:column;gap:.5rem}.spot-selection__option{display:block;position:relative;transition:background .2s;cursor:pointer;font-size:var(--spot-radio-text-font-size);font-weight:var(--spot-radio-text-font-weight);color:var(--spot-radio-text-font-color);font-family:var(--spot-radio-text-font-family);padding:var(--spot-radio-text-padding);margin-right:.5rem;gap:.5rem;flex:1 0 0;align-self:stretch}.spot-selection__option.selected{background:var(--spot-radio-selection-background);border-radius:var(--spot-radio-selection-border-radius);padding:var(--spot-radio-selection-padding)}.spot-selection__recommended-tag{background:var(--spot-recommended-tag-background);color:var(--spot-recommended-tag-font-color);font-size:var(--spot-recommended-tag-font-size);font-weight:var(--spot-recommended-tag-font-weight);padding:var(--spot-recommended-tag-padding);border-radius:var(--spot-recommended-tag-border-radius);margin-left:1.5rem;white-space:nowrap}.spot-selection__error{color:var(--spot-selection-error-font-color);font-size:var(--spot-selection-error-font-size);padding:var(--spot-selection-error-padding);display:none}.spot-footer__terms{margin-top:.625rem;margin-right:.25rem;font-size:var(--spot-terms-font-size);font-weight:var(--spot-terms-font-weight);color:var(--spot-terms-font-color);font-family:var(--spot-terms-font-family);padding:var(--spot-terms-padding)}.spot-footer__terms-link{text-decoration:var(--spot-terms-link-text-decoration);font-size:var(--spot-terms-link-font-size);font-weight:var(--spot-terms-link-font-weight);color:var(--spot-terms-link-font-color);font-family:var(--spot-terms-link-font-family);padding:var(--spot-terms-link-padding)}.spot-footer__container{display:flex;justify-content:space-between;align-items:center}.spot-footer__powered-by{margin-top:1.5rem}";function b(s){const t=document.createElement("style");t.textContent=s,document.head.appendChild(t)}b(_);class C{constructor(t={}){this.options={location:"body",showTable:!0,optInSelected:!1,apiConfig:{endpoint:"",partnerId:""},quoteRequestData:{},callbacks:{},...t},this._onResize=this._updateLayout.bind(this),this.root=typeof this.options.location=="string"?document.querySelector(this.options.location):this.options.location,this.currentSelection=this.options.optInSelected?"yes":null,this._init()}async _init(){try{const t=await l(this.options.apiConfig.endpoint,this.options.apiConfig.partnerId,this.options.quoteRequestData);if(t.status!=="QUOTE_AVAILABLE"){t.status==="NO_MATCHING_QUOTE"&&this.options.callbacks.noMatchingQuote({status:"NO_MATCHING_QUOTE",data:this.options.quoteRequestData});return}this.quote=t.data,this._renderWidget(),this.options.optInSelected&&this.options.callbacks.onOptIn&&this.options.callbacks.onOptIn({status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id}),this.options.callbacks.onQuoteRetrieved&&this.options.callbacks.onQuoteRetrieved(this.quote)}catch(t){this.options.callbacks.onError({message:t.message,status:t.status,responseBody:t.responseBody})}}_renderWidget(){this.container=document.createElement("div"),this.container.className="spot-refund-guarantee",this.root.appendChild(this.container),Object.entries(this.options.theme||{}).forEach(([r,i])=>{const n=`--${r}`;this.container.style.setProperty(n,i)}),m(this.container,this.quote.communication);const t=document.createElement("div");t.className="spot-content__wrapper",this.container.appendChild(t),f(t,this.quote.communication.bulletPoints),this.options.showTable&&h(t,this.quote.payoutSchedule);const o=u(t,this.quote.spotPrice,this.options.optInSelected);t.appendChild(o),g(this.container,this.quote),window.addEventListener("resize",this._onResize),this._updateLayout(),this._setupOptionListeners(o)}_updateLayout(){const t=window.matchMedia("(min-width: 768px)").matches;this.container.querySelector(".spot-content__wrapper").classList.toggle("desktop-layout",t&&this.options.showTable)}_setupOptionListeners(t){const o=t.querySelectorAll('input[type="radio"]'),r=t.querySelectorAll(".spot-selection__option");o.forEach(i=>{i.addEventListener("change",n=>{var p;const a=n.target.value;this.hideSelectionError(),this.currentSelection=a,r.forEach(d=>d.classList.remove("selected")),(p=n.target.closest(".spot-selection__option"))==null||p.classList.add("selected"),a==="yes"&&this.options.callbacks.onOptIn&&this.options.callbacks.onOptIn({status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id}),a==="no"&&this.options.callbacks.onOptOut&&this.options.callbacks.onOptOut({status:"QUOTE_DECLINED",quoteId:this.quote.id})})})}showSelectionError(){var t;if(!this.errorEl){this.errorEl=document.createElement("div"),this.errorEl.className="spot-selection__error",this.errorEl.textContent="Please make a selection";const o=(t=this.container)==null?void 0:t.querySelector(".spot-selection__options");o&&o.insertAdjacentElement("afterend",this.errorEl)}this.errorEl.style.display="block"}hideSelectionError(){this.errorEl&&(this.errorEl.style.display="none")}validateSelection(){if(!this.container)return!1;const t=!!this.container.querySelector('input[name="selection"]:checked');return t?this.hideSelectionError():this.showSelectionError(),t}getSelection(){var t,o;return this.currentSelection==null?null:{selection:this.currentSelection,quoteId:(t=this.quote)==null?void 0:t.id,spotPrice:(o=this.quote)==null?void 0:o.spotPrice,status:this.currentSelection==="yes"?"QUOTE_ACCEPTED":"QUOTE_DECLINED"}}destroy(){window.removeEventListener("resize",this._onResize),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}}return C});
20
+ </svg>`,t}const x=":root{--spot-font-family: Arial;--spot-padding: 1.25rem;--spot-background-color: #ffffff;--spot-font-color: #000000;--spot-border-radius: .5rem;--spot-title-font-size: 1.25rem;--spot-title-font-weight: 700;--spot-title-padding: 0 0 1.25rem 0;--spot-description-font-size: .875rem;--spot-description-font-weight: 400;--spot-description-padding: 0 0 .5rem 0;--spot-bullets-font-size: .875rem;--spot-bullets-font-weight: 400;--spot-bullets-padding: .3125rem;--spot-table-border-radius: .625rem;--spot-table-header-font-size: .875rem;--spot-table-header-font-weight: 700;--spot-table-header-padding: 0 .5rem .625rem;--spot-table-cell-font-size: .815rem;--spot-table-cell-font-weight: 400;--spot-table-cell-padding: 0 .625rem;--spot-radio-border: #000000;--spot-radio-border-radius: .625rem;--spot-radio-checked-background: #000000;--spot-radio-text-font-size: .875rem;--spot-radio-text-font-weight: 400;--spot-radio-text-padding: .625rem;--spot-radio-selection-background: #f4f4f4;--spot-radio-selection-border-radius: .625rem;--spot-radio-selection-padding: .625rem;--spot-recommended-tag-background: #000000;--spot-recommended-tag-font-color: #ffffff;--spot-recommended-tag-font-size: .875rem;--spot-recommended-tag-font-weight: 700;--spot-recommended-tag-padding: .25rem .5rem;--spot-recommended-tag-border-radius: .5rem;--spot-selection-error-font-color: #ff0000;--spot-selection-error-font-size: .875rem;--spot-selection-error-padding: .5rem;--spot-terms-font-size: .75rem;--spot-terms-font-weight: 400;--spot-terms-font-color: #636569;--spot-terms-padding: 0;--spot-terms-link-text-decoration: underline;--spot-terms-link-font-size: .75rem;--spot-terms-link-font-weight: 400;--spot-terms-link-font-color: #636569;--spot-terms-link-padding: 0}.spot-refund-guarantee{font-family:var(--spot-font-family);padding:var(--spot-padding);background-color:var(--spot-background-color);color:var(--spot-font-color);border:.0625rem solid #e0e0e0;border-radius:var(--spot-border-radius);max-width:51rem;margin:1rem}.spot-refund-guarantee *{color:inherit}.spot-header__title{font-size:var(--spot-title-font-size);font-weight:var(--spot-title-font-weight);padding:var(--spot-title-padding);color:var(--spot-title-font-color);font-family:var(--spot-title-font-family);line-height:120%;letter-spacing:-.03125rem}.spot-header__description{font-size:var(--spot-description-font-size);font-weight:var(--spot-description-font-weight);color:var(--spot-description-font-color);font-family:var(--spot-description-font-family);padding:var(--spot-description-padding);line-height:125%;letter-spacing:-.025rem}.spot-content__wrapper{display:flex;flex-direction:column}@media (min-width: 48rem){.spot-content__wrapper.desktop-layout{display:grid;grid-template-columns:1fr 20.3125rem;align-items:start;gap:1rem}.desktop-layout .spot-benefits__list{grid-row:1}.desktop-layout .spot-selection__options{grid-row:2}.desktop-layout .spot-table__container{grid-row:1 / span 2}}@media (max-width: 52.438rem){.spot-selection__recommended-tag{display:inline-block;margin-left:0}}@media (max-width: 47.938rem){.spot-selection__recommended-tag{margin-top:0rem}}@media (max-width: 32.125rem){.spot-selection__recommended-tag{margin-top:.5rem}}@media (max-width: 47.9375rem){.spot-table__container{display:flex;justify-content:center}.spot-selection__recommended-tag{display:inline-block;margin-left:0}.spot-footer__container{flex-direction:column;margin-top:.5rem}.spot-refund__table{width:100%;table-layout:auto}.spot-refund__table th{padding:0rem}}.spot-benefits__list{list-style-type:none;line-height:125%;gap:.5625rem;font-size:var(--spot-bullets-font-size);font-weight:var(--spot-bullets-font-weight);color:var(--spot-bullets-font-color);font-family:var(--spot-bullets-font-family);padding:var(--spot-bullets-padding);margin-block-start:0rem;margin-block-end:0rem}.spot-benefits__list li{margin-bottom:.5rem;display:flex;align-items:flex-start;gap:.5rem}.spot-benefits__list li svg{flex-shrink:0;position:relative;top:.125rem}.spot-table__container{width:100%}.spot-refund__table{max-width:22rem;border-radius:var(--spot-table-border-radius);overflow:hidden;border:.09375rem solid #636569;table-layout:fixed;margin-bottom:1.5rem;margin-top:.25rem;padding:.625rem}.spot-refund__table--dynamic{height:auto!important;min-height:7.5rem}.spot-refund__table td,.spot-refund__table th{padding:.375rem .625rem;text-align:left}.spot-refund__table th{text-align:left;font-size:var(--spot-table-header-font-size);font-weight:var(--spot-table-header-font-weight);color:var(--spot-table-header-font-color);font-family:var(--spot-table-header-font-family);padding:var(--spot-table-header-padding)}.spot-refund__table td{text-align:left;font-size:var(--spot-table-cell-font-size);font-weight:var(--spot-table-cell-font-weight);color:var(--spot-table-cell-font-color);font-family:var(--spot-table-cell-font-family);padding:var(--spot-table-cell-padding)}input[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:.75rem;height:.75rem;border:.0625rem solid var(--spot-radio-border);border-radius:var(--spot-radio-border-radius);margin-right:.5rem;position:relative;vertical-align:middle;top:-.0625rem;cursor:pointer}input[type=radio]:checked{background:var(--spot-radio-checked-background);box-shadow:inset 0 0 0 .0625rem #fff}.spot-selection__options{display:flex;flex-direction:column;gap:.5rem}.spot-selection__option{display:block;position:relative;transition:background .2s;cursor:pointer;font-size:var(--spot-radio-text-font-size);font-weight:var(--spot-radio-text-font-weight);color:var(--spot-radio-text-font-color);font-family:var(--spot-radio-text-font-family);padding:var(--spot-radio-text-padding);margin-right:.5rem;gap:.5rem;flex:1 0 0;align-self:stretch}.spot-selection__option.selected{background:var(--spot-radio-selection-background);border-radius:var(--spot-radio-selection-border-radius);padding:var(--spot-radio-selection-padding)}.spot-selection__recommended-tag{background:var(--spot-recommended-tag-background);color:var(--spot-recommended-tag-font-color);font-size:var(--spot-recommended-tag-font-size);font-weight:var(--spot-recommended-tag-font-weight);padding:var(--spot-recommended-tag-padding);border-radius:var(--spot-recommended-tag-border-radius);margin-left:1.5rem;white-space:nowrap}.spot-selection__error{color:var(--spot-selection-error-font-color);font-size:var(--spot-selection-error-font-size);padding:var(--spot-selection-error-padding);display:none}.spot-footer__terms{margin-top:.625rem;margin-right:.25rem;font-size:var(--spot-terms-font-size);font-weight:var(--spot-terms-font-weight);color:var(--spot-terms-font-color);font-family:var(--spot-terms-font-family);padding:var(--spot-terms-padding)}.spot-footer__terms-link{text-decoration:var(--spot-terms-link-text-decoration);font-size:var(--spot-terms-link-font-size);font-weight:var(--spot-terms-link-font-weight);color:var(--spot-terms-link-font-color);font-family:var(--spot-terms-link-font-family);padding:var(--spot-terms-link-padding)}.spot-footer__container{display:flex;justify-content:space-between;align-items:center}.spot-footer__powered-by{margin-top:1.5rem}";function H(s){const e=document.createElement("style");e.textContent=s,document.head.appendChild(e)}H(x);const q={sandbox:"https://api.sandbox.getspot.com/v1/quote",production:"https://api.getspot.com/v1/quote"};class T{constructor(e={}){this.options={location:"body",showTable:!0,optInSelected:!1,apiConfig:{environment:"production",partnerId:""},quoteRequestData:{},callbacks:{},...e},this._onResize=this._updateLayout.bind(this),this.root=typeof this.options.location=="string"?document.querySelector(this.options.location):this.options.location,this.currentSelection=this.options.optInSelected?"yes":null,this._init()}async _init(){try{_(this.options);const{environment:e,partnerId:t,endpoint:o}=this.options.apiConfig,n=o||q[e],i=await l(n,t,this.options.quoteRequestData);if(i.status!=="QUOTE_AVAILABLE"){i.status==="NO_MATCHING_QUOTE"&&this.options.callbacks.noMatchingQuote({status:"NO_MATCHING_QUOTE",data:this.options.quoteRequestData});return}this.quote=i.data,this._renderWidget(),this.options.optInSelected&&this.options.callbacks.onOptIn&&this.options.callbacks.onOptIn({status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id}),this.options.callbacks.onQuoteRetrieved&&this.options.callbacks.onQuoteRetrieved(this.quote)}catch(e){this.options.callbacks.onError({message:e.message,status:e.status,responseBody:e.responseBody})}}_renderWidget(){this.container=document.createElement("div"),this.container.className="spot-refund-guarantee",this.root.appendChild(this.container),Object.entries(this.options.theme||{}).forEach(([o,n])=>{const i=`--${o}`;this.container.style.setProperty(i,n)}),y(this.container,this.quote.communication);const e=document.createElement("div");e.className="spot-content__wrapper",this.container.appendChild(e),C(e,this.quote.communication.bulletPoints),this.options.showTable&&v(e,this.quote.payoutSchedule);const t=E(e,this.quote.spotPrice,this.options.optInSelected);e.appendChild(t),k(this.container,this.quote),window.addEventListener("resize",this._onResize),this._updateLayout(),this._setupOptionListeners(t)}_updateLayout(){const e=window.matchMedia("(min-width: 768px)").matches;this.container.querySelector(".spot-content__wrapper").classList.toggle("desktop-layout",e&&this.options.showTable)}_setupOptionListeners(e){const t=e.querySelectorAll('input[type="radio"]'),o=e.querySelectorAll(".spot-selection__option");t.forEach(n=>{n.addEventListener("change",i=>{var p;const a=i.target.value;this.hideSelectionError(),this.currentSelection=a,o.forEach(f=>f.classList.remove("selected")),(p=i.target.closest(".spot-selection__option"))==null||p.classList.add("selected"),a==="yes"&&this.options.callbacks.onOptIn&&this.options.callbacks.onOptIn({status:"QUOTE_ACCEPTED",spotPrice:this.quote.spotPrice,quoteId:this.quote.id}),a==="no"&&this.options.callbacks.onOptOut&&this.options.callbacks.onOptOut({status:"QUOTE_DECLINED",quoteId:this.quote.id})})})}showSelectionError(){var e;if(!this.errorEl){this.errorEl=document.createElement("div"),this.errorEl.className="spot-selection__error",this.errorEl.textContent="Please make a selection";const t=(e=this.container)==null?void 0:e.querySelector(".spot-selection__options");t&&t.insertAdjacentElement("afterend",this.errorEl)}this.errorEl.style.display="block"}hideSelectionError(){this.errorEl&&(this.errorEl.style.display="none")}validateSelection(){if(!this.container)return!1;const e=!!this.container.querySelector('input[name="selection"]:checked');return e?this.hideSelectionError():this.showSelectionError(),e}getSelection(){var e,t;return this.currentSelection==null?null:{selection:this.currentSelection,quoteId:(e=this.quote)==null?void 0:e.id,spotPrice:(t=this.quote)==null?void 0:t.spotPrice,status:this.currentSelection==="yes"?"QUOTE_ACCEPTED":"QUOTE_DECLINED"}}destroy(){window.removeEventListener("resize",this._onResize),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}}return T});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getspot/spot-widget",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { fetchQuote } from "./api.js";
2
+ import { validateOptions } from "./validateOptions.js";
2
3
  import {
3
4
  renderHeader,
4
5
  renderBenefits,
@@ -17,13 +18,18 @@ function injectStyles(css) {
17
18
 
18
19
  injectStyles(styles);
19
20
 
21
+ const apiEndpoint = {
22
+ sandbox: "https://api.sandbox.getspot.com/v1/quote",
23
+ production: "https://api.getspot.com/v1/quote",
24
+ };
25
+
20
26
  class SpotWidget {
21
27
  constructor(options = {}) {
22
28
  this.options = {
23
29
  location: "body",
24
30
  showTable: true,
25
31
  optInSelected: false,
26
- apiConfig: { endpoint: "", partnerId: "" },
32
+ apiConfig: { environment: "production", partnerId: "" },
27
33
  quoteRequestData: {},
28
34
  callbacks: {},
29
35
  ...options,
@@ -41,9 +47,18 @@ class SpotWidget {
41
47
 
42
48
  async _init() {
43
49
  try {
50
+ validateOptions(this.options);
51
+ const {
52
+ environment,
53
+ partnerId,
54
+ endpoint: customEndpoint,
55
+ } = this.options.apiConfig;
56
+
57
+ const endpoint = customEndpoint || apiEndpoint[environment];
58
+
44
59
  const response = await fetchQuote(
45
- this.options.apiConfig.endpoint,
46
- this.options.apiConfig.partnerId,
60
+ endpoint,
61
+ partnerId,
47
62
  this.options.quoteRequestData
48
63
  );
49
64
 
@@ -81,12 +96,10 @@ class SpotWidget {
81
96
  }
82
97
 
83
98
  _renderWidget() {
84
- // outer container
85
99
  this.container = document.createElement("div");
86
100
  this.container.className = "spot-refund-guarantee";
87
101
  this.root.appendChild(this.container);
88
102
 
89
- // apply theme
90
103
  Object.entries(this.options.theme || {}).forEach(([k, v]) => {
91
104
  const cssVariable = `--${k}`;
92
105
  this.container.style.setProperty(cssVariable, v);
@@ -0,0 +1,138 @@
1
+ const apiEndpoint = {
2
+ sandbox: "https://api.sandbox.getspot.com/v1/quote",
3
+ production: "https://api.getspot.com/v1/quote",
4
+ };
5
+
6
+ export function validateOptions(options) {
7
+ const {
8
+ apiConfig = {},
9
+ quoteRequestData,
10
+ callbacks = {},
11
+ location,
12
+ theme,
13
+ } = options;
14
+
15
+ const {
16
+ environment = "sandbox",
17
+ partnerId,
18
+ endpoint: customEndpoint,
19
+ } = apiConfig;
20
+
21
+ if (!partnerId || typeof partnerId !== "string") {
22
+ throw new Error("Invalid or missing partnerId in apiConfig");
23
+ }
24
+
25
+ const endpoint = customEndpoint || apiEndpoint[environment];
26
+
27
+ if (!endpoint) {
28
+ throw new Error(`Invalid environment in apiConfig: ${environment}`);
29
+ }
30
+
31
+ if (!quoteRequestData || typeof quoteRequestData !== "object") {
32
+ throw new Error("quoteRequestData must be a non-null object");
33
+ }
34
+
35
+ const requiredFields = [
36
+ "startDate",
37
+ "endDate",
38
+ "currencyCode",
39
+ "eventType",
40
+ "productType",
41
+ "productDuration",
42
+ "productPrice",
43
+ "productId",
44
+ "productName",
45
+ ];
46
+
47
+ requiredFields.forEach((field) => {
48
+ if (
49
+ !Object.prototype.hasOwnProperty.call(quoteRequestData, field) ||
50
+ quoteRequestData[field] === undefined ||
51
+ quoteRequestData[field] === null
52
+ ) {
53
+ throw new Error(`Missing required quoteRequestData field: '${field}'`);
54
+ }
55
+ });
56
+
57
+ const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
58
+ if (!iso8601Regex.test(quoteRequestData.startDate)) {
59
+ throw new Error("startDate must be a valid ISO8601 string");
60
+ }
61
+ if (!iso8601Regex.test(quoteRequestData.endDate)) {
62
+ throw new Error("endDate must be a valid ISO8601 string");
63
+ }
64
+
65
+ if (typeof quoteRequestData.currencyCode !== "string") {
66
+ throw new Error("currencyCode must be a string");
67
+ }
68
+
69
+ const validCurrencyCodes = ["USD", "CAD", "AUD"];
70
+ if (!validCurrencyCodes.includes(quoteRequestData.currencyCode)) {
71
+ throw new Error(`Invalid currency code: ${quoteRequestData.currencyCode}`);
72
+ }
73
+
74
+ if (typeof quoteRequestData.eventType !== "string") {
75
+ throw new Error("eventType must be a string");
76
+ }
77
+
78
+ if (typeof quoteRequestData.productType !== "string") {
79
+ throw new Error("productType must be a string");
80
+ }
81
+
82
+ const validProductTypes = ["Pass", "Trip", "Registration"];
83
+ if (!validProductTypes.includes(quoteRequestData.productType)) {
84
+ throw new Error(
85
+ `productType must be one of ${validProductTypes.join(", ")}`
86
+ );
87
+ }
88
+
89
+ if (typeof quoteRequestData.productDuration !== "string") {
90
+ throw new Error("productDuration must be a string");
91
+ }
92
+
93
+ const validDurations = ["Daily", "Seasonal", "Trip", "Event"];
94
+ if (!validDurations.includes(quoteRequestData.productDuration)) {
95
+ throw new Error(
96
+ `productDuration must be one of ${validDurations.join(", ")}`
97
+ );
98
+ }
99
+
100
+ if (
101
+ typeof quoteRequestData.productPrice !== "number" ||
102
+ isNaN(quoteRequestData.productPrice)
103
+ ) {
104
+ throw new Error("productPrice must be a valid number");
105
+ }
106
+
107
+ if (typeof quoteRequestData.productId !== "string") {
108
+ throw new Error("productId must be a string");
109
+ }
110
+
111
+ if (typeof quoteRequestData.productName !== "string") {
112
+ throw new Error("productName must be a string");
113
+ }
114
+
115
+ const callbackNames = [
116
+ "onOptIn",
117
+ "onOptOut",
118
+ "onQuoteRetrieved",
119
+ "onError",
120
+ "noMatchingQuote",
121
+ ];
122
+ callbackNames.forEach((cbName) => {
123
+ const cb = callbacks[cbName];
124
+ if (cb && typeof cb !== "function") {
125
+ throw new Error(`Callback '${cbName}' must be a function.`);
126
+ }
127
+ });
128
+
129
+ if (typeof location === "string" && !document.querySelector(location)) {
130
+ throw new Error(`Invalid location selector: '${location}'`);
131
+ }
132
+
133
+ if (theme && typeof theme !== "object") {
134
+ throw new Error(
135
+ "Theme must be an object with CSS variables, do not include the '--' prefix"
136
+ );
137
+ }
138
+ }