@tanstack/create 0.65.0 → 0.67.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/dist/frameworks/react/add-ons/powersync/README.md.ejs +26 -0
  3. package/dist/frameworks/react/add-ons/powersync/assets/_dot_env.local.append +3 -0
  4. package/dist/frameworks/react/add-ons/powersync/assets/powersync-vite-plugin.ts +17 -0
  5. package/dist/frameworks/react/add-ons/powersync/assets/src/integrations/powersync/provider.tsx +26 -0
  6. package/dist/frameworks/react/add-ons/powersync/assets/src/lib/powersync/AppSchema.ts +17 -0
  7. package/dist/frameworks/react/add-ons/powersync/assets/src/lib/powersync/BackendConnector.ts +52 -0
  8. package/dist/frameworks/react/add-ons/powersync/assets/src/routes/demo/powersync.tsx +129 -0
  9. package/dist/frameworks/react/add-ons/powersync/info.json +46 -0
  10. package/dist/frameworks/react/add-ons/powersync/package.json.ejs +7 -0
  11. package/dist/frameworks/react/add-ons/powersync/small-logo.svg +6 -0
  12. package/dist/frameworks/react/add-ons/shopify/README.md +86 -0
  13. package/dist/frameworks/react/add-ons/shopify/assets/_dot_env.local.append +19 -0
  14. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/account-nav.tsx.ejs +41 -0
  15. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/add-to-cart-button.tsx +48 -0
  16. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/cart-line-item.tsx +94 -0
  17. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/cart-summary.tsx +111 -0
  18. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/empty-state.tsx +29 -0
  19. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/money.tsx +11 -0
  20. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/product-card.tsx +74 -0
  21. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/product-grid.tsx +24 -0
  22. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/shop-image.tsx +57 -0
  23. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/shop.css +58 -0
  24. package/dist/frameworks/react/add-ons/shopify/assets/src/components/shop/variant-selector.tsx +79 -0
  25. package/dist/frameworks/react/add-ons/shopify/assets/src/hooks/use-cart.ts +276 -0
  26. package/dist/frameworks/react/add-ons/shopify/assets/src/hooks/use-customer.ts.ejs +22 -0
  27. package/dist/frameworks/react/add-ons/shopify/assets/src/integrations/shopify/header-cart.tsx +37 -0
  28. package/dist/frameworks/react/add-ons/shopify/assets/src/lib/shopify/customer-queries.ts.ejs +228 -0
  29. package/dist/frameworks/react/add-ons/shopify/assets/src/lib/shopify/format.ts +33 -0
  30. package/dist/frameworks/react/add-ons/shopify/assets/src/lib/shopify/queries.ts +684 -0
  31. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.addresses.tsx.ejs +67 -0
  32. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.callback.tsx.ejs +45 -0
  33. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.index.tsx.ejs +70 -0
  34. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.login.tsx.ejs +59 -0
  35. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.logout.tsx.ejs +16 -0
  36. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.orders.$id.tsx.ejs +126 -0
  37. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.orders.tsx.ejs +50 -0
  38. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.tsx.ejs +34 -0
  39. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.cart.tsx +45 -0
  40. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.collections.$handle.tsx +66 -0
  41. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.index.tsx +36 -0
  42. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.pages.$handle.tsx +39 -0
  43. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.policies.$handle.tsx +30 -0
  44. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.products.$handle.tsx +106 -0
  45. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.search.tsx +75 -0
  46. package/dist/frameworks/react/add-ons/shopify/assets/src/routes/shop.tsx +78 -0
  47. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/cart.functions.ts +207 -0
  48. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/catalog.functions.ts +244 -0
  49. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/cookies.ts +29 -0
  50. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/customer-client.ts.ejs +99 -0
  51. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/customer-cookies.ts.ejs +49 -0
  52. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/customer.functions.ts.ejs +168 -0
  53. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/env.ts +89 -0
  54. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/oauth.ts.ejs +301 -0
  55. package/dist/frameworks/react/add-ons/shopify/assets/src/server/shopify/storefront-client.ts +101 -0
  56. package/dist/frameworks/react/add-ons/shopify/info.json +104 -0
  57. package/dist/frameworks/react/add-ons/shopify/package.json +6 -0
  58. package/dist/frameworks/react/add-ons/shopify/small-logo.svg +1 -0
  59. package/dist/frameworks/react/examples/shopify-storefront/README.md +39 -0
  60. package/dist/frameworks/react/examples/shopify-storefront/assets/src/components/FeaturedCollections.tsx +43 -0
  61. package/dist/frameworks/react/examples/shopify-storefront/assets/src/components/ShopHero.tsx +39 -0
  62. package/dist/frameworks/react/examples/shopify-storefront/assets/src/routes/index.tsx +65 -0
  63. package/dist/frameworks/react/examples/shopify-storefront/info.json +18 -0
  64. package/dist/frameworks/react/examples/shopify-storefront/package.json +3 -0
  65. package/dist/frameworks/react/project/base/src/components/Header.tsx.ejs +34 -34
  66. package/package.json +1 -1
  67. package/src/frameworks/react/add-ons/powersync/README.md.ejs +26 -0
  68. package/src/frameworks/react/add-ons/powersync/assets/_dot_env.local.append +3 -0
  69. package/src/frameworks/react/add-ons/powersync/assets/powersync-vite-plugin.ts +17 -0
  70. package/src/frameworks/react/add-ons/powersync/assets/src/integrations/powersync/provider.tsx +26 -0
  71. package/src/frameworks/react/add-ons/powersync/assets/src/lib/powersync/AppSchema.ts +17 -0
  72. package/src/frameworks/react/add-ons/powersync/assets/src/lib/powersync/BackendConnector.ts +52 -0
  73. package/src/frameworks/react/add-ons/powersync/assets/src/routes/demo/powersync.tsx +129 -0
  74. package/src/frameworks/react/add-ons/powersync/info.json +46 -0
  75. package/src/frameworks/react/add-ons/powersync/package.json.ejs +7 -0
  76. package/src/frameworks/react/add-ons/powersync/small-logo.svg +6 -0
  77. package/src/frameworks/react/add-ons/shopify/README.md +86 -0
  78. package/src/frameworks/react/add-ons/shopify/assets/_dot_env.local.append +19 -0
  79. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/account-nav.tsx.ejs +41 -0
  80. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/add-to-cart-button.tsx +48 -0
  81. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/cart-line-item.tsx +94 -0
  82. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/cart-summary.tsx +111 -0
  83. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/empty-state.tsx +29 -0
  84. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/money.tsx +11 -0
  85. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/product-card.tsx +74 -0
  86. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/product-grid.tsx +24 -0
  87. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/shop-image.tsx +57 -0
  88. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/shop.css +58 -0
  89. package/src/frameworks/react/add-ons/shopify/assets/src/components/shop/variant-selector.tsx +79 -0
  90. package/src/frameworks/react/add-ons/shopify/assets/src/hooks/use-cart.ts +276 -0
  91. package/src/frameworks/react/add-ons/shopify/assets/src/hooks/use-customer.ts.ejs +22 -0
  92. package/src/frameworks/react/add-ons/shopify/assets/src/integrations/shopify/header-cart.tsx +37 -0
  93. package/src/frameworks/react/add-ons/shopify/assets/src/lib/shopify/customer-queries.ts.ejs +228 -0
  94. package/src/frameworks/react/add-ons/shopify/assets/src/lib/shopify/format.ts +33 -0
  95. package/src/frameworks/react/add-ons/shopify/assets/src/lib/shopify/queries.ts +684 -0
  96. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.addresses.tsx.ejs +67 -0
  97. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.callback.tsx.ejs +45 -0
  98. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.index.tsx.ejs +70 -0
  99. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.login.tsx.ejs +59 -0
  100. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.logout.tsx.ejs +16 -0
  101. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.orders.$id.tsx.ejs +126 -0
  102. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.orders.tsx.ejs +50 -0
  103. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.account.tsx.ejs +34 -0
  104. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.cart.tsx +45 -0
  105. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.collections.$handle.tsx +66 -0
  106. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.index.tsx +36 -0
  107. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.pages.$handle.tsx +39 -0
  108. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.policies.$handle.tsx +30 -0
  109. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.products.$handle.tsx +106 -0
  110. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.search.tsx +75 -0
  111. package/src/frameworks/react/add-ons/shopify/assets/src/routes/shop.tsx +78 -0
  112. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/cart.functions.ts +207 -0
  113. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/catalog.functions.ts +244 -0
  114. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/cookies.ts +29 -0
  115. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/customer-client.ts.ejs +99 -0
  116. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/customer-cookies.ts.ejs +49 -0
  117. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/customer.functions.ts.ejs +168 -0
  118. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/env.ts +89 -0
  119. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/oauth.ts.ejs +301 -0
  120. package/src/frameworks/react/add-ons/shopify/assets/src/server/shopify/storefront-client.ts +101 -0
  121. package/src/frameworks/react/add-ons/shopify/info.json +104 -0
  122. package/src/frameworks/react/add-ons/shopify/package.json +6 -0
  123. package/src/frameworks/react/add-ons/shopify/small-logo.svg +1 -0
  124. package/src/frameworks/react/examples/shopify-storefront/README.md +39 -0
  125. package/src/frameworks/react/examples/shopify-storefront/assets/src/components/FeaturedCollections.tsx +43 -0
  126. package/src/frameworks/react/examples/shopify-storefront/assets/src/components/ShopHero.tsx +39 -0
  127. package/src/frameworks/react/examples/shopify-storefront/assets/src/routes/index.tsx +65 -0
  128. package/src/frameworks/react/examples/shopify-storefront/info.json +18 -0
  129. package/src/frameworks/react/examples/shopify-storefront/package.json +3 -0
  130. package/src/frameworks/react/project/base/src/components/Header.tsx.ejs +34 -34
