shop-client 3.8.2 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +93 -1
- package/dist/checkout.mjs +1 -7
- package/dist/chunk-6GPWNCDO.mjs +130 -0
- package/dist/chunk-EJO5U4BT.mjs +2 -0
- package/dist/chunk-FFKWCNLU.mjs +1 -0
- package/dist/chunk-KYLPIEU3.mjs +2 -0
- package/dist/chunk-MB2INNNP.mjs +1 -0
- package/dist/chunk-MI7754VX.mjs +2 -0
- package/dist/chunk-SZQPMLZG.mjs +1 -0
- package/dist/collections.d.ts +1 -1
- package/dist/collections.mjs +1 -9
- package/dist/enrich-OZHBXKK6.mjs +1 -0
- package/dist/index.d.ts +24 -6
- package/dist/index.mjs +2 -702
- package/dist/products.d.ts +1 -1
- package/dist/products.mjs +1 -9
- package/dist/{store-CJVUz2Yb.d.mts → store-iQARl6J3.d.ts} +3 -3
- package/dist/store.d.ts +1 -1
- package/dist/store.mjs +1 -9
- package/dist/utils/rate-limit.d.ts +5 -0
- package/dist/utils/rate-limit.mjs +1 -11
- package/package.json +8 -10
- package/dist/checkout.d.mts +0 -31
- package/dist/checkout.js +0 -115
- package/dist/checkout.js.map +0 -1
- package/dist/checkout.mjs.map +0 -1
- package/dist/chunk-2KBOKOAD.mjs +0 -177
- package/dist/chunk-2KBOKOAD.mjs.map +0 -1
- package/dist/chunk-BWKBRM2Z.mjs +0 -136
- package/dist/chunk-BWKBRM2Z.mjs.map +0 -1
- package/dist/chunk-O4BPIIQ6.mjs +0 -503
- package/dist/chunk-O4BPIIQ6.mjs.map +0 -1
- package/dist/chunk-QCTICSBE.mjs +0 -398
- package/dist/chunk-QCTICSBE.mjs.map +0 -1
- package/dist/chunk-QL5OUZGP.mjs +0 -91
- package/dist/chunk-QL5OUZGP.mjs.map +0 -1
- package/dist/chunk-WTK5HUFI.mjs +0 -1287
- package/dist/chunk-WTK5HUFI.mjs.map +0 -1
- package/dist/collections.d.mts +0 -64
- package/dist/collections.js +0 -540
- package/dist/collections.js.map +0 -1
- package/dist/collections.mjs.map +0 -1
- package/dist/index.d.mts +0 -233
- package/dist/index.js +0 -3241
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/products.d.mts +0 -63
- package/dist/products.js +0 -1206
- package/dist/products.js.map +0 -1
- package/dist/products.mjs.map +0 -1
- package/dist/store-CJVUz2Yb.d.ts +0 -608
- package/dist/store.d.mts +0 -1
- package/dist/store.js +0 -698
- package/dist/store.js.map +0 -1
- package/dist/store.mjs.map +0 -1
- package/dist/utils/rate-limit.d.mts +0 -25
- package/dist/utils/rate-limit.js +0 -203
- package/dist/utils/rate-limit.js.map +0 -1
- package/dist/utils/rate-limit.mjs.map +0 -1
package/dist/products.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { y as CurrencyCode, c as Product, P as ProductClassification, S as SEOContent, b as ShopifyProduct, d as ShopifySingleProduct, g as StoreInfo } from './store-
|
|
1
|
+
import { y as CurrencyCode, c as Product, P as ProductClassification, S as SEOContent, b as ShopifyProduct, d as ShopifySingleProduct, g as StoreInfo } from './store-iQARl6J3.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Interface for product operations
|
package/dist/products.mjs
CHANGED
|
@@ -1,9 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
createProductOperations
|
|
3
|
-
} from "./chunk-WTK5HUFI.mjs";
|
|
4
|
-
import "./chunk-BWKBRM2Z.mjs";
|
|
5
|
-
import "./chunk-2KBOKOAD.mjs";
|
|
6
|
-
export {
|
|
7
|
-
createProductOperations
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=products.mjs.map
|
|
1
|
+
export{a as createProductOperations}from'./chunk-EJO5U4BT.mjs';import'./chunk-KYLPIEU3.mjs';import'./chunk-MB2INNNP.mjs';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Type definitions for the shop-
|
|
2
|
+
* @fileoverview Type definitions for the shop-client package.
|
|
3
3
|
*
|
|
4
|
-
* This file contains all TypeScript type definitions used throughout the shop-
|
|
4
|
+
* This file contains all TypeScript type definitions used throughout the shop-client library,
|
|
5
5
|
* including Shopify API response types, normalized product/collection types, and utility types.
|
|
6
6
|
*
|
|
7
|
-
* @author shop-
|
|
7
|
+
* @author shop-client
|
|
8
8
|
*/
|
|
9
9
|
type RequireAtLeastOne<T> = {
|
|
10
10
|
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
|
package/dist/store.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { g as StoreInfo, a as StoreOperations, T as createStoreOperations } from './store-
|
|
1
|
+
export { g as StoreInfo, a as StoreOperations, T as createStoreOperations } from './store-iQARl6J3.js';
|
package/dist/store.mjs
CHANGED
|
@@ -1,9 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
createStoreOperations
|
|
3
|
-
} from "./chunk-O4BPIIQ6.mjs";
|
|
4
|
-
import "./chunk-BWKBRM2Z.mjs";
|
|
5
|
-
import "./chunk-2KBOKOAD.mjs";
|
|
6
|
-
export {
|
|
7
|
-
createStoreOperations
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=store.mjs.map
|
|
1
|
+
export{c as createStoreOperations}from'./chunk-MI7754VX.mjs';import'./chunk-KYLPIEU3.mjs';import'./chunk-MB2INNNP.mjs';
|
|
@@ -5,6 +5,11 @@ interface RateLimitOptions {
|
|
|
5
5
|
}
|
|
6
6
|
type RateLimitedRequestInit = RequestInit & {
|
|
7
7
|
rateLimitClass?: string;
|
|
8
|
+
retry?: {
|
|
9
|
+
maxRetries?: number;
|
|
10
|
+
baseDelayMs?: number;
|
|
11
|
+
retryOnStatuses?: number[];
|
|
12
|
+
};
|
|
8
13
|
};
|
|
9
14
|
declare function configureRateLimit(options: Partial<RateLimitOptions & {
|
|
10
15
|
enabled: boolean;
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
configureRateLimit,
|
|
3
|
-
getRateLimitStatus,
|
|
4
|
-
rateLimitedFetch
|
|
5
|
-
} from "../chunk-2KBOKOAD.mjs";
|
|
6
|
-
export {
|
|
7
|
-
configureRateLimit,
|
|
8
|
-
getRateLimitStatus,
|
|
9
|
-
rateLimitedFetch
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=rate-limit.mjs.map
|
|
1
|
+
export{a as configureRateLimit,c as getRateLimitStatus,b as rateLimitedFetch}from'../chunk-MB2INNNP.mjs';
|
package/package.json
CHANGED
|
@@ -1,39 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shop-client",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"
|
|
3
|
+
"version": "3.9.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.mjs",
|
|
5
6
|
"module": "./dist/index.mjs",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
|
-
"require": "./dist/index.js",
|
|
10
10
|
"import": "./dist/index.mjs",
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
12
|
},
|
|
13
13
|
"./products": {
|
|
14
|
-
"require": "./dist/products.js",
|
|
15
14
|
"import": "./dist/products.mjs",
|
|
16
15
|
"types": "./dist/products.d.ts"
|
|
17
16
|
},
|
|
18
17
|
"./collections": {
|
|
19
|
-
"require": "./dist/collections.js",
|
|
20
18
|
"import": "./dist/collections.mjs",
|
|
21
19
|
"types": "./dist/collections.d.ts"
|
|
22
20
|
},
|
|
23
21
|
"./checkout": {
|
|
24
|
-
"require": "./dist/checkout.js",
|
|
25
22
|
"import": "./dist/checkout.mjs",
|
|
26
23
|
"types": "./dist/checkout.d.ts"
|
|
27
24
|
},
|
|
28
25
|
"./store": {
|
|
29
|
-
"require": "./dist/store.js",
|
|
30
26
|
"import": "./dist/store.mjs",
|
|
31
27
|
"types": "./dist/store.d.ts"
|
|
32
28
|
},
|
|
33
29
|
"./rate-limit": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"types": "./dist/rate-limit.d.ts"
|
|
30
|
+
"import": "./dist/utils/rate-limit.mjs",
|
|
31
|
+
"types": "./dist/utils/rate-limit.d.ts"
|
|
37
32
|
}
|
|
38
33
|
},
|
|
39
34
|
"sideEffects": false,
|
|
@@ -50,6 +45,7 @@
|
|
|
50
45
|
"dev": "tsup --watch",
|
|
51
46
|
"test": "bun test",
|
|
52
47
|
"test:ci": "bun test --coverage",
|
|
48
|
+
"docs:build": "typedoc",
|
|
53
49
|
"semantic-release": "semantic-release",
|
|
54
50
|
"format": "biome format --write --no-errors-on-unmatched",
|
|
55
51
|
"lint": "biome lint --diagnostic-level=error --no-errors-on-unmatched . && tsc --noEmit -p tsconfig.json",
|
|
@@ -92,6 +88,8 @@
|
|
|
92
88
|
"husky": "^9.1.7",
|
|
93
89
|
"lint-staged": "^16.2.7",
|
|
94
90
|
"semantic-release": "^25.0.2",
|
|
91
|
+
"typedoc": "^0.25.7",
|
|
92
|
+
"typedoc-plugin-markdown": "^4.0.0",
|
|
95
93
|
"tsup": "^8.5.1",
|
|
96
94
|
"typescript": "^5.9.3"
|
|
97
95
|
},
|
package/dist/checkout.d.mts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interface for checkout operations
|
|
3
|
-
*/
|
|
4
|
-
interface CheckoutOperations {
|
|
5
|
-
/**
|
|
6
|
-
* Creates a Shopify checkout URL with pre-filled customer information and cart items.
|
|
7
|
-
*/
|
|
8
|
-
createUrl(params: {
|
|
9
|
-
email: string;
|
|
10
|
-
items: Array<{
|
|
11
|
-
productVariantId: string;
|
|
12
|
-
quantity: string;
|
|
13
|
-
}>;
|
|
14
|
-
address: {
|
|
15
|
-
firstName: string;
|
|
16
|
-
lastName: string;
|
|
17
|
-
address1: string;
|
|
18
|
-
city: string;
|
|
19
|
-
zip: string;
|
|
20
|
-
country: string;
|
|
21
|
-
province: string;
|
|
22
|
-
phone: string;
|
|
23
|
-
};
|
|
24
|
-
}): string;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Creates checkout operations for a store instance
|
|
28
|
-
*/
|
|
29
|
-
declare function createCheckoutOperations(baseUrl: string): CheckoutOperations;
|
|
30
|
-
|
|
31
|
-
export { type CheckoutOperations, createCheckoutOperations };
|
package/dist/checkout.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/checkout.ts
|
|
21
|
-
var checkout_exports = {};
|
|
22
|
-
__export(checkout_exports, {
|
|
23
|
-
createCheckoutOperations: () => createCheckoutOperations
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(checkout_exports);
|
|
26
|
-
function createCheckoutOperations(baseUrl) {
|
|
27
|
-
return {
|
|
28
|
-
/**
|
|
29
|
-
* Creates a Shopify checkout URL with pre-filled customer information and cart items.
|
|
30
|
-
*
|
|
31
|
-
* @param params - Checkout parameters
|
|
32
|
-
* @param params.email - Customer's email address (must be valid email format)
|
|
33
|
-
* @param params.items - Array of products to add to cart
|
|
34
|
-
* @param params.items[].productVariantId - Shopify product variant ID
|
|
35
|
-
* @param params.items[].quantity - Quantity as string (must be positive number)
|
|
36
|
-
* @param params.address - Customer's shipping address
|
|
37
|
-
* @param params.address.firstName - Customer's first name
|
|
38
|
-
* @param params.address.lastName - Customer's last name
|
|
39
|
-
* @param params.address.address1 - Street address
|
|
40
|
-
* @param params.address.city - City name
|
|
41
|
-
* @param params.address.zip - Postal/ZIP code
|
|
42
|
-
* @param params.address.country - Country name
|
|
43
|
-
* @param params.address.province - State/Province name
|
|
44
|
-
* @param params.address.phone - Phone number
|
|
45
|
-
*
|
|
46
|
-
* @returns {string} Complete Shopify checkout URL with pre-filled information
|
|
47
|
-
*
|
|
48
|
-
* @throws {Error} When email is invalid, items array is empty, or required address fields are missing
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```typescript
|
|
52
|
-
* const shop = new ShopClient('https://exampleshop.com');
|
|
53
|
-
* const checkoutUrl = await shop.checkout.create([
|
|
54
|
-
* { variantId: '123', quantity: 2 },
|
|
55
|
-
* { variantId: '456', quantity: 1 }
|
|
56
|
-
* ]);
|
|
57
|
-
* console.log(checkoutUrl);
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
createUrl: ({
|
|
61
|
-
email,
|
|
62
|
-
items,
|
|
63
|
-
address
|
|
64
|
-
}) => {
|
|
65
|
-
if (!email || !email.includes("@")) {
|
|
66
|
-
throw new Error("Invalid email address");
|
|
67
|
-
}
|
|
68
|
-
if (!items || items.length === 0) {
|
|
69
|
-
throw new Error("Items array cannot be empty");
|
|
70
|
-
}
|
|
71
|
-
for (const item of items) {
|
|
72
|
-
if (!item.productVariantId || !item.quantity) {
|
|
73
|
-
throw new Error("Each item must have productVariantId and quantity");
|
|
74
|
-
}
|
|
75
|
-
const qty = Number.parseInt(item.quantity, 10);
|
|
76
|
-
if (Number.isNaN(qty) || qty <= 0) {
|
|
77
|
-
throw new Error("Quantity must be a positive number");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
const requiredFields = [
|
|
81
|
-
"firstName",
|
|
82
|
-
"lastName",
|
|
83
|
-
"address1",
|
|
84
|
-
"city",
|
|
85
|
-
"zip",
|
|
86
|
-
"country"
|
|
87
|
-
];
|
|
88
|
-
for (const field of requiredFields) {
|
|
89
|
-
if (!address[field]) {
|
|
90
|
-
throw new Error(`Address field '${field}' is required`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const cartPath = items.map(
|
|
94
|
-
(item) => `${encodeURIComponent(item.productVariantId)}:${encodeURIComponent(item.quantity)}`
|
|
95
|
-
).join(",");
|
|
96
|
-
const params = new URLSearchParams({
|
|
97
|
-
"checkout[email]": email,
|
|
98
|
-
"checkout[shipping_address][first_name]": address.firstName,
|
|
99
|
-
"checkout[shipping_address][last_name]": address.lastName,
|
|
100
|
-
"checkout[shipping_address][address1]": address.address1,
|
|
101
|
-
"checkout[shipping_address][city]": address.city,
|
|
102
|
-
"checkout[shipping_address][zip]": address.zip,
|
|
103
|
-
"checkout[shipping_address][country]": address.country,
|
|
104
|
-
"checkout[shipping_address][province]": address.province,
|
|
105
|
-
"checkout[shipping_address][phone]": address.phone
|
|
106
|
-
});
|
|
107
|
-
return `${baseUrl}cart/${cartPath}?${params.toString()}`;
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
112
|
-
0 && (module.exports = {
|
|
113
|
-
createCheckoutOperations
|
|
114
|
-
});
|
|
115
|
-
//# sourceMappingURL=checkout.js.map
|
package/dist/checkout.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/checkout.ts"],"sourcesContent":["/**\n * Interface for checkout operations\n */\nexport interface CheckoutOperations {\n /**\n * Creates a Shopify checkout URL with pre-filled customer information and cart items.\n */\n createUrl(params: {\n email: string;\n items: Array<{ productVariantId: string; quantity: string }>;\n address: {\n firstName: string;\n lastName: string;\n address1: string;\n city: string;\n zip: string;\n country: string;\n province: string;\n phone: string;\n };\n }): string;\n}\n\n/**\n * Creates checkout operations for a store instance\n */\nexport function createCheckoutOperations(baseUrl: string): CheckoutOperations {\n return {\n /**\n * Creates a Shopify checkout URL with pre-filled customer information and cart items.\n *\n * @param params - Checkout parameters\n * @param params.email - Customer's email address (must be valid email format)\n * @param params.items - Array of products to add to cart\n * @param params.items[].productVariantId - Shopify product variant ID\n * @param params.items[].quantity - Quantity as string (must be positive number)\n * @param params.address - Customer's shipping address\n * @param params.address.firstName - Customer's first name\n * @param params.address.lastName - Customer's last name\n * @param params.address.address1 - Street address\n * @param params.address.city - City name\n * @param params.address.zip - Postal/ZIP code\n * @param params.address.country - Country name\n * @param params.address.province - State/Province name\n * @param params.address.phone - Phone number\n *\n * @returns {string} Complete Shopify checkout URL with pre-filled information\n *\n * @throws {Error} When email is invalid, items array is empty, or required address fields are missing\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const checkoutUrl = await shop.checkout.create([\n * { variantId: '123', quantity: 2 },\n * { variantId: '456', quantity: 1 }\n * ]);\n * console.log(checkoutUrl);\n * ```\n */\n createUrl: ({\n email,\n items,\n address,\n }: {\n email: string;\n items: Array<{ productVariantId: string; quantity: string }>;\n address: {\n firstName: string;\n lastName: string;\n address1: string;\n city: string;\n zip: string;\n country: string;\n province: string;\n phone: string;\n };\n }) => {\n // Validate and sanitize inputs\n if (!email || !email.includes(\"@\")) {\n throw new Error(\"Invalid email address\");\n }\n\n if (!items || items.length === 0) {\n throw new Error(\"Items array cannot be empty\");\n }\n\n // Validate items\n for (const item of items) {\n if (!item.productVariantId || !item.quantity) {\n throw new Error(\"Each item must have productVariantId and quantity\");\n }\n // Ensure quantity is a positive number\n const qty = Number.parseInt(item.quantity, 10);\n\n if (Number.isNaN(qty) || qty <= 0) {\n throw new Error(\"Quantity must be a positive number\");\n }\n }\n\n // Validate required address fields\n const requiredFields = [\n \"firstName\",\n \"lastName\",\n \"address1\",\n \"city\",\n \"zip\",\n \"country\",\n ];\n\n for (const field of requiredFields) {\n if (!address[field as keyof typeof address]) {\n throw new Error(`Address field '${field}' is required`);\n }\n }\n\n // Properly encode all URL parameters to prevent injection attacks\n const cartPath = items\n .map(\n (item) =>\n `${encodeURIComponent(item.productVariantId)}:${encodeURIComponent(item.quantity)}`\n )\n .join(\",\");\n\n const params = new URLSearchParams({\n \"checkout[email]\": email,\n \"checkout[shipping_address][first_name]\": address.firstName,\n \"checkout[shipping_address][last_name]\": address.lastName,\n \"checkout[shipping_address][address1]\": address.address1,\n \"checkout[shipping_address][city]\": address.city,\n \"checkout[shipping_address][zip]\": address.zip,\n \"checkout[shipping_address][country]\": address.country,\n \"checkout[shipping_address][province]\": address.province,\n \"checkout[shipping_address][phone]\": address.phone,\n });\n\n return `${baseUrl}cart/${cartPath}?${params.toString()}`;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BO,SAAS,yBAAyB,SAAqC;AAC5E,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiCL,WAAW,CAAC;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,IACF,MAaM;AAEJ,UAAI,CAAC,SAAS,CAAC,MAAM,SAAS,GAAG,GAAG;AAClC,cAAM,IAAI,MAAM,uBAAuB;AAAA,MACzC;AAEA,UAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC/C;AAGA,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,KAAK,oBAAoB,CAAC,KAAK,UAAU;AAC5C,gBAAM,IAAI,MAAM,mDAAmD;AAAA,QACrE;AAEA,cAAM,MAAM,OAAO,SAAS,KAAK,UAAU,EAAE;AAE7C,YAAI,OAAO,MAAM,GAAG,KAAK,OAAO,GAAG;AACjC,gBAAM,IAAI,MAAM,oCAAoC;AAAA,QACtD;AAAA,MACF;AAGA,YAAM,iBAAiB;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,iBAAW,SAAS,gBAAgB;AAClC,YAAI,CAAC,QAAQ,KAA6B,GAAG;AAC3C,gBAAM,IAAI,MAAM,kBAAkB,KAAK,eAAe;AAAA,QACxD;AAAA,MACF;AAGA,YAAM,WAAW,MACd;AAAA,QACC,CAAC,SACC,GAAG,mBAAmB,KAAK,gBAAgB,CAAC,IAAI,mBAAmB,KAAK,QAAQ,CAAC;AAAA,MACrF,EACC,KAAK,GAAG;AAEX,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC,mBAAmB;AAAA,QACnB,0CAA0C,QAAQ;AAAA,QAClD,yCAAyC,QAAQ;AAAA,QACjD,wCAAwC,QAAQ;AAAA,QAChD,oCAAoC,QAAQ;AAAA,QAC5C,mCAAmC,QAAQ;AAAA,QAC3C,uCAAuC,QAAQ;AAAA,QAC/C,wCAAwC,QAAQ;AAAA,QAChD,qCAAqC,QAAQ;AAAA,MAC/C,CAAC;AAED,aAAO,GAAG,OAAO,QAAQ,QAAQ,IAAI,OAAO,SAAS,CAAC;AAAA,IACxD;AAAA,EACF;AACF;","names":[]}
|
package/dist/checkout.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/chunk-2KBOKOAD.mjs
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
// src/utils/rate-limit.ts
|
|
2
|
-
var RateLimiter = class {
|
|
3
|
-
constructor(options) {
|
|
4
|
-
this.queue = [];
|
|
5
|
-
this.inFlight = 0;
|
|
6
|
-
this.refillTimer = null;
|
|
7
|
-
this.options = options;
|
|
8
|
-
this.tokens = options.maxRequestsPerInterval;
|
|
9
|
-
}
|
|
10
|
-
startRefill() {
|
|
11
|
-
if (this.refillTimer) return;
|
|
12
|
-
this.refillTimer = setInterval(() => {
|
|
13
|
-
this.tokens = this.options.maxRequestsPerInterval;
|
|
14
|
-
this.tryRun();
|
|
15
|
-
}, this.options.intervalMs);
|
|
16
|
-
if (this.refillTimer && typeof this.refillTimer.unref === "function") {
|
|
17
|
-
this.refillTimer.unref();
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
stopRefill() {
|
|
21
|
-
if (this.refillTimer) {
|
|
22
|
-
clearInterval(this.refillTimer);
|
|
23
|
-
this.refillTimer = null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
ensureRefillStarted() {
|
|
27
|
-
if (!this.refillTimer) {
|
|
28
|
-
this.startRefill();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
configure(next) {
|
|
32
|
-
this.options = { ...this.options, ...next };
|
|
33
|
-
this.options.maxRequestsPerInterval = Math.max(
|
|
34
|
-
1,
|
|
35
|
-
this.options.maxRequestsPerInterval
|
|
36
|
-
);
|
|
37
|
-
this.options.intervalMs = Math.max(10, this.options.intervalMs);
|
|
38
|
-
this.options.maxConcurrency = Math.max(1, this.options.maxConcurrency);
|
|
39
|
-
}
|
|
40
|
-
schedule(fn) {
|
|
41
|
-
return new Promise((resolve, reject) => {
|
|
42
|
-
this.ensureRefillStarted();
|
|
43
|
-
this.queue.push({ fn, resolve, reject });
|
|
44
|
-
this.tryRun();
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
tryRun() {
|
|
48
|
-
while (this.queue.length > 0 && this.inFlight < this.options.maxConcurrency && this.tokens > 0) {
|
|
49
|
-
const task = this.queue.shift();
|
|
50
|
-
this.tokens -= 1;
|
|
51
|
-
this.inFlight += 1;
|
|
52
|
-
Promise.resolve().then(task.fn).then((result) => task.resolve(result)).catch((err) => task.reject(err)).finally(() => {
|
|
53
|
-
this.inFlight -= 1;
|
|
54
|
-
setTimeout(() => this.tryRun(), 0);
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
var enabled = false;
|
|
60
|
-
var defaultOptions = {
|
|
61
|
-
maxRequestsPerInterval: 5,
|
|
62
|
-
// 5 requests
|
|
63
|
-
intervalMs: 1e3,
|
|
64
|
-
// per second
|
|
65
|
-
maxConcurrency: 5
|
|
66
|
-
// up to 5 in parallel
|
|
67
|
-
};
|
|
68
|
-
var limiter = new RateLimiter(defaultOptions);
|
|
69
|
-
var hostLimiters = /* @__PURE__ */ new Map();
|
|
70
|
-
var classLimiters = /* @__PURE__ */ new Map();
|
|
71
|
-
function getHost(input) {
|
|
72
|
-
try {
|
|
73
|
-
if (typeof input === "string") {
|
|
74
|
-
return new URL(input).host;
|
|
75
|
-
}
|
|
76
|
-
if (input instanceof URL) {
|
|
77
|
-
return input.host;
|
|
78
|
-
}
|
|
79
|
-
const url = input.url;
|
|
80
|
-
if (url) {
|
|
81
|
-
return new URL(url).host;
|
|
82
|
-
}
|
|
83
|
-
} catch {
|
|
84
|
-
}
|
|
85
|
-
return void 0;
|
|
86
|
-
}
|
|
87
|
-
function getHostLimiter(host) {
|
|
88
|
-
if (!host) return void 0;
|
|
89
|
-
const exact = hostLimiters.get(host);
|
|
90
|
-
if (exact) return exact;
|
|
91
|
-
for (const [key, lim] of hostLimiters.entries()) {
|
|
92
|
-
if (key.startsWith("*.") && host.endsWith(key.slice(2))) {
|
|
93
|
-
return lim;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return void 0;
|
|
97
|
-
}
|
|
98
|
-
function configureRateLimit(options) {
|
|
99
|
-
var _a, _b, _c, _d, _e, _f;
|
|
100
|
-
if (typeof options.enabled === "boolean") {
|
|
101
|
-
enabled = options.enabled;
|
|
102
|
-
}
|
|
103
|
-
const { perHost, perClass } = options;
|
|
104
|
-
const globalOpts = {};
|
|
105
|
-
if (typeof options.maxRequestsPerInterval === "number") {
|
|
106
|
-
globalOpts.maxRequestsPerInterval = options.maxRequestsPerInterval;
|
|
107
|
-
}
|
|
108
|
-
if (typeof options.intervalMs === "number") {
|
|
109
|
-
globalOpts.intervalMs = options.intervalMs;
|
|
110
|
-
}
|
|
111
|
-
if (typeof options.maxConcurrency === "number") {
|
|
112
|
-
globalOpts.maxConcurrency = options.maxConcurrency;
|
|
113
|
-
}
|
|
114
|
-
if (Object.keys(globalOpts).length) {
|
|
115
|
-
limiter.configure(globalOpts);
|
|
116
|
-
}
|
|
117
|
-
if (perHost) {
|
|
118
|
-
for (const host of Object.keys(perHost)) {
|
|
119
|
-
const opts = perHost[host];
|
|
120
|
-
const existing = hostLimiters.get(host);
|
|
121
|
-
if (existing) {
|
|
122
|
-
existing.configure(opts);
|
|
123
|
-
} else {
|
|
124
|
-
hostLimiters.set(
|
|
125
|
-
host,
|
|
126
|
-
new RateLimiter({
|
|
127
|
-
maxRequestsPerInterval: (_a = opts.maxRequestsPerInterval) != null ? _a : defaultOptions.maxRequestsPerInterval,
|
|
128
|
-
intervalMs: (_b = opts.intervalMs) != null ? _b : defaultOptions.intervalMs,
|
|
129
|
-
maxConcurrency: (_c = opts.maxConcurrency) != null ? _c : defaultOptions.maxConcurrency
|
|
130
|
-
})
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (perClass) {
|
|
136
|
-
for (const klass of Object.keys(perClass)) {
|
|
137
|
-
const opts = perClass[klass];
|
|
138
|
-
const existing = classLimiters.get(klass);
|
|
139
|
-
if (existing) {
|
|
140
|
-
existing.configure(opts);
|
|
141
|
-
} else {
|
|
142
|
-
classLimiters.set(
|
|
143
|
-
klass,
|
|
144
|
-
new RateLimiter({
|
|
145
|
-
maxRequestsPerInterval: (_d = opts.maxRequestsPerInterval) != null ? _d : defaultOptions.maxRequestsPerInterval,
|
|
146
|
-
intervalMs: (_e = opts.intervalMs) != null ? _e : defaultOptions.intervalMs,
|
|
147
|
-
maxConcurrency: (_f = opts.maxConcurrency) != null ? _f : defaultOptions.maxConcurrency
|
|
148
|
-
})
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
async function rateLimitedFetch(input, init) {
|
|
155
|
-
var _a;
|
|
156
|
-
if (!enabled) {
|
|
157
|
-
return fetch(input, init);
|
|
158
|
-
}
|
|
159
|
-
const klass = init == null ? void 0 : init.rateLimitClass;
|
|
160
|
-
const byClass = klass ? classLimiters.get(klass) : void 0;
|
|
161
|
-
const byHost = getHostLimiter(getHost(input));
|
|
162
|
-
const eff = (_a = byClass != null ? byClass : byHost) != null ? _a : limiter;
|
|
163
|
-
return eff.schedule(() => fetch(input, init));
|
|
164
|
-
}
|
|
165
|
-
function getRateLimitStatus() {
|
|
166
|
-
return {
|
|
167
|
-
enabled,
|
|
168
|
-
options: { ...defaultOptions }
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export {
|
|
173
|
-
configureRateLimit,
|
|
174
|
-
rateLimitedFetch,
|
|
175
|
-
getRateLimitStatus
|
|
176
|
-
};
|
|
177
|
-
//# sourceMappingURL=chunk-2KBOKOAD.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/rate-limit.ts"],"sourcesContent":["export interface RateLimitOptions {\n maxRequestsPerInterval: number; // tokens refilled every interval\n intervalMs: number; // refill period\n maxConcurrency: number; // simultaneous in-flight requests\n}\n\ntype Task<T> = {\n fn: () => Promise<T>;\n resolve: (value: T) => void;\n reject: (reason?: any) => void;\n};\n\nclass RateLimiter {\n private options: RateLimitOptions;\n private queue: Task<any>[] = [];\n private tokens: number;\n private inFlight = 0;\n private refillTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: RateLimitOptions) {\n this.options = options;\n this.tokens = options.maxRequestsPerInterval;\n }\n\n private startRefill() {\n if (this.refillTimer) return;\n this.refillTimer = setInterval(() => {\n this.tokens = this.options.maxRequestsPerInterval;\n this.tryRun();\n }, this.options.intervalMs);\n // In some runtimes, timers keep process alive; make it best-effort\n if (\n this.refillTimer &&\n typeof (this.refillTimer as any).unref === \"function\"\n ) {\n (this.refillTimer as any).unref();\n }\n }\n\n private stopRefill() {\n if (this.refillTimer) {\n clearInterval(this.refillTimer);\n this.refillTimer = null;\n }\n }\n\n private ensureRefillStarted() {\n if (!this.refillTimer) {\n this.startRefill();\n }\n }\n\n configure(next: Partial<RateLimitOptions>) {\n this.options = { ...this.options, ...next };\n // Clamp to sensible minimums\n this.options.maxRequestsPerInterval = Math.max(\n 1,\n this.options.maxRequestsPerInterval\n );\n this.options.intervalMs = Math.max(10, this.options.intervalMs);\n this.options.maxConcurrency = Math.max(1, this.options.maxConcurrency);\n }\n\n schedule<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n // Start the timer lazily on first schedule/use\n this.ensureRefillStarted();\n this.queue.push({ fn, resolve, reject });\n this.tryRun();\n });\n }\n\n private tryRun() {\n while (\n this.queue.length > 0 &&\n this.inFlight < this.options.maxConcurrency &&\n this.tokens > 0\n ) {\n const task = this.queue.shift()!;\n this.tokens -= 1;\n this.inFlight += 1;\n\n // Execute task and release slot when done\n Promise.resolve()\n .then(task.fn)\n .then((result) => task.resolve(result))\n .catch((err) => task.reject(err))\n .finally(() => {\n this.inFlight -= 1;\n // Yield to event loop, then continue draining\n setTimeout(() => this.tryRun(), 0);\n });\n }\n }\n}\n\nlet enabled = false;\nconst defaultOptions: RateLimitOptions = {\n maxRequestsPerInterval: 5, // 5 requests\n intervalMs: 1000, // per second\n maxConcurrency: 5, // up to 5 in parallel\n};\n\nconst limiter = new RateLimiter(defaultOptions);\nconst hostLimiters = new Map<string, RateLimiter>();\nconst classLimiters = new Map<string, RateLimiter>();\n\nexport type RateLimitedRequestInit = RequestInit & { rateLimitClass?: string };\n\nfunction getHost(input: RequestInfo | URL): string | undefined {\n try {\n if (typeof input === \"string\") {\n return new URL(input).host;\n }\n if (input instanceof URL) {\n return input.host;\n }\n // Request object or similar\n const url = (input as any).url as string | undefined;\n if (url) {\n return new URL(url).host;\n }\n } catch {\n // ignore parsing errors\n }\n return undefined;\n}\n\nfunction getHostLimiter(host?: string): RateLimiter | undefined {\n if (!host) return undefined;\n // Exact match first\n const exact = hostLimiters.get(host);\n if (exact) return exact;\n // Wildcard suffix match: keys of the form '*.example.com'\n for (const [key, lim] of hostLimiters.entries()) {\n if (key.startsWith(\"*.\") && host.endsWith(key.slice(2))) {\n return lim;\n }\n }\n return undefined;\n}\n\nexport function configureRateLimit(\n options: Partial<RateLimitOptions & { enabled: boolean }> & {\n perHost?: Record<string, Partial<RateLimitOptions>>; // key: host (supports '*.example.com')\n perClass?: Record<string, Partial<RateLimitOptions>>; // key: arbitrary class name\n }\n) {\n if (typeof options.enabled === \"boolean\") {\n enabled = options.enabled;\n }\n const { perHost, perClass } = options;\n const globalOpts: Partial<RateLimitOptions> = {};\n if (typeof options.maxRequestsPerInterval === \"number\") {\n globalOpts.maxRequestsPerInterval = options.maxRequestsPerInterval;\n }\n if (typeof options.intervalMs === \"number\") {\n globalOpts.intervalMs = options.intervalMs;\n }\n if (typeof options.maxConcurrency === \"number\") {\n globalOpts.maxConcurrency = options.maxConcurrency;\n }\n if (Object.keys(globalOpts).length) {\n limiter.configure(globalOpts);\n }\n if (perHost) {\n for (const host of Object.keys(perHost)) {\n const opts = perHost[host]!;\n const existing = hostLimiters.get(host);\n if (existing) {\n existing.configure(opts);\n } else {\n hostLimiters.set(\n host,\n new RateLimiter({\n maxRequestsPerInterval:\n opts.maxRequestsPerInterval ??\n defaultOptions.maxRequestsPerInterval,\n intervalMs: opts.intervalMs ?? defaultOptions.intervalMs,\n maxConcurrency:\n opts.maxConcurrency ?? defaultOptions.maxConcurrency,\n })\n );\n }\n }\n }\n if (perClass) {\n for (const klass of Object.keys(perClass)) {\n const opts = perClass[klass]!;\n const existing = classLimiters.get(klass);\n if (existing) {\n existing.configure(opts);\n } else {\n classLimiters.set(\n klass,\n new RateLimiter({\n maxRequestsPerInterval:\n opts.maxRequestsPerInterval ??\n defaultOptions.maxRequestsPerInterval,\n intervalMs: opts.intervalMs ?? defaultOptions.intervalMs,\n maxConcurrency:\n opts.maxConcurrency ?? defaultOptions.maxConcurrency,\n })\n );\n }\n }\n }\n}\n\nexport async function rateLimitedFetch(\n input: RequestInfo | URL,\n init?: RateLimitedRequestInit\n): Promise<Response> {\n if (!enabled) {\n return fetch(input as any, init);\n }\n const klass = init?.rateLimitClass;\n const byClass = klass ? classLimiters.get(klass) : undefined;\n const byHost = getHostLimiter(getHost(input));\n const eff = byClass ?? byHost ?? limiter;\n return eff.schedule(() => fetch(input as any, init));\n}\n\nexport function getRateLimitStatus() {\n return {\n enabled,\n options: { ...defaultOptions },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-BWKBRM2Z.mjs
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
// src/utils/func.ts
|
|
2
|
-
import { parse } from "tldts";
|
|
3
|
-
function extractDomainWithoutSuffix(domain) {
|
|
4
|
-
const parsedDomain = parse(domain);
|
|
5
|
-
return parsedDomain.domainWithoutSuffix;
|
|
6
|
-
}
|
|
7
|
-
function generateStoreSlug(domain) {
|
|
8
|
-
var _a;
|
|
9
|
-
const input = new URL(domain);
|
|
10
|
-
const parsedDomain = parse(input.href);
|
|
11
|
-
const domainName = (_a = parsedDomain.domainWithoutSuffix) != null ? _a : input.hostname.split(".")[0];
|
|
12
|
-
return (domainName || "").toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
13
|
-
}
|
|
14
|
-
var genProductSlug = ({
|
|
15
|
-
handle,
|
|
16
|
-
storeDomain
|
|
17
|
-
}) => {
|
|
18
|
-
const storeSlug = generateStoreSlug(storeDomain);
|
|
19
|
-
return `${handle}-by-${storeSlug}`;
|
|
20
|
-
};
|
|
21
|
-
var calculateDiscount = (price, compareAtPrice) => !compareAtPrice || compareAtPrice === 0 ? 0 : Math.max(
|
|
22
|
-
0,
|
|
23
|
-
Math.round(100 - price / compareAtPrice * 100)
|
|
24
|
-
// Removed the decimal precision
|
|
25
|
-
);
|
|
26
|
-
function sanitizeDomain(input, opts) {
|
|
27
|
-
var _a;
|
|
28
|
-
if (typeof input !== "string") {
|
|
29
|
-
throw new Error("sanitizeDomain: input must be a string");
|
|
30
|
-
}
|
|
31
|
-
let raw = input.trim();
|
|
32
|
-
if (!raw) {
|
|
33
|
-
throw new Error("sanitizeDomain: input cannot be empty");
|
|
34
|
-
}
|
|
35
|
-
const hasProtocol = /^[a-z]+:\/\//i.test(raw);
|
|
36
|
-
if (!hasProtocol && !raw.startsWith("//")) {
|
|
37
|
-
raw = `https://${raw}`;
|
|
38
|
-
}
|
|
39
|
-
const stripWWW = (_a = opts == null ? void 0 : opts.stripWWW) != null ? _a : true;
|
|
40
|
-
try {
|
|
41
|
-
let url;
|
|
42
|
-
if (raw.startsWith("//")) {
|
|
43
|
-
url = new URL(`https:${raw}`);
|
|
44
|
-
} else if (raw.includes("://")) {
|
|
45
|
-
url = new URL(raw);
|
|
46
|
-
} else {
|
|
47
|
-
url = new URL(`https://${raw}`);
|
|
48
|
-
}
|
|
49
|
-
let hostname = url.hostname.toLowerCase();
|
|
50
|
-
const hadWWW = /^www\./i.test(url.hostname);
|
|
51
|
-
if (stripWWW) hostname = hostname.replace(/^www\./, "");
|
|
52
|
-
if (!hostname.includes(".")) {
|
|
53
|
-
throw new Error("sanitizeDomain: invalid domain (missing suffix)");
|
|
54
|
-
}
|
|
55
|
-
const parsed = parse(hostname);
|
|
56
|
-
if (!parsed.publicSuffix || parsed.isIcann === false) {
|
|
57
|
-
throw new Error("sanitizeDomain: invalid domain (missing suffix)");
|
|
58
|
-
}
|
|
59
|
-
if (!stripWWW && hadWWW) {
|
|
60
|
-
return `www.${parsed.domain || hostname}`;
|
|
61
|
-
}
|
|
62
|
-
return parsed.domain || hostname;
|
|
63
|
-
} catch {
|
|
64
|
-
let hostname = raw.toLowerCase();
|
|
65
|
-
hostname = hostname.replace(/^[a-z]+:\/\//, "");
|
|
66
|
-
hostname = hostname.replace(/^\/\//, "");
|
|
67
|
-
hostname = hostname.replace(/[/:#?].*$/, "");
|
|
68
|
-
const hadWWW = /^www\./i.test(hostname);
|
|
69
|
-
if (stripWWW) hostname = hostname.replace(/^www\./, "");
|
|
70
|
-
if (!hostname.includes(".")) {
|
|
71
|
-
throw new Error("sanitizeDomain: invalid domain (missing suffix)");
|
|
72
|
-
}
|
|
73
|
-
const parsed = parse(hostname);
|
|
74
|
-
if (!parsed.publicSuffix || parsed.isIcann === false) {
|
|
75
|
-
throw new Error("sanitizeDomain: invalid domain (missing suffix)");
|
|
76
|
-
}
|
|
77
|
-
if (!stripWWW && hadWWW) {
|
|
78
|
-
return `www.${parsed.domain || hostname}`;
|
|
79
|
-
}
|
|
80
|
-
return parsed.domain || hostname;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function safeParseDate(input) {
|
|
84
|
-
if (!input || typeof input !== "string") return void 0;
|
|
85
|
-
const d = new Date(input);
|
|
86
|
-
return Number.isNaN(d.getTime()) ? void 0 : d;
|
|
87
|
-
}
|
|
88
|
-
function normalizeKey(input) {
|
|
89
|
-
return input.toLowerCase().replace(/\s+/g, "_");
|
|
90
|
-
}
|
|
91
|
-
function buildVariantOptionsMap(optionNames, variants) {
|
|
92
|
-
const keys = optionNames.map(normalizeKey);
|
|
93
|
-
const map = {};
|
|
94
|
-
for (const v of variants) {
|
|
95
|
-
const parts = [];
|
|
96
|
-
if (keys[0] && v.option1)
|
|
97
|
-
parts.push(`${keys[0]}#${normalizeKey(v.option1)}`);
|
|
98
|
-
if (keys[1] && v.option2)
|
|
99
|
-
parts.push(`${keys[1]}#${normalizeKey(v.option2)}`);
|
|
100
|
-
if (keys[2] && v.option3)
|
|
101
|
-
parts.push(`${keys[2]}#${normalizeKey(v.option3)}`);
|
|
102
|
-
if (parts.length > 0) {
|
|
103
|
-
if (parts.length > 1) parts.sort();
|
|
104
|
-
const key = parts.join("##");
|
|
105
|
-
const id = v.id.toString();
|
|
106
|
-
if (map[key] === void 0) {
|
|
107
|
-
map[key] = id;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return map;
|
|
112
|
-
}
|
|
113
|
-
function formatPrice(amountInCents, currency) {
|
|
114
|
-
try {
|
|
115
|
-
return new Intl.NumberFormat(void 0, {
|
|
116
|
-
style: "currency",
|
|
117
|
-
currency
|
|
118
|
-
}).format((amountInCents || 0) / 100);
|
|
119
|
-
} catch {
|
|
120
|
-
const val = (amountInCents || 0) / 100;
|
|
121
|
-
return `${val} ${currency}`;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export {
|
|
126
|
-
extractDomainWithoutSuffix,
|
|
127
|
-
generateStoreSlug,
|
|
128
|
-
genProductSlug,
|
|
129
|
-
calculateDiscount,
|
|
130
|
-
sanitizeDomain,
|
|
131
|
-
safeParseDate,
|
|
132
|
-
normalizeKey,
|
|
133
|
-
buildVariantOptionsMap,
|
|
134
|
-
formatPrice
|
|
135
|
-
};
|
|
136
|
-
//# sourceMappingURL=chunk-BWKBRM2Z.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/func.ts"],"sourcesContent":["import { parse } from \"tldts\";\nimport type { CurrencyCode } from \"../types\";\n\nexport function extractDomainWithoutSuffix(domain: string) {\n const parsedDomain = parse(domain);\n return parsedDomain.domainWithoutSuffix;\n}\n\nexport function generateStoreSlug(domain: string): string {\n const input = new URL(domain);\n const parsedDomain = parse(input.href);\n const domainName =\n parsedDomain.domainWithoutSuffix ?? input.hostname.split(\".\")[0];\n\n return (domainName || \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nexport const genProductSlug = ({\n handle,\n storeDomain,\n}: {\n handle: string;\n storeDomain: string;\n}) => {\n const storeSlug = generateStoreSlug(storeDomain);\n return `${handle}-by-${storeSlug}`;\n};\n\nexport const calculateDiscount = (\n price: number,\n compareAtPrice?: number\n): number =>\n !compareAtPrice || compareAtPrice === 0\n ? 0\n : Math.max(\n 0,\n Math.round(100 - (price / compareAtPrice) * 100) // Removed the decimal precision\n );\n\n/**\n * Normalize and sanitize a domain string.\n *\n * Accepts inputs like full URLs, protocol-relative URLs, bare hostnames,\n * or strings with paths/query/fragment, and returns a normalized domain.\n *\n * Examples:\n * - \"https://WWW.Example.com/path\" -> \"example.com\"\n * - \"//sub.example.co.uk\" -> \"example.co.uk\"\n * - \"www.example.com:8080\" -> \"example.com\"\n * - \"example\" -> \"example\"\n */\nexport function sanitizeDomain(\n input: string,\n opts?: { stripWWW?: boolean }\n): string {\n if (typeof input !== \"string\") {\n throw new Error(\"sanitizeDomain: input must be a string\");\n }\n let raw = input.trim();\n if (!raw) {\n throw new Error(\"sanitizeDomain: input cannot be empty\");\n }\n // Only add protocol if it's missing and not protocol-relative\n const hasProtocol = /^[a-z]+:\\/\\//i.test(raw);\n if (!hasProtocol && !raw.startsWith(\"//\")) {\n raw = `https://${raw}`;\n }\n\n const stripWWW = opts?.stripWWW ?? true;\n\n try {\n let url: URL;\n if (raw.startsWith(\"//\")) {\n url = new URL(`https:${raw}`);\n } else if (raw.includes(\"://\")) {\n url = new URL(raw);\n } else {\n url = new URL(`https://${raw}`);\n }\n let hostname = url.hostname.toLowerCase();\n const hadWWW = /^www\\./i.test(url.hostname);\n if (stripWWW) hostname = hostname.replace(/^www\\./, \"\");\n if (!hostname.includes(\".\")) {\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n const parsed = parse(hostname);\n if (!parsed.publicSuffix || parsed.isIcann === false) {\n // Require a valid public suffix (e.g., TLD); reject bare hostnames\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n if (!stripWWW && hadWWW) {\n return `www.${parsed.domain || hostname}`;\n }\n return parsed.domain || hostname;\n } catch {\n // Fallback: attempt to sanitize without URL parsing\n let hostname = raw.toLowerCase();\n hostname = hostname.replace(/^[a-z]+:\\/\\//, \"\"); // remove protocol if present\n hostname = hostname.replace(/^\\/\\//, \"\"); // remove protocol-relative\n hostname = hostname.replace(/[/:#?].*$/, \"\"); // remove path/query/fragment/port\n const hadWWW = /^www\\./i.test(hostname);\n if (stripWWW) hostname = hostname.replace(/^www\\./, \"\");\n if (!hostname.includes(\".\")) {\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n const parsed = parse(hostname);\n if (!parsed.publicSuffix || parsed.isIcann === false) {\n throw new Error(\"sanitizeDomain: invalid domain (missing suffix)\");\n }\n if (!stripWWW && hadWWW) {\n return `www.${parsed.domain || hostname}`;\n }\n return parsed.domain || hostname;\n }\n}\n\n/**\n * Safely parse a date string into a Date object.\n *\n * Returns `undefined` when input is falsy or cannot be parsed into a valid date.\n * Use `|| null` at call sites that expect `null` instead of `undefined`.\n */\nexport function safeParseDate(input?: string | null): Date | undefined {\n if (!input || typeof input !== \"string\") return undefined;\n const d = new Date(input);\n return Number.isNaN(d.getTime()) ? undefined : d;\n}\n\n/**\n * Normalize an option name or value to a lowercase, underscore-separated key.\n */\nexport function normalizeKey(input: string): string {\n return input.toLowerCase().replace(/\\s+/g, \"_\");\n}\n\n/**\n * Build a map from normalized option combination → variant id strings.\n * Example key: `size#xl##color#blue`.\n */\nexport function buildVariantOptionsMap(\n optionNames: string[],\n variants: Array<{\n id: number;\n option1: string | null;\n option2: string | null;\n option3: string | null;\n }>\n): Record<string, string> {\n const keys = optionNames.map(normalizeKey);\n const map: Record<string, string> = {};\n\n for (const v of variants) {\n const parts: string[] = [];\n if (keys[0] && v.option1)\n parts.push(`${keys[0]}#${normalizeKey(v.option1)}`);\n if (keys[1] && v.option2)\n parts.push(`${keys[1]}#${normalizeKey(v.option2)}`);\n if (keys[2] && v.option3)\n parts.push(`${keys[2]}#${normalizeKey(v.option3)}`);\n\n if (parts.length > 0) {\n // Ensure deterministic alphabetical ordering of parts\n if (parts.length > 1) parts.sort();\n const key = parts.join(\"##\");\n const id = v.id.toString();\n // First-write wins: do not override if key already exists\n if (map[key] === undefined) {\n map[key] = id;\n }\n }\n }\n\n return map;\n}\n\n/**\n * Build a normalized variant key string from an object of option name → value.\n * - Normalizes both names and values using `normalizeKey`\n * - Sorts parts alphabetically for deterministic output\n * - Joins parts using `##` and uses `name#value` for each part\n *\n * Example output: `color#blue##size#xl`\n */\nexport function buildVariantKey(\n obj: Record<string, string | null | undefined>\n): string {\n const parts: string[] = [];\n for (const [name, value] of Object.entries(obj)) {\n if (value) {\n parts.push(`${normalizeKey(name)}#${normalizeKey(value)}`);\n }\n }\n if (parts.length === 0) return \"\";\n parts.sort((a, b) => a.localeCompare(b));\n return parts.join(\"##\");\n}\n\n/**\n * Format a price amount (in cents) using a given ISO 4217 currency code.\n * Falls back to a simple string when Intl formatting fails.\n */\nexport function formatPrice(\n amountInCents: number,\n currency: CurrencyCode\n): string {\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency,\n }).format((amountInCents || 0) / 100);\n } catch {\n const val = (amountInCents || 0) / 100;\n return `${val} ${currency}`;\n }\n}\n"],"mappings":"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","names":[]}
|