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