@sixtyninecommerce/admin 0.10.0 → 0.11.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 (247) hide show
  1. package/README.md +4347 -171
  2. package/dist/catalog/assets.d.ts +120 -8
  3. package/dist/catalog/assets.js +98 -12
  4. package/dist/catalog/collections.d.ts +279 -14
  5. package/dist/catalog/collections.js +246 -15
  6. package/dist/catalog/index.d.ts +9 -2
  7. package/dist/catalog/index.js +4 -0
  8. package/dist/catalog/products.d.ts +159 -19
  9. package/dist/catalog/products.js +88 -13
  10. package/dist/catalog/properties.d.ts +102 -14
  11. package/dist/catalog/properties.js +13 -0
  12. package/dist/catalog/recommendations.d.ts +261 -0
  13. package/dist/catalog/recommendations.js +145 -0
  14. package/dist/catalog/reviews.d.ts +368 -0
  15. package/dist/catalog/reviews.js +318 -0
  16. package/dist/catalog/types.d.ts +17 -1
  17. package/dist/catalog/types.js +10 -0
  18. package/dist/catalog/variant-find.d.ts +136 -0
  19. package/dist/catalog/variant-find.js +239 -0
  20. package/dist/catalog/variants.d.ts +233 -26
  21. package/dist/catalog/variants.js +125 -7
  22. package/dist/catalog/visibility.d.ts +20 -5
  23. package/dist/catalog/visibility.js +19 -1
  24. package/dist/client.d.ts +95 -9
  25. package/dist/client.js +35 -7
  26. package/dist/content/assets.d.ts +286 -0
  27. package/dist/content/assets.js +181 -0
  28. package/dist/content/items.d.ts +269 -0
  29. package/dist/content/items.js +170 -0
  30. package/dist/content/types.d.ts +282 -0
  31. package/dist/content/types.js +100 -0
  32. package/dist/customers/age-verification.d.ts +66 -0
  33. package/dist/customers/age-verification.js +79 -0
  34. package/dist/customers/credit.d.ts +263 -0
  35. package/dist/customers/credit.js +244 -0
  36. package/dist/customers/delivery-preferences.d.ts +120 -0
  37. package/dist/customers/delivery-preferences.js +97 -0
  38. package/dist/customers/groups.d.ts +207 -0
  39. package/dist/customers/groups.js +438 -0
  40. package/dist/customers/index.d.ts +447 -37
  41. package/dist/customers/index.js +313 -40
  42. package/dist/customers/invitation-status.d.ts +57 -0
  43. package/dist/customers/invitation-status.js +21 -0
  44. package/dist/customers/notes.d.ts +67 -0
  45. package/dist/customers/notes.js +51 -0
  46. package/dist/customers/notification-recipients.d.ts +97 -0
  47. package/dist/customers/notification-recipients.js +84 -0
  48. package/dist/customers/saved-lists.d.ts +42 -0
  49. package/dist/customers/saved-lists.js +61 -0
  50. package/dist/customers/vat.d.ts +83 -0
  51. package/dist/customers/vat.js +46 -0
  52. package/dist/customers/wire.d.ts +20 -0
  53. package/dist/customers/wire.js +35 -0
  54. package/dist/documents/ar.d.ts +307 -0
  55. package/dist/documents/ar.js +254 -0
  56. package/dist/documents/index.d.ts +731 -21
  57. package/dist/documents/index.js +436 -24
  58. package/dist/documents/pdf.d.ts +2 -0
  59. package/dist/documents/pdf.js +11 -0
  60. package/dist/fulfillments/create.d.ts +167 -0
  61. package/dist/fulfillments/create.js +216 -0
  62. package/dist/fulfillments/index.d.ts +44 -10
  63. package/dist/fulfillments/index.js +11 -2
  64. package/dist/fulfillments/payment-policy.d.ts +64 -0
  65. package/dist/fulfillments/payment-policy.js +140 -0
  66. package/dist/fulfillments/warehouse.d.ts +724 -0
  67. package/dist/fulfillments/warehouse.js +914 -0
  68. package/dist/geo/index.d.ts +119 -0
  69. package/dist/geo/index.js +107 -0
  70. package/dist/index.d.ts +123 -35
  71. package/dist/index.js +45 -12
  72. package/dist/integrations/afas-actions.d.ts +234 -0
  73. package/dist/integrations/afas-actions.js +310 -0
  74. package/dist/integrations/afas-types.d.ts +483 -0
  75. package/dist/integrations/afas-types.js +5 -0
  76. package/dist/integrations/afas.d.ts +234 -0
  77. package/dist/integrations/afas.js +290 -0
  78. package/dist/integrations/connections.d.ts +103 -0
  79. package/dist/integrations/connections.js +55 -0
  80. package/dist/integrations/esp.d.ts +313 -0
  81. package/dist/integrations/esp.js +169 -0
  82. package/dist/integrations/exact-online-types.d.ts +423 -0
  83. package/dist/integrations/exact-online-types.js +6 -0
  84. package/dist/integrations/exact-online.d.ts +248 -0
  85. package/dist/integrations/exact-online.js +376 -0
  86. package/dist/integrations/index.d.ts +29 -3
  87. package/dist/integrations/index.js +18 -3
  88. package/dist/integrations/pipeline-keys.d.ts +13 -0
  89. package/dist/integrations/pipeline-keys.js +22 -0
  90. package/dist/integrations/pipelines.d.ts +15 -14
  91. package/dist/integrations/pipelines.js +6 -22
  92. package/dist/integrations/types.d.ts +146 -0
  93. package/dist/locations/index.d.ts +66 -0
  94. package/dist/locations/index.js +31 -0
  95. package/dist/meta/index.d.ts +162 -0
  96. package/dist/orders/cancel-with-money.d.ts +227 -0
  97. package/dist/orders/cancel-with-money.js +153 -0
  98. package/dist/orders/commit-edit.d.ts +7 -1
  99. package/dist/orders/commit-edit.js +5 -1
  100. package/dist/orders/drafts.d.ts +800 -0
  101. package/dist/orders/drafts.js +771 -0
  102. package/dist/orders/fields.d.ts +245 -0
  103. package/dist/orders/fields.js +188 -0
  104. package/dist/orders/get.d.ts +29 -19
  105. package/dist/orders/get.js +17 -8
  106. package/dist/orders/index.d.ts +49 -9
  107. package/dist/orders/index.js +22 -2
  108. package/dist/orders/lifecycle.d.ts +128 -0
  109. package/dist/orders/lifecycle.js +158 -0
  110. package/dist/orders/line-price-preview.d.ts +7 -2
  111. package/dist/orders/list.d.ts +222 -0
  112. package/dist/orders/list.js +377 -0
  113. package/dist/orders/manual-pricing.d.ts +5 -0
  114. package/dist/orders/order-entry.d.ts +20 -7
  115. package/dist/orders/plan-edit.d.ts +1 -1
  116. package/dist/orders/plan-edit.js +4 -1
  117. package/dist/orders/settings.d.ts +201 -0
  118. package/dist/orders/settings.js +325 -0
  119. package/dist/orders/status.d.ts +159 -13
  120. package/dist/orders/status.js +21 -0
  121. package/dist/orders/surcharges.d.ts +8 -1
  122. package/dist/orders/surcharges.js +3 -2
  123. package/dist/payments/account-credit.d.ts +30 -0
  124. package/dist/payments/account-credit.js +19 -0
  125. package/dist/payments/ar.d.ts +292 -0
  126. package/dist/payments/ar.js +223 -0
  127. package/dist/payments/connected-account.d.ts +47 -0
  128. package/dist/payments/connected-account.js +61 -0
  129. package/dist/payments/credit-noted-fields.d.ts +12 -0
  130. package/dist/payments/credit-noted-fields.js +37 -0
  131. package/dist/payments/index.d.ts +257 -11
  132. package/dist/payments/index.js +131 -1
  133. package/dist/payments/methods.d.ts +303 -8
  134. package/dist/payments/methods.js +337 -7
  135. package/dist/payments/mollie-methods.d.ts +159 -0
  136. package/dist/payments/mollie-methods.js +141 -0
  137. package/dist/payments/payment-links.d.ts +180 -0
  138. package/dist/payments/payment-links.js +97 -0
  139. package/dist/payments/provider-keys.d.ts +150 -0
  140. package/dist/payments/provider-keys.js +118 -0
  141. package/dist/payments/providers.d.ts +86 -11
  142. package/dist/payments/providers.js +29 -0
  143. package/dist/payments/requests.d.ts +144 -0
  144. package/dist/payments/requests.js +92 -0
  145. package/dist/payments/store-credit-fields.d.ts +15 -0
  146. package/dist/payments/store-credit-fields.js +51 -0
  147. package/dist/pickups/index.d.ts +104 -0
  148. package/dist/pickups/index.js +105 -0
  149. package/dist/platform/channels.d.ts +132 -0
  150. package/dist/platform/channels.js +128 -0
  151. package/dist/platform/geo.d.ts +123 -0
  152. package/dist/platform/geo.js +144 -0
  153. package/dist/platform/tax.d.ts +154 -0
  154. package/dist/platform/tax.js +139 -0
  155. package/dist/pos/anomalies.d.ts +158 -0
  156. package/dist/pos/anomalies.js +64 -0
  157. package/dist/pos/assisted-selling.d.ts +79 -0
  158. package/dist/pos/assisted-selling.js +38 -0
  159. package/dist/pos/exchanges.d.ts +103 -0
  160. package/dist/pos/exchanges.js +22 -0
  161. package/dist/pos/gift-cards.d.ts +135 -0
  162. package/dist/pos/gift-cards.js +47 -0
  163. package/dist/pos/index.d.ts +2125 -32
  164. package/dist/pos/index.js +742 -4
  165. package/dist/pos/no-receipt.d.ts +94 -0
  166. package/dist/pos/no-receipt.js +25 -0
  167. package/dist/pos/offline-sales.d.ts +187 -0
  168. package/dist/pos/offline-sales.js +116 -0
  169. package/dist/pos/pay-by-link.d.ts +143 -0
  170. package/dist/pos/pay-by-link.js +57 -0
  171. package/dist/pos/quick-keys.d.ts +103 -0
  172. package/dist/pos/quick-keys.js +45 -0
  173. package/dist/pos/reports.d.ts +360 -0
  174. package/dist/pos/reports.js +62 -0
  175. package/dist/pos/returns.d.ts +217 -0
  176. package/dist/pos/returns.js +53 -0
  177. package/dist/pricing/bulk-coupons.d.ts +139 -0
  178. package/dist/pricing/bulk-coupons.js +112 -0
  179. package/dist/pricing/index.d.ts +174 -4
  180. package/dist/pricing/index.js +174 -1
  181. package/dist/pricing/price-lists.d.ts +604 -0
  182. package/dist/pricing/price-lists.js +600 -0
  183. package/dist/purchasing/alerts.d.ts +89 -0
  184. package/dist/purchasing/alerts.js +62 -0
  185. package/dist/purchasing/cost.d.ts +110 -0
  186. package/dist/purchasing/cost.js +86 -0
  187. package/dist/purchasing/index.d.ts +36 -0
  188. package/dist/purchasing/index.js +16 -0
  189. package/dist/purchasing/purchase-orders.d.ts +548 -0
  190. package/dist/purchasing/purchase-orders.js +491 -0
  191. package/dist/purchasing/replenishment.d.ts +281 -0
  192. package/dist/purchasing/replenishment.js +240 -0
  193. package/dist/purchasing/settings.d.ts +152 -0
  194. package/dist/purchasing/settings.js +181 -0
  195. package/dist/purchasing/suppliers.d.ts +476 -0
  196. package/dist/purchasing/suppliers.js +438 -0
  197. package/dist/quotes/index.d.ts +501 -0
  198. package/dist/quotes/index.js +439 -0
  199. package/dist/reports/index.d.ts +71 -8
  200. package/dist/reports/index.js +74 -11
  201. package/dist/returns/index.d.ts +614 -21
  202. package/dist/returns/index.js +695 -11
  203. package/dist/search/index.d.ts +76 -1
  204. package/dist/search/index.js +63 -0
  205. package/dist/shipping/definitions.d.ts +29 -0
  206. package/dist/shipping/definitions.js +47 -0
  207. package/dist/shipping/fulfillment-profiles.d.ts +174 -0
  208. package/dist/shipping/fulfillment-profiles.js +104 -0
  209. package/dist/shipping/hendriks.d.ts +97 -0
  210. package/dist/shipping/hendriks.js +50 -0
  211. package/dist/shipping/index.d.ts +48 -0
  212. package/dist/shipping/index.js +25 -0
  213. package/dist/shipping/methods.d.ts +198 -0
  214. package/dist/shipping/methods.js +240 -0
  215. package/dist/shipping/myparcel.d.ts +254 -0
  216. package/dist/shipping/myparcel.js +178 -0
  217. package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
  218. package/dist/shipping/parcel-rule.d.ts +6 -5
  219. package/dist/shipping/parcel-rule.js +21 -23
  220. package/dist/shipping/rate-tables.d.ts +179 -0
  221. package/dist/shipping/rate-tables.js +184 -0
  222. package/dist/stock/index.d.ts +10 -3
  223. package/dist/stock/index.js +3 -0
  224. package/dist/stock/locations.d.ts +144 -0
  225. package/dist/stock/locations.js +168 -0
  226. package/dist/stock/transfers.d.ts +216 -43
  227. package/dist/stock/transfers.js +140 -13
  228. package/dist/stored-value/index.d.ts +214 -4
  229. package/dist/stored-value/index.js +152 -0
  230. package/dist/subscriptions/index.d.ts +682 -0
  231. package/dist/subscriptions/index.js +815 -0
  232. package/dist/tax/index.d.ts +76 -1
  233. package/dist/tax/index.js +50 -0
  234. package/dist/tenant/email-style.d.ts +102 -0
  235. package/dist/tenant/email-style.js +72 -0
  236. package/dist/tenant/email-templates.d.ts +66 -0
  237. package/dist/tenant/email-templates.js +54 -0
  238. package/dist/tenant/notifications.d.ts +23 -17
  239. package/dist/tenant/settings-sources.d.ts +1 -1
  240. package/dist/tenant/time-zone.d.ts +17 -0
  241. package/dist/tenant/time-zone.js +11 -0
  242. package/dist/types.d.ts +50 -3
  243. package/dist/users/index.d.ts +3 -0
  244. package/dist/users/index.js +2 -0
  245. package/dist/users/warehouses.d.ts +50 -0
  246. package/dist/users/warehouses.js +51 -0
  247. package/package.json +2 -2
