@sixtyninecommerce/admin 0.10.0 → 0.12.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 (302) hide show
  1. package/README.md +5772 -118
  2. package/dist/auth/index.d.ts +7 -1
  3. package/dist/auth/index.js +8 -1
  4. package/dist/catalog/assets.d.ts +120 -8
  5. package/dist/catalog/assets.js +98 -12
  6. package/dist/catalog/collections.d.ts +279 -14
  7. package/dist/catalog/collections.js +246 -15
  8. package/dist/catalog/documents.d.ts +142 -0
  9. package/dist/catalog/documents.js +114 -0
  10. package/dist/catalog/index.d.ts +54 -6
  11. package/dist/catalog/index.js +10 -1
  12. package/dist/catalog/metadata.d.ts +244 -0
  13. package/dist/catalog/metadata.js +220 -0
  14. package/dist/catalog/products.d.ts +159 -19
  15. package/dist/catalog/products.js +88 -13
  16. package/dist/catalog/properties.d.ts +140 -14
  17. package/dist/catalog/properties.js +65 -1
  18. package/dist/catalog/recommendations.d.ts +288 -0
  19. package/dist/catalog/recommendations.js +237 -0
  20. package/dist/catalog/reviews.d.ts +381 -0
  21. package/dist/catalog/reviews.js +325 -0
  22. package/dist/catalog/types.d.ts +17 -1
  23. package/dist/catalog/types.js +10 -0
  24. package/dist/catalog/variant-find.d.ts +136 -0
  25. package/dist/catalog/variant-find.js +239 -0
  26. package/dist/catalog/variants.d.ts +293 -26
  27. package/dist/catalog/variants.js +164 -7
  28. package/dist/catalog/visibility.d.ts +38 -7
  29. package/dist/catalog/visibility.js +19 -1
  30. package/dist/client.d.ts +121 -9
  31. package/dist/client.js +53 -14
  32. package/dist/content/assets.d.ts +285 -0
  33. package/dist/content/assets.js +181 -0
  34. package/dist/content/forms.d.ts +332 -1
  35. package/dist/content/forms.js +140 -4
  36. package/dist/content/items.d.ts +292 -0
  37. package/dist/content/items.js +170 -0
  38. package/dist/content/menus.d.ts +254 -0
  39. package/dist/content/menus.js +70 -0
  40. package/dist/content/strings.d.ts +137 -0
  41. package/dist/content/strings.js +82 -0
  42. package/dist/content/types.d.ts +282 -0
  43. package/dist/content/types.js +100 -0
  44. package/dist/customers/age-verification.d.ts +66 -0
  45. package/dist/customers/age-verification.js +79 -0
  46. package/dist/customers/credit.d.ts +263 -0
  47. package/dist/customers/credit.js +244 -0
  48. package/dist/customers/delivery-preferences.d.ts +120 -0
  49. package/dist/customers/delivery-preferences.js +97 -0
  50. package/dist/customers/delivery-terms.d.ts +44 -0
  51. package/dist/customers/delivery-terms.js +32 -0
  52. package/dist/customers/groups.d.ts +207 -0
  53. package/dist/customers/groups.js +438 -0
  54. package/dist/customers/index.d.ts +462 -37
  55. package/dist/customers/index.js +317 -40
  56. package/dist/customers/invitation-status.d.ts +57 -0
  57. package/dist/customers/invitation-status.js +21 -0
  58. package/dist/customers/notes.d.ts +67 -0
  59. package/dist/customers/notes.js +51 -0
  60. package/dist/customers/notification-recipients.d.ts +97 -0
  61. package/dist/customers/notification-recipients.js +84 -0
  62. package/dist/customers/saved-lists.d.ts +42 -0
  63. package/dist/customers/saved-lists.js +61 -0
  64. package/dist/customers/vat.d.ts +83 -0
  65. package/dist/customers/vat.js +46 -0
  66. package/dist/customers/wire.d.ts +20 -0
  67. package/dist/customers/wire.js +35 -0
  68. package/dist/documents/ar.d.ts +307 -0
  69. package/dist/documents/ar.js +254 -0
  70. package/dist/documents/emails.d.ts +150 -0
  71. package/dist/documents/emails.js +220 -0
  72. package/dist/documents/index.d.ts +744 -21
  73. package/dist/documents/index.js +438 -24
  74. package/dist/documents/pdf.d.ts +2 -0
  75. package/dist/documents/pdf.js +11 -0
  76. package/dist/fulfillments/create.d.ts +167 -0
  77. package/dist/fulfillments/create.js +216 -0
  78. package/dist/fulfillments/index.d.ts +163 -11
  79. package/dist/fulfillments/index.js +135 -6
  80. package/dist/fulfillments/payment-policy.d.ts +64 -0
  81. package/dist/fulfillments/payment-policy.js +140 -0
  82. package/dist/fulfillments/warehouse.d.ts +724 -0
  83. package/dist/fulfillments/warehouse.js +914 -0
  84. package/dist/geo/index.d.ts +119 -0
  85. package/dist/geo/index.js +107 -0
  86. package/dist/index.d.ts +147 -39
  87. package/dist/index.js +51 -12
  88. package/dist/integrations/afas-actions.d.ts +234 -0
  89. package/dist/integrations/afas-actions.js +310 -0
  90. package/dist/integrations/afas-types.d.ts +483 -0
  91. package/dist/integrations/afas-types.js +5 -0
  92. package/dist/integrations/afas.d.ts +234 -0
  93. package/dist/integrations/afas.js +290 -0
  94. package/dist/integrations/connections.d.ts +103 -0
  95. package/dist/integrations/connections.js +55 -0
  96. package/dist/integrations/esp.d.ts +313 -0
  97. package/dist/integrations/esp.js +169 -0
  98. package/dist/integrations/exact-online-types.d.ts +965 -0
  99. package/dist/integrations/exact-online-types.js +6 -0
  100. package/dist/integrations/exact-online.d.ts +784 -0
  101. package/dist/integrations/exact-online.js +1089 -0
  102. package/dist/integrations/index.d.ts +44 -3
  103. package/dist/integrations/index.js +23 -3
  104. package/dist/integrations/pipeline-discoveries.d.ts +65 -0
  105. package/dist/integrations/pipeline-discoveries.js +65 -0
  106. package/dist/integrations/pipeline-keys.d.ts +13 -0
  107. package/dist/integrations/pipeline-keys.js +22 -0
  108. package/dist/integrations/pipeline-runs.d.ts +119 -0
  109. package/dist/integrations/pipeline-runs.js +87 -0
  110. package/dist/integrations/pipelines.d.ts +87 -15
  111. package/dist/integrations/pipelines.js +41 -22
  112. package/dist/integrations/price-import.d.ts +223 -0
  113. package/dist/integrations/price-import.js +124 -0
  114. package/dist/integrations/types.d.ts +367 -3
  115. package/dist/integrations/types.js +4 -3
  116. package/dist/locations/index.d.ts +66 -0
  117. package/dist/locations/index.js +31 -0
  118. package/dist/meta/index.d.ts +172 -0
  119. package/dist/metafields/index.d.ts +8 -1
  120. package/dist/metafields/index.js +10 -0
  121. package/dist/orders/cancel-with-money.d.ts +237 -0
  122. package/dist/orders/cancel-with-money.js +155 -0
  123. package/dist/orders/commit-edit.d.ts +23 -1
  124. package/dist/orders/commit-edit.js +19 -2
  125. package/dist/orders/deposit-policy.d.ts +3 -1
  126. package/dist/orders/drafts.d.ts +800 -0
  127. package/dist/orders/drafts.js +771 -0
  128. package/dist/orders/fields.d.ts +245 -0
  129. package/dist/orders/fields.js +188 -0
  130. package/dist/orders/get.d.ts +29 -19
  131. package/dist/orders/get.js +17 -8
  132. package/dist/orders/index.d.ts +66 -15
  133. package/dist/orders/index.js +25 -2
  134. package/dist/orders/lifecycle.d.ts +128 -0
  135. package/dist/orders/lifecycle.js +158 -0
  136. package/dist/orders/line-price-preview.d.ts +7 -2
  137. package/dist/orders/list.d.ts +243 -0
  138. package/dist/orders/list.js +487 -0
  139. package/dist/orders/manual-discounts.d.ts +13 -5
  140. package/dist/orders/manual-pricing.d.ts +5 -0
  141. package/dist/orders/money-view.d.ts +149 -0
  142. package/dist/orders/money-view.js +99 -0
  143. package/dist/orders/order-entry.d.ts +20 -7
  144. package/dist/orders/plan-edit.d.ts +34 -2
  145. package/dist/orders/plan-edit.js +112 -11
  146. package/dist/orders/settings.d.ts +201 -0
  147. package/dist/orders/settings.js +325 -0
  148. package/dist/orders/status.d.ts +159 -13
  149. package/dist/orders/status.js +21 -0
  150. package/dist/orders/surcharges.d.ts +20 -1
  151. package/dist/orders/surcharges.js +37 -11
  152. package/dist/payments/account-credit.d.ts +46 -0
  153. package/dist/payments/account-credit.js +42 -0
  154. package/dist/payments/ar.d.ts +292 -0
  155. package/dist/payments/ar.js +223 -0
  156. package/dist/payments/connected-account.d.ts +47 -0
  157. package/dist/payments/connected-account.js +61 -0
  158. package/dist/payments/credit-noted-fields.d.ts +19 -0
  159. package/dist/payments/credit-noted-fields.js +69 -0
  160. package/dist/payments/customer-consent.d.ts +30 -0
  161. package/dist/payments/customer-consent.js +49 -0
  162. package/dist/payments/index.d.ts +340 -19
  163. package/dist/payments/index.js +151 -1
  164. package/dist/payments/methods.d.ts +303 -8
  165. package/dist/payments/methods.js +337 -7
  166. package/dist/payments/mollie-methods.d.ts +159 -0
  167. package/dist/payments/mollie-methods.js +141 -0
  168. package/dist/payments/payment-links.d.ts +180 -0
  169. package/dist/payments/payment-links.js +97 -0
  170. package/dist/payments/provider-keys.d.ts +150 -0
  171. package/dist/payments/provider-keys.js +118 -0
  172. package/dist/payments/providers.d.ts +86 -11
  173. package/dist/payments/providers.js +29 -0
  174. package/dist/payments/requests.d.ts +147 -0
  175. package/dist/payments/requests.js +93 -0
  176. package/dist/payments/store-credit-fields.d.ts +15 -0
  177. package/dist/payments/store-credit-fields.js +51 -0
  178. package/dist/pickups/index.d.ts +106 -0
  179. package/dist/pickups/index.js +108 -0
  180. package/dist/platform/channels.d.ts +132 -0
  181. package/dist/platform/channels.js +128 -0
  182. package/dist/platform/email-gateways.d.ts +165 -0
  183. package/dist/platform/email-gateways.js +78 -0
  184. package/dist/platform/geo.d.ts +123 -0
  185. package/dist/platform/geo.js +144 -0
  186. package/dist/platform/tax.d.ts +162 -0
  187. package/dist/platform/tax.js +169 -0
  188. package/dist/pos/anomalies.d.ts +158 -0
  189. package/dist/pos/anomalies.js +64 -0
  190. package/dist/pos/assisted-selling.d.ts +79 -0
  191. package/dist/pos/assisted-selling.js +38 -0
  192. package/dist/pos/exchanges.d.ts +108 -0
  193. package/dist/pos/exchanges.js +22 -0
  194. package/dist/pos/gift-cards.d.ts +135 -0
  195. package/dist/pos/gift-cards.js +47 -0
  196. package/dist/pos/index.d.ts +2370 -58
  197. package/dist/pos/index.js +854 -7
  198. package/dist/pos/no-receipt.d.ts +94 -0
  199. package/dist/pos/no-receipt.js +25 -0
  200. package/dist/pos/offline-sales.d.ts +228 -0
  201. package/dist/pos/offline-sales.js +170 -0
  202. package/dist/pos/pay-by-link.d.ts +143 -0
  203. package/dist/pos/pay-by-link.js +57 -0
  204. package/dist/pos/quick-keys.d.ts +103 -0
  205. package/dist/pos/quick-keys.js +45 -0
  206. package/dist/pos/reports.d.ts +360 -0
  207. package/dist/pos/reports.js +62 -0
  208. package/dist/pos/returns.d.ts +244 -0
  209. package/dist/pos/returns.js +83 -0
  210. package/dist/pricing/bulk-coupons.d.ts +139 -0
  211. package/dist/pricing/bulk-coupons.js +112 -0
  212. package/dist/pricing/index.d.ts +174 -4
  213. package/dist/pricing/index.js +174 -1
  214. package/dist/pricing/price-lists.d.ts +606 -0
  215. package/dist/pricing/price-lists.js +600 -0
  216. package/dist/purchasing/alerts.d.ts +104 -0
  217. package/dist/purchasing/alerts.js +88 -0
  218. package/dist/purchasing/backorders.d.ts +61 -0
  219. package/dist/purchasing/backorders.js +92 -0
  220. package/dist/purchasing/cost.d.ts +110 -0
  221. package/dist/purchasing/cost.js +86 -0
  222. package/dist/purchasing/index.d.ts +54 -0
  223. package/dist/purchasing/index.js +24 -0
  224. package/dist/purchasing/links.d.ts +263 -0
  225. package/dist/purchasing/links.js +192 -0
  226. package/dist/purchasing/purchase-orders.d.ts +551 -0
  227. package/dist/purchasing/purchase-orders.js +491 -0
  228. package/dist/purchasing/receipts.d.ts +396 -0
  229. package/dist/purchasing/receipts.js +287 -0
  230. package/dist/purchasing/replenishment.d.ts +281 -0
  231. package/dist/purchasing/replenishment.js +240 -0
  232. package/dist/purchasing/returns.d.ts +273 -0
  233. package/dist/purchasing/returns.js +229 -0
  234. package/dist/purchasing/settings.d.ts +152 -0
  235. package/dist/purchasing/settings.js +181 -0
  236. package/dist/purchasing/suppliers.d.ts +476 -0
  237. package/dist/purchasing/suppliers.js +438 -0
  238. package/dist/quotes/index.d.ts +518 -0
  239. package/dist/quotes/index.js +439 -0
  240. package/dist/reports/index.d.ts +71 -8
  241. package/dist/reports/index.js +74 -11
  242. package/dist/returns/index.d.ts +675 -21
  243. package/dist/returns/index.js +756 -11
  244. package/dist/search/index.d.ts +76 -1
  245. package/dist/search/index.js +63 -0
  246. package/dist/seo/config.d.ts +128 -0
  247. package/dist/seo/config.js +95 -0
  248. package/dist/seo/redirects.d.ts +125 -0
  249. package/dist/seo/redirects.js +60 -1
  250. package/dist/seo/settings.d.ts +147 -0
  251. package/dist/seo/settings.js +209 -0
  252. package/dist/shipping/definitions.d.ts +29 -0
  253. package/dist/shipping/definitions.js +47 -0
  254. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  255. package/dist/shipping/fulfillment-profiles.js +104 -0
  256. package/dist/shipping/hendriks.d.ts +97 -0
  257. package/dist/shipping/hendriks.js +50 -0
  258. package/dist/shipping/index.d.ts +54 -0
  259. package/dist/shipping/index.js +27 -0
  260. package/dist/shipping/methods.d.ts +205 -0
  261. package/dist/shipping/methods.js +240 -0
  262. package/dist/shipping/myparcel.d.ts +668 -0
  263. package/dist/shipping/myparcel.js +466 -0
  264. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  265. package/dist/shipping/parcel-rule.d.ts +6 -5
  266. package/dist/shipping/parcel-rule.js +21 -23
  267. package/dist/shipping/rate-tables.d.ts +324 -0
  268. package/dist/shipping/rate-tables.js +390 -0
  269. package/dist/shipping/ship-to-countries.d.ts +75 -0
  270. package/dist/shipping/ship-to-countries.js +47 -0
  271. package/dist/stock/by-location.d.ts +86 -0
  272. package/dist/stock/by-location.js +94 -0
  273. package/dist/stock/index.d.ts +18 -4
  274. package/dist/stock/index.js +7 -0
  275. package/dist/stock/locations.d.ts +144 -0
  276. package/dist/stock/locations.js +168 -0
  277. package/dist/stock/settings.d.ts +106 -0
  278. package/dist/stock/settings.js +103 -0
  279. package/dist/stock/transfers.d.ts +386 -52
  280. package/dist/stock/transfers.js +222 -17
  281. package/dist/stored-value/index.d.ts +312 -11
  282. package/dist/stored-value/index.js +223 -4
  283. package/dist/subscriptions/index.d.ts +682 -0
  284. package/dist/subscriptions/index.js +815 -0
  285. package/dist/tax/display-policy.d.ts +105 -0
  286. package/dist/tax/display-policy.js +84 -0
  287. package/dist/tax/index.d.ts +79 -1
  288. package/dist/tax/index.js +52 -0
  289. package/dist/tenant/email-style.d.ts +102 -0
  290. package/dist/tenant/email-style.js +72 -0
  291. package/dist/tenant/email-templates.d.ts +287 -0
  292. package/dist/tenant/email-templates.js +271 -0
  293. package/dist/tenant/notifications.d.ts +23 -17
  294. package/dist/tenant/settings-sources.d.ts +1 -1
  295. package/dist/tenant/time-zone.d.ts +17 -0
  296. package/dist/tenant/time-zone.js +11 -0
  297. package/dist/types.d.ts +50 -3
  298. package/dist/users/index.d.ts +3 -0
  299. package/dist/users/index.js +2 -0
  300. package/dist/users/warehouses.d.ts +50 -0
  301. package/dist/users/warehouses.js +51 -0
  302. package/package.json +2 -2
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Product recommendations (headless spec §9.2 `catalog`: "recommendations"; W10-13): the store's
3
+ * recommendation groups ("Customers also bought", "Complete the look"), what each one draws its
4
+ * products from, and the products hand-picked per product for a manual group.
5
+ *
6
+ * - **A `DYNAMIC` group's entries are its rules**, read in `position` order: one named product
7
+ * (`PRODUCT`), the products of a collection (`COLLECTION`) or with a facet value
8
+ * (`FACET_VALUE`), or the viewed product's own collections (`SAME_COLLECTION`) or properties
9
+ * (`SAME_PROPERTIES`). The storefront fills the group up to `maxItems` from the first entry on,
10
+ * never with the viewed product, a disabled product or one the store hides from the shopper.
11
+ * - **A `MANUAL` group has no entries**: its products are picked per viewed product
12
+ * (`manual.set`), and the storefront shows them in the order picked. A group's mode is chosen
13
+ * when it is created and never changes.
14
+ * - **A disabled group** (`enabled: false`) is kept but never shown on the storefront.
15
+ * - **Positions** are integers; a group or entry created without one goes after the last.
16
+ * `reorder` rewrites them as `0, 1, 2…` in the order given.
17
+ *
18
+ * Permissions: reads need `ReadCatalog`, writes `UpdateCatalog`. Every row is the store's own: a
19
+ * group or entry of another store is never answered and never changed (a read answers `null` or
20
+ * leaves it out; a write refuses it as unknown), and a rule only ever draws from the store's own
21
+ * products, collections and facet values.
22
+ *
23
+ * Refusals, GraphQL errors mapped by `core`. An unknown id and another store's get the same answer,
24
+ * so none tells whether another store holds the row:
25
+ * - `ENTITY_NOT_FOUND` → `NotFoundError` (404): a group or entry the store does not have, and the
26
+ * viewed product of `manual.set` when the store does not hold it.
27
+ * - `RECOMMENDATION_SOURCE_NOT_FOUND` → `NotFoundError` (404): a rule's `sourceId` that is not a
28
+ * product, collection or facet value of the store (`entries.create`, `entries.update`).
29
+ * - `RECOMMENDATION_TARGET_NOT_FOUND` → `NotFoundError` (404): `manual.set` picks the store does
30
+ * not hold, each named in the message and in `details` (`field` `/targetProductIds/<index>`).
31
+ * - `RECOMMENDATION_GROUP_SLUG_TAKEN` → `ConflictError` (409): another of the store's groups holds
32
+ * the slug, also when two writes race for one free slug.
33
+ * - `RECOMMENDATION_MAX_ITEMS_INVALID` → `ValidationError` (400): a group's `maxItems` that is not
34
+ * a whole number from 1 to 50 ({@link RECOMMENDATION_MAX_ITEMS_RANGE}), on `groups.create` and
35
+ * on a `groups.update` that sends it, `details[0].field` `/input/maxItems`. This SDK refuses it
36
+ * before anything is sent, with the same code and detail, so an older platform release (which
37
+ * stores any value) never receives one.
38
+ * - `USER_INPUT_ERROR` → `ValidationError` (400), in the engine's words: a rule that does not fit
39
+ * its group or its type (a `MANUAL` group, a `sourceId` missing or sent where none goes), picks
40
+ * in a `DYNAMIC` group.
41
+ * - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
42
+ *
43
+ * An id that is not a uuid (any id argument, a `sourceId` and each pick included) is refused
44
+ * before all of these with core's `ENTITY_NOT_FOUND` (`NotFoundError`, 404, no `details`), so
45
+ * branch on the class for not-found and on the code only to tell which row is missing.
46
+ *
47
+ * A platform release older than these codes answers each not-found and a taken slug as
48
+ * `USER_INPUT_ERROR` (400) in its own words ("Recommendation group not found"), a slug lost in a
49
+ * race as `DUPLICATE_RECORD`, and takes another store's collection or facet value as a rule's
50
+ * source.
51
+ *
52
+ * No release gate: every operation here is served by every platform release this SDK supports.
53
+ * No method deduplicates on `idempotencyKey`: repeating a `create` makes a second row.
54
+ */
55
+ import { ValidationError } from '@sixtyninecommerce/core';
56
+ import { assertDeleted, DELETION_FIELDS, fieldOf } from './types.js';
57
+ /**
58
+ * The fewest and the most products a group may show (`maxItems`), both included. The platform
59
+ * refuses any other value with `RECOMMENDATION_MAX_ITEMS_INVALID`, and so does this SDK before
60
+ * sending.
61
+ */
62
+ export const RECOMMENDATION_MAX_ITEMS_RANGE = { min: 1, max: 50 };
63
+ /** The source types whose entry names a row (`sourceId`); the others must name none. */
64
+ export const RECOMMENDATION_SOURCE_TYPES_WITH_ID = [
65
+ 'PRODUCT',
66
+ 'COLLECTION',
67
+ 'FACET_VALUE',
68
+ ];
69
+ const ENTRY_FIELDS = /* GraphQL */ `
70
+ id
71
+ groupId
72
+ sourceType
73
+ sourceId
74
+ position
75
+ createdAt
76
+ updatedAt
77
+ `;
78
+ const GROUP_SUMMARY_FIELDS = /* GraphQL */ `
79
+ id
80
+ name
81
+ slug
82
+ description
83
+ maxItems
84
+ position
85
+ enabled
86
+ mode
87
+ createdAt
88
+ updatedAt
89
+ `;
90
+ const GROUP_FIELDS = /* GraphQL */ `
91
+ ${GROUP_SUMMARY_FIELDS}
92
+ entries { ${ENTRY_FIELDS} }
93
+ `;
94
+ export const RECOMMENDATION_GROUPS_QUERY = /* GraphQL */ `
95
+ query CatalogRecommendationGroups {
96
+ recommendationGroups { ${GROUP_FIELDS} }
97
+ }
98
+ `;
99
+ export const RECOMMENDATION_GROUP_QUERY = /* GraphQL */ `
100
+ query CatalogRecommendationGroup($id: ID!) {
101
+ recommendationGroup(id: $id) { ${GROUP_FIELDS} }
102
+ }
103
+ `;
104
+ export const CREATE_RECOMMENDATION_GROUP_MUTATION = /* GraphQL */ `
105
+ mutation CatalogCreateRecommendationGroup($input: CreateRecommendationGroupInput!) {
106
+ createRecommendationGroup(input: $input) { ${GROUP_FIELDS} }
107
+ }
108
+ `;
109
+ export const UPDATE_RECOMMENDATION_GROUP_MUTATION = /* GraphQL */ `
110
+ mutation CatalogUpdateRecommendationGroup($id: ID!, $input: UpdateRecommendationGroupInput!) {
111
+ updateRecommendationGroup(id: $id, input: $input) { ${GROUP_FIELDS} }
112
+ }
113
+ `;
114
+ export const DELETE_RECOMMENDATION_GROUP_MUTATION = /* GraphQL */ `
115
+ mutation CatalogDeleteRecommendationGroup($id: ID!) {
116
+ deleteRecommendationGroup(id: $id) { ${DELETION_FIELDS} }
117
+ }
118
+ `;
119
+ export const REORDER_RECOMMENDATION_GROUPS_MUTATION = /* GraphQL */ `
120
+ mutation CatalogReorderRecommendationGroups($ids: [ID!]!) {
121
+ reorderRecommendationGroups(ids: $ids) { ${GROUP_FIELDS} }
122
+ }
123
+ `;
124
+ export const CREATE_RECOMMENDATION_ENTRY_MUTATION = /* GraphQL */ `
125
+ mutation CatalogCreateRecommendationEntry($input: CreateRecommendationEntryInput!) {
126
+ createRecommendationEntry(input: $input) { ${ENTRY_FIELDS} }
127
+ }
128
+ `;
129
+ export const UPDATE_RECOMMENDATION_ENTRY_MUTATION = /* GraphQL */ `
130
+ mutation CatalogUpdateRecommendationEntry($id: ID!, $input: UpdateRecommendationEntryInput!) {
131
+ updateRecommendationEntry(id: $id, input: $input) { ${ENTRY_FIELDS} }
132
+ }
133
+ `;
134
+ export const DELETE_RECOMMENDATION_ENTRY_MUTATION = /* GraphQL */ `
135
+ mutation CatalogDeleteRecommendationEntry($id: ID!) {
136
+ deleteRecommendationEntry(id: $id) { ${DELETION_FIELDS} }
137
+ }
138
+ `;
139
+ export const REORDER_RECOMMENDATION_ENTRIES_MUTATION = /* GraphQL */ `
140
+ mutation CatalogReorderRecommendationEntries($groupId: ID!, $ids: [ID!]!) {
141
+ reorderRecommendationEntries(groupId: $groupId, ids: $ids) { ${ENTRY_FIELDS} }
142
+ }
143
+ `;
144
+ // The engine does not load `entries` for these groups (a MANUAL group has none), and selecting a
145
+ // non-null field it did not load fails the whole read: never add `entries` here.
146
+ export const PRODUCT_MANUAL_RECOMMENDATIONS_QUERY = /* GraphQL */ `
147
+ query CatalogProductManualRecommendations($productId: ID!) {
148
+ productManualRecommendations(productId: $productId) {
149
+ group { ${GROUP_SUMMARY_FIELDS} }
150
+ productIds
151
+ }
152
+ }
153
+ `;
154
+ export const SET_PRODUCT_MANUAL_RECOMMENDATIONS_MUTATION = /* GraphQL */ `
155
+ mutation CatalogSetProductManualRecommendations(
156
+ $productId: ID!
157
+ $groupId: ID!
158
+ $targetProductIds: [ID!]!
159
+ ) {
160
+ setProductManualRecommendations(
161
+ productId: $productId
162
+ groupId: $groupId
163
+ targetProductIds: $targetProductIds
164
+ ) { ${GROUP_SUMMARY_FIELDS} }
165
+ }
166
+ `;
167
+ /**
168
+ * The platform's `maxItems` rule, checked before sending: a value present and not null must be a
169
+ * whole number in {@link RECOMMENDATION_MAX_ITEMS_RANGE}. Same code, message and detail as the
170
+ * engine's refusal, so a caller handles one shape whichever side refused.
171
+ */
172
+ function assertMaxItems(input) {
173
+ const maxItems = input?.maxItems;
174
+ if (maxItems == null)
175
+ return;
176
+ const { min, max } = RECOMMENDATION_MAX_ITEMS_RANGE;
177
+ if (typeof maxItems === 'number' &&
178
+ Number.isInteger(maxItems) &&
179
+ maxItems >= min &&
180
+ maxItems <= max) {
181
+ return;
182
+ }
183
+ const message = `Max items must be a whole number from ${min} to ${max}; got ${JSON.stringify(maxItems)}`;
184
+ throw new ValidationError({
185
+ code: 'RECOMMENDATION_MAX_ITEMS_INVALID',
186
+ message,
187
+ status: 400,
188
+ service: 'vendure',
189
+ details: [{ field: '/input/maxItems', code: 'OUT_OF_RANGE', message }],
190
+ });
191
+ }
192
+ /** Entries in `position` order, the older first on a tie, as the engine's own entry reads sort. */
193
+ function byPosition(entries) {
194
+ return [...entries].sort((a, b) => a.position - b.position || a.createdAt.localeCompare(b.createdAt));
195
+ }
196
+ // The engine's group list loads `entries` without an order; only its one-group read sorts them.
197
+ function withSortedEntries(group) {
198
+ return { ...group, entries: byPosition(group.entries) };
199
+ }
200
+ export function createRecommendationsModule(transport) {
201
+ const call = (document, field, variables, options) => fieldOf(transport, document, field, variables, options);
202
+ return {
203
+ groups: {
204
+ list: async (options) => (await call(RECOMMENDATION_GROUPS_QUERY, 'recommendationGroups', {}, options)).map(withSortedEntries),
205
+ async get(id, options) {
206
+ const group = await call(RECOMMENDATION_GROUP_QUERY, 'recommendationGroup', { id }, options);
207
+ return group === null ? null : withSortedEntries(group);
208
+ },
209
+ create: async (input, options) => {
210
+ assertMaxItems(input);
211
+ return withSortedEntries(await call(CREATE_RECOMMENDATION_GROUP_MUTATION, 'createRecommendationGroup', { input }, options));
212
+ },
213
+ update: async (id, input, options) => {
214
+ assertMaxItems(input);
215
+ return withSortedEntries(await call(UPDATE_RECOMMENDATION_GROUP_MUTATION, 'updateRecommendationGroup', { id, input }, options));
216
+ },
217
+ async delete(id, options) {
218
+ const response = await call(DELETE_RECOMMENDATION_GROUP_MUTATION, 'deleteRecommendationGroup', { id }, options);
219
+ assertDeleted('RECOMMENDATION_GROUP_NOT_DELETED', [response]);
220
+ },
221
+ reorder: async (ids, options) => (await call(REORDER_RECOMMENDATION_GROUPS_MUTATION, 'reorderRecommendationGroups', { ids }, options)).map(withSortedEntries),
222
+ },
223
+ entries: {
224
+ create: (input, options) => call(CREATE_RECOMMENDATION_ENTRY_MUTATION, 'createRecommendationEntry', { input }, options),
225
+ update: (id, input, options) => call(UPDATE_RECOMMENDATION_ENTRY_MUTATION, 'updateRecommendationEntry', { id, input }, options),
226
+ async delete(id, options) {
227
+ const response = await call(DELETE_RECOMMENDATION_ENTRY_MUTATION, 'deleteRecommendationEntry', { id }, options);
228
+ assertDeleted('RECOMMENDATION_ENTRY_NOT_DELETED', [response]);
229
+ },
230
+ reorder: (groupId, ids, options) => call(REORDER_RECOMMENDATION_ENTRIES_MUTATION, 'reorderRecommendationEntries', { groupId, ids }, options),
231
+ },
232
+ manual: {
233
+ get: (productId, options) => call(PRODUCT_MANUAL_RECOMMENDATIONS_QUERY, 'productManualRecommendations', { productId }, options),
234
+ set: (productId, groupId, productIds, options) => call(SET_PRODUCT_MANUAL_RECOMMENDATIONS_MUTATION, 'setProductManualRecommendations', { productId, groupId, targetProductIds: productIds }, options),
235
+ },
236
+ };
237
+ }
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Product reviews (headless spec §9.2 `catalog`: "reviews moderation"; W10-117): the reviews the
3
+ * store's customers wrote, moderating them (what the storefront shows under the store's name),
4
+ * the store's reply to one, the review statistics, and the review attributes (the questions a
5
+ * review form asks: "Quality", "Fit", …).
6
+ *
7
+ * - **What the storefront shows**: only `APPROVED` reviews, with the reviewer's display name,
8
+ * rating, title, text, attribute answers and the store's reply. `PENDING` (waiting), `FLAGGED`
9
+ * (the automatic checks or a shopper's report asked for a look) and `REJECTED` reviews are never
10
+ * shown. Approving publishes the review; rejecting, flagging or re-opening it (`PENDING`) takes it
11
+ * off. Each moderation is recorded in the review's moderation log with the staff member and the
12
+ * reason given.
13
+ * - **The reply** is the store's own text, shown under an approved review on the storefront.
14
+ *
15
+ * Customer data (headless spec §6.3): an app installation below `customerData: full` reads every
16
+ * review with what the customer typed blanked: `title`, `body`, `attributeValues` (ratings and
17
+ * self-declared details such as an age band) and `ipAddressHash`, the edit history's previous
18
+ * texts and the moderation log's reasons and AI analysis answer `null`, and so does
19
+ * `customerDisplayName` below `identity`. Every record of such an answer carries `redacted: true`,
20
+ * so a blanked field never reads as "the customer wrote none". A `search` below `full` is refused
21
+ * `CUSTOMER_DATA_NOT_GRANTED` (`PermissionDeniedError`, 403). The answers of the writes are
22
+ * blanked the same way.
23
+ *
24
+ * Publishing needs `full` (user decision O6): approving a review (`moderate`, or `bulkModerate`
25
+ * with `APPROVED`) and `reply.set` publish under the store's name next to text a caller below
26
+ * `full` reads blanked, so an app installation below it (or a request narrowed below it) is
27
+ * refused them with `CUSTOMER_DATA_NOT_GRANTED` (`PermissionDeniedError`, 403; `bulkModerate`
28
+ * reports it on each review). It may still reject, flag or re-open a review and remove the
29
+ * store's reply. A staff session and a personal API token act at `full`.
30
+ *
31
+ * Permissions: reads need `ReadCatalog`, writes `UpdateCatalog`. Every row is the store's own: a
32
+ * review or attribute of another store is never answered and never changed.
33
+ *
34
+ * Refusals, GraphQL errors mapped by `core`. An unknown id and another store's get the same
35
+ * answer, so none tells whether another store holds the row:
36
+ * - `ENTITY_NOT_FOUND` → `NotFoundError` (404): a review or attribute the store does not have. An
37
+ * id that is not a uuid gets it too, before anything is read.
38
+ * - `REVIEW_ATTRIBUTE_SLUG_TAKEN` → `ConflictError` (409): another of the store's attributes holds
39
+ * the slug, the one derived from the label included, also when two writes race for it.
40
+ * - `REVIEW_ATTRIBUTE_COLLECTION_NOT_FOUND` → `NotFoundError` (404): `attributes.setCollections`
41
+ * named a collection the store does not hold, each in `details` (`field` `/collectionIds/<i>`).
42
+ * - `BULK_ACTION_INPUT_INVALID` → `ValidationError` (400): a `bulkModerate` selection of no
43
+ * review, more than {@link MAX_BULK_REVIEWS} or one twice, refused before anything is sent.
44
+ * - `USER_INPUT_ERROR` → `ValidationError` (400), in the engine's words: a reply over 2,000
45
+ * characters, an attribute type other than `rating` / `selection`, a selection with fewer than
46
+ * two options.
47
+ * - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
48
+ *
49
+ * Release: `bulkModerate` needs a platform release with `moderateReviews` (deploy vendure-server
50
+ * first: an older one refuses the document). Every other method is served by every release this
51
+ * SDK supports; an older release answers each not-found and a taken slug as `USER_INPUT_ERROR`
52
+ * (400), a taken slug as an uncoded database error, and takes another store's collection in
53
+ * `setCollections`. No method deduplicates on `idempotencyKey`.
54
+ */
55
+ import { type Page, type Transport } from '@sixtyninecommerce/core';
56
+ import type { Call } from '../types.js';
57
+ export type ReviewStatus = 'PENDING' | 'APPROVED' | 'REJECTED' | 'FLAGGED';
58
+ export declare const REVIEW_STATUSES: readonly ReviewStatus[];
59
+ /** The admin list's orders; ties go to the newer review. */
60
+ export type ReviewSortKey = 'NEWEST' | 'OLDEST' | 'HIGHEST_RATING' | 'LOWEST_RATING' | 'MOST_HELPFUL';
61
+ /** How many reviews one list page holds at most: the engine answers no more. */
62
+ export declare const MAX_REVIEW_PAGE_SIZE = 50;
63
+ /** How many reviews one `bulkModerate` call may name. */
64
+ export declare const MAX_BULK_REVIEWS = 100;
65
+ /** A review as the list and the writes answer it. */
66
+ export interface ProductReviewSummary {
67
+ id: string;
68
+ channelId: string;
69
+ customerId: string;
70
+ productId: string;
71
+ /** The product's name in the request's language (any language when it has none); null when gone. */
72
+ productName: string | null;
73
+ variantId: string | null;
74
+ /** 1 to 5; null when the store asks for no rating. */
75
+ rating: number | null;
76
+ title: string | null;
77
+ body: string | null;
78
+ status: ReviewStatus;
79
+ /** True when the customer had an order with this product when they wrote it. */
80
+ isVerifiedPurchase: boolean;
81
+ /** ISO-8601 UTC. */
82
+ purchaseDate: string | null;
83
+ /** ISO-8601 UTC. */
84
+ deliveryDate: string | null;
85
+ isEdited: boolean;
86
+ editCount: number;
87
+ /** The reviewer's first and last name as the storefront shows them; null when none. */
88
+ customerDisplayName: string | null;
89
+ imageAssetIds: string[] | null;
90
+ videoAssetIds: string[] | null;
91
+ /** The answers to the review attributes, keyed by attribute id: a 1–5 rating or an option. */
92
+ attributeValues: Record<string, number | string> | null;
93
+ helpfulCount: number;
94
+ notHelpfulCount: number;
95
+ /** The store's reply, shown under the review once it is approved. */
96
+ merchantReply: string | null;
97
+ /** ISO-8601 UTC. */
98
+ merchantReplyAt: string | null;
99
+ /** A hash of the address the review was sent from (spam checks). */
100
+ ipAddressHash: string | null;
101
+ /** ISO-8601 UTC. */
102
+ createdAt: string;
103
+ /** ISO-8601 UTC. */
104
+ updatedAt: string;
105
+ /**
106
+ * Set when the caller's customer-data level blanked part of the answer (an app installation
107
+ * below `customerData: full`): read a `null` text or name as "not granted", never as "none".
108
+ */
109
+ redacted?: true;
110
+ }
111
+ export interface ReviewEditHistoryEntry {
112
+ id: string;
113
+ previousRating: number | null;
114
+ previousTitle: string | null;
115
+ previousBody: string | null;
116
+ previousAttributeValues: Record<string, number | string> | null;
117
+ /** ISO-8601 UTC. */
118
+ editedAt: string;
119
+ /** True when the edit sent the review back to moderation. */
120
+ reEnteredModeration: boolean;
121
+ /** ISO-8601 UTC. */
122
+ createdAt: string;
123
+ }
124
+ export interface ReviewModerationLogEntry {
125
+ id: string;
126
+ /** `profanity`, `spam`, `ai`, `manual` (staff) or `customer_report`. */
127
+ step: string;
128
+ /** `approved`, `rejected`, `flagged` or `skipped`. */
129
+ action: string;
130
+ reason: string | null;
131
+ /** The AI check's confidence, 0 to 1. */
132
+ confidence: number | null;
133
+ aiAnalysis: Record<string, unknown> | null;
134
+ /** The staff user (a User id) for a `manual` step. */
135
+ performedBy: string | null;
136
+ tokenUsage: {
137
+ inputTokens: number;
138
+ outputTokens: number;
139
+ } | null;
140
+ /** ISO-8601 UTC. */
141
+ createdAt: string;
142
+ }
143
+ /** One review with its edit history and moderation log, as `get` answers it. */
144
+ export interface ProductReview extends ProductReviewSummary {
145
+ editHistory: ReviewEditHistoryEntry[];
146
+ moderationLogs: ReviewModerationLogEntry[];
147
+ }
148
+ export interface ReviewStats {
149
+ /** Every review of the store, whatever its status. */
150
+ totalReviews: number;
151
+ pendingCount: number;
152
+ approvedCount: number;
153
+ rejectedCount: number;
154
+ flaggedCount: number;
155
+ /** Over the approved reviews with a rating; null when there is none. */
156
+ averageRating: number | null;
157
+ /** Reviews of any status written in the last 7 days, counted back from now (not the calendar week). */
158
+ ratingsThisWeek: number;
159
+ /** Reviews of any status written in the last 30 days, counted back from now. */
160
+ ratingsThisMonth: number;
161
+ }
162
+ export interface ReviewListOptions extends Call {
163
+ status?: ReviewStatus;
164
+ productId?: string;
165
+ customerId?: string;
166
+ /**
167
+ * Matches the title or the text, any case (`%` and `_` are literal). Refused
168
+ * `CUSTOMER_DATA_NOT_GRANTED` below `customerData: full`: it searches what the customer typed.
169
+ */
170
+ search?: string;
171
+ /** 1 to 5. The engine filters nothing on any other value. */
172
+ rating?: number;
173
+ /** Defaults to `NEWEST`. */
174
+ sort?: ReviewSortKey;
175
+ /** 1 to {@link MAX_REVIEW_PAGE_SIZE}; 25 when left out. */
176
+ limit?: number;
177
+ /** The cursor from the previous page. */
178
+ after?: string;
179
+ }
180
+ /** A moderation: the status the review takes, and the reason recorded with it. */
181
+ export interface ReviewModerationInput {
182
+ action: ReviewStatus;
183
+ /** Recorded in the review's moderation log; not shown on the storefront. */
184
+ reason?: string;
185
+ }
186
+ export interface ReviewBulkModerationInput extends ReviewModerationInput {
187
+ /** 1 to {@link MAX_BULK_REVIEWS} reviews of the store, each named once. */
188
+ ids: readonly string[];
189
+ }
190
+ /** One review's outcome in a `bulkModerate` call. */
191
+ export interface ReviewModerationOutcome {
192
+ id: string;
193
+ /** `OK`, or `FAILED` (a string on the wire: read anything else as not done). */
194
+ status: 'OK' | 'FAILED' | (string & {});
195
+ /**
196
+ * When FAILED: the single door's own code: `ENTITY_NOT_FOUND` (no such review in this store),
197
+ * …; `INTERNAL_SERVER_ERROR` for a fault the engine logged.
198
+ */
199
+ code: string | null;
200
+ /** When FAILED: the door's sentence. */
201
+ message: string | null;
202
+ }
203
+ export type ReviewAttributeInputType = 'rating' | 'selection';
204
+ export type ReviewAttributeSection = 'product' | 'demographic';
205
+ /** A question the store's review form asks. */
206
+ export interface ReviewAttribute {
207
+ id: string;
208
+ /** Unique within the store. */
209
+ slug: string;
210
+ label: string;
211
+ /** `rating` (1 to 5) or `selection` (one of `options`). A string on the wire. */
212
+ inputType: ReviewAttributeInputType | (string & {});
213
+ /** The choices of a `selection`; null for a `rating`. */
214
+ options: string[] | null;
215
+ isRequired: boolean;
216
+ /** A disabled attribute is kept but never asked. */
217
+ enabled: boolean;
218
+ /** `product` (feedback on the product) or `demographic` (about the reviewer). */
219
+ section: ReviewAttributeSection | (string & {});
220
+ position: number;
221
+ /**
222
+ * The store's collections the attribute is asked for: it applies to a product in any of them.
223
+ * Empty: it applies to every product.
224
+ */
225
+ collectionIds: string[];
226
+ /** ISO-8601 UTC. */
227
+ createdAt: string;
228
+ /** ISO-8601 UTC. */
229
+ updatedAt: string;
230
+ }
231
+ export interface CreateReviewAttributeInput {
232
+ label: string;
233
+ inputType: ReviewAttributeInputType;
234
+ /** Defaults to the label, lower-cased, every run of other characters than `a-z0-9` a `-`. */
235
+ slug?: string;
236
+ /** Required for a `selection`: at least two. */
237
+ options?: string[];
238
+ /** Defaults to false. */
239
+ isRequired?: boolean;
240
+ /** Defaults to true. */
241
+ enabled?: boolean;
242
+ /** Defaults to `product`. */
243
+ section?: ReviewAttributeSection;
244
+ }
245
+ /** Only the fields named change. */
246
+ export interface UpdateReviewAttributeInput {
247
+ label?: string;
248
+ slug?: string;
249
+ inputType?: ReviewAttributeInputType;
250
+ options?: string[];
251
+ isRequired?: boolean;
252
+ enabled?: boolean;
253
+ section?: ReviewAttributeSection;
254
+ }
255
+ export interface ReviewsModule {
256
+ /**
257
+ * One page of the store's reviews, every status unless `status` is given, in `sort` order.
258
+ * `total` counts every match.
259
+ *
260
+ * @effect read
261
+ */
262
+ list(options?: ReviewListOptions): Promise<Page<ProductReviewSummary>>;
263
+ /**
264
+ * One review with its edit history and moderation log; `null` when the store has none with
265
+ * that id.
266
+ *
267
+ * @effect read
268
+ */
269
+ get(id: string, options?: Call): Promise<ProductReview | null>;
270
+ /** @effect read */
271
+ stats(options?: Call): Promise<ReviewStats>;
272
+ /**
273
+ * Give one review a status: `APPROVED` publishes it on the storefront; `REJECTED`, `FLAGGED`
274
+ * and `PENDING` take it off. Recorded in its moderation log with `reason`. The product's
275
+ * average rating follows within seconds. Refused (`ENTITY_NOT_FOUND`) for a review the store
276
+ * does not have. `APPROVED` is refused `CUSTOMER_DATA_NOT_GRANTED` (`PermissionDeniedError`) to
277
+ * a caller below `customerData: full`, whatever the review; the other statuses are open at every
278
+ * level.
279
+ *
280
+ * @effect write
281
+ */
282
+ moderate(id: string, input: ReviewModerationInput, options?: Call): Promise<ProductReviewSummary>;
283
+ /**
284
+ * Moderate many reviews in one call. Each goes through `moderate`'s rules in a transaction of
285
+ * its own and is reported on its own, in the order named: a refused review
286
+ * (`ENTITY_NOT_FOUND`) changes nothing, and the others still go through. A selection of no
287
+ * review, more than {@link MAX_BULK_REVIEWS} or one twice is refused
288
+ * `BULK_ACTION_INPUT_INVALID` (400) before anything is sent. Not keyed: a repeat moderates
289
+ * again (and logs again). Needs a platform release with `moderateReviews`. `APPROVED` below
290
+ * `customerData: full` reports every review `FAILED` with code `CUSTOMER_DATA_NOT_GRANTED` and
291
+ * changes none; the call itself succeeds.
292
+ *
293
+ * @effect write
294
+ */
295
+ bulkModerate(input: ReviewBulkModerationInput, options?: Call): Promise<ReviewModerationOutcome[]>;
296
+ /** The store's reply under a review. */
297
+ reply: {
298
+ /**
299
+ * Set (or replace) the store's reply, trimmed; shown under the review on the storefront once
300
+ * it is approved. Over 2,000 characters is refused (`USER_INPUT_ERROR`). An empty reply is
301
+ * stored as one: use `remove` to take a reply away. Refused `CUSTOMER_DATA_NOT_GRANTED`
302
+ * (`PermissionDeniedError`) below `customerData: full`: the reply is published under the
303
+ * store's name.
304
+ *
305
+ * @effect write
306
+ */
307
+ set(reviewId: string, reply: string, options?: Call): Promise<ProductReviewSummary>;
308
+ /**
309
+ * Take the store's reply away. Open at every customer-data level: it publishes nothing.
310
+ *
311
+ * @effect destructive
312
+ */
313
+ remove(reviewId: string, options?: Call): Promise<ProductReviewSummary>;
314
+ };
315
+ /** The questions the store's review form asks. */
316
+ attributes: {
317
+ /**
318
+ * Every attribute of the store, disabled ones included, in `position` order (ties: the
319
+ * older first). The engine answers them all at once; there are no pages.
320
+ *
321
+ * @effect read
322
+ */
323
+ list(options?: Call): Promise<ReviewAttribute[]>;
324
+ /**
325
+ * Created after the store's last attribute. Refused: a taken slug
326
+ * (`REVIEW_ATTRIBUTE_SLUG_TAKEN`), an unknown type or a selection with fewer than two
327
+ * options (`USER_INPUT_ERROR`).
328
+ *
329
+ * @effect write
330
+ */
331
+ create(input: CreateReviewAttributeInput, options?: Call): Promise<ReviewAttribute>;
332
+ /**
333
+ * Refused: an attribute the store does not have (`ENTITY_NOT_FOUND`), a slug another of its
334
+ * attributes holds (`REVIEW_ATTRIBUTE_SLUG_TAKEN`), a change to an unknown type or to a
335
+ * selection with fewer than two options (`USER_INPUT_ERROR`). Answers given before keep the
336
+ * attribute id they named; a review's answers are not rewritten.
337
+ *
338
+ * @effect write
339
+ */
340
+ update(id: string, input: UpdateReviewAttributeInput, options?: Call): Promise<ReviewAttribute>;
341
+ /**
342
+ * Deletes the attribute and its collection scoping. Reviews keep the answers given to it,
343
+ * no longer labelled. Refused (`ENTITY_NOT_FOUND`) for an attribute the store does not have.
344
+ *
345
+ * @effect destructive
346
+ */
347
+ delete(id: string, options?: Call): Promise<void>;
348
+ /**
349
+ * Gives the named attributes the positions `0, 1, 2…` in the order given, and answers every
350
+ * attribute as `list` does. An id the store does not hold is skipped without a word; an
351
+ * attribute left out keeps its position, which may then equal a named one's: name them all.
352
+ *
353
+ * @effect write
354
+ */
355
+ reorder(ids: string[], options?: Call): Promise<ReviewAttribute[]>;
356
+ /**
357
+ * Replace the collections the attribute is asked for (`[]`: every product). Repeats are
358
+ * dropped. A collection the store does not hold refuses the whole call and changes nothing
359
+ * (`REVIEW_ATTRIBUTE_COLLECTION_NOT_FOUND`, each in `details`), except one the attribute is
360
+ * already scoped to that the store no longer holds (deleted since), which is dropped: sending
361
+ * the attribute's `collectionIds` back always saves.
362
+ *
363
+ * @effect write
364
+ */
365
+ setCollections(attributeId: string, collectionIds: string[], options?: Call): Promise<ReviewAttribute>;
366
+ };
367
+ }
368
+ export declare const PRODUCT_REVIEWS_QUERY = "\n query CatalogProductReviews($options: AdminReviewListOptions) {\n adminProductReviews(options: $options) {\n items { \n id\n channelId\n customerId\n productId\n productName\n variantId\n rating\n title\n body\n status\n isVerifiedPurchase\n purchaseDate\n deliveryDate\n isEdited\n editCount\n customerDisplayName\n imageAssetIds\n videoAssetIds\n attributeValues\n helpfulCount\n notHelpfulCount\n merchantReply\n merchantReplyAt\n ipAddressHash\n createdAt\n updatedAt\n }\n totalItems\n }\n }\n";
369
+ export declare const PRODUCT_REVIEW_QUERY = "\n query CatalogProductReview($id: ID!) {\n adminProductReview(id: $id) { \n \n id\n channelId\n customerId\n productId\n productName\n variantId\n rating\n title\n body\n status\n isVerifiedPurchase\n purchaseDate\n deliveryDate\n isEdited\n editCount\n customerDisplayName\n imageAssetIds\n videoAssetIds\n attributeValues\n helpfulCount\n notHelpfulCount\n merchantReply\n merchantReplyAt\n ipAddressHash\n createdAt\n updatedAt\n\n editHistory {\n id\n previousRating\n previousTitle\n previousBody\n previousAttributeValues\n editedAt\n reEnteredModeration\n createdAt\n }\n moderationLogs {\n id\n step\n action\n reason\n confidence\n aiAnalysis\n performedBy\n tokenUsage\n createdAt\n }\n }\n }\n";
370
+ export declare const REVIEW_STATS_QUERY = "\n query CatalogReviewStats {\n reviewStats {\n totalReviews\n pendingCount\n approvedCount\n rejectedCount\n flaggedCount\n averageRating\n ratingsThisWeek\n ratingsThisMonth\n }\n }\n";
371
+ export declare const MODERATE_REVIEW_MUTATION = "\n mutation CatalogModerateReview($id: ID!, $action: ReviewStatus!, $reason: String) {\n moderateReview(id: $id, action: $action, reason: $reason) { \n id\n channelId\n customerId\n productId\n productName\n variantId\n rating\n title\n body\n status\n isVerifiedPurchase\n purchaseDate\n deliveryDate\n isEdited\n editCount\n customerDisplayName\n imageAssetIds\n videoAssetIds\n attributeValues\n helpfulCount\n notHelpfulCount\n merchantReply\n merchantReplyAt\n ipAddressHash\n createdAt\n updatedAt\n }\n }\n";
372
+ export declare const MODERATE_REVIEWS_MUTATION = "\n mutation CatalogModerateReviews($input: ModerateReviewsInput!) {\n moderateReviews(input: $input) {\n items { id status code message }\n }\n }\n";
373
+ export declare const SET_MERCHANT_REPLY_MUTATION = "\n mutation CatalogSetMerchantReply($reviewId: ID!, $reply: String!) {\n setMerchantReply(reviewId: $reviewId, reply: $reply) { \n id\n channelId\n customerId\n productId\n productName\n variantId\n rating\n title\n body\n status\n isVerifiedPurchase\n purchaseDate\n deliveryDate\n isEdited\n editCount\n customerDisplayName\n imageAssetIds\n videoAssetIds\n attributeValues\n helpfulCount\n notHelpfulCount\n merchantReply\n merchantReplyAt\n ipAddressHash\n createdAt\n updatedAt\n }\n }\n";
374
+ export declare const REMOVE_MERCHANT_REPLY_MUTATION = "\n mutation CatalogRemoveMerchantReply($reviewId: ID!) {\n removeMerchantReply(reviewId: $reviewId) { \n id\n channelId\n customerId\n productId\n productName\n variantId\n rating\n title\n body\n status\n isVerifiedPurchase\n purchaseDate\n deliveryDate\n isEdited\n editCount\n customerDisplayName\n imageAssetIds\n videoAssetIds\n attributeValues\n helpfulCount\n notHelpfulCount\n merchantReply\n merchantReplyAt\n ipAddressHash\n createdAt\n updatedAt\n }\n }\n";
375
+ export declare const REVIEW_ATTRIBUTES_QUERY = "\n query CatalogReviewAttributes {\n adminReviewAttributes { \n id\n slug\n label\n inputType\n options\n isRequired\n enabled\n section\n position\n collectionIds\n createdAt\n updatedAt\n }\n }\n";
376
+ export declare const CREATE_REVIEW_ATTRIBUTE_MUTATION = "\n mutation CatalogCreateReviewAttribute($input: AdminReviewAttributeInput!) {\n createReviewAttribute(input: $input) { \n id\n slug\n label\n inputType\n options\n isRequired\n enabled\n section\n position\n collectionIds\n createdAt\n updatedAt\n }\n }\n";
377
+ export declare const UPDATE_REVIEW_ATTRIBUTE_MUTATION = "\n mutation CatalogUpdateReviewAttribute($id: ID!, $input: UpdateReviewAttributeInput!) {\n updateReviewAttribute(id: $id, input: $input) { \n id\n slug\n label\n inputType\n options\n isRequired\n enabled\n section\n position\n collectionIds\n createdAt\n updatedAt\n }\n }\n";
378
+ export declare const DELETE_REVIEW_ATTRIBUTE_MUTATION = "\n mutation CatalogDeleteReviewAttribute($id: ID!) {\n deleteReviewAttribute(id: $id) { \n result\n message\n }\n }\n";
379
+ export declare const REORDER_REVIEW_ATTRIBUTES_MUTATION = "\n mutation CatalogReorderReviewAttributes($ids: [ID!]!) {\n reorderReviewAttributes(ids: $ids) { \n id\n slug\n label\n inputType\n options\n isRequired\n enabled\n section\n position\n collectionIds\n createdAt\n updatedAt\n }\n }\n";
380
+ export declare const SET_REVIEW_ATTRIBUTE_COLLECTIONS_MUTATION = "\n mutation CatalogSetReviewAttributeCollections($attributeId: ID!, $collectionIds: [ID!]!) {\n setReviewAttributeCollections(attributeId: $attributeId, collectionIds: $collectionIds) {\n \n id\n slug\n label\n inputType\n options\n isRequired\n enabled\n section\n position\n collectionIds\n createdAt\n updatedAt\n\n }\n }\n";
381
+ export declare function createReviewsModule(transport: Transport): ReviewsModule;