@getspot/spot-widget 1.1.1 → 1.3.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.
@@ -1,6 +1,7 @@
1
1
  const apiEndpoint = {
2
2
  sandbox: "https://api.sandbox.getspot.com/v1/quote",
3
3
  production: "https://api.getspot.com/v1/quote",
4
+ local: "http://localhost:3999/api/v1/quote"
4
5
  };
5
6
 
6
7
  export function validateOptions(options) {
@@ -12,6 +13,7 @@ export function validateOptions(options) {
12
13
  theme,
13
14
  } = options;
14
15
 
16
+
15
17
  const {
16
18
  environment = "sandbox",
17
19
  partnerId,
@@ -28,8 +30,8 @@ export function validateOptions(options) {
28
30
  throw new Error(`Invalid environment in apiConfig: ${environment}`);
29
31
  }
30
32
 
31
- if (!quoteRequestData || typeof quoteRequestData !== "object") {
32
- throw new Error("quoteRequestData must be a non-null object");
33
+ if (!quoteRequestData || (typeof quoteRequestData !== "object" && !Array.isArray(quoteRequestData))) {
34
+ throw new Error("quoteRequestData must be a non-null object or array");
33
35
  }
34
36
 
35
37
  const requiredFields = [
@@ -45,76 +47,184 @@ export function validateOptions(options) {
45
47
  "productName",
46
48
  ];
47
49
 
48
- requiredFields.forEach((field) => {
49
- if (
50
- !Object.prototype.hasOwnProperty.call(quoteRequestData, field) ||
51
- quoteRequestData[field] === undefined ||
52
- quoteRequestData[field] === null
53
- ) {
54
- throw new Error(`Missing required quoteRequestData field: '${field}'`);
50
+ function validateQuoteItem(item, index = null) {
51
+ const prefix = index !== null ? `quoteRequestData[${index}]` : "quoteRequestData";
52
+
53
+ requiredFields.forEach((field) => {
54
+ if (
55
+ !Object.prototype.hasOwnProperty.call(item, field) ||
56
+ item[field] === undefined ||
57
+ item[field] === null
58
+ ) {
59
+ throw new Error(`Missing required ${prefix} field: '${field}'`);
60
+ }
61
+ });
62
+
63
+ const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
64
+ if (!iso8601Regex.test(item.startDate)) {
65
+ throw new Error(`${prefix}.startDate must be a valid ISO8601 string`);
66
+ }
67
+ if (!iso8601Regex.test(item.endDate)) {
68
+ throw new Error(`${prefix}.endDate must be a valid ISO8601 string`);
55
69
  }
56
- });
57
70
 
58
- const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
59
- if (!iso8601Regex.test(quoteRequestData.startDate)) {
60
- throw new Error("startDate must be a valid ISO8601 string");
61
- }
62
- if (!iso8601Regex.test(quoteRequestData.endDate)) {
63
- throw new Error("endDate must be a valid ISO8601 string");
64
- }
71
+ if (typeof item.currencyCode !== "string") {
72
+ throw new Error(`${prefix}.currencyCode must be a string`);
73
+ }
65
74
 
66
- if (typeof quoteRequestData.currencyCode !== "string") {
67
- throw new Error("currencyCode must be a string");
68
- }
75
+ const validCurrencyCodes = ["USD", "CAD", "AUD"];
76
+ if (!validCurrencyCodes.includes(item.currencyCode)) {
77
+ throw new Error(`Invalid ${prefix}.currencyCode: ${item.currencyCode}`);
78
+ }
69
79
 
70
- const validCurrencyCodes = ["USD", "CAD", "AUD"];
71
- if (!validCurrencyCodes.includes(quoteRequestData.currencyCode)) {
72
- throw new Error(`Invalid currency code: ${quoteRequestData.currencyCode}`);
73
- }
80
+ if (typeof item.eventType !== "string") {
81
+ throw new Error(`${prefix}.eventType must be a string`);
82
+ }
74
83
 
75
- if (typeof quoteRequestData.eventType !== "string") {
76
- throw new Error("eventType must be a string");
77
- }
84
+ if (typeof item.productType !== "string") {
85
+ throw new Error(`${prefix}.productType must be a string`);
86
+ }
78
87
 
79
- if (typeof quoteRequestData.productType !== "string") {
80
- throw new Error("productType must be a string");
81
- }
88
+ const validProductTypes = ["Pass", "Trip", "Registration"];
89
+ if (!validProductTypes.includes(item.productType)) {
90
+ throw new Error(
91
+ `${prefix}.productType must be one of ${validProductTypes.join(", ")}`
92
+ );
93
+ }
82
94
 
83
- const validProductTypes = ["Pass", "Trip", "Registration"];
84
- if (!validProductTypes.includes(quoteRequestData.productType)) {
85
- throw new Error(
86
- `productType must be one of ${validProductTypes.join(", ")}`
87
- );
88
- }
95
+ if (typeof item.productDuration !== "string") {
96
+ throw new Error(`${prefix}.productDuration must be a string`);
97
+ }
89
98
 
90
- if (typeof quoteRequestData.productDuration !== "string") {
91
- throw new Error("productDuration must be a string");
92
- }
99
+ const validDurations = ["Daily", "Seasonal", "Trip", "Event"];
100
+ if (!validDurations.includes(item.productDuration)) {
101
+ throw new Error(
102
+ `${prefix}.productDuration must be one of ${validDurations.join(", ")}`
103
+ );
104
+ }
93
105
 
94
- const validDurations = ["Daily", "Seasonal", "Trip", "Event"];
95
- if (!validDurations.includes(quoteRequestData.productDuration)) {
96
- throw new Error(
97
- `productDuration must be one of ${validDurations.join(", ")}`
98
- );
99
- }
106
+ if (
107
+ typeof item.productPrice !== "number" ||
108
+ isNaN(item.productPrice)
109
+ ) {
110
+ throw new Error(`${prefix}.productPrice must be a valid number`);
111
+ }
100
112
 
101
- if (
102
- typeof quoteRequestData.productPrice !== "number" ||
103
- isNaN(quoteRequestData.productPrice)
104
- ) {
105
- throw new Error("productPrice must be a valid number");
106
- }
113
+ if (typeof item.productId !== "string") {
114
+ throw new Error(`${prefix}.productId must be a string`);
115
+ }
107
116
 
108
- if (typeof quoteRequestData.productId !== "string") {
109
- throw new Error("productId must be a string");
110
- }
117
+ if (typeof item.cartId !== "string") {
118
+ throw new Error(`${prefix}.cartId must be a string`);
119
+ }
111
120
 
112
- if (typeof quoteRequestData.cartId !== "string") {
113
- throw new Error("cartId must be a string");
121
+ if (typeof item.productName !== "string") {
122
+ throw new Error(`${prefix}.productName must be a string`);
123
+ }
114
124
  }
115
125
 
116
- if (typeof quoteRequestData.productName !== "string") {
117
- throw new Error("productName must be a string");
126
+ // Check if it's a batch quote format
127
+ if (quoteRequestData.cartInfo && quoteRequestData.items) {
128
+ // Validate batch format
129
+ const { cartInfo, items } = quoteRequestData;
130
+
131
+ if (!cartInfo || typeof cartInfo !== "object") {
132
+ throw new Error("quoteRequestData.cartInfo must be a non-null object");
133
+ }
134
+
135
+ // Validate cart-level fields
136
+ if (!cartInfo.cartId || typeof cartInfo.cartId !== "string") {
137
+ throw new Error("quoteRequestData.cartInfo.cartId must be a string");
138
+ }
139
+ if (!cartInfo.cartName || typeof cartInfo.cartName !== "string") {
140
+ throw new Error("quoteRequestData.cartInfo.cartName must be a string");
141
+ }
142
+ if (!cartInfo.currencyCode || typeof cartInfo.currencyCode !== "string") {
143
+ throw new Error("quoteRequestData.cartInfo.currencyCode must be a string");
144
+ }
145
+
146
+ const validCurrencyCodes = ["USD", "CAD", "AUD"];
147
+ if (!validCurrencyCodes.includes(cartInfo.currencyCode)) {
148
+ throw new Error(`Invalid quoteRequestData.cartInfo.currencyCode: ${cartInfo.currencyCode}`);
149
+ }
150
+
151
+ if (!Array.isArray(items) || items.length === 0) {
152
+ throw new Error("quoteRequestData.items must be a non-empty array");
153
+ }
154
+
155
+ // Validate each item but without cartId and currencyCode since they're at cart level
156
+ const itemRequiredFields = requiredFields.filter(f => f !== "cartId" && f !== "currencyCode");
157
+
158
+ items.forEach((item, index) => {
159
+ if (!item || typeof item !== "object") {
160
+ throw new Error(`quoteRequestData.items[${index}] must be a non-null object`);
161
+ }
162
+
163
+ const prefix = `quoteRequestData.items[${index}]`;
164
+
165
+ itemRequiredFields.forEach((field) => {
166
+ if (!Object.prototype.hasOwnProperty.call(item, field) || item[field] === undefined || item[field] === null) {
167
+ throw new Error(`Missing required ${prefix} field: '${field}'`);
168
+ }
169
+ });
170
+
171
+ // Validate item-specific fields
172
+ const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
173
+ if (!iso8601Regex.test(item.startDate)) {
174
+ throw new Error(`${prefix}.startDate must be a valid ISO8601 string`);
175
+ }
176
+ if (!iso8601Regex.test(item.endDate)) {
177
+ throw new Error(`${prefix}.endDate must be a valid ISO8601 string`);
178
+ }
179
+
180
+ if (typeof item.eventType !== "string") {
181
+ throw new Error(`${prefix}.eventType must be a string`);
182
+ }
183
+
184
+ if (typeof item.productType !== "string") {
185
+ throw new Error(`${prefix}.productType must be a string`);
186
+ }
187
+
188
+ const validProductTypes = ["Pass", "Trip", "Registration"];
189
+ if (!validProductTypes.includes(item.productType)) {
190
+ throw new Error(`${prefix}.productType must be one of ${validProductTypes.join(", ")}`);
191
+ }
192
+
193
+ if (typeof item.productDuration !== "string") {
194
+ throw new Error(`${prefix}.productDuration must be a string`);
195
+ }
196
+
197
+ const validDurations = ["Daily", "Seasonal", "Trip", "Event"];
198
+ if (!validDurations.includes(item.productDuration)) {
199
+ throw new Error(`${prefix}.productDuration must be one of ${validDurations.join(", ")}`);
200
+ }
201
+
202
+ if (typeof item.productPrice !== "number" || isNaN(item.productPrice)) {
203
+ throw new Error(`${prefix}.productPrice must be a valid number`);
204
+ }
205
+
206
+ if (typeof item.productId !== "string") {
207
+ throw new Error(`${prefix}.productId must be a string`);
208
+ }
209
+
210
+ if (typeof item.productName !== "string") {
211
+ throw new Error(`${prefix}.productName must be a string`);
212
+ }
213
+ });
214
+ } else if (Array.isArray(quoteRequestData)) {
215
+ // Legacy array format - kept for backward compatibility
216
+ if (quoteRequestData.length === 0) {
217
+ throw new Error("quoteRequestData array cannot be empty");
218
+ }
219
+ quoteRequestData.forEach((item, index) => {
220
+ if (!item || typeof item !== "object") {
221
+ throw new Error(`quoteRequestData[${index}] must be a non-null object`);
222
+ }
223
+ validateQuoteItem(item, index);
224
+ });
225
+ } else {
226
+ // Single quote format
227
+ validateQuoteItem(quoteRequestData);
118
228
  }
119
229
 
120
230
  const callbackNames = [