@@ -1,9 +1,11 @@
1
1
  import { ValidationError } from '@sixtyninecommerce/core';
2
2
  import { bulkProducts, readProductBulkRun, } from './products-bulk.js';
3
- import { ASSET_REF_FIELDS, assertDeleted, availabilityCustomFields, CATALOGUE_OWNERSHIP_FIELDS, DELETION_FIELDS, FACET_VALUE_REF_FIELDS, fieldOf, unwrapResult, vendureListPage, } from './types.js';
3
+ import { ASSET_REF_FIELDS, assertDeleted, availabilityCustomFields, CATALOGUE_OWNERSHIP_FIELDS, DELETION_FIELDS, FACET_VALUE_REF_FIELDS, fieldOf, toAssetRef, unwrapResult, vendureListPage, } from './types.js';
4
4
  import { assertPricesStateBasis, } from './variants.js';
5
5
  /** The engine's bound on one `exclude` or `include` call (`STORE_EXCLUSION_BATCH_LIMIT`). */
6
6
  export const MAX_STORE_EXCLUSION_PRODUCTS = 500;
7
+ /** At most this many distinct products per {@link ProductsModule.storePriceRanges} call. */
8
+ export const MAX_STORE_PRICE_RANGE_PRODUCTS = 100;
7
9
  /**
8
10
  * The engine's bounds on one `setAvailabilityInStores` or `availabilityInStores` call
9
11
  * (`AVAILABILITY_*_LIMIT`).
@@ -29,11 +31,19 @@ export const PRODUCT_FIELDS = /* GraphQL */ `
29
31
  id
30
32
  code
31
33
  name
34
+ customFields {
35
+ isShared
36
+ }
32
37
  options {
33
38
  id
34
39
  code
35
40
  name
36
41
  }
42
+ translations {
43
+ id
44
+ languageCode
45
+ name
46
+ }
37
47
  }
38
48
  translations {
39
49
  id
@@ -50,6 +60,8 @@ export const PRODUCT_FIELDS = /* GraphQL */ `
50
60
  availabilityState
