@cimplify/sdk 0.6.7 → 0.6.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 +19 -0
- package/dist/advanced.d.mts +2 -2
- package/dist/advanced.d.ts +2 -2
- package/dist/advanced.js +115 -84
- package/dist/advanced.mjs +115 -84
- package/dist/{client-B4etj3AD.d.mts → client-D4vA6FY_.d.ts} +36 -34
- package/dist/{client-CYVVuP5J.d.ts → client-FQUyv41r.d.mts} +36 -34
- package/dist/{index-DzNb32O3.d.mts → index-DaKJxoEh.d.mts} +13 -12
- package/dist/{index-BOYF-efj.d.ts → index-pztT_bcJ.d.ts} +13 -12
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +147 -89
- package/dist/index.mjs +144 -90
- package/dist/{payment-pjpfIKX8.d.mts → payment-Cu75tmUc.d.mts} +22 -7
- package/dist/{payment-pjpfIKX8.d.ts → payment-Cu75tmUc.d.ts} +22 -7
- package/dist/react.d.mts +99 -4
- package/dist/react.d.ts +99 -4
- package/dist/react.js +827 -22
- package/dist/react.mjs +822 -23
- package/dist/utils.d.mts +2 -2
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +76 -4
- package/dist/utils.mjs +76 -4
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -713,6 +713,15 @@ function CimplifyCheckout({
|
|
|
713
713
|
}
|
|
714
714
|
|
|
715
715
|
// src/types/common.ts
|
|
716
|
+
function money(value) {
|
|
717
|
+
return value;
|
|
718
|
+
}
|
|
719
|
+
function moneyFromNumber(value) {
|
|
720
|
+
return value.toFixed(2);
|
|
721
|
+
}
|
|
722
|
+
function currencyCode(value) {
|
|
723
|
+
return value;
|
|
724
|
+
}
|
|
716
725
|
var ErrorCode = {
|
|
717
726
|
// General
|
|
718
727
|
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
@@ -824,6 +833,11 @@ function err(error) {
|
|
|
824
833
|
return { ok: false, error };
|
|
825
834
|
}
|
|
826
835
|
|
|
836
|
+
// src/query/builder.ts
|
|
837
|
+
function escapeQueryValue(value) {
|
|
838
|
+
return value.replace(/'/g, "\\'");
|
|
839
|
+
}
|
|
840
|
+
|
|
827
841
|
// src/catalogue.ts
|
|
828
842
|
function toCimplifyError(error) {
|
|
829
843
|
if (error instanceof CimplifyError) return enrichError(error);
|
|
@@ -904,7 +918,7 @@ var CatalogueQueries = class {
|
|
|
904
918
|
let query = "products";
|
|
905
919
|
const filters = [];
|
|
906
920
|
if (options?.category) {
|
|
907
|
-
filters.push(`@.category_id=='${options.category}'`);
|
|
921
|
+
filters.push(`@.category_id=='${escapeQueryValue(options.category)}'`);
|
|
908
922
|
}
|
|
909
923
|
if (options?.featured !== void 0) {
|
|
910
924
|
filters.push(`@.featured==${options.featured}`);
|
|
@@ -913,7 +927,7 @@ var CatalogueQueries = class {
|
|
|
913
927
|
filters.push(`@.in_stock==${options.in_stock}`);
|
|
914
928
|
}
|
|
915
929
|
if (options?.search) {
|
|
916
|
-
filters.push(`@.name contains '${options.search}'`);
|
|
930
|
+
filters.push(`@.name contains '${escapeQueryValue(options.search)}'`);
|
|
917
931
|
}
|
|
918
932
|
if (options?.min_price !== void 0) {
|
|
919
933
|
filters.push(`@.price>=${options.min_price}`);
|
|
@@ -944,7 +958,9 @@ var CatalogueQueries = class {
|
|
|
944
958
|
}
|
|
945
959
|
async getProductBySlug(slug) {
|
|
946
960
|
const filteredResult = await safe(
|
|
947
|
-
this.client.query(
|
|
961
|
+
this.client.query(
|
|
962
|
+
`products[?(@.slug=='${escapeQueryValue(slug)}')]`
|
|
963
|
+
)
|
|
948
964
|
);
|
|
949
965
|
if (!filteredResult.ok) return filteredResult;
|
|
950
966
|
const exactMatch = findProductBySlug(filteredResult.value, slug);
|
|
@@ -996,7 +1012,7 @@ var CatalogueQueries = class {
|
|
|
996
1012
|
}
|
|
997
1013
|
async getCategoryBySlug(slug) {
|
|
998
1014
|
const result = await safe(
|
|
999
|
-
this.client.query(`categories[?(@.slug=='${slug}')]`)
|
|
1015
|
+
this.client.query(`categories[?(@.slug=='${escapeQueryValue(slug)}')]`)
|
|
1000
1016
|
);
|
|
1001
1017
|
if (!result.ok) return result;
|
|
1002
1018
|
if (!result.value.length) {
|
|
@@ -1005,7 +1021,11 @@ var CatalogueQueries = class {
|
|
|
1005
1021
|
return ok(result.value[0]);
|
|
1006
1022
|
}
|
|
1007
1023
|
async getCategoryProducts(categoryId) {
|
|
1008
|
-
return safe(
|
|
1024
|
+
return safe(
|
|
1025
|
+
this.client.query(
|
|
1026
|
+
`products[?(@.category_id=='${escapeQueryValue(categoryId)}')]`
|
|
1027
|
+
)
|
|
1028
|
+
);
|
|
1009
1029
|
}
|
|
1010
1030
|
async getCollections() {
|
|
1011
1031
|
return safe(this.client.query("collections"));
|
|
@@ -1015,7 +1035,9 @@ var CatalogueQueries = class {
|
|
|
1015
1035
|
}
|
|
1016
1036
|
async getCollectionBySlug(slug) {
|
|
1017
1037
|
const result = await safe(
|
|
1018
|
-
this.client.query(
|
|
1038
|
+
this.client.query(
|
|
1039
|
+
`collections[?(@.slug=='${escapeQueryValue(slug)}')]`
|
|
1040
|
+
)
|
|
1019
1041
|
);
|
|
1020
1042
|
if (!result.ok) return result;
|
|
1021
1043
|
if (!result.value.length) {
|
|
@@ -1028,7 +1050,9 @@ var CatalogueQueries = class {
|
|
|
1028
1050
|
}
|
|
1029
1051
|
async searchCollections(query, limit = 20) {
|
|
1030
1052
|
return safe(
|
|
1031
|
-
this.client.query(
|
|
1053
|
+
this.client.query(
|
|
1054
|
+
`collections[?(@.name contains '${escapeQueryValue(query)}')]#limit(${limit})`
|
|
1055
|
+
)
|
|
1032
1056
|
);
|
|
1033
1057
|
}
|
|
1034
1058
|
async getBundles() {
|
|
@@ -1039,7 +1063,9 @@ var CatalogueQueries = class {
|
|
|
1039
1063
|
}
|
|
1040
1064
|
async getBundleBySlug(slug) {
|
|
1041
1065
|
const result = await safe(
|
|
1042
|
-
this.client.query(
|
|
1066
|
+
this.client.query(
|
|
1067
|
+
`bundles[?(@.slug=='${escapeQueryValue(slug)}')]`
|
|
1068
|
+
)
|
|
1043
1069
|
);
|
|
1044
1070
|
if (!result.ok) return result;
|
|
1045
1071
|
if (!result.value.length) {
|
|
@@ -1049,7 +1075,9 @@ var CatalogueQueries = class {
|
|
|
1049
1075
|
}
|
|
1050
1076
|
async searchBundles(query, limit = 20) {
|
|
1051
1077
|
return safe(
|
|
1052
|
-
this.client.query(
|
|
1078
|
+
this.client.query(
|
|
1079
|
+
`bundles[?(@.name contains '${escapeQueryValue(query)}')]#limit(${limit})`
|
|
1080
|
+
)
|
|
1053
1081
|
);
|
|
1054
1082
|
}
|
|
1055
1083
|
async getComposites(options) {
|
|
@@ -1089,9 +1117,9 @@ var CatalogueQueries = class {
|
|
|
1089
1117
|
}
|
|
1090
1118
|
async search(query, options) {
|
|
1091
1119
|
const limit = options?.limit ?? 20;
|
|
1092
|
-
let searchQuery = `products[?(@.name contains '${query}')]`;
|
|
1120
|
+
let searchQuery = `products[?(@.name contains '${escapeQueryValue(query)}')]`;
|
|
1093
1121
|
if (options?.category) {
|
|
1094
|
-
searchQuery = `products[?(@.name contains '${query}' && @.category_id=='${options.category}')]`;
|
|
1122
|
+
searchQuery = `products[?(@.name contains '${escapeQueryValue(query)}' && @.category_id=='${escapeQueryValue(options.category)}')]`;
|
|
1095
1123
|
}
|
|
1096
1124
|
searchQuery += `#limit(${limit})`;
|
|
1097
1125
|
return safe(this.client.query(searchQuery));
|
|
@@ -1108,7 +1136,7 @@ var CatalogueQueries = class {
|
|
|
1108
1136
|
async getMenu(options) {
|
|
1109
1137
|
let query = "menu";
|
|
1110
1138
|
if (options?.category) {
|
|
1111
|
-
query = `menu[?(@.category=='${options.category}')]`;
|
|
1139
|
+
query = `menu[?(@.category=='${escapeQueryValue(options.category)}')]`;
|
|
1112
1140
|
}
|
|
1113
1141
|
if (options?.limit) {
|
|
1114
1142
|
query += `#limit(${options.limit})`;
|
|
@@ -1402,6 +1430,8 @@ function normalizeStatusResponse(response) {
|
|
|
1402
1430
|
}
|
|
1403
1431
|
const res = response;
|
|
1404
1432
|
const normalizedStatus = normalizePaymentStatusValue(res.status ?? void 0);
|
|
1433
|
+
const normalizedAmount = typeof res.amount === "string" ? money(res.amount) : typeof res.amount === "number" && Number.isFinite(res.amount) ? moneyFromNumber(res.amount) : void 0;
|
|
1434
|
+
const normalizedCurrency = typeof res.currency === "string" && res.currency.trim().length > 0 ? currencyCode(res.currency) : void 0;
|
|
1405
1435
|
const paidValue = res.paid === true;
|
|
1406
1436
|
const derivedPaid = paidValue || [
|
|
1407
1437
|
"success",
|
|
@@ -1414,8 +1444,8 @@ function normalizeStatusResponse(response) {
|
|
|
1414
1444
|
return {
|
|
1415
1445
|
status: normalizedStatus,
|
|
1416
1446
|
paid: derivedPaid,
|
|
1417
|
-
amount:
|
|
1418
|
-
currency:
|
|
1447
|
+
amount: normalizedAmount,
|
|
1448
|
+
currency: normalizedCurrency,
|
|
1419
1449
|
reference: res.reference,
|
|
1420
1450
|
message: res.message || ""
|
|
1421
1451
|
};
|
|
@@ -2107,14 +2137,17 @@ var CheckoutService = class {
|
|
|
2107
2137
|
pay_currency: data.pay_currency,
|
|
2108
2138
|
fx_quote_id: data.fx_quote_id
|
|
2109
2139
|
};
|
|
2110
|
-
const baseCurrency = (
|
|
2140
|
+
const baseCurrency = currencyCode(
|
|
2141
|
+
(cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase()
|
|
2142
|
+
);
|
|
2111
2143
|
const payCurrency = data.pay_currency?.trim().toUpperCase();
|
|
2144
|
+
const payCurrencyCode = payCurrency ? currencyCode(payCurrency) : void 0;
|
|
2112
2145
|
const cartTotalAmount = Number.parseFloat(cart.pricing.total_price || "0");
|
|
2113
|
-
if (
|
|
2146
|
+
if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
|
|
2114
2147
|
const fxQuoteResult = await this.client.fx.lockQuote({
|
|
2115
2148
|
from: baseCurrency,
|
|
2116
|
-
to:
|
|
2117
|
-
amount:
|
|
2149
|
+
to: payCurrencyCode,
|
|
2150
|
+
amount: cart.pricing.total_price
|
|
2118
2151
|
});
|
|
2119
2152
|
if (!fxQuoteResult.ok) {
|
|
2120
2153
|
return ok(
|
|
@@ -2125,7 +2158,7 @@ var CheckoutService = class {
|
|
|
2125
2158
|
)
|
|
2126
2159
|
);
|
|
2127
2160
|
}
|
|
2128
|
-
checkoutData.pay_currency =
|
|
2161
|
+
checkoutData.pay_currency = payCurrencyCode;
|
|
2129
2162
|
checkoutData.fx_quote_id = fxQuoteResult.value.id;
|
|
2130
2163
|
}
|
|
2131
2164
|
data.on_status_change?.("processing", {});
|
|
@@ -3576,7 +3609,7 @@ var CimplifyClient = class {
|
|
|
3576
3609
|
signal: controller.signal
|
|
3577
3610
|
});
|
|
3578
3611
|
clearTimeout(timeoutId);
|
|
3579
|
-
if (response.ok
|
|
3612
|
+
if (response.ok) {
|
|
3580
3613
|
this.hooks.onRequestSuccess?.({
|
|
3581
3614
|
...context,
|
|
3582
3615
|
status: response.status,
|
|
@@ -3584,6 +3617,21 @@ var CimplifyClient = class {
|
|
|
3584
3617
|
});
|
|
3585
3618
|
return response;
|
|
3586
3619
|
}
|
|
3620
|
+
if (response.status >= 400 && response.status < 500) {
|
|
3621
|
+
this.hooks.onRequestError?.({
|
|
3622
|
+
...context,
|
|
3623
|
+
error: new CimplifyError(
|
|
3624
|
+
`HTTP_${response.status}`,
|
|
3625
|
+
`Request failed with status ${response.status}`,
|
|
3626
|
+
false
|
|
3627
|
+
),
|
|
3628
|
+
status: response.status,
|
|
3629
|
+
durationMs: Date.now() - startTime,
|
|
3630
|
+
retryCount,
|
|
3631
|
+
retryable: false
|
|
3632
|
+
});
|
|
3633
|
+
return response;
|
|
3634
|
+
}
|
|
3587
3635
|
if (response.status >= 500 && attempt < this.maxRetries) {
|
|
3588
3636
|
retryCount++;
|
|
3589
3637
|
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
@@ -3596,10 +3644,17 @@ var CimplifyClient = class {
|
|
|
3596
3644
|
await sleep(delay);
|
|
3597
3645
|
continue;
|
|
3598
3646
|
}
|
|
3599
|
-
this.hooks.
|
|
3647
|
+
this.hooks.onRequestError?.({
|
|
3600
3648
|
...context,
|
|
3649
|
+
error: new CimplifyError(
|
|
3650
|
+
"SERVER_ERROR",
|
|
3651
|
+
`Server error ${response.status} after ${retryCount} retries`,
|
|
3652
|
+
false
|
|
3653
|
+
),
|
|
3601
3654
|
status: response.status,
|
|
3602
|
-
durationMs: Date.now() - startTime
|
|
3655
|
+
durationMs: Date.now() - startTime,
|
|
3656
|
+
retryCount,
|
|
3657
|
+
retryable: false
|
|
3603
3658
|
});
|
|
3604
3659
|
return response;
|
|
3605
3660
|
} catch (error) {
|
|
@@ -5085,6 +5140,750 @@ function useLocations(options = {}) {
|
|
|
5085
5140
|
isLoading
|
|
5086
5141
|
};
|
|
5087
5142
|
}
|
|
5143
|
+
var collectionsCache = /* @__PURE__ */ new Map();
|
|
5144
|
+
var collectionsInflight = /* @__PURE__ */ new Map();
|
|
5145
|
+
function buildCollectionsCacheKey(client, locationId) {
|
|
5146
|
+
return JSON.stringify({
|
|
5147
|
+
key: client.getPublicKey(),
|
|
5148
|
+
location_id: locationId || "__none__"
|
|
5149
|
+
});
|
|
5150
|
+
}
|
|
5151
|
+
function useCollections(options = {}) {
|
|
5152
|
+
const context = useOptionalCimplify();
|
|
5153
|
+
const client = options.client ?? context?.client;
|
|
5154
|
+
if (!client) {
|
|
5155
|
+
throw new Error("useCollections must be used within CimplifyProvider or passed { client }.");
|
|
5156
|
+
}
|
|
5157
|
+
const enabled = options.enabled ?? true;
|
|
5158
|
+
const locationId = client.getLocationId();
|
|
5159
|
+
const previousLocationIdRef = react.useRef(locationId);
|
|
5160
|
+
const requestIdRef = react.useRef(0);
|
|
5161
|
+
const cacheKey = react.useMemo(
|
|
5162
|
+
() => buildCollectionsCacheKey(client, locationId),
|
|
5163
|
+
[client, locationId]
|
|
5164
|
+
);
|
|
5165
|
+
const cached = collectionsCache.get(cacheKey);
|
|
5166
|
+
const [collections, setCollections] = react.useState(cached?.collections ?? []);
|
|
5167
|
+
const [isLoading, setIsLoading] = react.useState(enabled && !cached);
|
|
5168
|
+
const [error, setError] = react.useState(null);
|
|
5169
|
+
react.useEffect(() => {
|
|
5170
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5171
|
+
collectionsCache.clear();
|
|
5172
|
+
collectionsInflight.clear();
|
|
5173
|
+
previousLocationIdRef.current = locationId;
|
|
5174
|
+
}
|
|
5175
|
+
}, [locationId]);
|
|
5176
|
+
const load = react.useCallback(
|
|
5177
|
+
async (force = false) => {
|
|
5178
|
+
if (!enabled) {
|
|
5179
|
+
setIsLoading(false);
|
|
5180
|
+
return;
|
|
5181
|
+
}
|
|
5182
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5183
|
+
setError(null);
|
|
5184
|
+
if (!force) {
|
|
5185
|
+
const cacheEntry = collectionsCache.get(cacheKey);
|
|
5186
|
+
if (cacheEntry) {
|
|
5187
|
+
setCollections(cacheEntry.collections);
|
|
5188
|
+
setIsLoading(false);
|
|
5189
|
+
return;
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
setIsLoading(true);
|
|
5193
|
+
try {
|
|
5194
|
+
const existing = collectionsInflight.get(cacheKey);
|
|
5195
|
+
const promise = existing ?? (async () => {
|
|
5196
|
+
const result = await client.catalogue.getCollections();
|
|
5197
|
+
if (!result.ok) {
|
|
5198
|
+
throw result.error;
|
|
5199
|
+
}
|
|
5200
|
+
return result.value;
|
|
5201
|
+
})();
|
|
5202
|
+
if (!existing) {
|
|
5203
|
+
collectionsInflight.set(cacheKey, promise);
|
|
5204
|
+
promise.finally(() => {
|
|
5205
|
+
collectionsInflight.delete(cacheKey);
|
|
5206
|
+
}).catch(() => void 0);
|
|
5207
|
+
}
|
|
5208
|
+
const value = await promise;
|
|
5209
|
+
collectionsCache.set(cacheKey, { collections: value });
|
|
5210
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5211
|
+
setCollections(value);
|
|
5212
|
+
setError(null);
|
|
5213
|
+
}
|
|
5214
|
+
} catch (loadError) {
|
|
5215
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5216
|
+
setError(loadError);
|
|
5217
|
+
}
|
|
5218
|
+
} finally {
|
|
5219
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5220
|
+
setIsLoading(false);
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
},
|
|
5224
|
+
[cacheKey, client, enabled]
|
|
5225
|
+
);
|
|
5226
|
+
react.useEffect(() => {
|
|
5227
|
+
void load(false);
|
|
5228
|
+
}, [load]);
|
|
5229
|
+
const refetch = react.useCallback(async () => {
|
|
5230
|
+
collectionsCache.delete(cacheKey);
|
|
5231
|
+
await load(true);
|
|
5232
|
+
}, [cacheKey, load]);
|
|
5233
|
+
return { collections, isLoading, error, refetch };
|
|
5234
|
+
}
|
|
5235
|
+
var collectionCache = /* @__PURE__ */ new Map();
|
|
5236
|
+
var collectionInflight = /* @__PURE__ */ new Map();
|
|
5237
|
+
function isLikelySlug2(value) {
|
|
5238
|
+
return /^[a-z0-9-]+$/.test(value);
|
|
5239
|
+
}
|
|
5240
|
+
function buildCollectionCacheKey(client, locationId, idOrSlug) {
|
|
5241
|
+
return JSON.stringify({
|
|
5242
|
+
key: client.getPublicKey(),
|
|
5243
|
+
location_id: locationId || "__none__",
|
|
5244
|
+
collection: idOrSlug
|
|
5245
|
+
});
|
|
5246
|
+
}
|
|
5247
|
+
function useCollection(idOrSlug, options = {}) {
|
|
5248
|
+
const context = useOptionalCimplify();
|
|
5249
|
+
const client = options.client ?? context?.client;
|
|
5250
|
+
if (!client) {
|
|
5251
|
+
throw new Error("useCollection must be used within CimplifyProvider or passed { client }.");
|
|
5252
|
+
}
|
|
5253
|
+
const enabled = options.enabled ?? true;
|
|
5254
|
+
const locationId = client.getLocationId();
|
|
5255
|
+
const previousLocationIdRef = react.useRef(locationId);
|
|
5256
|
+
const requestIdRef = react.useRef(0);
|
|
5257
|
+
const normalizedIdOrSlug = react.useMemo(() => (idOrSlug || "").trim(), [idOrSlug]);
|
|
5258
|
+
const cacheKey = react.useMemo(
|
|
5259
|
+
() => buildCollectionCacheKey(client, locationId, normalizedIdOrSlug),
|
|
5260
|
+
[client, locationId, normalizedIdOrSlug]
|
|
5261
|
+
);
|
|
5262
|
+
const cached = collectionCache.get(cacheKey);
|
|
5263
|
+
const [collection, setCollection] = react.useState(cached?.collection ?? null);
|
|
5264
|
+
const [products, setProducts] = react.useState(cached?.products ?? []);
|
|
5265
|
+
const [isLoading, setIsLoading] = react.useState(
|
|
5266
|
+
enabled && normalizedIdOrSlug.length > 0 && !cached
|
|
5267
|
+
);
|
|
5268
|
+
const [error, setError] = react.useState(null);
|
|
5269
|
+
react.useEffect(() => {
|
|
5270
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5271
|
+
collectionCache.clear();
|
|
5272
|
+
collectionInflight.clear();
|
|
5273
|
+
previousLocationIdRef.current = locationId;
|
|
5274
|
+
}
|
|
5275
|
+
}, [locationId]);
|
|
5276
|
+
const load = react.useCallback(
|
|
5277
|
+
async (force = false) => {
|
|
5278
|
+
if (!enabled || normalizedIdOrSlug.length === 0) {
|
|
5279
|
+
setCollection(null);
|
|
5280
|
+
setProducts([]);
|
|
5281
|
+
setIsLoading(false);
|
|
5282
|
+
return;
|
|
5283
|
+
}
|
|
5284
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5285
|
+
setError(null);
|
|
5286
|
+
if (!force) {
|
|
5287
|
+
const cacheEntry = collectionCache.get(cacheKey);
|
|
5288
|
+
if (cacheEntry) {
|
|
5289
|
+
setCollection(cacheEntry.collection);
|
|
5290
|
+
setProducts(cacheEntry.products);
|
|
5291
|
+
setIsLoading(false);
|
|
5292
|
+
return;
|
|
5293
|
+
}
|
|
5294
|
+
}
|
|
5295
|
+
setIsLoading(true);
|
|
5296
|
+
try {
|
|
5297
|
+
const existing = collectionInflight.get(cacheKey);
|
|
5298
|
+
const promise = existing ?? (async () => {
|
|
5299
|
+
const collectionResult = isLikelySlug2(normalizedIdOrSlug) ? await client.catalogue.getCollectionBySlug(normalizedIdOrSlug) : await client.catalogue.getCollection(normalizedIdOrSlug);
|
|
5300
|
+
if (!collectionResult.ok) {
|
|
5301
|
+
throw collectionResult.error;
|
|
5302
|
+
}
|
|
5303
|
+
const productsResult = await client.catalogue.getCollectionProducts(
|
|
5304
|
+
collectionResult.value.id
|
|
5305
|
+
);
|
|
5306
|
+
if (!productsResult.ok) {
|
|
5307
|
+
throw productsResult.error;
|
|
5308
|
+
}
|
|
5309
|
+
return {
|
|
5310
|
+
collection: collectionResult.value,
|
|
5311
|
+
products: productsResult.value
|
|
5312
|
+
};
|
|
5313
|
+
})();
|
|
5314
|
+
if (!existing) {
|
|
5315
|
+
collectionInflight.set(cacheKey, promise);
|
|
5316
|
+
promise.finally(() => {
|
|
5317
|
+
collectionInflight.delete(cacheKey);
|
|
5318
|
+
}).catch(() => void 0);
|
|
5319
|
+
}
|
|
5320
|
+
const value = await promise;
|
|
5321
|
+
collectionCache.set(cacheKey, value);
|
|
5322
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5323
|
+
setCollection(value.collection);
|
|
5324
|
+
setProducts(value.products);
|
|
5325
|
+
setError(null);
|
|
5326
|
+
}
|
|
5327
|
+
} catch (loadError) {
|
|
5328
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5329
|
+
setError(loadError);
|
|
5330
|
+
}
|
|
5331
|
+
} finally {
|
|
5332
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5333
|
+
setIsLoading(false);
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
},
|
|
5337
|
+
[cacheKey, client, enabled, normalizedIdOrSlug]
|
|
5338
|
+
);
|
|
5339
|
+
react.useEffect(() => {
|
|
5340
|
+
void load(false);
|
|
5341
|
+
}, [load]);
|
|
5342
|
+
const refetch = react.useCallback(async () => {
|
|
5343
|
+
collectionCache.delete(cacheKey);
|
|
5344
|
+
await load(true);
|
|
5345
|
+
}, [cacheKey, load]);
|
|
5346
|
+
return { collection, products, isLoading, error, refetch };
|
|
5347
|
+
}
|
|
5348
|
+
var bundleCache = /* @__PURE__ */ new Map();
|
|
5349
|
+
var bundleInflight = /* @__PURE__ */ new Map();
|
|
5350
|
+
function isLikelySlug3(value) {
|
|
5351
|
+
return /^[a-z0-9-]+$/.test(value);
|
|
5352
|
+
}
|
|
5353
|
+
function buildBundleCacheKey(client, locationId, idOrSlug) {
|
|
5354
|
+
return JSON.stringify({
|
|
5355
|
+
key: client.getPublicKey(),
|
|
5356
|
+
location_id: locationId || "__none__",
|
|
5357
|
+
bundle: idOrSlug
|
|
5358
|
+
});
|
|
5359
|
+
}
|
|
5360
|
+
function useBundle(idOrSlug, options = {}) {
|
|
5361
|
+
const context = useOptionalCimplify();
|
|
5362
|
+
const client = options.client ?? context?.client;
|
|
5363
|
+
if (!client) {
|
|
5364
|
+
throw new Error("useBundle must be used within CimplifyProvider or passed { client }.");
|
|
5365
|
+
}
|
|
5366
|
+
const enabled = options.enabled ?? true;
|
|
5367
|
+
const locationId = client.getLocationId();
|
|
5368
|
+
const previousLocationIdRef = react.useRef(locationId);
|
|
5369
|
+
const requestIdRef = react.useRef(0);
|
|
5370
|
+
const normalizedIdOrSlug = react.useMemo(() => (idOrSlug || "").trim(), [idOrSlug]);
|
|
5371
|
+
const cacheKey = react.useMemo(
|
|
5372
|
+
() => buildBundleCacheKey(client, locationId, normalizedIdOrSlug),
|
|
5373
|
+
[client, locationId, normalizedIdOrSlug]
|
|
5374
|
+
);
|
|
5375
|
+
const cached = bundleCache.get(cacheKey);
|
|
5376
|
+
const [bundle, setBundle] = react.useState(cached?.bundle ?? null);
|
|
5377
|
+
const [isLoading, setIsLoading] = react.useState(
|
|
5378
|
+
enabled && normalizedIdOrSlug.length > 0 && !cached
|
|
5379
|
+
);
|
|
5380
|
+
const [error, setError] = react.useState(null);
|
|
5381
|
+
react.useEffect(() => {
|
|
5382
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5383
|
+
bundleCache.clear();
|
|
5384
|
+
bundleInflight.clear();
|
|
5385
|
+
previousLocationIdRef.current = locationId;
|
|
5386
|
+
}
|
|
5387
|
+
}, [locationId]);
|
|
5388
|
+
const load = react.useCallback(
|
|
5389
|
+
async (force = false) => {
|
|
5390
|
+
if (!enabled || normalizedIdOrSlug.length === 0) {
|
|
5391
|
+
setBundle(null);
|
|
5392
|
+
setIsLoading(false);
|
|
5393
|
+
return;
|
|
5394
|
+
}
|
|
5395
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5396
|
+
setError(null);
|
|
5397
|
+
if (!force) {
|
|
5398
|
+
const cacheEntry = bundleCache.get(cacheKey);
|
|
5399
|
+
if (cacheEntry) {
|
|
5400
|
+
setBundle(cacheEntry.bundle);
|
|
5401
|
+
setIsLoading(false);
|
|
5402
|
+
return;
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
setIsLoading(true);
|
|
5406
|
+
try {
|
|
5407
|
+
const existing = bundleInflight.get(cacheKey);
|
|
5408
|
+
const promise = existing ?? (async () => {
|
|
5409
|
+
const result = isLikelySlug3(normalizedIdOrSlug) ? await client.catalogue.getBundleBySlug(normalizedIdOrSlug) : await client.catalogue.getBundle(normalizedIdOrSlug);
|
|
5410
|
+
if (!result.ok) {
|
|
5411
|
+
throw result.error;
|
|
5412
|
+
}
|
|
5413
|
+
return result.value;
|
|
5414
|
+
})();
|
|
5415
|
+
if (!existing) {
|
|
5416
|
+
bundleInflight.set(cacheKey, promise);
|
|
5417
|
+
promise.finally(() => {
|
|
5418
|
+
bundleInflight.delete(cacheKey);
|
|
5419
|
+
}).catch(() => void 0);
|
|
5420
|
+
}
|
|
5421
|
+
const value = await promise;
|
|
5422
|
+
bundleCache.set(cacheKey, { bundle: value });
|
|
5423
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5424
|
+
setBundle(value);
|
|
5425
|
+
setError(null);
|
|
5426
|
+
}
|
|
5427
|
+
} catch (loadError) {
|
|
5428
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5429
|
+
setError(loadError);
|
|
5430
|
+
}
|
|
5431
|
+
} finally {
|
|
5432
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5433
|
+
setIsLoading(false);
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5436
|
+
},
|
|
5437
|
+
[cacheKey, client, enabled, normalizedIdOrSlug]
|
|
5438
|
+
);
|
|
5439
|
+
react.useEffect(() => {
|
|
5440
|
+
void load(false);
|
|
5441
|
+
}, [load]);
|
|
5442
|
+
const refetch = react.useCallback(async () => {
|
|
5443
|
+
bundleCache.delete(cacheKey);
|
|
5444
|
+
await load(true);
|
|
5445
|
+
}, [cacheKey, load]);
|
|
5446
|
+
return { bundle, isLoading, error, refetch };
|
|
5447
|
+
}
|
|
5448
|
+
var compositeCache = /* @__PURE__ */ new Map();
|
|
5449
|
+
var compositeInflight = /* @__PURE__ */ new Map();
|
|
5450
|
+
function shouldFetchByProductId(idOrProductId, byProductId) {
|
|
5451
|
+
if (typeof byProductId === "boolean") {
|
|
5452
|
+
return byProductId;
|
|
5453
|
+
}
|
|
5454
|
+
return idOrProductId.startsWith("prod_");
|
|
5455
|
+
}
|
|
5456
|
+
function buildCompositeCacheKey(client, locationId, idOrProductId, byProductId) {
|
|
5457
|
+
return JSON.stringify({
|
|
5458
|
+
key: client.getPublicKey(),
|
|
5459
|
+
location_id: locationId || "__none__",
|
|
5460
|
+
composite: idOrProductId,
|
|
5461
|
+
by_product_id: byProductId
|
|
5462
|
+
});
|
|
5463
|
+
}
|
|
5464
|
+
function useComposite(idOrProductId, options = {}) {
|
|
5465
|
+
const context = useOptionalCimplify();
|
|
5466
|
+
const client = options.client ?? context?.client;
|
|
5467
|
+
if (!client) {
|
|
5468
|
+
throw new Error("useComposite must be used within CimplifyProvider or passed { client }.");
|
|
5469
|
+
}
|
|
5470
|
+
const enabled = options.enabled ?? true;
|
|
5471
|
+
const locationId = client.getLocationId();
|
|
5472
|
+
const previousLocationIdRef = react.useRef(locationId);
|
|
5473
|
+
const requestIdRef = react.useRef(0);
|
|
5474
|
+
const priceRequestIdRef = react.useRef(0);
|
|
5475
|
+
const normalizedIdOrProductId = react.useMemo(
|
|
5476
|
+
() => (idOrProductId || "").trim(),
|
|
5477
|
+
[idOrProductId]
|
|
5478
|
+
);
|
|
5479
|
+
const byProductId = react.useMemo(
|
|
5480
|
+
() => shouldFetchByProductId(normalizedIdOrProductId, options.byProductId),
|
|
5481
|
+
[normalizedIdOrProductId, options.byProductId]
|
|
5482
|
+
);
|
|
5483
|
+
const cacheKey = react.useMemo(
|
|
5484
|
+
() => buildCompositeCacheKey(client, locationId, normalizedIdOrProductId, byProductId),
|
|
5485
|
+
[byProductId, client, locationId, normalizedIdOrProductId]
|
|
5486
|
+
);
|
|
5487
|
+
const cached = compositeCache.get(cacheKey);
|
|
5488
|
+
const [composite, setComposite] = react.useState(cached?.composite ?? null);
|
|
5489
|
+
const [isLoading, setIsLoading] = react.useState(
|
|
5490
|
+
enabled && normalizedIdOrProductId.length > 0 && !cached
|
|
5491
|
+
);
|
|
5492
|
+
const [error, setError] = react.useState(null);
|
|
5493
|
+
const [priceResult, setPriceResult] = react.useState(null);
|
|
5494
|
+
const [isPriceLoading, setIsPriceLoading] = react.useState(false);
|
|
5495
|
+
react.useEffect(() => {
|
|
5496
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5497
|
+
compositeCache.clear();
|
|
5498
|
+
compositeInflight.clear();
|
|
5499
|
+
previousLocationIdRef.current = locationId;
|
|
5500
|
+
}
|
|
5501
|
+
}, [locationId]);
|
|
5502
|
+
const load = react.useCallback(
|
|
5503
|
+
async (force = false) => {
|
|
5504
|
+
if (!enabled || normalizedIdOrProductId.length === 0) {
|
|
5505
|
+
setComposite(null);
|
|
5506
|
+
setPriceResult(null);
|
|
5507
|
+
setIsLoading(false);
|
|
5508
|
+
return;
|
|
5509
|
+
}
|
|
5510
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5511
|
+
setError(null);
|
|
5512
|
+
if (!force) {
|
|
5513
|
+
const cacheEntry = compositeCache.get(cacheKey);
|
|
5514
|
+
if (cacheEntry) {
|
|
5515
|
+
setComposite(cacheEntry.composite);
|
|
5516
|
+
setIsLoading(false);
|
|
5517
|
+
return;
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
setIsLoading(true);
|
|
5521
|
+
try {
|
|
5522
|
+
const existing = compositeInflight.get(cacheKey);
|
|
5523
|
+
const promise = existing ?? (async () => {
|
|
5524
|
+
const result = byProductId ? await client.catalogue.getCompositeByProductId(normalizedIdOrProductId) : await client.catalogue.getComposite(normalizedIdOrProductId);
|
|
5525
|
+
if (!result.ok) {
|
|
5526
|
+
throw result.error;
|
|
5527
|
+
}
|
|
5528
|
+
return result.value;
|
|
5529
|
+
})();
|
|
5530
|
+
if (!existing) {
|
|
5531
|
+
compositeInflight.set(cacheKey, promise);
|
|
5532
|
+
promise.finally(() => {
|
|
5533
|
+
compositeInflight.delete(cacheKey);
|
|
5534
|
+
}).catch(() => void 0);
|
|
5535
|
+
}
|
|
5536
|
+
const value = await promise;
|
|
5537
|
+
compositeCache.set(cacheKey, { composite: value });
|
|
5538
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5539
|
+
setComposite(value);
|
|
5540
|
+
setPriceResult(null);
|
|
5541
|
+
setError(null);
|
|
5542
|
+
}
|
|
5543
|
+
} catch (loadError) {
|
|
5544
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5545
|
+
setError(loadError);
|
|
5546
|
+
}
|
|
5547
|
+
} finally {
|
|
5548
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5549
|
+
setIsLoading(false);
|
|
5550
|
+
}
|
|
5551
|
+
}
|
|
5552
|
+
},
|
|
5553
|
+
[byProductId, cacheKey, client, enabled, normalizedIdOrProductId]
|
|
5554
|
+
);
|
|
5555
|
+
react.useEffect(() => {
|
|
5556
|
+
void load(false);
|
|
5557
|
+
}, [load]);
|
|
5558
|
+
const calculatePrice = react.useCallback(
|
|
5559
|
+
async (selections, overrideLocationId) => {
|
|
5560
|
+
if (!composite) {
|
|
5561
|
+
return null;
|
|
5562
|
+
}
|
|
5563
|
+
const nextRequestId = ++priceRequestIdRef.current;
|
|
5564
|
+
setIsPriceLoading(true);
|
|
5565
|
+
try {
|
|
5566
|
+
const result = await client.catalogue.calculateCompositePrice(
|
|
5567
|
+
composite.id,
|
|
5568
|
+
selections,
|
|
5569
|
+
overrideLocationId
|
|
5570
|
+
);
|
|
5571
|
+
if (!result.ok) {
|
|
5572
|
+
throw result.error;
|
|
5573
|
+
}
|
|
5574
|
+
if (nextRequestId === priceRequestIdRef.current) {
|
|
5575
|
+
setPriceResult(result.value);
|
|
5576
|
+
setError(null);
|
|
5577
|
+
}
|
|
5578
|
+
return result.value;
|
|
5579
|
+
} catch (loadError) {
|
|
5580
|
+
if (nextRequestId === priceRequestIdRef.current) {
|
|
5581
|
+
setError(loadError);
|
|
5582
|
+
}
|
|
5583
|
+
return null;
|
|
5584
|
+
} finally {
|
|
5585
|
+
if (nextRequestId === priceRequestIdRef.current) {
|
|
5586
|
+
setIsPriceLoading(false);
|
|
5587
|
+
}
|
|
5588
|
+
}
|
|
5589
|
+
},
|
|
5590
|
+
[client, composite]
|
|
5591
|
+
);
|
|
5592
|
+
const refetch = react.useCallback(async () => {
|
|
5593
|
+
compositeCache.delete(cacheKey);
|
|
5594
|
+
await load(true);
|
|
5595
|
+
}, [cacheKey, load]);
|
|
5596
|
+
return { composite, isLoading, error, refetch, calculatePrice, priceResult, isPriceLoading };
|
|
5597
|
+
}
|
|
5598
|
+
function useSearch(options = {}) {
|
|
5599
|
+
const context = useOptionalCimplify();
|
|
5600
|
+
const client = options.client ?? context?.client;
|
|
5601
|
+
if (!client) {
|
|
5602
|
+
throw new Error("useSearch must be used within CimplifyProvider or passed { client }.");
|
|
5603
|
+
}
|
|
5604
|
+
const minLength = Math.max(0, options.minLength ?? 2);
|
|
5605
|
+
const debounceMs = Math.max(0, options.debounceMs ?? 300);
|
|
5606
|
+
const limit = Math.max(1, options.limit ?? 20);
|
|
5607
|
+
const [query, setQueryState] = react.useState("");
|
|
5608
|
+
const [results, setResults] = react.useState([]);
|
|
5609
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
5610
|
+
const [error, setError] = react.useState(null);
|
|
5611
|
+
const requestIdRef = react.useRef(0);
|
|
5612
|
+
const timerRef = react.useRef(null);
|
|
5613
|
+
react.useEffect(() => {
|
|
5614
|
+
if (timerRef.current) {
|
|
5615
|
+
clearTimeout(timerRef.current);
|
|
5616
|
+
timerRef.current = null;
|
|
5617
|
+
}
|
|
5618
|
+
const trimmedQuery = query.trim();
|
|
5619
|
+
if (trimmedQuery.length < minLength) {
|
|
5620
|
+
setResults([]);
|
|
5621
|
+
setError(null);
|
|
5622
|
+
setIsLoading(false);
|
|
5623
|
+
return;
|
|
5624
|
+
}
|
|
5625
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5626
|
+
setError(null);
|
|
5627
|
+
setIsLoading(true);
|
|
5628
|
+
timerRef.current = setTimeout(() => {
|
|
5629
|
+
void (async () => {
|
|
5630
|
+
try {
|
|
5631
|
+
const result = await client.catalogue.searchProducts(trimmedQuery, {
|
|
5632
|
+
limit,
|
|
5633
|
+
category: options.category
|
|
5634
|
+
});
|
|
5635
|
+
if (!result.ok) {
|
|
5636
|
+
throw result.error;
|
|
5637
|
+
}
|
|
5638
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5639
|
+
setResults(result.value);
|
|
5640
|
+
setError(null);
|
|
5641
|
+
}
|
|
5642
|
+
} catch (loadError) {
|
|
5643
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5644
|
+
setError(loadError);
|
|
5645
|
+
}
|
|
5646
|
+
} finally {
|
|
5647
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5648
|
+
setIsLoading(false);
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
})();
|
|
5652
|
+
}, debounceMs);
|
|
5653
|
+
return () => {
|
|
5654
|
+
if (timerRef.current) {
|
|
5655
|
+
clearTimeout(timerRef.current);
|
|
5656
|
+
timerRef.current = null;
|
|
5657
|
+
}
|
|
5658
|
+
};
|
|
5659
|
+
}, [client, debounceMs, limit, minLength, options.category, query]);
|
|
5660
|
+
const setQuery = react.useCallback((nextQuery) => {
|
|
5661
|
+
setQueryState(nextQuery);
|
|
5662
|
+
}, []);
|
|
5663
|
+
const clear = react.useCallback(() => {
|
|
5664
|
+
requestIdRef.current += 1;
|
|
5665
|
+
if (timerRef.current) {
|
|
5666
|
+
clearTimeout(timerRef.current);
|
|
5667
|
+
timerRef.current = null;
|
|
5668
|
+
}
|
|
5669
|
+
setQueryState("");
|
|
5670
|
+
setResults([]);
|
|
5671
|
+
setError(null);
|
|
5672
|
+
setIsLoading(false);
|
|
5673
|
+
}, []);
|
|
5674
|
+
return { results, isLoading, error, query, setQuery, clear };
|
|
5675
|
+
}
|
|
5676
|
+
var quoteCache = /* @__PURE__ */ new Map();
|
|
5677
|
+
var quoteInflight = /* @__PURE__ */ new Map();
|
|
5678
|
+
function buildQuoteCacheKey(client, locationId, inputSignature) {
|
|
5679
|
+
return JSON.stringify({
|
|
5680
|
+
key: client.getPublicKey(),
|
|
5681
|
+
location_id: locationId || "__none__",
|
|
5682
|
+
input: inputSignature
|
|
5683
|
+
});
|
|
5684
|
+
}
|
|
5685
|
+
function isQuoteExpired(quote) {
|
|
5686
|
+
if (!quote?.expires_at) {
|
|
5687
|
+
return false;
|
|
5688
|
+
}
|
|
5689
|
+
const expiresAt = Date.parse(quote.expires_at);
|
|
5690
|
+
if (!Number.isFinite(expiresAt)) {
|
|
5691
|
+
return false;
|
|
5692
|
+
}
|
|
5693
|
+
return expiresAt <= Date.now();
|
|
5694
|
+
}
|
|
5695
|
+
function normalizeInput(input, fallbackLocationId) {
|
|
5696
|
+
const productId = input.productId.trim();
|
|
5697
|
+
const variantId = input.variantId?.trim();
|
|
5698
|
+
const locationId = input.locationId?.trim() || fallbackLocationId || void 0;
|
|
5699
|
+
return {
|
|
5700
|
+
product_id: productId,
|
|
5701
|
+
variant_id: variantId && variantId.length > 0 ? variantId : void 0,
|
|
5702
|
+
location_id: locationId && locationId.length > 0 ? locationId : void 0,
|
|
5703
|
+
quantity: input.quantity,
|
|
5704
|
+
add_on_option_ids: input.addOnOptionIds,
|
|
5705
|
+
bundle_selections: input.bundleSelections,
|
|
5706
|
+
composite_selections: input.compositeSelections
|
|
5707
|
+
};
|
|
5708
|
+
}
|
|
5709
|
+
function useQuote(input, options = {}) {
|
|
5710
|
+
const context = useOptionalCimplify();
|
|
5711
|
+
const client = options.client ?? context?.client;
|
|
5712
|
+
if (!client) {
|
|
5713
|
+
throw new Error("useQuote must be used within CimplifyProvider or passed { client }.");
|
|
5714
|
+
}
|
|
5715
|
+
const enabled = options.enabled ?? true;
|
|
5716
|
+
const autoRefresh = options.autoRefresh ?? true;
|
|
5717
|
+
const refreshBeforeExpiryMs = Math.max(0, options.refreshBeforeExpiryMs ?? 3e4);
|
|
5718
|
+
const locationId = client.getLocationId();
|
|
5719
|
+
const requestIdRef = react.useRef(0);
|
|
5720
|
+
const refreshTimerRef = react.useRef(null);
|
|
5721
|
+
const expiryTimerRef = react.useRef(null);
|
|
5722
|
+
const inputSignature = react.useMemo(() => JSON.stringify(input ?? null), [input]);
|
|
5723
|
+
const normalizedInput = react.useMemo(() => {
|
|
5724
|
+
if (!input) {
|
|
5725
|
+
return null;
|
|
5726
|
+
}
|
|
5727
|
+
const normalized = normalizeInput(input, locationId);
|
|
5728
|
+
return normalized.product_id.length > 0 ? normalized : null;
|
|
5729
|
+
}, [inputSignature, locationId]);
|
|
5730
|
+
const cacheKey = react.useMemo(
|
|
5731
|
+
() => buildQuoteCacheKey(client, locationId, inputSignature),
|
|
5732
|
+
[client, inputSignature, locationId]
|
|
5733
|
+
);
|
|
5734
|
+
const cached = quoteCache.get(cacheKey);
|
|
5735
|
+
const [quote, setQuote] = react.useState(cached?.quote ?? null);
|
|
5736
|
+
const [isLoading, setIsLoading] = react.useState(enabled && normalizedInput !== null && !cached);
|
|
5737
|
+
const [error, setError] = react.useState(null);
|
|
5738
|
+
const [isExpired, setIsExpired] = react.useState(isQuoteExpired(cached?.quote ?? null));
|
|
5739
|
+
const [messages, setMessages] = react.useState(cached?.quote?.ui_messages ?? []);
|
|
5740
|
+
const load = react.useCallback(
|
|
5741
|
+
async (force = false) => {
|
|
5742
|
+
if (!enabled || !normalizedInput) {
|
|
5743
|
+
setQuote(null);
|
|
5744
|
+
setMessages([]);
|
|
5745
|
+
setIsExpired(false);
|
|
5746
|
+
setError(null);
|
|
5747
|
+
setIsLoading(false);
|
|
5748
|
+
return;
|
|
5749
|
+
}
|
|
5750
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5751
|
+
setError(null);
|
|
5752
|
+
if (!force) {
|
|
5753
|
+
const cacheEntry = quoteCache.get(cacheKey);
|
|
5754
|
+
if (cacheEntry) {
|
|
5755
|
+
setQuote(cacheEntry.quote);
|
|
5756
|
+
setMessages(cacheEntry.quote?.ui_messages ?? []);
|
|
5757
|
+
setIsExpired(isQuoteExpired(cacheEntry.quote));
|
|
5758
|
+
setIsLoading(false);
|
|
5759
|
+
return;
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
setIsLoading(true);
|
|
5763
|
+
try {
|
|
5764
|
+
const existing = quoteInflight.get(cacheKey);
|
|
5765
|
+
const promise = existing ?? (async () => {
|
|
5766
|
+
const result = await client.catalogue.fetchQuote(normalizedInput);
|
|
5767
|
+
if (!result.ok) {
|
|
5768
|
+
throw result.error;
|
|
5769
|
+
}
|
|
5770
|
+
return result.value;
|
|
5771
|
+
})();
|
|
5772
|
+
if (!existing) {
|
|
5773
|
+
quoteInflight.set(cacheKey, promise);
|
|
5774
|
+
promise.finally(() => {
|
|
5775
|
+
quoteInflight.delete(cacheKey);
|
|
5776
|
+
}).catch(() => void 0);
|
|
5777
|
+
}
|
|
5778
|
+
const value = await promise;
|
|
5779
|
+
quoteCache.set(cacheKey, { quote: value });
|
|
5780
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5781
|
+
setQuote(value);
|
|
5782
|
+
setMessages(value.ui_messages ?? []);
|
|
5783
|
+
setIsExpired(isQuoteExpired(value));
|
|
5784
|
+
setError(null);
|
|
5785
|
+
}
|
|
5786
|
+
} catch (loadError) {
|
|
5787
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5788
|
+
setError(loadError);
|
|
5789
|
+
}
|
|
5790
|
+
} finally {
|
|
5791
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5792
|
+
setIsLoading(false);
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
},
|
|
5796
|
+
[cacheKey, client, enabled, normalizedInput]
|
|
5797
|
+
);
|
|
5798
|
+
react.useEffect(() => {
|
|
5799
|
+
void load(false);
|
|
5800
|
+
}, [load]);
|
|
5801
|
+
const refresh = react.useCallback(async () => {
|
|
5802
|
+
if (!enabled || !normalizedInput) {
|
|
5803
|
+
return;
|
|
5804
|
+
}
|
|
5805
|
+
if (!quote?.quote_id) {
|
|
5806
|
+
await load(true);
|
|
5807
|
+
return;
|
|
5808
|
+
}
|
|
5809
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5810
|
+
setError(null);
|
|
5811
|
+
setIsLoading(true);
|
|
5812
|
+
try {
|
|
5813
|
+
const result = await client.catalogue.refreshQuote({
|
|
5814
|
+
quote_id: quote.quote_id,
|
|
5815
|
+
...normalizedInput
|
|
5816
|
+
});
|
|
5817
|
+
if (!result.ok) {
|
|
5818
|
+
throw result.error;
|
|
5819
|
+
}
|
|
5820
|
+
const refreshed = result.value.quote;
|
|
5821
|
+
quoteCache.set(cacheKey, { quote: refreshed });
|
|
5822
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5823
|
+
setQuote(refreshed);
|
|
5824
|
+
setMessages(refreshed.ui_messages ?? []);
|
|
5825
|
+
setIsExpired(isQuoteExpired(refreshed));
|
|
5826
|
+
setError(null);
|
|
5827
|
+
}
|
|
5828
|
+
} catch (refreshError) {
|
|
5829
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5830
|
+
setError(refreshError);
|
|
5831
|
+
}
|
|
5832
|
+
} finally {
|
|
5833
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5834
|
+
setIsLoading(false);
|
|
5835
|
+
}
|
|
5836
|
+
}
|
|
5837
|
+
}, [cacheKey, client, enabled, load, normalizedInput, quote]);
|
|
5838
|
+
react.useEffect(() => {
|
|
5839
|
+
if (expiryTimerRef.current) {
|
|
5840
|
+
clearTimeout(expiryTimerRef.current);
|
|
5841
|
+
expiryTimerRef.current = null;
|
|
5842
|
+
}
|
|
5843
|
+
const expiresAt = quote?.expires_at ? Date.parse(quote.expires_at) : NaN;
|
|
5844
|
+
if (!Number.isFinite(expiresAt)) {
|
|
5845
|
+
setIsExpired(false);
|
|
5846
|
+
return;
|
|
5847
|
+
}
|
|
5848
|
+
const expired = expiresAt <= Date.now();
|
|
5849
|
+
setIsExpired(expired);
|
|
5850
|
+
if (!expired) {
|
|
5851
|
+
expiryTimerRef.current = setTimeout(() => {
|
|
5852
|
+
setIsExpired(true);
|
|
5853
|
+
}, Math.max(0, expiresAt - Date.now()));
|
|
5854
|
+
}
|
|
5855
|
+
return () => {
|
|
5856
|
+
if (expiryTimerRef.current) {
|
|
5857
|
+
clearTimeout(expiryTimerRef.current);
|
|
5858
|
+
expiryTimerRef.current = null;
|
|
5859
|
+
}
|
|
5860
|
+
};
|
|
5861
|
+
}, [quote?.expires_at, quote?.quote_id]);
|
|
5862
|
+
react.useEffect(() => {
|
|
5863
|
+
if (refreshTimerRef.current) {
|
|
5864
|
+
clearTimeout(refreshTimerRef.current);
|
|
5865
|
+
refreshTimerRef.current = null;
|
|
5866
|
+
}
|
|
5867
|
+
if (!autoRefresh || !enabled || !quote?.expires_at) {
|
|
5868
|
+
return;
|
|
5869
|
+
}
|
|
5870
|
+
const expiresAt = Date.parse(quote.expires_at);
|
|
5871
|
+
if (!Number.isFinite(expiresAt)) {
|
|
5872
|
+
return;
|
|
5873
|
+
}
|
|
5874
|
+
const delay = Math.max(0, expiresAt - Date.now() - refreshBeforeExpiryMs);
|
|
5875
|
+
refreshTimerRef.current = setTimeout(() => {
|
|
5876
|
+
void refresh();
|
|
5877
|
+
}, delay);
|
|
5878
|
+
return () => {
|
|
5879
|
+
if (refreshTimerRef.current) {
|
|
5880
|
+
clearTimeout(refreshTimerRef.current);
|
|
5881
|
+
refreshTimerRef.current = null;
|
|
5882
|
+
}
|
|
5883
|
+
};
|
|
5884
|
+
}, [autoRefresh, enabled, quote?.expires_at, quote?.quote_id, refresh, refreshBeforeExpiryMs]);
|
|
5885
|
+
return { quote, isLoading, error, refresh, isExpired, messages };
|
|
5886
|
+
}
|
|
5088
5887
|
var ElementsContext = react.createContext({
|
|
5089
5888
|
elements: null,
|
|
5090
5889
|
isReady: false
|
|
@@ -5254,10 +6053,14 @@ exports.CimplifyProvider = CimplifyProvider;
|
|
|
5254
6053
|
exports.ElementsProvider = ElementsProvider;
|
|
5255
6054
|
exports.PaymentElement = PaymentElement;
|
|
5256
6055
|
exports.useAds = useAds;
|
|
6056
|
+
exports.useBundle = useBundle;
|
|
5257
6057
|
exports.useCart = useCart;
|
|
5258
6058
|
exports.useCategories = useCategories;
|
|
5259
6059
|
exports.useCheckout = useCheckout;
|
|
5260
6060
|
exports.useCimplify = useCimplify;
|
|
6061
|
+
exports.useCollection = useCollection;
|
|
6062
|
+
exports.useCollections = useCollections;
|
|
6063
|
+
exports.useComposite = useComposite;
|
|
5261
6064
|
exports.useElements = useElements;
|
|
5262
6065
|
exports.useElementsReady = useElementsReady;
|
|
5263
6066
|
exports.useLocations = useLocations;
|
|
@@ -5265,3 +6068,5 @@ exports.useOptionalCimplify = useOptionalCimplify;
|
|
|
5265
6068
|
exports.useOrder = useOrder;
|
|
5266
6069
|
exports.useProduct = useProduct;
|
|
5267
6070
|
exports.useProducts = useProducts;
|
|
6071
|
+
exports.useQuote = useQuote;
|
|
6072
|
+
exports.useSearch = useSearch;
|