@cimplify/sdk 0.6.7 → 0.6.9
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 +127 -98
- package/dist/advanced.mjs +127 -98
- package/dist/{client-B4etj3AD.d.mts → client-COpV6Yuu.d.ts} +39 -34
- package/dist/{client-CYVVuP5J.d.ts → client-vmXPt1j0.d.mts} +39 -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 +165 -99
- package/dist/index.mjs +162 -100
- 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 +845 -39
- package/dist/react.mjs +840 -40
- 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})`;
|
|
@@ -1253,9 +1281,6 @@ var CartOperations = class {
|
|
|
1253
1281
|
|
|
1254
1282
|
// src/constants.ts
|
|
1255
1283
|
var LINK_QUERY = {
|
|
1256
|
-
DATA: "link.data",
|
|
1257
|
-
ADDRESSES: "link.addresses",
|
|
1258
|
-
MOBILE_MONEY: "link.mobile_money",
|
|
1259
1284
|
PREFERENCES: "link.preferences"};
|
|
1260
1285
|
var LINK_MUTATION = {
|
|
1261
1286
|
CHECK_STATUS: "link.check_status",
|
|
@@ -1264,12 +1289,8 @@ var LINK_MUTATION = {
|
|
|
1264
1289
|
UPDATE_PREFERENCES: "link.update_preferences",
|
|
1265
1290
|
CREATE_ADDRESS: "link.create_address",
|
|
1266
1291
|
UPDATE_ADDRESS: "link.update_address",
|
|
1267
|
-
DELETE_ADDRESS: "link.delete_address",
|
|
1268
|
-
SET_DEFAULT_ADDRESS: "link.set_default_address",
|
|
1269
1292
|
TRACK_ADDRESS_USAGE: "link.track_address_usage",
|
|
1270
1293
|
CREATE_MOBILE_MONEY: "link.create_mobile_money",
|
|
1271
|
-
DELETE_MOBILE_MONEY: "link.delete_mobile_money",
|
|
1272
|
-
SET_DEFAULT_MOBILE_MONEY: "link.set_default_mobile_money",
|
|
1273
1294
|
TRACK_MOBILE_MONEY_USAGE: "link.track_mobile_money_usage",
|
|
1274
1295
|
VERIFY_MOBILE_MONEY: "link.verify_mobile_money"};
|
|
1275
1296
|
var AUTH_MUTATION = {
|
|
@@ -1400,6 +1421,8 @@ function normalizeStatusResponse(response) {
|
|
|
1400
1421
|
}
|
|
1401
1422
|
const res = response;
|
|
1402
1423
|
const normalizedStatus = normalizePaymentStatusValue(res.status ?? void 0);
|
|
1424
|
+
const normalizedAmount = typeof res.amount === "string" ? money(res.amount) : typeof res.amount === "number" && Number.isFinite(res.amount) ? moneyFromNumber(res.amount) : void 0;
|
|
1425
|
+
const normalizedCurrency = typeof res.currency === "string" && res.currency.trim().length > 0 ? currencyCode(res.currency) : void 0;
|
|
1403
1426
|
const paidValue = res.paid === true;
|
|
1404
1427
|
const derivedPaid = paidValue || [
|
|
1405
1428
|
"success",
|
|
@@ -1412,8 +1435,8 @@ function normalizeStatusResponse(response) {
|
|
|
1412
1435
|
return {
|
|
1413
1436
|
status: normalizedStatus,
|
|
1414
1437
|
paid: derivedPaid,
|
|
1415
|
-
amount:
|
|
1416
|
-
currency:
|
|
1438
|
+
amount: normalizedAmount,
|
|
1439
|
+
currency: normalizedCurrency,
|
|
1417
1440
|
reference: res.reference,
|
|
1418
1441
|
message: res.message || ""
|
|
1419
1442
|
};
|
|
@@ -2105,14 +2128,17 @@ var CheckoutService = class {
|
|
|
2105
2128
|
pay_currency: data.pay_currency,
|
|
2106
2129
|
fx_quote_id: data.fx_quote_id
|
|
2107
2130
|
};
|
|
2108
|
-
const baseCurrency = (
|
|
2131
|
+
const baseCurrency = currencyCode(
|
|
2132
|
+
(cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase()
|
|
2133
|
+
);
|
|
2109
2134
|
const payCurrency = data.pay_currency?.trim().toUpperCase();
|
|
2135
|
+
const payCurrencyCode = payCurrency ? currencyCode(payCurrency) : void 0;
|
|
2110
2136
|
const cartTotalAmount = Number.parseFloat(cart.pricing.total_price || "0");
|
|
2111
|
-
if (
|
|
2137
|
+
if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
|
|
2112
2138
|
const fxQuoteResult = await this.client.fx.lockQuote({
|
|
2113
2139
|
from: baseCurrency,
|
|
2114
|
-
to:
|
|
2115
|
-
amount:
|
|
2140
|
+
to: payCurrencyCode,
|
|
2141
|
+
amount: cart.pricing.total_price
|
|
2116
2142
|
});
|
|
2117
2143
|
if (!fxQuoteResult.ok) {
|
|
2118
2144
|
return ok(
|
|
@@ -2123,7 +2149,7 @@ var CheckoutService = class {
|
|
|
2123
2149
|
)
|
|
2124
2150
|
);
|
|
2125
2151
|
}
|
|
2126
|
-
checkoutData.pay_currency =
|
|
2152
|
+
checkoutData.pay_currency = payCurrencyCode;
|
|
2127
2153
|
checkoutData.fx_quote_id = fxQuoteResult.value.id;
|
|
2128
2154
|
}
|
|
2129
2155
|
data.on_status_change?.("processing", {});
|
|
@@ -2228,6 +2254,9 @@ var LinkService = class {
|
|
|
2228
2254
|
constructor(client) {
|
|
2229
2255
|
this.client = client;
|
|
2230
2256
|
}
|
|
2257
|
+
async getProfile() {
|
|
2258
|
+
return safe5(this.client.linkGet("/v1/link/profile"));
|
|
2259
|
+
}
|
|
2231
2260
|
async requestOtp(input) {
|
|
2232
2261
|
return safe5(this.client.linkPost("/v1/link/auth/request-otp", input));
|
|
2233
2262
|
}
|
|
@@ -2255,13 +2284,13 @@ var LinkService = class {
|
|
|
2255
2284
|
);
|
|
2256
2285
|
}
|
|
2257
2286
|
async getLinkData() {
|
|
2258
|
-
return safe5(this.client.
|
|
2287
|
+
return safe5(this.client.linkGet("/v1/link/data"));
|
|
2259
2288
|
}
|
|
2260
2289
|
async getAddresses() {
|
|
2261
|
-
return safe5(this.client.
|
|
2290
|
+
return safe5(this.client.linkGet("/v1/link/addresses"));
|
|
2262
2291
|
}
|
|
2263
2292
|
async getMobileMoney() {
|
|
2264
|
-
return safe5(this.client.
|
|
2293
|
+
return safe5(this.client.linkGet("/v1/link/mobile-money"));
|
|
2265
2294
|
}
|
|
2266
2295
|
async getPreferences() {
|
|
2267
2296
|
return safe5(this.client.query(LINK_QUERY.PREFERENCES));
|
|
@@ -2284,10 +2313,10 @@ var LinkService = class {
|
|
|
2284
2313
|
return safe5(this.client.call(LINK_MUTATION.UPDATE_ADDRESS, input));
|
|
2285
2314
|
}
|
|
2286
2315
|
async deleteAddress(addressId) {
|
|
2287
|
-
return safe5(this.client.
|
|
2316
|
+
return safe5(this.client.linkDelete(`/v1/link/addresses/${addressId}`));
|
|
2288
2317
|
}
|
|
2289
2318
|
async setDefaultAddress(addressId) {
|
|
2290
|
-
return safe5(this.client.
|
|
2319
|
+
return safe5(this.client.linkPost(`/v1/link/addresses/${addressId}/default`));
|
|
2291
2320
|
}
|
|
2292
2321
|
async trackAddressUsage(addressId) {
|
|
2293
2322
|
return safe5(
|
|
@@ -2300,11 +2329,13 @@ var LinkService = class {
|
|
|
2300
2329
|
return safe5(this.client.call(LINK_MUTATION.CREATE_MOBILE_MONEY, input));
|
|
2301
2330
|
}
|
|
2302
2331
|
async deleteMobileMoney(mobileMoneyId) {
|
|
2303
|
-
return safe5(
|
|
2332
|
+
return safe5(
|
|
2333
|
+
this.client.linkDelete(`/v1/link/mobile-money/${mobileMoneyId}`)
|
|
2334
|
+
);
|
|
2304
2335
|
}
|
|
2305
2336
|
async setDefaultMobileMoney(mobileMoneyId) {
|
|
2306
2337
|
return safe5(
|
|
2307
|
-
this.client.
|
|
2338
|
+
this.client.linkPost(`/v1/link/mobile-money/${mobileMoneyId}/default`)
|
|
2308
2339
|
);
|
|
2309
2340
|
}
|
|
2310
2341
|
async trackMobileMoneyUsage(mobileMoneyId) {
|
|
@@ -3430,7 +3461,7 @@ var CimplifyClient = class {
|
|
|
3430
3461
|
this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
|
|
3431
3462
|
this.hooks = config.hooks ?? {};
|
|
3432
3463
|
this.accessToken = this.loadAccessToken();
|
|
3433
|
-
if (!this.publicKey) {
|
|
3464
|
+
if (!this.publicKey && !config.suppressPublicKeyWarning) {
|
|
3434
3465
|
console.warn(
|
|
3435
3466
|
'[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "pk_..." } to createCimplifyClient().'
|
|
3436
3467
|
);
|
|
@@ -3543,9 +3574,12 @@ var CimplifyClient = class {
|
|
|
3543
3574
|
}
|
|
3544
3575
|
getHeaders() {
|
|
3545
3576
|
const headers = {
|
|
3546
|
-
"Content-Type": "application/json"
|
|
3547
|
-
"X-API-Key": this.publicKey
|
|
3577
|
+
"Content-Type": "application/json"
|
|
3548
3578
|
};
|
|
3579
|
+
const trimmedPublicKey = this.publicKey.trim();
|
|
3580
|
+
if (trimmedPublicKey) {
|
|
3581
|
+
headers["X-API-Key"] = trimmedPublicKey;
|
|
3582
|
+
}
|
|
3549
3583
|
if (this.accessToken) {
|
|
3550
3584
|
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
3551
3585
|
}
|
|
@@ -3574,7 +3608,7 @@ var CimplifyClient = class {
|
|
|
3574
3608
|
signal: controller.signal
|
|
3575
3609
|
});
|
|
3576
3610
|
clearTimeout(timeoutId);
|
|
3577
|
-
if (response.ok
|
|
3611
|
+
if (response.ok) {
|
|
3578
3612
|
this.hooks.onRequestSuccess?.({
|
|
3579
3613
|
...context,
|
|
3580
3614
|
status: response.status,
|
|
@@ -3582,6 +3616,21 @@ var CimplifyClient = class {
|
|
|
3582
3616
|
});
|
|
3583
3617
|
return response;
|
|
3584
3618
|
}
|
|
3619
|
+
if (response.status >= 400 && response.status < 500) {
|
|
3620
|
+
this.hooks.onRequestError?.({
|
|
3621
|
+
...context,
|
|
3622
|
+
error: new CimplifyError(
|
|
3623
|
+
`HTTP_${response.status}`,
|
|
3624
|
+
`Request failed with status ${response.status}`,
|
|
3625
|
+
false
|
|
3626
|
+
),
|
|
3627
|
+
status: response.status,
|
|
3628
|
+
durationMs: Date.now() - startTime,
|
|
3629
|
+
retryCount,
|
|
3630
|
+
retryable: false
|
|
3631
|
+
});
|
|
3632
|
+
return response;
|
|
3633
|
+
}
|
|
3585
3634
|
if (response.status >= 500 && attempt < this.maxRetries) {
|
|
3586
3635
|
retryCount++;
|
|
3587
3636
|
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
@@ -3594,10 +3643,17 @@ var CimplifyClient = class {
|
|
|
3594
3643
|
await sleep(delay);
|
|
3595
3644
|
continue;
|
|
3596
3645
|
}
|
|
3597
|
-
this.hooks.
|
|
3646
|
+
this.hooks.onRequestError?.({
|
|
3598
3647
|
...context,
|
|
3648
|
+
error: new CimplifyError(
|
|
3649
|
+
"SERVER_ERROR",
|
|
3650
|
+
`Server error ${response.status} after ${retryCount} retries`,
|
|
3651
|
+
false
|
|
3652
|
+
),
|
|
3599
3653
|
status: response.status,
|
|
3600
|
-
durationMs: Date.now() - startTime
|
|
3654
|
+
durationMs: Date.now() - startTime,
|
|
3655
|
+
retryCount,
|
|
3656
|
+
retryable: false
|
|
3601
3657
|
});
|
|
3602
3658
|
return response;
|
|
3603
3659
|
} catch (error) {
|
|
@@ -5083,6 +5139,750 @@ function useLocations(options = {}) {
|
|
|
5083
5139
|
isLoading
|
|
5084
5140
|
};
|
|
5085
5141
|
}
|
|
5142
|
+
var collectionsCache = /* @__PURE__ */ new Map();
|
|
5143
|
+
var collectionsInflight = /* @__PURE__ */ new Map();
|
|
5144
|
+
function buildCollectionsCacheKey(client, locationId) {
|
|
5145
|
+
return JSON.stringify({
|
|
5146
|
+
key: client.getPublicKey(),
|
|
5147
|
+
location_id: locationId || "__none__"
|
|
5148
|
+
});
|
|
5149
|
+
}
|
|
5150
|
+
function useCollections(options = {}) {
|
|
5151
|
+
const context = useOptionalCimplify();
|
|
5152
|
+
const client = options.client ?? context?.client;
|
|
5153
|
+
if (!client) {
|
|
5154
|
+
throw new Error("useCollections must be used within CimplifyProvider or passed { client }.");
|
|
5155
|
+
}
|
|
5156
|
+
const enabled = options.enabled ?? true;
|
|
5157
|
+
const locationId = client.getLocationId();
|
|
5158
|
+
const previousLocationIdRef = useRef(locationId);
|
|
5159
|
+
const requestIdRef = useRef(0);
|
|
5160
|
+
const cacheKey = useMemo(
|
|
5161
|
+
() => buildCollectionsCacheKey(client, locationId),
|
|
5162
|
+
[client, locationId]
|
|
5163
|
+
);
|
|
5164
|
+
const cached = collectionsCache.get(cacheKey);
|
|
5165
|
+
const [collections, setCollections] = useState(cached?.collections ?? []);
|
|
5166
|
+
const [isLoading, setIsLoading] = useState(enabled && !cached);
|
|
5167
|
+
const [error, setError] = useState(null);
|
|
5168
|
+
useEffect(() => {
|
|
5169
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5170
|
+
collectionsCache.clear();
|
|
5171
|
+
collectionsInflight.clear();
|
|
5172
|
+
previousLocationIdRef.current = locationId;
|
|
5173
|
+
}
|
|
5174
|
+
}, [locationId]);
|
|
5175
|
+
const load = useCallback(
|
|
5176
|
+
async (force = false) => {
|
|
5177
|
+
if (!enabled) {
|
|
5178
|
+
setIsLoading(false);
|
|
5179
|
+
return;
|
|
5180
|
+
}
|
|
5181
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5182
|
+
setError(null);
|
|
5183
|
+
if (!force) {
|
|
5184
|
+
const cacheEntry = collectionsCache.get(cacheKey);
|
|
5185
|
+
if (cacheEntry) {
|
|
5186
|
+
setCollections(cacheEntry.collections);
|
|
5187
|
+
setIsLoading(false);
|
|
5188
|
+
return;
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
setIsLoading(true);
|
|
5192
|
+
try {
|
|
5193
|
+
const existing = collectionsInflight.get(cacheKey);
|
|
5194
|
+
const promise = existing ?? (async () => {
|
|
5195
|
+
const result = await client.catalogue.getCollections();
|
|
5196
|
+
if (!result.ok) {
|
|
5197
|
+
throw result.error;
|
|
5198
|
+
}
|
|
5199
|
+
return result.value;
|
|
5200
|
+
})();
|
|
5201
|
+
if (!existing) {
|
|
5202
|
+
collectionsInflight.set(cacheKey, promise);
|
|
5203
|
+
promise.finally(() => {
|
|
5204
|
+
collectionsInflight.delete(cacheKey);
|
|
5205
|
+
}).catch(() => void 0);
|
|
5206
|
+
}
|
|
5207
|
+
const value = await promise;
|
|
5208
|
+
collectionsCache.set(cacheKey, { collections: value });
|
|
5209
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5210
|
+
setCollections(value);
|
|
5211
|
+
setError(null);
|
|
5212
|
+
}
|
|
5213
|
+
} catch (loadError) {
|
|
5214
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5215
|
+
setError(loadError);
|
|
5216
|
+
}
|
|
5217
|
+
} finally {
|
|
5218
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5219
|
+
setIsLoading(false);
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
},
|
|
5223
|
+
[cacheKey, client, enabled]
|
|
5224
|
+
);
|
|
5225
|
+
useEffect(() => {
|
|
5226
|
+
void load(false);
|
|
5227
|
+
}, [load]);
|
|
5228
|
+
const refetch = useCallback(async () => {
|
|
5229
|
+
collectionsCache.delete(cacheKey);
|
|
5230
|
+
await load(true);
|
|
5231
|
+
}, [cacheKey, load]);
|
|
5232
|
+
return { collections, isLoading, error, refetch };
|
|
5233
|
+
}
|
|
5234
|
+
var collectionCache = /* @__PURE__ */ new Map();
|
|
5235
|
+
var collectionInflight = /* @__PURE__ */ new Map();
|
|
5236
|
+
function isLikelySlug2(value) {
|
|
5237
|
+
return /^[a-z0-9-]+$/.test(value);
|
|
5238
|
+
}
|
|
5239
|
+
function buildCollectionCacheKey(client, locationId, idOrSlug) {
|
|
5240
|
+
return JSON.stringify({
|
|
5241
|
+
key: client.getPublicKey(),
|
|
5242
|
+
location_id: locationId || "__none__",
|
|
5243
|
+
collection: idOrSlug
|
|
5244
|
+
});
|
|
5245
|
+
}
|
|
5246
|
+
function useCollection(idOrSlug, options = {}) {
|
|
5247
|
+
const context = useOptionalCimplify();
|
|
5248
|
+
const client = options.client ?? context?.client;
|
|
5249
|
+
if (!client) {
|
|
5250
|
+
throw new Error("useCollection must be used within CimplifyProvider or passed { client }.");
|
|
5251
|
+
}
|
|
5252
|
+
const enabled = options.enabled ?? true;
|
|
5253
|
+
const locationId = client.getLocationId();
|
|
5254
|
+
const previousLocationIdRef = useRef(locationId);
|
|
5255
|
+
const requestIdRef = useRef(0);
|
|
5256
|
+
const normalizedIdOrSlug = useMemo(() => (idOrSlug || "").trim(), [idOrSlug]);
|
|
5257
|
+
const cacheKey = useMemo(
|
|
5258
|
+
() => buildCollectionCacheKey(client, locationId, normalizedIdOrSlug),
|
|
5259
|
+
[client, locationId, normalizedIdOrSlug]
|
|
5260
|
+
);
|
|
5261
|
+
const cached = collectionCache.get(cacheKey);
|
|
5262
|
+
const [collection, setCollection] = useState(cached?.collection ?? null);
|
|
5263
|
+
const [products, setProducts] = useState(cached?.products ?? []);
|
|
5264
|
+
const [isLoading, setIsLoading] = useState(
|
|
5265
|
+
enabled && normalizedIdOrSlug.length > 0 && !cached
|
|
5266
|
+
);
|
|
5267
|
+
const [error, setError] = useState(null);
|
|
5268
|
+
useEffect(() => {
|
|
5269
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5270
|
+
collectionCache.clear();
|
|
5271
|
+
collectionInflight.clear();
|
|
5272
|
+
previousLocationIdRef.current = locationId;
|
|
5273
|
+
}
|
|
5274
|
+
}, [locationId]);
|
|
5275
|
+
const load = useCallback(
|
|
5276
|
+
async (force = false) => {
|
|
5277
|
+
if (!enabled || normalizedIdOrSlug.length === 0) {
|
|
5278
|
+
setCollection(null);
|
|
5279
|
+
setProducts([]);
|
|
5280
|
+
setIsLoading(false);
|
|
5281
|
+
return;
|
|
5282
|
+
}
|
|
5283
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5284
|
+
setError(null);
|
|
5285
|
+
if (!force) {
|
|
5286
|
+
const cacheEntry = collectionCache.get(cacheKey);
|
|
5287
|
+
if (cacheEntry) {
|
|
5288
|
+
setCollection(cacheEntry.collection);
|
|
5289
|
+
setProducts(cacheEntry.products);
|
|
5290
|
+
setIsLoading(false);
|
|
5291
|
+
return;
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
setIsLoading(true);
|
|
5295
|
+
try {
|
|
5296
|
+
const existing = collectionInflight.get(cacheKey);
|
|
5297
|
+
const promise = existing ?? (async () => {
|
|
5298
|
+
const collectionResult = isLikelySlug2(normalizedIdOrSlug) ? await client.catalogue.getCollectionBySlug(normalizedIdOrSlug) : await client.catalogue.getCollection(normalizedIdOrSlug);
|
|
5299
|
+
if (!collectionResult.ok) {
|
|
5300
|
+
throw collectionResult.error;
|
|
5301
|
+
}
|
|
5302
|
+
const productsResult = await client.catalogue.getCollectionProducts(
|
|
5303
|
+
collectionResult.value.id
|
|
5304
|
+
);
|
|
5305
|
+
if (!productsResult.ok) {
|
|
5306
|
+
throw productsResult.error;
|
|
5307
|
+
}
|
|
5308
|
+
return {
|
|
5309
|
+
collection: collectionResult.value,
|
|
5310
|
+
products: productsResult.value
|
|
5311
|
+
};
|
|
5312
|
+
})();
|
|
5313
|
+
if (!existing) {
|
|
5314
|
+
collectionInflight.set(cacheKey, promise);
|
|
5315
|
+
promise.finally(() => {
|
|
5316
|
+
collectionInflight.delete(cacheKey);
|
|
5317
|
+
}).catch(() => void 0);
|
|
5318
|
+
}
|
|
5319
|
+
const value = await promise;
|
|
5320
|
+
collectionCache.set(cacheKey, value);
|
|
5321
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5322
|
+
setCollection(value.collection);
|
|
5323
|
+
setProducts(value.products);
|
|
5324
|
+
setError(null);
|
|
5325
|
+
}
|
|
5326
|
+
} catch (loadError) {
|
|
5327
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5328
|
+
setError(loadError);
|
|
5329
|
+
}
|
|
5330
|
+
} finally {
|
|
5331
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5332
|
+
setIsLoading(false);
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
},
|
|
5336
|
+
[cacheKey, client, enabled, normalizedIdOrSlug]
|
|
5337
|
+
);
|
|
5338
|
+
useEffect(() => {
|
|
5339
|
+
void load(false);
|
|
5340
|
+
}, [load]);
|
|
5341
|
+
const refetch = useCallback(async () => {
|
|
5342
|
+
collectionCache.delete(cacheKey);
|
|
5343
|
+
await load(true);
|
|
5344
|
+
}, [cacheKey, load]);
|
|
5345
|
+
return { collection, products, isLoading, error, refetch };
|
|
5346
|
+
}
|
|
5347
|
+
var bundleCache = /* @__PURE__ */ new Map();
|
|
5348
|
+
var bundleInflight = /* @__PURE__ */ new Map();
|
|
5349
|
+
function isLikelySlug3(value) {
|
|
5350
|
+
return /^[a-z0-9-]+$/.test(value);
|
|
5351
|
+
}
|
|
5352
|
+
function buildBundleCacheKey(client, locationId, idOrSlug) {
|
|
5353
|
+
return JSON.stringify({
|
|
5354
|
+
key: client.getPublicKey(),
|
|
5355
|
+
location_id: locationId || "__none__",
|
|
5356
|
+
bundle: idOrSlug
|
|
5357
|
+
});
|
|
5358
|
+
}
|
|
5359
|
+
function useBundle(idOrSlug, options = {}) {
|
|
5360
|
+
const context = useOptionalCimplify();
|
|
5361
|
+
const client = options.client ?? context?.client;
|
|
5362
|
+
if (!client) {
|
|
5363
|
+
throw new Error("useBundle must be used within CimplifyProvider or passed { client }.");
|
|
5364
|
+
}
|
|
5365
|
+
const enabled = options.enabled ?? true;
|
|
5366
|
+
const locationId = client.getLocationId();
|
|
5367
|
+
const previousLocationIdRef = useRef(locationId);
|
|
5368
|
+
const requestIdRef = useRef(0);
|
|
5369
|
+
const normalizedIdOrSlug = useMemo(() => (idOrSlug || "").trim(), [idOrSlug]);
|
|
5370
|
+
const cacheKey = useMemo(
|
|
5371
|
+
() => buildBundleCacheKey(client, locationId, normalizedIdOrSlug),
|
|
5372
|
+
[client, locationId, normalizedIdOrSlug]
|
|
5373
|
+
);
|
|
5374
|
+
const cached = bundleCache.get(cacheKey);
|
|
5375
|
+
const [bundle, setBundle] = useState(cached?.bundle ?? null);
|
|
5376
|
+
const [isLoading, setIsLoading] = useState(
|
|
5377
|
+
enabled && normalizedIdOrSlug.length > 0 && !cached
|
|
5378
|
+
);
|
|
5379
|
+
const [error, setError] = useState(null);
|
|
5380
|
+
useEffect(() => {
|
|
5381
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5382
|
+
bundleCache.clear();
|
|
5383
|
+
bundleInflight.clear();
|
|
5384
|
+
previousLocationIdRef.current = locationId;
|
|
5385
|
+
}
|
|
5386
|
+
}, [locationId]);
|
|
5387
|
+
const load = useCallback(
|
|
5388
|
+
async (force = false) => {
|
|
5389
|
+
if (!enabled || normalizedIdOrSlug.length === 0) {
|
|
5390
|
+
setBundle(null);
|
|
5391
|
+
setIsLoading(false);
|
|
5392
|
+
return;
|
|
5393
|
+
}
|
|
5394
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5395
|
+
setError(null);
|
|
5396
|
+
if (!force) {
|
|
5397
|
+
const cacheEntry = bundleCache.get(cacheKey);
|
|
5398
|
+
if (cacheEntry) {
|
|
5399
|
+
setBundle(cacheEntry.bundle);
|
|
5400
|
+
setIsLoading(false);
|
|
5401
|
+
return;
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5404
|
+
setIsLoading(true);
|
|
5405
|
+
try {
|
|
5406
|
+
const existing = bundleInflight.get(cacheKey);
|
|
5407
|
+
const promise = existing ?? (async () => {
|
|
5408
|
+
const result = isLikelySlug3(normalizedIdOrSlug) ? await client.catalogue.getBundleBySlug(normalizedIdOrSlug) : await client.catalogue.getBundle(normalizedIdOrSlug);
|
|
5409
|
+
if (!result.ok) {
|
|
5410
|
+
throw result.error;
|
|
5411
|
+
}
|
|
5412
|
+
return result.value;
|
|
5413
|
+
})();
|
|
5414
|
+
if (!existing) {
|
|
5415
|
+
bundleInflight.set(cacheKey, promise);
|
|
5416
|
+
promise.finally(() => {
|
|
5417
|
+
bundleInflight.delete(cacheKey);
|
|
5418
|
+
}).catch(() => void 0);
|
|
5419
|
+
}
|
|
5420
|
+
const value = await promise;
|
|
5421
|
+
bundleCache.set(cacheKey, { bundle: value });
|
|
5422
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5423
|
+
setBundle(value);
|
|
5424
|
+
setError(null);
|
|
5425
|
+
}
|
|
5426
|
+
} catch (loadError) {
|
|
5427
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5428
|
+
setError(loadError);
|
|
5429
|
+
}
|
|
5430
|
+
} finally {
|
|
5431
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5432
|
+
setIsLoading(false);
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
},
|
|
5436
|
+
[cacheKey, client, enabled, normalizedIdOrSlug]
|
|
5437
|
+
);
|
|
5438
|
+
useEffect(() => {
|
|
5439
|
+
void load(false);
|
|
5440
|
+
}, [load]);
|
|
5441
|
+
const refetch = useCallback(async () => {
|
|
5442
|
+
bundleCache.delete(cacheKey);
|
|
5443
|
+
await load(true);
|
|
5444
|
+
}, [cacheKey, load]);
|
|
5445
|
+
return { bundle, isLoading, error, refetch };
|
|
5446
|
+
}
|
|
5447
|
+
var compositeCache = /* @__PURE__ */ new Map();
|
|
5448
|
+
var compositeInflight = /* @__PURE__ */ new Map();
|
|
5449
|
+
function shouldFetchByProductId(idOrProductId, byProductId) {
|
|
5450
|
+
if (typeof byProductId === "boolean") {
|
|
5451
|
+
return byProductId;
|
|
5452
|
+
}
|
|
5453
|
+
return idOrProductId.startsWith("prod_");
|
|
5454
|
+
}
|
|
5455
|
+
function buildCompositeCacheKey(client, locationId, idOrProductId, byProductId) {
|
|
5456
|
+
return JSON.stringify({
|
|
5457
|
+
key: client.getPublicKey(),
|
|
5458
|
+
location_id: locationId || "__none__",
|
|
5459
|
+
composite: idOrProductId,
|
|
5460
|
+
by_product_id: byProductId
|
|
5461
|
+
});
|
|
5462
|
+
}
|
|
5463
|
+
function useComposite(idOrProductId, options = {}) {
|
|
5464
|
+
const context = useOptionalCimplify();
|
|
5465
|
+
const client = options.client ?? context?.client;
|
|
5466
|
+
if (!client) {
|
|
5467
|
+
throw new Error("useComposite must be used within CimplifyProvider or passed { client }.");
|
|
5468
|
+
}
|
|
5469
|
+
const enabled = options.enabled ?? true;
|
|
5470
|
+
const locationId = client.getLocationId();
|
|
5471
|
+
const previousLocationIdRef = useRef(locationId);
|
|
5472
|
+
const requestIdRef = useRef(0);
|
|
5473
|
+
const priceRequestIdRef = useRef(0);
|
|
5474
|
+
const normalizedIdOrProductId = useMemo(
|
|
5475
|
+
() => (idOrProductId || "").trim(),
|
|
5476
|
+
[idOrProductId]
|
|
5477
|
+
);
|
|
5478
|
+
const byProductId = useMemo(
|
|
5479
|
+
() => shouldFetchByProductId(normalizedIdOrProductId, options.byProductId),
|
|
5480
|
+
[normalizedIdOrProductId, options.byProductId]
|
|
5481
|
+
);
|
|
5482
|
+
const cacheKey = useMemo(
|
|
5483
|
+
() => buildCompositeCacheKey(client, locationId, normalizedIdOrProductId, byProductId),
|
|
5484
|
+
[byProductId, client, locationId, normalizedIdOrProductId]
|
|
5485
|
+
);
|
|
5486
|
+
const cached = compositeCache.get(cacheKey);
|
|
5487
|
+
const [composite, setComposite] = useState(cached?.composite ?? null);
|
|
5488
|
+
const [isLoading, setIsLoading] = useState(
|
|
5489
|
+
enabled && normalizedIdOrProductId.length > 0 && !cached
|
|
5490
|
+
);
|
|
5491
|
+
const [error, setError] = useState(null);
|
|
5492
|
+
const [priceResult, setPriceResult] = useState(null);
|
|
5493
|
+
const [isPriceLoading, setIsPriceLoading] = useState(false);
|
|
5494
|
+
useEffect(() => {
|
|
5495
|
+
if (previousLocationIdRef.current !== locationId) {
|
|
5496
|
+
compositeCache.clear();
|
|
5497
|
+
compositeInflight.clear();
|
|
5498
|
+
previousLocationIdRef.current = locationId;
|
|
5499
|
+
}
|
|
5500
|
+
}, [locationId]);
|
|
5501
|
+
const load = useCallback(
|
|
5502
|
+
async (force = false) => {
|
|
5503
|
+
if (!enabled || normalizedIdOrProductId.length === 0) {
|
|
5504
|
+
setComposite(null);
|
|
5505
|
+
setPriceResult(null);
|
|
5506
|
+
setIsLoading(false);
|
|
5507
|
+
return;
|
|
5508
|
+
}
|
|
5509
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5510
|
+
setError(null);
|
|
5511
|
+
if (!force) {
|
|
5512
|
+
const cacheEntry = compositeCache.get(cacheKey);
|
|
5513
|
+
if (cacheEntry) {
|
|
5514
|
+
setComposite(cacheEntry.composite);
|
|
5515
|
+
setIsLoading(false);
|
|
5516
|
+
return;
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
setIsLoading(true);
|
|
5520
|
+
try {
|
|
5521
|
+
const existing = compositeInflight.get(cacheKey);
|
|
5522
|
+
const promise = existing ?? (async () => {
|
|
5523
|
+
const result = byProductId ? await client.catalogue.getCompositeByProductId(normalizedIdOrProductId) : await client.catalogue.getComposite(normalizedIdOrProductId);
|
|
5524
|
+
if (!result.ok) {
|
|
5525
|
+
throw result.error;
|
|
5526
|
+
}
|
|
5527
|
+
return result.value;
|
|
5528
|
+
})();
|
|
5529
|
+
if (!existing) {
|
|
5530
|
+
compositeInflight.set(cacheKey, promise);
|
|
5531
|
+
promise.finally(() => {
|
|
5532
|
+
compositeInflight.delete(cacheKey);
|
|
5533
|
+
}).catch(() => void 0);
|
|
5534
|
+
}
|
|
5535
|
+
const value = await promise;
|
|
5536
|
+
compositeCache.set(cacheKey, { composite: value });
|
|
5537
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5538
|
+
setComposite(value);
|
|
5539
|
+
setPriceResult(null);
|
|
5540
|
+
setError(null);
|
|
5541
|
+
}
|
|
5542
|
+
} catch (loadError) {
|
|
5543
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5544
|
+
setError(loadError);
|
|
5545
|
+
}
|
|
5546
|
+
} finally {
|
|
5547
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5548
|
+
setIsLoading(false);
|
|
5549
|
+
}
|
|
5550
|
+
}
|
|
5551
|
+
},
|
|
5552
|
+
[byProductId, cacheKey, client, enabled, normalizedIdOrProductId]
|
|
5553
|
+
);
|
|
5554
|
+
useEffect(() => {
|
|
5555
|
+
void load(false);
|
|
5556
|
+
}, [load]);
|
|
5557
|
+
const calculatePrice = useCallback(
|
|
5558
|
+
async (selections, overrideLocationId) => {
|
|
5559
|
+
if (!composite) {
|
|
5560
|
+
return null;
|
|
5561
|
+
}
|
|
5562
|
+
const nextRequestId = ++priceRequestIdRef.current;
|
|
5563
|
+
setIsPriceLoading(true);
|
|
5564
|
+
try {
|
|
5565
|
+
const result = await client.catalogue.calculateCompositePrice(
|
|
5566
|
+
composite.id,
|
|
5567
|
+
selections,
|
|
5568
|
+
overrideLocationId
|
|
5569
|
+
);
|
|
5570
|
+
if (!result.ok) {
|
|
5571
|
+
throw result.error;
|
|
5572
|
+
}
|
|
5573
|
+
if (nextRequestId === priceRequestIdRef.current) {
|
|
5574
|
+
setPriceResult(result.value);
|
|
5575
|
+
setError(null);
|
|
5576
|
+
}
|
|
5577
|
+
return result.value;
|
|
5578
|
+
} catch (loadError) {
|
|
5579
|
+
if (nextRequestId === priceRequestIdRef.current) {
|
|
5580
|
+
setError(loadError);
|
|
5581
|
+
}
|
|
5582
|
+
return null;
|
|
5583
|
+
} finally {
|
|
5584
|
+
if (nextRequestId === priceRequestIdRef.current) {
|
|
5585
|
+
setIsPriceLoading(false);
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
},
|
|
5589
|
+
[client, composite]
|
|
5590
|
+
);
|
|
5591
|
+
const refetch = useCallback(async () => {
|
|
5592
|
+
compositeCache.delete(cacheKey);
|
|
5593
|
+
await load(true);
|
|
5594
|
+
}, [cacheKey, load]);
|
|
5595
|
+
return { composite, isLoading, error, refetch, calculatePrice, priceResult, isPriceLoading };
|
|
5596
|
+
}
|
|
5597
|
+
function useSearch(options = {}) {
|
|
5598
|
+
const context = useOptionalCimplify();
|
|
5599
|
+
const client = options.client ?? context?.client;
|
|
5600
|
+
if (!client) {
|
|
5601
|
+
throw new Error("useSearch must be used within CimplifyProvider or passed { client }.");
|
|
5602
|
+
}
|
|
5603
|
+
const minLength = Math.max(0, options.minLength ?? 2);
|
|
5604
|
+
const debounceMs = Math.max(0, options.debounceMs ?? 300);
|
|
5605
|
+
const limit = Math.max(1, options.limit ?? 20);
|
|
5606
|
+
const [query, setQueryState] = useState("");
|
|
5607
|
+
const [results, setResults] = useState([]);
|
|
5608
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
5609
|
+
const [error, setError] = useState(null);
|
|
5610
|
+
const requestIdRef = useRef(0);
|
|
5611
|
+
const timerRef = useRef(null);
|
|
5612
|
+
useEffect(() => {
|
|
5613
|
+
if (timerRef.current) {
|
|
5614
|
+
clearTimeout(timerRef.current);
|
|
5615
|
+
timerRef.current = null;
|
|
5616
|
+
}
|
|
5617
|
+
const trimmedQuery = query.trim();
|
|
5618
|
+
if (trimmedQuery.length < minLength) {
|
|
5619
|
+
setResults([]);
|
|
5620
|
+
setError(null);
|
|
5621
|
+
setIsLoading(false);
|
|
5622
|
+
return;
|
|
5623
|
+
}
|
|
5624
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5625
|
+
setError(null);
|
|
5626
|
+
setIsLoading(true);
|
|
5627
|
+
timerRef.current = setTimeout(() => {
|
|
5628
|
+
void (async () => {
|
|
5629
|
+
try {
|
|
5630
|
+
const result = await client.catalogue.searchProducts(trimmedQuery, {
|
|
5631
|
+
limit,
|
|
5632
|
+
category: options.category
|
|
5633
|
+
});
|
|
5634
|
+
if (!result.ok) {
|
|
5635
|
+
throw result.error;
|
|
5636
|
+
}
|
|
5637
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5638
|
+
setResults(result.value);
|
|
5639
|
+
setError(null);
|
|
5640
|
+
}
|
|
5641
|
+
} catch (loadError) {
|
|
5642
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5643
|
+
setError(loadError);
|
|
5644
|
+
}
|
|
5645
|
+
} finally {
|
|
5646
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5647
|
+
setIsLoading(false);
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
})();
|
|
5651
|
+
}, debounceMs);
|
|
5652
|
+
return () => {
|
|
5653
|
+
if (timerRef.current) {
|
|
5654
|
+
clearTimeout(timerRef.current);
|
|
5655
|
+
timerRef.current = null;
|
|
5656
|
+
}
|
|
5657
|
+
};
|
|
5658
|
+
}, [client, debounceMs, limit, minLength, options.category, query]);
|
|
5659
|
+
const setQuery = useCallback((nextQuery) => {
|
|
5660
|
+
setQueryState(nextQuery);
|
|
5661
|
+
}, []);
|
|
5662
|
+
const clear = useCallback(() => {
|
|
5663
|
+
requestIdRef.current += 1;
|
|
5664
|
+
if (timerRef.current) {
|
|
5665
|
+
clearTimeout(timerRef.current);
|
|
5666
|
+
timerRef.current = null;
|
|
5667
|
+
}
|
|
5668
|
+
setQueryState("");
|
|
5669
|
+
setResults([]);
|
|
5670
|
+
setError(null);
|
|
5671
|
+
setIsLoading(false);
|
|
5672
|
+
}, []);
|
|
5673
|
+
return { results, isLoading, error, query, setQuery, clear };
|
|
5674
|
+
}
|
|
5675
|
+
var quoteCache = /* @__PURE__ */ new Map();
|
|
5676
|
+
var quoteInflight = /* @__PURE__ */ new Map();
|
|
5677
|
+
function buildQuoteCacheKey(client, locationId, inputSignature) {
|
|
5678
|
+
return JSON.stringify({
|
|
5679
|
+
key: client.getPublicKey(),
|
|
5680
|
+
location_id: locationId || "__none__",
|
|
5681
|
+
input: inputSignature
|
|
5682
|
+
});
|
|
5683
|
+
}
|
|
5684
|
+
function isQuoteExpired(quote) {
|
|
5685
|
+
if (!quote?.expires_at) {
|
|
5686
|
+
return false;
|
|
5687
|
+
}
|
|
5688
|
+
const expiresAt = Date.parse(quote.expires_at);
|
|
5689
|
+
if (!Number.isFinite(expiresAt)) {
|
|
5690
|
+
return false;
|
|
5691
|
+
}
|
|
5692
|
+
return expiresAt <= Date.now();
|
|
5693
|
+
}
|
|
5694
|
+
function normalizeInput(input, fallbackLocationId) {
|
|
5695
|
+
const productId = input.productId.trim();
|
|
5696
|
+
const variantId = input.variantId?.trim();
|
|
5697
|
+
const locationId = input.locationId?.trim() || fallbackLocationId || void 0;
|
|
5698
|
+
return {
|
|
5699
|
+
product_id: productId,
|
|
5700
|
+
variant_id: variantId && variantId.length > 0 ? variantId : void 0,
|
|
5701
|
+
location_id: locationId && locationId.length > 0 ? locationId : void 0,
|
|
5702
|
+
quantity: input.quantity,
|
|
5703
|
+
add_on_option_ids: input.addOnOptionIds,
|
|
5704
|
+
bundle_selections: input.bundleSelections,
|
|
5705
|
+
composite_selections: input.compositeSelections
|
|
5706
|
+
};
|
|
5707
|
+
}
|
|
5708
|
+
function useQuote(input, options = {}) {
|
|
5709
|
+
const context = useOptionalCimplify();
|
|
5710
|
+
const client = options.client ?? context?.client;
|
|
5711
|
+
if (!client) {
|
|
5712
|
+
throw new Error("useQuote must be used within CimplifyProvider or passed { client }.");
|
|
5713
|
+
}
|
|
5714
|
+
const enabled = options.enabled ?? true;
|
|
5715
|
+
const autoRefresh = options.autoRefresh ?? true;
|
|
5716
|
+
const refreshBeforeExpiryMs = Math.max(0, options.refreshBeforeExpiryMs ?? 3e4);
|
|
5717
|
+
const locationId = client.getLocationId();
|
|
5718
|
+
const requestIdRef = useRef(0);
|
|
5719
|
+
const refreshTimerRef = useRef(null);
|
|
5720
|
+
const expiryTimerRef = useRef(null);
|
|
5721
|
+
const inputSignature = useMemo(() => JSON.stringify(input ?? null), [input]);
|
|
5722
|
+
const normalizedInput = useMemo(() => {
|
|
5723
|
+
if (!input) {
|
|
5724
|
+
return null;
|
|
5725
|
+
}
|
|
5726
|
+
const normalized = normalizeInput(input, locationId);
|
|
5727
|
+
return normalized.product_id.length > 0 ? normalized : null;
|
|
5728
|
+
}, [inputSignature, locationId]);
|
|
5729
|
+
const cacheKey = useMemo(
|
|
5730
|
+
() => buildQuoteCacheKey(client, locationId, inputSignature),
|
|
5731
|
+
[client, inputSignature, locationId]
|
|
5732
|
+
);
|
|
5733
|
+
const cached = quoteCache.get(cacheKey);
|
|
5734
|
+
const [quote, setQuote] = useState(cached?.quote ?? null);
|
|
5735
|
+
const [isLoading, setIsLoading] = useState(enabled && normalizedInput !== null && !cached);
|
|
5736
|
+
const [error, setError] = useState(null);
|
|
5737
|
+
const [isExpired, setIsExpired] = useState(isQuoteExpired(cached?.quote ?? null));
|
|
5738
|
+
const [messages, setMessages] = useState(cached?.quote?.ui_messages ?? []);
|
|
5739
|
+
const load = useCallback(
|
|
5740
|
+
async (force = false) => {
|
|
5741
|
+
if (!enabled || !normalizedInput) {
|
|
5742
|
+
setQuote(null);
|
|
5743
|
+
setMessages([]);
|
|
5744
|
+
setIsExpired(false);
|
|
5745
|
+
setError(null);
|
|
5746
|
+
setIsLoading(false);
|
|
5747
|
+
return;
|
|
5748
|
+
}
|
|
5749
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5750
|
+
setError(null);
|
|
5751
|
+
if (!force) {
|
|
5752
|
+
const cacheEntry = quoteCache.get(cacheKey);
|
|
5753
|
+
if (cacheEntry) {
|
|
5754
|
+
setQuote(cacheEntry.quote);
|
|
5755
|
+
setMessages(cacheEntry.quote?.ui_messages ?? []);
|
|
5756
|
+
setIsExpired(isQuoteExpired(cacheEntry.quote));
|
|
5757
|
+
setIsLoading(false);
|
|
5758
|
+
return;
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
5761
|
+
setIsLoading(true);
|
|
5762
|
+
try {
|
|
5763
|
+
const existing = quoteInflight.get(cacheKey);
|
|
5764
|
+
const promise = existing ?? (async () => {
|
|
5765
|
+
const result = await client.catalogue.fetchQuote(normalizedInput);
|
|
5766
|
+
if (!result.ok) {
|
|
5767
|
+
throw result.error;
|
|
5768
|
+
}
|
|
5769
|
+
return result.value;
|
|
5770
|
+
})();
|
|
5771
|
+
if (!existing) {
|
|
5772
|
+
quoteInflight.set(cacheKey, promise);
|
|
5773
|
+
promise.finally(() => {
|
|
5774
|
+
quoteInflight.delete(cacheKey);
|
|
5775
|
+
}).catch(() => void 0);
|
|
5776
|
+
}
|
|
5777
|
+
const value = await promise;
|
|
5778
|
+
quoteCache.set(cacheKey, { quote: value });
|
|
5779
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5780
|
+
setQuote(value);
|
|
5781
|
+
setMessages(value.ui_messages ?? []);
|
|
5782
|
+
setIsExpired(isQuoteExpired(value));
|
|
5783
|
+
setError(null);
|
|
5784
|
+
}
|
|
5785
|
+
} catch (loadError) {
|
|
5786
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5787
|
+
setError(loadError);
|
|
5788
|
+
}
|
|
5789
|
+
} finally {
|
|
5790
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5791
|
+
setIsLoading(false);
|
|
5792
|
+
}
|
|
5793
|
+
}
|
|
5794
|
+
},
|
|
5795
|
+
[cacheKey, client, enabled, normalizedInput]
|
|
5796
|
+
);
|
|
5797
|
+
useEffect(() => {
|
|
5798
|
+
void load(false);
|
|
5799
|
+
}, [load]);
|
|
5800
|
+
const refresh = useCallback(async () => {
|
|
5801
|
+
if (!enabled || !normalizedInput) {
|
|
5802
|
+
return;
|
|
5803
|
+
}
|
|
5804
|
+
if (!quote?.quote_id) {
|
|
5805
|
+
await load(true);
|
|
5806
|
+
return;
|
|
5807
|
+
}
|
|
5808
|
+
const nextRequestId = ++requestIdRef.current;
|
|
5809
|
+
setError(null);
|
|
5810
|
+
setIsLoading(true);
|
|
5811
|
+
try {
|
|
5812
|
+
const result = await client.catalogue.refreshQuote({
|
|
5813
|
+
quote_id: quote.quote_id,
|
|
5814
|
+
...normalizedInput
|
|
5815
|
+
});
|
|
5816
|
+
if (!result.ok) {
|
|
5817
|
+
throw result.error;
|
|
5818
|
+
}
|
|
5819
|
+
const refreshed = result.value.quote;
|
|
5820
|
+
quoteCache.set(cacheKey, { quote: refreshed });
|
|
5821
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5822
|
+
setQuote(refreshed);
|
|
5823
|
+
setMessages(refreshed.ui_messages ?? []);
|
|
5824
|
+
setIsExpired(isQuoteExpired(refreshed));
|
|
5825
|
+
setError(null);
|
|
5826
|
+
}
|
|
5827
|
+
} catch (refreshError) {
|
|
5828
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5829
|
+
setError(refreshError);
|
|
5830
|
+
}
|
|
5831
|
+
} finally {
|
|
5832
|
+
if (nextRequestId === requestIdRef.current) {
|
|
5833
|
+
setIsLoading(false);
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
}, [cacheKey, client, enabled, load, normalizedInput, quote]);
|
|
5837
|
+
useEffect(() => {
|
|
5838
|
+
if (expiryTimerRef.current) {
|
|
5839
|
+
clearTimeout(expiryTimerRef.current);
|
|
5840
|
+
expiryTimerRef.current = null;
|
|
5841
|
+
}
|
|
5842
|
+
const expiresAt = quote?.expires_at ? Date.parse(quote.expires_at) : NaN;
|
|
5843
|
+
if (!Number.isFinite(expiresAt)) {
|
|
5844
|
+
setIsExpired(false);
|
|
5845
|
+
return;
|
|
5846
|
+
}
|
|
5847
|
+
const expired = expiresAt <= Date.now();
|
|
5848
|
+
setIsExpired(expired);
|
|
5849
|
+
if (!expired) {
|
|
5850
|
+
expiryTimerRef.current = setTimeout(() => {
|
|
5851
|
+
setIsExpired(true);
|
|
5852
|
+
}, Math.max(0, expiresAt - Date.now()));
|
|
5853
|
+
}
|
|
5854
|
+
return () => {
|
|
5855
|
+
if (expiryTimerRef.current) {
|
|
5856
|
+
clearTimeout(expiryTimerRef.current);
|
|
5857
|
+
expiryTimerRef.current = null;
|
|
5858
|
+
}
|
|
5859
|
+
};
|
|
5860
|
+
}, [quote?.expires_at, quote?.quote_id]);
|
|
5861
|
+
useEffect(() => {
|
|
5862
|
+
if (refreshTimerRef.current) {
|
|
5863
|
+
clearTimeout(refreshTimerRef.current);
|
|
5864
|
+
refreshTimerRef.current = null;
|
|
5865
|
+
}
|
|
5866
|
+
if (!autoRefresh || !enabled || !quote?.expires_at) {
|
|
5867
|
+
return;
|
|
5868
|
+
}
|
|
5869
|
+
const expiresAt = Date.parse(quote.expires_at);
|
|
5870
|
+
if (!Number.isFinite(expiresAt)) {
|
|
5871
|
+
return;
|
|
5872
|
+
}
|
|
5873
|
+
const delay = Math.max(0, expiresAt - Date.now() - refreshBeforeExpiryMs);
|
|
5874
|
+
refreshTimerRef.current = setTimeout(() => {
|
|
5875
|
+
void refresh();
|
|
5876
|
+
}, delay);
|
|
5877
|
+
return () => {
|
|
5878
|
+
if (refreshTimerRef.current) {
|
|
5879
|
+
clearTimeout(refreshTimerRef.current);
|
|
5880
|
+
refreshTimerRef.current = null;
|
|
5881
|
+
}
|
|
5882
|
+
};
|
|
5883
|
+
}, [autoRefresh, enabled, quote?.expires_at, quote?.quote_id, refresh, refreshBeforeExpiryMs]);
|
|
5884
|
+
return { quote, isLoading, error, refresh, isExpired, messages };
|
|
5885
|
+
}
|
|
5086
5886
|
var ElementsContext = createContext({
|
|
5087
5887
|
elements: null,
|
|
5088
5888
|
isReady: false
|
|
@@ -5243,4 +6043,4 @@ function useCheckout() {
|
|
|
5243
6043
|
return { submit, process, isLoading };
|
|
5244
6044
|
}
|
|
5245
6045
|
|
|
5246
|
-
export { Ad, AdProvider, AddressElement, AuthElement, CimplifyCheckout, CimplifyProvider, ElementsProvider, PaymentElement, useAds, useCart, useCategories, useCheckout, useCimplify, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts };
|
|
6046
|
+
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 };
|