51
61
  availableFrom
52
62
  preorderLimit
63
+ brand
64
+ condition
53
65
  }
54
66
  `;
55
67
  export const PRODUCTS_QUERY = /* GraphQL */ `
@@ -60,9 +72,36 @@ export const PRODUCTS_QUERY = /* GraphQL */ `
60
72
  }
61
73
  }
62
74
  `;
75
+ const STORE_PRICE_FIELDS = 'price priceWithTax currencyCode';
76
+ export const PRODUCT_STORE_PRICE_RANGES_QUERY = /* GraphQL */ `
77
+ query CatalogProductStorePriceRanges($options: ProductListOptions) {
78
+ products(options: $options) {
79
+ items {
80
+ id
81
+ storePriceRange {
82
+ min { ${STORE_PRICE_FIELDS} }
83
+ max { ${STORE_PRICE_FIELDS} }
84
+ someVariantsUnpriced
85
+ }
86
+ }
87
+ }
88
+ }
89
+ `;
90
+ /**
91
+ * One product, with the collections it is in. No variant data beyond `variantList`'s count: a
92
+ * product can have 100,000 variants, and core's `variants` would hand back the first 1,000 with
93
+ * no sign of the rest (`catalog.variants.list` pages them).
94
+ */
63
95
  export const PRODUCT_QUERY = /* GraphQL */ `
64
96
  query CatalogProduct($id: ID!) {
65
- product(id: $id) { ${PRODUCT_FIELDS} }
97
+ product(id: $id) {
98
+ ${PRODUCT_FIELDS}
99
+ collections {
100
+ id
101
+ name
102
+ slug
103
+ }
104
+ }
66
105
  }
67
106
  `;
68
107
  export const CREATE_PRODUCT_MUTATION = /* GraphQL */ `
@@ -196,15 +235,28 @@ export const SAVE_PRODUCT_MUTATION = /* GraphQL */ `
196
235
  * outright, so the defaults below only cover a NULL value, never an absent one.
197
236
  */
198
237
  export function toProduct(wire) {
199
- const { variantList, customFields, ...rest } = wire;
238
+ const { variantList, customFields, featuredAsset, assets, optionGroups, ...rest } = wire;
200
239
  return {
201
240
  ...rest,
241
+ featuredAsset: featuredAsset ? toAssetRef(featuredAsset) : null,
242
+ assets: assets.map(toAssetRef),
243
+ optionGroups: optionGroups.map(({ customFields: groupFields, ...group }) => ({
244
+ ...group,
245
+ isShared: groupFields?.isShared === true,
246
+ })),
202
247
  variantCount: variantList.totalItems,
203
248
  availabilityState: customFields?.availabilityState ?? 'none',
204
249
  availableFrom: customFields?.availableFrom ?? null,
205
250
  preorderLimit: customFields?.preorderLimit ?? null,
251
+ brand: customFields?.brand ?? null,
252
+ condition: customFields?.condition ?? null,
206
253
  };
207
254
  }