@@ -0,0 +1,684 @@
1
+ import type {
2
+ Cart,
3
+ CartLine,
4
+ Collection,
5
+ Image as StorefrontImage,
6
+ MoneyV2,
7
+ Page,
8
+ Product,
9
+ ProductOption,
10
+ ProductSortKeys,
11
+ ProductVariant,
12
+ } from '@shopify/hydrogen-react/storefront-api-types'
13
+
14
+ /**
15
+ * GraphQL queries for the Shopify Storefront API.
16
+ *
17
+ * Result types are hand-picked slices of `@shopify/hydrogen-react`
18
+ * Storefront API types. The `@shopify/hydrogen-react` import is type-only
19
+ * (zero runtime cost). When the query count grows, swap to
20
+ * `@shopify/hydrogen-codegen` and regenerate; consuming code won't change.
21
+ */
22
+
23
+ /* ─── Shop info ─────────────────────────────────────────────────────────── */
24
+
25
+ export const SHOP_QUERY = /* GraphQL */ `
26
+ query Shop {
27
+ shop {
28
+ name
29
+ description
30
+ primaryDomain {
31
+ url
32
+ }
33
+ }
34
+ }
35
+ `
36
+
37
+ export type ShopQueryResult = {
38
+ shop: {
39
+ name: string
40
+ description: string | null
41
+ primaryDomain: { url: string }
42
+ }
43
+ }
44
+
45
+ /* ─── Product card fragment + product list ──────────────────────────────── */
46
+
47
+ const PRODUCT_CARD_FRAGMENT = /* GraphQL */ `
48
+ fragment ProductCard on Product {
49
+ id
50
+ handle
51
+ title
52
+ productType
53
+ tags
54
+ publishedAt
55
+ options {
56
+ name
57
+ values
58
+ }
59
+ featuredImage {
60
+ url
61
+ altText
62
+ width
63
+ height
64
+ }
65
+ variants(first: 1) {
66
+ nodes {
67
+ id
68
+ availableForSale
69
+ }
70
+ }
71
+ priceRange {
72
+ minVariantPrice {
73
+ amount
74
+ currencyCode
75
+ }
76
+ maxVariantPrice {
77
+ amount
78
+ currencyCode
79
+ }
80
+ }
81
+ compareAtPriceRange {
82
+ minVariantPrice {
83
+ amount
84
+ currencyCode
85
+ }
86
+ }
87
+ }
88
+ `
89
+
90
+ export const PRODUCTS_QUERY = /* GraphQL */ `
91
+ ${PRODUCT_CARD_FRAGMENT}
92
+ query Products(
93
+ $first: Int!
94
+ $after: String
95
+ $sortKey: ProductSortKeys
96
+ $reverse: Boolean
97
+ ) {
98
+ products(
99
+ first: $first
100
+ after: $after
101
+ sortKey: $sortKey
102
+ reverse: $reverse
103
+ ) {
104
+ pageInfo {
105
+ hasNextPage
106
+ endCursor
107
+ }
108
+ nodes {
109
+ ...ProductCard
110
+ }
111
+ }
112
+ }
113
+ `
114
+
115
+ type CardImage = Pick<
116
+ StorefrontImage,
117
+ 'url' | 'altText' | 'width' | 'height'
118
+ > | null
119
+
120
+ export type ProductListItem = Pick<
121
+ Product,
122
+ 'id' | 'handle' | 'title' | 'productType' | 'tags' | 'publishedAt'
123
+ > & {
124
+ options: Array<Pick<ProductOption, 'name' | 'values'>>
125
+ featuredImage: CardImage
126
+ variants: {
127
+ nodes: Array<{ id: string; availableForSale: boolean }>
128
+ }
129
+ priceRange: {
130
+ minVariantPrice: Pick<MoneyV2, 'amount' | 'currencyCode'>
131
+ maxVariantPrice: Pick<MoneyV2, 'amount' | 'currencyCode'>
132
+ }
133
+ compareAtPriceRange: {
134
+ minVariantPrice: Pick<MoneyV2, 'amount' | 'currencyCode'>
135
+ }
136
+ }
137
+
138
+ export type ProductListPage = {
139
+ nodes: Array<ProductListItem>
140
+ pageInfo: { hasNextPage: boolean; endCursor: string | null }
141
+ }
142
+
143
+ export type ProductsQueryVariables = {
144
+ first: number
145
+ after?: string | null
146
+ sortKey?: ProductSortKeys | null
147
+ reverse?: boolean | null
148
+ }
149
+
150
+ export type ProductsQueryResult = {
151
+ products: ProductListPage
152
+ }
153
+
154
+ /* ─── Single product (PDP) ──────────────────────────────────────────────── */
155
+
156
+ export const PRODUCT_QUERY = /* GraphQL */ `
157
+ query Product($handle: String!) {
158
+ product(handle: $handle) {
159
+ id
160
+ handle
161
+ title
162
+ descriptionHtml
163
+ options {
164
+ id
165
+ name
166
+ values
167
+ }
168
+ images(first: 10) {
169
+ nodes {
170
+ url
171
+ altText
172
+ width
173
+ height
174
+ }
175
+ }
176
+ variants(first: 100) {
177
+ nodes {
178
+ id
179
+ title
180
+ availableForSale
181
+ selectedOptions {
182
+ name
183
+ value
184
+ }
185
+ price {
186
+ amount
187
+ currencyCode
188
+ }
189
+ image {
190
+ url
191
+ altText
192
+ width
193
+ height
194
+ }
195
+ }
196
+ }
197
+ seo {
198
+ title
199
+ description
200
+ }
201
+ }
202
+ }
203
+ `
204
+
205
+ export type ProductDetailVariant = Pick<
206
+ ProductVariant,
207
+ 'id' | 'title' | 'availableForSale'
208
+ > & {
209
+ selectedOptions: Array<{ name: string; value: string }>
210
+ price: Pick<MoneyV2, 'amount' | 'currencyCode'>
211
+ image: Pick<StorefrontImage, 'url' | 'altText' | 'width' | 'height'> | null
212
+ }
213
+
214
+ export type ProductDetail = Pick<
215
+ Product,
216
+ 'id' | 'handle' | 'title' | 'descriptionHtml'
217
+ > & {
218
+ options: Array<Pick<ProductOption, 'id' | 'name' | 'values'>>
219
+ images: {
220
+ nodes: Array<Pick<StorefrontImage, 'url' | 'altText' | 'width' | 'height'>>
221
+ }
222
+ variants: { nodes: Array<ProductDetailVariant> }
223
+ seo: { title: string | null; description: string | null }
224
+ }
225
+
226
+ export type ProductQueryResult = {
227
+ product: ProductDetail | null
228
+ }
229
+
230
+ /* ─── Collections list ──────────────────────────────────────────────────── */
231
+
232
+ export const COLLECTIONS_QUERY = /* GraphQL */ `
233
+ query Collections($first: Int!) {
234
+ collections(first: $first, sortKey: TITLE) {
235
+ nodes {
236
+ id
237
+ handle
238
+ title
239
+ description
240
+ image {
241
+ url
242
+ altText
243
+ width
244
+ height
245
+ }
246
+ }
247
+ }
248
+ }
249
+ `
250
+
251
+ export type CollectionListItem = Pick<
252
+ Collection,
253
+ 'id' | 'handle' | 'title' | 'description'
254
+ > & {
255
+ image: Pick<StorefrontImage, 'url' | 'altText' | 'width' | 'height'> | null
256
+ }
257
+
258
+ export type CollectionsQueryResult = {
259
+ collections: { nodes: Array<CollectionListItem> }
260
+ }
261
+
262
+ /* ─── Collection by handle ──────────────────────────────────────────────── */
263
+
264
+ export const COLLECTION_QUERY = /* GraphQL */ `
265
+ ${PRODUCT_CARD_FRAGMENT}
266
+ query Collection(
267
+ $handle: String!
268
+ $first: Int!
269
+ $after: String
270
+ $sortKey: ProductCollectionSortKeys
271
+ $reverse: Boolean
272
+ ) {
273
+ collection(handle: $handle) {
274
+ id
275
+ handle
276
+ title
277
+ description
278
+ descriptionHtml
279
+ image {
280
+ url
281
+ altText
282
+ width
283
+ height
284
+ }
285
+ seo {
286
+ title
287
+ description
288
+ }
289
+ products(
290
+ first: $first
291
+ after: $after
292
+ sortKey: $sortKey
293
+ reverse: $reverse
294
+ ) {
295
+ pageInfo {
296
+ hasNextPage
297
+ endCursor
298
+ }
299
+ nodes {
300
+ ...ProductCard
301
+ }
302
+ }
303
+ }
304
+ }
305
+ `
306
+
307
+ export type CollectionDetail = Pick<
308
+ Collection,
309
+ 'id' | 'handle' | 'title' | 'description' | 'descriptionHtml'
310
+ > & {
311
+ image: Pick<StorefrontImage, 'url' | 'altText' | 'width' | 'height'> | null
312
+ seo: { title: string | null; description: string | null }
313
+ products: ProductListPage
314
+ }
315
+
316
+ export type CollectionQueryResult = {
317
+ collection: CollectionDetail | null
318
+ }
319
+
320
+ /* ─── Cart fragment + queries + mutations ───────────────────────────────── */
321
+
322
+ const CART_FRAGMENT = /* GraphQL */ `
323
+ fragment CartFields on Cart {
324
+ id
325
+ checkoutUrl
326
+ totalQuantity
327
+ cost {
328
+ totalAmount {
329
+ amount
330
+ currencyCode
331
+ }
332
+ subtotalAmount {
333
+ amount
334
+ currencyCode
335
+ }
336
+ totalTaxAmount {
337
+ amount
338
+ currencyCode
339
+ }
340
+ }
341
+ lines(first: 100) {
342
+ nodes {
343
+ id
344
+ quantity
345
+ merchandise {
346
+ ... on ProductVariant {
347
+ id
348
+ title
349
+ availableForSale
350
+ selectedOptions {
351
+ name
352
+ value
353
+ }
354
+ price {
355
+ amount
356
+ currencyCode
357
+ }
358
+ image {
359
+ url
360
+ altText
361
+ width
362
+ height
363
+ }
364
+ product {
365
+ handle
366
+ title
367
+ }
368
+ }
369
+ }
370
+ cost {
371
+ totalAmount {
372
+ amount
373
+ currencyCode
374
+ }
375
+ }
376
+ }
377
+ }
378
+ discountCodes {
379
+ code
380
+ applicable
381
+ }
382
+ }
383
+ `
384
+
385
+ export const CART_QUERY = /* GraphQL */ `
386
+ ${CART_FRAGMENT}
387
+ query Cart($cartId: ID!) {
388
+ cart(id: $cartId) {
389
+ ...CartFields
390
+ }
391
+ }
392
+ `
393
+
394
+ export const CART_CREATE_MUTATION = /* GraphQL */ `
395
+ ${CART_FRAGMENT}
396
+ mutation CartCreate($input: CartInput!) {
397
+ cartCreate(input: $input) {
398
+ cart {
399
+ ...CartFields
400
+ }
401
+ userErrors {
402
+ field
403
+ message
404
+ }
405
+ }
406
+ }
407
+ `
408
+
409
+ export const CART_LINES_ADD_MUTATION = /* GraphQL */ `
410
+ ${CART_FRAGMENT}
411
+ mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
412
+ cartLinesAdd(cartId: $cartId, lines: $lines) {
413
+ cart {
414
+ ...CartFields
415
+ }
416
+ userErrors {
417
+ field
418
+ message
419
+ }
420
+ }
421
+ }
422
+ `
423
+
424
+ export const CART_LINES_UPDATE_MUTATION = /* GraphQL */ `
425
+ ${CART_FRAGMENT}
426
+ mutation CartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
427
+ cartLinesUpdate(cartId: $cartId, lines: $lines) {
428
+ cart {
429
+ ...CartFields
430
+ }
431
+ userErrors {
432
+ field
433
+ message
434
+ }
435
+ }
436
+ }
437
+ `
438
+
439
+ export const CART_LINES_REMOVE_MUTATION = /* GraphQL */ `
440
+ ${CART_FRAGMENT}
441
+ mutation CartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
442
+ cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
443
+ cart {
444
+ ...CartFields
445
+ }
446
+ userErrors {
447
+ field
448
+ message
449
+ }
450
+ }
451
+ }
452
+ `
453
+
454
+ export const CART_DISCOUNT_CODES_UPDATE_MUTATION = /* GraphQL */ `
455
+ ${CART_FRAGMENT}
456
+ mutation CartDiscountCodesUpdate($cartId: ID!, $discountCodes: [String!]) {
457
+ cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
458
+ cart {
459
+ ...CartFields
460
+ }
461
+ userErrors {
462
+ field
463
+ message
464
+ }
465
+ }
466
+ }
467
+ `
468
+
469
+ export type CartLineMerchandise = Pick<
470
+ ProductVariant,
471
+ 'id' | 'title' | 'availableForSale'
472
+ > & {
473
+ selectedOptions: Array<{ name: string; value: string }>
474
+ price: Pick<MoneyV2, 'amount' | 'currencyCode'>
475
+ image: Pick<StorefrontImage, 'url' | 'altText' | 'width' | 'height'> | null
476
+ product: Pick<Product, 'handle' | 'title'>
477
+ }
478
+
479
+ export type CartLineDetail = Pick<CartLine, 'id' | 'quantity'> & {
480
+ merchandise: CartLineMerchandise
481
+ cost: {
482
+ totalAmount: Pick<MoneyV2, 'amount' | 'currencyCode'>
483
+ }
484
+ }
485
+
486
+ export type CartDetail = Pick<Cart, 'id' | 'checkoutUrl' | 'totalQuantity'> & {
487
+ cost: {
488
+ totalAmount: Pick<MoneyV2, 'amount' | 'currencyCode'>
489
+ subtotalAmount: Pick<MoneyV2, 'amount' | 'currencyCode'>
490
+ totalTaxAmount: Pick<MoneyV2, 'amount' | 'currencyCode'> | null
491
+ }
492
+ lines: { nodes: Array<CartLineDetail> }
493
+ discountCodes: Array<{ code: string; applicable: boolean }>
494
+ }
495
+
496
+ export type CartQueryResult = { cart: CartDetail | null }
497
+ export type CartUserError = { field: string[] | null; message: string }
498
+
499
+ type CartMutationResult<TName extends string> = {
500
+ [K in TName]: { cart: CartDetail | null; userErrors: Array<CartUserError> }
501
+ }
502
+
503
+ export type CartCreateResult = CartMutationResult<'cartCreate'>
504
+ export type CartLinesAddResult = CartMutationResult<'cartLinesAdd'>
505
+ export type CartLinesUpdateResult = CartMutationResult<'cartLinesUpdate'>
506
+ export type CartLinesRemoveResult = CartMutationResult<'cartLinesRemove'>
507
+ export type CartDiscountCodesUpdateResult =
508
+ CartMutationResult<'cartDiscountCodesUpdate'>
509
+
510
+ /* ─── Sort options ──────────────────────────────────────────────────────── */
511
+
512
+ export const SORT_OPTIONS = [
513
+ { key: 'BEST_SELLING', reverse: false, label: 'Best selling' },
514
+ { key: 'CREATED_AT', reverse: true, label: 'Newest' },
515
+ { key: 'PRICE', reverse: false, label: 'Price: low to high' },
516
+ { key: 'PRICE', reverse: true, label: 'Price: high to low' },
517
+ { key: 'TITLE', reverse: false, label: 'Title: A–Z' },
518
+ ] as const satisfies ReadonlyArray<{
519
+ key: ProductSortKeys
520
+ reverse: boolean
521
+ label: string
522
+ }>
523
+
524
+ export type SortOption = (typeof SORT_OPTIONS)[number]
525
+ export type SortOptionId = `${SortOption['key']}${'' | ':rev'}`
526
+
527
+ export function sortOptionId(opt: SortOption): SortOptionId {
528
+ return (opt.reverse ? `${opt.key}:rev` : opt.key) as SortOptionId
529
+ }
530
+
531
+ export function resolveSortOption(id: string | undefined): SortOption {
532
+ if (!id) return SORT_OPTIONS[0]
533
+ for (const opt of SORT_OPTIONS) {
534
+ if (sortOptionId(opt) === id) return opt
535
+ }
536
+ return SORT_OPTIONS[0]
537
+ }
538
+
539
+ export const COLLECTION_SORT_OPTIONS = [
540
+ { key: 'COLLECTION_DEFAULT', reverse: false, label: 'Featured' },
541
+ { key: 'BEST_SELLING', reverse: false, label: 'Best selling' },
542
+ { key: 'CREATED', reverse: true, label: 'Newest' },
543
+ { key: 'PRICE', reverse: false, label: 'Price: low to high' },
544
+ { key: 'PRICE', reverse: true, label: 'Price: high to low' },
545
+ { key: 'TITLE', reverse: false, label: 'Title: A–Z' },
546
+ ] as const satisfies ReadonlyArray<{
547
+ key: string
548
+ reverse: boolean
549
+ label: string
550
+ }>
551
+
552
+ export type CollectionSortOption = (typeof COLLECTION_SORT_OPTIONS)[number]
553
+
554
+ export function resolveCollectionSortOption(
555
+ id: string | undefined,
556
+ ): CollectionSortOption {
557
+ if (!id) return COLLECTION_SORT_OPTIONS[0]
558
+ const expected = (opt: CollectionSortOption) =>
559
+ opt.reverse ? `${opt.key}:rev` : opt.key
560
+ for (const opt of COLLECTION_SORT_OPTIONS) {
561
+ if (expected(opt) === id) return opt
562
+ }
563
+ return COLLECTION_SORT_OPTIONS[0]
564
+ }
565
+
566
+ /* ─── Pages + policies ──────────────────────────────────────────────────── */
567
+
568
+ export const PAGE_QUERY = /* GraphQL */ `
569
+ query Page($handle: String!) {
570
+ page(handle: $handle) {
571
+ id
572
+ handle
573
+ title
574
+ body
575
+ bodySummary
576
+ seo {
577
+ title
578
+ description
579
+ }
580
+ }
581
+ }
582
+ `
583
+
584
+ export type PageDetail = Pick<
585
+ Page,
586
+ 'id' | 'handle' | 'title' | 'body' | 'bodySummary'
587
+ > & {
588
+ seo: { title: string | null; description: string | null }
589
+ }
590
+
591
+ export type PageQueryResult = { page: PageDetail | null }
592
+
593
+ export const SHOP_POLICIES_QUERY = /* GraphQL */ `
594
+ query ShopPolicies {
595
+ shop {
596
+ privacyPolicy {
597
+ handle
598
+ title
599
+ body
600
+ }
601
+ refundPolicy {
602
+ handle
603
+ title
604
+ body
605
+ }
606
+ termsOfService {
607
+ handle
608
+ title
609
+ body
610
+ }
611
+ shippingPolicy {
612
+ handle
613
+ title
614
+ body
615
+ }
616
+ }
617
+ }
618
+ `
619
+
620
+ export type ShopPolicy = {
621
+ handle: string
622
+ title: string
623
+ body: string
624
+ } | null
625
+
626
+ export type ShopPoliciesQueryResult = {
627
+ shop: {
628
+ privacyPolicy: ShopPolicy
629
+ refundPolicy: ShopPolicy
630
+ termsOfService: ShopPolicy
631
+ shippingPolicy: ShopPolicy
632
+ }
633
+ }
634
+
635
+ export type PolicySummary = { handle: string; title: string }
636
+
637
+ export function flattenPolicies(
638
+ shop: ShopPoliciesQueryResult['shop'],
639
+ ): Array<PolicySummary> {
640
+ const keys = [
641
+ 'shippingPolicy',
642
+ 'refundPolicy',
643
+ 'privacyPolicy',
644
+ 'termsOfService',
645
+ ] as const
646
+ return keys.flatMap((k) => {
647
+ const p = shop[k]
648
+ return p ? [{ handle: p.handle, title: p.title }] : []
649
+ })
650
+ }
651
+
652
+ /* ─── Search ────────────────────────────────────────────────────────────── */
653
+
654
+ export const SEARCH_QUERY = /* GraphQL */ `
655
+ ${PRODUCT_CARD_FRAGMENT}
656
+ query Search($query: String!, $first: Int!, $after: String) {
657
+ search(
658
+ query: $query
659
+ first: $first
660
+ after: $after
661
+ types: [PRODUCT]
662
+ productFilters: [{ available: true }]
663
+ ) {
664
+ totalCount
665
+ pageInfo {
666
+ hasNextPage
667
+ endCursor
668
+ }
669
+ nodes {
670
+ ... on Product {
671
+ ...ProductCard
672
+ }
673
+ }
674
+ }
675
+ }
676
+ `
677
+
678
+ export type SearchQueryResult = {
679
+ search: {
680
+ totalCount: number
681
+ pageInfo: { hasNextPage: boolean; endCursor: string | null }
682
+ nodes: Array<ProductListItem>
683
+ }
684
+ }