@doswiftly/storefront-operations 1.0.5 → 4.4.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @doswiftly/storefront-operations
2
2
 
3
- Pre-built GraphQL operations for DoSwiftly e-commerce storefronts. Contains ready-to-use queries, mutations, and fragments for products, collections, cart, checkout, and customer management.
3
+ Pre-built GraphQL operations and schema for DoSwiftly e-commerce storefronts. Contains the GraphQL schema file, ready-to-use queries, mutations, and fragments for products, collections, cart, checkout, and customer management.
4
+
5
+ **No running backend needed for codegen** — the schema is included as a file.
4
6
 
5
7
  ## Installation
6
8
 
@@ -24,13 +26,8 @@ npm install @doswiftly/storefront-operations @graphql-codegen/cli @graphql-codeg
24
26
  import { CodegenConfig } from "@graphql-codegen/cli";
25
27
 
26
28
  const config: CodegenConfig = {
27
- schema: {
28
- [process.env.NEXT_PUBLIC_STOREFRONT_API_URL!]: {
29
- headers: {
30
- "X-Shop-Slug": process.env.NEXT_PUBLIC_SHOP_SLUG!,
31
- },
32
- },
33
- },
29
+ // Schema from package — no running backend needed!
30
+ schema: "node_modules/@doswiftly/storefront-operations/schema.graphql",
34
31
  documents: [
35
32
  "node_modules/@doswiftly/storefront-operations/**/*.graphql",
36
33
  "src/**/*.{ts,tsx}", // Your custom operations (optional)
@@ -137,11 +134,16 @@ export function ProductList() {
137
134
  - `CustomerPasswordRecover` - Request password reset email
138
135
  - `CustomerPasswordReset` - Reset password with token
139
136
 
140
- ## Environment Variables
137
+ ## Schema
141
138
 
142
- ```env
143
- NEXT_PUBLIC_STOREFRONT_API_URL=https://api.doswiftly.pl/storefront/graphql
144
- NEXT_PUBLIC_SHOP_SLUG=your-shop-slug
139
+ The package includes `schema.graphql` — the full GraphQL schema auto-generated from the backend (NestJS code-first). This enables **offline codegen** without a running backend.
140
+
141
+ To update the schema after backend changes:
142
+
143
+ ```bash
144
+ # From monorepo root (requires backend to have been started at least once)
145
+ cd packages/@doswiftly/storefront-operations
146
+ pnpm run sync
145
147
  ```
146
148
 
147
149
  ## TypeScript Support
package/fragments.graphql CHANGED
@@ -34,10 +34,15 @@ fragment Money on Money {
34
34
  currencyCode
35
35
  }
36
36
 
37
- # Full price with conversion transparency (for price fields that support currency conversion)
38
- fragment PriceMoney on PriceMoney {
37
+ # Lightweight price for listing views (cards, grids, search results)
38
+ fragment Price on PriceMoney {
39
39
  amount
40
40
  currencyCode
41
+ }
42
+
43
+ # Full price with conversion transparency (for price fields that support currency conversion)
44
+ fragment PriceMoney on PriceMoney {
45
+ ...Price
41
46
  baseAmount
42
47
  baseCurrencyCode
43
48
  exchangeRate
@@ -84,37 +89,50 @@ fragment ProductVariant on ProductVariant {
84
89
  position
85
90
  }
86
91
 
87
- fragment ProductBase on Product {
92
+ # Minimal product data for listing views (cards, grids, search results).
93
+ # Matches template-local ProductCardFields for consistent typing.
94
+ fragment ProductCard on Product {
88
95
  id
89
96
  handle
90
97
  title
91
- description
92
- descriptionHtml
98
+ vendor
99
+ productType
100
+ availableForSale
101
+ averageRating
102
+ reviewCount
103
+ tags
93
104
  featuredImage {
94
105
  ...Image
95
106
  }
96
107
  priceRange {
97
108
  minVariantPrice {
98
- ...PriceMoney
109
+ ...Price
99
110
  }
100
111
  maxVariantPrice {
101
- ...PriceMoney
112
+ ...Price
102
113
  }
103
114
  }
104
- originalPriceRange {
115
+ compareAtPriceRange {
105
116
  minVariantPrice {
106
- ...Money
117
+ ...Price
107
118
  }
108
119
  maxVariantPrice {
109
- ...Money
120
+ ...Price
110
121
  }
111
122
  }
112
- compareAtPriceRange {
123
+ }
124
+
125
+ # Full product data for detail views and pages that need all fields.
126
+ fragment ProductBase on Product {
127
+ ...ProductCard
128
+ description
129
+ descriptionHtml
130
+ originalPriceRange {
113
131
  minVariantPrice {
114
- ...PriceMoney
132
+ ...Money
115
133
  }
116
134
  maxVariantPrice {
117
- ...PriceMoney
135
+ ...Money
118
136
  }
119
137
  }
120
138
  originalCompareAtPriceRange {
@@ -126,16 +144,10 @@ fragment ProductBase on Product {
126
144
  }
127
145
  }
128
146
  totalInventory
129
- vendor
130
- productType
131
147
  type
132
148
  collectRecipientInfo
133
- tags
134
149
  createdAt
135
150
  updatedAt
136
- averageRating
137
- reviewCount
138
- availableForSale
139
151
  }
140
152
 
141
153
  fragment ProductFull on Product {
@@ -225,7 +237,7 @@ fragment Customer on Customer {
225
237
  displayName
226
238
  phone
227
239
  emailVerified
228
- acceptsMarketing
240
+ emailMarketingState
229
241
  defaultAddress {
230
242
  ...MailingAddress
231
243
  }
@@ -252,43 +264,19 @@ fragment Order on Order {
252
264
  totalShipping {
253
265
  ...Money
254
266
  }
267
+ status
255
268
  financialStatus
256
269
  fulfillmentStatus
257
270
  processedAt
271
+ confirmedAt
272
+ cancelledAt
273
+ expiredAt
258
274
  shippingAddress {
259
275
  ...MailingAddress
260
276
  }
261
277
  lineItemsCount
262
278
  }
263
279
 
264
- # Order with shipments for tracking pages
265
- fragment OrderWithShipments on Order {
266
- id
267
- orderNumber
268
- totalPrice {
269
- ...Money
270
- }
271
- subtotalPrice {
272
- ...Money
273
- }
274
- totalTax {
275
- ...Money
276
- }
277
- totalShipping {
278
- ...Money
279
- }
280
- financialStatus
281
- fulfillmentStatus
282
- processedAt
283
- shippingAddress {
284
- ...MailingAddress
285
- }
286
- lineItemsCount
287
- shipments {
288
- ...Shipment
289
- }
290
- }
291
-
292
280
  # ============================================
293
281
  # Cart Fragments
294
282
  # ============================================
@@ -449,6 +437,22 @@ fragment ShopBranding on ShopBranding {
449
437
  }
450
438
  }
451
439
 
440
+ fragment BotProtectionProvider on BotProtectionProviderInfo {
441
+ provider
442
+ siteKey
443
+ scriptUrl
444
+ }
445
+
446
+ fragment BotProtection on BotProtectionInfo {
447
+ primary {
448
+ ...BotProtectionProvider
449
+ }
450
+ fallback {
451
+ ...BotProtectionProvider
452
+ }
453
+ protectedOperations
454
+ }
455
+
452
456
  fragment Shop on Shop {
453
457
  id
454
458
  name
@@ -457,6 +461,8 @@ fragment Shop on Shop {
457
461
  currencyCode
458
462
  supportedCurrencies
459
463
  paymentCurrencies
464
+ defaultLanguage
465
+ supportedLanguages
460
466
  logo {
461
467
  ...Image
462
468
  }
@@ -471,6 +477,9 @@ fragment Shop on Shop {
471
477
  branding {
472
478
  ...ShopBranding
473
479
  }
480
+ botProtection {
481
+ ...BotProtection
482
+ }
474
483
  }
475
484
 
476
485
  # ============================================
@@ -577,6 +586,9 @@ fragment CheckoutLineItem on CheckoutLineItem {
577
586
  variantId
578
587
  productId
579
588
  sku
589
+ image {
590
+ ...Image
591
+ }
580
592
  }
581
593
 
582
594
  # GAP-001: Gift Card Checkout Integration
@@ -797,17 +809,6 @@ fragment Return on Return {
797
809
  updatedAt
798
810
  }
799
811
 
800
- fragment ReturnBasic on Return {
801
- id
802
- returnNumber
803
- orderId
804
- orderNumber
805
- status
806
- reason
807
- createdAt
808
- updatedAt
809
- }
810
-
811
812
  fragment ReturnReasonOption on ReturnReasonOption {
812
813
  value
813
814
  label
@@ -1150,3 +1151,179 @@ fragment GenerateReferralCodePayload on GenerateReferralCodePayload {
1150
1151
  shareUrl
1151
1152
  userErrors
1152
1153
  }
1154
+
1155
+ # ============================================
1156
+ # Review Fragments
1157
+ # ============================================
1158
+
1159
+ fragment ProductReview on ProductReview {
1160
+ id
1161
+ productId
1162
+ rating
1163
+ title
1164
+ content
1165
+ pros
1166
+ cons
1167
+ images
1168
+ authorName
1169
+ isVerifiedPurchase
1170
+ helpfulCount
1171
+ response
1172
+ responseAt
1173
+ createdAt
1174
+ }
1175
+
1176
+ fragment ReviewStats on ReviewStats {
1177
+ averageRating
1178
+ totalCount
1179
+ fiveStarCount
1180
+ fourStarCount
1181
+ threeStarCount
1182
+ twoStarCount
1183
+ oneStarCount
1184
+ }
1185
+
1186
+ # ============================================
1187
+ # Wishlist Fragments
1188
+ # ============================================
1189
+
1190
+ fragment WishlistItem on WishlistItem {
1191
+ id
1192
+ productId
1193
+ variantId
1194
+ product {
1195
+ ...ProductCard
1196
+ }
1197
+ priceAtAdd {
1198
+ ...Money
1199
+ }
1200
+ notifyOnSale
1201
+ notifyOnRestock
1202
+ addedAt
1203
+ }
1204
+
1205
+ fragment Wishlist on Wishlist {
1206
+ id
1207
+ name
1208
+ isPublic
1209
+ shareToken
1210
+ items {
1211
+ ...WishlistItem
1212
+ }
1213
+ itemCount
1214
+ createdAt
1215
+ updatedAt
1216
+ }
1217
+
1218
+ # ============================================
1219
+ # Blog Fragments
1220
+ # ============================================
1221
+
1222
+ fragment BlogCategory on BlogCategory {
1223
+ id
1224
+ name
1225
+ slug
1226
+ description
1227
+ postCount
1228
+ }
1229
+
1230
+ fragment BlogTag on BlogTag {
1231
+ id
1232
+ name
1233
+ slug
1234
+ postCount
1235
+ }
1236
+
1237
+ fragment BlogPost on BlogPost {
1238
+ id
1239
+ title
1240
+ slug
1241
+ excerpt
1242
+ content
1243
+ contentType
1244
+ featuredImage {
1245
+ ...Image
1246
+ }
1247
+ author {
1248
+ id
1249
+ name
1250
+ bio
1251
+ avatar {
1252
+ ...Image
1253
+ }
1254
+ }
1255
+ category {
1256
+ ...BlogCategory
1257
+ }
1258
+ tags {
1259
+ ...BlogTag
1260
+ }
1261
+ publishedAt
1262
+ readingTime
1263
+ viewCount
1264
+ commentCount
1265
+ allowComments
1266
+ isFeatured
1267
+ status
1268
+ seo {
1269
+ title
1270
+ description
1271
+ }
1272
+ createdAt
1273
+ updatedAt
1274
+ }
1275
+
1276
+ # ============================================
1277
+ # Recommendation Fragments
1278
+ # ============================================
1279
+
1280
+ fragment ProductRecommendation on ProductRecommendation {
1281
+ product {
1282
+ ...ProductCard
1283
+ }
1284
+ type
1285
+ score
1286
+ reason
1287
+ }
1288
+
1289
+ fragment CartRecommendation on CartRecommendations {
1290
+ frequentlyBoughtTogether {
1291
+ product {
1292
+ ...ProductCard
1293
+ }
1294
+ type
1295
+ score
1296
+ reason
1297
+ }
1298
+ youMayAlsoLike {
1299
+ product {
1300
+ ...ProductCard
1301
+ }
1302
+ type
1303
+ score
1304
+ reason
1305
+ }
1306
+ }
1307
+
1308
+ # ============================================
1309
+ # Inventory Level Fragments (Multi-Warehouse)
1310
+ # ============================================
1311
+
1312
+ fragment InventoryLevel on InventoryLevel {
1313
+ locationId
1314
+ locationName
1315
+ locationType
1316
+ available
1317
+ onHand
1318
+ }
1319
+
1320
+ fragment VariantInventoryLevels on ProductVariant {
1321
+ id
1322
+ title
1323
+ sku
1324
+ available
1325
+ quantityAvailable
1326
+ inventoryLevels {
1327
+ ...InventoryLevel
1328
+ }
1329
+ }
package/mutations.graphql CHANGED
@@ -431,3 +431,69 @@ mutation GenerateReferralCode {
431
431
  ...GenerateReferralCodePayload
432
432
  }
433
433
  }
434
+
435
+ # ============================================
436
+ # Review Mutations
437
+ # ============================================
438
+
439
+ mutation ReviewCreate($input: ReviewCreateInput!) {
440
+ reviewCreate(input: $input) {
441
+ review {
442
+ ...ProductReview
443
+ }
444
+ userErrors {
445
+ ...UserError
446
+ }
447
+ }
448
+ }
449
+
450
+ mutation ReviewVote($reviewId: ID!, $isHelpful: Boolean!) {
451
+ reviewVote(reviewId: $reviewId, isHelpful: $isHelpful) {
452
+ review {
453
+ ...ProductReview
454
+ }
455
+ userErrors {
456
+ ...UserError
457
+ }
458
+ }
459
+ }
460
+
461
+ # ============================================
462
+ # Wishlist Mutations
463
+ # ============================================
464
+
465
+ mutation WishlistCreate($input: WishlistCreateInput!) {
466
+ wishlistCreate(input: $input) {
467
+ wishlist {
468
+ ...Wishlist
469
+ }
470
+ userErrors
471
+ }
472
+ }
473
+
474
+ mutation WishlistAddItem($wishlistId: ID!, $input: WishlistItemInput!) {
475
+ wishlistAddItem(wishlistId: $wishlistId, input: $input) {
476
+ wishlist {
477
+ ...Wishlist
478
+ }
479
+ userErrors
480
+ }
481
+ }
482
+
483
+ mutation WishlistRemoveItem($wishlistId: ID!, $itemId: ID!) {
484
+ wishlistRemoveItem(wishlistId: $wishlistId, itemId: $itemId) {
485
+ wishlist {
486
+ ...Wishlist
487
+ }
488
+ userErrors
489
+ }
490
+ }
491
+
492
+ mutation WishlistDelete($wishlistId: ID!) {
493
+ wishlistDelete(wishlistId: $wishlistId) {
494
+ wishlist {
495
+ ...Wishlist
496
+ }
497
+ userErrors
498
+ }
499
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doswiftly/storefront-operations",
3
- "version": "1.0.5",
3
+ "version": "4.4.0",
4
4
  "description": "GraphQL operations for DoSwiftly Storefront - SSOT from backend",
5
5
  "homepage": "https://doswiftly.pl",
6
6
  "publishConfig": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "main": "index.js",
10
10
  "exports": {
11
+ "./schema.graphql": "./schema.graphql",
11
12
  "./queries.graphql": "./queries.graphql",
12
13
  "./mutations.graphql": "./mutations.graphql",
13
14
  "./fragments.graphql": "./fragments.graphql",
@@ -27,6 +28,7 @@
27
28
  "author": "DoSwiftly Team",
28
29
  "license": "MIT",
29
30
  "files": [
31
+ "schema.graphql",
30
32
  "queries.graphql",
31
33
  "mutations.graphql",
32
34
  "fragments.graphql",
package/queries.graphql CHANGED
@@ -34,7 +34,7 @@ query Products(
34
34
  products(first: $first, after: $after, query: $query, sortKey: $sortKey, reverse: $reverse, filters: $filters) {
35
35
  edges {
36
36
  node {
37
- ...ProductBase
37
+ ...ProductCard
38
38
  }
39
39
  cursor
40
40
  }
@@ -49,7 +49,7 @@ query ProductSearch($query: String!, $first: Int = 20, $after: String, $filters:
49
49
  products(query: $query, first: $first, after: $after, filters: $filters) {
50
50
  edges {
51
51
  node {
52
- ...ProductBase
52
+ ...ProductCard
53
53
  }
54
54
  cursor
55
55
  }
@@ -70,7 +70,7 @@ query Collection($id: ID, $handle: String, $productsFirst: Int = 20, $productsAf
70
70
  products(first: $productsFirst, after: $productsAfter) {
71
71
  edges {
72
72
  node {
73
- ...ProductBase
73
+ ...ProductCard
74
74
  }
75
75
  cursor
76
76
  }
@@ -82,8 +82,8 @@ query Collection($id: ID, $handle: String, $productsFirst: Int = 20, $productsAf
82
82
  }
83
83
  }
84
84
 
85
- query Collections($first: Int = 20, $after: String, $sortKey: CollectionSortKeys = TITLE, $reverse: Boolean = false) {
86
- collections(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse) {
85
+ query Collections($first: Int = 20, $after: String, $query: String, $sortKey: CollectionSortKeys = TITLE, $reverse: Boolean = false) {
86
+ collections(first: $first, after: $after, query: $query, sortKey: $sortKey, reverse: $reverse) {
87
87
  edges {
88
88
  node {
89
89
  ...Collection
@@ -163,6 +163,21 @@ query Customer($customerAccessToken: String!) {
163
163
  }
164
164
  }
165
165
 
166
+ # Lightweight customer profile query (no orders, no addresses list)
167
+ # Use for settings/profile pages that only need basic customer info.
168
+ query CustomerProfile($customerAccessToken: String!) {
169
+ customer(customerAccessToken: $customerAccessToken) {
170
+ ...Customer
171
+ }
172
+ }
173
+
174
+ # Single order query — more efficient than fetching all customer data
175
+ query CustomerOrder($orderId: ID!, $customerAccessToken: String!) {
176
+ customerOrder(orderId: $orderId, customerAccessToken: $customerAccessToken) {
177
+ ...Order
178
+ }
179
+ }
180
+
166
181
  # ============================================
167
182
  # Checkout
168
183
  # ============================================
@@ -353,3 +368,111 @@ query ReferralStats {
353
368
  ...ReferralStats
354
369
  }
355
370
  }
371
+
372
+ # ============================================
373
+ # Reviews
374
+ # ============================================
375
+
376
+ query ProductReviews($productId: ID!, $first: Int = 10, $after: String, $sortKey: ReviewSortKey = CREATED_AT, $reverse: Boolean = true) {
377
+ productReviews(productId: $productId, first: $first, after: $after, sortKey: $sortKey, reverse: $reverse) {
378
+ edges {
379
+ node {
380
+ ...ProductReview
381
+ }
382
+ cursor
383
+ }
384
+ pageInfo {
385
+ ...PageInfo
386
+ }
387
+ totalCount
388
+ }
389
+ }
390
+
391
+ query ReviewStats($productId: ID!) {
392
+ reviewStats(productId: $productId) {
393
+ ...ReviewStats
394
+ }
395
+ }
396
+
397
+ # ============================================
398
+ # Wishlists
399
+ # ============================================
400
+
401
+ query Wishlists {
402
+ wishlists {
403
+ ...Wishlist
404
+ }
405
+ }
406
+
407
+ query WishlistById($id: ID!) {
408
+ wishlist(id: $id) {
409
+ ...Wishlist
410
+ }
411
+ }
412
+
413
+ # ============================================
414
+ # Blog
415
+ # ============================================
416
+
417
+ query BlogPosts($first: Int = 20, $after: String, $categorySlug: String, $tagSlug: String, $featured: Boolean, $sortKey: BlogPostSortKey = PUBLISHED_AT, $reverse: Boolean = false) {
418
+ blogPosts(first: $first, after: $after, categorySlug: $categorySlug, tagSlug: $tagSlug, featured: $featured, sortKey: $sortKey, reverse: $reverse) {
419
+ edges {
420
+ node {
421
+ ...BlogPost
422
+ }
423
+ cursor
424
+ }
425
+ pageInfo {
426
+ ...PageInfo
427
+ }
428
+ totalCount
429
+ }
430
+ }
431
+
432
+ query BlogPost($id: ID, $slug: String) {
433
+ blogPost(id: $id, slug: $slug) {
434
+ ...BlogPost
435
+ }
436
+ }
437
+
438
+ query BlogCategories {
439
+ blogCategories {
440
+ ...BlogCategory
441
+ }
442
+ }
443
+
444
+ query BlogTags {
445
+ blogTags {
446
+ ...BlogTag
447
+ }
448
+ }
449
+
450
+ # ============================================
451
+ # Recommendations
452
+ # ============================================
453
+
454
+ query ProductRecommendations($productId: ID!, $limit: Int = 8, $intent: RecommendationIntent = SIMILAR) {
455
+ productRecommendations(productId: $productId, limit: $limit, intent: $intent) {
456
+ ...ProductCard
457
+ }
458
+ }
459
+
460
+ query SimilarProducts($productId: String!, $first: Int = 6) {
461
+ similarProducts(productId: $productId, first: $first) {
462
+ items {
463
+ ...ProductCard
464
+ }
465
+ totalCount
466
+ }
467
+ }
468
+
469
+ query CartRecommendations($cartId: String!, $first: Int = 4) {
470
+ cartRecommendations(cartId: $cartId, first: $first) {
471
+ frequentlyBoughtTogether {
472
+ ...ProductCard
473
+ }
474
+ youMayAlsoLike {
475
+ ...ProductCard
476
+ }
477
+ }
478
+ }