255
+ /** {@link toProduct}, plus the collections. */
256
+ function toProductDetail(wire) {
257
+ const { collections, ...rest } = wire;
258
+ return { ...toProduct(rest), collections };
259
+ }
208
260
  function toWireInput(input) {
209
261
  const { availabilityState, availableFrom, preorderLimit, brand, condition, ...rest } = input;
210
262
  const customFields = availabilityCustomFields({
@@ -237,6 +289,16 @@ export function productFilter(filter) {
237
289
  }
238
290
  return out;
239
291
  }
292
+ /** The engine's `ProductListOptions` for one page of {@link ProductListOptions}. */
293
+ export function productListOptions(options, skip, take) {
294
+ const filter = productFilter(options?.filter);
295
+ return {
296
+ skip,
297
+ take,
298
+ ...(Object.keys(filter).length > 0 ? { filter, filterOperator: 'AND' } : {}),
299
+ ...(options?.sort ? { sort: { [options.sort.field]: options.sort.direction } } : {}),
300
+ };
301
+ }
240
302
  function refuse(code, message) {
241
303
  return new ValidationError({ code, message, status: 400, service: 'vendure' });
242
304
  }
@@ -290,19 +352,11 @@ export function createProductsModule(transport) {
290
352
  };
291
353
  return {
292
354
  async list(options) {
293
- const filter = productFilter(options?.filter);
294
- return vendureListPage(transport, PRODUCTS_QUERY, 'products', (skip, take) => ({
295
- options: {
296
- skip,
297
- take,
298
- ...(Object.keys(filter).length > 0 ? { filter, filterOperator: 'AND' } : {}),
299
- ...(options?.sort ? { sort: { [options.sort.field]: options.sort.direction } } : {}),
300
- },
301
- }), options, toProduct);
355
+ return vendureListPage(transport, PRODUCTS_QUERY, 'products', (skip, take) => ({ options: productListOptions(options, skip, take) }), options, toProduct);
302
356
  },
303
357
  async get(id, options) {
304
358
  const product = await fieldOf(transport, PRODUCT_QUERY, 'product', { id }, options);
305
- return product ? toProduct(product) : null;
359
+ return product ? toProductDetail(product) : null;
306
360
  },
307
361
  // `async` so every refusal below — including `toWireInput`'s datetime
308
362
  // check — arrives as a rejection, never as a synchronous throw a caller's
@@ -362,6 +416,27 @@ export function createProductsModule(transport) {
362
416
  assertAvailabilityInput(input, 'read');
363
417
  return fieldOf(transport, ORGANIZATION_PRODUCT_AVAILABILITY_QUERY, 'organizationProductAvailability', { productIds: input.productIds, channelIds: input.channelIds }, options);
364
418
  },
419
+ async storePriceRanges(productIds, options) {
420
+ // The engine stores a uuid lower-case, so an upper-case repeat is the same product.
421
+ const named = new Map();
422
+ for (const id of productIds) {
423
+ const key = String(id).toLowerCase();
424
+ if (!named.has(key))
425
+ named.set(key, id);
426
+ }
427
+ if (named.size > MAX_STORE_PRICE_RANGE_PRODUCTS) {
428
+ throw refuse('PRODUCT_STORE_PRICE_RANGE_INPUT_INVALID', `Ask for at most ${String(MAX_STORE_PRICE_RANGE_PRODUCTS)} products per call; this one named ${String(named.size)}. Nothing was sent.`);
429
+ }
430
+ if (named.size === 0)
431
+ return [];
432
+ const ids = [...named.values()];
433
+ const page = await fieldOf(transport, PRODUCT_STORE_PRICE_RANGES_QUERY, 'products', { options: { filter: { id: { in: ids } }, take: ids.length } }, options);
434
+ const answered = new Map(page.items.map((item) => [item.id.toLowerCase(), item]));
435
+ return [...named.keys()].flatMap((key) => {
436
+ const item = answered.get(key);
437
+ return item ? [{ productId: item.id, range: item.storePriceRange }] : [];
438
+ });
439
+ },
365
440
  async makeLocalCopy(productId, options) {
366
441
  const { skus, ...call } = options ?? {};
367
442
  const result = await fieldOf(transport, MAKE_LOCAL_COPY_MUTATION, 'makeLocalCopy', {
@@ -33,6 +33,11 @@ export interface Property {
33
33
  position: number;
34
34
  config: PropertyConfig | null;
35
35
  facetId: string | null;
36
+ /**
37
+ * A filterable `content_item` property's field-level facets, one per content field it filters
38
+ * by, as `configureFieldFacets` last wrote them; null when it has none.
39
+ */
40
+ fieldFacets: PropertyFieldFacet[] | null;
36
41
  translations: Array<{
37
42
  id: string;
38
43
  languageCode: LanguageCode;
@@ -40,6 +45,25 @@ export interface Property {
40
45
  helpText: string | null;
41
46
  }>;
42
47
  }
48
+ /** One field-level facet of a `content_item` property: the facet a content field's values fill. */
49
+ export interface PropertyFieldFacet {
50
+ fieldSlug: string;
51
+ /** The content field's type (`enum`, `multi_enum`, `boolean`). */
52
+ fieldType: string;
53
+ facetId: string;
54
+ }
55
+ /** A content field a `content_item` property filters by (`configureFieldFacets`). */
56
+ export interface FieldFacetDefinition {
57
+ /** The content type's field slug. */
58
+ fieldSlug: string;
59
+ /** The content field's type (`enum`, `multi_enum`, `boolean`). */
60
+ fieldType: string;
61
+ /** For an `enum` / `multi_enum` field: its options, each becoming one facet value. */
62
+ options?: Array<{
63
+ value: string;
64
+ label: string;
65
+ }>;
66
+ }
43
67
  export interface PropertyGroup {
44
68
  id: string;
45
69
  createdAt: string;
@@ -186,47 +210,111 @@ export interface ContentFacetSyncResult {
186
210
  */
187
211
  export interface PropertiesModule {
188
212
  groups: {
189
- /** The complete list, as one terminal page — the query takes no paging. */
213
+ /**
214
+ * The store's own groups, the complete list as one terminal page (the query takes no paging).
215
+ *
216
+ * @effect read
217
+ */
190
218
  list(options?: Call): Promise<Page<PropertyGroup>>;
219
+ /**
220
+ * One of the store's own groups, or null.
221
+ *
222
+ * @effect read
223
+ */
191
224
  get(id: string, options?: Call): Promise<PropertyGroup | null>;
225
+ /** @effect write */
192
226
  create(input: CreatePropertyGroupInput, options?: Call): Promise<PropertyGroup>;
227
+ /** @effect write */
193
228
  update(input: UpdatePropertyGroupInput, options?: Call): Promise<PropertyGroup>;
194
- /** Deletes the group AND every property in it, with their values. */
229
+ /**
230
+ * Deletes the group AND every property in it, with their values on every product and the
231
+ * filter values (facets) the filterable ones mirror. Not undoable.
232
+ *
233
+ * @effect destructive
234
+ */
195
235
  delete(id: string, options?: Call): Promise<void>;
196
236
  /**
197
237
  * Pass EVERY group id of the store, in the order wanted. Only the store's own groups:
198
238
  * a workspace group (`editableHere` false) or another store's id refuses the whole call.
239
+ *
240
+ * @effect write
199
241
  */
200
242
  reorder(ids: string[], options?: Call): Promise<PropertyGroup[]>;
201
243
  };
244
+ /** @effect write */
202
245
  create(input: CreatePropertyInput, options?: Call): Promise<Property>;
246
+ /** @effect write */
203
247
  update(input: UpdatePropertyInput, options?: Call): Promise<Property>;
248
+ /**
249
+ * Deletes the property with its values on every product, and the filter values (facets) a
250
+ * filterable one mirrors. Not undoable.
251
+ *
252
+ * @effect destructive
253
+ */
204
254
  delete(id: string, options?: Call): Promise<void>;
205
- /** Pass every property id of the group, in the order wanted. */
255
+ /**
256
+ * Pass every property id of the group, in the order wanted.
257
+ *
258
+ * @effect write
259
+ */
206
260
  reorder(groupId: string, propertyIds: string[], options?: Call): Promise<Property[]>;
261
+ /**
262
+ * Sets which content fields a filterable `content_item` property filters by: one field-level
263
+ * facet per field named, its values the field's options (an `enum` / `multi_enum` field) or
264
+ * yes/no (a `boolean` one). Needs `UpdateCatalog`.
265
+ *
266
+ * `fields` is the WHOLE set: a field already configured keeps its facet (its values follow the
267
+ * options sent), a new one gets a facet, and a field left out loses its facet, which is removed
268
+ * from every product of the store. `[]` removes them all. Send the fields the property was
269
+ * SAVED with (`config.filterableFieldSlugs`), never an unsaved form's. The products' field
270
+ * values are filled by `catalog.syncContentFacets`, not here. It resolves with nothing (the
271
+ * engine answers only that it is done): the property's `fieldFacets`, read again through
272
+ * `groups.get`, holds what was written.
273
+ *
274
+ * Refusals (nothing is written):
275
+ * - `PROPERTY_NOT_FOUND` → `NotFoundError` (404): no such property among the store's own groups.
276
+ * - `PROPERTY_FACET_SYNC_INVALID` → `ValidationError` (400): not a `content_item` property, or
277
+ * not saved as filterable.
278
+ * - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): in a store of an organization, a
279
+ * property of the workspace's groups (`editableHere` false): configure it in the workspace.
280
+ *
281
+ * Not idempotent by key; repeating the same `fields` writes the same facets again.
282
+ *
283
+ * Destructive: a dropped field's facet and a dropped option's filter value leave every product.
284
+ *
285
+ * @effect destructive
286
+ */
287
+ configureFieldFacets(propertyId: string, fields: FieldFacetDefinition[], options?: Call): Promise<void>;
207
288
  values: {
208
289
  /**
209
290
  * Every group of the store, each with this product's stored values in every language. For a
210
291
  * product the store holds from its organization, the workspace's groups and values come too,
211
292
  * with `group.editableHere` false.
293
+ *
294
+ * @effect read
212
295
  */
213
296
  get(productId: string, options?: Call): Promise<ProductPropertyGroupValues[]>;
214
- /** Upsert the given values; properties not named keep theirs. */
297
+ /**
298
+ * Upsert the given values; properties not named keep theirs.
299
+ *
300
+ * @effect write
301
+ */
215
302
  set(productId: string, values: SetPropertyValueInput[], options?: Call): Promise<void>;
216
303
  };
217
304
  }
218
- export declare const PROPERTY_GROUP_FIELDS = "\n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n";
219
- export declare const PROPERTY_GROUPS_QUERY = "\n query CatalogPropertyGroups {\n propertyGroups { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
220
- export declare const PROPERTY_GROUP_QUERY = "\n query CatalogPropertyGroup($id: ID!) {\n propertyGroup(id: $id) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
221
- export declare const CREATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogCreatePropertyGroup($input: CreatePropertyGroupInput!) {\n createPropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
222
- export declare const UPDATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogUpdatePropertyGroup($input: UpdatePropertyGroupInput!) {\n updatePropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
305
+ export declare const PROPERTY_GROUP_FIELDS = "\n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n";
306
+ export declare const PROPERTY_GROUPS_QUERY = "\n query CatalogPropertyGroups {\n propertyGroups { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
307
+ export declare const PROPERTY_GROUP_QUERY = "\n query CatalogPropertyGroup($id: ID!) {\n propertyGroup(id: $id) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
308
+ export declare const CREATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogCreatePropertyGroup($input: CreatePropertyGroupInput!) {\n createPropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
309
+ export declare const UPDATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogUpdatePropertyGroup($input: UpdatePropertyGroupInput!) {\n updatePropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
223
310
  export declare const DELETE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogDeletePropertyGroup($id: ID!) {\n deletePropertyGroup(id: $id) { \n result\n message\n }\n }\n";
224
- export declare const REORDER_PROPERTY_GROUPS_MUTATION = "\n mutation CatalogReorderPropertyGroups($ids: [ID!]!) {\n reorderPropertyGroups(ids: $ids) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
225
- export declare const CREATE_PROPERTY_MUTATION = "\n mutation CatalogCreateProperty($input: CreatePropertyInput!) {\n createProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
226
- export declare const UPDATE_PROPERTY_MUTATION = "\n mutation CatalogUpdateProperty($input: UpdatePropertyInput!) {\n updateProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
311
+ export declare const REORDER_PROPERTY_GROUPS_MUTATION = "\n mutation CatalogReorderPropertyGroups($ids: [ID!]!) {\n reorderPropertyGroups(ids: $ids) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
312
+ export declare const CREATE_PROPERTY_MUTATION = "\n mutation CatalogCreateProperty($input: CreatePropertyInput!) {\n createProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
313
+ export declare const UPDATE_PROPERTY_MUTATION = "\n mutation CatalogUpdateProperty($input: UpdatePropertyInput!) {\n updateProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
227
314
  export declare const DELETE_PROPERTY_MUTATION = "\n mutation CatalogDeleteProperty($id: ID!) {\n deleteProperty(id: $id) { \n result\n message\n }\n }\n";
228
- export declare const REORDER_PROPERTIES_MUTATION = "\n mutation CatalogReorderProperties($groupId: ID!, $propertyIds: [ID!]!) {\n reorderProperties(groupId: $groupId, propertyIds: $propertyIds) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
229
- export declare const PRODUCT_PROPERTY_VALUES_QUERY = "\n query CatalogProductPropertyValues($productId: ID!) {\n productPropertyValues(productId: $productId) {\n group { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n values {\n id\n propertyId\n languageCode\n value\n }\n }\n }\n";
315
+ export declare const REORDER_PROPERTIES_MUTATION = "\n mutation CatalogReorderProperties($groupId: ID!, $propertyIds: [ID!]!) {\n reorderProperties(groupId: $groupId, propertyIds: $propertyIds) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
316
+ export declare const CONFIGURE_FIELD_FACETS_MUTATION = "\n mutation CatalogConfigureFieldFacets($propertyId: ID!, $fields: [FieldFacetDefinitionInput!]!) {\n configureFieldFacets(propertyId: $propertyId, fields: $fields)\n }\n";
317
+ export declare const PRODUCT_PROPERTY_VALUES_QUERY = "\n query CatalogProductPropertyValues($productId: ID!) {\n productPropertyValues(productId: $productId) {\n group { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n values {\n id\n propertyId\n languageCode\n value\n }\n }\n }\n";
230
318
  export declare const SET_PRODUCT_PROPERTY_VALUES_MUTATION = "\n mutation CatalogSetProductPropertyValues($productId: ID!, $values: [SetProductPropertyValueInput!]!) {\n setProductPropertyValues(productId: $productId, values: $values)\n }\n";
231
319
  export declare const SYNC_PROPERTY_FACETS_MUTATION = "\n mutation CatalogSyncPropertyFacets($propertyId: ID!) {\n syncPropertyFacets(propertyId: $propertyId) {\n propertyId\n items\n unresolved\n fieldFacets\n }\n }\n";
232
320
  /** `catalog.syncContentFacets`; its contract is on {@link CatalogModule.syncContentFacets}. */
@@ -22,6 +22,11 @@ const PROPERTY_FIELDS = /* GraphQL */ `
22
22
  productFilter
23
23
  }
24
24
  facetId
25
+ fieldFacets {
26
+ fieldSlug
27
+ fieldType
28
+ facetId
29
+ }
25
30
  translations {
26
31
  id
27
32
  languageCode
@@ -92,6 +97,11 @@ export const REORDER_PROPERTIES_MUTATION = /* GraphQL */ `
92
97
  reorderProperties(groupId: $groupId, propertyIds: $propertyIds) { ${PROPERTY_FIELDS} }
93
98
  }
94
99
  `;
100
+ export const CONFIGURE_FIELD_FACETS_MUTATION = /* GraphQL */ `
101
+ mutation CatalogConfigureFieldFacets($propertyId: ID!, $fields: [FieldFacetDefinitionInput!]!) {
102
+ configureFieldFacets(propertyId: $propertyId, fields: $fields)
103
+ }
104
+ `;
95
105
  export const PRODUCT_PROPERTY_VALUES_QUERY = /* GraphQL */ `
96
106
  query CatalogProductPropertyValues($productId: ID!) {
97
107
  productPropertyValues(productId: $productId) {
@@ -148,6 +158,9 @@ export function createPropertiesModule(transport) {
148
158
  update: (input, options) => call(UPDATE_PROPERTY_MUTATION, 'updateProperty', { input }, options),
149
159
  delete: (id, options) => deleted(DELETE_PROPERTY_MUTATION, 'deleteProperty', 'PROPERTY_NOT_DELETED', id, options),
150
160
  reorder: (groupId, propertyIds, options) => call(REORDER_PROPERTIES_MUTATION, 'reorderProperties', { groupId, propertyIds }, options),
161
+ async configureFieldFacets(propertyId, fields, options) {
162
+ await call(CONFIGURE_FIELD_FACETS_MUTATION, 'configureFieldFacets', { propertyId, fields }, options);
163
+ },
151
164
  values: {
152
165
  get: (productId, options) => call(PRODUCT_PROPERTY_VALUES_QUERY, 'productPropertyValues', { productId }, options),
153
166
  async set(productId, values, options) {
@@ -0,0 +1,261 @@
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
+ * - `USER_INPUT_ERROR` → `ValidationError` (400), in the engine's words: a rule that does not fit
34
+ * its group or its type (a `MANUAL` group, a `sourceId` missing or sent where none goes), picks
35
+ * in a `DYNAMIC` group.
36
+ * - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
37
+ *
38
+ * An id that is not a uuid (any id argument, a `sourceId` and each pick included) is refused
39
+ * before all of these with core's `ENTITY_NOT_FOUND` (`NotFoundError`, 404, no `details`), so
40
+ * branch on the class for not-found and on the code only to tell which row is missing.
41
+ *
42
+ * A platform release older than these codes answers each not-found and a taken slug as
43
+ * `USER_INPUT_ERROR` (400) in its own words ("Recommendation group not found"), a slug lost in a
44
+ * race as `DUPLICATE_RECORD`, and takes another store's collection or facet value as a rule's
45
+ * source.
46
+ *
47
+ * No release gate: every operation here is served by every platform release this SDK supports.
48
+ * No method deduplicates on `idempotencyKey`: repeating a `create` makes a second row.
49
+ */
50
+ import type { Transport } from '@sixtyninecommerce/core';
51
+ import type { Call } from '../types.js';
52
+ /** What a `DYNAMIC` group's entry draws its products from. */
53
+ export type RecommendationSourceType = 'PRODUCT' | 'COLLECTION' | 'FACET_VALUE' | 'SAME_COLLECTION' | 'SAME_PROPERTIES';
54
+ /** `DYNAMIC`: rules (entries). `MANUAL`: products picked per viewed product (`manual.set`). */
55
+ export type RecommendationGroupMode = 'DYNAMIC' | 'MANUAL';
56
+ /** The source types whose entry names a row (`sourceId`); the others must name none. */
57
+ export declare const RECOMMENDATION_SOURCE_TYPES_WITH_ID: readonly RecommendationSourceType[];
58
+ /** One rule of a `DYNAMIC` group. */
59
+ export interface RecommendationEntry {
60
+ id: string;
61
+ groupId: string;
62
+ sourceType: RecommendationSourceType;
63
+ /**
64
+ * The product, collection or facet value for `PRODUCT`, `COLLECTION` and `FACET_VALUE`; `null`
65
+ * for `SAME_COLLECTION` and `SAME_PROPERTIES`. Always a row of this store when it was written:
66
+ * `entries.create` and `entries.update` refuse any other (`RECOMMENDATION_SOURCE_NOT_FOUND`). A
67
+ * row deleted or taken out of the store since adds nothing, and the storefront never draws from
68
+ * another store's collection or facet value, a rule stored before the refusal included. A deleted
69
+ * product's `PRODUCT` entries are removed with it.
70
+ */
71
+ sourceId: string | null;
72
+ position: number;
73
+ /** ISO-8601 UTC. */
74
+ createdAt: string;
75
+ /** ISO-8601 UTC. */
76
+ updatedAt: string;
77
+ }
78
+ /** A group without its entries, as `manual.get` and `manual.set` answer it. */
79
+ export interface RecommendationGroupSummary {
80
+ id: string;
81
+ name: string;
82
+ /** Unique within the store; the storefront asks for one group by it. */
83
+ slug: string;
84
+ description: string | null;
85
+ /** The most products the storefront shows for this group. */
86
+ maxItems: number;
87
+ position: number;
88
+ enabled: boolean;
89
+ mode: RecommendationGroupMode;
90
+ /** ISO-8601 UTC. */
91
+ createdAt: string;
92
+ /** ISO-8601 UTC. */
93
+ updatedAt: string;
94
+ }
95
+ export interface RecommendationGroup extends RecommendationGroupSummary {
96
+ /** In `position` order (ties: the older first). Always empty on a `MANUAL` group. */
97
+ entries: RecommendationEntry[];
98
+ }
99
+ /** One `MANUAL` group and the products picked in it for one viewed product. */
100
+ export interface ProductManualRecommendations {
101
+ group: RecommendationGroupSummary;
102
+ /** In the order picked; empty when none are. */
103
+ productIds: string[];
104
+ }
105
+ export interface CreateRecommendationGroupInput {
106
+ name: string;
107
+ /** Defaults to the name, lower-cased, every run of other characters than `a-z0-9` a `-`. */
108
+ slug?: string;
109
+ description?: string;
110
+ /** Defaults to 10. The engine sets no bounds. */
111
+ maxItems?: number;
112
+ /** Defaults to after the store's last group. */
113
+ position?: number;
114
+ /** Defaults to true. */
115
+ enabled?: boolean;
116
+ /** Defaults to `DYNAMIC`; never changes afterwards. */
117
+ mode?: RecommendationGroupMode;
118
+ }
119
+ /** Only the fields named change; `description: null` clears it. */
120
+ export interface UpdateRecommendationGroupInput {
121
+ name?: string;
122
+ slug?: string;
123
+ description?: string | null;
124
+ maxItems?: number;
125
+ position?: number;
126
+ enabled?: boolean;
127
+ }
128
+ export interface CreateRecommendationEntryInput {
129
+ groupId: string;
130
+ sourceType: RecommendationSourceType;
131
+ /** Required for `PRODUCT`, `COLLECTION` and `FACET_VALUE`; refused for the other two. */
132
+ sourceId?: string;
133
+ /** Defaults to after the group's last entry. */
134
+ position?: number;
135
+ }
136
+ /** Only the fields named change; `sourceId: null` clears it (for a `SAME_*` source type). */
137
+ export interface UpdateRecommendationEntryInput {
138
+ sourceType?: RecommendationSourceType;
139
+ sourceId?: string | null;
140
+ position?: number;
141
+ }
142
+ export interface RecommendationsModule {
143
+ groups: {
144
+ /**
145
+ * Every group of the store, in `position` order (ties: the older first), each with its
146
+ * entries. The engine answers them all at once; there are no pages.
147
+ *
148
+ * @effect read
149
+ */
150
+ list(options?: Call): Promise<RecommendationGroup[]>;
151
+ /**
152
+ * One group with its entries; `null` when the store has no group with that id.
153
+ *
154
+ * @effect read
155
+ */
156
+ get(id: string, options?: Call): Promise<RecommendationGroup | null>;
157
+ /**
158
+ * Refused (`RECOMMENDATION_GROUP_SLUG_TAKEN`) when the store already has a group with that
159
+ * slug, the defaulted one included.
160
+ *
161
+ * @effect write
162
+ */
163
+ create(input: CreateRecommendationGroupInput, options?: Call): Promise<RecommendationGroup>;
164
+ /**
165
+ * Refused: a group the store does not have (`ENTITY_NOT_FOUND`), a slug another of its groups
166
+ * holds (`RECOMMENDATION_GROUP_SLUG_TAKEN`).
167
+ *
168
+ * @effect write
169
+ */
170
+ update(id: string, input: UpdateRecommendationGroupInput, options?: Call): Promise<RecommendationGroup>;
171
+ /**
172
+ * Deletes the group, its entries and every product's picks in it. Refused
173
+ * (`ENTITY_NOT_FOUND`) for a group the store does not have.
174
+ *
175
+ * @effect destructive
176
+ */
177
+ delete(id: string, options?: Call): Promise<void>;
178
+ /**
179
+ * Gives the named groups the positions `0, 1, 2…` in the order given, and answers every group
180
+ * as `list` does. An id the store does not hold is skipped without a word; a group left out
181
+ * keeps its position, which may then equal a named one's: name them all.
182
+ *
183
+ * @effect write
184
+ */
185
+ reorder(ids: string[], options?: Call): Promise<RecommendationGroup[]>;
186
+ };
187
+ /** The rules of `DYNAMIC` groups. */
188
+ entries: {
189
+ /**
190
+ * Refused: a group the store does not have (`ENTITY_NOT_FOUND`); a `sourceId` that is not a
191
+ * product, collection or facet value of the store (`RECOMMENDATION_SOURCE_NOT_FOUND`: another
192
+ * store's is refused like an unknown id); a `MANUAL` group, or a `sourceId` missing for
193
+ * `PRODUCT` / `COLLECTION` / `FACET_VALUE` or sent for `SAME_COLLECTION` / `SAME_PROPERTIES`
194
+ * (`USER_INPUT_ERROR`).
195
+ *
196
+ * @effect write
197
+ */
198
+ create(input: CreateRecommendationEntryInput, options?: Call): Promise<RecommendationEntry>;
199
+ /**
200
+ * Refused: an entry the store does not have (`ENTITY_NOT_FOUND`); a source type and
201
+ * `sourceId` that do not go together once the change is applied (`USER_INPUT_ERROR`); a
202
+ * change of either that leaves the rule naming no row of the store
203
+ * (`RECOMMENDATION_SOURCE_NOT_FOUND`). A change of `position` alone is not judged.
204
+ *
205
+ * @effect write
206
+ */
207
+ update(id: string, input: UpdateRecommendationEntryInput, options?: Call): Promise<RecommendationEntry>;
208
+ /**
209
+ * Refused (`ENTITY_NOT_FOUND`) for an entry the store does not have.
210
+ *
211
+ * @effect destructive
212
+ */
213
+ delete(id: string, options?: Call): Promise<void>;
214
+ /**
215
+ * Gives the group's named entries the positions `0, 1, 2…` in the order given, and answers
216
+ * the group's entries in `position` order. An id that is not one of THIS group's entries is
217
+ * skipped without a word (another group's entry never moves); an entry left out keeps its
218
+ * position. Refused (`ENTITY_NOT_FOUND`) for a group the store does not have.
219
+ *
220
+ * @effect write
221
+ */
222
+ reorder(groupId: string, ids: string[], options?: Call): Promise<RecommendationEntry[]>;
223
+ };
224
+ /** The products picked per viewed product in `MANUAL` groups (the product page's card). */
225
+ manual: {
226
+ /**
227
+ * Every `MANUAL` group of the store, in `position` order, each with the products picked for
228
+ * `productId`. A product with no picks (or none of this store) answers every group with an
229
+ * empty `productIds`; a store with no manual group answers `[]`.
230
+ *
231
+ * @effect read
232
+ */
233
+ get(productId: string, options?: Call): Promise<ProductManualRecommendations[]>;
234
+ /**
235
+ * Replaces the products picked for `productId` in one `MANUAL` group, in the order given:
236
+ * `[]` clears them. The product itself and repeats are dropped (the first one kept). One
237
+ * write: refused, nothing changes.
238
+ *
239
+ * Refused: a group the store does not have, or `productId` not a product of this store
240
+ * (`ENTITY_NOT_FOUND`); a picked product that is not (`RECOMMENDATION_TARGET_NOT_FOUND`,
241
+ * naming each, `details` pointing at where it was sent); a `DYNAMIC` group
242
+ * (`USER_INPUT_ERROR`).
243
+ *
244
+ * @effect write
245
+ */
246
+ set(productId: string, groupId: string, productIds: string[], options?: Call): Promise<RecommendationGroupSummary>;
247
+ };
248
+ }
249
+ export declare const RECOMMENDATION_GROUPS_QUERY = "\n query CatalogRecommendationGroups {\n recommendationGroups { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
250
+ export declare const RECOMMENDATION_GROUP_QUERY = "\n query CatalogRecommendationGroup($id: ID!) {\n recommendationGroup(id: $id) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
251
+ export declare const CREATE_RECOMMENDATION_GROUP_MUTATION = "\n mutation CatalogCreateRecommendationGroup($input: CreateRecommendationGroupInput!) {\n createRecommendationGroup(input: $input) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
252
+ export declare const UPDATE_RECOMMENDATION_GROUP_MUTATION = "\n mutation CatalogUpdateRecommendationGroup($id: ID!, $input: UpdateRecommendationGroupInput!) {\n updateRecommendationGroup(id: $id, input: $input) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
253
+ export declare const DELETE_RECOMMENDATION_GROUP_MUTATION = "\n mutation CatalogDeleteRecommendationGroup($id: ID!) {\n deleteRecommendationGroup(id: $id) { \n result\n message\n }\n }\n";
254
+ export declare const REORDER_RECOMMENDATION_GROUPS_MUTATION = "\n mutation CatalogReorderRecommendationGroups($ids: [ID!]!) {\n reorderRecommendationGroups(ids: $ids) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
255
+ export declare const CREATE_RECOMMENDATION_ENTRY_MUTATION = "\n mutation CatalogCreateRecommendationEntry($input: CreateRecommendationEntryInput!) {\n createRecommendationEntry(input: $input) { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n";
256
+ export declare const UPDATE_RECOMMENDATION_ENTRY_MUTATION = "\n mutation CatalogUpdateRecommendationEntry($id: ID!, $input: UpdateRecommendationEntryInput!) {\n updateRecommendationEntry(id: $id, input: $input) { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n";
257
+ export declare const DELETE_RECOMMENDATION_ENTRY_MUTATION = "\n mutation CatalogDeleteRecommendationEntry($id: ID!) {\n deleteRecommendationEntry(id: $id) { \n result\n message\n }\n }\n";
258
+ export declare const REORDER_RECOMMENDATION_ENTRIES_MUTATION = "\n mutation CatalogReorderRecommendationEntries($groupId: ID!, $ids: [ID!]!) {\n reorderRecommendationEntries(groupId: $groupId, ids: $ids) { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n";
259
+ export declare const PRODUCT_MANUAL_RECOMMENDATIONS_QUERY = "\n query CatalogProductManualRecommendations($productId: ID!) {\n productManualRecommendations(productId: $productId) {\n group { \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n }\n productIds\n }\n }\n";
260
+ export declare const SET_PRODUCT_MANUAL_RECOMMENDATIONS_MUTATION = "\n mutation CatalogSetProductManualRecommendations(\n $productId: ID!\n $groupId: ID!\n $targetProductIds: [ID!]!\n ) {\n setProductManualRecommendations(\n productId: $productId\n groupId: $groupId\n targetProductIds: $targetProductIds\n ) { \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n }\n }\n";
261
+ export declare function createRecommendationsModule(transport: Transport): RecommendationsModule;