@cloudcart/nitro 0.1.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/dist/analytics/index.d.ts +39 -0
- package/dist/analytics/index.js +13 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/cache/index.d.ts +39 -0
- package/dist/cache/index.js +19 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cart/index.d.ts +73 -0
- package/dist/cart/index.js +9 -0
- package/dist/cart/index.js.map +1 -0
- package/dist/chunk-2VLIYNGM.js +51 -0
- package/dist/chunk-2VLIYNGM.js.map +1 -0
- package/dist/chunk-3YTOR4GE.js +25 -0
- package/dist/chunk-3YTOR4GE.js.map +1 -0
- package/dist/chunk-6DOUKEDG.js +555 -0
- package/dist/chunk-6DOUKEDG.js.map +1 -0
- package/dist/chunk-IZ4Y4UBN.js +279 -0
- package/dist/chunk-IZ4Y4UBN.js.map +1 -0
- package/dist/chunk-JHZ4UBA2.js +57 -0
- package/dist/chunk-JHZ4UBA2.js.map +1 -0
- package/dist/chunk-LVUU4ZBR.js +65 -0
- package/dist/chunk-LVUU4ZBR.js.map +1 -0
- package/dist/chunk-RKT3VKU2.js +94 -0
- package/dist/chunk-RKT3VKU2.js.map +1 -0
- package/dist/chunk-RTJ73SCX.js +45 -0
- package/dist/chunk-RTJ73SCX.js.map +1 -0
- package/dist/chunk-WJCN2EO3.js +47 -0
- package/dist/chunk-WJCN2EO3.js.map +1 -0
- package/dist/csp/index.d.ts +38 -0
- package/dist/csp/index.js +11 -0
- package/dist/csp/index.js.map +1 -0
- package/dist/errors-a5iG09pc.d.ts +42 -0
- package/dist/i18n/index.d.ts +23 -0
- package/dist/i18n/index.js +9 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/seo/index.d.ts +59 -0
- package/dist/seo/index.js +11 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/session/index.d.ts +17 -0
- package/dist/session/index.js +7 -0
- package/dist/session/index.js.map +1 -0
- package/dist/storefront/index.d.ts +10 -0
- package/dist/storefront/index.js +14 -0
- package/dist/storefront/index.js.map +1 -0
- package/dist/types-BIsRLMWU.d.ts +152 -0
- package/dist/types-CaRAihQJ.d.ts +9 -0
- package/package.json +47 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CacheCustom,
|
|
3
|
+
CacheLong,
|
|
4
|
+
CacheNone,
|
|
5
|
+
CacheShort,
|
|
6
|
+
InMemoryCache
|
|
7
|
+
} from "./chunk-JHZ4UBA2.js";
|
|
8
|
+
|
|
9
|
+
// src/storefront/errors.ts
|
|
10
|
+
var StorefrontApiError = class extends Error {
|
|
11
|
+
status;
|
|
12
|
+
requestId;
|
|
13
|
+
graphqlErrors;
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "StorefrontApiError";
|
|
17
|
+
this.status = options.status;
|
|
18
|
+
this.requestId = options.requestId;
|
|
19
|
+
this.graphqlErrors = options.graphqlErrors;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/storefront/query-minifier.ts
|
|
24
|
+
function minifyQuery(query) {
|
|
25
|
+
return query.replace(/#.*$/gm, "").replace(/\s+/g, " ").replace(/\s*([{}(),:])\s*/g, "$1").trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/storefront/mock.ts
|
|
29
|
+
var MOCK_IMAGE = {
|
|
30
|
+
id: "img-1",
|
|
31
|
+
url: "https://placehold.co/600x400?text=Nitro+Product",
|
|
32
|
+
altText: "Product image",
|
|
33
|
+
width: 600,
|
|
34
|
+
height: 400
|
|
35
|
+
};
|
|
36
|
+
function createMockProduct(i) {
|
|
37
|
+
const price = ((i + 1) * 19.99).toFixed(2);
|
|
38
|
+
return {
|
|
39
|
+
id: `product-${i}`,
|
|
40
|
+
title: `Product ${i + 1}`,
|
|
41
|
+
handle: `product-${i + 1}`,
|
|
42
|
+
description: `Sample product description for Product ${i + 1}.`,
|
|
43
|
+
descriptionHtml: `<p>Sample product description for <strong>Product ${i + 1}</strong>.</p>`,
|
|
44
|
+
featuredImage: {
|
|
45
|
+
...MOCK_IMAGE,
|
|
46
|
+
id: `img-${i}`,
|
|
47
|
+
url: `https://placehold.co/600x400?text=Product+${i + 1}`
|
|
48
|
+
},
|
|
49
|
+
options: [
|
|
50
|
+
{ name: "Size", values: ["S", "M", "L"] },
|
|
51
|
+
{ name: "Color", values: ["Black", "White"] }
|
|
52
|
+
],
|
|
53
|
+
variants: {
|
|
54
|
+
nodes: [
|
|
55
|
+
{
|
|
56
|
+
id: `variant-${i}-default`,
|
|
57
|
+
title: "Default",
|
|
58
|
+
availableForSale: true,
|
|
59
|
+
price: { amount: price, currencyCode: "USD" },
|
|
60
|
+
compareAtPrice: null,
|
|
61
|
+
image: null,
|
|
62
|
+
selectedOptions: [
|
|
63
|
+
{ name: "Size", value: "M" },
|
|
64
|
+
{ name: "Color", value: "Black" }
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
priceRange: {
|
|
70
|
+
minVariantPrice: { amount: price, currencyCode: "USD" },
|
|
71
|
+
maxVariantPrice: { amount: price, currencyCode: "USD" }
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
var MOCK_PRODUCTS = Array.from({ length: 8 }, (_, i) => createMockProduct(i));
|
|
76
|
+
var MOCK_COLLECTIONS = [
|
|
77
|
+
{
|
|
78
|
+
id: "col-1",
|
|
79
|
+
title: "Featured Collection",
|
|
80
|
+
handle: "featured",
|
|
81
|
+
description: "Our hand-picked selection of featured products.",
|
|
82
|
+
image: { id: "col-img-1", url: "https://placehold.co/1200x400?text=Featured+Collection", altText: "Collection image", width: 1200, height: 400 },
|
|
83
|
+
products: { nodes: MOCK_PRODUCTS.slice(0, 4) }
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "col-2",
|
|
87
|
+
title: "New Arrivals",
|
|
88
|
+
handle: "new-arrivals",
|
|
89
|
+
description: "The latest additions to our store.",
|
|
90
|
+
image: { id: "col-img-2", url: "https://placehold.co/1200x400?text=New+Arrivals", altText: "Collection image", width: 1200, height: 400 },
|
|
91
|
+
products: { nodes: MOCK_PRODUCTS.slice(4, 8) }
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
function createMockStorefrontClient() {
|
|
95
|
+
return {
|
|
96
|
+
async query() {
|
|
97
|
+
return {};
|
|
98
|
+
},
|
|
99
|
+
async mutate() {
|
|
100
|
+
return {};
|
|
101
|
+
},
|
|
102
|
+
async getShop() {
|
|
103
|
+
return { name: "Nitro Store", description: "A modern headless commerce storefront" };
|
|
104
|
+
},
|
|
105
|
+
async getProducts(first = 8) {
|
|
106
|
+
return MOCK_PRODUCTS.slice(0, first);
|
|
107
|
+
},
|
|
108
|
+
async getProduct(handle) {
|
|
109
|
+
return MOCK_PRODUCTS.find((p) => p.handle === handle) ?? null;
|
|
110
|
+
},
|
|
111
|
+
async getCollections(first = 10) {
|
|
112
|
+
return MOCK_COLLECTIONS.slice(0, first);
|
|
113
|
+
},
|
|
114
|
+
async getCollection(handle) {
|
|
115
|
+
return MOCK_COLLECTIONS.find((c) => c.handle === handle) ?? null;
|
|
116
|
+
},
|
|
117
|
+
async getMenu(handle) {
|
|
118
|
+
if (handle === "main-menu") {
|
|
119
|
+
return {
|
|
120
|
+
id: "main-menu",
|
|
121
|
+
items: [
|
|
122
|
+
{ id: "menu-1", title: "Collections", url: "/collections", items: [] },
|
|
123
|
+
{ id: "menu-2", title: "Products", url: "/products", items: [] },
|
|
124
|
+
{ id: "menu-3", title: "About", url: "/pages/about", items: [] }
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (handle === "footer") {
|
|
129
|
+
return {
|
|
130
|
+
id: "footer",
|
|
131
|
+
items: [
|
|
132
|
+
{ id: "f-1", title: "Privacy Policy", url: "/pages/privacy", items: [] },
|
|
133
|
+
{ id: "f-2", title: "Terms of Service", url: "/pages/terms", items: [] },
|
|
134
|
+
{ id: "f-3", title: "Contact", url: "/pages/contact", items: [] }
|
|
135
|
+
]
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
},
|
|
140
|
+
async getPage(handle) {
|
|
141
|
+
const pages = {
|
|
142
|
+
about: { id: "page-about", title: "About", handle: "about", body: "<h2>About Nitro Store</h2><p>A modern headless commerce storefront built with React Router.</p>" },
|
|
143
|
+
contact: { id: "page-contact", title: "Contact", handle: "contact", body: "<h2>Contact Us</h2><p>Email us at hello@nitro.store</p>" }
|
|
144
|
+
};
|
|
145
|
+
return pages[handle] ?? null;
|
|
146
|
+
},
|
|
147
|
+
async searchProducts(query) {
|
|
148
|
+
const lower = query.toLowerCase();
|
|
149
|
+
return MOCK_PRODUCTS.filter((p) => p.title.toLowerCase().includes(lower));
|
|
150
|
+
},
|
|
151
|
+
async getBlogs() {
|
|
152
|
+
return MOCK_BLOGS;
|
|
153
|
+
},
|
|
154
|
+
async getBlog(handle) {
|
|
155
|
+
return MOCK_BLOGS.find((b) => b.handle === handle) ?? null;
|
|
156
|
+
},
|
|
157
|
+
async getArticle(blogHandle, articleHandle) {
|
|
158
|
+
return MOCK_ARTICLES.find((a) => a.blog.handle === blogHandle && a.handle === articleHandle) ?? null;
|
|
159
|
+
},
|
|
160
|
+
async getArticles(blogHandle) {
|
|
161
|
+
return MOCK_ARTICLES.filter((a) => a.blog.handle === blogHandle);
|
|
162
|
+
},
|
|
163
|
+
async getPolicies() {
|
|
164
|
+
return MOCK_POLICIES;
|
|
165
|
+
},
|
|
166
|
+
async getPolicy(handle) {
|
|
167
|
+
return MOCK_POLICIES.find((p) => p.handle === handle) ?? null;
|
|
168
|
+
},
|
|
169
|
+
CacheShort,
|
|
170
|
+
CacheLong,
|
|
171
|
+
CacheNone,
|
|
172
|
+
CacheCustom
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
var MOCK_BLOGS = [
|
|
176
|
+
{ id: "blog-1", title: "News", handle: "news" },
|
|
177
|
+
{ id: "blog-2", title: "Journal", handle: "journal" }
|
|
178
|
+
];
|
|
179
|
+
var MOCK_ARTICLES = [
|
|
180
|
+
{
|
|
181
|
+
id: "article-1",
|
|
182
|
+
title: "Welcome to Nitro",
|
|
183
|
+
handle: "welcome-to-nitro",
|
|
184
|
+
contentHtml: "<p>Welcome to our new store powered by Nitro, the headless commerce framework by CloudCart.</p>",
|
|
185
|
+
excerpt: "Welcome to our new store powered by Nitro.",
|
|
186
|
+
publishedAt: "2026-03-01T00:00:00Z",
|
|
187
|
+
image: { id: "art-img-1", url: "https://placehold.co/800x400?text=Welcome", altText: "Welcome", width: 800, height: 400 },
|
|
188
|
+
blog: { handle: "news" },
|
|
189
|
+
author: { name: "Nitro Team" }
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: "article-2",
|
|
193
|
+
title: "Spring Collection Launch",
|
|
194
|
+
handle: "spring-collection",
|
|
195
|
+
contentHtml: "<p>Our spring collection is here. Check out the latest arrivals.</p>",
|
|
196
|
+
excerpt: "Our spring collection is here.",
|
|
197
|
+
publishedAt: "2026-03-15T00:00:00Z",
|
|
198
|
+
image: { id: "art-img-2", url: "https://placehold.co/800x400?text=Spring", altText: "Spring", width: 800, height: 400 },
|
|
199
|
+
blog: { handle: "news" },
|
|
200
|
+
author: { name: "Nitro Team" }
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
var MOCK_POLICIES = [
|
|
204
|
+
{ id: "pol-1", title: "Privacy Policy", handle: "privacy-policy", body: "<h2>Privacy Policy</h2><p>We respect your privacy. This policy describes how we collect, use, and share your personal information.</p>" },
|
|
205
|
+
{ id: "pol-2", title: "Terms of Service", handle: "terms-of-service", body: "<h2>Terms of Service</h2><p>By using our store, you agree to these terms and conditions.</p>" },
|
|
206
|
+
{ id: "pol-3", title: "Refund Policy", handle: "refund-policy", body: "<h2>Refund Policy</h2><p>We offer a 30-day return policy on all items.</p>" },
|
|
207
|
+
{ id: "pol-4", title: "Shipping Policy", handle: "shipping-policy", body: "<h2>Shipping Policy</h2><p>Free shipping on orders over $50. Standard delivery in 3-5 business days.</p>" }
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
// src/storefront/client.ts
|
|
211
|
+
var sharedCaches = /* @__PURE__ */ new Map();
|
|
212
|
+
function createStorefrontClient(config) {
|
|
213
|
+
if (!config.publicStorefrontToken) {
|
|
214
|
+
return createMockStorefrontClient();
|
|
215
|
+
}
|
|
216
|
+
const {
|
|
217
|
+
storeDomain,
|
|
218
|
+
publicStorefrontToken,
|
|
219
|
+
privateStorefrontToken,
|
|
220
|
+
apiVersion = "2026-01",
|
|
221
|
+
apiPath = "/api/sf",
|
|
222
|
+
i18n,
|
|
223
|
+
defaultCache
|
|
224
|
+
} = config;
|
|
225
|
+
const baseUrl = storeDomain.startsWith("http") ? storeDomain.replace(/\/$/, "") : `https://${storeDomain}`;
|
|
226
|
+
const endpoint = `${baseUrl}${apiPath}`;
|
|
227
|
+
if (!sharedCaches.has(endpoint)) {
|
|
228
|
+
sharedCaches.set(endpoint, new InMemoryCache());
|
|
229
|
+
}
|
|
230
|
+
const cache = sharedCaches.get(endpoint);
|
|
231
|
+
async function executeGraphQL(query, options = {}) {
|
|
232
|
+
const { variables = {}, cache: cacheStrategy } = options;
|
|
233
|
+
const vars = { ...variables };
|
|
234
|
+
if (i18n) {
|
|
235
|
+
if (query.includes("$country") && !("country" in vars)) {
|
|
236
|
+
vars.country = i18n.country;
|
|
237
|
+
}
|
|
238
|
+
if (query.includes("$language") && !("language" in vars)) {
|
|
239
|
+
vars.language = i18n.language;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const minified = minifyQuery(query);
|
|
243
|
+
const body = JSON.stringify({ query: minified, variables: vars });
|
|
244
|
+
const strategy = cacheStrategy ?? defaultCache ?? CacheShort();
|
|
245
|
+
const cacheKey = `storefront:${hashCode(body)}`;
|
|
246
|
+
if (strategy.mode !== "no-store") {
|
|
247
|
+
const cached = await cache.get(cacheKey);
|
|
248
|
+
if (cached !== void 0) return cached;
|
|
249
|
+
}
|
|
250
|
+
const headers = {
|
|
251
|
+
"Content-Type": "application/json",
|
|
252
|
+
"X-Storefront-Access-Token": publicStorefrontToken,
|
|
253
|
+
"X-API-Version": apiVersion
|
|
254
|
+
};
|
|
255
|
+
if (privateStorefrontToken) {
|
|
256
|
+
headers["Authorization"] = `Bearer ${privateStorefrontToken}`;
|
|
257
|
+
}
|
|
258
|
+
const response = await fetch(endpoint, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
headers,
|
|
261
|
+
body
|
|
262
|
+
});
|
|
263
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
264
|
+
if (!response.ok) {
|
|
265
|
+
throw new StorefrontApiError(
|
|
266
|
+
`Storefront API request failed: ${response.status} ${response.statusText}`,
|
|
267
|
+
{ status: response.status, requestId }
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
const json = await response.json();
|
|
271
|
+
if (json.errors?.length) {
|
|
272
|
+
throw new StorefrontApiError(
|
|
273
|
+
`Storefront API GraphQL error: ${json.errors.map((e) => e.message).join(", ")}`,
|
|
274
|
+
{ status: 200, requestId, graphqlErrors: json.errors }
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
const data = json.data;
|
|
278
|
+
if (strategy.mode !== "no-store") {
|
|
279
|
+
await cache.set(cacheKey, data, strategy);
|
|
280
|
+
}
|
|
281
|
+
return data;
|
|
282
|
+
}
|
|
283
|
+
const client = {
|
|
284
|
+
query: executeGraphQL,
|
|
285
|
+
mutate(mutation, options) {
|
|
286
|
+
return executeGraphQL(mutation, {
|
|
287
|
+
...options,
|
|
288
|
+
cache: CacheNone()
|
|
289
|
+
// Never cache mutations
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
async getShop() {
|
|
293
|
+
const data = await executeGraphQL(SHOP_QUERY, {
|
|
294
|
+
cache: CacheLong()
|
|
295
|
+
});
|
|
296
|
+
return data.shop;
|
|
297
|
+
},
|
|
298
|
+
async getProducts(first = 8) {
|
|
299
|
+
const data = await executeGraphQL(
|
|
300
|
+
PRODUCTS_QUERY,
|
|
301
|
+
{ variables: { first }, cache: CacheShort() }
|
|
302
|
+
);
|
|
303
|
+
return data.products.nodes;
|
|
304
|
+
},
|
|
305
|
+
async getProduct(handle) {
|
|
306
|
+
const data = await executeGraphQL(
|
|
307
|
+
PRODUCT_BY_HANDLE_QUERY,
|
|
308
|
+
{ variables: { handle }, cache: CacheShort() }
|
|
309
|
+
);
|
|
310
|
+
return data.product;
|
|
311
|
+
},
|
|
312
|
+
async getCollections(first = 10) {
|
|
313
|
+
const data = await executeGraphQL(
|
|
314
|
+
COLLECTIONS_QUERY,
|
|
315
|
+
{ variables: { first }, cache: CacheShort() }
|
|
316
|
+
);
|
|
317
|
+
return data.collections.nodes;
|
|
318
|
+
},
|
|
319
|
+
async getCollection(handle) {
|
|
320
|
+
const data = await executeGraphQL(
|
|
321
|
+
COLLECTION_BY_HANDLE_QUERY,
|
|
322
|
+
{ variables: { handle }, cache: CacheShort() }
|
|
323
|
+
);
|
|
324
|
+
return data.collection;
|
|
325
|
+
},
|
|
326
|
+
async getMenu(handle) {
|
|
327
|
+
const data = await executeGraphQL(
|
|
328
|
+
MENU_QUERY,
|
|
329
|
+
{ variables: { handle }, cache: CacheLong() }
|
|
330
|
+
);
|
|
331
|
+
return data.menu;
|
|
332
|
+
},
|
|
333
|
+
async getPage(handle) {
|
|
334
|
+
const data = await executeGraphQL(
|
|
335
|
+
PAGE_QUERY,
|
|
336
|
+
{ variables: { handle }, cache: CacheLong() }
|
|
337
|
+
);
|
|
338
|
+
return data.page;
|
|
339
|
+
},
|
|
340
|
+
async searchProducts(query, first = 20) {
|
|
341
|
+
const data = await executeGraphQL(
|
|
342
|
+
SEARCH_QUERY,
|
|
343
|
+
{ variables: { query, first }, cache: CacheNone() }
|
|
344
|
+
);
|
|
345
|
+
return data.search.nodes;
|
|
346
|
+
},
|
|
347
|
+
async getBlogs(first = 10) {
|
|
348
|
+
const data = await executeGraphQL(
|
|
349
|
+
`query Blogs($first: Int!) { blogs(first: $first) { nodes { id title handle } } }`,
|
|
350
|
+
{ variables: { first }, cache: CacheLong() }
|
|
351
|
+
);
|
|
352
|
+
return data.blogs.nodes;
|
|
353
|
+
},
|
|
354
|
+
async getBlog(handle) {
|
|
355
|
+
const data = await executeGraphQL(
|
|
356
|
+
`query Blog($handle: String!) { blog(handle: $handle) { id title handle } }`,
|
|
357
|
+
{ variables: { handle }, cache: CacheLong() }
|
|
358
|
+
);
|
|
359
|
+
return data.blog;
|
|
360
|
+
},
|
|
361
|
+
async getArticle(blogHandle, articleHandle) {
|
|
362
|
+
const data = await executeGraphQL(
|
|
363
|
+
`query Article($blogHandle: String!, $articleHandle: String!) { blog(handle: $blogHandle) { articleByHandle(handle: $articleHandle) { id title handle contentHtml excerpt publishedAt image { id url altText width height } blog { handle } author { name } } } }`,
|
|
364
|
+
{ variables: { blogHandle, articleHandle }, cache: CacheShort() }
|
|
365
|
+
);
|
|
366
|
+
return data.blog?.articleByHandle ?? null;
|
|
367
|
+
},
|
|
368
|
+
async getArticles(blogHandle, first = 10) {
|
|
369
|
+
const data = await executeGraphQL(
|
|
370
|
+
`query Articles($blogHandle: String!, $first: Int!) { blog(handle: $blogHandle) { articles(first: $first, sortKey: PUBLISHED_AT, reverse: true) { nodes { id title handle excerpt publishedAt image { id url altText width height } blog { handle } author { name } } } } }`,
|
|
371
|
+
{ variables: { blogHandle, first }, cache: CacheShort() }
|
|
372
|
+
);
|
|
373
|
+
return data.blog?.articles.nodes ?? [];
|
|
374
|
+
},
|
|
375
|
+
async getPolicies() {
|
|
376
|
+
const data = await executeGraphQL(
|
|
377
|
+
`query Policies { shop { privacyPolicy { id title handle body } termsOfService { id title handle body } refundPolicy { id title handle body } shippingPolicy { id title handle body } } }`,
|
|
378
|
+
{ cache: CacheLong() }
|
|
379
|
+
);
|
|
380
|
+
return [data.shop.privacyPolicy, data.shop.termsOfService, data.shop.refundPolicy, data.shop.shippingPolicy].filter(Boolean);
|
|
381
|
+
},
|
|
382
|
+
async getPolicy(handle) {
|
|
383
|
+
const policies = await client.getPolicies();
|
|
384
|
+
return policies.find((p) => p.handle === handle) ?? null;
|
|
385
|
+
},
|
|
386
|
+
// Expose cache strategy factories on the client for convenience
|
|
387
|
+
CacheShort,
|
|
388
|
+
CacheLong,
|
|
389
|
+
CacheNone,
|
|
390
|
+
CacheCustom
|
|
391
|
+
};
|
|
392
|
+
return client;
|
|
393
|
+
}
|
|
394
|
+
function hashCode(str) {
|
|
395
|
+
let hash = 0;
|
|
396
|
+
for (let i = 0; i < str.length; i++) {
|
|
397
|
+
const char = str.charCodeAt(i);
|
|
398
|
+
hash = (hash << 5) - hash + char | 0;
|
|
399
|
+
}
|
|
400
|
+
return hash.toString(36);
|
|
401
|
+
}
|
|
402
|
+
var SHOP_QUERY = `#graphql
|
|
403
|
+
query ShopQuery {
|
|
404
|
+
shop {
|
|
405
|
+
name
|
|
406
|
+
description
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
`;
|
|
410
|
+
var PRODUCTS_QUERY = `#graphql
|
|
411
|
+
query Products($first: Int!, $country: CountryCode, $language: LanguageCode)
|
|
412
|
+
@inContext(country: $country, language: $language) {
|
|
413
|
+
products(first: $first, sortKey: UPDATED_AT, reverse: true) {
|
|
414
|
+
nodes {
|
|
415
|
+
id
|
|
416
|
+
title
|
|
417
|
+
handle
|
|
418
|
+
description
|
|
419
|
+
descriptionHtml
|
|
420
|
+
featuredImage { id url altText width height }
|
|
421
|
+
options { name values }
|
|
422
|
+
variants(first: 1) {
|
|
423
|
+
nodes {
|
|
424
|
+
id title availableForSale
|
|
425
|
+
price { amount currencyCode }
|
|
426
|
+
compareAtPrice { amount currencyCode }
|
|
427
|
+
image { id url altText width height }
|
|
428
|
+
selectedOptions { name value }
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
priceRange {
|
|
432
|
+
minVariantPrice { amount currencyCode }
|
|
433
|
+
maxVariantPrice { amount currencyCode }
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
`;
|
|
439
|
+
var PRODUCT_BY_HANDLE_QUERY = `#graphql
|
|
440
|
+
query ProductByHandle($handle: String!, $country: CountryCode, $language: LanguageCode)
|
|
441
|
+
@inContext(country: $country, language: $language) {
|
|
442
|
+
product(handle: $handle) {
|
|
443
|
+
id
|
|
444
|
+
title
|
|
445
|
+
handle
|
|
446
|
+
description
|
|
447
|
+
descriptionHtml
|
|
448
|
+
featuredImage { id url altText width height }
|
|
449
|
+
options { name values }
|
|
450
|
+
variants(first: 50) {
|
|
451
|
+
nodes {
|
|
452
|
+
id title availableForSale
|
|
453
|
+
price { amount currencyCode }
|
|
454
|
+
compareAtPrice { amount currencyCode }
|
|
455
|
+
image { id url altText width height }
|
|
456
|
+
selectedOptions { name value }
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
priceRange {
|
|
460
|
+
minVariantPrice { amount currencyCode }
|
|
461
|
+
maxVariantPrice { amount currencyCode }
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
`;
|
|
466
|
+
var COLLECTIONS_QUERY = `#graphql
|
|
467
|
+
query Collections($first: Int!, $country: CountryCode, $language: LanguageCode)
|
|
468
|
+
@inContext(country: $country, language: $language) {
|
|
469
|
+
collections(first: $first, sortKey: UPDATED_AT, reverse: true) {
|
|
470
|
+
nodes {
|
|
471
|
+
id title handle description
|
|
472
|
+
image { id url altText width height }
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
`;
|
|
477
|
+
var COLLECTION_BY_HANDLE_QUERY = `#graphql
|
|
478
|
+
query CollectionByHandle($handle: String!, $country: CountryCode, $language: LanguageCode)
|
|
479
|
+
@inContext(country: $country, language: $language) {
|
|
480
|
+
collection(handle: $handle) {
|
|
481
|
+
id title handle description
|
|
482
|
+
image { id url altText width height }
|
|
483
|
+
products(first: 50) {
|
|
484
|
+
nodes {
|
|
485
|
+
id title handle description descriptionHtml
|
|
486
|
+
featuredImage { id url altText width height }
|
|
487
|
+
options { name values }
|
|
488
|
+
variants(first: 1) {
|
|
489
|
+
nodes {
|
|
490
|
+
id title availableForSale
|
|
491
|
+
price { amount currencyCode }
|
|
492
|
+
compareAtPrice { amount currencyCode }
|
|
493
|
+
image { id url altText width height }
|
|
494
|
+
selectedOptions { name value }
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
priceRange {
|
|
498
|
+
minVariantPrice { amount currencyCode }
|
|
499
|
+
maxVariantPrice { amount currencyCode }
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
`;
|
|
506
|
+
var MENU_QUERY = `#graphql
|
|
507
|
+
query Menu($handle: String!) {
|
|
508
|
+
menu(handle: $handle) {
|
|
509
|
+
id
|
|
510
|
+
items {
|
|
511
|
+
id title url
|
|
512
|
+
items { id title url items { id title url } }
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
`;
|
|
517
|
+
var PAGE_QUERY = `#graphql
|
|
518
|
+
query Page($handle: String!) {
|
|
519
|
+
page(handle: $handle) {
|
|
520
|
+
id title handle body
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
`;
|
|
524
|
+
var SEARCH_QUERY = `#graphql
|
|
525
|
+
query Search($query: String!, $first: Int!, $country: CountryCode, $language: LanguageCode)
|
|
526
|
+
@inContext(country: $country, language: $language) {
|
|
527
|
+
search(query: $query, first: $first, types: PRODUCT) {
|
|
528
|
+
nodes {
|
|
529
|
+
... on Product {
|
|
530
|
+
id title handle description descriptionHtml
|
|
531
|
+
featuredImage { id url altText width height }
|
|
532
|
+
priceRange {
|
|
533
|
+
minVariantPrice { amount currencyCode }
|
|
534
|
+
maxVariantPrice { amount currencyCode }
|
|
535
|
+
}
|
|
536
|
+
variants(first: 1) {
|
|
537
|
+
nodes {
|
|
538
|
+
id title availableForSale
|
|
539
|
+
price { amount currencyCode }
|
|
540
|
+
selectedOptions { name value }
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
`;
|
|
548
|
+
|
|
549
|
+
export {
|
|
550
|
+
StorefrontApiError,
|
|
551
|
+
minifyQuery,
|
|
552
|
+
createMockStorefrontClient,
|
|
553
|
+
createStorefrontClient
|
|
554
|
+
};
|
|
555
|
+
//# sourceMappingURL=chunk-6DOUKEDG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storefront/errors.ts","../src/storefront/query-minifier.ts","../src/storefront/mock.ts","../src/storefront/client.ts"],"sourcesContent":["export class StorefrontApiError extends Error {\n public status: number;\n public requestId?: string;\n public graphqlErrors?: Array<{message: string; locations?: unknown[]; path?: string[]}>;\n\n constructor(\n message: string,\n options: {\n status: number;\n requestId?: string;\n graphqlErrors?: Array<{message: string; locations?: unknown[]; path?: string[]}>;\n },\n ) {\n super(message);\n this.name = 'StorefrontApiError';\n this.status = options.status;\n this.requestId = options.requestId;\n this.graphqlErrors = options.graphqlErrors;\n }\n}\n","/**\n * Minifies a GraphQL query string by removing comments and excess whitespace.\n */\nexport function minifyQuery(query: string): string {\n return query\n .replace(/#.*$/gm, '') // Remove comments\n .replace(/\\s+/g, ' ') // Collapse whitespace\n .replace(/\\s*([{}(),:])\\s*/g, '$1') // Remove space around punctuation\n .trim();\n}\n","/**\n * Mock storefront client — returns sample data for development\n * when no API token is configured.\n */\n\nimport type {StorefrontClient} from './types.js';\nimport type {Product, Collection, Shop, Menu, Page, Blog, Article, Policy} from '../types/storefront.js';\nimport type {Image} from '../types/image.js';\nimport {CacheShort, CacheLong, CacheNone, CacheCustom} from '../cache/strategies.js';\n\nconst MOCK_IMAGE: Image = {\n id: 'img-1',\n url: 'https://placehold.co/600x400?text=Nitro+Product',\n altText: 'Product image',\n width: 600,\n height: 400,\n};\n\nfunction createMockProduct(i: number): Product {\n const price = ((i + 1) * 19.99).toFixed(2);\n return {\n id: `product-${i}`,\n title: `Product ${i + 1}`,\n handle: `product-${i + 1}`,\n description: `Sample product description for Product ${i + 1}.`,\n descriptionHtml: `<p>Sample product description for <strong>Product ${i + 1}</strong>.</p>`,\n featuredImage: {\n ...MOCK_IMAGE,\n id: `img-${i}`,\n url: `https://placehold.co/600x400?text=Product+${i + 1}`,\n },\n options: [\n {name: 'Size', values: ['S', 'M', 'L']},\n {name: 'Color', values: ['Black', 'White']},\n ],\n variants: {\n nodes: [\n {\n id: `variant-${i}-default`,\n title: 'Default',\n availableForSale: true,\n price: {amount: price, currencyCode: 'USD'},\n compareAtPrice: null,\n image: null,\n selectedOptions: [\n {name: 'Size', value: 'M'},\n {name: 'Color', value: 'Black'},\n ],\n },\n ],\n },\n priceRange: {\n minVariantPrice: {amount: price, currencyCode: 'USD'},\n maxVariantPrice: {amount: price, currencyCode: 'USD'},\n },\n };\n}\n\nconst MOCK_PRODUCTS: Product[] = Array.from({length: 8}, (_, i) => createMockProduct(i));\n\nconst MOCK_COLLECTIONS: Collection[] = [\n {\n id: 'col-1',\n title: 'Featured Collection',\n handle: 'featured',\n description: 'Our hand-picked selection of featured products.',\n image: {id: 'col-img-1', url: 'https://placehold.co/1200x400?text=Featured+Collection', altText: 'Collection image', width: 1200, height: 400},\n products: {nodes: MOCK_PRODUCTS.slice(0, 4)},\n },\n {\n id: 'col-2',\n title: 'New Arrivals',\n handle: 'new-arrivals',\n description: 'The latest additions to our store.',\n image: {id: 'col-img-2', url: 'https://placehold.co/1200x400?text=New+Arrivals', altText: 'Collection image', width: 1200, height: 400},\n products: {nodes: MOCK_PRODUCTS.slice(4, 8)},\n },\n];\n\nexport function createMockStorefrontClient(): StorefrontClient {\n return {\n async query() {\n return {} as never;\n },\n async mutate() {\n return {} as never;\n },\n async getShop(): Promise<Shop> {\n return {name: 'Nitro Store', description: 'A modern headless commerce storefront'};\n },\n async getProducts(first = 8) {\n return MOCK_PRODUCTS.slice(0, first);\n },\n async getProduct(handle) {\n return MOCK_PRODUCTS.find((p) => p.handle === handle) ?? null;\n },\n async getCollections(first = 10) {\n return MOCK_COLLECTIONS.slice(0, first);\n },\n async getCollection(handle) {\n return MOCK_COLLECTIONS.find((c) => c.handle === handle) ?? null;\n },\n async getMenu(handle) {\n if (handle === 'main-menu') {\n return {\n id: 'main-menu',\n items: [\n {id: 'menu-1', title: 'Collections', url: '/collections', items: []},\n {id: 'menu-2', title: 'Products', url: '/products', items: []},\n {id: 'menu-3', title: 'About', url: '/pages/about', items: []},\n ],\n };\n }\n if (handle === 'footer') {\n return {\n id: 'footer',\n items: [\n {id: 'f-1', title: 'Privacy Policy', url: '/pages/privacy', items: []},\n {id: 'f-2', title: 'Terms of Service', url: '/pages/terms', items: []},\n {id: 'f-3', title: 'Contact', url: '/pages/contact', items: []},\n ],\n };\n }\n return null;\n },\n async getPage(handle) {\n const pages: Record<string, Page> = {\n about: {id: 'page-about', title: 'About', handle: 'about', body: '<h2>About Nitro Store</h2><p>A modern headless commerce storefront built with React Router.</p>'},\n contact: {id: 'page-contact', title: 'Contact', handle: 'contact', body: '<h2>Contact Us</h2><p>Email us at hello@nitro.store</p>'},\n };\n return pages[handle] ?? null;\n },\n async searchProducts(query) {\n const lower = query.toLowerCase();\n return MOCK_PRODUCTS.filter((p) => p.title.toLowerCase().includes(lower));\n },\n async getBlogs() {\n return MOCK_BLOGS;\n },\n async getBlog(handle) {\n return MOCK_BLOGS.find((b) => b.handle === handle) ?? null;\n },\n async getArticle(blogHandle, articleHandle) {\n return MOCK_ARTICLES.find((a) => a.blog.handle === blogHandle && a.handle === articleHandle) ?? null;\n },\n async getArticles(blogHandle) {\n return MOCK_ARTICLES.filter((a) => a.blog.handle === blogHandle);\n },\n async getPolicies() {\n return MOCK_POLICIES;\n },\n async getPolicy(handle) {\n return MOCK_POLICIES.find((p) => p.handle === handle) ?? null;\n },\n CacheShort,\n CacheLong,\n CacheNone,\n CacheCustom,\n };\n}\n\nconst MOCK_BLOGS: Blog[] = [\n {id: 'blog-1', title: 'News', handle: 'news'},\n {id: 'blog-2', title: 'Journal', handle: 'journal'},\n];\n\nconst MOCK_ARTICLES: Article[] = [\n {\n id: 'article-1', title: 'Welcome to Nitro', handle: 'welcome-to-nitro',\n contentHtml: '<p>Welcome to our new store powered by Nitro, the headless commerce framework by CloudCart.</p>',\n excerpt: 'Welcome to our new store powered by Nitro.',\n publishedAt: '2026-03-01T00:00:00Z',\n image: {id: 'art-img-1', url: 'https://placehold.co/800x400?text=Welcome', altText: 'Welcome', width: 800, height: 400},\n blog: {handle: 'news'}, author: {name: 'Nitro Team'},\n },\n {\n id: 'article-2', title: 'Spring Collection Launch', handle: 'spring-collection',\n contentHtml: '<p>Our spring collection is here. Check out the latest arrivals.</p>',\n excerpt: 'Our spring collection is here.',\n publishedAt: '2026-03-15T00:00:00Z',\n image: {id: 'art-img-2', url: 'https://placehold.co/800x400?text=Spring', altText: 'Spring', width: 800, height: 400},\n blog: {handle: 'news'}, author: {name: 'Nitro Team'},\n },\n];\n\nconst MOCK_POLICIES: Policy[] = [\n {id: 'pol-1', title: 'Privacy Policy', handle: 'privacy-policy', body: '<h2>Privacy Policy</h2><p>We respect your privacy. This policy describes how we collect, use, and share your personal information.</p>'},\n {id: 'pol-2', title: 'Terms of Service', handle: 'terms-of-service', body: '<h2>Terms of Service</h2><p>By using our store, you agree to these terms and conditions.</p>'},\n {id: 'pol-3', title: 'Refund Policy', handle: 'refund-policy', body: '<h2>Refund Policy</h2><p>We offer a 30-day return policy on all items.</p>'},\n {id: 'pol-4', title: 'Shipping Policy', handle: 'shipping-policy', body: '<h2>Shipping Policy</h2><p>Free shipping on orders over $50. Standard delivery in 3-5 business days.</p>'},\n];\n","/**\n * createStorefrontClient — the core commerce data client.\n *\n * Adapted from cc-cli's GraphQLClient pattern. Key additions:\n * - Dual-token auth (public vs private)\n * - Automatic i18n variable injection\n * - Query minification\n * - Cache-aware queries\n * - Mock fallback when no token is provided\n */\n\nimport type {StorefrontClientConfig, StorefrontClient, QueryOptions} from './types.js';\nimport type {Product, Collection, Shop, Menu, Page, Blog, Article, Policy} from '../types/storefront.js';\nimport type {CacheStrategy} from '../cache/types.js';\nimport {CacheShort, CacheLong, CacheNone, CacheCustom} from '../cache/strategies.js';\nimport {InMemoryCache} from '../cache/in-memory.js';\nimport {StorefrontApiError} from './errors.js';\nimport {minifyQuery} from './query-minifier.js';\nimport {createMockStorefrontClient} from './mock.js';\n\n// Shared cache across requests — prevents redundant API calls\nconst sharedCaches = new Map<string, InMemoryCache>();\n\nexport function createStorefrontClient(\n config: StorefrontClientConfig,\n): StorefrontClient {\n // Fall back to mock when no token is provided\n if (!config.publicStorefrontToken) {\n return createMockStorefrontClient();\n }\n\n const {\n storeDomain,\n publicStorefrontToken,\n privateStorefrontToken,\n apiVersion = '2026-01',\n apiPath = '/api/sf',\n i18n,\n defaultCache,\n } = config;\n\n const baseUrl = storeDomain.startsWith('http')\n ? storeDomain.replace(/\\/$/, '')\n : `https://${storeDomain}`;\n\n const endpoint = `${baseUrl}${apiPath}`;\n\n // Use a shared cache across requests so repeated queries don't hit the API.\n // The cache is keyed by endpoint so different stores don't share data.\n if (!sharedCaches.has(endpoint)) {\n sharedCaches.set(endpoint, new InMemoryCache());\n }\n const cache = sharedCaches.get(endpoint)!;\n\n async function executeGraphQL<T>(\n query: string,\n options: QueryOptions = {},\n ): Promise<T> {\n const {variables = {}, cache: cacheStrategy} = options;\n\n // Inject i18n variables if the query references them\n const vars = {...variables};\n if (i18n) {\n if (query.includes('$country') && !('country' in vars)) {\n vars.country = i18n.country;\n }\n if (query.includes('$language') && !('language' in vars)) {\n vars.language = i18n.language;\n }\n }\n\n const minified = minifyQuery(query);\n const body = JSON.stringify({query: minified, variables: vars});\n\n // Check cache\n const strategy = cacheStrategy ?? defaultCache ?? CacheShort();\n const cacheKey = `storefront:${hashCode(body)}`;\n\n if (strategy.mode !== 'no-store') {\n const cached = await cache.get<T>(cacheKey);\n if (cached !== undefined) return cached;\n }\n\n // Build headers\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Storefront-Access-Token': publicStorefrontToken,\n 'X-API-Version': apiVersion,\n };\n\n if (privateStorefrontToken) {\n headers['Authorization'] = `Bearer ${privateStorefrontToken}`;\n }\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body,\n });\n\n const requestId = response.headers.get('x-request-id') ?? undefined;\n\n if (!response.ok) {\n throw new StorefrontApiError(\n `Storefront API request failed: ${response.status} ${response.statusText}`,\n {status: response.status, requestId},\n );\n }\n\n const json = (await response.json()) as {data?: T; errors?: Array<{message: string; locations?: unknown[]; path?: string[]}>};\n\n if (json.errors?.length) {\n throw new StorefrontApiError(\n `Storefront API GraphQL error: ${json.errors.map((e) => e.message).join(', ')}`,\n {status: 200, requestId, graphqlErrors: json.errors},\n );\n }\n\n const data = json.data as T;\n\n // Store in cache\n if (strategy.mode !== 'no-store') {\n await cache.set(cacheKey, data, strategy);\n }\n\n return data;\n }\n\n // ── Convenience methods ──\n // These use predefined GraphQL queries for the CloudCart Storefront API.\n // The query strings will be updated when the real API schema is finalized.\n\n const client: StorefrontClient = {\n query: executeGraphQL,\n\n mutate<T>(mutation: string, options?: QueryOptions): Promise<T> {\n return executeGraphQL<T>(mutation, {\n ...options,\n cache: CacheNone(), // Never cache mutations\n });\n },\n\n async getShop() {\n const data = await executeGraphQL<{shop: Shop}>(SHOP_QUERY, {\n cache: CacheLong(),\n });\n return data.shop;\n },\n\n async getProducts(first = 8) {\n const data = await executeGraphQL<{products: {nodes: Product[]}}>(\n PRODUCTS_QUERY,\n {variables: {first}, cache: CacheShort()},\n );\n return data.products.nodes;\n },\n\n async getProduct(handle) {\n const data = await executeGraphQL<{product: Product | null}>(\n PRODUCT_BY_HANDLE_QUERY,\n {variables: {handle}, cache: CacheShort()},\n );\n return data.product;\n },\n\n async getCollections(first = 10) {\n const data = await executeGraphQL<{collections: {nodes: Collection[]}}>(\n COLLECTIONS_QUERY,\n {variables: {first}, cache: CacheShort()},\n );\n return data.collections.nodes;\n },\n\n async getCollection(handle) {\n const data = await executeGraphQL<{collection: Collection | null}>(\n COLLECTION_BY_HANDLE_QUERY,\n {variables: {handle}, cache: CacheShort()},\n );\n return data.collection;\n },\n\n async getMenu(handle) {\n const data = await executeGraphQL<{menu: Menu | null}>(\n MENU_QUERY,\n {variables: {handle}, cache: CacheLong()},\n );\n return data.menu;\n },\n\n async getPage(handle) {\n const data = await executeGraphQL<{page: Page | null}>(\n PAGE_QUERY,\n {variables: {handle}, cache: CacheLong()},\n );\n return data.page;\n },\n\n async searchProducts(query, first = 20) {\n const data = await executeGraphQL<{search: {nodes: Product[]}}>(\n SEARCH_QUERY,\n {variables: {query, first}, cache: CacheNone()},\n );\n return data.search.nodes;\n },\n\n async getBlogs(first = 10) {\n const data = await executeGraphQL<{blogs: {nodes: Blog[]}}>(\n `query Blogs($first: Int!) { blogs(first: $first) { nodes { id title handle } } }`,\n {variables: {first}, cache: CacheLong()},\n );\n return data.blogs.nodes;\n },\n\n async getBlog(handle) {\n const data = await executeGraphQL<{blog: Blog | null}>(\n `query Blog($handle: String!) { blog(handle: $handle) { id title handle } }`,\n {variables: {handle}, cache: CacheLong()},\n );\n return data.blog;\n },\n\n async getArticle(blogHandle, articleHandle) {\n const data = await executeGraphQL<{blog: {articleByHandle: Article | null} | null}>(\n `query Article($blogHandle: String!, $articleHandle: String!) { blog(handle: $blogHandle) { articleByHandle(handle: $articleHandle) { id title handle contentHtml excerpt publishedAt image { id url altText width height } blog { handle } author { name } } } }`,\n {variables: {blogHandle, articleHandle}, cache: CacheShort()},\n );\n return data.blog?.articleByHandle ?? null;\n },\n\n async getArticles(blogHandle, first = 10) {\n const data = await executeGraphQL<{blog: {articles: {nodes: Article[]}} | null}>(\n `query Articles($blogHandle: String!, $first: Int!) { blog(handle: $blogHandle) { articles(first: $first, sortKey: PUBLISHED_AT, reverse: true) { nodes { id title handle excerpt publishedAt image { id url altText width height } blog { handle } author { name } } } } }`,\n {variables: {blogHandle, first}, cache: CacheShort()},\n );\n return data.blog?.articles.nodes ?? [];\n },\n\n async getPolicies() {\n const data = await executeGraphQL<{shop: {privacyPolicy: Policy | null; termsOfService: Policy | null; refundPolicy: Policy | null; shippingPolicy: Policy | null}}>(\n `query Policies { shop { privacyPolicy { id title handle body } termsOfService { id title handle body } refundPolicy { id title handle body } shippingPolicy { id title handle body } } }`,\n {cache: CacheLong()},\n );\n return [data.shop.privacyPolicy, data.shop.termsOfService, data.shop.refundPolicy, data.shop.shippingPolicy].filter(Boolean) as Policy[];\n },\n\n async getPolicy(handle) {\n const policies = await client.getPolicies();\n return policies.find((p) => p.handle === handle) ?? null;\n },\n\n // Expose cache strategy factories on the client for convenience\n CacheShort,\n CacheLong,\n CacheNone,\n CacheCustom,\n };\n\n return client;\n}\n\n// ── Hash utility ──\n\nfunction hashCode(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash + char) | 0;\n }\n return hash.toString(36);\n}\n\n// ── GraphQL Queries ──\n// These define the expected CloudCart Storefront API schema.\n// Update these when the real API is finalized.\n\nconst SHOP_QUERY = `#graphql\n query ShopQuery {\n shop {\n name\n description\n }\n }\n`;\n\nconst PRODUCTS_QUERY = `#graphql\n query Products($first: Int!, $country: CountryCode, $language: LanguageCode)\n @inContext(country: $country, language: $language) {\n products(first: $first, sortKey: UPDATED_AT, reverse: true) {\n nodes {\n id\n title\n handle\n description\n descriptionHtml\n featuredImage { id url altText width height }\n options { name values }\n variants(first: 1) {\n nodes {\n id title availableForSale\n price { amount currencyCode }\n compareAtPrice { amount currencyCode }\n image { id url altText width height }\n selectedOptions { name value }\n }\n }\n priceRange {\n minVariantPrice { amount currencyCode }\n maxVariantPrice { amount currencyCode }\n }\n }\n }\n }\n`;\n\nconst PRODUCT_BY_HANDLE_QUERY = `#graphql\n query ProductByHandle($handle: String!, $country: CountryCode, $language: LanguageCode)\n @inContext(country: $country, language: $language) {\n product(handle: $handle) {\n id\n title\n handle\n description\n descriptionHtml\n featuredImage { id url altText width height }\n options { name values }\n variants(first: 50) {\n nodes {\n id title availableForSale\n price { amount currencyCode }\n compareAtPrice { amount currencyCode }\n image { id url altText width height }\n selectedOptions { name value }\n }\n }\n priceRange {\n minVariantPrice { amount currencyCode }\n maxVariantPrice { amount currencyCode }\n }\n }\n }\n`;\n\nconst COLLECTIONS_QUERY = `#graphql\n query Collections($first: Int!, $country: CountryCode, $language: LanguageCode)\n @inContext(country: $country, language: $language) {\n collections(first: $first, sortKey: UPDATED_AT, reverse: true) {\n nodes {\n id title handle description\n image { id url altText width height }\n }\n }\n }\n`;\n\nconst COLLECTION_BY_HANDLE_QUERY = `#graphql\n query CollectionByHandle($handle: String!, $country: CountryCode, $language: LanguageCode)\n @inContext(country: $country, language: $language) {\n collection(handle: $handle) {\n id title handle description\n image { id url altText width height }\n products(first: 50) {\n nodes {\n id title handle description descriptionHtml\n featuredImage { id url altText width height }\n options { name values }\n variants(first: 1) {\n nodes {\n id title availableForSale\n price { amount currencyCode }\n compareAtPrice { amount currencyCode }\n image { id url altText width height }\n selectedOptions { name value }\n }\n }\n priceRange {\n minVariantPrice { amount currencyCode }\n maxVariantPrice { amount currencyCode }\n }\n }\n }\n }\n }\n`;\n\nconst MENU_QUERY = `#graphql\n query Menu($handle: String!) {\n menu(handle: $handle) {\n id\n items {\n id title url\n items { id title url items { id title url } }\n }\n }\n }\n`;\n\nconst PAGE_QUERY = `#graphql\n query Page($handle: String!) {\n page(handle: $handle) {\n id title handle body\n }\n }\n`;\n\nconst SEARCH_QUERY = `#graphql\n query Search($query: String!, $first: Int!, $country: CountryCode, $language: LanguageCode)\n @inContext(country: $country, language: $language) {\n search(query: $query, first: $first, types: PRODUCT) {\n nodes {\n ... on Product {\n id title handle description descriptionHtml\n featuredImage { id url altText width height }\n priceRange {\n minVariantPrice { amount currencyCode }\n maxVariantPrice { amount currencyCode }\n }\n variants(first: 1) {\n nodes {\n id title availableForSale\n price { amount currencyCode }\n selectedOptions { name value }\n }\n }\n }\n }\n }\n }\n`;\n"],"mappings":"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","names":[]}
|